关于 syncable-entity-types-and-constants
用于在 Twenty 中添加可同步实体的开发技能,指导定义类型、实体及中央注册项,为后续缓存、迁移和集成步骤奠定基础。
主要功能
- 01注册元数据名称
- 02定义 TypeORM 实体
- 03定义扁平实体与操作类型
- 04配置可编辑属性
- 05注册关系常量
适用场景
- 01启动可同步实体的实现
- 02配置实体属性和关系
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ twentyhq/ twenty --skill syncable-entity-types-and-constants安装后在 Claude Code 中输入/syncable-entity-types-and-constants确认已加载。
详细介绍
可同步实体:类型与常量(第 1/6 步)
目的:定义所有类型、实体,并在中央常量中注册。这是基础——其他一切都依赖于这些类型是否正确。
何时使用:创建任何新的可同步实体时的第一步。必须在其他步骤之前完成。
快速开始
此步骤会创建:
- 元数据名称常量(twenty-shared)
- TypeORM 实体(继承
SyncableEntity) - 扁平实体类型
- 操作类型(通用 + 扁平)
- 中央常量注册(5 个常量)
第 1 步:添加元数据名称
文件:packages/twenty-shared/src/metadata/all-metadata-name.constant.ts
export const ALL_METADATA_NAME = {
// ... existing entries
myEntity: 'myEntity',
} as const;
第 2 步:创建 TypeORM 实体
文件:src/engine/metadata-modules/my-entity/entities/my-entity.entity.ts
import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
@Entity({ name: 'myEntity' })
export class MyEntityEntity extends SyncableEntity {
@Column({ type: 'varchar' })
name: string;
@Column({ type: 'varchar' })
label: string;
@Column({ type: 'boolean', default: true })
isCustom: boolean;
// Foreign key example (optional)
@Column({ type: 'uuid', nullable: true })
parentEntityId: string | null;
@ManyToOne(() => ParentEntityEntity, { nullable: true })
@JoinColumn({ name: 'parentEntityId' })
parentEntity: ParentEntityEntity | null;
// JSONB column example (optional)
@Column({ type: 'jsonb', nullable: true })
settings: Record<string, any> | null;
}
关键规则:
- 必须继承
SyncableEntity(提供id、universalIdentifier、applicationId等) - 必须有
isCustom布尔列 - 对 JSON 数据使用
@Column({ type: 'jsonb' })
第 3 步:定义扁平实体类型
文件:src/engine/metadata-modules/flat-my-entity/types/flat-my-entity.type.ts
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
import { type MyEntityEntity } from 'src/engine/metadata-modules/my-entity/entities/my-entity.entity';
export type FlatMyEntity = FlatEntityFrom<MyEntityEntity>;
映射文件(如果实体有索引查找):
// flat-my-entity-maps.type.ts
export type FlatMyEntityMaps = {
byId: Record<string, FlatMyEntity>;
byName: Record<string, FlatMyEntity>;
// Add other indexes as needed
};
第 4 步:定义可编辑属性
文件:src/engine/metadata-modules/flat-my-entity/constants/editable-flat-my-entity-properties.constant.ts
export const EDITABLE_FLAT_MY_ENTITY_PROPERTIES = [
'name',
'label',
'description',
'parentEntityId',
'settings',
] as const satisfies ReadonlyArray<keyof FlatMyEntity>;
规则:只包含可更新的属性(排除 id、createdAt、universalIdentifier 等)
第 5 步:定义操作类型
文件:src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/my-entity/types/workspace-migration-my-entity-action.type.ts
import { type FlatMyEntity } from 'src/engine/metadata-modules/flat-my-entity/types/flat-my-entity.type';
import { type UniversalFlatMyEntity } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-my-entity.type';
// Universal actions (used by builder/runner)
export type UniversalCreateMyEntityAction = {
type: 'create';
metadataName: 'myEntity';
universalFlatEntity: UniversalFlatMyEntity;
};
export type UniversalUpdateMyEntityAction = {
type: 'update';
metadataName: 'myEntity';
universalFlatEntity: UniversalFlatMyEntity;
universalUpdates: Partial<UniversalFlatMyEntity>;
};
export type UniversalDeleteMyEntityAction = {
type: 'delete';
metadataName: 'myEntity';
universalFlatEntity: UniversalFlatMyEntity;
};
// Flat actions (internal to runner)
export type FlatCreateMyEntityAction = {
type: 'create';
metadataName: 'myEntity';
flatEntity: FlatMyEntity;
};
export type FlatUpdateMyEntityAction = {
type: 'update';
metadataName: 'myEntity';
flatEntity: FlatMyEntity;
updates: Partial<FlatMyEntity>;
};
export type FlatDeleteMyEntityAction = {
type: 'delete';
metadataName: 'myEntity';
flatEntity: FlatMyEntity;
};
第 6 步:在中央常量中注册
6a. AllFlatEntityTypesByMetadataName
文件:src/engine/metadata-modules/flat-entity/types/all-flat-entity-types-by-metadata-name.type.ts
export type AllFlatEntityTypesByMetadataName = {
// ... existing entries
myEntity: {
flatEntityMaps: FlatMyEntityMaps;
universalActions: {
create: UniversalCreateMyEntityAction;
update: UniversalUpdateMyEntityAction;
delete: UniversalDeleteMyEntityAction;
};
flatActions: {
create: FlatCreateMyEntityAction;
update: FlatUpdateMyEntityAction;
delete: FlatDeleteMyEntityAction;
};
flatEntity: FlatMyEntity;
universalFlatEntity: UniversalFlatMyEntity;
entity: MyEntityEntity;
};
};
6b. ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME
文件:src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts
export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
// ... existing entries
myEntity: {
name: { toCompare: true },
label: { toCompare: true },
description: { toCompare: true },
parentEntityId: {
toCompare: true,
universalProperty: 'parentEntityUniversalIdentifier',
},
settings: {
toCompare: true,
toStringify: true,
universalProperty: 'universalSettings',
},
},
} as const;
规则:
toCompare: true→ 可编辑属性(检查是否有变更)toStringify: true→ JSONB/对象属性(需要 JSON 序列化)universalProperty→ 映射到通用版本(用于外键和带SerializedRelation的 JSONB)
6c. ALL_ONE_TO_MANY_METADATA_RELATIONS
文件:src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant.ts
此常量经过类型检查——metadataName、flatEntityForeignKeyAggregator 和 universalFlatEntityForeignKeyAggregator 的值派生自实体类型定义。聚合器名称遵循以下模式:从关系属性名称中移除末尾的 's',然后追加 Ids 或 UniversalIdentifiers。
export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
// ... existing entries
myEntity: {
// If myEntity has a `childEntities: ChildEntityEntity[]` property:
childEntities: {
metadataName: 'childEntity',
flatEntityForeignKeyAggregator: 'childEntityIds',
universalFlatEntityForeignKeyAggregator: 'childEntityUniversalIdentifiers',
},
// null for relations to non-syncable entities
someNonSyncableRelation: null,
},
} as const;
6d. ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY
文件:src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant.ts
底层基础常量。仅包含 foreignKey——即以 Id 结尾、用于存储外键的列名。会根据实体属性进行类型检查。
export const ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY = {
// ... existing entries
myEntity: {
workspace: null,
application: null,
parentEntity: {
foreignKey: 'parentEntityId',
},
},
} as const;
6e. ALL_MANY_TO_ONE_METADATA_RELATIONS
文件:src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts
派生自 ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY(用于 foreignKey 类型和 universalForeignKey 的推导)和 ALL_ONE_TO_MANY_METADATA_RELATIONS(用于 inverseOneToManyProperty 键的约束)。这是工具函数和乐观工具所消费的主要常量。
export const ALL_MANY_TO_ONE_METADATA_RELATIONS = {
// ... existing entries
myEntity: {
workspace: null,
application: null,
parentEntity: {
metadataName: 'parentEntity',
foreignKey: 'parentEntityId',
inverseOneToManyProperty: 'myEntities', // key in ALL_ONE_TO_MANY_METADATA_RELATIONS['parentEntity'], or null if no inverse
isNullable: false,
universalForeignKey: 'parentEntityUniversalIdentifier',
},
},
} as const;
推导依赖图:
ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY ALL_ONE_TO_MANY_METADATA_RELATIONS
(foreignKey only) (metadataName, aggregators)
│ │
│ FK type + universalFK derivation │ inverseOneToManyProperty keys
│ │
└────────────────┬───────────────────────┘
▼
ALL_MANY_TO_ONE_METADATA_RELATIONS
(metadataName, foreignKey, inverseOneToManyProperty,
isNullable, universalForeignKey)
规则:
workspace: null、application: null— 始终存在,始终为 null(不可同步的关系)inverseOneToManyProperty— 必须是ALL_ONE_TO_MANY_METADATA_RELATIONS[targetMetadataName]中的一个键,如果目标实体未暴露反向的一对多关系,则为nulluniversalForeignKey— 通过将foreignKey中的Id后缀替换为UniversalIdentifier推导而来- 乐观工具函数在运行时通过在
ALL_ONE_TO_MANY_METADATA_RELATIONS中查找inverseOneToManyProperty来解析flatEntityForeignKeyAggregator/universalFlatEntityForeignKeyAggregator
检查清单
在进入第 2 步之前:
- 已将元数据名称添加到
ALL_METADATA_NAME - 已创建 TypeORM 实体(继承
SyncableEntity) - 已添加
isCustom列 - 已定义扁平实体类型
- 已定义扁平实体映射类型(如需要)
- 已定义可编辑属性常量
- 已定义 universal 和 flat 操作类型
- 已在
AllFlatEntityTypesByMetadataName中注册 - 已在
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME中注册 - 已在
ALL_ONE_TO_MANY_METADATA_RELATIONS中注册(如果实体有一对多关系) - 已在
ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY中注册 - 已在
ALL_MANY_TO_ONE_METADATA_RELATIONS中注册 - TypeScript 编译无错误
下一步
一旦所有类型和常量都已定义,请继续: 可同步实体:缓存与转换(第 2/6 步)
完整工作流请参见 @creating-syncable-entity 规则。