/** * lib/entity-columns.ts — THE definition of « which columns an index may name ». * * scaffold-entity/validate.ts owned this set alone (`knownIndexColumns`), and * it was a hard ERROR there: a typo'd `**Index**` column emitted a HasIndex over * a phantom property and failed the .NET build. That check ran SIX phases too * late — after the BA readiness GO, inside /ba-develop Phase 2. DM-023 moves it * left, and the two sides must never drift on what counts as a column: both * import this function, and the test pins the set. * * A known column is: * - a declared field (the attribute table); * - a SYNTHESISED foreign-key column — the FK named on an OWNING relation * (`*→1` / `1→1`); the skeleton never lists FK columns in the table; * - `code` on a coded entity (the engine-owned column of `**Code pattern**`); * - a data-scope ownership column (scaffold side only — the BA model has no * such notion, the scaffolder passes them explicitly); * - the technical columns the base class + tenancy add: `TenantId`, * `CreatedAt`. (`Id`, `UpdatedAt`, `DeletedAt` exist too, but an index on * them is nonsense the scaffolder never accepted — kept out on purpose.) * * Comparison is case-insensitive: the corpus writes `TenantId`, `tenantId` * and `code` alike. */ /** Technical columns every generated entity carries and an index may name. */ export const TECHNICAL_INDEX_COLUMNS: readonly string[] = ['TenantId', 'CreatedAt'] export interface KnownColumnsInput { /** Declared field / attribute names. */ fields: readonly string[] /** FK column names of the OWNING relations (`ClientId`, …). */ owningFks: readonly string[] /** Extra columns the caller synthesises (data-scope ownership columns). */ extra?: readonly string[] /** The entity carries a `**Code pattern**` → the engine adds `Code`. */ coded: boolean } /** Lower-cased set of every column an `**Index**` declaration may reference. */ export function knownIndexColumns(input: KnownColumnsInput): Set { const out = new Set() const add = (n: string): void => { const v = n.trim().toLowerCase() if (v !== '') out.add(v) } for (const f of input.fields) add(f) for (const fk of input.owningFks) add(fk) for (const x of input.extra ?? []) add(x) if (input.coded) add('code') for (const t of TECHNICAL_INDEX_COLUMNS) add(t) return out } /** True when `column` is the tenant discriminator (any casing). */ export function isTenantColumn(column: string): boolean { return column.trim().toLowerCase() === 'tenantid' }