본문으로 건너뛰기
cccskills
무료GitHub에 공개

syncable-entity-types-and-constants

Define the types and shared registrations needed to add a syncable entity to Twenty's server. Set up database entities, editable fields, action types, and relationships as the foundation for workspace migration implementation.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

Contributing to the Twenty codebase itself (twentyhq/twenty server internals), not for building apps on top of Twenty. Define types, entities, and central constant registrations for syncable entities in Twenty's workspace migration system. Use when creating new syncable entities, defining TypeORM entities, flat entity types, or registering in central constants (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).

설치 방법 보기

이럴 때 유용합니다

  • Define a new syncable entity in Twenty
  • Create flat data and migration action types
  • Register editable fields and comparison rules
  • Register relationships between entities

포함된 파일(1)

  • SKILL.md11.1 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Syncable Entity: Types & Constants (Step 1/6)

Purpose: Define all types, entities, and register in central constants. This is the foundation - everything else depends on these types being correct.

When to use: First step when creating any new syncable entity. Must be completed before other steps.


Quick Start

This step creates:

  1. Metadata name constant (twenty-shared)
  2. TypeORM entity (extends SyncableEntity)
  3. Flat entity types
  4. Action types (universal + flat)
  5. Central constant registrations (5 constants)

Step 1: Add Metadata Name

File: packages/twenty-shared/src/metadata/all-metadata-name.constant.ts

export const ALL_METADATA_NAME = {
  // ... existing entries
  myEntity: 'myEntity',
} as const;

Step 2: Create TypeORM Entity

File: 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;
}

Key rules:

  • Must extend SyncableEntity (provides id, universalIdentifier, applicationId, etc.)
  • Must have isCustom boolean column
  • Use @Column({ type: 'jsonb' }) for JSON data

Step 3: Define Flat Entity Types

File: 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>;

Maps file (if entity has indexed lookups):

// flat-my-entity-maps.type.ts
export type FlatMyEntityMaps = {
  byId: Record<string, FlatMyEntity>;
  byName: Record<string, FlatMyEntity>;
  // Add other indexes as needed
};

Step 4: Define Editable Properties

File: 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>;

Rule: Only include properties that can be updated (exclude id, createdAt, universalIdentifier, etc.)


Step 5: Define Action Types

File: 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;
};

Step 6: Register in Central Constants

6a. AllFlatEntityTypesByMetadataName

File: 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

File: 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;

Rules:

  • toCompare: true → Editable property (checked for changes)
  • toStringify: true → JSONB/object property (needs JSON serialization)
  • universalProperty → Maps to universal version (for foreign keys & JSONB with SerializedRelation)

6c. ALL_ONE_TO_MANY_METADATA_RELATIONS

File: src/engine/metadata-modules/flat-entity/constant/all-one-to-many-metadata-relations.constant.ts

This constant is type-checked — values for metadataName, flatEntityForeignKeyAggregator, and universalFlatEntityForeignKeyAggregator are derived from entity type definitions. The aggregator names follow the pattern: remove trailing 's' from the relation property name, then append Ids or 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

File: src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-foreign-key.constant.ts

Low-level primitive constant. Only contains foreignKey — the column name ending in Id that stores the foreign key. Type-checked against entity properties.

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

File: src/engine/metadata-modules/flat-entity/constant/all-many-to-one-metadata-relations.constant.ts

Derived from both ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY (for foreignKey type and universalForeignKey derivation) and ALL_ONE_TO_MANY_METADATA_RELATIONS (for inverseOneToManyProperty key constraint). This is the main constant consumed by utils and optimistic tooling.

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;

Derivation dependency graph:

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)

Rules:

  • workspace: null, application: null — always present, always null (non-syncable relations)
  • inverseOneToManyProperty — must be a key in ALL_ONE_TO_MANY_METADATA_RELATIONS[targetMetadataName], or null if the target entity doesn't expose an inverse one-to-many relation
  • universalForeignKey — derived from foreignKey by replacing the Id suffix with UniversalIdentifier
  • Optimistic utils resolve flatEntityForeignKeyAggregator / universalFlatEntityForeignKeyAggregator at runtime by looking up inverseOneToManyProperty in ALL_ONE_TO_MANY_METADATA_RELATIONS

Checklist

Before moving to Step 2:

  • Metadata name added to ALL_METADATA_NAME
  • TypeORM entity created (extends SyncableEntity)
  • isCustom column added
  • Flat entity type defined
  • Flat entity maps type defined (if needed)
  • Editable properties constant defined
  • Universal and flat action types defined
  • Registered in AllFlatEntityTypesByMetadataName
  • Registered in ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME
  • Registered in ALL_ONE_TO_MANY_METADATA_RELATIONS (if entity has one-to-many relations)
  • Registered in ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY
  • Registered in ALL_MANY_TO_ONE_METADATA_RELATIONS
  • TypeScript compiles without errors

Next Step

Once all types and constants are defined, proceed to: Syncable Entity: Cache & Transform (Step 2/6)

For complete workflow, see @creating-syncable-entity rule.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Create a new app that extends a running Twenty instance. Guide project naming and instance selection, then use the scaffolder to install dependencies, authenticate, initialize Git, and complete the initial sync and installation.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Start a new Twenty app project
  • Scaffold a CRM extension or integration
  • Create an app connected to an existing Twenty instance
twentyhq/twenty5.9만2026년 10월 18일 업데이트

Extend an existing Twenty app with data fields, layouts, custom screens, automated logic, workflows, and permissions. The skill guides code organization, entity creation, type checking, linting, and syncing definitions to the active remote.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Add custom fields and objects to an existing Twenty app
  • Build custom screens and standalone pages
  • Run logic when records are created
twentyhq/twenty5.9만2026년 10월 18일 업데이트

Operate an existing Twenty app by syncing changes, managing server connections, building and deploying releases, and inspecting function logs. It also guides troubleshooting and CI/CD, the workflows used to automate testing and deployment.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Sync app changes to a Twenty server
  • Prepare and deploy an app update
  • Investigate failed builds or deployments
twentyhq/twenty5.9만2026년 10월 18일 업데이트

Prepare an existing Twenty app for npm or marketplace publication by reviewing its README, listing details, package metadata, logos, and screenshots. The workflow includes checking publication settings, validating the build, and running the publishing command.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Prepare a Twenty app marketplace listing
  • Write or review README and About text
  • Check logos, screenshots, and public assets
twentyhq/twenty5.9만2026년 10월 18일 업데이트

qa-scout

무료

Check a Twenty pull request through real browser scenarios derived from its changes. Verify database effects and server logs alongside visible results, then produce screenshots, a structured verdict, and a report of any failures or uncertainty.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Validate user flows before or after merging a Twenty pull request
  • Check database writes and timeline activity after saving records
  • Investigate backend errors hidden behind a successful screen
twentyhq/twenty5.9만2026년 10월 18일 업데이트

Implement validation and migration action builders for syncable entities, data types handled by Twenty's synchronization system. This skill covers required fields, uniqueness, reference checks, and orchestration wiring, with validators that return errors without throwing exceptions or changing data.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Add required-field and duplicate checks
  • Validate references to parent entities
  • Build create, update, and delete migration actions
twentyhq/twenty5.9만2026년 10월 18일 업데이트

twentyhq의 스킬 모두 보기

이 스킬의 문제 신고