/** * Extension template — columns YOUR application adds to an entity the platform * or another application owns. Rules: references/extensions.md, SKILL.md rule 20. * * File name: __owner-entity-kebab__.extension.entity.ts (e.g. users.extension.entity.ts) * Class name: __OwnerEntity__Extension (e.g. UsersExtension) * * The platform decides the mode at start, by OWNERSHIP: an entity that is * nobody's or already yours converges in full; one that belongs to someone else * takes only your new fields. Nothing in this file says which — the FILENAME is * what declares the intent, and what lets the gate apply extension mode's * restrictions before the build refuses them. * * Register it in EntityModule.forFeature() exactly like any other entity. */ import { Column, Entity, FieldTypeEnum, Index, ManyToOne, SystemBaseEntity, } from '@suppa/sdk'; // @Entity() carries the OWNER'S existing name and nothing else. title, icon, // type, options, representativeFieldName, importKeyFields and the entity's // localization are ignored in extension mode — they belong to the owner, and // writing them here is a promise the platform does not keep (W618). // Written out literally, here and on every field below: options handed over // by reference or through a spread cannot be read, and extension mode's whole // restriction set is read out of them (E123). @Entity({ name: '__OwnerEntityName__' }) // Indexes and checks may cover only the columns declared BELOW — never one of // the owner's (E121), and the column list is written out here as a literal // array: one handed over as a const compiles, so it ships, and nothing can // check what it covers (E122). @Index('idx___owner_table_____field__', ['__field__']) export class __OwnerEntity__Extension extends SystemBaseEntity { // Your own columns, and only yours. The owner's fields are never part of // your declaration, and you cannot remove what you do not own. // nullable: true is the safe default here. The owner's table already has // rows, so a NOT NULL column needs a `default` for every one of them (E120) // — and on a large table that backfill holds a lock for the whole migration // transaction. Ship it nullable, fill it separately. @Column({ type: FieldTypeEnum.Text, nullable: true, title: { en: '__Field title__', uk: '__Назва поля__' }, }) __field__?: string; // NOT NULL is allowed only with a default — a SQL expression STRING. // @Column({ type: FieldTypeEnum.Boolean, nullable: false, default: 'false' }) // __flag__: boolean; // A relation to an entity you do not own has no class to import. The SDK // accepts NO string target — @ManyToOne is typed () => Target and calls it // at registration — so name the entity on a raw column instead. The thunk // is only consulted when relationEntityName is absent (W620). @Column({ type: RelationTypeEnum.ManyToOne, relationEntityName: '__TargetEntityName__', relationFieldName: 'id', nullable: true, title: { en: '__Target title__', uk: '__Назва звʼязку__' }, }) __target__?: { id: number }; // A relation to an entity YOU define stays an ordinary thunk: // @ManyToOne(() => __YourEntity__, { title: { en: '…' } }) // __yourRef__?: __YourEntity__; // NEVER here: primary: true (the owner's key is not yours — E119), and // @OneToMany / @ManyToManyBackRef (a reverse relation creates a field on the // OTHER entity — E118). Declare the owning side on this class instead. // Enums: prefer the entity-scoped form — pass NO global name. A global name // has no owning entity, so two applications using the same one write to the // same enum rows. // @Enum(__Field__Enum, __Field__Titles, { nullable: true }) // __choice__?: __Field__Enum; }