mcpField
S
syncable-entity-types-and-constants

syncable-entity-types-and-constants

github.com/twentyhq开发60.0k54 次安装今天更新
代码仓库

关于 syncable-entity-types-and-constants

用于在 Twenty 中添加可同步实体的开发技能,指导定义类型、实体及中央注册项,为后续缓存、迁移和集成步骤奠定基础。

主要功能

  1. 01注册元数据名称
  2. 02定义 TypeORM 实体
  3. 03定义扁平实体与操作类型
  4. 04配置可编辑属性
  5. 05注册关系常量

适用场景

  1. 01启动可同步实体的实现
  2. 02配置实体属性和关系

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/twentyhq/twenty --skill syncable-entity-types-and-constants

安装后在 Claude Code 中输入/syncable-entity-types-and-constants确认已加载。

详细介绍

可同步实体:类型与常量(第 1/6 步)

目的:定义所有类型、实体,并在中央常量中注册。这是基础——其他一切都依赖于这些类型是否正确。

何时使用:创建任何新的可同步实体时的第一步。必须在其他步骤之前完成。


快速开始

此步骤会创建:

  1. 元数据名称常量(twenty-shared)
  2. TypeORM 实体(继承 SyncableEntity)
  3. 扁平实体类型
  4. 操作类型(通用 + 扁平)
  5. 中央常量注册(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] 中的一个键,如果目标实体未暴露反向的一对多关系,则为 null
  • universalForeignKey — 通过将 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 规则。

评论

支持 Markdown