@dforge-core/metadata 0.0.26 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -673,8 +673,8 @@ interface AccumulationConfig {
673
673
  sign?: SignConfig;
674
674
  autoCreateBalance?: boolean;
675
675
  allowNegative?: boolean;
676
- /** Document fields locked once posted. */
677
- lockedFields?: string[];
676
+ /** Document fields locked once posted. Required; `["*"]` freezes the whole row. */
677
+ lockedFields: string[];
678
678
  }
679
679
  /** One registry target within an L-column's `registries` array. */
680
680
  interface LedgerRegistryEntry {
@@ -708,6 +708,8 @@ interface EntityDef {
708
708
  viewSql?: string;
709
709
  /** Display pattern using column placeholders, e.g. "{first_name} {last_name}". */
710
710
  toString?: string;
711
+ /** Records of this entity accept comments (composer + thread on the card). */
712
+ comments?: boolean;
711
713
  /** Traits expanded at install time. */
712
714
  traits?: TraitCd[];
713
715
  /** Column definitions keyed by column code. Optional when a trait (e.g. `period`) supplies all columns. */
@@ -756,7 +758,7 @@ declare function getIdentityKeys(entity: EntityDef, entityCode: string): string[
756
758
  declare function getPrimaryKeys(entity: EntityDef, entityCode: string): string[];
757
759
 
758
760
  /** `viewType` values for `ui/data_views.json`. Source: data_views.schema.json. */
759
- type ViewType = "grid" | "list" | "kanban" | "calendar" | "gallery" | "tree-grid" | "diagram" | "master-detail" | "library" | "matrix";
761
+ type ViewType = "grid" | "list" | "kanban" | "calendar" | "gallery" | "tree-grid" | "diagram" | "library" | "matrix";
760
762
  /** Friendly picker list of the view kinds. */
761
763
  declare const dataViewKinds: readonly NamedKind[];
762
764
  /** A column in a data source: shorthand string code or a configured object. */
@@ -776,7 +778,7 @@ interface DataSource {
776
778
  entityCode: string;
777
779
  /** Nesting level (0 = root, 1+ = detail). */
778
780
  level?: number;
779
- /** Human-readable label (used in master-detail tabs). */
781
+ /** Human-readable label for this data source. */
780
782
  label?: string;
781
783
  /** Column configuration. */
782
784
  columns?: ViewColumn[];
@@ -835,7 +837,7 @@ interface DataViewDef {
835
837
  /** Bootstrap icon class (e.g. 'bi-bounding-box'). */
836
838
  icon?: string;
837
839
  description?: string;
838
- /** Data sources (at least one; master-detail needs two). */
840
+ /** Data sources (at least one). Only the first is rendered today. */
839
841
  dataSources: DataSource[];
840
842
  /** Default filter applied to all sources unless one declares its own. */
841
843
  filter?: Filter;
@@ -1064,6 +1066,8 @@ interface ManifestDef {
1064
1066
  dependencies?: Record<string, ModuleDependency>;
1065
1067
  /** Default audit history mode for this module's entities. */
1066
1068
  auditHistory?: "none" | "minimal" | "full";
1069
+ /** Default for `comments` on this module's entities; each entity may override. */
1070
+ comments?: boolean;
1067
1071
  /** Entity code → relative path of its JSON file (dotted keys = extensions). */
1068
1072
  entities?: Record<string, string>;
1069
1073
  /** Module category for display (e.g. 'Integration', 'Finance'). */
@@ -1422,6 +1426,95 @@ interface PrintTemplateDef {
1422
1426
  /** A `ui/print_templates.json` file: template_cd → definition. */
1423
1427
  type PrintTemplatesFile = Record<string, PrintTemplateDef>;
1424
1428
 
1429
+ /** A field in a card section — either a bare column code or pixel sizing with it. */
1430
+ type CardFieldEntry = string | CardFieldDef;
1431
+ interface CardFieldDef {
1432
+ column_cd: string;
1433
+ /** Field width in px. */
1434
+ width?: number;
1435
+ /** Field height in px — for multiline/textarea fields. */
1436
+ height?: number;
1437
+ }
1438
+ /** A group of fields laid out in 1, 2 or 3 columns. */
1439
+ interface ColumnGroupSection {
1440
+ type: "columnGroup";
1441
+ code: string;
1442
+ /** Section heading, in the module's authoring language. The installer gives it an
1443
+ * `entity_column_group` row keyed on this section's `code`, so
1444
+ * `translations/<locale>.json` localizes it under
1445
+ * `entities.<entity>.columnGroups.<code>.label` and the card renders that instead. */
1446
+ label?: string;
1447
+ columns: CardFieldEntry[];
1448
+ /** Field columns across the group (1-3). Defaults to the card's own setting. */
1449
+ cols?: number;
1450
+ }
1451
+ /**
1452
+ * An embedded detail grid for a 1:N set column — the master/detail building
1453
+ * block. The label comes from the set column's own metadata.
1454
+ */
1455
+ interface SetSection {
1456
+ type: "set";
1457
+ code: string;
1458
+ /** Set column code on this entity (columnType 'S'). */
1459
+ setField: string;
1460
+ }
1461
+ /** Reference to a tab item: a columnGroup section's code, or a set column's code. */
1462
+ interface CardTabRef {
1463
+ type: "section" | "set";
1464
+ code: string;
1465
+ }
1466
+ /** A tab bar whose tabs are other sections — several detail grids without one long form. */
1467
+ interface TabGroupSection {
1468
+ type: "tabGroup";
1469
+ code: string;
1470
+ tabs: CardTabRef[];
1471
+ }
1472
+ type CardSection = ColumnGroupSection | SetSection | TabGroupSection;
1473
+ /** Per-set-field renderer choice, keyed by the set column code. */
1474
+ interface CardSetConfig {
1475
+ viewType?: "grid" | "list";
1476
+ /** Opaque per-renderer options, passed verbatim to the set registration. */
1477
+ options?: Record<string, unknown>;
1478
+ }
1479
+ /** The layout body, stored verbatim into `dForge.entity_view_layout.layout`. */
1480
+ interface CardLayout {
1481
+ sections: CardSection[];
1482
+ sets?: Record<string, CardSetConfig>;
1483
+ }
1484
+ /**
1485
+ * A module-shipped card layout (value in the card_layouts map; the key is the
1486
+ * layout name).
1487
+ *
1488
+ * Rows written from here carry `module_id`, so the installer recreates them on
1489
+ * every install and removes them on uninstall. A layout the tenant drew in the
1490
+ * card editor has `module_id` NULL and is never touched.
1491
+ */
1492
+ interface CardLayoutDef {
1493
+ /**
1494
+ * Entity code the layout belongs to. Unqualified means this module owns the
1495
+ * entity; `module.entity` targets another module's (the bridge case).
1496
+ */
1497
+ entity: string;
1498
+ /**
1499
+ * Entity view the layout hangs off. Defaults to `"default"` — the same
1500
+ * fallback the runtime uses for an entity with no folder binding of its own.
1501
+ * Matched against the entity's declared views case-insensitively, and filed
1502
+ * under their spelling.
1503
+ */
1504
+ view?: string;
1505
+ /**
1506
+ * Whether this layout opens by default. Honoured on FIRST install only, and
1507
+ * only when the view has no default yet: once a tenant has chosen their own,
1508
+ * an upgrade does not take it back.
1509
+ */
1510
+ isDefault?: boolean;
1511
+ /** Human-readable note for the package author. Not stored. */
1512
+ description?: string;
1513
+ layout: CardLayout;
1514
+ }
1515
+ /** `ui/card_layouts.json` — layout name → definition. */
1516
+ type CardLayoutsFile = Record<string, CardLayoutDef>;
1517
+
1425
1518
  /** A seed-data file: records to insert into one entity. */
1426
1519
  interface SeedDataFile {
1427
1520
  /** Target entity code. */
@@ -1435,4 +1528,4 @@ declare const flagDefs: readonly {
1435
1528
  label: string;
1436
1529
  }[];
1437
1530
 
1438
- export { AGG_TYPE_LIST, type AccumulationConfig, AggType, type AlignType, type BaseDatatypeCd, type CalendarViewConfig, type ColumnDef, type ColumnTypeCd, type ColumnTypeDef, DEP_COLUMN_TYPES, type DataSource, type DataViewDef, type DataViewsFile, type Dataset, type DepColumn, type DepColumnMode, type DepColumnType, type DepEntity, type DepProvenance, type DepProvenanceKind, type DepsFile, type DeriveOptions, type DiagramViewConfig, type DomainDef, type DomainsFile, type EntityDef, type EntityEvent, type EntityReference, type EntityViewColumnDef, type EntityViewDef, FILTER_GROUP_OPERATORS, FILTER_OPERATORS, type FieldDef, type FieldLink, type FieldOption, type FieldTypeCd, type FieldTypeDef, type Filter, type FilterCondition, type FilterGroup, type FilterGroupOperator, type FilterOperator, type FlagCd, type FolderDef, type FolderEntityBinding, type FoldersFile, type JobDef, type JobsFile, type KanbanViewConfig, type LedgerRegistryEntry, type ListLevelConfig, type ListViewConfig, type ManifestAuthor, type ManifestDef, type MenuDef, type MenuItemDef, type MenuItemType, type MenusFile, type ModuleDependency, type ModuleFeature, NUMERIC_TOTAL, type NamedKind, type NumberSequenceDef, type ParamDef, type ParamDefBase, type PeriodConfig, type PrintMargins, type PrintPageSettings, type PrintTemplateDef, type PrintTemplatesFile, type ReportDef, type ReportEntityAttachment, type ReportLayout, type ReportPanel, type ReportQuery, type ReportsFile, type RoleDef, type RolesFile, type SeedDataFile, type SettingBaseDatatype, type SettingDef, type SettingsFile, type SignConfig, type SortClause, type TraitCd, type TraitDef, type TraitFieldConflict, type TraitsFile, type TreeGridViewConfig, type TriggerDef, type TriggersFile, type ViewColumn, type ViewType, type VizType, type WebhookPayload, type WebhookSubscription, type WebhooksFile, baseDatatypes, baseToDbDatatype, chartTypes, columnTypes, dataViewKinds, defaultParams, deriveBaseDatatype, deriveDbDatatype, expandTrait, expandTraits, fieldTypeCds, fieldTypes, fieldTypesByColumnType, flagDefs, getColumnType, getFieldType, getIdentityKeys, getLinks, getPrimaryKeys, getTrait, isFieldTypeCd, traitFieldConflicts, traits, vizTypes };
1531
+ export { AGG_TYPE_LIST, type AccumulationConfig, AggType, type AlignType, type BaseDatatypeCd, type CalendarViewConfig, type CardFieldDef, type CardFieldEntry, type CardLayout, type CardLayoutDef, type CardLayoutsFile, type CardSection, type CardSetConfig, type CardTabRef, type ColumnDef, type ColumnGroupSection, type ColumnTypeCd, type ColumnTypeDef, DEP_COLUMN_TYPES, type DataSource, type DataViewDef, type DataViewsFile, type Dataset, type DepColumn, type DepColumnMode, type DepColumnType, type DepEntity, type DepProvenance, type DepProvenanceKind, type DepsFile, type DeriveOptions, type DiagramViewConfig, type DomainDef, type DomainsFile, type EntityDef, type EntityEvent, type EntityReference, type EntityViewColumnDef, type EntityViewDef, FILTER_GROUP_OPERATORS, FILTER_OPERATORS, type FieldDef, type FieldLink, type FieldOption, type FieldTypeCd, type FieldTypeDef, type Filter, type FilterCondition, type FilterGroup, type FilterGroupOperator, type FilterOperator, type FlagCd, type FolderDef, type FolderEntityBinding, type FoldersFile, type JobDef, type JobsFile, type KanbanViewConfig, type LedgerRegistryEntry, type ListLevelConfig, type ListViewConfig, type ManifestAuthor, type ManifestDef, type MenuDef, type MenuItemDef, type MenuItemType, type MenusFile, type ModuleDependency, type ModuleFeature, NUMERIC_TOTAL, type NamedKind, type NumberSequenceDef, type ParamDef, type ParamDefBase, type PeriodConfig, type PrintMargins, type PrintPageSettings, type PrintTemplateDef, type PrintTemplatesFile, type ReportDef, type ReportEntityAttachment, type ReportLayout, type ReportPanel, type ReportQuery, type ReportsFile, type RoleDef, type RolesFile, type SeedDataFile, type SetSection, type SettingBaseDatatype, type SettingDef, type SettingsFile, type SignConfig, type SortClause, type TabGroupSection, type TraitCd, type TraitDef, type TraitFieldConflict, type TraitsFile, type TreeGridViewConfig, type TriggerDef, type TriggersFile, type ViewColumn, type ViewType, type VizType, type WebhookPayload, type WebhookSubscription, type WebhooksFile, baseDatatypes, baseToDbDatatype, chartTypes, columnTypes, dataViewKinds, defaultParams, deriveBaseDatatype, deriveDbDatatype, expandTrait, expandTraits, fieldTypeCds, fieldTypes, fieldTypesByColumnType, flagDefs, getColumnType, getFieldType, getIdentityKeys, getLinks, getPrimaryKeys, getTrait, isFieldTypeCd, traitFieldConflicts, traits, vizTypes };
package/dist/index.js CHANGED
@@ -366,7 +366,6 @@ var dataViewKinds = [
366
366
  { cd: "gallery", name: "Gallery" },
367
367
  { cd: "tree-grid", name: "Tree Grid" },
368
368
  { cd: "diagram", name: "Diagram" },
369
- { cd: "master-detail", name: "Master / Detail" },
370
369
  { cd: "library", name: "Library" },
371
370
  { cd: "matrix", name: "Matrix" }
372
371
  ];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/aggregation.ts","../src/field-types.ts","../src/base-datatypes.ts","../src/column-types.ts","../src/traits.ts","../src/derive.ts","../src/filter.ts","../src/entity-meta.ts","../src/data-views.ts","../src/reports.ts","../src/deps.ts","../src/index.ts"],"sourcesContent":["// Aggregation types — the kinds of aggregation a column/report value can use.\n//\n// Declared as a `const` object + derived union (not a TS `enum`) so the package\n// stays enum-free like the rest of its registries, while callers keep the\n// ergonomic `AggType.Sum` member access. `AggType.Sum === \"sum\"` at runtime, so\n// the value is interchangeable with the plain string everywhere.\n\nexport const AggType = {\n\tSum: \"sum\",\n\tCount: \"count\",\n\tAvg: \"avg\",\n\tMin: \"min\",\n\tMax: \"max\",\n\tGroup: \"group\",\n} as const;\n\n/** One of the aggregation codes (\"sum\" | \"count\" | …). */\nexport type AggType = (typeof AggType)[keyof typeof AggType];\n\n/** All aggregation types as an array (useful for iteration). */\nexport const AGG_TYPE_LIST: readonly AggType[] = Object.values(AggType);\n","// Field-type registry — mirror of\n// server/database/system-modules/metadata/seed-data/field_types.json\n//\n// This is the authoring source of truth: an author picks a `fieldTypeCd` and the\n// editor derives `baseDatatypeCd` + `dbDatatype` from the entry here. Kept in\n// sync with the server seed by the parity test in test/registries.test.ts —\n// if that test fails after a seed change, regenerate this list.\n\nimport type { FieldTypeCd, FieldTypeDef } from \"./types\";\n\n// `satisfies` (not `: readonly FieldTypeDef[]`) so the literal `cd` values are\n// preserved in the type — that powers the compile-time coverage guard below.\nexport const fieldTypes = [\n\t{ cd: \"text\", name: \"Text\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"avatar\", name: \"Avatar Name\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"email\", name: \"Email\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"phone\", name: \"Phone\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"url\", name: \"URL\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"textarea\", name: \"Multiline Text\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"number\", name: \"Number\", columnType: \"D\", baseDatatypeCd: \"number\", supportedAgg: \"SCAM\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"currency\", name: \"Currency\", columnType: \"D\", baseDatatypeCd: \"number\", supportedAgg: \"SCAM\", maxLen: null, precision: 2, useStringInFilter: false },\n\t{ cd: \"percent\", name: \"Percent\", columnType: \"D\", baseDatatypeCd: \"number\", supportedAgg: \"CAM\", maxLen: null, precision: 2, useStringInFilter: false },\n\t{ cd: \"checkbox\", name: \"Checkbox\", columnType: \"D\", baseDatatypeCd: \"bool\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"date\", name: \"Date\", columnType: \"D\", baseDatatypeCd: \"date\", supportedAgg: \"CM\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"datetime\", name: \"Date & Time\", columnType: \"D\", baseDatatypeCd: \"timestamp\", supportedAgg: \"CM\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"time\", name: \"Time\", columnType: \"D\", baseDatatypeCd: \"time\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"dropdown\", name: \"Dropdown\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"flags\", name: \"Flags\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true, defParams: { style: \"buttons\" } },\n\t{ cd: \"json\", name: \"JSON Editor\", columnType: \"D\", baseDatatypeCd: \"json\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"hidden\", name: \"Hidden\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"color\", name: \"Color Picker\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: 20, precision: null, useStringInFilter: false },\n\t{ cd: \"image\", name: \"Image\", columnType: \"D\", baseDatatypeCd: \"binary\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"file\", name: \"File\", columnType: \"D\", baseDatatypeCd: \"binary\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"markdown\", name: \"Markdown\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"richtext\", name: \"Rich Text Editor\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"lookup\", name: \"Lookup\", columnType: \"R\", baseDatatypeCd: \"guid\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"user\", name: \"User Picker\", columnType: \"D\", baseDatatypeCd: \"cuid\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"grid\", name: \"Detail Grid\", columnType: \"S\", baseDatatypeCd: \"set\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"list\", name: \"Detail List\", columnType: \"S\", baseDatatypeCd: \"set\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"tags\", name: \"Tags\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"entitylink\", name: \"Entity Link\", columnType: \"D\", baseDatatypeCd: \"json\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"code\", name: \"Code Editor\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n] as const satisfies readonly FieldTypeDef[];\n\n// Compile-time coverage guard: every `FieldTypeCd` must have a row above, and\n// every row's `cd` must be a `FieldTypeCd`. If the union and the array drift\n// apart, one of these two assignments fails to compile.\ntype _MissingFromArray = Exclude<FieldTypeCd, (typeof fieldTypes)[number][\"cd\"]>;\ntype _MissingFromUnion = Exclude<(typeof fieldTypes)[number][\"cd\"], FieldTypeCd>;\nconst _coverageOk: [_MissingFromArray, _MissingFromUnion] = [undefined as never, undefined as never];\nvoid _coverageOk;\n\nconst byCd = new Map<string, FieldTypeDef>(fieldTypes.map((ft) => [ft.cd, ft]));\n\n/** Set of all known field type codes, for membership checks. */\nexport const fieldTypeCds: ReadonlySet<string> = new Set(fieldTypes.map((ft) => ft.cd));\n\n/** Type guard: is `cd` one of the known field type codes? */\nexport function isFieldTypeCd(cd: string | undefined): cd is FieldTypeCd {\n\treturn cd != null && fieldTypeCds.has(cd);\n}\n\n/** Look up a field type by code, or `undefined` if unknown. */\nexport function getFieldType(cd: string): FieldTypeDef | undefined {\n\treturn byCd.get(cd);\n}\n\n/** Field types whose default column kind is `columnType` (e.g. \"D\" value types). */\nexport function fieldTypesByColumnType(columnType: string): readonly FieldTypeDef[] {\n\treturn fieldTypes.filter((ft) => ft.columnType === columnType);\n}\n","// Base datatype registry — mirror of\n// server/database/system-modules/metadata/seed-data/base_datatypes.json\n//\n// Plus the canonical mapping from an abstract base datatype to the default\n// PostgreSQL column type the platform generates. The visual editor uses this so\n// the author never has to type a raw SQL type.\n\nimport type { BaseDatatypeCd } from \"./types\";\n\nexport const baseDatatypes: readonly BaseDatatypeCd[] = [\n\t\"binary\",\n\t\"bool\",\n\t\"culture\",\n\t\"cuid\",\n\t\"date\",\n\t\"guid\",\n\t\"json\",\n\t\"link\",\n\t\"number\",\n\t\"project\",\n\t\"ref\",\n\t\"set\",\n\t\"string\",\n\t\"time\",\n\t\"timestamp\",\n\t\"xml\",\n] as const;\n\n// Default PostgreSQL type per base datatype. `null` entries are non-physical\n// (relationship/virtual) base types that never produce a column on their own.\nconst BASE_TO_DB: Record<BaseDatatypeCd, string | null> = {\n\tbinary: \"bytea\",\n\tbool: \"bool\",\n\tculture: \"varchar\",\n\tcuid: \"cuid\",\n\tdate: \"date\",\n\tguid: \"uuid\",\n\tjson: \"jsonb\",\n\tlink: null,\n\tnumber: \"numeric\",\n\tproject: \"varchar\",\n\tref: null,\n\tset: null,\n\tstring: \"varchar\",\n\ttime: \"time\",\n\ttimestamp: \"timestamptz\",\n\txml: \"xml\",\n};\n\n/** Default PostgreSQL type for a base datatype, or `null` if non-physical. */\nexport function baseToDbDatatype(base: string): string | null {\n\treturn (BASE_TO_DB as Record<string, string | null>)[base] ?? null;\n}\n","// Column-type registry — the seven column kinds a field can have. Letters match\n// the `column.column_type` values and entity.schema.json. The `name` /\n// `description` here drive the friendly \"what kind of field is this?\" picker.\n\nimport type { ColumnTypeDef } from \"./types\";\n\nexport const columnTypes: readonly ColumnTypeDef[] = [\n\t{ cd: \"D\", name: \"Value\", description: \"A plain data column stored on this entity.\" },\n\t{ cd: \"R\", name: \"Reference\", description: \"An N:1 lookup to another entity (foreign key).\" },\n\t{ cd: \"S\", name: \"Child list\", description: \"A 1:N collection of child records from another entity.\" },\n\t{ cd: \"F\", name: \"Formula\", description: \"A virtual value calculated from a formula expression.\" },\n\t{ cd: \"G\", name: \"Generated\", description: \"A physical column auto-maintained by the database (aggregate or expression).\" },\n\t{ cd: \"A\", name: \"Accumulation register\", description: \"Posts this document into a balance entity (single direction).\" },\n\t{ cd: \"L\", name: \"Ledger register\", description: \"Posts document lines into movements + balances (double entry).\" },\n] as const;\n\nconst byCd = new Map<string, ColumnTypeDef>(columnTypes.map((ct) => [ct.cd, ct]));\n\n/** Look up a column type by letter. */\nexport function getColumnType(cd: string): ColumnTypeDef | undefined {\n\treturn byCd.get(cd);\n}\n","// Trait registry — mirror of\n// server/database/system-modules/metadata/traits.json\n//\n// Traits are authoring shortcuts: adding one to an entity injects a fixed set of\n// fields (and sometimes references) at install time. The visual editor offers\n// these as checkboxes and previews the expanded fields, so authors never\n// hand-write `created_date` / `{entity}_id` etc.\n\nimport type { FieldDef, TraitDef, TraitsFile } from \"./types\";\n\nexport const traits: readonly TraitDef[] = [\n\t{\n\t\tcd: \"identity\",\n\t\tdescription: \"Primary Key\",\n\t\tfields: {\n\t\t\t\"{entity}_id\": { dbDatatype: \"cuid\", flags: \"I\", isPk: true, isIdentity: true, isNullable: false, orderNum: 1, description: \"{Entity} ID\" },\n\t\t},\n\t},\n\t{\n\t\tcd: \"audit\",\n\t\tdescription: \"Creation and update timestamps\",\n\t\tfields: {\n\t\t\tcreated_date: { dbDatatype: \"timestamptz\", fieldTypeCd: \"datetime\", flags: \"I\", isNullable: false, orderNum: 9901, description: \"Created Date\", params: { serverDefault: { value: \"now()\", on: \"insert\" } } },\n\t\t\tlast_updated: { dbDatatype: \"timestamptz\", fieldTypeCd: \"datetime\", flags: \"I\", isNullable: false, orderNum: 9902, description: \"Last Updated\", params: { serverDefault: { value: \"now()\", on: \"save\" } } },\n\t\t},\n\t},\n\t{\n\t\tcd: \"audit-full\",\n\t\tdescription: \"Full audit with user tracking\",\n\t\tincludes: [\"audit\"],\n\t\tfields: {\n\t\t\tcreated_by: { dbDatatype: \"cuid\", flags: \"I\", isNullable: false, orderNum: 9903, description: \"Created By User ID\", params: { serverDefault: { value: \"currentUser()\", on: \"insert\" } } },\n\t\t\tcreated_by_user: { columnType: \"R\", fieldTypeCd: \"lookup\", flags: \"I\", orderNum: 9904, description: \"Created By\", link: { entity: \"user\", thisKey: \"created_by\", otherKey: \"user_id\" } },\n\t\t\tlast_updated_by: { dbDatatype: \"cuid\", flags: \"I\", isNullable: false, orderNum: 9905, description: \"Last Updated By User ID\", params: { serverDefault: { value: \"currentUser()\", on: \"save\" } } },\n\t\t\tlast_updated_by_user: { columnType: \"R\", fieldTypeCd: \"lookup\", flags: \"I\", orderNum: 9906, description: \"Last Updated By\", link: { entity: \"user\", thisKey: \"last_updated_by\", otherKey: \"user_id\" } },\n\t\t},\n\t\treferences: {\n\t\t\t\"FK_{Entity}_CreatedBy\": { from: { field: \"created_by\" }, to: { entity: \"user\", field: \"user_id\" } },\n\t\t\t\"FK_{Entity}_LastUpdatedBy\": { from: { field: \"last_updated_by\" }, to: { entity: \"user\", field: \"user_id\" } },\n\t\t},\n\t},\n\t{\n\t\tcd: \"soft-delete\",\n\t\tdescription: \"Soft delete flag\",\n\t\tfields: {\n\t\t\tactive: { dbDatatype: \"bool\", fieldTypeCd: \"checkbox\", flags: \"\", isNullable: false, formula: \"true\", orderNum: 9910, description: \"Active\" },\n\t\t},\n\t},\n\t{\n\t\tcd: \"sorting\",\n\t\tdescription: \"Display order\",\n\t\tfields: {\n\t\t\torder_num: { dbDatatype: \"int4\", fieldTypeCd: \"number\", flags: \"VE\", orderNum: 9920, description: \"Order\" },\n\t\t},\n\t},\n\t{ cd: \"postable\", description: \"Document posting support (marker trait — actual state managed by A/L columns)\", fields: {} },\n\t{ cd: \"accumulation\", description: \"Accumulation register (marker trait — config in A-type column params)\", fields: {} },\n\t{ cd: \"ledger\", description: \"Double-entry ledger (marker trait — config in L-type column params)\", fields: {} },\n\t{\n\t\tcd: \"period\",\n\t\tdescription: \"Period entity with key format, closed flag, date range\",\n\t\tfields: {\n\t\t\tperiod_key: { dbDatatype: \"varchar\", fieldTypeCd: \"text\", flags: \"VE\", isPk: true, isNullable: false, maxLen: 20, orderNum: 1, description: \"Period Key\" },\n\t\t\tdescription: { dbDatatype: \"varchar\", fieldTypeCd: \"text\", flags: \"VE\", maxLen: 100, orderNum: 30, description: \"Description\" },\n\t\t\tclosed: { dbDatatype: \"bool\", fieldTypeCd: \"checkbox\", flags: \"VE\", isNullable: false, formula: \"false\", orderNum: 40, description: \"Closed\" },\n\t\t\tstart_date: { dbDatatype: \"date\", fieldTypeCd: \"date\", flags: \"VE\", orderNum: 50, description: \"Start Date\" },\n\t\t\tend_date: { dbDatatype: \"date\", fieldTypeCd: \"date\", flags: \"VE\", orderNum: 60, description: \"End Date\" },\n\t\t},\n\t},\n] as const;\n\nconst byCd = new Map(traits.map((t) => [t.cd, t]));\n\n/** Look up a trait by code. */\nexport function getTrait(cd: string): TraitDef | undefined {\n\treturn byCd.get(cd);\n}\n\n/** Title-case a snake_case entity code for the `{Entity}` template token. */\nfunction titleCaseEntity(entity: string): string {\n\tconst local = entity.split(\".\").pop() ?? entity;\n\treturn local\n\t\t.split(/[_\\s]+/)\n\t\t.filter(Boolean)\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\" \");\n}\n\nfunction applyTemplate(value: string, entity: string): string {\n\tconst local = entity.split(\".\").pop() ?? entity;\n\treturn value.replace(/\\{entity\\}/g, local).replace(/\\{Entity\\}/g, titleCaseEntity(entity));\n}\n\nfunction templateField(def: FieldDef, entity: string): FieldDef {\n\tconst next: FieldDef = { ...def };\n\tif (typeof next.description === \"string\") next.description = applyTemplate(next.description, entity);\n\treturn next;\n}\n\n/**\n * The merged field map. Prototype-less: a field code is author-supplied, so on a\n * plain object one named `constructor` would read back as a field already there\n * and one named `__proto__` would not write at all.\n */\nfunction emptyFields(): Record<string, FieldDef> {\n\treturn Object.create(null) as Record<string, FieldDef>;\n}\n\n/**\n * The identity the installer compares on a key collision — `TryAddField` reads\n * `ColumnType ?? DbDatatype ?? FieldTypeCd` and treats two definitions as the\n * same field only when these match.\n */\nfunction typeKeyOf(def: FieldDef): string | undefined {\n\treturn def.columnType ?? def.dbDatatype ?? def.fieldTypeCd;\n}\n\n/** Where a field already in the merge came from, for conflict wording. */\ntype FieldOrigin = \"field\" | \"trait\";\n\n/**\n * A trait contributing a field the entity already has under a DIFFERENT type.\n * The installer throws on this (`Field 'x' conflicts with trait: ...`), so a\n * module that hits one cannot install — offline readers report it instead.\n */\nexport interface TraitFieldConflict {\n\t/** Column code, after `{entity}` / `{Entity}` resolution. */\n\tfield: string;\n\t/** Whether the definition being kept is an authored field or a trait's. */\n\texistingFrom: FieldOrigin;\n\t/** Type identity of the definition being kept. */\n\texistingType?: string;\n\t/** Type identity of the trait definition being dropped. */\n\ttraitType?: string;\n}\n\ninterface MergeSink {\n\tfields: Record<string, FieldDef>;\n\torigin: Map<string, FieldOrigin>;\n\tconflicts?: TraitFieldConflict[];\n}\n\n/**\n * Merge one trait-contributed field in, the way the installer does: the FIRST\n * definition wins, and a later one of a different type is a conflict rather\n * than an overwrite. See `TraitExpander.TryAddField`.\n */\nfunction addTraitField(sink: MergeSink, key: string, def: FieldDef): void {\n\tconst existing = sink.fields[key];\n\tif (!Object.hasOwn(sink.fields, key)) {\n\t\tsink.fields[key] = def;\n\t\tsink.origin.set(key, \"trait\");\n\t\treturn;\n\t}\n\tif (sink.conflicts && !sink.conflicts.some((c) => c.field === key)) {\n\t\tconst existingType = typeKeyOf(existing);\n\t\tconst traitType = typeKeyOf(def);\n\t\tif (existingType !== traitType) {\n\t\t\tsink.conflicts.push({\n\t\t\t\tfield: key,\n\t\t\t\texistingFrom: sink.origin.get(key) ?? \"trait\",\n\t\t\t\texistingType,\n\t\t\t\ttraitType,\n\t\t\t});\n\t\t}\n\t}\n\t// Same type (or a conflict already recorded) — keep the first definition,\n\t// which is what the installer does.\n}\n\n/**\n * Expand a trait (and any traits it includes) into the concrete fields it adds\n * for `entityName`, with `{entity}` / `{Entity}` tokens resolved. Returns an\n * empty object for unknown or marker traits. Field order follows include-depth\n * first, then declaration order.\n *\n * The FIRST definition of a key wins, mirroring the installer's\n * `TraitExpander.TryAddField`. A later definition of the same key under a\n * different type is one the installer refuses to expand at all — see\n * {@link traitFieldConflicts}, which reports those without throwing.\n *\n * `localTraits` is a module's own `traits.json`. A module may declare traits of\n * its own, and an entity in it names them exactly like a platform trait — so a\n * reader that knows only this registry sees an entity missing columns that\n * install will give it. Pass the file and those traits resolve; a code declared\n * in both wins locally, mirroring the installer, which overlays the module's\n * traits on the platform ones (`TraitExpanderFactory.ForPackage`).\n */\nexport function expandTrait(\n\ttraitCd: string,\n\tentityName: string,\n\tlocalTraits?: TraitsFile,\n): Record<string, FieldDef> {\n\tconst sink: MergeSink = { fields: emptyFields(), origin: new Map() };\n\texpandTraitInner(traitCd, entityName, localTraits, new Set(), sink);\n\treturn sink.fields;\n}\n\nfunction expandTraitInner(\n\ttraitCd: string,\n\tentityName: string,\n\tlocalTraits: TraitsFile | undefined,\n\tpath: Set<string>,\n\tsink: MergeSink,\n): void {\n\t// `includes` is author-supplied once local traits are in play, so a cycle is\n\t// reachable from a module file. Unguarded that is an editor hang, not a bad\n\t// expansion.\n\t//\n\t// The guard is the current path, not every trait already visited: in a\n\t// diamond — two includes that both reach a third — the third has to expand\n\t// under each of them, or which definition wins a key collision would depend\n\t// on which branch got there first.\n\tif (path.has(traitCd)) return;\n\tpath.add(traitCd);\n\n\t// Own-property lookup: `localTraits` is parsed JSON, so a code like\n\t// `constructor` would otherwise resolve to something off Object.prototype.\n\tconst trait =\n\t\tlocalTraits && Object.hasOwn(localTraits, traitCd) ? localTraits[traitCd] : byCd.get(traitCd);\n\tif (trait) {\n\t\tfor (const included of trait.includes ?? []) {\n\t\t\texpandTraitInner(included, entityName, localTraits, path, sink);\n\t\t}\n\t\t// Each field is templated and merged on its own: two keys in one trait can\n\t\t// resolve to the same code for a given entity (`{entity}_id` and `x_id`\n\t\t// for entity `x`), and the installer, which resolves and adds one key at a\n\t\t// time, sees that collision as a conflict.\n\t\tfor (const [name, def] of Object.entries(trait.fields ?? {})) {\n\t\t\taddTraitField(sink, applyTemplate(name, entityName), templateField(def, entityName));\n\t\t}\n\t}\n\n\tpath.delete(traitCd);\n}\n\n/** Expand several traits in order into one merged field map. */\nexport function expandTraits(\n\ttraitCds: readonly string[],\n\tentityName: string,\n\tlocalTraits?: TraitsFile,\n): Record<string, FieldDef> {\n\tconst sink: MergeSink = { fields: emptyFields(), origin: new Map() };\n\tfor (const cd of traitCds) {\n\t\texpandTraitInner(cd, entityName, localTraits, new Set(), sink);\n\t}\n\treturn sink.fields;\n}\n\n/**\n * The collisions that make the installer refuse an entity: a trait contributing\n * a field the entity already has under a different type identity.\n *\n * `fields` is the entity's own authored fields. The installer seeds its merge\n * with them and expands traits into it (`TraitExpander.ExpandAll`), so an\n * authored field can conflict with a trait's exactly as two traits can — and\n * either way install fails with `Field 'x' conflicts with trait`. Omit it to\n * check the traits against each other alone.\n *\n * Returns one entry per field, in the order the conflicts are reached. An empty\n * array means the expansion the installer performs is the one\n * {@link expandTraits} returns.\n */\nexport function traitFieldConflicts(\n\ttraitCds: readonly string[],\n\tentityName: string,\n\topts: { localTraits?: TraitsFile; fields?: Record<string, FieldDef> } = {},\n): TraitFieldConflict[] {\n\tconst sink: MergeSink = { fields: emptyFields(), origin: new Map(), conflicts: [] };\n\tfor (const [key, def] of Object.entries(opts.fields ?? {})) {\n\t\tsink.fields[key] = def;\n\t\tsink.origin.set(key, \"field\");\n\t}\n\tfor (const cd of traitCds) {\n\t\texpandTraitInner(cd, entityName, opts.localTraits, new Set(), sink);\n\t}\n\treturn sink.conflicts!;\n}\n","// Derivation helpers — turn a friendly field-type choice into the physical\n// metadata the platform needs, so the author never types a raw SQL type.\n//\n// Defaults follow how core modules actually author fields (e.g. currency →\n// numeric(18,2), color → varchar(20)); callers may always override the result.\n\nimport { baseToDbDatatype } from \"./base-datatypes\";\nimport { getFieldType } from \"./field-types\";\n\n/** Total digits used when building a `numeric(total, scale)` type. */\nexport const NUMERIC_TOTAL = 18;\n\n/** Field types whose natural storage is unbounded `text` rather than `varchar`. */\nconst TEXT_BACKED = new Set([\"textarea\", \"markdown\", \"richtext\", \"code\"]);\n\n/**\n * Field types the server pins to a fixed column type regardless of their base\n * datatype — mirrors `DdlService.ResolveDbTypeFromFieldType`. `file`/`image`\n * classify as `binary`, but dForge stores them as a JSON metadata reference\n * (`{ storagePath, fileName, … }`), NOT inline `bytea`; the column is `jsonb`.\n * Without this override the base path derives `bytea`, and the file-attach\n * pipeline — which reads/writes that JSON — fails on save.\n */\nconst DB_DATATYPE_OVERRIDE: Record<string, string> = {\n\tfile: \"jsonb\",\n\timage: \"jsonb\",\n};\n\nexport interface DeriveOptions {\n\t/** Author-specified max length; overrides the field type's default. */\n\tmaxLen?: number | null;\n\t/** Author-specified numeric scale; overrides the field type's default. */\n\tprecision?: number | null;\n}\n\n/**\n * Derive the PostgreSQL `dbDatatype` for a field type. Returns `null` for\n * field types backed by relationships/sets (lookup, grid) that have no physical\n * column of their own. Unknown field types fall back to `varchar`.\n */\nexport function deriveDbDatatype(fieldTypeCd: string, options: DeriveOptions = {}): string | null {\n\tconst ft = getFieldType(fieldTypeCd);\n\tif (!ft) return \"varchar\";\n\n\t// Relationship-backed types (lookup → R, grid → S) never own a physical\n\t// column — the FK lives on a separate hidden column.\n\tif (ft.columnType === \"R\" || ft.columnType === \"S\") return null;\n\n\t// Server-pinned column types that don't follow the base-datatype path\n\t// (file/image → jsonb, not bytea). Mirrors ResolveDbTypeFromFieldType.\n\tconst override = DB_DATATYPE_OVERRIDE[ft.cd];\n\tif (override) return override;\n\n\tconst base = baseToDbDatatype(ft.baseDatatypeCd);\n\tif (base === null) return null; // other non-physical base (link/ref/set)\n\n\tif (ft.baseDatatypeCd === \"number\") {\n\t\tconst precision = options.precision ?? ft.precision;\n\t\treturn precision != null ? `numeric(${NUMERIC_TOTAL},${precision})` : \"numeric\";\n\t}\n\n\tif (ft.baseDatatypeCd === \"string\") {\n\t\tif (TEXT_BACKED.has(ft.cd)) return \"text\";\n\t\tconst maxLen = options.maxLen ?? ft.maxLen;\n\t\treturn maxLen != null ? `varchar(${maxLen})` : \"varchar\";\n\t}\n\n\treturn base;\n}\n\n/** Default `params` to seed a new field of this type with (or `undefined`). */\nexport function defaultParams(fieldTypeCd: string): Record<string, unknown> | undefined {\n\tconst ft = getFieldType(fieldTypeCd);\n\tif (!ft?.defParams) return undefined;\n\treturn { ...ft.defParams };\n}\n\n/** The base datatype code a field type maps to (for `baseDatatypeCd` on F columns). */\nexport function deriveBaseDatatype(fieldTypeCd: string): string | undefined {\n\treturn getFieldType(fieldTypeCd)?.baseDatatypeCd;\n}\n","// Canonical dForge filter expression.\n//\n// One shape shared by data views, reports, folders (row-level security) and the\n// API: either a single condition `{c, o, v}` or a group `{g, i: [...]}`. Mirror\n// of the `filter` $def repeated across data_views / reports / folders schemas.\n\n/**\n * Every operator the server's FilterBuilder translates — the closed set the\n * `o` enum in the data_views / reports / folders schemas mirrors, and the set\n * the installer validates a package's filters against.\n *\n * Symbolic forms and their word aliases are equivalent (`=`/`eq`, `!=`/`notEq`,\n * `>`/`gr`, …); a leading `!` negates, with `n`-prefixed spellings as the alias\n * (`!in`/`nIn`). Note the two spellings authors most often get wrong: null\n * checks are `null` / `!null` (not `isNull` / `isNotNull`) and substring\n * matching is `contains` (not `like`).\n */\nexport const FILTER_OPERATORS = [\n\t\"=\", \"eq\", \"!=\", \"notEq\",\n\t\">\", \"gr\", \">=\", \"grEq\", \"<\", \"less\", \"<=\", \"lessEq\",\n\t\"eqRef\",\n\t\"contains\", \"!contains\", \"nContain\",\n\t\"start\", \"!start\", \"nStart\",\n\t\"end\", \"!end\", \"nEnd\",\n\t\"mask\", \"!mask\", \"nMask\",\n\t\"null\", \"empty\", \"!null\", \"nEmpty\",\n\t\"in\", \"!in\", \"nIn\",\n\t\"between\", \"btw\", \"!between\", \"nBetween\",\n\t\"f\", \"func\",\n] as const;\n\nexport type FilterOperator = (typeof FILTER_OPERATORS)[number];\n\n/** Every group combinator the server recognizes. `!`/`n` forms negate the group. */\nexport const FILTER_GROUP_OPERATORS = [\"and\", \"or\", \"!and\", \"nAnd\", \"!or\", \"nOr\"] as const;\n\nexport type FilterGroupOperator = (typeof FILTER_GROUP_OPERATORS)[number];\n\n/** A single filter condition. */\nexport interface FilterCondition {\n\t/** Column code (supports dot navigation: 'customer.name'). */\n\tc: string;\n\t/** Operator — see {@link FILTER_OPERATORS}. */\n\to: FilterOperator;\n\t/** Comparison value: scalar, array (in/nIn) or a `@param_code` reference. */\n\tv?: unknown;\n}\n\n/** A boolean group of nested conditions/sub-groups. */\nexport interface FilterGroup {\n\t/** Group combinator. */\n\tg: FilterGroupOperator;\n\t/** Group items: nested conditions or sub-groups. */\n\ti: Filter[];\n}\n\n/** A filter is either a single condition or a group. */\nexport type Filter = FilterCondition | FilterGroup;\n\n/** A sort clause `{ c, d }` as used by reports / per-source order. */\nexport interface SortClause {\n\t/** Column code. */\n\tc: string;\n\t/** Sort direction (default: asc). */\n\td?: \"asc\" | \"desc\";\n}\n","// Entity introspection helpers — derive the projections clients commonly need\n// (primary keys, identity-trait keys, reference links) from a raw EntityDef.\n// Shared so the editor, web app and CLI compute these the same way instead of\n// each reimplementing the rules.\n\nimport type { EntityDef } from \"./entity\";\nimport { expandTrait } from \"./traits\";\n\n/** `{ field, target }` for each Reference (R) column that declares a link. */\nexport function getLinks(entity: EntityDef): Array<{ field: string; target: string }> {\n\tconst links: Array<{ field: string; target: string }> = [];\n\tfor (const [code, field] of Object.entries(entity.fields ?? {})) {\n\t\tif (field.columnType === \"R\" && field.link?.entity) {\n\t\t\tlinks.push({ field: code, target: field.link.entity });\n\t\t}\n\t}\n\treturn links;\n}\n\n/**\n * Identity-trait primary-key column codes for `entityCode` (e.g. `invoice_id`),\n * excluding any the author already declared explicitly. Empty unless the entity\n * carries the `identity` trait.\n */\nexport function getIdentityKeys(entity: EntityDef, entityCode: string): string[] {\n\tif (!entity.traits?.includes(\"identity\")) return [];\n\tconst explicit = new Set(Object.keys(entity.fields ?? {}));\n\treturn Object.keys(expandTrait(\"identity\", entityCode)).filter((key) => !explicit.has(key));\n}\n\n/**\n * All primary-key column codes: fields flagged `isPk`, plus identity-trait keys.\n * Order is explicit-first, then identity keys; duplicates removed.\n */\nexport function getPrimaryKeys(entity: EntityDef, entityCode: string): string[] {\n\tconst explicit = Object.entries(entity.fields ?? {})\n\t\t.filter(([, field]) => field.isPk)\n\t\t.map(([code]) => code);\n\tconst seen = new Set(explicit);\n\tconst result = [...explicit];\n\tfor (const key of getIdentityKeys(entity, entityCode)) {\n\t\tif (!seen.has(key)) {\n\t\t\tseen.add(key);\n\t\t\tresult.push(key);\n\t\t}\n\t}\n\treturn result;\n}\n","// Data-view registries + the authoring shape of a `ui/data_views.json` file.\n//\n// `dataViewKinds` is the friendly picker list; `DataViewDef` and friends mirror\n// the structural `data_views.schema.json` so editors/MCP/CLI can build views.\n\nimport type { Filter } from \"./filter\";\nimport type { NamedKind } from \"./types\";\n\nexport type { NamedKind };\n\n/** `viewType` values for `ui/data_views.json`. Source: data_views.schema.json. */\nexport type ViewType =\n\t| \"grid\"\n\t| \"list\"\n\t| \"kanban\"\n\t| \"calendar\"\n\t| \"gallery\"\n\t| \"tree-grid\"\n\t| \"diagram\"\n\t| \"master-detail\"\n\t| \"library\"\n\t| \"matrix\";\n\n/** Friendly picker list of the view kinds. */\nexport const dataViewKinds: readonly NamedKind[] = [\n\t{ cd: \"grid\", name: \"Grid\" },\n\t{ cd: \"list\", name: \"List\" },\n\t{ cd: \"kanban\", name: \"Kanban\" },\n\t{ cd: \"calendar\", name: \"Calendar\" },\n\t{ cd: \"gallery\", name: \"Gallery\" },\n\t{ cd: \"tree-grid\", name: \"Tree Grid\" },\n\t{ cd: \"diagram\", name: \"Diagram\" },\n\t{ cd: \"master-detail\", name: \"Master / Detail\" },\n\t{ cd: \"library\", name: \"Library\" },\n\t{ cd: \"matrix\", name: \"Matrix\" },\n] as const;\n\n/** A column in a data source: shorthand string code or a configured object. */\nexport type ViewColumn =\n\t| string\n\t| {\n\t\t\t/** Column code. */\n\t\t\tcolumn_cd: string;\n\t\t\t/** Column width in pixels. */\n\t\t\twidth?: number;\n\t\t\t/** Whether the column is visible (default true). */\n\t\t\tvisible?: boolean;\n\t\t\t/** Pin column to an edge. */\n\t\t\tpinned?: \"left\" | \"right\";\n\t };\n\n/** One data source within a view. */\nexport interface DataSource {\n\t/** Entity code to query. */\n\tentityCode: string;\n\t/** Nesting level (0 = root, 1+ = detail). */\n\tlevel?: number;\n\t/** Human-readable label (used in master-detail tabs). */\n\tlabel?: string;\n\t/** Column configuration. */\n\tcolumns?: ViewColumn[];\n\t/** Set field on the parent entity linking to this detail level. */\n\tparentSetField?: string;\n\t/** Per-source filter; overrides the view-level filter for this source. */\n\tfilter?: Filter;\n\t/** Per-source sort order (array of `{ c, d }` entries). */\n\torder?: unknown;\n}\n\nexport interface KanbanViewConfig {\n\t/** Dropdown/flags/reference column to group cards by. */\n\tgroupByField: string;\n}\n\nexport interface CalendarViewConfig {\n\t/** Date/timestamp column used to place events. */\n\tdateField: string;\n\t/** Column shown as event title (defaults to the entity toString). */\n\ttitleField?: string;\n\t/** Column whose value drives the event pill color. */\n\tcolorField?: string;\n}\n\nexport interface TreeGridViewConfig {\n\t/** Self-referencing FK column (parent record). */\n\tparentField: string;\n\t/** Node label field (defaults to entity toString). */\n\tlabelField?: string;\n\t/** Field containing a Bootstrap icon class per node. */\n\ticonField?: string;\n}\n\nexport interface ListLevelConfig {\n\t/** Entity code for this child level. */\n\tentityCode: string;\n\t/** FK column in the child entity linking back to the parent. */\n\tforeignKey: string;\n\t/** Columns to display inline for each child row. */\n\tcolumns?: string[];\n}\n\nexport interface ListViewConfig {\n\ttitleField?: string;\n\tsubtitleField?: string;\n\t/** Hierarchical child levels for expandable list rows. */\n\tlevels?: ListLevelConfig[];\n}\n\nexport interface DiagramViewConfig {\n\t/** Column to group diagram nodes by. */\n\tgroupByField?: string;\n\t/** Group values hidden by default. */\n\tdefaultHiddenGroups?: string[];\n}\n\n/** A single data view (value in the `ui/data_views.json` map). */\nexport interface DataViewDef {\n\tviewType: ViewType;\n\t/** Human-readable view name. */\n\tlabel?: string;\n\t/** Bootstrap icon class (e.g. 'bi-bounding-box'). */\n\ticon?: string;\n\tdescription?: string;\n\t/** Data sources (at least one; master-detail needs two). */\n\tdataSources: DataSource[];\n\t/** Default filter applied to all sources unless one declares its own. */\n\tfilter?: Filter;\n\t/** Default sort order. */\n\torder?: unknown;\n\t/** View-type-specific configuration. */\n\tviewConfig?:\n\t\t| KanbanViewConfig\n\t\t| CalendarViewConfig\n\t\t| TreeGridViewConfig\n\t\t| ListViewConfig\n\t\t| DiagramViewConfig\n\t\t| Record<string, unknown>;\n}\n\n/** A `ui/data_views.json` file: data view code → definition. */\nexport type DataViewsFile = Record<string, DataViewDef>;\n","// Report registries + the authoring shape of a `ui/reports.json` file.\n//\n// `vizTypes` / `chartTypes` are the friendly picker lists; `ReportDef` and\n// friends mirror reports.schema.json so editors/MCP/CLI can build reports.\n\nimport type { Filter, SortClause } from \"./filter\";\nimport type { NamedKind } from \"./types\";\n\n/** Report visualization types. Source: reports.schema.json (`vizType`). */\nexport type VizType = \"table\" | \"chart\" | \"metric\" | \"pivot\";\n\n/** Friendly picker list of report visualization types. */\nexport const vizTypes: readonly NamedKind[] = [\n\t{ cd: \"table\", name: \"Table\" },\n\t{ cd: \"chart\", name: \"Chart\" },\n\t{ cd: \"metric\", name: \"Metric\" },\n\t{ cd: \"pivot\", name: \"Pivot\" },\n] as const;\n\n/** Chart kinds used by report `config.chartType` (curated; rendered via Chart.js). */\nexport const chartTypes: readonly NamedKind[] = [\n\t{ cd: \"bar\", name: \"Bar\" },\n\t{ cd: \"line\", name: \"Line\" },\n\t{ cd: \"area\", name: \"Area\" },\n\t{ cd: \"pie\", name: \"Pie\" },\n\t{ cd: \"donut\", name: \"Donut\" },\n\t{ cd: \"scatter\", name: \"Scatter\" },\n\t{ cd: \"bubble\", name: \"Bubble\" },\n\t{ cd: \"heatmap\", name: \"Heatmap\" },\n\t{ cd: \"funnel\", name: \"Funnel\" },\n] as const;\n\n/** Everything a report parameter carries regardless of where its control comes from. */\nexport interface ParamDefBase {\n\tlabel?: string;\n\t/** Display order in the parameter form (falls back to declaration order). */\n\torderNum?: number;\n\t/**\n\t * Whether the user must provide a value. Stored inverted as `param.is_nullable`.\n\t * There is no `isRequired` spelling — the installer reads this key only.\n\t */\n\trequired?: boolean;\n\t/** Default value applied when the user doesn't provide one. */\n\tdefault?: unknown;\n\t/**\n\t * Field-type-specific params, nested under this key — there is no top-level\n\t * `link`. A lookup param needs `{ link: { entity, otherKey? } }`.\n\t * Most importantly `options` for a dropdown.\n\t * Options accept the same rich `{value,label,icon,color}` form entity columns\n\t * use; a bare string list renders the raw codes in every locale, so label them.\n\t * Omitted on a domain-backed param: the domain owns the list.\n\t */\n\tparams?: Record<string, unknown>;\n\t/**\n\t * Declares a REFERENCE parameter: the submitted value is the whole picked\n\t * record rather than its bare key. The JSON counterpart of the action DSL's\n\t * `paramCd: ref <entityCd>`, stored in the same `param.column_type`.\n\t *\n\t * Optional, and the choice is not cosmetic. Omitting it (a plain `lookup`)\n\t * submits the key, which is what a filter comparing against a reference\n\t * column needs — the server destructures a key object across every key\n\t * column and ignores non-key fields. Declare `\"R\"` when the report wants the\n\t * record itself, not just its identity.\n\t *\n\t * Either way the picker is configured by `params.link` (`{ entity, otherKey? }`);\n\t * `otherKey` is filled from the target's primary key when omitted, comma-joined\n\t * and paired POSITIONALLY with the key columns.\n\t */\n\tcolumnType?: \"R\";\n}\n\n/**\n * A report parameter definition. Declared in the report-level `parameters` block,\n * or as shorthand under the `params` of the dataset that consumes it — the\n * installer merges both into the report's single param set, so either way the\n * parameter serves the whole report. Report level wins on a code collision, and\n * is the only sensible home for a parameter several datasets use.\n *\n * A parameter takes its control from an explicit `fieldTypeCd` **or** from a\n * column `domain` — never both. Declaring both is the one rejected combination\n * (`not: { required: [\"fieldTypeCd\", \"domain\"] }` in `reports.schema.json`, and an\n * install-time check that refuses to silently pick a winner). Declaring **neither**\n * is fine: the param installs as `text`.\n *\n * Prefer `domain` whenever the choices are the same list a column uses (the usual\n * case, since the value normally ends up written to that column): the options and\n * their translations then live on the domain and are authored once, instead of\n * being restated per parameter and left to drift.\n */\nexport type ParamDef =\n\t| (ParamDefBase & {\n\t\t/** Field type code (see settings for the common list). Defaults to `text`. */\n\t\tfieldTypeCd?: string;\n\t\tdomain?: never;\n\t})\n\t| (ParamDefBase & {\n\t\t/**\n\t\t * Column domain (`domain_cd` or `module_cd.domain_cd`) supplying the control\n\t\t * and the option list. The DSL equivalent for action params is\n\t\t * `paramCd: domain <domainCd>`.\n\t\t */\n\t\tdomain: string;\n\t\tfieldTypeCd?: never;\n\t});\n\n/** SQL/entity query for a `datasetType: 'Q'` dataset. */\nexport interface ReportQuery {\n\t/** Source entity code. */\n\tentityCd: string;\n\t/** Column codes to select (supports dot navigation: 'customer.name'). */\n\tcolumns?: string[];\n\tfilter?: Filter;\n\tsort?: SortClause[];\n}\n\n/** Per-column metadata override on a dataset. */\nexport interface ColumnDef {\n\tlabel?: string;\n\tvisible?: boolean;\n\twidth?: number;\n}\n\n/** A report dataset (value in the report's `datasets` map). */\nexport interface Dataset {\n\t/** Display label (used in panel headings if no `title`). */\n\tcaption?: string;\n\t/** Q = SQL/entity query, S = stored procedure. */\n\tdatasetType: \"Q\" | \"S\";\n\t/** Used when `datasetType` is 'Q'. */\n\tquery?: ReportQuery;\n\t/** Used when `datasetType` is 'S'. */\n\tprocedureName?: string;\n\t/** Optional column metadata overrides: field code → override. */\n\tcolumnsDef?: Record<string, ColumnDef>;\n\t/**\n\t * Parameters declared on this dataset — shorthand for the report-level\n\t * `parameters` block, convenient when exactly one dataset uses the parameter.\n\t * The installer merges both into the report's one param set either way.\n\t */\n\tparams?: Record<string, ParamDef>;\n}\n\n/**\n * One (entity, report) **record-report attachment**: the report becomes openable\n * from that entity's record, with the record's values feeding its parameters.\n * Each entry becomes one `dForge.entity_report` row.\n *\n * The binding is per-attachment, not per-report — the same report can attach to\n * several entities with different parameter mappings, which is why the mapping\n * lives here rather than on the report.\n */\nexport interface ReportEntityAttachment {\n\t/**\n\t * Entity the report attaches to, qualified `module.entity` for anything\n\t * outside the declaring module. That module must be a declared dependency.\n\t */\n\tentityCd: string;\n\t/**\n\t * Parameter mapping: report param code → **source column on the entity**.\n\t * (Not a param declaration — those live in the report's `parameters` block or a\n\t * dataset's `params`.) Resolved server-side\n\t * from the record's PK. Source columns are limited to the PK, reference (`R`)\n\t * columns, and bounded scalars; source and target must be type-compatible.\n\t * Params left unmapped keep their normal behaviour (default, or prompt).\n\t */\n\tparams?: Record<string, string>;\n\t/** Sort position in the record's combined action + report menu. */\n\torderNum?: number;\n}\n\n/** A single visualization panel within the report layout. */\nexport interface ReportPanel {\n\tvizType: VizType;\n\t/** Dataset code (key in the report's `datasets` map). */\n\tdatasetCd: string;\n\ttitle?: string;\n\t/** Viz-type-specific config (chart → { chartType, categoryCol, … }). */\n\tconfig?: Record<string, unknown>;\n}\n\nexport interface ReportLayout {\n\tpanels: ReportPanel[];\n}\n\n/** A single report (value in the `ui/reports.json` map). */\nexport interface ReportDef {\n\t/** Human-readable report title. */\n\tdescription: string;\n\tlayout: ReportLayout;\n\t/** Dataset code → dataset definition. */\n\tdatasets: Record<string, Dataset>;\n\t/** Auto-refresh interval in seconds. Omit (or 0) for no auto-refresh. */\n\treloadInterval?: number;\n\t/**\n\t * Report-level parameter declarations — the canonical home for a parameter, and\n\t * the only one that fits a parameter several datasets use. Merged with each\n\t * dataset's `params` into the report's single param set; this block wins on a\n\t * code collision.\n\t */\n\tparameters?: Record<string, ParamDef>;\n\t/**\n\t * Record-report attachments: entities this report can be opened from, with the\n\t * mapping from report parameter to source column on each. Omit for a report\n\t * that is only reached from a menu.\n\t */\n\tentities?: ReportEntityAttachment[];\n}\n\n/** A `ui/reports.json` file: report code → definition. */\nexport type ReportsFile = Record<string, ReportDef>;\n","// Dependency contracts — authoring shape of `deps/<provider>.json`. Mirror of deps.schema.json.\n//\n// One file per dependency declares everything a consumer module consumes from a\n// provider: the entities, columns (each pinned to its provider storage class — a\n// Postgres type token like int8/varchar/numeric, not a base datatype), and the\n// provenance of each usage. The provider stays the source of truth; this is the\n// consumer's import list. See docs/modules/dependency-contracts.md.\n\n/**\n * Allowed storage-class tokens for a consumed column — Postgres type names with\n * length/scale stripped. Closed allowlist, kept in lockstep with the `type` enum in\n * `deps.schema.json` (and `DependencyContractValidator.ValidColumnTypes` on the C#\n * side); the `schemas.test.ts` parity guard fails if this drifts from the schema.\n * Extend all three together if a provider column legitimately uses a type not here.\n */\nexport const DEP_COLUMN_TYPES = [\n\t\"int2\", \"int4\", \"int8\", \"smallint\", \"integer\", \"bigint\",\n\t\"numeric\", \"decimal\", \"float4\", \"float8\", \"real\", \"money\",\n\t\"varchar\", \"text\", \"char\", \"bpchar\",\n\t\"bool\", \"boolean\",\n\t\"date\", \"time\", \"timetz\", \"timestamp\", \"timestamptz\",\n\t\"uuid\", \"json\", \"jsonb\", \"bytea\",\n] as const;\n\n/**\n * Storage class pinned for a consumed column — the provider's physical Postgres type\n * with length/scale stripped (e.g. `int8`, `numeric`, `varchar`). dForge's `cuid`\n * Snowflake PK normalizes to `int8`. Pinning the storage class (not a coarse base\n * type) keeps FK/lookup compatibility meaningful — an `int8` key needs `int8`, not\n * any number. One of {@link DEP_COLUMN_TYPES}.\n */\nexport type DepColumnType = (typeof DEP_COLUMN_TYPES)[number];\n\n/** Whether the consumer reads or writes a column. Default `read`. */\nexport type DepColumnMode = \"read\" | \"write\";\n\n/** Kinds of cross-module usage a provenance token can name. */\nexport type DepProvenanceKind =\n\t| \"ref\"\n\t| \"extends\"\n\t| \"formula\"\n\t| \"action\"\n\t| \"view\"\n\t| \"report\"\n\t| \"print\"\n\t| \"role\";\n\n/**\n * A usage token of the form `<kind>:<symbol>` — e.g. `ref:invoice.customer`,\n * `extends:party`, `action:check_credit`, `role:fin.admin`. Names the consuming\n * symbol so the completeness lint can reconcile declared vs actual usage. Note:\n * `display` is intentionally not a kind — the label is the provider's concern.\n */\nexport type DepProvenance = `${DepProvenanceKind}:${string}`;\n\n/** A single consumed provider column. */\nexport interface DepColumn {\n\t/** Storage class — the provider column's physical Postgres type, params stripped (`int8`, `varchar`, `numeric`, …). */\n\ttype: DepColumnType;\n\t/** Per-column provenance: which symbols read/write/join on this column. */\n\tuse: DepProvenance[];\n\t/** `write` implies an insert/update right + settable column. Default `read`; omit for reads. */\n\tmode?: DepColumnMode;\n}\n\n/** What the consumer needs from one provider entity. */\nexport interface DepEntity {\n\t/** The provider entity's PK column — the anchor every reference joins on. No label/display. */\n\tpk: string;\n\t/** Entity-level provenance: why this entity is consumed (ref/extends/role/view/report/print/action). */\n\tuse: DepProvenance[];\n\t/** Provider columns actually read/written/joined. Omit for extension-only targets. */\n\tcolumns?: Record<string, DepColumn>;\n}\n\n/** A `deps/<provider>.json` file: the consumer's contract against one dependency. */\nexport interface DepsFile {\n\t/** Code of the dependency (provider) module this contract is against (e.g. `parties`). */\n\tmodule: string;\n\t/** Semver range the consumer requires of the provider (e.g. `>=0.1.0`). */\n\tversion: string;\n\t/** Consumed provider entities, keyed by entity code. */\n\tentities: Record<string, DepEntity>;\n}\n","// @dforge-core/metadata — canonical dForge authoring registries + module-file types.\n//\n// Single source of truth for:\n// • registries — field types, base datatypes, column types, traits, data-view\n// kinds, report viz/chart types, plus helpers that derive physical metadata\n// from a friendly field-type choice;\n// • structural types — the authoring shape of every module-package file\n// (manifest, entities, data views, reports, menus, folders, roles, settings,\n// jobs, triggers, webhooks, print templates, seed data, actions), mirrored\n// from the JSON schemas under docs/schemas/.\n//\n// No runtime dependencies; safe to import from editors, the web app, the CLI,\n// the MCP server and the VS Code extension.\n\n// ── Shared core types & registries ──────────────────────────────────────────\nimport type { FlagCd } from \"./types\";\n\nexport type {\n\tAlignType,\n\tBaseDatatypeCd,\n\tColumnTypeCd,\n\tColumnTypeDef,\n\tFieldDef,\n\tFieldLink,\n\tFieldOption,\n\tFieldTypeCd,\n\tFieldTypeDef,\n\tFlagCd,\n\tNamedKind,\n\tTraitDef,\n\tTraitsFile,\n} from \"./types\";\n\nexport { AggType, AGG_TYPE_LIST } from \"./aggregation\";\n\nexport { fieldTypes, fieldTypeCds, isFieldTypeCd, getFieldType, fieldTypesByColumnType } from \"./field-types\";\nexport { baseDatatypes, baseToDbDatatype } from \"./base-datatypes\";\nexport { columnTypes, getColumnType } from \"./column-types\";\nexport { traits, getTrait, expandTrait, expandTraits, traitFieldConflicts } from \"./traits\";\nexport type { TraitFieldConflict } from \"./traits\";\nexport {\n\tderiveDbDatatype,\n\tderiveBaseDatatype,\n\tdefaultParams,\n\tNUMERIC_TOTAL,\n\ttype DeriveOptions,\n} from \"./derive\";\n\n// ── Shared filter expression (data views / reports / folders / API) ──────────\nexport type {\n\tFilter,\n\tFilterCondition,\n\tFilterGroup,\n\tFilterGroupOperator,\n\tFilterOperator,\n\tSortClause,\n} from \"./filter\";\nexport { FILTER_GROUP_OPERATORS, FILTER_OPERATORS } from \"./filter\";\n\n// ── Module-file structural types ─────────────────────────────────────────────\nexport type {\n\tEntityDef,\n\tEntityReference,\n\tEntityViewDef,\n\tEntityViewColumnDef,\n\tNumberSequenceDef,\n\tPeriodConfig,\n\tAccumulationConfig,\n\tSignConfig,\n\tLedgerRegistryEntry,\n\tTraitCd,\n} from \"./entity\";\n\n// Entity introspection helpers (primary keys, identity keys, reference links).\nexport { getLinks, getIdentityKeys, getPrimaryKeys } from \"./entity-meta\";\n\nexport {\n\tdataViewKinds,\n\ttype DataViewsFile,\n\ttype DataViewDef,\n\ttype DataSource,\n\ttype ViewColumn,\n\ttype ViewType,\n\ttype KanbanViewConfig,\n\ttype CalendarViewConfig,\n\ttype TreeGridViewConfig,\n\ttype ListViewConfig,\n\ttype ListLevelConfig,\n\ttype DiagramViewConfig,\n} from \"./data-views\";\n\nexport {\n\tvizTypes,\n\tchartTypes,\n\ttype ReportsFile,\n\ttype ReportDef,\n\ttype ReportEntityAttachment,\n\ttype ReportLayout,\n\ttype ReportPanel,\n\ttype Dataset,\n\ttype ReportQuery,\n\ttype ColumnDef,\n\ttype ParamDef,\n\ttype ParamDefBase,\n\ttype VizType,\n} from \"./reports\";\n\nexport type { ManifestDef, ManifestAuthor, ModuleDependency, ModuleFeature } from \"./manifest\";\nexport type { DepsFile, DepEntity, DepColumn, DepColumnType, DepColumnMode, DepProvenance, DepProvenanceKind } from \"./deps\";\nexport { DEP_COLUMN_TYPES } from \"./deps\";\nexport type { DomainsFile, DomainDef } from \"./domains\";\nexport type { MenusFile, MenuDef, MenuItemDef, MenuItemType } from \"./menus\";\nexport type { FoldersFile, FolderDef, FolderEntityBinding } from \"./folders\";\nexport type { RolesFile, RoleDef } from \"./roles\";\nexport type { SettingsFile, SettingDef, SettingBaseDatatype } from \"./settings\";\nexport type { JobsFile, JobDef } from \"./jobs\";\nexport type { TriggersFile, TriggerDef, EntityEvent } from \"./triggers\";\nexport type { WebhooksFile, WebhookSubscription, WebhookPayload } from \"./webhooks\";\nexport type { PrintTemplatesFile, PrintTemplateDef, PrintPageSettings, PrintMargins } from \"./print-templates\";\nexport type { SeedDataFile } from \"./seed-data\";\nexport type { ActionsFile, ActionDef, ActionExecutionMode } from \"./actions\";\n\n// Flag catalog — the only letters valid on an authored column, at field level and\n// on a per-view override alike (both `flags` patterns in entity.schema.json are\n// ^[VIEM]*$). \"M\" is a shorthand rather than a flag anyone reads: the installer\n// folds it into `isNullable` at whichever level it was written.\nexport const flagDefs: readonly { value: FlagCd; label: string }[] = [\n\t{ value: \"V\", label: \"Visible\" },\n\t{ value: \"I\", label: \"Identity\" },\n\t{ value: \"E\", label: \"Editable\" },\n\t{ value: \"M\", label: \"Mandatory\" },\n] as const;\n"],"mappings":"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","names":["byCd","byCd"]}
1
+ {"version":3,"sources":["../src/aggregation.ts","../src/field-types.ts","../src/base-datatypes.ts","../src/column-types.ts","../src/traits.ts","../src/derive.ts","../src/filter.ts","../src/entity-meta.ts","../src/data-views.ts","../src/reports.ts","../src/deps.ts","../src/index.ts"],"sourcesContent":["// Aggregation types — the kinds of aggregation a column/report value can use.\n//\n// Declared as a `const` object + derived union (not a TS `enum`) so the package\n// stays enum-free like the rest of its registries, while callers keep the\n// ergonomic `AggType.Sum` member access. `AggType.Sum === \"sum\"` at runtime, so\n// the value is interchangeable with the plain string everywhere.\n\nexport const AggType = {\n\tSum: \"sum\",\n\tCount: \"count\",\n\tAvg: \"avg\",\n\tMin: \"min\",\n\tMax: \"max\",\n\tGroup: \"group\",\n} as const;\n\n/** One of the aggregation codes (\"sum\" | \"count\" | …). */\nexport type AggType = (typeof AggType)[keyof typeof AggType];\n\n/** All aggregation types as an array (useful for iteration). */\nexport const AGG_TYPE_LIST: readonly AggType[] = Object.values(AggType);\n","// Field-type registry — mirror of\n// server/database/system-modules/metadata/seed-data/field_types.json\n//\n// This is the authoring source of truth: an author picks a `fieldTypeCd` and the\n// editor derives `baseDatatypeCd` + `dbDatatype` from the entry here. Kept in\n// sync with the server seed by the parity test in test/registries.test.ts —\n// if that test fails after a seed change, regenerate this list.\n\nimport type { FieldTypeCd, FieldTypeDef } from \"./types\";\n\n// `satisfies` (not `: readonly FieldTypeDef[]`) so the literal `cd` values are\n// preserved in the type — that powers the compile-time coverage guard below.\nexport const fieldTypes = [\n\t{ cd: \"text\", name: \"Text\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"avatar\", name: \"Avatar Name\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"email\", name: \"Email\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"phone\", name: \"Phone\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"url\", name: \"URL\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"textarea\", name: \"Multiline Text\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"number\", name: \"Number\", columnType: \"D\", baseDatatypeCd: \"number\", supportedAgg: \"SCAM\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"currency\", name: \"Currency\", columnType: \"D\", baseDatatypeCd: \"number\", supportedAgg: \"SCAM\", maxLen: null, precision: 2, useStringInFilter: false },\n\t{ cd: \"percent\", name: \"Percent\", columnType: \"D\", baseDatatypeCd: \"number\", supportedAgg: \"CAM\", maxLen: null, precision: 2, useStringInFilter: false },\n\t{ cd: \"checkbox\", name: \"Checkbox\", columnType: \"D\", baseDatatypeCd: \"bool\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"date\", name: \"Date\", columnType: \"D\", baseDatatypeCd: \"date\", supportedAgg: \"CM\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"datetime\", name: \"Date & Time\", columnType: \"D\", baseDatatypeCd: \"timestamp\", supportedAgg: \"CM\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"time\", name: \"Time\", columnType: \"D\", baseDatatypeCd: \"time\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"dropdown\", name: \"Dropdown\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"flags\", name: \"Flags\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true, defParams: { style: \"buttons\" } },\n\t{ cd: \"json\", name: \"JSON Editor\", columnType: \"D\", baseDatatypeCd: \"json\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"hidden\", name: \"Hidden\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"color\", name: \"Color Picker\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: 20, precision: null, useStringInFilter: false },\n\t{ cd: \"image\", name: \"Image\", columnType: \"D\", baseDatatypeCd: \"binary\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"file\", name: \"File\", columnType: \"D\", baseDatatypeCd: \"binary\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"markdown\", name: \"Markdown\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"richtext\", name: \"Rich Text Editor\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"lookup\", name: \"Lookup\", columnType: \"R\", baseDatatypeCd: \"guid\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"user\", name: \"User Picker\", columnType: \"D\", baseDatatypeCd: \"cuid\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"grid\", name: \"Detail Grid\", columnType: \"S\", baseDatatypeCd: \"set\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"list\", name: \"Detail List\", columnType: \"S\", baseDatatypeCd: \"set\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"tags\", name: \"Tags\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: true },\n\t{ cd: \"entitylink\", name: \"Entity Link\", columnType: \"D\", baseDatatypeCd: \"json\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n\t{ cd: \"code\", name: \"Code Editor\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: \"C\", maxLen: null, precision: null, useStringInFilter: false },\n] as const satisfies readonly FieldTypeDef[];\n\n// Compile-time coverage guard: every `FieldTypeCd` must have a row above, and\n// every row's `cd` must be a `FieldTypeCd`. If the union and the array drift\n// apart, one of these two assignments fails to compile.\ntype _MissingFromArray = Exclude<FieldTypeCd, (typeof fieldTypes)[number][\"cd\"]>;\ntype _MissingFromUnion = Exclude<(typeof fieldTypes)[number][\"cd\"], FieldTypeCd>;\nconst _coverageOk: [_MissingFromArray, _MissingFromUnion] = [undefined as never, undefined as never];\nvoid _coverageOk;\n\nconst byCd = new Map<string, FieldTypeDef>(fieldTypes.map((ft) => [ft.cd, ft]));\n\n/** Set of all known field type codes, for membership checks. */\nexport const fieldTypeCds: ReadonlySet<string> = new Set(fieldTypes.map((ft) => ft.cd));\n\n/** Type guard: is `cd` one of the known field type codes? */\nexport function isFieldTypeCd(cd: string | undefined): cd is FieldTypeCd {\n\treturn cd != null && fieldTypeCds.has(cd);\n}\n\n/** Look up a field type by code, or `undefined` if unknown. */\nexport function getFieldType(cd: string): FieldTypeDef | undefined {\n\treturn byCd.get(cd);\n}\n\n/** Field types whose default column kind is `columnType` (e.g. \"D\" value types). */\nexport function fieldTypesByColumnType(columnType: string): readonly FieldTypeDef[] {\n\treturn fieldTypes.filter((ft) => ft.columnType === columnType);\n}\n","// Base datatype registry — mirror of\n// server/database/system-modules/metadata/seed-data/base_datatypes.json\n//\n// Plus the canonical mapping from an abstract base datatype to the default\n// PostgreSQL column type the platform generates. The visual editor uses this so\n// the author never has to type a raw SQL type.\n\nimport type { BaseDatatypeCd } from \"./types\";\n\nexport const baseDatatypes: readonly BaseDatatypeCd[] = [\n\t\"binary\",\n\t\"bool\",\n\t\"culture\",\n\t\"cuid\",\n\t\"date\",\n\t\"guid\",\n\t\"json\",\n\t\"link\",\n\t\"number\",\n\t\"project\",\n\t\"ref\",\n\t\"set\",\n\t\"string\",\n\t\"time\",\n\t\"timestamp\",\n\t\"xml\",\n] as const;\n\n// Default PostgreSQL type per base datatype. `null` entries are non-physical\n// (relationship/virtual) base types that never produce a column on their own.\nconst BASE_TO_DB: Record<BaseDatatypeCd, string | null> = {\n\tbinary: \"bytea\",\n\tbool: \"bool\",\n\tculture: \"varchar\",\n\tcuid: \"cuid\",\n\tdate: \"date\",\n\tguid: \"uuid\",\n\tjson: \"jsonb\",\n\tlink: null,\n\tnumber: \"numeric\",\n\tproject: \"varchar\",\n\tref: null,\n\tset: null,\n\tstring: \"varchar\",\n\ttime: \"time\",\n\ttimestamp: \"timestamptz\",\n\txml: \"xml\",\n};\n\n/** Default PostgreSQL type for a base datatype, or `null` if non-physical. */\nexport function baseToDbDatatype(base: string): string | null {\n\treturn (BASE_TO_DB as Record<string, string | null>)[base] ?? null;\n}\n","// Column-type registry — the seven column kinds a field can have. Letters match\n// the `column.column_type` values and entity.schema.json. The `name` /\n// `description` here drive the friendly \"what kind of field is this?\" picker.\n\nimport type { ColumnTypeDef } from \"./types\";\n\nexport const columnTypes: readonly ColumnTypeDef[] = [\n\t{ cd: \"D\", name: \"Value\", description: \"A plain data column stored on this entity.\" },\n\t{ cd: \"R\", name: \"Reference\", description: \"An N:1 lookup to another entity (foreign key).\" },\n\t{ cd: \"S\", name: \"Child list\", description: \"A 1:N collection of child records from another entity.\" },\n\t{ cd: \"F\", name: \"Formula\", description: \"A virtual value calculated from a formula expression.\" },\n\t{ cd: \"G\", name: \"Generated\", description: \"A physical column auto-maintained by the database (aggregate or expression).\" },\n\t{ cd: \"A\", name: \"Accumulation register\", description: \"Posts this document into a balance entity (single direction).\" },\n\t{ cd: \"L\", name: \"Ledger register\", description: \"Posts document lines into movements + balances (double entry).\" },\n] as const;\n\nconst byCd = new Map<string, ColumnTypeDef>(columnTypes.map((ct) => [ct.cd, ct]));\n\n/** Look up a column type by letter. */\nexport function getColumnType(cd: string): ColumnTypeDef | undefined {\n\treturn byCd.get(cd);\n}\n","// Trait registry — mirror of\n// server/database/system-modules/metadata/traits.json\n//\n// Traits are authoring shortcuts: adding one to an entity injects a fixed set of\n// fields (and sometimes references) at install time. The visual editor offers\n// these as checkboxes and previews the expanded fields, so authors never\n// hand-write `created_date` / `{entity}_id` etc.\n\nimport type { FieldDef, TraitDef, TraitsFile } from \"./types\";\n\nexport const traits: readonly TraitDef[] = [\n\t{\n\t\tcd: \"identity\",\n\t\tdescription: \"Primary Key\",\n\t\tfields: {\n\t\t\t\"{entity}_id\": { dbDatatype: \"cuid\", flags: \"I\", isPk: true, isIdentity: true, isNullable: false, orderNum: 1, description: \"{Entity} ID\" },\n\t\t},\n\t},\n\t{\n\t\tcd: \"audit\",\n\t\tdescription: \"Creation and update timestamps\",\n\t\tfields: {\n\t\t\tcreated_date: { dbDatatype: \"timestamptz\", fieldTypeCd: \"datetime\", flags: \"I\", isNullable: false, orderNum: 9901, description: \"Created Date\", params: { serverDefault: { value: \"now()\", on: \"insert\" } } },\n\t\t\tlast_updated: { dbDatatype: \"timestamptz\", fieldTypeCd: \"datetime\", flags: \"I\", isNullable: false, orderNum: 9902, description: \"Last Updated\", params: { serverDefault: { value: \"now()\", on: \"save\" } } },\n\t\t},\n\t},\n\t{\n\t\tcd: \"audit-full\",\n\t\tdescription: \"Full audit with user tracking\",\n\t\tincludes: [\"audit\"],\n\t\tfields: {\n\t\t\tcreated_by: { dbDatatype: \"cuid\", flags: \"I\", isNullable: false, orderNum: 9903, description: \"Created By User ID\", params: { serverDefault: { value: \"currentUser()\", on: \"insert\" } } },\n\t\t\tcreated_by_user: { columnType: \"R\", fieldTypeCd: \"lookup\", flags: \"I\", orderNum: 9904, description: \"Created By\", link: { entity: \"user\", thisKey: \"created_by\", otherKey: \"user_id\" } },\n\t\t\tlast_updated_by: { dbDatatype: \"cuid\", flags: \"I\", isNullable: false, orderNum: 9905, description: \"Last Updated By User ID\", params: { serverDefault: { value: \"currentUser()\", on: \"save\" } } },\n\t\t\tlast_updated_by_user: { columnType: \"R\", fieldTypeCd: \"lookup\", flags: \"I\", orderNum: 9906, description: \"Last Updated By\", link: { entity: \"user\", thisKey: \"last_updated_by\", otherKey: \"user_id\" } },\n\t\t},\n\t\treferences: {\n\t\t\t\"FK_{Entity}_CreatedBy\": { from: { field: \"created_by\" }, to: { entity: \"user\", field: \"user_id\" } },\n\t\t\t\"FK_{Entity}_LastUpdatedBy\": { from: { field: \"last_updated_by\" }, to: { entity: \"user\", field: \"user_id\" } },\n\t\t},\n\t},\n\t{\n\t\tcd: \"soft-delete\",\n\t\tdescription: \"Soft delete flag\",\n\t\tfields: {\n\t\t\tactive: { dbDatatype: \"bool\", fieldTypeCd: \"checkbox\", flags: \"\", isNullable: false, formula: \"true\", orderNum: 9910, description: \"Active\" },\n\t\t},\n\t},\n\t{\n\t\tcd: \"sorting\",\n\t\tdescription: \"Display order\",\n\t\tfields: {\n\t\t\torder_num: { dbDatatype: \"int4\", fieldTypeCd: \"number\", flags: \"VE\", orderNum: 9920, description: \"Order\" },\n\t\t},\n\t},\n\t{ cd: \"postable\", description: \"Document posting support (marker trait — actual state managed by A/L columns)\", fields: {} },\n\t{ cd: \"accumulation\", description: \"Accumulation register (marker trait — config in A-type column params)\", fields: {} },\n\t{ cd: \"ledger\", description: \"Double-entry ledger (marker trait — config in L-type column params)\", fields: {} },\n\t{\n\t\tcd: \"period\",\n\t\tdescription: \"Period entity with key format, closed flag, date range\",\n\t\tfields: {\n\t\t\tperiod_key: { dbDatatype: \"varchar\", fieldTypeCd: \"text\", flags: \"VE\", isPk: true, isNullable: false, maxLen: 20, orderNum: 1, description: \"Period Key\" },\n\t\t\tdescription: { dbDatatype: \"varchar\", fieldTypeCd: \"text\", flags: \"VE\", maxLen: 100, orderNum: 30, description: \"Description\" },\n\t\t\tclosed: { dbDatatype: \"bool\", fieldTypeCd: \"checkbox\", flags: \"VE\", isNullable: false, formula: \"false\", orderNum: 40, description: \"Closed\" },\n\t\t\tstart_date: { dbDatatype: \"date\", fieldTypeCd: \"date\", flags: \"VE\", orderNum: 50, description: \"Start Date\" },\n\t\t\tend_date: { dbDatatype: \"date\", fieldTypeCd: \"date\", flags: \"VE\", orderNum: 60, description: \"End Date\" },\n\t\t},\n\t},\n] as const;\n\nconst byCd = new Map(traits.map((t) => [t.cd, t]));\n\n/** Look up a trait by code. */\nexport function getTrait(cd: string): TraitDef | undefined {\n\treturn byCd.get(cd);\n}\n\n/** Title-case a snake_case entity code for the `{Entity}` template token. */\nfunction titleCaseEntity(entity: string): string {\n\tconst local = entity.split(\".\").pop() ?? entity;\n\treturn local\n\t\t.split(/[_\\s]+/)\n\t\t.filter(Boolean)\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\" \");\n}\n\nfunction applyTemplate(value: string, entity: string): string {\n\tconst local = entity.split(\".\").pop() ?? entity;\n\treturn value.replace(/\\{entity\\}/g, local).replace(/\\{Entity\\}/g, titleCaseEntity(entity));\n}\n\nfunction templateField(def: FieldDef, entity: string): FieldDef {\n\tconst next: FieldDef = { ...def };\n\tif (typeof next.description === \"string\") next.description = applyTemplate(next.description, entity);\n\treturn next;\n}\n\n/**\n * The merged field map. Prototype-less: a field code is author-supplied, so on a\n * plain object one named `constructor` would read back as a field already there\n * and one named `__proto__` would not write at all.\n */\nfunction emptyFields(): Record<string, FieldDef> {\n\treturn Object.create(null) as Record<string, FieldDef>;\n}\n\n/**\n * The identity the installer compares on a key collision — `TryAddField` reads\n * `ColumnType ?? DbDatatype ?? FieldTypeCd` and treats two definitions as the\n * same field only when these match.\n */\nfunction typeKeyOf(def: FieldDef): string | undefined {\n\treturn def.columnType ?? def.dbDatatype ?? def.fieldTypeCd;\n}\n\n/** Where a field already in the merge came from, for conflict wording. */\ntype FieldOrigin = \"field\" | \"trait\";\n\n/**\n * A trait contributing a field the entity already has under a DIFFERENT type.\n * The installer throws on this (`Field 'x' conflicts with trait: ...`), so a\n * module that hits one cannot install — offline readers report it instead.\n */\nexport interface TraitFieldConflict {\n\t/** Column code, after `{entity}` / `{Entity}` resolution. */\n\tfield: string;\n\t/** Whether the definition being kept is an authored field or a trait's. */\n\texistingFrom: FieldOrigin;\n\t/** Type identity of the definition being kept. */\n\texistingType?: string;\n\t/** Type identity of the trait definition being dropped. */\n\ttraitType?: string;\n}\n\ninterface MergeSink {\n\tfields: Record<string, FieldDef>;\n\torigin: Map<string, FieldOrigin>;\n\tconflicts?: TraitFieldConflict[];\n}\n\n/**\n * Merge one trait-contributed field in, the way the installer does: the FIRST\n * definition wins, and a later one of a different type is a conflict rather\n * than an overwrite. See `TraitExpander.TryAddField`.\n */\nfunction addTraitField(sink: MergeSink, key: string, def: FieldDef): void {\n\tconst existing = sink.fields[key];\n\tif (!Object.hasOwn(sink.fields, key)) {\n\t\tsink.fields[key] = def;\n\t\tsink.origin.set(key, \"trait\");\n\t\treturn;\n\t}\n\tif (sink.conflicts && !sink.conflicts.some((c) => c.field === key)) {\n\t\tconst existingType = typeKeyOf(existing);\n\t\tconst traitType = typeKeyOf(def);\n\t\tif (existingType !== traitType) {\n\t\t\tsink.conflicts.push({\n\t\t\t\tfield: key,\n\t\t\t\texistingFrom: sink.origin.get(key) ?? \"trait\",\n\t\t\t\texistingType,\n\t\t\t\ttraitType,\n\t\t\t});\n\t\t}\n\t}\n\t// Same type (or a conflict already recorded) — keep the first definition,\n\t// which is what the installer does.\n}\n\n/**\n * Expand a trait (and any traits it includes) into the concrete fields it adds\n * for `entityName`, with `{entity}` / `{Entity}` tokens resolved. Returns an\n * empty object for unknown or marker traits. Field order follows include-depth\n * first, then declaration order.\n *\n * The FIRST definition of a key wins, mirroring the installer's\n * `TraitExpander.TryAddField`. A later definition of the same key under a\n * different type is one the installer refuses to expand at all — see\n * {@link traitFieldConflicts}, which reports those without throwing.\n *\n * `localTraits` is a module's own `traits.json`. A module may declare traits of\n * its own, and an entity in it names them exactly like a platform trait — so a\n * reader that knows only this registry sees an entity missing columns that\n * install will give it. Pass the file and those traits resolve; a code declared\n * in both wins locally, mirroring the installer, which overlays the module's\n * traits on the platform ones (`TraitExpanderFactory.ForPackage`).\n */\nexport function expandTrait(\n\ttraitCd: string,\n\tentityName: string,\n\tlocalTraits?: TraitsFile,\n): Record<string, FieldDef> {\n\tconst sink: MergeSink = { fields: emptyFields(), origin: new Map() };\n\texpandTraitInner(traitCd, entityName, localTraits, new Set(), sink);\n\treturn sink.fields;\n}\n\nfunction expandTraitInner(\n\ttraitCd: string,\n\tentityName: string,\n\tlocalTraits: TraitsFile | undefined,\n\tpath: Set<string>,\n\tsink: MergeSink,\n): void {\n\t// `includes` is author-supplied once local traits are in play, so a cycle is\n\t// reachable from a module file. Unguarded that is an editor hang, not a bad\n\t// expansion.\n\t//\n\t// The guard is the current path, not every trait already visited: in a\n\t// diamond — two includes that both reach a third — the third has to expand\n\t// under each of them, or which definition wins a key collision would depend\n\t// on which branch got there first.\n\tif (path.has(traitCd)) return;\n\tpath.add(traitCd);\n\n\t// Own-property lookup: `localTraits` is parsed JSON, so a code like\n\t// `constructor` would otherwise resolve to something off Object.prototype.\n\tconst trait =\n\t\tlocalTraits && Object.hasOwn(localTraits, traitCd) ? localTraits[traitCd] : byCd.get(traitCd);\n\tif (trait) {\n\t\tfor (const included of trait.includes ?? []) {\n\t\t\texpandTraitInner(included, entityName, localTraits, path, sink);\n\t\t}\n\t\t// Each field is templated and merged on its own: two keys in one trait can\n\t\t// resolve to the same code for a given entity (`{entity}_id` and `x_id`\n\t\t// for entity `x`), and the installer, which resolves and adds one key at a\n\t\t// time, sees that collision as a conflict.\n\t\tfor (const [name, def] of Object.entries(trait.fields ?? {})) {\n\t\t\taddTraitField(sink, applyTemplate(name, entityName), templateField(def, entityName));\n\t\t}\n\t}\n\n\tpath.delete(traitCd);\n}\n\n/** Expand several traits in order into one merged field map. */\nexport function expandTraits(\n\ttraitCds: readonly string[],\n\tentityName: string,\n\tlocalTraits?: TraitsFile,\n): Record<string, FieldDef> {\n\tconst sink: MergeSink = { fields: emptyFields(), origin: new Map() };\n\tfor (const cd of traitCds) {\n\t\texpandTraitInner(cd, entityName, localTraits, new Set(), sink);\n\t}\n\treturn sink.fields;\n}\n\n/**\n * The collisions that make the installer refuse an entity: a trait contributing\n * a field the entity already has under a different type identity.\n *\n * `fields` is the entity's own authored fields. The installer seeds its merge\n * with them and expands traits into it (`TraitExpander.ExpandAll`), so an\n * authored field can conflict with a trait's exactly as two traits can — and\n * either way install fails with `Field 'x' conflicts with trait`. Omit it to\n * check the traits against each other alone.\n *\n * Returns one entry per field, in the order the conflicts are reached. An empty\n * array means the expansion the installer performs is the one\n * {@link expandTraits} returns.\n */\nexport function traitFieldConflicts(\n\ttraitCds: readonly string[],\n\tentityName: string,\n\topts: { localTraits?: TraitsFile; fields?: Record<string, FieldDef> } = {},\n): TraitFieldConflict[] {\n\tconst sink: MergeSink = { fields: emptyFields(), origin: new Map(), conflicts: [] };\n\tfor (const [key, def] of Object.entries(opts.fields ?? {})) {\n\t\tsink.fields[key] = def;\n\t\tsink.origin.set(key, \"field\");\n\t}\n\tfor (const cd of traitCds) {\n\t\texpandTraitInner(cd, entityName, opts.localTraits, new Set(), sink);\n\t}\n\treturn sink.conflicts!;\n}\n","// Derivation helpers — turn a friendly field-type choice into the physical\n// metadata the platform needs, so the author never types a raw SQL type.\n//\n// Defaults follow how core modules actually author fields (e.g. currency →\n// numeric(18,2), color → varchar(20)); callers may always override the result.\n\nimport { baseToDbDatatype } from \"./base-datatypes\";\nimport { getFieldType } from \"./field-types\";\n\n/** Total digits used when building a `numeric(total, scale)` type. */\nexport const NUMERIC_TOTAL = 18;\n\n/** Field types whose natural storage is unbounded `text` rather than `varchar`. */\nconst TEXT_BACKED = new Set([\"textarea\", \"markdown\", \"richtext\", \"code\"]);\n\n/**\n * Field types the server pins to a fixed column type regardless of their base\n * datatype — mirrors `DdlService.ResolveDbTypeFromFieldType`. `file`/`image`\n * classify as `binary`, but dForge stores them as a JSON metadata reference\n * (`{ storagePath, fileName, … }`), NOT inline `bytea`; the column is `jsonb`.\n * Without this override the base path derives `bytea`, and the file-attach\n * pipeline — which reads/writes that JSON — fails on save.\n */\nconst DB_DATATYPE_OVERRIDE: Record<string, string> = {\n\tfile: \"jsonb\",\n\timage: \"jsonb\",\n};\n\nexport interface DeriveOptions {\n\t/** Author-specified max length; overrides the field type's default. */\n\tmaxLen?: number | null;\n\t/** Author-specified numeric scale; overrides the field type's default. */\n\tprecision?: number | null;\n}\n\n/**\n * Derive the PostgreSQL `dbDatatype` for a field type. Returns `null` for\n * field types backed by relationships/sets (lookup, grid) that have no physical\n * column of their own. Unknown field types fall back to `varchar`.\n */\nexport function deriveDbDatatype(fieldTypeCd: string, options: DeriveOptions = {}): string | null {\n\tconst ft = getFieldType(fieldTypeCd);\n\tif (!ft) return \"varchar\";\n\n\t// Relationship-backed types (lookup → R, grid → S) never own a physical\n\t// column — the FK lives on a separate hidden column.\n\tif (ft.columnType === \"R\" || ft.columnType === \"S\") return null;\n\n\t// Server-pinned column types that don't follow the base-datatype path\n\t// (file/image → jsonb, not bytea). Mirrors ResolveDbTypeFromFieldType.\n\tconst override = DB_DATATYPE_OVERRIDE[ft.cd];\n\tif (override) return override;\n\n\tconst base = baseToDbDatatype(ft.baseDatatypeCd);\n\tif (base === null) return null; // other non-physical base (link/ref/set)\n\n\tif (ft.baseDatatypeCd === \"number\") {\n\t\tconst precision = options.precision ?? ft.precision;\n\t\treturn precision != null ? `numeric(${NUMERIC_TOTAL},${precision})` : \"numeric\";\n\t}\n\n\tif (ft.baseDatatypeCd === \"string\") {\n\t\tif (TEXT_BACKED.has(ft.cd)) return \"text\";\n\t\tconst maxLen = options.maxLen ?? ft.maxLen;\n\t\treturn maxLen != null ? `varchar(${maxLen})` : \"varchar\";\n\t}\n\n\treturn base;\n}\n\n/** Default `params` to seed a new field of this type with (or `undefined`). */\nexport function defaultParams(fieldTypeCd: string): Record<string, unknown> | undefined {\n\tconst ft = getFieldType(fieldTypeCd);\n\tif (!ft?.defParams) return undefined;\n\treturn { ...ft.defParams };\n}\n\n/** The base datatype code a field type maps to (for `baseDatatypeCd` on F columns). */\nexport function deriveBaseDatatype(fieldTypeCd: string): string | undefined {\n\treturn getFieldType(fieldTypeCd)?.baseDatatypeCd;\n}\n","// Canonical dForge filter expression.\n//\n// One shape shared by data views, reports, folders (row-level security) and the\n// API: either a single condition `{c, o, v}` or a group `{g, i: [...]}`. Mirror\n// of the `filter` $def repeated across data_views / reports / folders schemas.\n\n/**\n * Every operator the server's FilterBuilder translates — the closed set the\n * `o` enum in the data_views / reports / folders schemas mirrors, and the set\n * the installer validates a package's filters against.\n *\n * Symbolic forms and their word aliases are equivalent (`=`/`eq`, `!=`/`notEq`,\n * `>`/`gr`, …); a leading `!` negates, with `n`-prefixed spellings as the alias\n * (`!in`/`nIn`). Note the two spellings authors most often get wrong: null\n * checks are `null` / `!null` (not `isNull` / `isNotNull`) and substring\n * matching is `contains` (not `like`).\n */\nexport const FILTER_OPERATORS = [\n\t\"=\", \"eq\", \"!=\", \"notEq\",\n\t\">\", \"gr\", \">=\", \"grEq\", \"<\", \"less\", \"<=\", \"lessEq\",\n\t\"eqRef\",\n\t\"contains\", \"!contains\", \"nContain\",\n\t\"start\", \"!start\", \"nStart\",\n\t\"end\", \"!end\", \"nEnd\",\n\t\"mask\", \"!mask\", \"nMask\",\n\t\"null\", \"empty\", \"!null\", \"nEmpty\",\n\t\"in\", \"!in\", \"nIn\",\n\t\"between\", \"btw\", \"!between\", \"nBetween\",\n\t\"f\", \"func\",\n] as const;\n\nexport type FilterOperator = (typeof FILTER_OPERATORS)[number];\n\n/** Every group combinator the server recognizes. `!`/`n` forms negate the group. */\nexport const FILTER_GROUP_OPERATORS = [\"and\", \"or\", \"!and\", \"nAnd\", \"!or\", \"nOr\"] as const;\n\nexport type FilterGroupOperator = (typeof FILTER_GROUP_OPERATORS)[number];\n\n/** A single filter condition. */\nexport interface FilterCondition {\n\t/** Column code (supports dot navigation: 'customer.name'). */\n\tc: string;\n\t/** Operator — see {@link FILTER_OPERATORS}. */\n\to: FilterOperator;\n\t/** Comparison value: scalar, array (in/nIn) or a `@param_code` reference. */\n\tv?: unknown;\n}\n\n/** A boolean group of nested conditions/sub-groups. */\nexport interface FilterGroup {\n\t/** Group combinator. */\n\tg: FilterGroupOperator;\n\t/** Group items: nested conditions or sub-groups. */\n\ti: Filter[];\n}\n\n/** A filter is either a single condition or a group. */\nexport type Filter = FilterCondition | FilterGroup;\n\n/** A sort clause `{ c, d }` as used by reports / per-source order. */\nexport interface SortClause {\n\t/** Column code. */\n\tc: string;\n\t/** Sort direction (default: asc). */\n\td?: \"asc\" | \"desc\";\n}\n","// Entity introspection helpers — derive the projections clients commonly need\n// (primary keys, identity-trait keys, reference links) from a raw EntityDef.\n// Shared so the editor, web app and CLI compute these the same way instead of\n// each reimplementing the rules.\n\nimport type { EntityDef } from \"./entity\";\nimport { expandTrait } from \"./traits\";\n\n/** `{ field, target }` for each Reference (R) column that declares a link. */\nexport function getLinks(entity: EntityDef): Array<{ field: string; target: string }> {\n\tconst links: Array<{ field: string; target: string }> = [];\n\tfor (const [code, field] of Object.entries(entity.fields ?? {})) {\n\t\tif (field.columnType === \"R\" && field.link?.entity) {\n\t\t\tlinks.push({ field: code, target: field.link.entity });\n\t\t}\n\t}\n\treturn links;\n}\n\n/**\n * Identity-trait primary-key column codes for `entityCode` (e.g. `invoice_id`),\n * excluding any the author already declared explicitly. Empty unless the entity\n * carries the `identity` trait.\n */\nexport function getIdentityKeys(entity: EntityDef, entityCode: string): string[] {\n\tif (!entity.traits?.includes(\"identity\")) return [];\n\tconst explicit = new Set(Object.keys(entity.fields ?? {}));\n\treturn Object.keys(expandTrait(\"identity\", entityCode)).filter((key) => !explicit.has(key));\n}\n\n/**\n * All primary-key column codes: fields flagged `isPk`, plus identity-trait keys.\n * Order is explicit-first, then identity keys; duplicates removed.\n */\nexport function getPrimaryKeys(entity: EntityDef, entityCode: string): string[] {\n\tconst explicit = Object.entries(entity.fields ?? {})\n\t\t.filter(([, field]) => field.isPk)\n\t\t.map(([code]) => code);\n\tconst seen = new Set(explicit);\n\tconst result = [...explicit];\n\tfor (const key of getIdentityKeys(entity, entityCode)) {\n\t\tif (!seen.has(key)) {\n\t\t\tseen.add(key);\n\t\t\tresult.push(key);\n\t\t}\n\t}\n\treturn result;\n}\n","// Data-view registries + the authoring shape of a `ui/data_views.json` file.\n//\n// `dataViewKinds` is the friendly picker list; `DataViewDef` and friends mirror\n// the structural `data_views.schema.json` so editors/MCP/CLI can build views.\n\nimport type { Filter } from \"./filter\";\nimport type { NamedKind } from \"./types\";\n\nexport type { NamedKind };\n\n/** `viewType` values for `ui/data_views.json`. Source: data_views.schema.json. */\nexport type ViewType =\n\t| \"grid\"\n\t| \"list\"\n\t| \"kanban\"\n\t| \"calendar\"\n\t| \"gallery\"\n\t| \"tree-grid\"\n\t| \"diagram\"\n\t| \"library\"\n\t| \"matrix\";\n\n/** Friendly picker list of the view kinds. */\nexport const dataViewKinds: readonly NamedKind[] = [\n\t{ cd: \"grid\", name: \"Grid\" },\n\t{ cd: \"list\", name: \"List\" },\n\t{ cd: \"kanban\", name: \"Kanban\" },\n\t{ cd: \"calendar\", name: \"Calendar\" },\n\t{ cd: \"gallery\", name: \"Gallery\" },\n\t{ cd: \"tree-grid\", name: \"Tree Grid\" },\n\t{ cd: \"diagram\", name: \"Diagram\" },\n\t{ cd: \"library\", name: \"Library\" },\n\t{ cd: \"matrix\", name: \"Matrix\" },\n] as const;\n\n/** A column in a data source: shorthand string code or a configured object. */\nexport type ViewColumn =\n\t| string\n\t| {\n\t\t\t/** Column code. */\n\t\t\tcolumn_cd: string;\n\t\t\t/** Column width in pixels. */\n\t\t\twidth?: number;\n\t\t\t/** Whether the column is visible (default true). */\n\t\t\tvisible?: boolean;\n\t\t\t/** Pin column to an edge. */\n\t\t\tpinned?: \"left\" | \"right\";\n\t };\n\n/** One data source within a view. */\nexport interface DataSource {\n\t/** Entity code to query. */\n\tentityCode: string;\n\t/** Nesting level (0 = root, 1+ = detail). */\n\tlevel?: number;\n\t/** Human-readable label for this data source. */\n\tlabel?: string;\n\t/** Column configuration. */\n\tcolumns?: ViewColumn[];\n\t/** Set field on the parent entity linking to this detail level. */\n\tparentSetField?: string;\n\t/** Per-source filter; overrides the view-level filter for this source. */\n\tfilter?: Filter;\n\t/** Per-source sort order (array of `{ c, d }` entries). */\n\torder?: unknown;\n}\n\nexport interface KanbanViewConfig {\n\t/** Dropdown/flags/reference column to group cards by. */\n\tgroupByField: string;\n}\n\nexport interface CalendarViewConfig {\n\t/** Date/timestamp column used to place events. */\n\tdateField: string;\n\t/** Column shown as event title (defaults to the entity toString). */\n\ttitleField?: string;\n\t/** Column whose value drives the event pill color. */\n\tcolorField?: string;\n}\n\nexport interface TreeGridViewConfig {\n\t/** Self-referencing FK column (parent record). */\n\tparentField: string;\n\t/** Node label field (defaults to entity toString). */\n\tlabelField?: string;\n\t/** Field containing a Bootstrap icon class per node. */\n\ticonField?: string;\n}\n\nexport interface ListLevelConfig {\n\t/** Entity code for this child level. */\n\tentityCode: string;\n\t/** FK column in the child entity linking back to the parent. */\n\tforeignKey: string;\n\t/** Columns to display inline for each child row. */\n\tcolumns?: string[];\n}\n\nexport interface ListViewConfig {\n\ttitleField?: string;\n\tsubtitleField?: string;\n\t/** Hierarchical child levels for expandable list rows. */\n\tlevels?: ListLevelConfig[];\n}\n\nexport interface DiagramViewConfig {\n\t/** Column to group diagram nodes by. */\n\tgroupByField?: string;\n\t/** Group values hidden by default. */\n\tdefaultHiddenGroups?: string[];\n}\n\n/** A single data view (value in the `ui/data_views.json` map). */\nexport interface DataViewDef {\n\tviewType: ViewType;\n\t/** Human-readable view name. */\n\tlabel?: string;\n\t/** Bootstrap icon class (e.g. 'bi-bounding-box'). */\n\ticon?: string;\n\tdescription?: string;\n\t/** Data sources (at least one). Only the first is rendered today. */\n\tdataSources: DataSource[];\n\t/** Default filter applied to all sources unless one declares its own. */\n\tfilter?: Filter;\n\t/** Default sort order. */\n\torder?: unknown;\n\t/** View-type-specific configuration. */\n\tviewConfig?:\n\t\t| KanbanViewConfig\n\t\t| CalendarViewConfig\n\t\t| TreeGridViewConfig\n\t\t| ListViewConfig\n\t\t| DiagramViewConfig\n\t\t| Record<string, unknown>;\n}\n\n/** A `ui/data_views.json` file: data view code → definition. */\nexport type DataViewsFile = Record<string, DataViewDef>;\n","// Report registries + the authoring shape of a `ui/reports.json` file.\n//\n// `vizTypes` / `chartTypes` are the friendly picker lists; `ReportDef` and\n// friends mirror reports.schema.json so editors/MCP/CLI can build reports.\n\nimport type { Filter, SortClause } from \"./filter\";\nimport type { NamedKind } from \"./types\";\n\n/** Report visualization types. Source: reports.schema.json (`vizType`). */\nexport type VizType = \"table\" | \"chart\" | \"metric\" | \"pivot\";\n\n/** Friendly picker list of report visualization types. */\nexport const vizTypes: readonly NamedKind[] = [\n\t{ cd: \"table\", name: \"Table\" },\n\t{ cd: \"chart\", name: \"Chart\" },\n\t{ cd: \"metric\", name: \"Metric\" },\n\t{ cd: \"pivot\", name: \"Pivot\" },\n] as const;\n\n/** Chart kinds used by report `config.chartType` (curated; rendered via Chart.js). */\nexport const chartTypes: readonly NamedKind[] = [\n\t{ cd: \"bar\", name: \"Bar\" },\n\t{ cd: \"line\", name: \"Line\" },\n\t{ cd: \"area\", name: \"Area\" },\n\t{ cd: \"pie\", name: \"Pie\" },\n\t{ cd: \"donut\", name: \"Donut\" },\n\t{ cd: \"scatter\", name: \"Scatter\" },\n\t{ cd: \"bubble\", name: \"Bubble\" },\n\t{ cd: \"heatmap\", name: \"Heatmap\" },\n\t{ cd: \"funnel\", name: \"Funnel\" },\n] as const;\n\n/** Everything a report parameter carries regardless of where its control comes from. */\nexport interface ParamDefBase {\n\tlabel?: string;\n\t/** Display order in the parameter form (falls back to declaration order). */\n\torderNum?: number;\n\t/**\n\t * Whether the user must provide a value. Stored inverted as `param.is_nullable`.\n\t * There is no `isRequired` spelling — the installer reads this key only.\n\t */\n\trequired?: boolean;\n\t/** Default value applied when the user doesn't provide one. */\n\tdefault?: unknown;\n\t/**\n\t * Field-type-specific params, nested under this key — there is no top-level\n\t * `link`. A lookup param needs `{ link: { entity, otherKey? } }`.\n\t * Most importantly `options` for a dropdown.\n\t * Options accept the same rich `{value,label,icon,color}` form entity columns\n\t * use; a bare string list renders the raw codes in every locale, so label them.\n\t * Omitted on a domain-backed param: the domain owns the list.\n\t */\n\tparams?: Record<string, unknown>;\n\t/**\n\t * Declares a REFERENCE parameter: the submitted value is the whole picked\n\t * record rather than its bare key. The JSON counterpart of the action DSL's\n\t * `paramCd: ref <entityCd>`, stored in the same `param.column_type`.\n\t *\n\t * Optional, and the choice is not cosmetic. Omitting it (a plain `lookup`)\n\t * submits the key, which is what a filter comparing against a reference\n\t * column needs — the server destructures a key object across every key\n\t * column and ignores non-key fields. Declare `\"R\"` when the report wants the\n\t * record itself, not just its identity.\n\t *\n\t * Either way the picker is configured by `params.link` (`{ entity, otherKey? }`);\n\t * `otherKey` is filled from the target's primary key when omitted, comma-joined\n\t * and paired POSITIONALLY with the key columns.\n\t */\n\tcolumnType?: \"R\";\n}\n\n/**\n * A report parameter definition. Declared in the report-level `parameters` block,\n * or as shorthand under the `params` of the dataset that consumes it — the\n * installer merges both into the report's single param set, so either way the\n * parameter serves the whole report. Report level wins on a code collision, and\n * is the only sensible home for a parameter several datasets use.\n *\n * A parameter takes its control from an explicit `fieldTypeCd` **or** from a\n * column `domain` — never both. Declaring both is the one rejected combination\n * (`not: { required: [\"fieldTypeCd\", \"domain\"] }` in `reports.schema.json`, and an\n * install-time check that refuses to silently pick a winner). Declaring **neither**\n * is fine: the param installs as `text`.\n *\n * Prefer `domain` whenever the choices are the same list a column uses (the usual\n * case, since the value normally ends up written to that column): the options and\n * their translations then live on the domain and are authored once, instead of\n * being restated per parameter and left to drift.\n */\nexport type ParamDef =\n\t| (ParamDefBase & {\n\t\t/** Field type code (see settings for the common list). Defaults to `text`. */\n\t\tfieldTypeCd?: string;\n\t\tdomain?: never;\n\t})\n\t| (ParamDefBase & {\n\t\t/**\n\t\t * Column domain (`domain_cd` or `module_cd.domain_cd`) supplying the control\n\t\t * and the option list. The DSL equivalent for action params is\n\t\t * `paramCd: domain <domainCd>`.\n\t\t */\n\t\tdomain: string;\n\t\tfieldTypeCd?: never;\n\t});\n\n/** SQL/entity query for a `datasetType: 'Q'` dataset. */\nexport interface ReportQuery {\n\t/** Source entity code. */\n\tentityCd: string;\n\t/** Column codes to select (supports dot navigation: 'customer.name'). */\n\tcolumns?: string[];\n\tfilter?: Filter;\n\tsort?: SortClause[];\n}\n\n/** Per-column metadata override on a dataset. */\nexport interface ColumnDef {\n\tlabel?: string;\n\tvisible?: boolean;\n\twidth?: number;\n}\n\n/** A report dataset (value in the report's `datasets` map). */\nexport interface Dataset {\n\t/** Display label (used in panel headings if no `title`). */\n\tcaption?: string;\n\t/** Q = SQL/entity query, S = stored procedure. */\n\tdatasetType: \"Q\" | \"S\";\n\t/** Used when `datasetType` is 'Q'. */\n\tquery?: ReportQuery;\n\t/** Used when `datasetType` is 'S'. */\n\tprocedureName?: string;\n\t/** Optional column metadata overrides: field code → override. */\n\tcolumnsDef?: Record<string, ColumnDef>;\n\t/**\n\t * Parameters declared on this dataset — shorthand for the report-level\n\t * `parameters` block, convenient when exactly one dataset uses the parameter.\n\t * The installer merges both into the report's one param set either way.\n\t */\n\tparams?: Record<string, ParamDef>;\n}\n\n/**\n * One (entity, report) **record-report attachment**: the report becomes openable\n * from that entity's record, with the record's values feeding its parameters.\n * Each entry becomes one `dForge.entity_report` row.\n *\n * The binding is per-attachment, not per-report — the same report can attach to\n * several entities with different parameter mappings, which is why the mapping\n * lives here rather than on the report.\n */\nexport interface ReportEntityAttachment {\n\t/**\n\t * Entity the report attaches to, qualified `module.entity` for anything\n\t * outside the declaring module. That module must be a declared dependency.\n\t */\n\tentityCd: string;\n\t/**\n\t * Parameter mapping: report param code → **source column on the entity**.\n\t * (Not a param declaration — those live in the report's `parameters` block or a\n\t * dataset's `params`.) Resolved server-side\n\t * from the record's PK. Source columns are limited to the PK, reference (`R`)\n\t * columns, and bounded scalars; source and target must be type-compatible.\n\t * Params left unmapped keep their normal behaviour (default, or prompt).\n\t */\n\tparams?: Record<string, string>;\n\t/** Sort position in the record's combined action + report menu. */\n\torderNum?: number;\n}\n\n/** A single visualization panel within the report layout. */\nexport interface ReportPanel {\n\tvizType: VizType;\n\t/** Dataset code (key in the report's `datasets` map). */\n\tdatasetCd: string;\n\ttitle?: string;\n\t/** Viz-type-specific config (chart → { chartType, categoryCol, … }). */\n\tconfig?: Record<string, unknown>;\n}\n\nexport interface ReportLayout {\n\tpanels: ReportPanel[];\n}\n\n/** A single report (value in the `ui/reports.json` map). */\nexport interface ReportDef {\n\t/** Human-readable report title. */\n\tdescription: string;\n\tlayout: ReportLayout;\n\t/** Dataset code → dataset definition. */\n\tdatasets: Record<string, Dataset>;\n\t/** Auto-refresh interval in seconds. Omit (or 0) for no auto-refresh. */\n\treloadInterval?: number;\n\t/**\n\t * Report-level parameter declarations — the canonical home for a parameter, and\n\t * the only one that fits a parameter several datasets use. Merged with each\n\t * dataset's `params` into the report's single param set; this block wins on a\n\t * code collision.\n\t */\n\tparameters?: Record<string, ParamDef>;\n\t/**\n\t * Record-report attachments: entities this report can be opened from, with the\n\t * mapping from report parameter to source column on each. Omit for a report\n\t * that is only reached from a menu.\n\t */\n\tentities?: ReportEntityAttachment[];\n}\n\n/** A `ui/reports.json` file: report code → definition. */\nexport type ReportsFile = Record<string, ReportDef>;\n","// Dependency contracts — authoring shape of `deps/<provider>.json`. Mirror of deps.schema.json.\n//\n// One file per dependency declares everything a consumer module consumes from a\n// provider: the entities, columns (each pinned to its provider storage class — a\n// Postgres type token like int8/varchar/numeric, not a base datatype), and the\n// provenance of each usage. The provider stays the source of truth; this is the\n// consumer's import list. See docs/modules/dependency-contracts.md.\n\n/**\n * Allowed storage-class tokens for a consumed column — Postgres type names with\n * length/scale stripped. Closed allowlist, kept in lockstep with the `type` enum in\n * `deps.schema.json` (and `DependencyContractValidator.ValidColumnTypes` on the C#\n * side); the `schemas.test.ts` parity guard fails if this drifts from the schema.\n * Extend all three together if a provider column legitimately uses a type not here.\n */\nexport const DEP_COLUMN_TYPES = [\n\t\"int2\", \"int4\", \"int8\", \"smallint\", \"integer\", \"bigint\",\n\t\"numeric\", \"decimal\", \"float4\", \"float8\", \"real\", \"money\",\n\t\"varchar\", \"text\", \"char\", \"bpchar\",\n\t\"bool\", \"boolean\",\n\t\"date\", \"time\", \"timetz\", \"timestamp\", \"timestamptz\",\n\t\"uuid\", \"json\", \"jsonb\", \"bytea\",\n] as const;\n\n/**\n * Storage class pinned for a consumed column — the provider's physical Postgres type\n * with length/scale stripped (e.g. `int8`, `numeric`, `varchar`). dForge's `cuid`\n * Snowflake PK normalizes to `int8`. Pinning the storage class (not a coarse base\n * type) keeps FK/lookup compatibility meaningful — an `int8` key needs `int8`, not\n * any number. One of {@link DEP_COLUMN_TYPES}.\n */\nexport type DepColumnType = (typeof DEP_COLUMN_TYPES)[number];\n\n/** Whether the consumer reads or writes a column. Default `read`. */\nexport type DepColumnMode = \"read\" | \"write\";\n\n/** Kinds of cross-module usage a provenance token can name. */\nexport type DepProvenanceKind =\n\t| \"ref\"\n\t| \"extends\"\n\t| \"formula\"\n\t| \"action\"\n\t| \"view\"\n\t| \"report\"\n\t| \"print\"\n\t| \"role\";\n\n/**\n * A usage token of the form `<kind>:<symbol>` — e.g. `ref:invoice.customer`,\n * `extends:party`, `action:check_credit`, `role:fin.admin`. Names the consuming\n * symbol so the completeness lint can reconcile declared vs actual usage. Note:\n * `display` is intentionally not a kind — the label is the provider's concern.\n */\nexport type DepProvenance = `${DepProvenanceKind}:${string}`;\n\n/** A single consumed provider column. */\nexport interface DepColumn {\n\t/** Storage class — the provider column's physical Postgres type, params stripped (`int8`, `varchar`, `numeric`, …). */\n\ttype: DepColumnType;\n\t/** Per-column provenance: which symbols read/write/join on this column. */\n\tuse: DepProvenance[];\n\t/** `write` implies an insert/update right + settable column. Default `read`; omit for reads. */\n\tmode?: DepColumnMode;\n}\n\n/** What the consumer needs from one provider entity. */\nexport interface DepEntity {\n\t/** The provider entity's PK column — the anchor every reference joins on. No label/display. */\n\tpk: string;\n\t/** Entity-level provenance: why this entity is consumed (ref/extends/role/view/report/print/action). */\n\tuse: DepProvenance[];\n\t/** Provider columns actually read/written/joined. Omit for extension-only targets. */\n\tcolumns?: Record<string, DepColumn>;\n}\n\n/** A `deps/<provider>.json` file: the consumer's contract against one dependency. */\nexport interface DepsFile {\n\t/** Code of the dependency (provider) module this contract is against (e.g. `parties`). */\n\tmodule: string;\n\t/** Semver range the consumer requires of the provider (e.g. `>=0.1.0`). */\n\tversion: string;\n\t/** Consumed provider entities, keyed by entity code. */\n\tentities: Record<string, DepEntity>;\n}\n","// @dforge-core/metadata — canonical dForge authoring registries + module-file types.\n//\n// Single source of truth for:\n// • registries — field types, base datatypes, column types, traits, data-view\n// kinds, report viz/chart types, plus helpers that derive physical metadata\n// from a friendly field-type choice;\n// • structural types — the authoring shape of every module-package file\n// (manifest, entities, data views, reports, menus, folders, roles, settings,\n// jobs, triggers, webhooks, print templates, seed data, actions), mirrored\n// from the JSON schemas under docs/schemas/.\n//\n// No runtime dependencies; safe to import from editors, the web app, the CLI,\n// the MCP server and the VS Code extension.\n\n// ── Shared core types & registries ──────────────────────────────────────────\nimport type { FlagCd } from \"./types\";\n\nexport type {\n\tAlignType,\n\tBaseDatatypeCd,\n\tColumnTypeCd,\n\tColumnTypeDef,\n\tFieldDef,\n\tFieldLink,\n\tFieldOption,\n\tFieldTypeCd,\n\tFieldTypeDef,\n\tFlagCd,\n\tNamedKind,\n\tTraitDef,\n\tTraitsFile,\n} from \"./types\";\n\nexport { AggType, AGG_TYPE_LIST } from \"./aggregation\";\n\nexport { fieldTypes, fieldTypeCds, isFieldTypeCd, getFieldType, fieldTypesByColumnType } from \"./field-types\";\nexport { baseDatatypes, baseToDbDatatype } from \"./base-datatypes\";\nexport { columnTypes, getColumnType } from \"./column-types\";\nexport { traits, getTrait, expandTrait, expandTraits, traitFieldConflicts } from \"./traits\";\nexport type { TraitFieldConflict } from \"./traits\";\nexport {\n\tderiveDbDatatype,\n\tderiveBaseDatatype,\n\tdefaultParams,\n\tNUMERIC_TOTAL,\n\ttype DeriveOptions,\n} from \"./derive\";\n\n// ── Shared filter expression (data views / reports / folders / API) ──────────\nexport type {\n\tFilter,\n\tFilterCondition,\n\tFilterGroup,\n\tFilterGroupOperator,\n\tFilterOperator,\n\tSortClause,\n} from \"./filter\";\nexport { FILTER_GROUP_OPERATORS, FILTER_OPERATORS } from \"./filter\";\n\n// ── Module-file structural types ─────────────────────────────────────────────\nexport type {\n\tEntityDef,\n\tEntityReference,\n\tEntityViewDef,\n\tEntityViewColumnDef,\n\tNumberSequenceDef,\n\tPeriodConfig,\n\tAccumulationConfig,\n\tSignConfig,\n\tLedgerRegistryEntry,\n\tTraitCd,\n} from \"./entity\";\n\n// Entity introspection helpers (primary keys, identity keys, reference links).\nexport { getLinks, getIdentityKeys, getPrimaryKeys } from \"./entity-meta\";\n\nexport {\n\tdataViewKinds,\n\ttype DataViewsFile,\n\ttype DataViewDef,\n\ttype DataSource,\n\ttype ViewColumn,\n\ttype ViewType,\n\ttype KanbanViewConfig,\n\ttype CalendarViewConfig,\n\ttype TreeGridViewConfig,\n\ttype ListViewConfig,\n\ttype ListLevelConfig,\n\ttype DiagramViewConfig,\n} from \"./data-views\";\n\nexport {\n\tvizTypes,\n\tchartTypes,\n\ttype ReportsFile,\n\ttype ReportDef,\n\ttype ReportEntityAttachment,\n\ttype ReportLayout,\n\ttype ReportPanel,\n\ttype Dataset,\n\ttype ReportQuery,\n\ttype ColumnDef,\n\ttype ParamDef,\n\ttype ParamDefBase,\n\ttype VizType,\n} from \"./reports\";\n\nexport type { ManifestDef, ManifestAuthor, ModuleDependency, ModuleFeature } from \"./manifest\";\nexport type { DepsFile, DepEntity, DepColumn, DepColumnType, DepColumnMode, DepProvenance, DepProvenanceKind } from \"./deps\";\nexport { DEP_COLUMN_TYPES } from \"./deps\";\nexport type { DomainsFile, DomainDef } from \"./domains\";\nexport type { MenusFile, MenuDef, MenuItemDef, MenuItemType } from \"./menus\";\nexport type { FoldersFile, FolderDef, FolderEntityBinding } from \"./folders\";\nexport type { RolesFile, RoleDef } from \"./roles\";\nexport type { SettingsFile, SettingDef, SettingBaseDatatype } from \"./settings\";\nexport type { JobsFile, JobDef } from \"./jobs\";\nexport type { TriggersFile, TriggerDef, EntityEvent } from \"./triggers\";\nexport type { WebhooksFile, WebhookSubscription, WebhookPayload } from \"./webhooks\";\nexport type { PrintTemplatesFile, PrintTemplateDef, PrintPageSettings, PrintMargins } from \"./print-templates\";\nexport type {\n\tCardLayoutsFile,\n\tCardLayoutDef,\n\tCardLayout,\n\tCardSection,\n\tCardSetConfig,\n\tCardTabRef,\n\tCardFieldEntry,\n\tCardFieldDef,\n\tColumnGroupSection,\n\tSetSection,\n\tTabGroupSection,\n} from \"./card-layouts\";\nexport type { SeedDataFile } from \"./seed-data\";\nexport type { ActionsFile, ActionDef, ActionExecutionMode } from \"./actions\";\n\n// Flag catalog — the only letters valid on an authored column, at field level and\n// on a per-view override alike (both `flags` patterns in entity.schema.json are\n// ^[VIEM]*$). \"M\" is a shorthand rather than a flag anyone reads: the installer\n// folds it into `isNullable` at whichever level it was written.\nexport const flagDefs: readonly { value: FlagCd; label: string }[] = [\n\t{ value: \"V\", label: \"Visible\" },\n\t{ value: \"I\", label: \"Identity\" },\n\t{ value: \"E\", label: \"Editable\" },\n\t{ value: \"M\", label: \"Mandatory\" },\n] as const;\n"],"mappings":"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","names":["byCd","byCd"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dforge-core/metadata",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "description": "Canonical dForge authoring registries — field types, base datatypes, column types, traits, data-view kinds and chart types — with derivation helpers. Runtime-dependency-free; the single source of truth shared by editors, the web app and the CLI.",
5
5
  "license": "MIT",
6
6
  "author": "dForge",
@@ -0,0 +1,141 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "dForge Module Card Layouts",
4
+ "description": "Module-shipped card (form) layouts for a dForge module package (ui/card_layouts.json). Each key is the layout_name stored in dForge.entity_view_layout. Rows written from here carry module_id, so they are recreated on every install and removed on uninstall; a layout the tenant drew in the card editor has module_id NULL and is never touched.",
5
+ "type": "object",
6
+ "additionalProperties": {
7
+ "type": "object",
8
+ "properties": {
9
+ "entity": {
10
+ "type": "string",
11
+ "description": "Entity code the layout belongs to. Unqualified means this module owns the entity; the cross-module form 'module.entity' targets another module's entity (a bridge module shipping a layout for its host)."
12
+ },
13
+ "view": {
14
+ "type": "string",
15
+ "default": "default",
16
+ "description": "Entity view the layout hangs off, matching an entity_view row's view_name. Defaults to 'default' — the same fallback entity.getLayouts and entity.saveLayout use for an entity with no folder binding of its own. Matched against the entity's declared views case-insensitively, and filed under their spelling; the installer creates the view row only when nothing matches."
17
+ },
18
+ "isDefault": {
19
+ "type": "boolean",
20
+ "default": false,
21
+ "description": "Whether this layout opens by default. Honoured on FIRST install only, and only when the view has no default layout yet — once a tenant has chosen their own default, an upgrade does not take it back."
22
+ },
23
+ "description": {
24
+ "type": "string",
25
+ "description": "Human-readable note for the package author. Not stored."
26
+ },
27
+ "layout": {
28
+ "$ref": "#/$defs/cardLayout",
29
+ "description": "The layout body, stored verbatim into entity_view_layout.layout. Same shape the card editor saves."
30
+ }
31
+ },
32
+ "required": ["entity", "layout"],
33
+ "additionalProperties": false
34
+ },
35
+ "$defs": {
36
+ "cardLayout": {
37
+ "type": "object",
38
+ "properties": {
39
+ "sections": {
40
+ "type": "array",
41
+ "description": "Ordered sections rendered down the card.",
42
+ "items": { "$ref": "#/$defs/section" }
43
+ },
44
+ "sets": {
45
+ "type": "object",
46
+ "description": "Per-set-field view config, keyed by the set column code. Chooses the renderer a detail grid uses ('grid' or 'list') and its persisted options.",
47
+ "additionalProperties": {
48
+ "type": "object",
49
+ "properties": {
50
+ "viewType": {
51
+ "type": "string",
52
+ "enum": ["grid", "list"],
53
+ "description": "Set renderer. Defaults to the column's own metadata."
54
+ },
55
+ "options": {
56
+ "type": "object",
57
+ "description": "Opaque per-renderer options, passed verbatim to the set registration."
58
+ }
59
+ },
60
+ "additionalProperties": false
61
+ }
62
+ }
63
+ },
64
+ "required": ["sections"],
65
+ "additionalProperties": false
66
+ },
67
+ "section": {
68
+ "oneOf": [
69
+ { "$ref": "#/$defs/columnGroupSection" },
70
+ { "$ref": "#/$defs/setSection" },
71
+ { "$ref": "#/$defs/tabGroupSection" }
72
+ ]
73
+ },
74
+ "columnGroupSection": {
75
+ "type": "object",
76
+ "description": "A group of fields laid out in 1, 2 or 3 columns.",
77
+ "properties": {
78
+ "type": { "const": "columnGroup" },
79
+ "code": { "type": "string", "description": "Section code, unique within the layout." },
80
+ "label": { "type": "string", "description": "Section heading, in the module's authoring language. The installer gives it an entity_column_group row keyed on this section's code, so translations/<locale>.json localizes it under entities.<entity>.columnGroups.<code>.label and the card shows the localized heading." },
81
+ "cols": { "type": "integer", "minimum": 1, "maximum": 3, "description": "Field columns across the group. Defaults to the card's own setting." },
82
+ "columns": {
83
+ "type": "array",
84
+ "description": "Fields in the group, in render order. A bare string is the column code; the object form adds pixel sizing.",
85
+ "items": {
86
+ "oneOf": [
87
+ { "type": "string" },
88
+ {
89
+ "type": "object",
90
+ "properties": {
91
+ "column_cd": { "type": "string", "description": "Column code on this entity." },
92
+ "width": { "type": "integer", "minimum": 1, "description": "Field width in px." },
93
+ "height": { "type": "integer", "minimum": 1, "description": "Field height in px — for multiline/textarea fields." }
94
+ },
95
+ "required": ["column_cd"],
96
+ "additionalProperties": false
97
+ }
98
+ ]
99
+ }
100
+ }
101
+ },
102
+ "required": ["type", "code", "columns"],
103
+ "additionalProperties": false
104
+ },
105
+ "setSection": {
106
+ "type": "object",
107
+ "description": "An embedded detail grid for a 1:N set column — the master/detail building block. The label comes from the column's own metadata.",
108
+ "properties": {
109
+ "type": { "const": "set" },
110
+ "code": { "type": "string", "description": "Section code, unique within the layout." },
111
+ "setField": { "type": "string", "description": "Set column code on this entity (columnType 'S')." }
112
+ },
113
+ "required": ["type", "code", "setField"],
114
+ "additionalProperties": false
115
+ },
116
+ "tabGroupSection": {
117
+ "type": "object",
118
+ "description": "A tab bar whose tabs are other sections. This is how a record shows several detail grids without stacking them down one long form.",
119
+ "properties": {
120
+ "type": { "const": "tabGroup" },
121
+ "code": { "type": "string", "description": "Section code, unique within the layout." },
122
+ "tabs": {
123
+ "type": "array",
124
+ "minItems": 1,
125
+ "description": "Tabs in order. Each references a columnGroup section by its code, or a set column by its column code.",
126
+ "items": {
127
+ "type": "object",
128
+ "properties": {
129
+ "type": { "type": "string", "enum": ["section", "set"] },
130
+ "code": { "type": "string", "description": "The referenced section's code, or the set column's code." }
131
+ },
132
+ "required": ["type", "code"],
133
+ "additionalProperties": false
134
+ }
135
+ }
136
+ },
137
+ "required": ["type", "code", "tabs"],
138
+ "additionalProperties": false
139
+ }
140
+ }
141
+ }
@@ -14,7 +14,7 @@
14
14
  "properties": {
15
15
  "viewType": {
16
16
  "type": "string",
17
- "enum": ["grid", "list", "kanban", "calendar", "gallery", "tree-grid", "diagram", "master-detail", "library", "matrix"],
17
+ "enum": ["grid", "list", "kanban", "calendar", "gallery", "tree-grid", "diagram", "library", "matrix"],
18
18
  "default": "grid",
19
19
  "description": "View display type. Optional — omitted or unknown values fall back to 'grid' at runtime."
20
20
  },
@@ -57,7 +57,6 @@
57
57
  { "$ref": "#/$defs/rules/tree-grid" },
58
58
  { "$ref": "#/$defs/rules/list" },
59
59
  { "$ref": "#/$defs/rules/diagram" },
60
- { "$ref": "#/$defs/rules/master-detail" },
61
60
  { "$ref": "#/$defs/rules/matrix" }
62
61
  ]
63
62
  },
@@ -118,17 +117,6 @@
118
117
  }
119
118
  }
120
119
  },
121
- "master-detail": {
122
- "if": {
123
- "properties": { "viewType": { "const": "master-detail" } },
124
- "required": ["viewType"]
125
- },
126
- "then": {
127
- "properties": {
128
- "dataSources": { "minItems": 2 }
129
- }
130
- }
131
- },
132
120
  "matrix": {
133
121
  "if": {
134
122
  "properties": { "viewType": { "const": "matrix" } },
@@ -493,7 +481,7 @@
493
481
  },
494
482
  "label": {
495
483
  "type": "string",
496
- "description": "Human-readable label for this data source (used in master-detail tabs)"
484
+ "description": "Human-readable label for this data source"
497
485
  },
498
486
  "columns": {
499
487
  "type": "array",
@@ -38,6 +38,10 @@
38
38
  "toString": {
39
39
  "description": "Display pattern using column placeholders, e.g. \"{first_name} {last_name}\". Optional in extension entities (with `extends`). No `type` constraint here because `toString` collides with Object.prototype.toString in JSON validators — they read the inherited function and reject it, even when the property is absent from the JSON."
40
40
  },
41
+ "comments": {
42
+ "type": "boolean",
43
+ "description": "Records of this entity accept comments \u2014 the composer and thread appear on the record card, beside the audit history. Opt-in per entity; `comments` in the manifest sets a module-wide default that this overrides in both directions."
44
+ },
41
45
  "traits": {
42
46
  "type": "array",
43
47
  "description": "Entity traits expanded during module install (authoring shortcut). Trait definitions loaded from DB (seeded by metadata module).",
@@ -221,9 +225,48 @@
221
225
  },
222
226
  "additionalProperties": false
223
227
  },
228
+ "registryColumnParams": {
229
+ "type": "object",
230
+ "description": "params of an accumulation (A) or ledger (L) registry column. The lock lists are what BOTH generated guards freeze — the posted-document guard while the document is posted, the period guard while its period is closed — and they are required: install refuses a registry column that omits or empties them. The header's periodDateField and the state column are watched in addition, whatever the list says.",
231
+ "required": ["lockedFields"],
232
+ "properties": {
233
+ "lockedFields": {
234
+ "type": "array",
235
+ "minItems": 1,
236
+ "items": { "type": "string", "minLength": 1 },
237
+ "description": "Header fields frozen while the document is posted and while its period is closed. Required. [\"*\"] freezes the whole row."
238
+ },
239
+ "lockedLineFields": {
240
+ "type": "array",
241
+ "minItems": 1,
242
+ "items": { "type": "string", "minLength": 1 },
243
+ "description": "Line fields frozen under a posted or period-closed header. Required when lineEntity is declared, rejected when it is not. [\"*\"] freezes the whole row; the line's foreign key to the header is watched in addition."
244
+ },
245
+ "lineEntity": { "type": "string", "description": "The line entity, defined in this module. Declare together with lineField and lockedLineFields, or none of the three for a document without lines." },
246
+ "lineField": { "type": "string", "description": "The line's foreign-key column(s) to the header, comma-separated in the header's key order for a composite key." },
247
+ "stateField": { "type": "string", "description": "The bool column that records the posted state, when it is not the registry column itself. Written only by the register engine." },
248
+ "periodEntity": { "type": "string", "description": "Period entity this registry closes against — one of this module's, or 'module.entity' from a dependency." },
249
+ "periodDateField": { "type": "string", "description": "Header date column that resolves the period." },
250
+ "periodKeyField": { "type": "string", "default": "period_key" },
251
+ "periodClosedField": { "type": "string", "default": "closed" }
252
+ },
253
+ "dependencies": {
254
+ "lineEntity": ["lineField", "lockedLineFields"],
255
+ "lineField": ["lineEntity"],
256
+ "lockedLineFields": ["lineEntity"],
257
+ "periodEntity": ["periodDateField"]
258
+ },
259
+ "additionalProperties": true
260
+ },
224
261
  "field": {
225
262
  "type": "object",
226
263
  "description": "Entity column/field definition",
264
+ "allOf": [
265
+ {
266
+ "if": { "required": ["columnType"], "properties": { "columnType": { "enum": ["A", "L"] } } },
267
+ "then": { "required": ["params"], "properties": { "params": { "$ref": "#/$defs/registryColumnParams" } } }
268
+ }
269
+ ],
227
270
  "properties": {
228
271
  "dbDatatype": {
229
272
  "type": "string",
@@ -551,8 +594,9 @@
551
594
  "sign": { "$ref": "#/$defs/signConfig", "description": "Dynamic sign based on a field value (alternative to direction)" },
552
595
  "autoCreateBalance": { "type": "boolean", "default": true },
553
596
  "allowNegative": { "type": "boolean", "default": true },
554
- "lockedFields": { "type": "array", "items": { "type": "string" }, "description": "Document fields locked when posted" }
597
+ "lockedFields": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "description": "Document fields locked when posted. Required; [\"*\"] freezes the whole row." }
555
598
  },
599
+ "required": ["lockedFields"],
556
600
  "additionalProperties": false
557
601
  },
558
602
  "signConfig": {
@@ -107,6 +107,10 @@
107
107
  "enum": ["none", "minimal", "full"],
108
108
  "description": "Default audit history mode for entities in this module ('full' captures every field change; 'minimal' captures only inserts/deletes; 'none' disables). Individual entities can override."
109
109
  },
110
+ "comments": {
111
+ "type": "boolean",
112
+ "description": "Default for `comments` on every entity in this module. Individual entities override it, including overriding true with false."
113
+ },
110
114
  "entities": {
111
115
  "type": "object",
112
116
  "description": "Entity definitions and extensions: entity code → relative path to entity JSON file. Dotted keys (e.g. 'fin.invoice') indicate extensions of other modules' entities (the entity file must have an 'extends' property).",
@@ -0,0 +1,103 @@
1
+ // Card layouts — authoring shape of `ui/card_layouts.json`.
2
+ // Mirror of card_layouts.schema.json, and of the runtime CardLayout in
3
+ // @dforge/data's layouts.ts — the installer stores the body verbatim, so the
4
+ // two must not drift.
5
+
6
+ /** A field in a card section — either a bare column code or pixel sizing with it. */
7
+ export type CardFieldEntry = string | CardFieldDef;
8
+
9
+ export interface CardFieldDef {
10
+ column_cd: string;
11
+ /** Field width in px. */
12
+ width?: number;
13
+ /** Field height in px — for multiline/textarea fields. */
14
+ height?: number;
15
+ }
16
+
17
+ /** A group of fields laid out in 1, 2 or 3 columns. */
18
+ export interface ColumnGroupSection {
19
+ type: "columnGroup";
20
+ code: string;
21
+ /** Section heading, in the module's authoring language. The installer gives it an
22
+ * `entity_column_group` row keyed on this section's `code`, so
23
+ * `translations/<locale>.json` localizes it under
24
+ * `entities.<entity>.columnGroups.<code>.label` and the card renders that instead. */
25
+ label?: string;
26
+ columns: CardFieldEntry[];
27
+ /** Field columns across the group (1-3). Defaults to the card's own setting. */
28
+ cols?: number;
29
+ }
30
+
31
+ /**
32
+ * An embedded detail grid for a 1:N set column — the master/detail building
33
+ * block. The label comes from the set column's own metadata.
34
+ */
35
+ export interface SetSection {
36
+ type: "set";
37
+ code: string;
38
+ /** Set column code on this entity (columnType 'S'). */
39
+ setField: string;
40
+ }
41
+
42
+ /** Reference to a tab item: a columnGroup section's code, or a set column's code. */
43
+ export interface CardTabRef {
44
+ type: "section" | "set";
45
+ code: string;
46
+ }
47
+
48
+ /** A tab bar whose tabs are other sections — several detail grids without one long form. */
49
+ export interface TabGroupSection {
50
+ type: "tabGroup";
51
+ code: string;
52
+ tabs: CardTabRef[];
53
+ }
54
+
55
+ export type CardSection = ColumnGroupSection | SetSection | TabGroupSection;
56
+
57
+ /** Per-set-field renderer choice, keyed by the set column code. */
58
+ export interface CardSetConfig {
59
+ viewType?: "grid" | "list";
60
+ /** Opaque per-renderer options, passed verbatim to the set registration. */
61
+ options?: Record<string, unknown>;
62
+ }
63
+
64
+ /** The layout body, stored verbatim into `dForge.entity_view_layout.layout`. */
65
+ export interface CardLayout {
66
+ sections: CardSection[];
67
+ sets?: Record<string, CardSetConfig>;
68
+ }
69
+
70
+ /**
71
+ * A module-shipped card layout (value in the card_layouts map; the key is the
72
+ * layout name).
73
+ *
74
+ * Rows written from here carry `module_id`, so the installer recreates them on
75
+ * every install and removes them on uninstall. A layout the tenant drew in the
76
+ * card editor has `module_id` NULL and is never touched.
77
+ */
78
+ export interface CardLayoutDef {
79
+ /**
80
+ * Entity code the layout belongs to. Unqualified means this module owns the
81
+ * entity; `module.entity` targets another module's (the bridge case).
82
+ */
83
+ entity: string;
84
+ /**
85
+ * Entity view the layout hangs off. Defaults to `"default"` — the same
86
+ * fallback the runtime uses for an entity with no folder binding of its own.
87
+ * Matched against the entity's declared views case-insensitively, and filed
88
+ * under their spelling.
89
+ */
90
+ view?: string;
91
+ /**
92
+ * Whether this layout opens by default. Honoured on FIRST install only, and
93
+ * only when the view has no default yet: once a tenant has chosen their own,
94
+ * an upgrade does not take it back.
95
+ */
96
+ isDefault?: boolean;
97
+ /** Human-readable note for the package author. Not stored. */
98
+ description?: string;
99
+ layout: CardLayout;
100
+ }
101
+
102
+ /** `ui/card_layouts.json` — layout name → definition. */
103
+ export type CardLayoutsFile = Record<string, CardLayoutDef>;
package/src/data-views.ts CHANGED
@@ -17,7 +17,6 @@ export type ViewType =
17
17
  | "gallery"
18
18
  | "tree-grid"
19
19
  | "diagram"
20
- | "master-detail"
21
20
  | "library"
22
21
  | "matrix";
23
22
 
@@ -30,7 +29,6 @@ export const dataViewKinds: readonly NamedKind[] = [
30
29
  { cd: "gallery", name: "Gallery" },
31
30
  { cd: "tree-grid", name: "Tree Grid" },
32
31
  { cd: "diagram", name: "Diagram" },
33
- { cd: "master-detail", name: "Master / Detail" },
34
32
  { cd: "library", name: "Library" },
35
33
  { cd: "matrix", name: "Matrix" },
36
34
  ] as const;
@@ -55,7 +53,7 @@ export interface DataSource {
55
53
  entityCode: string;
56
54
  /** Nesting level (0 = root, 1+ = detail). */
57
55
  level?: number;
58
- /** Human-readable label (used in master-detail tabs). */
56
+ /** Human-readable label for this data source. */
59
57
  label?: string;
60
58
  /** Column configuration. */
61
59
  columns?: ViewColumn[];
@@ -121,7 +119,7 @@ export interface DataViewDef {
121
119
  /** Bootstrap icon class (e.g. 'bi-bounding-box'). */
122
120
  icon?: string;
123
121
  description?: string;
124
- /** Data sources (at least one; master-detail needs two). */
122
+ /** Data sources (at least one). Only the first is rendered today. */
125
123
  dataSources: DataSource[];
126
124
  /** Default filter applied to all sources unless one declares its own. */
127
125
  filter?: Filter;
package/src/entity.ts CHANGED
@@ -130,8 +130,8 @@ export interface AccumulationConfig {
130
130
  sign?: SignConfig;
131
131
  autoCreateBalance?: boolean;
132
132
  allowNegative?: boolean;
133
- /** Document fields locked once posted. */
134
- lockedFields?: string[];
133
+ /** Document fields locked once posted. Required; `["*"]` freezes the whole row. */
134
+ lockedFields: string[];
135
135
  }
136
136
 
137
137
  /** One registry target within an L-column's `registries` array. */
@@ -167,6 +167,8 @@ export interface EntityDef {
167
167
  viewSql?: string;
168
168
  /** Display pattern using column placeholders, e.g. "{first_name} {last_name}". */
169
169
  toString?: string;
170
+ /** Records of this entity accept comments (composer + thread on the card). */
171
+ comments?: boolean;
170
172
  /** Traits expanded at install time. */
171
173
  traits?: TraitCd[];
172
174
  /** Column definitions keyed by column code. Optional when a trait (e.g. `period`) supplies all columns. */
package/src/index.ts CHANGED
@@ -117,6 +117,19 @@ export type { JobsFile, JobDef } from "./jobs";
117
117
  export type { TriggersFile, TriggerDef, EntityEvent } from "./triggers";
118
118
  export type { WebhooksFile, WebhookSubscription, WebhookPayload } from "./webhooks";
119
119
  export type { PrintTemplatesFile, PrintTemplateDef, PrintPageSettings, PrintMargins } from "./print-templates";
120
+ export type {
121
+ CardLayoutsFile,
122
+ CardLayoutDef,
123
+ CardLayout,
124
+ CardSection,
125
+ CardSetConfig,
126
+ CardTabRef,
127
+ CardFieldEntry,
128
+ CardFieldDef,
129
+ ColumnGroupSection,
130
+ SetSection,
131
+ TabGroupSection,
132
+ } from "./card-layouts";
120
133
  export type { SeedDataFile } from "./seed-data";
121
134
  export type { ActionsFile, ActionDef, ActionExecutionMode } from "./actions";
122
135
 
package/src/manifest.ts CHANGED
@@ -44,6 +44,8 @@ export interface ManifestDef {
44
44
  dependencies?: Record<string, ModuleDependency>;
45
45
  /** Default audit history mode for this module's entities. */
46
46
  auditHistory?: "none" | "minimal" | "full";
47
+ /** Default for `comments` on this module's entities; each entity may override. */
48
+ comments?: boolean;
47
49
  /** Entity code → relative path of its JSON file (dotted keys = extensions). */
48
50
  entities?: Record<string, string>;
49
51
  /** Module category for display (e.g. 'Integration', 'Finance'). */