Add default fields on object manifest (#17977)

as title

<img width="830" height="227" alt="image"
src="https://github.com/user-attachments/assets/8cbf4a14-5d1d-496f-a146-f31079e6e602"
/>
This commit is contained in:
martmull
2026-02-16 16:50:46 +01:00
committed by GitHub
parent 2b7b05de2e
commit c3d565f266
11 changed files with 745 additions and 8 deletions
@@ -277,7 +277,11 @@ Key points:
- You can scaffold new objects using `yarn twenty entity:add`, which guides you through naming, fields, and relationships.
<Note>
**Base fields are created automatically.** When you define a custom object, Twenty automatically adds standard fields such as `name`, `createdAt`, `updatedAt`, `createdBy`, `position`, and `deletedAt`. You don't need to define these in your `fields` array — only add your custom fields.
**Base fields are created automatically.** When you define a custom object, Twenty automatically adds standard fields
such as `id`, `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy` and `deletedAt`.
You don't need to define these in your `fields` array — only add your custom fields.
You can override default fields by defining a field with the same name in your `fields` array,
but this is not recommended.
</Note>
@@ -1,6 +1,7 @@
import { FieldType } from '@/sdk';
import type { Manifest } from 'twenty-shared/application';
import { PermissionFlagType } from 'twenty-shared/constants';
import { FieldMetadataType } from 'twenty-shared/types';
export const EXPECTED_MANIFEST: Manifest = {
publicAssets: [
@@ -121,6 +122,82 @@ export const EXPECTED_MANIFEST: Manifest = {
type: FieldType.TEXT,
universalIdentifier: 'b0b1b2b3-b4b5-4000-8000-000000000003',
},
{
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
isNullable: false,
label: 'Id',
name: 'id',
type: FieldMetadataType.UUID,
universalIdentifier: 'b5830f92-1abe-5ae7-b689-7821dda00b15',
},
{
defaultValue: null,
description: 'Name',
icon: 'IconAbc',
isNullable: true,
label: 'Name',
name: 'name',
type: FieldMetadataType.TEXT,
universalIdentifier: 'ae2b2e5a-6c8b-5324-b639-8f17e3bc0420',
},
{
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
isNullable: false,
label: 'Creation date',
name: 'createdAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: '7c84a501-41a6-531e-bc5e-8062843f6cba',
},
{
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
isNullable: false,
label: 'Last update',
name: 'updatedAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: '72c2903a-91c9-5f36-8668-b53b083ae4cf',
},
{
defaultValue: null,
description: 'Deletion date',
icon: 'IconCalendarClock',
isNullable: true,
label: 'Deleted at',
name: 'deletedAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: '007f5d8e-d376-519e-bd07-aea41a53f7d9',
},
{
defaultValue: {
name: "''",
source: "'MANUAL'",
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
isNullable: false,
label: 'Created by',
name: 'createdBy',
type: FieldMetadataType.ACTOR,
universalIdentifier: '44053354-0d8b-5d66-bfa8-7e7d221de3db',
},
{
defaultValue: {
name: "''",
source: "'MANUAL'",
},
description: 'The workspace member who last updated the record',
icon: 'IconUserCircle',
isNullable: false,
label: 'Updated by',
name: 'updatedBy',
type: FieldMetadataType.ACTOR,
universalIdentifier: '38e2c671-cbf5-5773-a04a-e5a2d6c323c8',
},
],
icon: 'IconNote',
labelIdentifierFieldMetadataUniversalIdentifier:
@@ -199,6 +276,82 @@ export const EXPECTED_MANIFEST: Manifest = {
type: FieldType.DATE_TIME,
universalIdentifier: 'e06abe72-5b44-4e7f-93be-afc185a3c433',
},
{
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
isNullable: false,
label: 'Id',
name: 'id',
type: FieldMetadataType.UUID,
universalIdentifier: '56e652a3-fac2-54fd-a379-f01b72758268',
},
{
defaultValue: null,
description: 'Name',
icon: 'IconAbc',
isNullable: true,
label: 'Name',
name: 'name',
type: FieldMetadataType.TEXT,
universalIdentifier: 'c59a149f-5861-51a3-a924-2bd2fa31daaa',
},
{
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
isNullable: false,
label: 'Creation date',
name: 'createdAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: '236e2ec5-3636-5b97-b19d-f7799cb88d99',
},
{
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
isNullable: false,
label: 'Last update',
name: 'updatedAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: '2cf640eb-09a7-52f2-820b-419071c5ec24',
},
{
defaultValue: null,
description: 'Deletion date',
icon: 'IconCalendarClock',
isNullable: true,
label: 'Deleted at',
name: 'deletedAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: '2eb62b4a-6888-58ab-9887-a879321fff31',
},
{
defaultValue: {
name: "''",
source: "'MANUAL'",
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
isNullable: false,
label: 'Created by',
name: 'createdBy',
type: FieldMetadataType.ACTOR,
universalIdentifier: '36e95d21-9c86-5b6f-8133-a66d09fc8952',
},
{
defaultValue: {
name: "''",
source: "'MANUAL'",
},
description: 'The workspace member who last updated the record',
icon: 'IconUserCircle',
isNullable: false,
label: 'Updated by',
name: 'updatedBy',
type: FieldMetadataType.ACTOR,
universalIdentifier: 'dc5231a4-e128-57b0-ac9d-723678d9b239',
},
],
icon: 'IconMail',
labelIdentifierFieldMetadataUniversalIdentifier:
@@ -1,5 +1,6 @@
import { FieldType } from '@/sdk';
import type { Manifest } from 'twenty-shared/application';
import { FieldMetadataType } from 'twenty-shared/types';
export const EXPECTED_MANIFEST: Manifest = {
application: {
@@ -31,6 +32,82 @@ export const EXPECTED_MANIFEST: Manifest = {
label: 'Title',
name: 'title',
},
{
defaultValue: 'uuid',
description: 'Id',
icon: 'Icon123',
isNullable: false,
label: 'Id',
name: 'id',
type: FieldMetadataType.UUID,
universalIdentifier: '5bdb4f57-a349-56d0-b421-845fd55b3026',
},
{
defaultValue: null,
description: 'Name',
icon: 'IconAbc',
isNullable: true,
label: 'Name',
name: 'name',
type: FieldMetadataType.TEXT,
universalIdentifier: '86cc7b06-26de-5b7b-bf4a-31fa246dfd90',
},
{
defaultValue: 'now',
description: 'Creation date',
icon: 'IconCalendar',
isNullable: false,
label: 'Creation date',
name: 'createdAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: 'abe3bc69-1a51-5814-a722-74f5a025e92a',
},
{
defaultValue: 'now',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
isNullable: false,
label: 'Last update',
name: 'updatedAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: 'c296d408-9720-5a86-8586-3490fbf04b86',
},
{
defaultValue: null,
description: 'Deletion date',
icon: 'IconCalendarClock',
isNullable: true,
label: 'Deleted at',
name: 'deletedAt',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: '488333a2-f4ff-5790-88ef-21f07f8f1985',
},
{
defaultValue: {
name: "''",
source: "'MANUAL'",
},
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
isNullable: false,
label: 'Created by',
name: 'createdBy',
type: FieldMetadataType.ACTOR,
universalIdentifier: 'fba6e01b-6579-554e-ad64-a676003c8e68',
},
{
defaultValue: {
name: "''",
source: "'MANUAL'",
},
description: 'The workspace member who last updated the record',
icon: 'IconUserCircle',
isNullable: false,
label: 'Updated by',
name: 'updatedBy',
type: FieldMetadataType.ACTOR,
universalIdentifier: 'fa60f87d-15c8-5ff7-9da0-191fa14a922f',
},
],
},
],
@@ -205,7 +205,10 @@ export class ApiService {
try {
const mutation = `
mutation SyncApplication($manifest: JSON!) {
syncApplication(manifest: $manifest)
syncApplication(manifest: $manifest) {
applicationUniversalIdentifier
actions
}
}
`;
@@ -27,6 +27,7 @@ import {
} from 'twenty-shared/application';
import { getInputSchemaFromSourceCode } from 'twenty-shared/logic-function';
import { assertUnreachable } from 'twenty-shared/utils';
import { injectDefaultFieldsInObjectFields } from '@/cli/utilities/build/manifest/utils/inject-default-fields-in-object-fields';
const loadSources = async (appPath: string): Promise<string[]> => {
return await glob(['**/*.ts', '**/*.tsx'], {
@@ -104,14 +105,26 @@ export const buildManifest = async (
filePath,
});
const { labelIdentifierFieldMetadataUniversalIdentifier, ...rest } =
extract.config;
const objectFieldsWithDefaultFields = injectDefaultFieldsInObjectFields(
extract.config,
);
const labelIdentifierFieldMetadataUniversalIdentifier =
extract.config.labelIdentifierFieldMetadataUniversalIdentifier ??
objectFieldsWithDefaultFields.find((field) => field.name === 'name')
?.universalIdentifier;
if (!labelIdentifierFieldMetadataUniversalIdentifier) {
errors.push(
`No label identifier field found for object ${extract.config.nameSingular}. Please add a field with name "name" to your object.`,
);
break;
}
const objectManifest: ObjectManifest = {
...rest,
labelIdentifierFieldMetadataUniversalIdentifier:
// TODO replace by system id universal identifier once we've refactored it
labelIdentifierFieldMetadataUniversalIdentifier ?? '',
...extract.config,
fields: objectFieldsWithDefaultFields,
labelIdentifierFieldMetadataUniversalIdentifier,
};
objects.push(objectManifest);
@@ -0,0 +1,27 @@
import { generateDefaultFieldUniversalIdentifier } from '@/cli/utilities/build/manifest/utils/generate-default-field-universal-identifier';
import { type ObjectConfig } from '@/sdk/objects/object-config';
describe('generateDefaultFieldUniversalIdentifier', () => {
it('should generate a unique universal identifier', () => {
const objectConfig = {
universalIdentifier: '55b79f88-4094-4b3f-a0ac-1a91a55714f2',
} as ObjectConfig;
const uId1 = generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'id',
});
const uId2 = generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'id',
});
const anotherUId = generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'name',
});
expect(uId1).toEqual(uId2);
expect(uId1).not.toEqual(anotherUId);
});
});
@@ -0,0 +1,165 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { getDefaultObjectFields } from '@/cli/utilities/build/manifest/utils/get-default-object-fields';
import { generateDefaultFieldUniversalIdentifier } from '@/cli/utilities/build/manifest/utils/generate-default-field-universal-identifier';
import type { ObjectConfig } from '@/sdk/objects/object-config';
const mockObjectConfig: ObjectConfig = {
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
nameSingular: 'testObject',
namePlural: 'testObjects',
labelSingular: 'Test Object',
labelPlural: 'Test Objects',
fields: [],
};
const expectedUniversalId = (fieldName: string) =>
generateDefaultFieldUniversalIdentifier({
objectConfig: mockObjectConfig,
fieldName,
});
describe('getDefaultObjectFields', () => {
it('should return an array of 9 default fields', () => {
const fields = getDefaultObjectFields(mockObjectConfig);
expect(fields).toHaveLength(7);
});
it('should include an id field with UUID type', () => {
const fields = getDefaultObjectFields(mockObjectConfig);
const idField = fields.find((field) => field.name === 'id');
expect(idField).toBeDefined();
expect(idField).toEqual({
name: 'id',
label: 'Id',
description: 'Id',
icon: 'Icon123',
isNullable: false,
defaultValue: 'uuid',
type: FieldMetadataType.UUID,
universalIdentifier: expectedUniversalId('id'),
});
});
it('should include a name field with TEXT type', () => {
const fields = getDefaultObjectFields(mockObjectConfig);
const nameField = fields.find((field) => field.name === 'name');
expect(nameField).toBeDefined();
expect(nameField).toEqual({
name: 'name',
label: 'Name',
description: 'Name',
icon: 'IconAbc',
isNullable: true,
defaultValue: null,
type: FieldMetadataType.TEXT,
universalIdentifier: expectedUniversalId('name'),
});
});
it('should include updatedAt and deletedAt fields with DATE_TIME type', () => {
const fields = getDefaultObjectFields(mockObjectConfig);
const updatedAtFields = fields.filter(
(field) => field.name === 'updatedAt',
);
const deletedAtField = fields.find((field) => field.name === 'deletedAt');
expect(updatedAtFields).toHaveLength(1);
expect(updatedAtFields[0]).toEqual({
name: 'updatedAt',
label: 'Last update',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
isNullable: false,
defaultValue: 'now',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: expectedUniversalId('updatedAt'),
});
expect(deletedAtField).toBeDefined();
expect(deletedAtField).toEqual({
name: 'deletedAt',
label: 'Deleted at',
description: 'Deletion date',
icon: 'IconCalendarClock',
isNullable: true,
defaultValue: null,
type: FieldMetadataType.DATE_TIME,
universalIdentifier: expectedUniversalId('deletedAt'),
});
});
it('should include createdBy and updatedBy fields with ACTOR type', () => {
const fields = getDefaultObjectFields(mockObjectConfig);
const createdByField = fields.find((field) => field.name === 'createdBy');
const updatedByField = fields.find((field) => field.name === 'updatedBy');
expect(createdByField).toBeDefined();
expect(createdByField).toEqual({
name: 'createdBy',
label: 'Created by',
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
isNullable: false,
defaultValue: { name: "''", source: "'MANUAL'" },
type: FieldMetadataType.ACTOR,
universalIdentifier: expectedUniversalId('createdBy'),
});
expect(updatedByField).toBeDefined();
expect(updatedByField).toEqual({
name: 'updatedBy',
label: 'Updated by',
description: 'The workspace member who last updated the record',
icon: 'IconUserCircle',
isNullable: false,
defaultValue: { name: "''", source: "'MANUAL'" },
type: FieldMetadataType.ACTOR,
universalIdentifier: expectedUniversalId('updatedBy'),
});
});
it('should generate deterministic universalIdentifiers based on objectConfig', () => {
const firstResult = getDefaultObjectFields(mockObjectConfig);
const secondResult = getDefaultObjectFields(mockObjectConfig);
firstResult.forEach((field, index) => {
expect(field.universalIdentifier).toBe(
secondResult[index].universalIdentifier,
);
});
});
it('should generate different universalIdentifiers for different objectConfigs', () => {
const otherObjectConfig: ObjectConfig = {
...mockObjectConfig,
universalIdentifier: 'ffffffff-ffff-ffff-ffff-ffffffffffff',
};
const fieldsA = getDefaultObjectFields(mockObjectConfig);
const fieldsB = getDefaultObjectFields(otherObjectConfig);
fieldsA.forEach((fieldA, index) => {
expect(fieldA.universalIdentifier).not.toBe(
fieldsB[index].universalIdentifier,
);
});
});
it('should return fields in the expected order', () => {
const fields = getDefaultObjectFields(mockObjectConfig);
const fieldNames = fields.map((field) => field.name);
expect(fieldNames).toEqual([
'id',
'name',
'createdAt',
'updatedAt',
'deletedAt',
'createdBy',
'updatedBy',
]);
});
});
@@ -0,0 +1,143 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { injectDefaultFieldsInObjectFields } from '@/cli/utilities/build/manifest/utils/inject-default-fields-in-object-fields';
import { getDefaultObjectFields } from '@/cli/utilities/build/manifest/utils/get-default-object-fields';
import type { ObjectConfig } from '@/sdk/objects/object-config';
const baseObjectConfig: ObjectConfig = {
universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
nameSingular: 'testObject',
namePlural: 'testObjects',
labelSingular: 'Test Object',
labelPlural: 'Test Objects',
fields: [],
};
describe('injectDefaultFieldsInObjectFields', () => {
it('should return all default fields when objectConfig has no fields', () => {
const result = injectDefaultFieldsInObjectFields(baseObjectConfig);
const defaultFields = getDefaultObjectFields(baseObjectConfig);
expect(result).toEqual(defaultFields);
});
it('should preserve custom fields and append missing default fields', () => {
const customField = {
universalIdentifier: '11111111-1111-1111-1111-111111111111',
name: 'customField',
label: 'Custom Field',
type: FieldMetadataType.TEXT,
};
const objectConfig: ObjectConfig = {
...baseObjectConfig,
fields: [customField],
};
const result = injectDefaultFieldsInObjectFields(objectConfig);
const defaultFields = getDefaultObjectFields(objectConfig);
expect(result[0]).toEqual(customField);
expect(result).toHaveLength(1 + defaultFields.length);
});
it('should not inject a default field when a field with the same name exists', () => {
const customIdField = {
universalIdentifier: '22222222-2222-2222-2222-222222222222',
name: 'id',
label: 'Custom Id',
type: FieldMetadataType.TEXT,
};
const objectConfig: ObjectConfig = {
...baseObjectConfig,
fields: [customIdField],
};
const result = injectDefaultFieldsInObjectFields(objectConfig);
const idFields = result.filter((f) => f.name === 'id');
expect(idFields).toHaveLength(1);
expect(idFields[0]).toEqual(customIdField);
});
it('should skip multiple default fields when overridden by custom fields', () => {
const customFields = [
{
universalIdentifier: '33333333-3333-3333-3333-333333333333',
name: 'id',
label: 'Custom Id',
type: FieldMetadataType.TEXT,
},
{
universalIdentifier: '44444444-4444-4444-4444-444444444444',
name: 'name',
label: 'Custom Name',
type: FieldMetadataType.TEXT,
},
{
universalIdentifier: '55555555-5555-5555-5555-555555555555',
name: 'position',
label: 'Custom Position',
type: FieldMetadataType.NUMBER,
},
];
const objectConfig: ObjectConfig = {
...baseObjectConfig,
fields: customFields,
};
const result = injectDefaultFieldsInObjectFields(objectConfig);
const defaultFields = getDefaultObjectFields(objectConfig);
const overriddenCount = defaultFields.filter((df) =>
customFields.some((cf) => cf.name === df.name),
).length;
expect(result).toHaveLength(
customFields.length + defaultFields.length - overriddenCount,
);
expect(result.filter((f) => f.name === 'id')).toHaveLength(1);
expect(result.filter((f) => f.name === 'name')).toHaveLength(1);
expect(result.filter((f) => f.name === 'position')).toHaveLength(1);
});
it('should place custom fields before default fields in the result', () => {
const customField = {
universalIdentifier: '66666666-6666-6666-6666-666666666666',
name: 'customField',
label: 'Custom Field',
type: FieldMetadataType.TEXT,
};
const objectConfig: ObjectConfig = {
...baseObjectConfig,
fields: [customField],
};
const result = injectDefaultFieldsInObjectFields(objectConfig);
expect(result[0]).toEqual(customField);
});
it('should not mutate the original objectConfig fields array', () => {
const customField = {
universalIdentifier: '77777777-7777-7777-7777-777777777777',
name: 'customField',
label: 'Custom Field',
type: FieldMetadataType.TEXT,
};
const objectConfig: ObjectConfig = {
...baseObjectConfig,
fields: [customField],
};
const originalLength = objectConfig.fields.length;
injectDefaultFieldsInObjectFields(objectConfig);
expect(objectConfig.fields).toHaveLength(originalLength);
});
});
@@ -0,0 +1,16 @@
import type { ObjectConfig } from '@/sdk/objects/object-config';
import { v5 } from 'uuid';
const UNIVERSAL_IDENTIFIER_NAMESPACE = '142046f0-4d80-48b5-ad56-26ad410e895c';
export const generateDefaultFieldUniversalIdentifier = ({
objectConfig,
fieldName,
}: {
objectConfig: ObjectConfig;
fieldName: string;
}) => {
const seed = `${objectConfig.universalIdentifier}-${fieldName}`;
return v5(seed, UNIVERSAL_IDENTIFIER_NAMESPACE);
};
@@ -0,0 +1,116 @@
import { type ObjectManifest } from 'twenty-shared/application';
import type { ObjectConfig } from '@/sdk/objects/object-config';
import { FieldMetadataType } from 'twenty-shared/types';
import { generateDefaultFieldUniversalIdentifier } from '@/cli/utilities/build/manifest/utils/generate-default-field-universal-identifier';
export const getDefaultObjectFields = (
objectConfig: ObjectConfig,
): ObjectManifest['fields'] => {
const idField = {
name: 'id',
label: 'Id',
description: 'Id',
icon: 'Icon123',
isNullable: false,
defaultValue: 'uuid',
type: FieldMetadataType.UUID,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'id',
}),
};
const nameField = {
name: 'name',
label: 'Name',
description: 'Name',
icon: 'IconAbc',
isNullable: true,
defaultValue: null,
type: FieldMetadataType.TEXT,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'name',
}),
};
const createdAtField = {
name: 'createdAt',
label: 'Creation date',
description: 'Creation date',
icon: 'IconCalendar',
isNullable: false,
defaultValue: 'now',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'createdAt',
}),
};
const updatedAtField = {
name: 'updatedAt',
label: 'Last update',
description: 'Last time the record was changed',
icon: 'IconCalendarClock',
isNullable: false,
defaultValue: 'now',
type: FieldMetadataType.DATE_TIME,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'updatedAt',
}),
};
const deletedAtField = {
name: 'deletedAt',
label: 'Deleted at',
description: 'Deletion date',
icon: 'IconCalendarClock',
isNullable: true,
defaultValue: null,
type: FieldMetadataType.DATE_TIME,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'deletedAt',
}),
};
const createdByField = {
name: 'createdBy',
label: 'Created by',
description: 'The creator of the record',
icon: 'IconCreativeCommonsSa',
isNullable: false,
defaultValue: { name: "''", source: "'MANUAL'" },
type: FieldMetadataType.ACTOR,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'createdBy',
}),
};
const updatedByField = {
name: 'updatedBy',
label: 'Updated by',
description: 'The workspace member who last updated the record',
icon: 'IconUserCircle',
isNullable: false,
defaultValue: { name: "''", source: "'MANUAL'" },
type: FieldMetadataType.ACTOR,
universalIdentifier: generateDefaultFieldUniversalIdentifier({
objectConfig,
fieldName: 'updatedBy',
}),
};
return [
idField,
nameField,
createdAtField,
updatedAtField,
deletedAtField,
createdByField,
updatedByField,
];
};
@@ -0,0 +1,20 @@
import type { ObjectConfig } from '@/sdk/objects/object-config';
import { getDefaultObjectFields } from '@/cli/utilities/build/manifest/utils/get-default-object-fields';
export const injectDefaultFieldsInObjectFields = (
objectConfig: ObjectConfig,
) => {
const defaultObjectFields = getDefaultObjectFields(objectConfig);
const objectConfigFieldNames = (objectConfig.fields ?? []).map((f) => f.name);
const fieldsWithDefaultFields = [...objectConfig.fields];
for (const defaultField of defaultObjectFields) {
if (!objectConfigFieldNames.includes(defaultField.name)) {
fieldsWithDefaultFields.push(defaultField);
}
}
return fieldsWithDefaultFields;
};