@dforge-core/metadata 0.0.39 → 0.0.41

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/CHANGELOG.md CHANGED
@@ -7,6 +7,36 @@ and this package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.41] — 2026-10-13
11
+
12
+ ### Fixed
13
+
14
+ - **`entity.schema.json`: an extension that declares only `reactions` is valid.** The root
15
+ requirement of `fields` or `traits` now also accepts `extends` together with `reactions`, so a
16
+ bridge module's extension file carrying just field-change reactions (e.g. `crm-pricing`'s
17
+ `crm.quote_line`) no longer fails validation.
18
+
19
+ ## [0.0.40] — 2026-10-13
20
+
21
+ ### Added
22
+
23
+ - **`entity.schema.json`, a register column's `validation`:** now described — `balanced`,
24
+ `requireBalanced`, and the new `storno` (a red reversal: negative amounts on an accounting `L`
25
+ column, the other per-line checks kept). `requirePositiveAmounts` is listed as deprecated in favour
26
+ of `storno`.
27
+ - **`DepProvenanceKind`: the `function` kind** — `function:<library>` marks a function in
28
+ `logic/functions/<library>.dsl` that reads or writes the dependency, as an action does.
29
+ `deps.schema.json` and the server validator already accept it (dForge-core #1424); the TypeScript
30
+ type was the one place still missing it.
31
+ - **`DepProvenanceKind`: the `reaction` kind** — `reaction:<code>` marks a dependency column a
32
+ field-change reaction the consumer's extension file declares fires on or sets. Both
33
+ `deps.schema.json` copies accept it.
34
+ - **`entity.schema.json` and `EntityDef.reactions` (`EntityReaction`): field-change reactions** —
35
+ rules that fill a stored, editable column when the user edits another field: from a formula, or
36
+ from what a server rule's `call` function (a `logic/functions` function) returns. A rule `code`
37
+ may not contain `.` (a bridge module's rules are stored as `<module>.<code>`). See dForge-core
38
+ `docs/ideas/field-change-reactions.md`.
39
+
10
40
  ## [0.0.39] — 2026-10-12
11
41
 
12
42
  ### Fixed
package/dist/index.d.ts CHANGED
@@ -705,6 +705,29 @@ interface LedgerRegistryEntry {
705
705
  creditResource: string;
706
706
  };
707
707
  }
708
+ /** One field-change reaction rule of an entity. */
709
+ interface EntityReaction {
710
+ /** Names the rule in errors; unique within the entity. */
711
+ code: string;
712
+ /** The columns whose edit fires the rule. */
713
+ on: string[];
714
+ /**
715
+ * A server rule's function: one of the module's libraries (`name`), or an imported one
716
+ * (`namespace.name`). Called with the record being edited; returns the values by column.
717
+ */
718
+ call?: string;
719
+ /**
720
+ * A formula rule: column → formula, or `{ formula, ifEmpty }` to fill only an empty field.
721
+ * A server rule: the columns its function may set, each a column or `{ column, ifEmpty }`.
722
+ */
723
+ set: Record<string, string | {
724
+ formula: string;
725
+ ifEmpty?: boolean;
726
+ }> | (string | {
727
+ column: string;
728
+ ifEmpty?: boolean;
729
+ })[];
730
+ }
708
731
  /** A single entity definition (`entities/<code>.json`). */
709
732
  interface EntityDef {
710
733
  /** Qualified entity code to extend (e.g. 'fin.invoice') — turns this into an extension. */
@@ -720,6 +743,12 @@ interface EntityDef {
720
743
  auditHistory?: AuditHistoryLevel;
721
744
  /** Records of this entity accept comments (composer + thread on the card). */
722
745
  comments?: boolean;
746
+ /**
747
+ * Field-change reactions: when the user edits an `on` column, each `set` column is filled from
748
+ * its formula, or from what a server rule's `call` function returns. UI only (form, grid, bulk
749
+ * update). See docs/ideas/field-change-reactions.md.
750
+ */
751
+ reactions?: EntityReaction[];
723
752
  /** Traits expanded at install time. */
724
753
  traits?: TraitCd[];
725
754
  /** Column definitions keyed by column code. Optional when a trait (e.g. `period`) supplies all columns. */
@@ -1237,10 +1266,12 @@ type DepColumnType = (typeof DEP_COLUMN_TYPES)[number];
1237
1266
  /** Whether the consumer reads or writes a column. Default `read`. */
1238
1267
  type DepColumnMode = "read" | "write";
1239
1268
  /** Kinds of cross-module usage a provenance token can name. */
1240
- type DepProvenanceKind = "ref" | "extends" | "formula" | "action" | "view" | "report" | "print" | "role";
1269
+ type DepProvenanceKind = "ref" | "extends" | "formula" | "action" | "function" | "reaction" | "view" | "report" | "print" | "role";
1241
1270
  /**
1242
1271
  * A usage token of the form `<kind>:<symbol>` — e.g. `ref:invoice.customer`,
1243
- * `extends:party`, `action:check_credit`, `role:fin.admin`. Names the consuming
1272
+ * `extends:parties.party`, `action:check_credit`, `function:wh_posting` (a function in
1273
+ * `logic/functions/wh_posting.dsl`), `reaction:quote_line_pricing` (a field-change reaction
1274
+ * an extension declares on the dependency entity), `role:fin.admin`. Names the consuming
1244
1275
  * symbol so the completeness lint can reconcile declared vs actual usage. Note:
1245
1276
  * `display` is intentionally not a kind — the label is the provider's concern.
1246
1277
  */
@@ -2016,4 +2047,4 @@ declare const flagDefs: readonly {
2016
2047
  label: string;
2017
2048
  }[];
2018
2049
 
2019
- export { AGG_TYPE_LIST, type AccumulationConfig, AggType, type AlignType, type AuditHistoryLevel, 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 DiagramDef, type DiagramDetail, type DiagramEntity, type DiagramFile, type DiagramRelation, 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, REGISTER_CODE, REGISTER_COLUMN_NAME, REGISTER_KINDS, REGISTER_TOTALS_PERIODS, type RegisterBindingParams, type RegisterBindingTarget, type RegisterCompositionDef, type RegisterDef, type RegisterEntityInfo, type RegisterEntityLookup, type RegisterIssue, type RegisterKind, type RegisterMapValue, type RegisterResourceDef, type RegisterTotalsDef, type RegisterTotalsPeriod, type RegistersFile, type ReportDef, type ReportEntityAttachment, type ReportLayout, type ReportPanel, type ReportQuery, type ReportsFile, type RoleDef, type RolesFile, type SeedDataFile, type SeedDataRepeat, type SetSection, type SettingBaseDatatype, type SettingConstraintDef, type SettingDef, type SettingsFile, type SignConfig, type SortClause, type SpColumnDef, type SpParamDef, type StoredProcedureDef, type StoredProceduresFile, type TabGroupSection, type TraitCd, type TraitDef, type TraitFieldConflict, type TraitsFile, type TranslatedAction, type TranslatedEntity, type TranslatedMenu, type TranslatedOption, type TranslatedReport, type TranslatedWithOptions, type TranslationsFile, type TreeGridViewConfig, type TriggerDef, type TriggersFile, type ViewColumn, type ViewType, type VizType, type WebhookPayload, type WebhookSubscription, type WebhooksFile, baseDatatypes, baseToDbDatatype, chartTypes, checkRegister, checkRegisterBinding, checkRegisters, columnTypes, dataViewKinds, defaultParams, deriveBaseDatatype, deriveDbDatatype, expandTrait, expandTraits, fieldTypeCds, fieldTypes, fieldTypesByColumnType, flagDefs, getColumnType, getFieldType, getIdentityKeys, getLinks, getPrimaryKeys, getTrait, isFieldTypeCd, isPlainColumnRef, isRegisterKind, registerCarrier, registerDimensions, registerLineMode, registerRefusedProps, registerRequiredProps, registerResourceShape, registerResources, settingDefsOf, traitFieldConflicts, traits, vizTypes };
2050
+ export { AGG_TYPE_LIST, type AccumulationConfig, AggType, type AlignType, type AuditHistoryLevel, 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 DiagramDef, type DiagramDetail, type DiagramEntity, type DiagramFile, type DiagramRelation, type DiagramViewConfig, type DomainDef, type DomainsFile, type EntityDef, type EntityEvent, type EntityReaction, 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, REGISTER_CODE, REGISTER_COLUMN_NAME, REGISTER_KINDS, REGISTER_TOTALS_PERIODS, type RegisterBindingParams, type RegisterBindingTarget, type RegisterCompositionDef, type RegisterDef, type RegisterEntityInfo, type RegisterEntityLookup, type RegisterIssue, type RegisterKind, type RegisterMapValue, type RegisterResourceDef, type RegisterTotalsDef, type RegisterTotalsPeriod, type RegistersFile, type ReportDef, type ReportEntityAttachment, type ReportLayout, type ReportPanel, type ReportQuery, type ReportsFile, type RoleDef, type RolesFile, type SeedDataFile, type SeedDataRepeat, type SetSection, type SettingBaseDatatype, type SettingConstraintDef, type SettingDef, type SettingsFile, type SignConfig, type SortClause, type SpColumnDef, type SpParamDef, type StoredProcedureDef, type StoredProceduresFile, type TabGroupSection, type TraitCd, type TraitDef, type TraitFieldConflict, type TraitsFile, type TranslatedAction, type TranslatedEntity, type TranslatedMenu, type TranslatedOption, type TranslatedReport, type TranslatedWithOptions, type TranslationsFile, type TreeGridViewConfig, type TriggerDef, type TriggersFile, type ViewColumn, type ViewType, type VizType, type WebhookPayload, type WebhookSubscription, type WebhooksFile, baseDatatypes, baseToDbDatatype, chartTypes, checkRegister, checkRegisterBinding, checkRegisters, columnTypes, dataViewKinds, defaultParams, deriveBaseDatatype, deriveDbDatatype, expandTrait, expandTraits, fieldTypeCds, fieldTypes, fieldTypesByColumnType, flagDefs, getColumnType, getFieldType, getIdentityKeys, getLinks, getPrimaryKeys, getTrait, isFieldTypeCd, isPlainColumnRef, isRegisterKind, registerCarrier, registerDimensions, registerLineMode, registerRefusedProps, registerRequiredProps, registerResourceShape, registerResources, settingDefsOf, traitFieldConflicts, traits, vizTypes };
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/settings.ts","../src/registers/types.ts","../src/registers/read.ts","../src/registers/entities.ts","../src/registers/issues.ts","../src/registers/check-declaration.ts","../src/registers/check-binding.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, defParams: { pattern: \"^[^\\\\s@]+@[^\\\\s@]+\\\\.[^\\\\s@]+$\" } },\n\t{ cd: \"phone\", name: \"Phone\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true, defParams: { pattern: \"^\\\\+?[0-9\\\\s\\\\-().]+$\" } },\n\t{ cd: \"url\", name: \"URL\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true, defParams: { pattern: \"^https?://.+\", patternFlags: \"i\" } },\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/**\n * An accounting register read over an interval: one row per account of the register's chart,\n * with opening, turnover and closing debit/credit for one resource, netted per account.\n */\nexport interface RegisterSource {\n\t/** The register, `module_cd.register_cd`. */\n\tname: string;\n\t/** The one resource the six balance columns are about. */\n\tresource: string;\n\t/** Two date params, or the record of `entity` a key param finds and its two date columns. */\n\tinterval: { entity?: string; key?: string; from: string; to: string };\n\t/** Chart columns in nav notation, `<chart>.<column>`. */\n\tcolumns?: string[];\n\t/** A filter over the chart, in the same nav notation. */\n\tfilter?: Filter;\n\t/** Chart columns to sort by, in nav notation. */\n\tsort?: SortClause[];\n\t/** The query rollup without `leafKey` and `measures`. */\n\trollup?: { tree: string; treeKey: string; parentField: string; labels: string[]; order?: string };\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, R = an accounting register read over an interval. */\n\tdatasetType: \"Q\" | \"S\" | \"R\";\n\t/** Used when `datasetType` is 'Q'. */\n\tquery?: ReportQuery;\n\t/**\n\t * Used when `datasetType` is 'S': the stored-procedure code, as\n\t * `logic/stored_procedures.json` keys it — not the PostgreSQL function name,\n\t * and never schema-qualified. `module.spCd` binds a dependency's procedure.\n\t */\n\tspCd?: string;\n\t/** Used when `datasetType` is 'R'. */\n\tregister?: RegisterSource;\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/** Report name: page title, breadcrumb and menu entry. Omit and `description` is the name. */\n\tlabel?: string;\n\t/** What the report shows, under its name on the page. */\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","// Module settings — authoring shape of `settings.json`. Mirror of settings.schema.json.\n\n/** Underlying primitive datatype for a setting. */\nexport type SettingBaseDatatype = \"string\" | \"number\" | \"bool\" | \"date\" | \"dateTime\" | \"time\" | \"json\";\n\n/** Properties carried by every setting regardless of where its control comes from. */\nexport interface SettingDefBase {\n\t/** Display label (preferred for new settings). */\n\tlabel?: string;\n\t/** Display label (older style). Prefer `label`. */\n\tdescription?: string;\n\t/** Alternative older label key. Prefer `label`. */\n\tdesc?: string;\n\t/** Default value before the user sets one. */\n\tdefaultValue?: unknown;\n\t/** Formula to evaluate the default (alternative to `defaultValue`). */\n\tformula?: string;\n\t/** If true, must be set somewhere in the folder chain for install to proceed. */\n\trequired?: boolean;\n}\n\n/** A setting declares its control inline or borrows it from a reusable column domain. */\nexport type SettingDef =\n\t| (SettingDefBase & {\n\t\t/** Field type code (must match a `dforge.field_type` code). */\n\t\tfieldTypeCd: string;\n\t\t/** Underlying primitive datatype (older settings declare this explicitly). */\n\t\tbaseDatatypeCd?: SettingBaseDatatype;\n\t\t/** Field-type-specific parameters (dropdown options, min/max, lookup link, …). */\n\t\tparams?: Record<string, unknown>;\n\t\tdomain?: never;\n\t})\n\t| (SettingDefBase & {\n\t\t/** Column domain supplying fieldTypeCd, baseDatatypeCd and params. */\n\t\tdomain: string;\n\t\tfieldTypeCd?: never;\n\t\tbaseDatatypeCd?: never;\n\t\tparams?: never;\n\t});\n\n/** A check across a module's settings: SQL over setting codes, as an entity check constraint. */\nexport interface SettingConstraintDef {\n\ttype: \"check\";\n\t/** PostgreSQL boolean over setting codes; writing any setting it names re-checks it. */\n\texpression: string;\n\tmessage?: string;\n}\n\n/**\n * A `settings.json` file: setting code → definition, plus the optional `$constraints`. A\n * setting code cannot start with `$`, but TypeScript cannot exclude it from the index, so\n * the index admits the constraints map too.\n */\nexport type SettingsFile = {\n\t$constraints?: Record<string, SettingConstraintDef>;\n\t[settingCd: string]: SettingDef | Record<string, SettingConstraintDef> | undefined;\n};\n\n/** The settings of a file, without its `$constraints`. */\nexport function settingDefsOf(file: SettingsFile): Record<string, SettingDef> {\n\tconst defs: Record<string, SettingDef> = {};\n\tfor (const [code, def] of Object.entries(file))\n\t\tif (code !== \"$constraints\" && def) defs[code] = def as SettingDef;\n\treturn defs;\n}\n","// Registers — the authoring shape of `registers.json` and of an A / L column's\n// binding. Mirror of registers.schema.json and docs/business-logic/registers.md §3–4.\n\n/** The four kinds. Each requires and refuses a fixed set of properties. */\nexport type RegisterKind = \"balance\" | \"turnover\" | \"info\" | \"accounting\";\n\nexport const REGISTER_KINDS: readonly RegisterKind[] = [\"balance\", \"turnover\", \"info\", \"accounting\"];\n\nexport const isRegisterKind = (value: unknown): value is RegisterKind =>\n\ttypeof value === \"string\" && (REGISTER_KINDS as readonly string[]).includes(value);\n\n/** Register codes: lowercase letters, digits and underscores, starting with a letter. */\nexport const REGISTER_CODE = /^[a-z][a-z0-9_]*$/;\n\n/** A column name as `registers.json` takes one. */\nexport const REGISTER_COLUMN_NAME = /^[a-z_][a-z0-9_]*$/;\n\n/** How much one cached row of a `totals` cache covers. */\nexport type RegisterTotalsPeriod = \"day\" | \"month\" | \"quarter\" | \"year\";\n\nexport const REGISTER_TOTALS_PERIODS: readonly RegisterTotalsPeriod[] = [\"day\", \"month\", \"quarter\", \"year\"];\n\n/** A measured column of the register: one shared column, or one per side on an accounting register. */\nexport interface RegisterResourceDef {\n\t/** The column, on the state entity / journal / record entity. Not with `debitField` / `creditField`. */\n\tfield?: string;\n\t/** Debit-side column, on an accounting register. Declared with `creditField`. */\n\tdebitField?: string;\n\tcreditField?: string;\n\t/** Every binding must map it (default true). False for a resource only some writers supply. */\n\trequired?: boolean;\n\t/** Under `recordChangesOnly`: what the resource compares as before any record is in effect. */\n\tabsentAs?: number | string | boolean;\n\tdescription?: string;\n}\n\n/** The per-period cache (registers.md §8). */\nexport interface RegisterTotalsDef {\n\tentity: string;\n\tperiod: RegisterTotalsPeriod;\n\t/** Column holding the first date of the period a row covers. */\n\tperiodField: string;\n\t/** Account column; required on an accounting register, refused elsewhere. */\n\taccountField?: string;\n\t/** Which dimensions the cache is grouped by; accounting only; all of them when omitted. */\n\tdimensions?: string[];\n\t/** Every resource of the register: `inField` + `outField`, or `debitField` + `creditField` on an accounting register. */\n\tresources: Record<string, { inField?: string; outField?: string; debitField?: string; creditField?: string }>;\n}\n\n/** Where the chart says which analytic each account carries in which slot. */\nexport interface RegisterCompositionDef {\n\tentity: string;\n\taccount: string;\n\tslot: string;\n\tanalytic: string;\n}\n\n/** One declaration of `registers.json`. */\nexport interface RegisterDef {\n\tkind: RegisterKind;\n\tlabel?: string;\n\tdescription?: string;\n\t/** State table (`balance`) or record table (`info`). */\n\tentity?: string;\n\t/** Movement / posting entity (`balance` optional, `turnover` and `accounting` required). */\n\tjournal?: string;\n\t/** Journal column stamped +1 / −1 (`balance`, `turnover`). */\n\tjournalDirection?: string;\n\t/** Journal column holding each movement's date. */\n\tjournalDate?: string;\n\t/** Record-entity column holding when a record takes effect (`info`). */\n\tperiod?: string;\n\t/** Record-entity jsonb column the writing document is stamped into (`info`); makes the register subordinate. */\n\trecorder?: string;\n\trecordChangesOnly?: boolean;\n\t/** Chart of accounts (`accounting`). */\n\tchart?: string;\n\tlineMode?: \"classic\" | \"compound\";\n\t/** Posting column pointing at the chart (classic). */\n\taccount?: string;\n\tdebitAccount?: string;\n\tcreditAccount?: string;\n\t/** Analytic slot columns in slot order (classic). */\n\tanalyticFields?: string[];\n\tdebitAnalyticFields?: string[];\n\tcreditAnalyticFields?: string[];\n\tcomposition?: RegisterCompositionDef;\n\t/** 1–8 column names: the key of the register, on its carrier. */\n\tdimensions: string[];\n\t/** Two-sided dimensions of a compound register, with the column per side. */\n\tsideDimensions?: Record<string, { debitField: string; creditField: string }>;\n\tresources: Record<string, RegisterResourceDef>;\n\ttotals?: RegisterTotalsDef;\n\t/** `balance`: may the state go below zero (default false). */\n\tallowNegative?: boolean;\n\t/** `balance`: create the state row on first movement (default true). */\n\tautoCreate?: boolean;\n}\n\n/** `registers.json`: register code → declaration. */\nexport type RegistersFile = Record<string, RegisterDef>;\n\n/** A map entry: a column of the row read, or on a compound ledger one column per side. */\nexport type RegisterMapValue = string | { debit?: string; credit?: string };\n\n/** `params` of an A / L column bound to a register (registers.md §4). */\nexport interface RegisterBindingParams {\n\t/** `module_cd.register_cd`. */\n\tregister?: string;\n\t/** The bool column recording the posted state, when not the column itself. */\n\tstateField?: string;\n\tdirection?: \"increase\" | \"decrease\";\n\t/** Per-row direction from a document value, instead of `direction`. */\n\tsign?: { field: string; positive?: unknown[]; negative?: unknown[] };\n\t/** Document column (or path / expression) supplying the journal date. */\n\tdateField?: string;\n\tdateTrunc?: \"day\" | \"month\" | \"year\";\n\tlineEntity?: string;\n\tlineField?: string;\n\tgroupLinesBy?: string[];\n\t/** Register dimension or resource name → column of the row read. */\n\tmap?: Record<string, RegisterMapValue>;\n\t/** On a line posting: register name → DOCUMENT column. */\n\theaderMap?: Record<string, string>;\n\tlockedFields?: string[];\n\tlockedLineFields?: string[];\n\tskipPostingWhenEmpty?: string[];\n\t/** Classic ledger: the line's account column. */\n\taccountField?: string;\n\tdebitAccountField?: string;\n\tcreditAccountField?: string;\n\tperiodDimField?: string;\n\t/** Analytic kind → line column (classic ledger). */\n\tanalytics?: Record<string, string>;\n\tdebitAnalytics?: Record<string, string>;\n\tcreditAnalytics?: Record<string, string>;\n\tmovementMapping?: Record<string, string>;\n\t/** Inline restatement of a register; refused alongside `register`. */\n\tregistries?: unknown;\n}\n","// Reading a declaration: the loose-JSON helpers every check shares, and what each\n// kind requires and refuses.\n\nimport type { RegisterDef, RegisterKind, RegisterResourceDef } from \"./types\";\n\nexport type Raw = Record<string, unknown>;\n\nexport const isRecord = (value: unknown): value is Raw =>\n\t!!value && typeof value === \"object\" && !Array.isArray(value);\n\nexport const safeTrim = (value: unknown): string | undefined => (typeof value === \"string\" && value.trim() ? value : undefined);\n\nexport const stringList = (value: unknown): string[] =>\n\tArray.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n\n/** A key present but undefined is an editor's draft, not a declaration. */\nexport const hasKey = (obj: Raw, key: string): boolean => obj[key] !== undefined;\n\n/** A declaration's dimension names, only the ones that are strings. */\nexport const registerDimensions = (def: RegisterDef | Raw): string[] => stringList(def.dimensions);\n\n/** A declaration's resources in file order, each with its definition object. */\nexport const registerResources = (def: RegisterDef | Raw): Array<{ name: string; def: Raw }> =>\n\t!isRecord(def.resources)\n\t\t? []\n\t\t: Object.entries(def.resources).map(([name, raw]) => ({ name, def: isRecord(raw) ? raw : {} }));\n\n/** The entity the dimensions, resources and stamps are columns of. */\nexport function registerCarrier(def: RegisterDef | Raw): string | undefined {\n\tswitch (def.kind) {\n\t\tcase \"balance\":\n\t\tcase \"info\":\n\t\t\treturn safeTrim(def.entity);\n\t\tcase \"turnover\":\n\t\tcase \"accounting\":\n\t\t\treturn safeTrim(def.journal);\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/** How a resource names its column: one shared column, or one per side. */\nexport const registerResourceShape = (resource: RegisterResourceDef | Raw): \"field\" | \"split\" =>\n\t\"debitField\" in resource || \"creditField\" in resource ? \"split\" : \"field\";\n\nexport const registerLineMode = (def: RegisterDef | Raw): \"classic\" | \"compound\" =>\n\tdef.lineMode === \"compound\" ? \"compound\" : \"classic\";\n\nconst INFO_PROPERTIES = [\"period\", \"recorder\", \"recordChangesOnly\"] as const;\nconst ACCOUNTING_PROPERTIES = [\n\t\"chart\", \"composition\", \"analyticFields\", \"lineMode\", \"account\",\n\t\"debitAccount\", \"creditAccount\", \"debitAnalyticFields\", \"creditAnalyticFields\", \"sideDimensions\",\n] as const;\nconst BALANCE_PROPERTIES = [\"allowNegative\", \"autoCreate\"] as const;\n\n/** Properties each kind refuses — the schema's `false` properties, which install fails on. */\nconst REFUSED: Record<RegisterKind, readonly string[]> = {\n\tbalance: [...INFO_PROPERTIES, ...ACCOUNTING_PROPERTIES],\n\tturnover: [\"entity\", ...INFO_PROPERTIES, ...ACCOUNTING_PROPERTIES, ...BALANCE_PROPERTIES],\n\tinfo: [\"journal\", \"journalDirection\", \"journalDate\", ...ACCOUNTING_PROPERTIES, \"totals\", ...BALANCE_PROPERTIES],\n\taccounting: [\"entity\", ...INFO_PROPERTIES, \"journalDirection\", ...BALANCE_PROPERTIES],\n};\n\n/** Properties each kind requires beside `kind`, `dimensions` and `resources`. */\nconst REQUIRED: Record<RegisterKind, readonly string[]> = {\n\tbalance: [\"entity\"],\n\tturnover: [\"journal\", \"journalDirection\", \"journalDate\"],\n\tinfo: [\"entity\"],\n\taccounting: [\"chart\", \"journal\", \"journalDate\", \"lineMode\"],\n};\n\nexport const registerRefusedProps = (kind: RegisterKind): readonly string[] => REFUSED[kind];\nexport const registerRequiredProps = (kind: RegisterKind): readonly string[] => REQUIRED[kind];\n\n/** \"A balance register\" / \"a balance register\", for the start and the middle of a message. */\nexport const KIND_LABEL: Record<RegisterKind, { sentence: string; inline: string }> = {\n\tbalance: { sentence: \"A balance register\", inline: \"a balance register\" },\n\tturnover: { sentence: \"A turnover register\", inline: \"a turnover register\" },\n\tinfo: { sentence: \"An information register\", inline: \"an information register\" },\n\taccounting: { sentence: \"An accounting register\", inline: \"an accounting register\" },\n};\n","// Resolving the entities and columns a declaration names, through the caller's lookup.\n\nimport { REGISTER_COLUMN_NAME } from \"./types\";\nimport type { IssuePath, RegisterReporter } from \"./issues\";\n\n/**\n * What an entity a declaration names looks like, as far as the caller knows.\n * `columns` is left undefined when they can't be listed (a dependency known\n * only from its contract), and no column rule runs against it.\n */\nexport interface RegisterEntityInfo {\n\tcolumns?: Iterable<string>;\n}\n\n/**\n * Resolves an entity code as `registers.json` writes it — bare for the module's\n * own, `module.entity` for another's. Returns `null` when nothing can be known\n * (the module named isn't available: the rule stands down) and `undefined` when\n * the module is known and has no such entity.\n */\nexport type RegisterEntityLookup = (code: string) => RegisterEntityInfo | null | undefined;\n\nexport type ResolvedRegisterEntity = RegisterEntityInfo | null | undefined;\n\nexport interface EntityChecks {\n\t/** Resolve `code`, reporting `unknown-entity` when the module is known and has no such entity. */\n\tentityAt: (path: IssuePath, code: string | undefined, what: string) => ResolvedRegisterEntity;\n\t/** Check a column name, and that `entity` stores it. A non-string or blank column is skipped. */\n\tcolumnAt: (path: IssuePath, entity: ResolvedRegisterEntity, entityCode: string | undefined, column: unknown, what: string) => void;\n}\n\nexport function createEntityChecks(\n\tlookup: RegisterEntityLookup | undefined,\n\terror: RegisterReporter,\n\twarning: RegisterReporter,\n): EntityChecks {\n\tconst resolved = new Map<string, ResolvedRegisterEntity>();\n\tconst columnSets = new WeakMap<RegisterEntityInfo, Set<string> | null>();\n\n\tconst entityAt: EntityChecks[\"entityAt\"] = (path, code, what) => {\n\t\tif (!code || !lookup) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!resolved.has(code)) {\n\t\t\tresolved.set(code, lookup(code));\n\t\t}\n\t\tconst found = resolved.get(code);\n\t\tif (found === undefined) {\n\t\t\terror(path, \"unknown-entity\", `${what} '${code}' isn't an entity this module can see.`);\n\t\t}\n\t\treturn found;\n\t};\n\n\tconst columnsOf = (entity: ResolvedRegisterEntity): Set<string> | null => {\n\t\tif (!entity) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!columnSets.has(entity)) {\n\t\t\tcolumnSets.set(entity, entity.columns ? new Set(entity.columns) : null);\n\t\t}\n\t\treturn columnSets.get(entity) ?? null;\n\t};\n\n\tconst columnAt: EntityChecks[\"columnAt\"] = (path, entity, entityCode, column, what) => {\n\t\tif (typeof column !== \"string\" || !column.trim()) {\n\t\t\treturn;\n\t\t}\n\t\tif (!REGISTER_COLUMN_NAME.test(column)) {\n\t\t\terror(path, \"column-name\", `${what} '${column}' isn't a column name (lowercase letters, digits and '_').`);\n\t\t\treturn;\n\t\t}\n\t\tconst columns = columnsOf(entity);\n\t\tif (columns && !columns.has(column)) {\n\t\t\twarning(path, \"unknown-column\", `${what} '${column}' isn't a stored column of ${entityCode}.`);\n\t\t}\n\t};\n\n\treturn { entityAt, columnAt };\n}\n","// Findings: what a check reports, and the reporters that collect them.\n\nexport type IssuePath = Array<string | number>;\n\n/** One finding, addressed by a JSON path relative to what was checked. */\nexport interface RegisterIssue {\n\tpath: IssuePath;\n\tmessage: string;\n\t/** `error`: install refuses it. `warning`: worth a look. */\n\tseverity: \"error\" | \"warning\";\n\t/** Stable id, namespaced `registers/*` for a declaration and `entity/register-*` for a binding. */\n\tcode: string;\n}\n\nexport type RegisterReporter = (path: IssuePath, code: string, message: string) => void;\n\nexport function createIssueReporter(\n\tissues: RegisterIssue[],\n\tnamespace: \"registers\" | \"entity\",\n\tseverity: RegisterIssue[\"severity\"],\n): RegisterReporter {\n\treturn (path, code, message) => {\n\t\tissues.push({ path, code: `${namespace}/${code}`, message, severity });\n\t};\n}\n","// Checking one declaration of `registers.json`, and the file as a whole.\n// Every rule FAILS OPEN: what cannot be resolved is not reported.\n\nimport { createEntityChecks } from \"./entities\";\nimport type { EntityChecks, RegisterEntityLookup, ResolvedRegisterEntity } from \"./entities\";\nimport { createIssueReporter } from \"./issues\";\nimport type { IssuePath, RegisterIssue, RegisterReporter } from \"./issues\";\nimport {\n\tKIND_LABEL, hasKey, isRecord, registerCarrier, registerDimensions, registerLineMode, registerRefusedProps,\n\tregisterRequiredProps, registerResourceShape, registerResources, safeTrim, stringList,\n} from \"./read\";\nimport type { Raw } from \"./read\";\nimport { REGISTER_CODE, REGISTER_COLUMN_NAME, REGISTER_TOTALS_PERIODS, isRegisterKind } from \"./types\";\nimport type { RegisterKind } from \"./types\";\n\nconst MAX_DIMENSIONS = 8;\nconst MAX_RESOURCES = 64;\nconst MAX_ANALYTIC_SLOTS = 8;\nconst MAX_REGISTERS = 100;\n\ninterface Context extends EntityChecks {\n\tdef: Raw;\n\tkind: RegisterKind;\n\terror: RegisterReporter;\n\twarning: RegisterReporter;\n\tjournalCode?: string;\n\tjournal: ResolvedRegisterEntity;\n\tcarrierCode?: string;\n\tcarrier: ResolvedRegisterEntity;\n\tdimensions: string[];\n\tsides: Raw;\n\tresources: ReturnType<typeof registerResources>;\n}\n\n/** Two columns that must both be named and must differ — a debit/credit pair. */\nfunction checkColumnPair(\n\terror: RegisterReporter,\n\tcode: string,\n\tfirst: string | undefined,\n\tsecond: string | undefined,\n\twhere: { missing: IssuePath; missingMessage: string; same: IssuePath; sameMessage: string },\n): void {\n\tif (!first || !second) {\n\t\terror(where.missing, code, where.missingMessage);\n\t}\n\tif (first && first === second) {\n\t\terror(where.same, code, where.sameMessage);\n\t}\n}\n\n/** Report each of `keys` the declaration carries, with the same message. */\nfunction refuseKeys(def: Raw, keys: readonly string[], error: RegisterReporter, code: string, message: (key: string) => string): void {\n\tfor (const key of keys) {\n\t\tif (hasKey(def, key)) {\n\t\t\terror([key], code, message(key));\n\t\t}\n\t}\n}\n\nfunction checkProperties({ def, kind, error }: Pick<Context, \"def\" | \"kind\" | \"error\">): void {\n\tfor (const key of registerRequiredProps(kind)) {\n\t\tif (safeTrim(def[key]) === undefined) {\n\t\t\terror([key], \"required\", `${KIND_LABEL[kind].sentence} needs '${key}'.`);\n\t\t}\n\t}\n\trefuseKeys(def, registerRefusedProps(kind), error, \"refused\", (key) => `'${key}' isn't allowed on ${KIND_LABEL[kind].inline}.`);\n\tif ((safeTrim(def.journalDirection) || safeTrim(def.journalDate)) && !safeTrim(def.journal)) {\n\t\terror([\"journal\"], \"journal-stamps\", \"'journalDirection' and 'journalDate' are columns of the journal — name the journal entity.\");\n\t}\n\tif (kind === \"info\" && def.recordChangesOnly === true && !safeTrim(def.recorder)) {\n\t\terror([\"recordChangesOnly\"], \"record-changes-only\", \"'recordChangesOnly' needs a 'recorder' — only a document's record can be compared with the slice in effect.\");\n\t}\n}\n\nfunction resolveEntities(\n\tdef: Raw,\n\tkind: RegisterKind,\n\tchecks: EntityChecks,\n): Pick<Context, \"journalCode\" | \"journal\" | \"carrierCode\" | \"carrier\"> {\n\tconst { entityAt, columnAt } = checks;\n\tconst entityCode = safeTrim(def.entity);\n\tconst journalCode = safeTrim(def.journal);\n\tconst carriedByEntity = kind === \"balance\" || kind === \"info\";\n\tconst entity = carriedByEntity ? entityAt([\"entity\"], entityCode, \"Entity\") : null;\n\tconst journal = kind !== \"info\" ? entityAt([\"journal\"], journalCode, \"Journal\") : null;\n\tif (kind === \"accounting\") {\n\t\tentityAt([\"chart\"], safeTrim(def.chart), \"Chart\");\n\t}\n\n\tcolumnAt([\"journalDirection\"], journal, journalCode, def.journalDirection, \"Journal direction column\");\n\tcolumnAt([\"journalDate\"], journal, journalCode, def.journalDate, \"Journal date column\");\n\tcolumnAt([\"period\"], entity, entityCode, def.period, \"Period column\");\n\tcolumnAt([\"recorder\"], entity, entityCode, def.recorder, \"Recorder column\");\n\n\treturn { journalCode, journal, carrierCode: registerCarrier(def), carrier: carriedByEntity ? entity : journal };\n}\n\nfunction checkDimensions({ def, dimensions, sides, carrier, carrierCode, columnAt, error }: Context): void {\n\tif (!Array.isArray(def.dimensions) || dimensions.length === 0) {\n\t\terror([\"dimensions\"], \"dimensions\", \"A register needs at least one dimension.\");\n\t}\n\tif (dimensions.length > MAX_DIMENSIONS) {\n\t\terror([\"dimensions\"], \"dimensions\", `At most ${MAX_DIMENSIONS} dimensions.`);\n\t}\n\tconst twice = dimensions.filter((name, index) => dimensions.indexOf(name) !== index);\n\tif (twice.length > 0) {\n\t\terror([\"dimensions\"], \"dimensions\", `Dimension listed more than once: ${[...new Set(twice)].join(\", \")}.`);\n\t}\n\tdimensions.forEach((name, i) => {\n\t\t// A two-sided dimension lives in its side columns, not under its own name.\n\t\tif (!(name in sides)) {\n\t\t\tcolumnAt([\"dimensions\", i], carrier, carrierCode, name, \"Dimension\");\n\t\t}\n\t});\n}\n\n/** The shape a resource's columns take, per kind and line mode. */\nfunction checkResourceShape(ctx: Context, name: string, resource: Raw, path: (key?: string) => IssuePath): void {\n\tconst { def, kind, error } = ctx;\n\tconst field = safeTrim(resource.field);\n\tconst debit = safeTrim(resource.debitField);\n\tconst credit = safeTrim(resource.creditField);\n\tconst split = registerResourceShape(resource) === \"split\";\n\tconst classic = kind === \"accounting\" && registerLineMode(def) === \"classic\";\n\tif (split && hasKey(resource, \"field\")) {\n\t\terror(path(\"field\"), \"resource-shape\", `Resource '${name}': either 'field' or 'debitField' + 'creditField', not both.`);\n\t}\n\tif (classic && !split) {\n\t\terror(path(), \"resource-shape\", `Resource '${name}': on a classic accounting register every resource is split — 'debitField' and 'creditField'.`);\n\t} else if (kind !== \"accounting\" && split) {\n\t\terror(path(), \"resource-shape\", `Resource '${name}': 'debitField' / 'creditField' belong to an accounting register — name one 'field'.`);\n\t} else if (split) {\n\t\tcheckColumnPair(error, \"resource-shape\", debit, credit, {\n\t\t\tmissing: path(),\n\t\t\tmissingMessage: `Resource '${name}' needs both 'debitField' and 'creditField'.`,\n\t\t\tsame: path(\"creditField\"),\n\t\t\tsameMessage: `Resource '${name}': the debit and credit columns must differ.`,\n\t\t});\n\t} else if (!field) {\n\t\terror(path(), \"resource-shape\", `Resource '${name}' needs its column ('field').`);\n\t}\n}\n\nfunction checkResources(ctx: Context): void {\n\tconst { def, kind, resources, dimensions, carrier, carrierCode, columnAt, error } = ctx;\n\tif (!isRecord(def.resources) || resources.length === 0) {\n\t\terror([\"resources\"], \"resources\", \"A register needs at least one resource.\");\n\t}\n\tif (resources.length > MAX_RESOURCES) {\n\t\terror([\"resources\"], \"resources\", `At most ${MAX_RESOURCES} resources.`);\n\t}\n\tconst takesAbsentAs = kind === \"info\" && !!safeTrim(def.recorder) && def.recordChangesOnly === true;\n\tconst storage = new Map<string, string>();\n\tfor (const { name, def: resource } of resources) {\n\t\tconst path = (key?: string): IssuePath => (key ? [\"resources\", name, key] : [\"resources\", name]);\n\t\tif (!REGISTER_COLUMN_NAME.test(name)) {\n\t\t\terror(path(), \"resource-name\", `Resource '${name}' isn't a name (lowercase letters, digits and '_').`);\n\t\t}\n\t\tcheckResourceShape(ctx, name, resource, path);\n\t\tfor (const key of [\"field\", \"debitField\", \"creditField\"] as const) {\n\t\t\tconst column = safeTrim(resource[key]);\n\t\t\tif (!column) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcolumnAt(path(key), carrier, carrierCode, column, `Resource '${name}' column`);\n\t\t\tconst other = storage.get(column);\n\t\t\tif (other && other !== name) {\n\t\t\t\terror(path(key), \"resource-column-shared\", `Resources '${other}' and '${name}' both store into '${column}' — posting would add both into one column.`);\n\t\t\t}\n\t\t\tstorage.set(column, name);\n\t\t}\n\t\tconst field = safeTrim(resource.field);\n\t\tif (field && dimensions.includes(field)) {\n\t\t\terror(path(\"field\"), \"resource-is-dimension\", `'${field}' is both a dimension and resource '${name}'.`);\n\t\t}\n\t\tif (hasKey(resource, \"absentAs\") && !takesAbsentAs) {\n\t\t\terror(path(\"absentAs\"), \"absent-as\", `Resource '${name}': 'absentAs' needs 'recordChangesOnly' on a subordinate information register.`);\n\t\t}\n\t}\n}\n\nfunction checkAnalyticSlotColumns(ctx: Context, key: string, columns: string[]): void {\n\tcolumns.forEach((column, i) => ctx.columnAt([key, i], ctx.journal, ctx.journalCode, column, \"Analytic slot column\"));\n}\n\nfunction checkSideDimensions(ctx: Context): void {\n\tconst { dimensions, sides, journal, journalCode, columnAt, error } = ctx;\n\tfor (const [name, side] of Object.entries(sides)) {\n\t\tconst base: IssuePath = [\"sideDimensions\", name];\n\t\tif (!dimensions.includes(name)) {\n\t\t\terror(base, \"side-dimension\", `Two-sided dimension '${name}' must also be listed in 'dimensions'.`);\n\t\t}\n\t\tconst sideDef = isRecord(side) ? side : {};\n\t\tconst debitField = safeTrim(sideDef.debitField);\n\t\tconst creditField = safeTrim(sideDef.creditField);\n\t\tcheckColumnPair(error, \"side-dimension\", debitField, creditField, {\n\t\t\tmissing: base,\n\t\t\tmissingMessage: `Two-sided dimension '${name}' needs 'debitField' and 'creditField'.`,\n\t\t\tsame: [...base, \"creditField\"],\n\t\t\tsameMessage: `Two-sided dimension '${name}': the two columns must differ.`,\n\t\t});\n\t\tcolumnAt([...base, \"debitField\"], journal, journalCode, debitField, `Dimension '${name}' debit column`);\n\t\tcolumnAt([...base, \"creditField\"], journal, journalCode, creditField, `Dimension '${name}' credit column`);\n\t}\n}\n\n/** Returns whether the register declares analytic slots. */\nfunction checkCompoundAccounts(ctx: Context): boolean {\n\tconst { def, journal, journalCode, columnAt, error } = ctx;\n\trefuseKeys(def, [\"account\", \"analyticFields\"], error, \"line-mode\", (key) => `'${key}' belongs to a classic register; a compound one names 'debitAccount' and 'creditAccount'.`);\n\tconst debit = safeTrim(def.debitAccount);\n\tconst credit = safeTrim(def.creditAccount);\n\tcheckColumnPair(error, \"accounts\", debit, credit, {\n\t\tmissing: [\"debitAccount\"],\n\t\tmissingMessage: \"A compound register needs 'debitAccount' and 'creditAccount'.\",\n\t\tsame: [\"creditAccount\"],\n\t\tsameMessage: \"'debitAccount' and 'creditAccount' must differ — one column would record no correspondence.\",\n\t});\n\tcolumnAt([\"debitAccount\"], journal, journalCode, debit, \"Debit account column\");\n\tcolumnAt([\"creditAccount\"], journal, journalCode, credit, \"Credit account column\");\n\n\tconst debitSlots = stringList(def.debitAnalyticFields);\n\tconst creditSlots = stringList(def.creditAnalyticFields);\n\tif (debitSlots.length > MAX_ANALYTIC_SLOTS || creditSlots.length > MAX_ANALYTIC_SLOTS) {\n\t\terror([\"debitAnalyticFields\"], \"analytic-slots\", `At most ${MAX_ANALYTIC_SLOTS} analytic slots per side.`);\n\t}\n\tconst shared = debitSlots.filter((column) => creditSlots.includes(column));\n\tif (shared.length > 0) {\n\t\terror([\"creditAnalyticFields\"], \"analytic-slots\", `Analytic slot column on both sides: ${shared.join(\", \")}.`);\n\t}\n\tcheckAnalyticSlotColumns(ctx, \"debitAnalyticFields\", debitSlots);\n\tcheckAnalyticSlotColumns(ctx, \"creditAnalyticFields\", creditSlots);\n\tconst needsComposition = debitSlots.length > 0 || creditSlots.length > 0;\n\tcheckSideDimensions(ctx);\n\treturn needsComposition;\n}\n\n/** Returns whether the register declares analytic slots. */\nfunction checkClassicAccounts(ctx: Context): boolean {\n\tconst { def, journal, journalCode, columnAt, error } = ctx;\n\trefuseKeys(def, [\"debitAccount\", \"creditAccount\", \"debitAnalyticFields\", \"creditAnalyticFields\", \"sideDimensions\"], error, \"line-mode\", (key) => `'${key}' belongs to a compound register.`);\n\tif (!safeTrim(def.account)) {\n\t\terror([\"account\"], \"accounts\", \"A classic register names the posting's account column ('account').\");\n\t}\n\tcolumnAt([\"account\"], journal, journalCode, safeTrim(def.account), \"Account column\");\n\tconst slots = stringList(def.analyticFields);\n\tif (slots.length > MAX_ANALYTIC_SLOTS) {\n\t\terror([\"analyticFields\"], \"analytic-slots\", `At most ${MAX_ANALYTIC_SLOTS} analytic slots.`);\n\t}\n\tcheckAnalyticSlotColumns(ctx, \"analyticFields\", slots);\n\treturn slots.length > 0;\n}\n\nfunction checkComposition(ctx: Context, needsComposition: boolean): void {\n\tconst { def, entityAt, columnAt, error } = ctx;\n\tconst composition = isRecord(def.composition) ? def.composition : undefined;\n\tif (needsComposition && !composition) {\n\t\terror([\"composition\"], \"composition\", \"Analytic slots need a 'composition' — where the chart says which analytic each account takes in which slot.\");\n\t}\n\tif (!needsComposition && composition) {\n\t\terror([\"composition\"], \"composition\", \"'composition' is for a register with analytic slots; this one declares none.\");\n\t}\n\tif (!composition) {\n\t\treturn;\n\t}\n\tfor (const key of [\"entity\", \"account\", \"slot\", \"analytic\"]) {\n\t\tif (!safeTrim(composition[key])) {\n\t\t\terror([\"composition\", key], \"composition\", `'composition' needs '${key}'.`);\n\t\t}\n\t}\n\tconst code = safeTrim(composition.entity);\n\tconst entity = entityAt([\"composition\", \"entity\"], code, \"Composition entity\");\n\tfor (const key of [\"account\", \"slot\", \"analytic\"]) {\n\t\tcolumnAt([\"composition\", key], entity, code, composition[key], `Composition '${key}' column`);\n\t}\n}\n\nfunction checkAccounting(ctx: Context): void {\n\tconst { def, kind, error } = ctx;\n\tif (kind !== \"accounting\") {\n\t\treturn;\n\t}\n\tconst mode = safeTrim(def.lineMode);\n\tif (mode && mode !== \"classic\" && mode !== \"compound\") {\n\t\terror([\"lineMode\"], \"line-mode\", \"'lineMode' is 'classic' or 'compound'.\");\n\t}\n\tcheckComposition(ctx, mode === \"compound\" ? checkCompoundAccounts(ctx) : checkClassicAccounts(ctx));\n}\n\nfunction checkTotals(ctx: Context): void {\n\tconst { def, kind, dimensions, resources, entityAt, columnAt, error } = ctx;\n\tconst totals = isRecord(def.totals) ? def.totals : undefined;\n\tif (!totals || kind === \"info\") {\n\t\treturn;\n\t}\n\tconst totalsCode = safeTrim(totals.entity);\n\tif (!totalsCode) {\n\t\terror([\"totals\", \"entity\"], \"totals\", \"The totals cache needs its entity.\");\n\t}\n\tconst totalsEntity = entityAt([\"totals\", \"entity\"], totalsCode, \"Totals entity\");\n\tconst period = safeTrim(totals.period);\n\tif (!period || !(REGISTER_TOTALS_PERIODS as readonly string[]).includes(period)) {\n\t\terror([\"totals\", \"period\"], \"totals\", \"The totals cache needs a period: day, month, quarter or year.\");\n\t}\n\tif (!safeTrim(totals.periodField)) {\n\t\terror([\"totals\", \"periodField\"], \"totals\", \"The totals cache needs 'periodField' — the column holding each period's first date.\");\n\t}\n\tcolumnAt([\"totals\", \"periodField\"], totalsEntity, totalsCode, safeTrim(totals.periodField), \"Totals period column\");\n\tif (kind === \"accounting\") {\n\t\tif (!safeTrim(totals.accountField)) {\n\t\t\terror([\"totals\", \"accountField\"], \"totals\", \"An accounting register's cache needs 'accountField'.\");\n\t\t}\n\t\tcolumnAt([\"totals\", \"accountField\"], totalsEntity, totalsCode, safeTrim(totals.accountField), \"Totals account column\");\n\t\tstringList(totals.dimensions).forEach((name, i) => {\n\t\t\tif (!dimensions.includes(name)) {\n\t\t\t\terror([\"totals\", \"dimensions\", i], \"totals\", `Totals dimension '${name}' isn't a dimension of the register.`);\n\t\t\t}\n\t\t});\n\t} else {\n\t\tif (hasKey(totals, \"accountField\")) {\n\t\t\terror([\"totals\", \"accountField\"], \"totals\", \"'accountField' belongs to an accounting register's cache.\");\n\t\t}\n\t\tif (hasKey(totals, \"dimensions\")) {\n\t\t\terror([\"totals\", \"dimensions\"], \"totals\", \"A cache grouped by fewer dimensions is for an accounting register only.\");\n\t\t}\n\t}\n\tconst cached = isRecord(totals.resources) ? totals.resources : {};\n\tconst pair = kind === \"accounting\" ? [\"debitField\", \"creditField\"] : [\"inField\", \"outField\"];\n\tfor (const { name } of resources) {\n\t\tconst entry = cached[name];\n\t\tif (!isRecord(entry)) {\n\t\t\terror([\"totals\", \"resources\"], \"totals\", `The totals cache must cover every resource — '${name}' is missing.`);\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const key of pair) {\n\t\t\tif (!safeTrim(entry[key])) {\n\t\t\t\terror([\"totals\", \"resources\", name], \"totals\", `Totals resource '${name}' needs '${key}'.`);\n\t\t\t}\n\t\t\tcolumnAt([\"totals\", \"resources\", name, key], totalsEntity, totalsCode, safeTrim(entry[key]), `Totals '${name}' ${key}`);\n\t\t}\n\t}\n\tfor (const name of Object.keys(cached)) {\n\t\tif (!resources.some((resource) => resource.name === name)) {\n\t\t\terror([\"totals\", \"resources\", name], \"totals\", `Totals cache names '${name}', which isn't a resource of the register.`);\n\t\t}\n\t}\n}\n\nfunction checkWarnings({ def, kind, journalCode, warning }: Context): void {\n\tif (!safeTrim(def.label)) {\n\t\twarning([\"label\"], \"label\", \"No label — the query builder shows the code.\");\n\t}\n\tif (kind === \"balance\") {\n\t\tif (!journalCode) {\n\t\t\twarning([\"journal\"], \"no-journal\", \"No journal: unposting has to recompute from the document, and as-of-date reads are refused.\");\n\t\t} else if (!safeTrim(def.journalDirection) || !safeTrim(def.journalDate)) {\n\t\t\twarning([\"journal\"], \"journal-stamps\", \"Declare both journal stamps (direction and date) from the start — rows journalled without them can't be backfilled.\");\n\t\t}\n\t}\n}\n\n/**\n * Check one declaration against the schema's per-kind rules and the installer's\n * — paths relative to the declaration. With a lookup, the entities it names are\n * checked to exist and the columns it names to be stored on them.\n */\nexport function checkRegister(raw: unknown, lookup?: RegisterEntityLookup): RegisterIssue[] {\n\tconst out: RegisterIssue[] = [];\n\tconst error = createIssueReporter(out, \"registers\", \"error\");\n\tconst warning = createIssueReporter(out, \"registers\", \"warning\");\n\tif (!isRecord(raw)) {\n\t\terror([], \"shape\", \"A register declaration is a JSON object.\");\n\t\treturn out;\n\t}\n\tconst def = raw;\n\tif (!isRegisterKind(def.kind)) {\n\t\terror([\"kind\"], \"kind\", \"Pick a kind: balance, turnover, info or accounting.\");\n\t\treturn out;\n\t}\n\tconst kind = def.kind;\n\tcheckProperties({ def, kind, error });\n\tconst checks = createEntityChecks(lookup, error, warning);\n\tconst ctx: Context = {\n\t\tdef, kind, error, warning, ...checks,\n\t\t...resolveEntities(def, kind, checks),\n\t\tdimensions: registerDimensions(def),\n\t\tsides: isRecord(def.sideDimensions) ? def.sideDimensions : {},\n\t\tresources: registerResources(def),\n\t};\n\tcheckDimensions(ctx);\n\tcheckResources(ctx);\n\tcheckAccounting(ctx);\n\tcheckTotals(ctx);\n\tcheckWarnings(ctx);\n\treturn out;\n}\n\n/**\n * Check a whole `registers.json`: every declaration (paths prefixed by its\n * code), plus the codes themselves and the file's limits.\n */\nexport function checkRegisters(file: unknown, lookup?: RegisterEntityLookup): RegisterIssue[] {\n\tconst out: RegisterIssue[] = [];\n\tconst error = createIssueReporter(out, \"registers\", \"error\");\n\tif (!isRecord(file)) {\n\t\terror([], \"shape\", \"registers.json is an object of register declarations.\");\n\t\treturn out;\n\t}\n\tconst codes = Object.keys(file).filter((code) => !code.startsWith(\"$\"));\n\tif (codes.length === 0) {\n\t\terror([], \"empty\", \"A module with no registers has no registers.json — the file needs at least one.\");\n\t}\n\tif (codes.length > MAX_REGISTERS) {\n\t\terror([], \"limit\", `A module declares at most ${MAX_REGISTERS} registers.`);\n\t}\n\tfor (const code of codes) {\n\t\tif (!REGISTER_CODE.test(code)) {\n\t\t\terror([code], \"code\", `'${code}': lowercase letters, digits and '_', starting with a letter — a binding names it as module.code.`);\n\t\t}\n\t\tfor (const issue of checkRegister(file[code], lookup)) {\n\t\t\tout.push({ ...issue, path: [code, ...issue.path] });\n\t\t}\n\t}\n\treturn out;\n}\n","// Checking an A / L column's `params` against the register it binds.\n\nimport { createIssueReporter } from \"./issues\";\nimport type { RegisterIssue, RegisterReporter } from \"./issues\";\nimport { hasKey, isRecord, registerResourceShape, safeTrim, stringList } from \"./read\";\nimport type { Raw } from \"./read\";\nimport type { RegisterDef } from \"./types\";\n\n/** What a binding is checked against: the parts of a declaration it has to agree with. */\nexport type RegisterBindingTarget = Pick<RegisterDef, \"kind\" | \"dimensions\" | \"resources\" | \"recorder\" | \"lineMode\" | \"sideDimensions\">;\n\n/** A reference path (`ref.column`), an expression (`=…`) or a quoted constant — not a plain column. */\nexport const isPlainColumnRef = (value: string): boolean => !value.startsWith(\"=\") && !value.startsWith(\"'\") && !value.includes(\".\");\n\ninterface BindingContext {\n\tparams: Raw;\n\tregister: RegisterBindingTarget;\n\tqualified: string;\n\tledger: boolean;\n\terror: RegisterReporter;\n}\n\nfunction checkPosting(params: Raw, ledger: boolean, error: RegisterReporter): void {\n\tconst lineEntity = safeTrim(params.lineEntity);\n\tconst locked = stringList(params.lockedFields);\n\tconst lockedLines = stringList(params.lockedLineFields);\n\n\tif (!Array.isArray(params.lockedFields) || locked.length === 0) {\n\t\terror([\"lockedFields\"], \"register-locked-fields\", \"'lockedFields' is required: the document columns frozen while posted (or [\\\"*\\\"] for the whole row).\");\n\t}\n\tif (lineEntity) {\n\t\tif (!safeTrim(params.lineField)) {\n\t\t\terror([\"lineField\"], \"register-line\", \"A line posting needs 'lineField' — the line's key to the document.\");\n\t\t}\n\t\tif (!Array.isArray(params.lockedLineFields) || lockedLines.length === 0) {\n\t\t\terror([\"lockedLineFields\"], \"register-locked-fields\", \"A line posting needs 'lockedLineFields' (or [\\\"*\\\"]).\");\n\t\t}\n\t} else {\n\t\tif (hasKey(params, \"lineField\")) {\n\t\t\terror([\"lineField\"], \"register-line\", \"'lineField' needs a 'lineEntity'.\");\n\t\t}\n\t\tif (hasKey(params, \"lockedLineFields\")) {\n\t\t\terror([\"lockedLineFields\"], \"register-line\", \"'lockedLineFields' is for a line posting — name the 'lineEntity'.\");\n\t\t}\n\t\tif (hasKey(params, \"headerMap\")) {\n\t\t\terror([\"headerMap\"], \"register-header-map\", \"'headerMap' is for a binding that posts from lines; a document posting reads every value from the document already.\");\n\t\t}\n\t\tif (hasKey(params, \"groupLinesBy\")) {\n\t\t\terror([\"groupLinesBy\"], \"register-group-lines\", \"'groupLinesBy' needs lines to group — name the 'lineEntity'.\");\n\t\t}\n\t}\n\tif (ledger) {\n\t\tif (!lineEntity) {\n\t\t\terror([\"lineEntity\"], \"register-line\", \"An L column posts lines: 'lineEntity' and 'lineField' are required.\");\n\t\t}\n\t\tfor (const key of [\"headerMap\", \"skipPostingWhenEmpty\", \"dateTrunc\", \"groupLinesBy\"]) {\n\t\t\tif (hasKey(params, key)) {\n\t\t\t\terror([key], \"register-ledger-only\", `'${key}' is read by A columns only.`);\n\t\t\t}\n\t\t}\n\t}\n\tconst dateField = safeTrim(params.dateField);\n\tif (hasKey(params, \"dateTrunc\") && !dateField) {\n\t\terror([\"dateTrunc\"], \"register-date\", \"'dateTrunc' needs a 'dateField'.\");\n\t}\n\tif (dateField && !locked.includes(\"*\") && isPlainColumnRef(dateField) && !locked.includes(dateField)) {\n\t\terror([\"dateField\"], \"register-date-unlocked\", `'${dateField}' must be in 'lockedFields': a posted document re-dated would leave what it posted under the old date.`);\n\t}\n}\n\nfunction checkInfoBinding({ params, register, qualified, error }: BindingContext): void {\n\tif (!safeTrim(register.recorder)) {\n\t\terror([\"register\"], \"register-info-independent\", `\"${qualified}\" is an independent information register (no 'recorder'): its records are written by data operations, not by posting — unposting could never find them.`);\n\t}\n\tfor (const key of [\"direction\", \"sign\"]) {\n\t\tif (hasKey(params, key)) {\n\t\t\terror([key], \"register-info-direction\", `'${key}' is refused on a binding to an information register, which records values rather than quantities.`);\n\t\t}\n\t}\n}\n\nfunction checkLedgerAccounts({ params, register, error }: BindingContext): void {\n\tif (register.lineMode === \"compound\") {\n\t\tif (hasKey(params, \"accountField\")) {\n\t\t\terror([\"accountField\"], \"register-accounts\", \"A compound register takes 'debitAccountField' and 'creditAccountField', not 'accountField'.\");\n\t\t}\n\t\tif (!safeTrim(params.debitAccountField) || !safeTrim(params.creditAccountField)) {\n\t\t\terror([\"register\"], \"register-accounts\", \"A binding to a compound register names 'debitAccountField' and 'creditAccountField'.\");\n\t\t}\n\t\treturn;\n\t}\n\tif (!safeTrim(params.accountField)) {\n\t\terror([\"register\"], \"register-accounts\", \"A binding to a classic register names the line's 'accountField'.\");\n\t}\n\tfor (const key of [\"debitAccountField\", \"creditAccountField\"]) {\n\t\tif (hasKey(params, key)) {\n\t\t\terror([key], \"register-accounts\", `'${key}' belongs to a compound register; a classic one takes 'accountField'.`);\n\t\t}\n\t}\n}\n\n/** A ledger `map` entry takes a column per side where the register keeps one per side. */\nfunction checkLedgerMapValue(ctx: BindingContext, key: string, value: unknown, resources: Raw): void {\n\tconst { register, error } = ctx;\n\tconst resource = resources[key];\n\tconst split = key in resources\n\t\t? isRecord(resource) && registerResourceShape(resource) === \"split\"\n\t\t: isRecord(register.sideDimensions) && key in register.sideDimensions;\n\tif (split) {\n\t\tif (!isRecord(value) || !safeTrim(value.debit) || !safeTrim(value.credit)) {\n\t\t\terror([\"map\", key], \"register-map-value\", `\"${key}\" has a column per side; map both 'debit' and 'credit' to line columns.`);\n\t\t}\n\t} else if (!safeTrim(value)) {\n\t\terror([\"map\", key], \"register-map-value\", `\"${key}\" is shared between the sides; map it to one line column.`);\n\t}\n}\n\n/** Returns the names the binding maps, from `map` and `headerMap` alike. */\nfunction checkMaps(ctx: BindingContext, dimensions: string[], resources: Raw): Set<string> {\n\tconst { params, qualified, ledger, register, error } = ctx;\n\tconst known = new Set([...dimensions, ...Object.keys(resources)]);\n\tconst mapped = new Set<string>();\n\tfor (const mapKey of [\"map\", \"headerMap\"] as const) {\n\t\tconst map = isRecord(params[mapKey]) ? params[mapKey] : null;\n\t\tif (!map) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const key of Object.keys(map)) {\n\t\t\tif (mapped.has(key)) {\n\t\t\t\terror([mapKey, key], \"register-map-key\", `\"${key}\" is in both 'map' and 'headerMap' — one value cannot come from two places.`);\n\t\t\t}\n\t\t\tmapped.add(key);\n\t\t\tif (!known.has(key)) {\n\t\t\t\terror([mapKey, key], \"register-map-key\", `\"${key}\" is neither a dimension nor a resource of ${qualified} (dimensions: ${dimensions.join(\", \") || \"none\"}; resources: ${Object.keys(resources).join(\", \") || \"none\"}).`);\n\t\t\t} else if (ledger && mapKey === \"map\") {\n\t\t\t\tcheckLedgerMapValue(ctx, key, map[key], resources);\n\t\t\t} else if (mapKey === \"headerMap\" && key in resources && register.kind !== \"info\") {\n\t\t\t\terror([mapKey, key], \"register-header-map\", `A resource (\"${key}\") can't come from the document on a line posting — it would be added once per line. Only an information register's records take it.`);\n\t\t\t}\n\t\t}\n\t}\n\treturn mapped;\n}\n\nfunction checkMapping(ctx: BindingContext): void {\n\tconst { params, register, qualified, error } = ctx;\n\tconst dimensions = stringList(register.dimensions);\n\tconst resources: Raw = isRecord(register.resources) ? register.resources : {};\n\tconst mapped = checkMaps(ctx, dimensions, resources);\n\n\tconst unmapped = Object.entries(resources)\n\t\t.filter(([resource, def]) => !mapped.has(resource) && !(isRecord(def) && def.required === false))\n\t\t.map(([resource]) => resource);\n\tif (unmapped.length > 0) {\n\t\terror(\n\t\t\t[hasKey(params, \"map\") ? \"map\" : \"register\"],\n\t\t\t\"register-unmapped-resource\",\n\t\t\t`Every required resource of ${qualified} must be mapped — missing: ${unmapped.join(\", \")}. A writer that omitted one would post a silent zero.`,\n\t\t);\n\t}\n\tstringList(params.skipPostingWhenEmpty).forEach((name, i) => {\n\t\tif (!dimensions.includes(name)) {\n\t\t\terror([\"skipPostingWhenEmpty\", i], \"register-skip\", `\"${name}\" isn't a dimension of ${qualified}; 'skipPostingWhenEmpty' names register dimensions.`);\n\t\t}\n\t});\n}\n\n/**\n * Check an A / L column's `params` against the register it binds — paths\n * relative to `params`. `register` is the resolved declaration, `undefined`\n * when the module named declares no such register, `null` when that module\n * can't be seen (every rule that needs the declaration then stands down).\n */\nexport function checkRegisterBinding(\n\tcolumnType: \"A\" | \"L\",\n\trawParams: unknown,\n\tregister: RegisterBindingTarget | null | undefined,\n\toptions: { moduleCode?: string } = {},\n): RegisterIssue[] {\n\tconst out: RegisterIssue[] = [];\n\tconst error = createIssueReporter(out, \"entity\", \"error\");\n\tif (!isRecord(rawParams)) {\n\t\treturn out;\n\t}\n\tconst params = rawParams;\n\tconst qualified = safeTrim(params.register);\n\tif (!qualified) {\n\t\treturn out;\n\t}\n\tconst ledger = columnType === \"L\";\n\n\tif (hasKey(params, \"registries\")) {\n\t\terror([\"registries\"], \"register-and-inline\", \"A column binds to a register OR restates one inline in 'registries' — keep one.\");\n\t}\n\tif (!qualified.includes(\".\")) {\n\t\terror([\"register\"], \"register-unqualified\", `A register is named as module.register, e.g. \"${options.moduleCode ?? \"module\"}.${qualified}\".`);\n\t\treturn out;\n\t}\n\tconst dot = qualified.indexOf(\".\");\n\tconst moduleCode = qualified.slice(0, dot);\n\tconst registerCode = qualified.slice(dot + 1);\n\n\t// Posting inputs are validated even when the target register cannot be resolved.\n\tcheckPosting(params, ledger, error);\n\n\tif (register === null) {\n\t\treturn out;\n\t}\n\tif (!register) {\n\t\terror([\"register\"], \"unknown-register\", `No register \"${qualified}\" — ${moduleCode}/registers.json doesn't declare \"${registerCode}\".`);\n\t\treturn out;\n\t}\n\tconst kind = register.kind;\n\tif (ledger && kind !== \"accounting\") {\n\t\terror([\"register\"], \"register-kind\", `An L column posts to an accounting register; \"${qualified}\" is a ${kind || \"?\"} register — use an A column.`);\n\t\treturn out;\n\t}\n\tif (!ledger && kind === \"accounting\") {\n\t\terror([\"register\"], \"register-kind\", `An A column posts to a balance, turnover or info register; \"${qualified}\" is an accounting register — use an L column.`);\n\t\treturn out;\n\t}\n\tconst ctx: BindingContext = { params, register, qualified, ledger, error };\n\tif (kind === \"info\") {\n\t\tcheckInfoBinding(ctx);\n\t}\n\t// No rule on a missing `direction`: the engine posts an increase by default\n\t// (gl's VAT binding ships without one).\n\tif (ledger) {\n\t\tcheckLedgerAccounts(ctx);\n\t}\n\tcheckMapping(ctx);\n\treturn out;\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, stored procedures, menus, folders,\n// roles, settings, registers, jobs, triggers, webhooks, print templates, seed data,\n// translations, actions, diagrams), mirrored from the JSON schemas under docs/schemas/;\n// • shared rules — the action-DSL analyzer (./dsl) and the register checks\n// (checkRegisters / checkRegisterBinding), run by editors and validators alike.\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\tAuditHistoryLevel,\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 {\n\tStoredProceduresFile,\n\tStoredProcedureDef,\n\tSpParamDef,\n\tSpColumnDef,\n} from \"./stored-procedures\";\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 { DiagramFile, DiagramDef, DiagramEntity, DiagramDetail, DiagramRelation } from \"./diagrams\";\nexport type { RolesFile, RoleDef } from \"./roles\";\nexport type {\n\tTranslationsFile,\n\tTranslatedAction,\n\tTranslatedEntity,\n\tTranslatedMenu,\n\tTranslatedOption,\n\tTranslatedReport,\n\tTranslatedWithOptions,\n} from \"./translations\";\nexport { settingDefsOf } from \"./settings\";\nexport type { SettingsFile, SettingDef, SettingConstraintDef, SettingBaseDatatype } from \"./settings\";\nexport {\n\tREGISTER_KINDS,\n\tREGISTER_CODE,\n\tREGISTER_COLUMN_NAME,\n\tREGISTER_TOTALS_PERIODS,\n\tisRegisterKind,\n\tisPlainColumnRef,\n\tregisterDimensions,\n\tregisterResources,\n\tregisterCarrier,\n\tregisterResourceShape,\n\tregisterLineMode,\n\tregisterRefusedProps,\n\tregisterRequiredProps,\n\tcheckRegister,\n\tcheckRegisters,\n\tcheckRegisterBinding,\n\ttype RegisterKind,\n\ttype RegisterDef,\n\ttype RegistersFile,\n\ttype RegisterResourceDef,\n\ttype RegisterTotalsDef,\n\ttype RegisterTotalsPeriod,\n\ttype RegisterCompositionDef,\n\ttype RegisterBindingParams,\n\ttype RegisterMapValue,\n\ttype RegisterBindingTarget,\n\ttype RegisterIssue,\n\ttype RegisterEntityInfo,\n\ttype RegisterEntityLookup,\n} from \"./registers\";\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, SeedDataRepeat } 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":";AAOO,IAAM,UAAU;AAAA,EACtB,KAAK;AAAA,EACL,OAAO;AAAA,EACP,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AACR;AAMO,IAAM,gBAAoC,OAAO,OAAO,OAAO;;;ACR/D,IAAM,aAAa;AAAA,EACzB,EAAE,IAAI,QAAQ,MAAM,QAAQ,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EAClJ,EAAE,IAAI,UAAU,MAAM,eAAe,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EAC3J,EAAE,IAAI,SAAS,MAAM,SAAS,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM,WAAW,EAAE,SAAS,iCAAiC,EAAE;AAAA,EAC9M,EAAE,IAAI,SAAS,MAAM,SAAS,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM,WAAW,EAAE,SAAS,wBAAwB,EAAE;AAAA,EACrM,EAAE,IAAI,OAAO,MAAM,OAAO,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM,WAAW,EAAE,SAAS,gBAAgB,cAAc,IAAI,EAAE;AAAA,EAC3M,EAAE,IAAI,YAAY,MAAM,kBAAkB,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EAChK,EAAE,IAAI,UAAU,MAAM,UAAU,YAAY,KAAK,gBAAgB,UAAU,cAAc,QAAQ,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACzJ,EAAE,IAAI,YAAY,MAAM,YAAY,YAAY,KAAK,gBAAgB,UAAU,cAAc,QAAQ,QAAQ,MAAM,WAAW,GAAG,mBAAmB,MAAM;AAAA,EAC1J,EAAE,IAAI,WAAW,MAAM,WAAW,YAAY,KAAK,gBAAgB,UAAU,cAAc,OAAO,QAAQ,MAAM,WAAW,GAAG,mBAAmB,MAAM;AAAA,EACvJ,EAAE,IAAI,YAAY,MAAM,YAAY,YAAY,KAAK,gBAAgB,QAAQ,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACxJ,EAAE,IAAI,QAAQ,MAAM,QAAQ,YAAY,KAAK,gBAAgB,QAAQ,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACjJ,EAAE,IAAI,YAAY,MAAM,eAAe,YAAY,KAAK,gBAAgB,aAAa,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACjK,EAAE,IAAI,QAAQ,MAAM,QAAQ,YAAY,KAAK,gBAAgB,QAAQ,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACjJ,EAAE,IAAI,YAAY,MAAM,YAAY,YAAY,KAAK,gBAAgB,UAAU,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EACzJ,EAAE,IAAI,SAAS,MAAM,SAAS,YAAY,KAAK,gBAAgB,UAAU,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM,WAAW,EAAE,OAAO,UAAU,EAAE;AAAA,EACpL,EAAE,IAAI,QAAQ,MAAM,eAAe,YAAY,KAAK,gBAAgB,QAAQ,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACxJ,EAAE,IAAI,UAAU,MAAM,UAAU,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACvJ,EAAE,IAAI,SAAS,MAAM,gBAAgB,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,IAAI,WAAW,MAAM,mBAAmB,MAAM;AAAA,EAC1J,EAAE,IAAI,SAAS,MAAM,SAAS,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACrJ,EAAE,IAAI,QAAQ,MAAM,QAAQ,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACnJ,EAAE,IAAI,YAAY,MAAM,YAAY,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EAC1J,EAAE,IAAI,YAAY,MAAM,oBAAoB,YAAY,KAAK,gBAAgB,UAAU,cAAc,MAAM,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EAClK,EAAE,IAAI,UAAU,MAAM,UAAU,YAAY,KAAK,gBAAgB,QAAQ,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EACnJ,EAAE,IAAI,QAAQ,MAAM,eAAe,YAAY,KAAK,gBAAgB,QAAQ,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACvJ,EAAE,IAAI,QAAQ,MAAM,eAAe,YAAY,KAAK,gBAAgB,OAAO,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACtJ,EAAE,IAAI,QAAQ,MAAM,eAAe,YAAY,KAAK,gBAAgB,OAAO,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EACtJ,EAAE,IAAI,QAAQ,MAAM,QAAQ,YAAY,KAAK,gBAAgB,UAAU,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,KAAK;AAAA,EACjJ,EAAE,IAAI,cAAc,MAAM,eAAe,YAAY,KAAK,gBAAgB,QAAQ,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAAA,EAC7J,EAAE,IAAI,QAAQ,MAAM,eAAe,YAAY,KAAK,gBAAgB,UAAU,cAAc,KAAK,QAAQ,MAAM,WAAW,MAAM,mBAAmB,MAAM;AAC1J;AAUA,IAAM,OAAO,IAAI,IAA0B,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AAGvE,IAAM,eAAoC,IAAI,IAAI,WAAW,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;AAG/E,SAAS,cAAc,IAA2C;AACxE,SAAO,MAAM,QAAQ,aAAa,IAAI,EAAE;AACzC;AAGO,SAAS,aAAa,IAAsC;AAClE,SAAO,KAAK,IAAI,EAAE;AACnB;AAGO,SAAS,uBAAuB,YAA6C;AACnF,SAAO,WAAW,OAAO,CAAC,OAAO,GAAG,eAAe,UAAU;AAC9D;;;AC7DO,IAAM,gBAA2C;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAIA,IAAM,aAAoD;AAAA,EACzD,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,KAAK;AAAA,EACL,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,WAAW;AAAA,EACX,KAAK;AACN;AAGO,SAAS,iBAAiB,MAA6B;AAC7D,SAAQ,WAA6C,IAAI,KAAK;AAC/D;;;AC9CO,IAAM,cAAwC;AAAA,EACpD,EAAE,IAAI,KAAK,MAAM,SAAS,aAAa,6CAA6C;AAAA,EACpF,EAAE,IAAI,KAAK,MAAM,aAAa,aAAa,iDAAiD;AAAA,EAC5F,EAAE,IAAI,KAAK,MAAM,cAAc,aAAa,yDAAyD;AAAA,EACrG,EAAE,IAAI,KAAK,MAAM,WAAW,aAAa,wDAAwD;AAAA,EACjG,EAAE,IAAI,KAAK,MAAM,aAAa,aAAa,+EAA+E;AAAA,EAC1H,EAAE,IAAI,KAAK,MAAM,yBAAyB,aAAa,gEAAgE;AAAA,EACvH,EAAE,IAAI,KAAK,MAAM,mBAAmB,aAAa,iEAAiE;AACnH;AAEA,IAAMA,QAAO,IAAI,IAA2B,YAAY,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;AAGzE,SAAS,cAAc,IAAuC;AACpE,SAAOA,MAAK,IAAI,EAAE;AACnB;;;ACXO,IAAM,SAA8B;AAAA,EAC1C;AAAA,IACC,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACP,eAAe,EAAE,YAAY,QAAQ,OAAO,KAAK,MAAM,MAAM,YAAY,MAAM,YAAY,OAAO,UAAU,GAAG,aAAa,cAAc;AAAA,IAC3I;AAAA,EACD;AAAA,EACA;AAAA,IACC,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACP,cAAc,EAAE,YAAY,eAAe,aAAa,YAAY,OAAO,KAAK,YAAY,OAAO,UAAU,MAAM,aAAa,gBAAgB,QAAQ,EAAE,eAAe,EAAE,OAAO,SAAS,IAAI,SAAS,EAAE,EAAE;AAAA,MAC5M,cAAc,EAAE,YAAY,eAAe,aAAa,YAAY,OAAO,KAAK,YAAY,OAAO,UAAU,MAAM,aAAa,gBAAgB,QAAQ,EAAE,eAAe,EAAE,OAAO,SAAS,IAAI,OAAO,EAAE,EAAE;AAAA,IAC3M;AAAA,EACD;AAAA,EACA;AAAA,IACC,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,UAAU,CAAC,OAAO;AAAA,IAClB,QAAQ;AAAA,MACP,YAAY,EAAE,YAAY,QAAQ,OAAO,KAAK,YAAY,OAAO,UAAU,MAAM,aAAa,sBAAsB,QAAQ,EAAE,eAAe,EAAE,OAAO,iBAAiB,IAAI,SAAS,EAAE,EAAE;AAAA,MACxL,iBAAiB,EAAE,YAAY,KAAK,aAAa,UAAU,OAAO,KAAK,UAAU,MAAM,aAAa,cAAc,MAAM,EAAE,QAAQ,QAAQ,SAAS,cAAc,UAAU,UAAU,EAAE;AAAA,MACvL,iBAAiB,EAAE,YAAY,QAAQ,OAAO,KAAK,YAAY,OAAO,UAAU,MAAM,aAAa,2BAA2B,QAAQ,EAAE,eAAe,EAAE,OAAO,iBAAiB,IAAI,OAAO,EAAE,EAAE;AAAA,MAChM,sBAAsB,EAAE,YAAY,KAAK,aAAa,UAAU,OAAO,KAAK,UAAU,MAAM,aAAa,mBAAmB,MAAM,EAAE,QAAQ,QAAQ,SAAS,mBAAmB,UAAU,UAAU,EAAE;AAAA,IACvM;AAAA,IACA,YAAY;AAAA,MACX,yBAAyB,EAAE,MAAM,EAAE,OAAO,aAAa,GAAG,IAAI,EAAE,QAAQ,QAAQ,OAAO,UAAU,EAAE;AAAA,MACnG,6BAA6B,EAAE,MAAM,EAAE,OAAO,kBAAkB,GAAG,IAAI,EAAE,QAAQ,QAAQ,OAAO,UAAU,EAAE;AAAA,IAC7G;AAAA,EACD;AAAA,EACA;AAAA,IACC,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACP,QAAQ,EAAE,YAAY,QAAQ,aAAa,YAAY,OAAO,IAAI,YAAY,OAAO,SAAS,QAAQ,UAAU,MAAM,aAAa,SAAS;AAAA,IAC7I;AAAA,EACD;AAAA,EACA;AAAA,IACC,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACP,WAAW,EAAE,YAAY,QAAQ,aAAa,UAAU,OAAO,MAAM,UAAU,MAAM,aAAa,QAAQ;AAAA,IAC3G;AAAA,EACD;AAAA,EACA,EAAE,IAAI,YAAY,aAAa,sFAAiF,QAAQ,CAAC,EAAE;AAAA,EAC3H,EAAE,IAAI,gBAAgB,aAAa,8EAAyE,QAAQ,CAAC,EAAE;AAAA,EACvH,EAAE,IAAI,UAAU,aAAa,4EAAuE,QAAQ,CAAC,EAAE;AAAA,EAC/G;AAAA,IACC,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACP,YAAY,EAAE,YAAY,WAAW,aAAa,QAAQ,OAAO,MAAM,MAAM,MAAM,YAAY,OAAO,QAAQ,IAAI,UAAU,GAAG,aAAa,aAAa;AAAA,MACzJ,aAAa,EAAE,YAAY,WAAW,aAAa,QAAQ,OAAO,MAAM,QAAQ,KAAK,UAAU,IAAI,aAAa,cAAc;AAAA,MAC9H,QAAQ,EAAE,YAAY,QAAQ,aAAa,YAAY,OAAO,MAAM,YAAY,OAAO,SAAS,SAAS,UAAU,IAAI,aAAa,SAAS;AAAA,MAC7I,YAAY,EAAE,YAAY,QAAQ,aAAa,QAAQ,OAAO,MAAM,UAAU,IAAI,aAAa,aAAa;AAAA,MAC5G,UAAU,EAAE,YAAY,QAAQ,aAAa,QAAQ,OAAO,MAAM,UAAU,IAAI,aAAa,WAAW;AAAA,IACzG;AAAA,EACD;AACD;AAEA,IAAMC,QAAO,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAG1C,SAAS,SAAS,IAAkC;AAC1D,SAAOA,MAAK,IAAI,EAAE;AACnB;AAGA,SAAS,gBAAgB,QAAwB;AAChD,QAAM,QAAQ,OAAO,MAAM,GAAG,EAAE,IAAI,KAAK;AACzC,SAAO,MACL,MAAM,QAAQ,EACd,OAAO,OAAO,EACd,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAC1D,KAAK,GAAG;AACX;AAEA,SAAS,cAAc,OAAe,QAAwB;AAC7D,QAAM,QAAQ,OAAO,MAAM,GAAG,EAAE,IAAI,KAAK;AACzC,SAAO,MAAM,QAAQ,eAAe,KAAK,EAAE,QAAQ,eAAe,gBAAgB,MAAM,CAAC;AAC1F;AAEA,SAAS,cAAc,KAAe,QAA0B;AAC/D,QAAM,OAAiB,EAAE,GAAG,IAAI;AAChC,MAAI,OAAO,KAAK,gBAAgB,SAAU,MAAK,cAAc,cAAc,KAAK,aAAa,MAAM;AACnG,SAAO;AACR;AAOA,SAAS,cAAwC;AAChD,SAAO,uBAAO,OAAO,IAAI;AAC1B;AAOA,SAAS,UAAU,KAAmC;AACrD,SAAO,IAAI,cAAc,IAAI,cAAc,IAAI;AAChD;AAgCA,SAAS,cAAc,MAAiB,KAAa,KAAqB;AACzE,QAAM,WAAW,KAAK,OAAO,GAAG;AAChC,MAAI,CAAC,OAAO,OAAO,KAAK,QAAQ,GAAG,GAAG;AACrC,SAAK,OAAO,GAAG,IAAI;AACnB,SAAK,OAAO,IAAI,KAAK,OAAO;AAC5B;AAAA,EACD;AACA,MAAI,KAAK,aAAa,CAAC,KAAK,UAAU,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,GAAG;AACnE,UAAM,eAAe,UAAU,QAAQ;AACvC,UAAM,YAAY,UAAU,GAAG;AAC/B,QAAI,iBAAiB,WAAW;AAC/B,WAAK,UAAU,KAAK;AAAA,QACnB,OAAO;AAAA,QACP,cAAc,KAAK,OAAO,IAAI,GAAG,KAAK;AAAA,QACtC;AAAA,QACA;AAAA,MACD,CAAC;AAAA,IACF;AAAA,EACD;AAGD;AAoBO,SAAS,YACf,SACA,YACA,aAC2B;AAC3B,QAAM,OAAkB,EAAE,QAAQ,YAAY,GAAG,QAAQ,oBAAI,IAAI,EAAE;AACnE,mBAAiB,SAAS,YAAY,aAAa,oBAAI,IAAI,GAAG,IAAI;AAClE,SAAO,KAAK;AACb;AAEA,SAAS,iBACR,SACA,YACA,aACA,MACA,MACO;AASP,MAAI,KAAK,IAAI,OAAO,EAAG;AACvB,OAAK,IAAI,OAAO;AAIhB,QAAM,QACL,eAAe,OAAO,OAAO,aAAa,OAAO,IAAI,YAAY,OAAO,IAAIA,MAAK,IAAI,OAAO;AAC7F,MAAI,OAAO;AACV,eAAW,YAAY,MAAM,YAAY,CAAC,GAAG;AAC5C,uBAAiB,UAAU,YAAY,aAAa,MAAM,IAAI;AAAA,IAC/D;AAKA,eAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,MAAM,UAAU,CAAC,CAAC,GAAG;AAC7D,oBAAc,MAAM,cAAc,MAAM,UAAU,GAAG,cAAc,KAAK,UAAU,CAAC;AAAA,IACpF;AAAA,EACD;AAEA,OAAK,OAAO,OAAO;AACpB;AAGO,SAAS,aACf,UACA,YACA,aAC2B;AAC3B,QAAM,OAAkB,EAAE,QAAQ,YAAY,GAAG,QAAQ,oBAAI,IAAI,EAAE;AACnE,aAAW,MAAM,UAAU;AAC1B,qBAAiB,IAAI,YAAY,aAAa,oBAAI,IAAI,GAAG,IAAI;AAAA,EAC9D;AACA,SAAO,KAAK;AACb;AAgBO,SAAS,oBACf,UACA,YACA,OAAwE,CAAC,GAClD;AACvB,QAAM,OAAkB,EAAE,QAAQ,YAAY,GAAG,QAAQ,oBAAI,IAAI,GAAG,WAAW,CAAC,EAAE;AAClF,aAAW,CAAC,KAAK,GAAG,KAAK,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,GAAG;AAC3D,SAAK,OAAO,GAAG,IAAI;AACnB,SAAK,OAAO,IAAI,KAAK,OAAO;AAAA,EAC7B;AACA,aAAW,MAAM,UAAU;AAC1B,qBAAiB,IAAI,YAAY,KAAK,aAAa,oBAAI,IAAI,GAAG,IAAI;AAAA,EACnE;AACA,SAAO,KAAK;AACb;;;AC3QO,IAAM,gBAAgB;AAG7B,IAAM,cAAc,oBAAI,IAAI,CAAC,YAAY,YAAY,YAAY,MAAM,CAAC;AAUxE,IAAM,uBAA+C;AAAA,EACpD,MAAM;AAAA,EACN,OAAO;AACR;AAcO,SAAS,iBAAiB,aAAqB,UAAyB,CAAC,GAAkB;AACjG,QAAM,KAAK,aAAa,WAAW;AACnC,MAAI,CAAC,GAAI,QAAO;AAIhB,MAAI,GAAG,eAAe,OAAO,GAAG,eAAe,IAAK,QAAO;AAI3D,QAAM,WAAW,qBAAqB,GAAG,EAAE;AAC3C,MAAI,SAAU,QAAO;AAErB,QAAM,OAAO,iBAAiB,GAAG,cAAc;AAC/C,MAAI,SAAS,KAAM,QAAO;AAE1B,MAAI,GAAG,mBAAmB,UAAU;AACnC,UAAM,YAAY,QAAQ,aAAa,GAAG;AAC1C,WAAO,aAAa,OAAO,WAAW,aAAa,IAAI,SAAS,MAAM;AAAA,EACvE;AAEA,MAAI,GAAG,mBAAmB,UAAU;AACnC,QAAI,YAAY,IAAI,GAAG,EAAE,EAAG,QAAO;AACnC,UAAM,SAAS,QAAQ,UAAU,GAAG;AACpC,WAAO,UAAU,OAAO,WAAW,MAAM,MAAM;AAAA,EAChD;AAEA,SAAO;AACR;AAGO,SAAS,cAAc,aAA0D;AACvF,QAAM,KAAK,aAAa,WAAW;AACnC,MAAI,CAAC,IAAI,UAAW,QAAO;AAC3B,SAAO,EAAE,GAAG,GAAG,UAAU;AAC1B;AAGO,SAAS,mBAAmB,aAAyC;AAC3E,SAAO,aAAa,WAAW,GAAG;AACnC;;;AC/DO,IAAM,mBAAmB;AAAA,EAC/B;AAAA,EAAK;AAAA,EAAM;AAAA,EAAM;AAAA,EACjB;AAAA,EAAK;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAK;AAAA,EAAQ;AAAA,EAAM;AAAA,EAC5C;AAAA,EACA;AAAA,EAAY;AAAA,EAAa;AAAA,EACzB;AAAA,EAAS;AAAA,EAAU;AAAA,EACnB;AAAA,EAAO;AAAA,EAAQ;AAAA,EACf;AAAA,EAAQ;AAAA,EAAS;AAAA,EACjB;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAC1B;AAAA,EAAM;AAAA,EAAO;AAAA,EACb;AAAA,EAAW;AAAA,EAAO;AAAA,EAAY;AAAA,EAC9B;AAAA,EAAK;AACN;AAKO,IAAM,yBAAyB,CAAC,OAAO,MAAM,QAAQ,QAAQ,OAAO,KAAK;;;ACzBzE,SAAS,SAAS,QAA6D;AACrF,QAAM,QAAkD,CAAC;AACzD,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,UAAU,CAAC,CAAC,GAAG;AAChE,QAAI,MAAM,eAAe,OAAO,MAAM,MAAM,QAAQ;AACnD,YAAM,KAAK,EAAE,OAAO,MAAM,QAAQ,MAAM,KAAK,OAAO,CAAC;AAAA,IACtD;AAAA,EACD;AACA,SAAO;AACR;AAOO,SAAS,gBAAgB,QAAmB,YAA8B;AAChF,MAAI,CAAC,OAAO,QAAQ,SAAS,UAAU,EAAG,QAAO,CAAC;AAClD,QAAM,WAAW,IAAI,IAAI,OAAO,KAAK,OAAO,UAAU,CAAC,CAAC,CAAC;AACzD,SAAO,OAAO,KAAK,YAAY,YAAY,UAAU,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,IAAI,GAAG,CAAC;AAC3F;AAMO,SAAS,eAAe,QAAmB,YAA8B;AAC/E,QAAM,WAAW,OAAO,QAAQ,OAAO,UAAU,CAAC,CAAC,EACjD,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,MAAM,IAAI,EAChC,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AACtB,QAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,QAAM,SAAS,CAAC,GAAG,QAAQ;AAC3B,aAAW,OAAO,gBAAgB,QAAQ,UAAU,GAAG;AACtD,QAAI,CAAC,KAAK,IAAI,GAAG,GAAG;AACnB,WAAK,IAAI,GAAG;AACZ,aAAO,KAAK,GAAG;AAAA,IAChB;AAAA,EACD;AACA,SAAO;AACR;;;ACxBO,IAAM,gBAAsC;AAAA,EAClD,EAAE,IAAI,QAAQ,MAAM,OAAO;AAAA,EAC3B,EAAE,IAAI,QAAQ,MAAM,OAAO;AAAA,EAC3B,EAAE,IAAI,UAAU,MAAM,SAAS;AAAA,EAC/B,EAAE,IAAI,YAAY,MAAM,WAAW;AAAA,EACnC,EAAE,IAAI,WAAW,MAAM,UAAU;AAAA,EACjC,EAAE,IAAI,aAAa,MAAM,YAAY;AAAA,EACrC,EAAE,IAAI,WAAW,MAAM,UAAU;AAAA,EACjC,EAAE,IAAI,WAAW,MAAM,UAAU;AAAA,EACjC,EAAE,IAAI,UAAU,MAAM,SAAS;AAChC;;;ACrBO,IAAM,WAAiC;AAAA,EAC7C,EAAE,IAAI,SAAS,MAAM,QAAQ;AAAA,EAC7B,EAAE,IAAI,SAAS,MAAM,QAAQ;AAAA,EAC7B,EAAE,IAAI,UAAU,MAAM,SAAS;AAAA,EAC/B,EAAE,IAAI,SAAS,MAAM,QAAQ;AAC9B;AAGO,IAAM,aAAmC;AAAA,EAC/C,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,QAAQ,MAAM,OAAO;AAAA,EAC3B,EAAE,IAAI,QAAQ,MAAM,OAAO;AAAA,EAC3B,EAAE,IAAI,OAAO,MAAM,MAAM;AAAA,EACzB,EAAE,IAAI,SAAS,MAAM,QAAQ;AAAA,EAC7B,EAAE,IAAI,WAAW,MAAM,UAAU;AAAA,EACjC,EAAE,IAAI,UAAU,MAAM,SAAS;AAAA,EAC/B,EAAE,IAAI,WAAW,MAAM,UAAU;AAAA,EACjC,EAAE,IAAI,UAAU,MAAM,SAAS;AAChC;;;ACfO,IAAM,mBAAmB;AAAA,EAC/B;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAY;AAAA,EAAW;AAAA,EAC/C;AAAA,EAAW;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAQ;AAAA,EAClD;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAC3B;AAAA,EAAQ;AAAA,EACR;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAa;AAAA,EACvC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAC1B;;;ACqCO,SAAS,cAAc,MAAgD;AAC7E,QAAM,OAAmC,CAAC;AAC1C,aAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,IAAI;AAC5C,QAAI,SAAS,kBAAkB,IAAK,MAAK,IAAI,IAAI;AAClD,SAAO;AACR;;;AC1DO,IAAM,iBAA0C,CAAC,WAAW,YAAY,QAAQ,YAAY;AAE5F,IAAM,iBAAiB,CAAC,UAC9B,OAAO,UAAU,YAAa,eAAqC,SAAS,KAAK;AAG3E,IAAM,gBAAgB;AAGtB,IAAM,uBAAuB;AAK7B,IAAM,0BAA2D,CAAC,OAAO,SAAS,WAAW,MAAM;;;ACbnG,IAAM,WAAW,CAAC,UACxB,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAEtD,IAAM,WAAW,CAAC,UAAwC,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAE9G,IAAM,aAAa,CAAC,UAC1B,MAAM,QAAQ,KAAK,IAAI,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,IAAI,CAAC;AAGrF,IAAM,SAAS,CAAC,KAAU,QAAyB,IAAI,GAAG,MAAM;AAGhE,IAAM,qBAAqB,CAAC,QAAqC,WAAW,IAAI,UAAU;AAG1F,IAAM,oBAAoB,CAAC,QACjC,CAAC,SAAS,IAAI,SAAS,IACpB,CAAC,IACD,OAAO,QAAQ,IAAI,SAAS,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,KAAK,SAAS,GAAG,IAAI,MAAM,CAAC,EAAE,EAAE;AAGzF,SAAS,gBAAgB,KAA4C;AAC3E,UAAQ,IAAI,MAAM;AAAA,IACjB,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,SAAS,IAAI,MAAM;AAAA,IAC3B,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,SAAS,IAAI,OAAO;AAAA,IAC5B;AACC,aAAO;AAAA,EACT;AACD;AAGO,IAAM,wBAAwB,CAAC,aACrC,gBAAgB,YAAY,iBAAiB,WAAW,UAAU;AAE5D,IAAM,mBAAmB,CAAC,QAChC,IAAI,aAAa,aAAa,aAAa;AAE5C,IAAM,kBAAkB,CAAC,UAAU,YAAY,mBAAmB;AAClE,IAAM,wBAAwB;AAAA,EAC7B;AAAA,EAAS;AAAA,EAAe;AAAA,EAAkB;AAAA,EAAY;AAAA,EACtD;AAAA,EAAgB;AAAA,EAAiB;AAAA,EAAuB;AAAA,EAAwB;AACjF;AACA,IAAM,qBAAqB,CAAC,iBAAiB,YAAY;AAGzD,IAAM,UAAmD;AAAA,EACxD,SAAS,CAAC,GAAG,iBAAiB,GAAG,qBAAqB;AAAA,EACtD,UAAU,CAAC,UAAU,GAAG,iBAAiB,GAAG,uBAAuB,GAAG,kBAAkB;AAAA,EACxF,MAAM,CAAC,WAAW,oBAAoB,eAAe,GAAG,uBAAuB,UAAU,GAAG,kBAAkB;AAAA,EAC9G,YAAY,CAAC,UAAU,GAAG,iBAAiB,oBAAoB,GAAG,kBAAkB;AACrF;AAGA,IAAM,WAAoD;AAAA,EACzD,SAAS,CAAC,QAAQ;AAAA,EAClB,UAAU,CAAC,WAAW,oBAAoB,aAAa;AAAA,EACvD,MAAM,CAAC,QAAQ;AAAA,EACf,YAAY,CAAC,SAAS,WAAW,eAAe,UAAU;AAC3D;AAEO,IAAM,uBAAuB,CAAC,SAA0C,QAAQ,IAAI;AACpF,IAAM,wBAAwB,CAAC,SAA0C,SAAS,IAAI;AAGtF,IAAM,aAAyE;AAAA,EACrF,SAAS,EAAE,UAAU,sBAAsB,QAAQ,qBAAqB;AAAA,EACxE,UAAU,EAAE,UAAU,uBAAuB,QAAQ,sBAAsB;AAAA,EAC3E,MAAM,EAAE,UAAU,2BAA2B,QAAQ,0BAA0B;AAAA,EAC/E,YAAY,EAAE,UAAU,0BAA0B,QAAQ,yBAAyB;AACpF;;;ACjDO,SAAS,mBACf,QACA,OACA,SACe;AACf,QAAM,WAAW,oBAAI,IAAoC;AACzD,QAAM,aAAa,oBAAI,QAAgD;AAEvE,QAAM,WAAqC,CAAC,MAAM,MAAM,SAAS;AAChE,QAAI,CAAC,QAAQ,CAAC,QAAQ;AACrB,aAAO;AAAA,IACR;AACA,QAAI,CAAC,SAAS,IAAI,IAAI,GAAG;AACxB,eAAS,IAAI,MAAM,OAAO,IAAI,CAAC;AAAA,IAChC;AACA,UAAM,QAAQ,SAAS,IAAI,IAAI;AAC/B,QAAI,UAAU,QAAW;AACxB,YAAM,MAAM,kBAAkB,GAAG,IAAI,KAAK,IAAI,wCAAwC;AAAA,IACvF;AACA,WAAO;AAAA,EACR;AAEA,QAAM,YAAY,CAAC,WAAuD;AACzE,QAAI,CAAC,QAAQ;AACZ,aAAO;AAAA,IACR;AACA,QAAI,CAAC,WAAW,IAAI,MAAM,GAAG;AAC5B,iBAAW,IAAI,QAAQ,OAAO,UAAU,IAAI,IAAI,OAAO,OAAO,IAAI,IAAI;AAAA,IACvE;AACA,WAAO,WAAW,IAAI,MAAM,KAAK;AAAA,EAClC;AAEA,QAAM,WAAqC,CAAC,MAAM,QAAQ,YAAY,QAAQ,SAAS;AACtF,QAAI,OAAO,WAAW,YAAY,CAAC,OAAO,KAAK,GAAG;AACjD;AAAA,IACD;AACA,QAAI,CAAC,qBAAqB,KAAK,MAAM,GAAG;AACvC,YAAM,MAAM,eAAe,GAAG,IAAI,KAAK,MAAM,4DAA4D;AACzG;AAAA,IACD;AACA,UAAM,UAAU,UAAU,MAAM;AAChC,QAAI,WAAW,CAAC,QAAQ,IAAI,MAAM,GAAG;AACpC,cAAQ,MAAM,kBAAkB,GAAG,IAAI,KAAK,MAAM,8BAA8B,UAAU,GAAG;AAAA,IAC9F;AAAA,EACD;AAEA,SAAO,EAAE,UAAU,SAAS;AAC7B;;;AC9DO,SAAS,oBACf,QACA,WACA,UACmB;AACnB,SAAO,CAAC,MAAM,MAAM,YAAY;AAC/B,WAAO,KAAK,EAAE,MAAM,MAAM,GAAG,SAAS,IAAI,IAAI,IAAI,SAAS,SAAS,CAAC;AAAA,EACtE;AACD;;;ACTA,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AACtB,IAAM,qBAAqB;AAC3B,IAAM,gBAAgB;AAiBtB,SAAS,gBACR,OACA,MACA,OACA,QACA,OACO;AACP,MAAI,CAAC,SAAS,CAAC,QAAQ;AACtB,UAAM,MAAM,SAAS,MAAM,MAAM,cAAc;AAAA,EAChD;AACA,MAAI,SAAS,UAAU,QAAQ;AAC9B,UAAM,MAAM,MAAM,MAAM,MAAM,WAAW;AAAA,EAC1C;AACD;AAGA,SAAS,WAAW,KAAU,MAAyB,OAAyB,MAAc,SAAwC;AACrI,aAAW,OAAO,MAAM;AACvB,QAAI,OAAO,KAAK,GAAG,GAAG;AACrB,YAAM,CAAC,GAAG,GAAG,MAAM,QAAQ,GAAG,CAAC;AAAA,IAChC;AAAA,EACD;AACD;AAEA,SAAS,gBAAgB,EAAE,KAAK,MAAM,MAAM,GAAkD;AAC7F,aAAW,OAAO,sBAAsB,IAAI,GAAG;AAC9C,QAAI,SAAS,IAAI,GAAG,CAAC,MAAM,QAAW;AACrC,YAAM,CAAC,GAAG,GAAG,YAAY,GAAG,WAAW,IAAI,EAAE,QAAQ,WAAW,GAAG,IAAI;AAAA,IACxE;AAAA,EACD;AACA,aAAW,KAAK,qBAAqB,IAAI,GAAG,OAAO,WAAW,CAAC,QAAQ,IAAI,GAAG,sBAAsB,WAAW,IAAI,EAAE,MAAM,GAAG;AAC9H,OAAK,SAAS,IAAI,gBAAgB,KAAK,SAAS,IAAI,WAAW,MAAM,CAAC,SAAS,IAAI,OAAO,GAAG;AAC5F,UAAM,CAAC,SAAS,GAAG,kBAAkB,iGAA4F;AAAA,EAClI;AACA,MAAI,SAAS,UAAU,IAAI,sBAAsB,QAAQ,CAAC,SAAS,IAAI,QAAQ,GAAG;AACjF,UAAM,CAAC,mBAAmB,GAAG,uBAAuB,kHAA6G;AAAA,EAClK;AACD;AAEA,SAAS,gBACR,KACA,MACA,QACuE;AACvE,QAAM,EAAE,UAAU,SAAS,IAAI;AAC/B,QAAM,aAAa,SAAS,IAAI,MAAM;AACtC,QAAM,cAAc,SAAS,IAAI,OAAO;AACxC,QAAM,kBAAkB,SAAS,aAAa,SAAS;AACvD,QAAM,SAAS,kBAAkB,SAAS,CAAC,QAAQ,GAAG,YAAY,QAAQ,IAAI;AAC9E,QAAM,UAAU,SAAS,SAAS,SAAS,CAAC,SAAS,GAAG,aAAa,SAAS,IAAI;AAClF,MAAI,SAAS,cAAc;AAC1B,aAAS,CAAC,OAAO,GAAG,SAAS,IAAI,KAAK,GAAG,OAAO;AAAA,EACjD;AAEA,WAAS,CAAC,kBAAkB,GAAG,SAAS,aAAa,IAAI,kBAAkB,0BAA0B;AACrG,WAAS,CAAC,aAAa,GAAG,SAAS,aAAa,IAAI,aAAa,qBAAqB;AACtF,WAAS,CAAC,QAAQ,GAAG,QAAQ,YAAY,IAAI,QAAQ,eAAe;AACpE,WAAS,CAAC,UAAU,GAAG,QAAQ,YAAY,IAAI,UAAU,iBAAiB;AAE1E,SAAO,EAAE,aAAa,SAAS,aAAa,gBAAgB,GAAG,GAAG,SAAS,kBAAkB,SAAS,QAAQ;AAC/G;AAEA,SAAS,gBAAgB,EAAE,KAAK,YAAY,OAAO,SAAS,aAAa,UAAU,MAAM,GAAkB;AAC1G,MAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,KAAK,WAAW,WAAW,GAAG;AAC9D,UAAM,CAAC,YAAY,GAAG,cAAc,0CAA0C;AAAA,EAC/E;AACA,MAAI,WAAW,SAAS,gBAAgB;AACvC,UAAM,CAAC,YAAY,GAAG,cAAc,WAAW,cAAc,cAAc;AAAA,EAC5E;AACA,QAAM,QAAQ,WAAW,OAAO,CAAC,MAAM,UAAU,WAAW,QAAQ,IAAI,MAAM,KAAK;AACnF,MAAI,MAAM,SAAS,GAAG;AACrB,UAAM,CAAC,YAAY,GAAG,cAAc,oCAAoC,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG;AAAA,EAC1G;AACA,aAAW,QAAQ,CAAC,MAAM,MAAM;AAE/B,QAAI,EAAE,QAAQ,QAAQ;AACrB,eAAS,CAAC,cAAc,CAAC,GAAG,SAAS,aAAa,MAAM,WAAW;AAAA,IACpE;AAAA,EACD,CAAC;AACF;AAGA,SAAS,mBAAmB,KAAc,MAAc,UAAe,MAAyC;AAC/G,QAAM,EAAE,KAAK,MAAM,MAAM,IAAI;AAC7B,QAAM,QAAQ,SAAS,SAAS,KAAK;AACrC,QAAM,QAAQ,SAAS,SAAS,UAAU;AAC1C,QAAM,SAAS,SAAS,SAAS,WAAW;AAC5C,QAAM,QAAQ,sBAAsB,QAAQ,MAAM;AAClD,QAAM,UAAU,SAAS,gBAAgB,iBAAiB,GAAG,MAAM;AACnE,MAAI,SAAS,OAAO,UAAU,OAAO,GAAG;AACvC,UAAM,KAAK,OAAO,GAAG,kBAAkB,aAAa,IAAI,8DAA8D;AAAA,EACvH;AACA,MAAI,WAAW,CAAC,OAAO;AACtB,UAAM,KAAK,GAAG,kBAAkB,aAAa,IAAI,oGAA+F;AAAA,EACjJ,WAAW,SAAS,gBAAgB,OAAO;AAC1C,UAAM,KAAK,GAAG,kBAAkB,aAAa,IAAI,2FAAsF;AAAA,EACxI,WAAW,OAAO;AACjB,oBAAgB,OAAO,kBAAkB,OAAO,QAAQ;AAAA,MACvD,SAAS,KAAK;AAAA,MACd,gBAAgB,aAAa,IAAI;AAAA,MACjC,MAAM,KAAK,aAAa;AAAA,MACxB,aAAa,aAAa,IAAI;AAAA,IAC/B,CAAC;AAAA,EACF,WAAW,CAAC,OAAO;AAClB,UAAM,KAAK,GAAG,kBAAkB,aAAa,IAAI,+BAA+B;AAAA,EACjF;AACD;AAEA,SAAS,eAAe,KAAoB;AAC3C,QAAM,EAAE,KAAK,MAAM,WAAW,YAAY,SAAS,aAAa,UAAU,MAAM,IAAI;AACpF,MAAI,CAAC,SAAS,IAAI,SAAS,KAAK,UAAU,WAAW,GAAG;AACvD,UAAM,CAAC,WAAW,GAAG,aAAa,yCAAyC;AAAA,EAC5E;AACA,MAAI,UAAU,SAAS,eAAe;AACrC,UAAM,CAAC,WAAW,GAAG,aAAa,WAAW,aAAa,aAAa;AAAA,EACxE;AACA,QAAM,gBAAgB,SAAS,UAAU,CAAC,CAAC,SAAS,IAAI,QAAQ,KAAK,IAAI,sBAAsB;AAC/F,QAAM,UAAU,oBAAI,IAAoB;AACxC,aAAW,EAAE,MAAM,KAAK,SAAS,KAAK,WAAW;AAChD,UAAM,OAAO,CAAC,QAA6B,MAAM,CAAC,aAAa,MAAM,GAAG,IAAI,CAAC,aAAa,IAAI;AAC9F,QAAI,CAAC,qBAAqB,KAAK,IAAI,GAAG;AACrC,YAAM,KAAK,GAAG,iBAAiB,aAAa,IAAI,qDAAqD;AAAA,IACtG;AACA,uBAAmB,KAAK,MAAM,UAAU,IAAI;AAC5C,eAAW,OAAO,CAAC,SAAS,cAAc,aAAa,GAAY;AAClE,YAAM,SAAS,SAAS,SAAS,GAAG,CAAC;AACrC,UAAI,CAAC,QAAQ;AACZ;AAAA,MACD;AACA,eAAS,KAAK,GAAG,GAAG,SAAS,aAAa,QAAQ,aAAa,IAAI,UAAU;AAC7E,YAAM,QAAQ,QAAQ,IAAI,MAAM;AAChC,UAAI,SAAS,UAAU,MAAM;AAC5B,cAAM,KAAK,GAAG,GAAG,0BAA0B,cAAc,KAAK,UAAU,IAAI,sBAAsB,MAAM,kDAA6C;AAAA,MACtJ;AACA,cAAQ,IAAI,QAAQ,IAAI;AAAA,IACzB;AACA,UAAM,QAAQ,SAAS,SAAS,KAAK;AACrC,QAAI,SAAS,WAAW,SAAS,KAAK,GAAG;AACxC,YAAM,KAAK,OAAO,GAAG,yBAAyB,IAAI,KAAK,uCAAuC,IAAI,IAAI;AAAA,IACvG;AACA,QAAI,OAAO,UAAU,UAAU,KAAK,CAAC,eAAe;AACnD,YAAM,KAAK,UAAU,GAAG,aAAa,aAAa,IAAI,gFAAgF;AAAA,IACvI;AAAA,EACD;AACD;AAEA,SAAS,yBAAyB,KAAc,KAAa,SAAyB;AACrF,UAAQ,QAAQ,CAAC,QAAQ,MAAM,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,SAAS,IAAI,aAAa,QAAQ,sBAAsB,CAAC;AACpH;AAEA,SAAS,oBAAoB,KAAoB;AAChD,QAAM,EAAE,YAAY,OAAO,SAAS,aAAa,UAAU,MAAM,IAAI;AACrE,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,UAAM,OAAkB,CAAC,kBAAkB,IAAI;AAC/C,QAAI,CAAC,WAAW,SAAS,IAAI,GAAG;AAC/B,YAAM,MAAM,kBAAkB,wBAAwB,IAAI,wCAAwC;AAAA,IACnG;AACA,UAAM,UAAU,SAAS,IAAI,IAAI,OAAO,CAAC;AACzC,UAAM,aAAa,SAAS,QAAQ,UAAU;AAC9C,UAAM,cAAc,SAAS,QAAQ,WAAW;AAChD,oBAAgB,OAAO,kBAAkB,YAAY,aAAa;AAAA,MACjE,SAAS;AAAA,MACT,gBAAgB,wBAAwB,IAAI;AAAA,MAC5C,MAAM,CAAC,GAAG,MAAM,aAAa;AAAA,MAC7B,aAAa,wBAAwB,IAAI;AAAA,IAC1C,CAAC;AACD,aAAS,CAAC,GAAG,MAAM,YAAY,GAAG,SAAS,aAAa,YAAY,cAAc,IAAI,gBAAgB;AACtG,aAAS,CAAC,GAAG,MAAM,aAAa,GAAG,SAAS,aAAa,aAAa,cAAc,IAAI,iBAAiB;AAAA,EAC1G;AACD;AAGA,SAAS,sBAAsB,KAAuB;AACrD,QAAM,EAAE,KAAK,SAAS,aAAa,UAAU,MAAM,IAAI;AACvD,aAAW,KAAK,CAAC,WAAW,gBAAgB,GAAG,OAAO,aAAa,CAAC,QAAQ,IAAI,GAAG,2FAA2F;AAC9K,QAAM,QAAQ,SAAS,IAAI,YAAY;AACvC,QAAM,SAAS,SAAS,IAAI,aAAa;AACzC,kBAAgB,OAAO,YAAY,OAAO,QAAQ;AAAA,IACjD,SAAS,CAAC,cAAc;AAAA,IACxB,gBAAgB;AAAA,IAChB,MAAM,CAAC,eAAe;AAAA,IACtB,aAAa;AAAA,EACd,CAAC;AACD,WAAS,CAAC,cAAc,GAAG,SAAS,aAAa,OAAO,sBAAsB;AAC9E,WAAS,CAAC,eAAe,GAAG,SAAS,aAAa,QAAQ,uBAAuB;AAEjF,QAAM,aAAa,WAAW,IAAI,mBAAmB;AACrD,QAAM,cAAc,WAAW,IAAI,oBAAoB;AACvD,MAAI,WAAW,SAAS,sBAAsB,YAAY,SAAS,oBAAoB;AACtF,UAAM,CAAC,qBAAqB,GAAG,kBAAkB,WAAW,kBAAkB,2BAA2B;AAAA,EAC1G;AACA,QAAM,SAAS,WAAW,OAAO,CAAC,WAAW,YAAY,SAAS,MAAM,CAAC;AACzE,MAAI,OAAO,SAAS,GAAG;AACtB,UAAM,CAAC,sBAAsB,GAAG,kBAAkB,uCAAuC,OAAO,KAAK,IAAI,CAAC,GAAG;AAAA,EAC9G;AACA,2BAAyB,KAAK,uBAAuB,UAAU;AAC/D,2BAAyB,KAAK,wBAAwB,WAAW;AACjE,QAAM,mBAAmB,WAAW,SAAS,KAAK,YAAY,SAAS;AACvE,sBAAoB,GAAG;AACvB,SAAO;AACR;AAGA,SAAS,qBAAqB,KAAuB;AACpD,QAAM,EAAE,KAAK,SAAS,aAAa,UAAU,MAAM,IAAI;AACvD,aAAW,KAAK,CAAC,gBAAgB,iBAAiB,uBAAuB,wBAAwB,gBAAgB,GAAG,OAAO,aAAa,CAAC,QAAQ,IAAI,GAAG,mCAAmC;AAC3L,MAAI,CAAC,SAAS,IAAI,OAAO,GAAG;AAC3B,UAAM,CAAC,SAAS,GAAG,YAAY,oEAAoE;AAAA,EACpG;AACA,WAAS,CAAC,SAAS,GAAG,SAAS,aAAa,SAAS,IAAI,OAAO,GAAG,gBAAgB;AACnF,QAAM,QAAQ,WAAW,IAAI,cAAc;AAC3C,MAAI,MAAM,SAAS,oBAAoB;AACtC,UAAM,CAAC,gBAAgB,GAAG,kBAAkB,WAAW,kBAAkB,kBAAkB;AAAA,EAC5F;AACA,2BAAyB,KAAK,kBAAkB,KAAK;AACrD,SAAO,MAAM,SAAS;AACvB;AAEA,SAAS,iBAAiB,KAAc,kBAAiC;AACxE,QAAM,EAAE,KAAK,UAAU,UAAU,MAAM,IAAI;AAC3C,QAAM,cAAc,SAAS,IAAI,WAAW,IAAI,IAAI,cAAc;AAClE,MAAI,oBAAoB,CAAC,aAAa;AACrC,UAAM,CAAC,aAAa,GAAG,eAAe,kHAA6G;AAAA,EACpJ;AACA,MAAI,CAAC,oBAAoB,aAAa;AACrC,UAAM,CAAC,aAAa,GAAG,eAAe,8EAA8E;AAAA,EACrH;AACA,MAAI,CAAC,aAAa;AACjB;AAAA,EACD;AACA,aAAW,OAAO,CAAC,UAAU,WAAW,QAAQ,UAAU,GAAG;AAC5D,QAAI,CAAC,SAAS,YAAY,GAAG,CAAC,GAAG;AAChC,YAAM,CAAC,eAAe,GAAG,GAAG,eAAe,wBAAwB,GAAG,IAAI;AAAA,IAC3E;AAAA,EACD;AACA,QAAM,OAAO,SAAS,YAAY,MAAM;AACxC,QAAM,SAAS,SAAS,CAAC,eAAe,QAAQ,GAAG,MAAM,oBAAoB;AAC7E,aAAW,OAAO,CAAC,WAAW,QAAQ,UAAU,GAAG;AAClD,aAAS,CAAC,eAAe,GAAG,GAAG,QAAQ,MAAM,YAAY,GAAG,GAAG,gBAAgB,GAAG,UAAU;AAAA,EAC7F;AACD;AAEA,SAAS,gBAAgB,KAAoB;AAC5C,QAAM,EAAE,KAAK,MAAM,MAAM,IAAI;AAC7B,MAAI,SAAS,cAAc;AAC1B;AAAA,EACD;AACA,QAAM,OAAO,SAAS,IAAI,QAAQ;AAClC,MAAI,QAAQ,SAAS,aAAa,SAAS,YAAY;AACtD,UAAM,CAAC,UAAU,GAAG,aAAa,wCAAwC;AAAA,EAC1E;AACA,mBAAiB,KAAK,SAAS,aAAa,sBAAsB,GAAG,IAAI,qBAAqB,GAAG,CAAC;AACnG;AAEA,SAAS,YAAY,KAAoB;AACxC,QAAM,EAAE,KAAK,MAAM,YAAY,WAAW,UAAU,UAAU,MAAM,IAAI;AACxE,QAAM,SAAS,SAAS,IAAI,MAAM,IAAI,IAAI,SAAS;AACnD,MAAI,CAAC,UAAU,SAAS,QAAQ;AAC/B;AAAA,EACD;AACA,QAAM,aAAa,SAAS,OAAO,MAAM;AACzC,MAAI,CAAC,YAAY;AAChB,UAAM,CAAC,UAAU,QAAQ,GAAG,UAAU,oCAAoC;AAAA,EAC3E;AACA,QAAM,eAAe,SAAS,CAAC,UAAU,QAAQ,GAAG,YAAY,eAAe;AAC/E,QAAM,SAAS,SAAS,OAAO,MAAM;AACrC,MAAI,CAAC,UAAU,CAAE,wBAA8C,SAAS,MAAM,GAAG;AAChF,UAAM,CAAC,UAAU,QAAQ,GAAG,UAAU,+DAA+D;AAAA,EACtG;AACA,MAAI,CAAC,SAAS,OAAO,WAAW,GAAG;AAClC,UAAM,CAAC,UAAU,aAAa,GAAG,UAAU,0FAAqF;AAAA,EACjI;AACA,WAAS,CAAC,UAAU,aAAa,GAAG,cAAc,YAAY,SAAS,OAAO,WAAW,GAAG,sBAAsB;AAClH,MAAI,SAAS,cAAc;AAC1B,QAAI,CAAC,SAAS,OAAO,YAAY,GAAG;AACnC,YAAM,CAAC,UAAU,cAAc,GAAG,UAAU,sDAAsD;AAAA,IACnG;AACA,aAAS,CAAC,UAAU,cAAc,GAAG,cAAc,YAAY,SAAS,OAAO,YAAY,GAAG,uBAAuB;AACrH,eAAW,OAAO,UAAU,EAAE,QAAQ,CAAC,MAAM,MAAM;AAClD,UAAI,CAAC,WAAW,SAAS,IAAI,GAAG;AAC/B,cAAM,CAAC,UAAU,cAAc,CAAC,GAAG,UAAU,qBAAqB,IAAI,sCAAsC;AAAA,MAC7G;AAAA,IACD,CAAC;AAAA,EACF,OAAO;AACN,QAAI,OAAO,QAAQ,cAAc,GAAG;AACnC,YAAM,CAAC,UAAU,cAAc,GAAG,UAAU,2DAA2D;AAAA,IACxG;AACA,QAAI,OAAO,QAAQ,YAAY,GAAG;AACjC,YAAM,CAAC,UAAU,YAAY,GAAG,UAAU,yEAAyE;AAAA,IACpH;AAAA,EACD;AACA,QAAM,SAAS,SAAS,OAAO,SAAS,IAAI,OAAO,YAAY,CAAC;AAChE,QAAM,OAAO,SAAS,eAAe,CAAC,cAAc,aAAa,IAAI,CAAC,WAAW,UAAU;AAC3F,aAAW,EAAE,KAAK,KAAK,WAAW;AACjC,UAAM,QAAQ,OAAO,IAAI;AACzB,QAAI,CAAC,SAAS,KAAK,GAAG;AACrB,YAAM,CAAC,UAAU,WAAW,GAAG,UAAU,sDAAiD,IAAI,eAAe;AAC7G;AAAA,IACD;AACA,eAAW,OAAO,MAAM;AACvB,UAAI,CAAC,SAAS,MAAM,GAAG,CAAC,GAAG;AAC1B,cAAM,CAAC,UAAU,aAAa,IAAI,GAAG,UAAU,oBAAoB,IAAI,YAAY,GAAG,IAAI;AAAA,MAC3F;AACA,eAAS,CAAC,UAAU,aAAa,MAAM,GAAG,GAAG,cAAc,YAAY,SAAS,MAAM,GAAG,CAAC,GAAG,WAAW,IAAI,KAAK,GAAG,EAAE;AAAA,IACvH;AAAA,EACD;AACA,aAAW,QAAQ,OAAO,KAAK,MAAM,GAAG;AACvC,QAAI,CAAC,UAAU,KAAK,CAAC,aAAa,SAAS,SAAS,IAAI,GAAG;AAC1D,YAAM,CAAC,UAAU,aAAa,IAAI,GAAG,UAAU,uBAAuB,IAAI,4CAA4C;AAAA,IACvH;AAAA,EACD;AACD;AAEA,SAAS,cAAc,EAAE,KAAK,MAAM,aAAa,QAAQ,GAAkB;AAC1E,MAAI,CAAC,SAAS,IAAI,KAAK,GAAG;AACzB,YAAQ,CAAC,OAAO,GAAG,SAAS,mDAA8C;AAAA,EAC3E;AACA,MAAI,SAAS,WAAW;AACvB,QAAI,CAAC,aAAa;AACjB,cAAQ,CAAC,SAAS,GAAG,cAAc,6FAA6F;AAAA,IACjI,WAAW,CAAC,SAAS,IAAI,gBAAgB,KAAK,CAAC,SAAS,IAAI,WAAW,GAAG;AACzE,cAAQ,CAAC,SAAS,GAAG,kBAAkB,0HAAqH;AAAA,IAC7J;AAAA,EACD;AACD;AAOO,SAAS,cAAc,KAAc,QAAgD;AAC3F,QAAM,MAAuB,CAAC;AAC9B,QAAM,QAAQ,oBAAoB,KAAK,aAAa,OAAO;AAC3D,QAAM,UAAU,oBAAoB,KAAK,aAAa,SAAS;AAC/D,MAAI,CAAC,SAAS,GAAG,GAAG;AACnB,UAAM,CAAC,GAAG,SAAS,0CAA0C;AAC7D,WAAO;AAAA,EACR;AACA,QAAM,MAAM;AACZ,MAAI,CAAC,eAAe,IAAI,IAAI,GAAG;AAC9B,UAAM,CAAC,MAAM,GAAG,QAAQ,qDAAqD;AAC7E,WAAO;AAAA,EACR;AACA,QAAM,OAAO,IAAI;AACjB,kBAAgB,EAAE,KAAK,MAAM,MAAM,CAAC;AACpC,QAAM,SAAS,mBAAmB,QAAQ,OAAO,OAAO;AACxD,QAAM,MAAe;AAAA,IACpB;AAAA,IAAK;AAAA,IAAM;AAAA,IAAO;AAAA,IAAS,GAAG;AAAA,IAC9B,GAAG,gBAAgB,KAAK,MAAM,MAAM;AAAA,IACpC,YAAY,mBAAmB,GAAG;AAAA,IAClC,OAAO,SAAS,IAAI,cAAc,IAAI,IAAI,iBAAiB,CAAC;AAAA,IAC5D,WAAW,kBAAkB,GAAG;AAAA,EACjC;AACA,kBAAgB,GAAG;AACnB,iBAAe,GAAG;AAClB,kBAAgB,GAAG;AACnB,cAAY,GAAG;AACf,gBAAc,GAAG;AACjB,SAAO;AACR;AAMO,SAAS,eAAe,MAAe,QAAgD;AAC7F,QAAM,MAAuB,CAAC;AAC9B,QAAM,QAAQ,oBAAoB,KAAK,aAAa,OAAO;AAC3D,MAAI,CAAC,SAAS,IAAI,GAAG;AACpB,UAAM,CAAC,GAAG,SAAS,uDAAuD;AAC1E,WAAO;AAAA,EACR;AACA,QAAM,QAAQ,OAAO,KAAK,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW,GAAG,CAAC;AACtE,MAAI,MAAM,WAAW,GAAG;AACvB,UAAM,CAAC,GAAG,SAAS,sFAAiF;AAAA,EACrG;AACA,MAAI,MAAM,SAAS,eAAe;AACjC,UAAM,CAAC,GAAG,SAAS,6BAA6B,aAAa,aAAa;AAAA,EAC3E;AACA,aAAW,QAAQ,OAAO;AACzB,QAAI,CAAC,cAAc,KAAK,IAAI,GAAG;AAC9B,YAAM,CAAC,IAAI,GAAG,QAAQ,IAAI,IAAI,wGAAmG;AAAA,IAClI;AACA,eAAW,SAAS,cAAc,KAAK,IAAI,GAAG,MAAM,GAAG;AACtD,UAAI,KAAK,EAAE,GAAG,OAAO,MAAM,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;AAAA,IACnD;AAAA,EACD;AACA,SAAO;AACR;;;AC5ZO,IAAM,mBAAmB,CAAC,UAA2B,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,SAAS,GAAG;AAUnI,SAAS,aAAa,QAAa,QAAiB,OAA+B;AAClF,QAAM,aAAa,SAAS,OAAO,UAAU;AAC7C,QAAM,SAAS,WAAW,OAAO,YAAY;AAC7C,QAAM,cAAc,WAAW,OAAO,gBAAgB;AAEtD,MAAI,CAAC,MAAM,QAAQ,OAAO,YAAY,KAAK,OAAO,WAAW,GAAG;AAC/D,UAAM,CAAC,cAAc,GAAG,0BAA0B,oGAAsG;AAAA,EACzJ;AACA,MAAI,YAAY;AACf,QAAI,CAAC,SAAS,OAAO,SAAS,GAAG;AAChC,YAAM,CAAC,WAAW,GAAG,iBAAiB,yEAAoE;AAAA,IAC3G;AACA,QAAI,CAAC,MAAM,QAAQ,OAAO,gBAAgB,KAAK,YAAY,WAAW,GAAG;AACxE,YAAM,CAAC,kBAAkB,GAAG,0BAA0B,qDAAuD;AAAA,IAC9G;AAAA,EACD,OAAO;AACN,QAAI,OAAO,QAAQ,WAAW,GAAG;AAChC,YAAM,CAAC,WAAW,GAAG,iBAAiB,mCAAmC;AAAA,IAC1E;AACA,QAAI,OAAO,QAAQ,kBAAkB,GAAG;AACvC,YAAM,CAAC,kBAAkB,GAAG,iBAAiB,wEAAmE;AAAA,IACjH;AACA,QAAI,OAAO,QAAQ,WAAW,GAAG;AAChC,YAAM,CAAC,WAAW,GAAG,uBAAuB,qHAAqH;AAAA,IAClK;AACA,QAAI,OAAO,QAAQ,cAAc,GAAG;AACnC,YAAM,CAAC,cAAc,GAAG,wBAAwB,mEAA8D;AAAA,IAC/G;AAAA,EACD;AACA,MAAI,QAAQ;AACX,QAAI,CAAC,YAAY;AAChB,YAAM,CAAC,YAAY,GAAG,iBAAiB,qEAAqE;AAAA,IAC7G;AACA,eAAW,OAAO,CAAC,aAAa,wBAAwB,aAAa,cAAc,GAAG;AACrF,UAAI,OAAO,QAAQ,GAAG,GAAG;AACxB,cAAM,CAAC,GAAG,GAAG,wBAAwB,IAAI,GAAG,8BAA8B;AAAA,MAC3E;AAAA,IACD;AAAA,EACD;AACA,QAAM,YAAY,SAAS,OAAO,SAAS;AAC3C,MAAI,OAAO,QAAQ,WAAW,KAAK,CAAC,WAAW;AAC9C,UAAM,CAAC,WAAW,GAAG,iBAAiB,kCAAkC;AAAA,EACzE;AACA,MAAI,aAAa,CAAC,OAAO,SAAS,GAAG,KAAK,iBAAiB,SAAS,KAAK,CAAC,OAAO,SAAS,SAAS,GAAG;AACrG,UAAM,CAAC,WAAW,GAAG,0BAA0B,IAAI,SAAS,wGAAwG;AAAA,EACrK;AACD;AAEA,SAAS,iBAAiB,EAAE,QAAQ,UAAU,WAAW,MAAM,GAAyB;AACvF,MAAI,CAAC,SAAS,SAAS,QAAQ,GAAG;AACjC,UAAM,CAAC,UAAU,GAAG,6BAA6B,IAAI,SAAS,8JAAyJ;AAAA,EACxN;AACA,aAAW,OAAO,CAAC,aAAa,MAAM,GAAG;AACxC,QAAI,OAAO,QAAQ,GAAG,GAAG;AACxB,YAAM,CAAC,GAAG,GAAG,2BAA2B,IAAI,GAAG,oGAAoG;AAAA,IACpJ;AAAA,EACD;AACD;AAEA,SAAS,oBAAoB,EAAE,QAAQ,UAAU,MAAM,GAAyB;AAC/E,MAAI,SAAS,aAAa,YAAY;AACrC,QAAI,OAAO,QAAQ,cAAc,GAAG;AACnC,YAAM,CAAC,cAAc,GAAG,qBAAqB,6FAA6F;AAAA,IAC3I;AACA,QAAI,CAAC,SAAS,OAAO,iBAAiB,KAAK,CAAC,SAAS,OAAO,kBAAkB,GAAG;AAChF,YAAM,CAAC,UAAU,GAAG,qBAAqB,sFAAsF;AAAA,IAChI;AACA;AAAA,EACD;AACA,MAAI,CAAC,SAAS,OAAO,YAAY,GAAG;AACnC,UAAM,CAAC,UAAU,GAAG,qBAAqB,kEAAkE;AAAA,EAC5G;AACA,aAAW,OAAO,CAAC,qBAAqB,oBAAoB,GAAG;AAC9D,QAAI,OAAO,QAAQ,GAAG,GAAG;AACxB,YAAM,CAAC,GAAG,GAAG,qBAAqB,IAAI,GAAG,uEAAuE;AAAA,IACjH;AAAA,EACD;AACD;AAGA,SAAS,oBAAoB,KAAqB,KAAa,OAAgB,WAAsB;AACpG,QAAM,EAAE,UAAU,MAAM,IAAI;AAC5B,QAAM,WAAW,UAAU,GAAG;AAC9B,QAAM,QAAQ,OAAO,YAClB,SAAS,QAAQ,KAAK,sBAAsB,QAAQ,MAAM,UAC1D,SAAS,SAAS,cAAc,KAAK,OAAO,SAAS;AACxD,MAAI,OAAO;AACV,QAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,MAAM,KAAK,KAAK,CAAC,SAAS,MAAM,MAAM,GAAG;AAC1E,YAAM,CAAC,OAAO,GAAG,GAAG,sBAAsB,IAAI,GAAG,yEAAyE;AAAA,IAC3H;AAAA,EACD,WAAW,CAAC,SAAS,KAAK,GAAG;AAC5B,UAAM,CAAC,OAAO,GAAG,GAAG,sBAAsB,IAAI,GAAG,2DAA2D;AAAA,EAC7G;AACD;AAGA,SAAS,UAAU,KAAqB,YAAsB,WAA6B;AAC1F,QAAM,EAAE,QAAQ,WAAW,QAAQ,UAAU,MAAM,IAAI;AACvD,QAAM,QAAQ,oBAAI,IAAI,CAAC,GAAG,YAAY,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC;AAChE,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,UAAU,CAAC,OAAO,WAAW,GAAY;AACnD,UAAM,MAAM,SAAS,OAAO,MAAM,CAAC,IAAI,OAAO,MAAM,IAAI;AACxD,QAAI,CAAC,KAAK;AACT;AAAA,IACD;AACA,eAAW,OAAO,OAAO,KAAK,GAAG,GAAG;AACnC,UAAI,OAAO,IAAI,GAAG,GAAG;AACpB,cAAM,CAAC,QAAQ,GAAG,GAAG,oBAAoB,IAAI,GAAG,kFAA6E;AAAA,MAC9H;AACA,aAAO,IAAI,GAAG;AACd,UAAI,CAAC,MAAM,IAAI,GAAG,GAAG;AACpB,cAAM,CAAC,QAAQ,GAAG,GAAG,oBAAoB,IAAI,GAAG,8CAA8C,SAAS,iBAAiB,WAAW,KAAK,IAAI,KAAK,MAAM,gBAAgB,OAAO,KAAK,SAAS,EAAE,KAAK,IAAI,KAAK,MAAM,IAAI;AAAA,MACvN,WAAW,UAAU,WAAW,OAAO;AACtC,4BAAoB,KAAK,KAAK,IAAI,GAAG,GAAG,SAAS;AAAA,MAClD,WAAW,WAAW,eAAe,OAAO,aAAa,SAAS,SAAS,QAAQ;AAClF,cAAM,CAAC,QAAQ,GAAG,GAAG,uBAAuB,gBAAgB,GAAG,2IAAsI;AAAA,MACtM;AAAA,IACD;AAAA,EACD;AACA,SAAO;AACR;AAEA,SAAS,aAAa,KAA2B;AAChD,QAAM,EAAE,QAAQ,UAAU,WAAW,MAAM,IAAI;AAC/C,QAAM,aAAa,WAAW,SAAS,UAAU;AACjD,QAAM,YAAiB,SAAS,SAAS,SAAS,IAAI,SAAS,YAAY,CAAC;AAC5E,QAAM,SAAS,UAAU,KAAK,YAAY,SAAS;AAEnD,QAAM,WAAW,OAAO,QAAQ,SAAS,EACvC,OAAO,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC,OAAO,IAAI,QAAQ,KAAK,EAAE,SAAS,GAAG,KAAK,IAAI,aAAa,MAAM,EAC/F,IAAI,CAAC,CAAC,QAAQ,MAAM,QAAQ;AAC9B,MAAI,SAAS,SAAS,GAAG;AACxB;AAAA,MACC,CAAC,OAAO,QAAQ,KAAK,IAAI,QAAQ,UAAU;AAAA,MAC3C;AAAA,MACA,8BAA8B,SAAS,mCAA8B,SAAS,KAAK,IAAI,CAAC;AAAA,IACzF;AAAA,EACD;AACA,aAAW,OAAO,oBAAoB,EAAE,QAAQ,CAAC,MAAM,MAAM;AAC5D,QAAI,CAAC,WAAW,SAAS,IAAI,GAAG;AAC/B,YAAM,CAAC,wBAAwB,CAAC,GAAG,iBAAiB,IAAI,IAAI,0BAA0B,SAAS,qDAAqD;AAAA,IACrJ;AAAA,EACD,CAAC;AACF;AAQO,SAAS,qBACf,YACA,WACA,UACA,UAAmC,CAAC,GAClB;AAClB,QAAM,MAAuB,CAAC;AAC9B,QAAM,QAAQ,oBAAoB,KAAK,UAAU,OAAO;AACxD,MAAI,CAAC,SAAS,SAAS,GAAG;AACzB,WAAO;AAAA,EACR;AACA,QAAM,SAAS;AACf,QAAM,YAAY,SAAS,OAAO,QAAQ;AAC1C,MAAI,CAAC,WAAW;AACf,WAAO;AAAA,EACR;AACA,QAAM,SAAS,eAAe;AAE9B,MAAI,OAAO,QAAQ,YAAY,GAAG;AACjC,UAAM,CAAC,YAAY,GAAG,uBAAuB,sFAAiF;AAAA,EAC/H;AACA,MAAI,CAAC,UAAU,SAAS,GAAG,GAAG;AAC7B,UAAM,CAAC,UAAU,GAAG,wBAAwB,iDAAiD,QAAQ,cAAc,QAAQ,IAAI,SAAS,IAAI;AAC5I,WAAO;AAAA,EACR;AACA,QAAM,MAAM,UAAU,QAAQ,GAAG;AACjC,QAAM,aAAa,UAAU,MAAM,GAAG,GAAG;AACzC,QAAM,eAAe,UAAU,MAAM,MAAM,CAAC;AAG5C,eAAa,QAAQ,QAAQ,KAAK;AAElC,MAAI,aAAa,MAAM;AACtB,WAAO;AAAA,EACR;AACA,MAAI,CAAC,UAAU;AACd,UAAM,CAAC,UAAU,GAAG,oBAAoB,gBAAgB,SAAS,YAAO,UAAU,oCAAoC,YAAY,IAAI;AACtI,WAAO;AAAA,EACR;AACA,QAAM,OAAO,SAAS;AACtB,MAAI,UAAU,SAAS,cAAc;AACpC,UAAM,CAAC,UAAU,GAAG,iBAAiB,iDAAiD,SAAS,UAAU,QAAQ,GAAG,mCAA8B;AAClJ,WAAO;AAAA,EACR;AACA,MAAI,CAAC,UAAU,SAAS,cAAc;AACrC,UAAM,CAAC,UAAU,GAAG,iBAAiB,+DAA+D,SAAS,qDAAgD;AAC7J,WAAO;AAAA,EACR;AACA,QAAM,MAAsB,EAAE,QAAQ,UAAU,WAAW,QAAQ,MAAM;AACzE,MAAI,SAAS,QAAQ;AACpB,qBAAiB,GAAG;AAAA,EACrB;AAGA,MAAI,QAAQ;AACX,wBAAoB,GAAG;AAAA,EACxB;AACA,eAAa,GAAG;AAChB,SAAO;AACR;;;ACzCO,IAAM,WAAwD;AAAA,EACpE,EAAE,OAAO,KAAK,OAAO,UAAU;AAAA,EAC/B,EAAE,OAAO,KAAK,OAAO,WAAW;AAAA,EAChC,EAAE,OAAO,KAAK,OAAO,WAAW;AAAA,EAChC,EAAE,OAAO,KAAK,OAAO,YAAY;AAClC;","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/settings.ts","../src/registers/types.ts","../src/registers/read.ts","../src/registers/entities.ts","../src/registers/issues.ts","../src/registers/check-declaration.ts","../src/registers/check-binding.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, defParams: { pattern: \"^[^\\\\s@]+@[^\\\\s@]+\\\\.[^\\\\s@]+$\" } },\n\t{ cd: \"phone\", name: \"Phone\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true, defParams: { pattern: \"^\\\\+?[0-9\\\\s\\\\-().]+$\" } },\n\t{ cd: \"url\", name: \"URL\", columnType: \"D\", baseDatatypeCd: \"string\", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true, defParams: { pattern: \"^https?://.+\", patternFlags: \"i\" } },\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/**\n * An accounting register read over an interval: one row per account of the register's chart,\n * with opening, turnover and closing debit/credit for one resource, netted per account.\n */\nexport interface RegisterSource {\n\t/** The register, `module_cd.register_cd`. */\n\tname: string;\n\t/** The one resource the six balance columns are about. */\n\tresource: string;\n\t/** Two date params, or the record of `entity` a key param finds and its two date columns. */\n\tinterval: { entity?: string; key?: string; from: string; to: string };\n\t/** Chart columns in nav notation, `<chart>.<column>`. */\n\tcolumns?: string[];\n\t/** A filter over the chart, in the same nav notation. */\n\tfilter?: Filter;\n\t/** Chart columns to sort by, in nav notation. */\n\tsort?: SortClause[];\n\t/** The query rollup without `leafKey` and `measures`. */\n\trollup?: { tree: string; treeKey: string; parentField: string; labels: string[]; order?: string };\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, R = an accounting register read over an interval. */\n\tdatasetType: \"Q\" | \"S\" | \"R\";\n\t/** Used when `datasetType` is 'Q'. */\n\tquery?: ReportQuery;\n\t/**\n\t * Used when `datasetType` is 'S': the stored-procedure code, as\n\t * `logic/stored_procedures.json` keys it — not the PostgreSQL function name,\n\t * and never schema-qualified. `module.spCd` binds a dependency's procedure.\n\t */\n\tspCd?: string;\n\t/** Used when `datasetType` is 'R'. */\n\tregister?: RegisterSource;\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/** Report name: page title, breadcrumb and menu entry. Omit and `description` is the name. */\n\tlabel?: string;\n\t/** What the report shows, under its name on the page. */\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| \"function\"\n\t| \"reaction\"\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:parties.party`, `action:check_credit`, `function:wh_posting` (a function in\n * `logic/functions/wh_posting.dsl`), `reaction:quote_line_pricing` (a field-change reaction\n * an extension declares on the dependency entity), `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","// Module settings — authoring shape of `settings.json`. Mirror of settings.schema.json.\n\n/** Underlying primitive datatype for a setting. */\nexport type SettingBaseDatatype = \"string\" | \"number\" | \"bool\" | \"date\" | \"dateTime\" | \"time\" | \"json\";\n\n/** Properties carried by every setting regardless of where its control comes from. */\nexport interface SettingDefBase {\n\t/** Display label (preferred for new settings). */\n\tlabel?: string;\n\t/** Display label (older style). Prefer `label`. */\n\tdescription?: string;\n\t/** Alternative older label key. Prefer `label`. */\n\tdesc?: string;\n\t/** Default value before the user sets one. */\n\tdefaultValue?: unknown;\n\t/** Formula to evaluate the default (alternative to `defaultValue`). */\n\tformula?: string;\n\t/** If true, must be set somewhere in the folder chain for install to proceed. */\n\trequired?: boolean;\n}\n\n/** A setting declares its control inline or borrows it from a reusable column domain. */\nexport type SettingDef =\n\t| (SettingDefBase & {\n\t\t/** Field type code (must match a `dforge.field_type` code). */\n\t\tfieldTypeCd: string;\n\t\t/** Underlying primitive datatype (older settings declare this explicitly). */\n\t\tbaseDatatypeCd?: SettingBaseDatatype;\n\t\t/** Field-type-specific parameters (dropdown options, min/max, lookup link, …). */\n\t\tparams?: Record<string, unknown>;\n\t\tdomain?: never;\n\t})\n\t| (SettingDefBase & {\n\t\t/** Column domain supplying fieldTypeCd, baseDatatypeCd and params. */\n\t\tdomain: string;\n\t\tfieldTypeCd?: never;\n\t\tbaseDatatypeCd?: never;\n\t\tparams?: never;\n\t});\n\n/** A check across a module's settings: SQL over setting codes, as an entity check constraint. */\nexport interface SettingConstraintDef {\n\ttype: \"check\";\n\t/** PostgreSQL boolean over setting codes; writing any setting it names re-checks it. */\n\texpression: string;\n\tmessage?: string;\n}\n\n/**\n * A `settings.json` file: setting code → definition, plus the optional `$constraints`. A\n * setting code cannot start with `$`, but TypeScript cannot exclude it from the index, so\n * the index admits the constraints map too.\n */\nexport type SettingsFile = {\n\t$constraints?: Record<string, SettingConstraintDef>;\n\t[settingCd: string]: SettingDef | Record<string, SettingConstraintDef> | undefined;\n};\n\n/** The settings of a file, without its `$constraints`. */\nexport function settingDefsOf(file: SettingsFile): Record<string, SettingDef> {\n\tconst defs: Record<string, SettingDef> = {};\n\tfor (const [code, def] of Object.entries(file))\n\t\tif (code !== \"$constraints\" && def) defs[code] = def as SettingDef;\n\treturn defs;\n}\n","// Registers — the authoring shape of `registers.json` and of an A / L column's\n// binding. Mirror of registers.schema.json and docs/business-logic/registers.md §3–4.\n\n/** The four kinds. Each requires and refuses a fixed set of properties. */\nexport type RegisterKind = \"balance\" | \"turnover\" | \"info\" | \"accounting\";\n\nexport const REGISTER_KINDS: readonly RegisterKind[] = [\"balance\", \"turnover\", \"info\", \"accounting\"];\n\nexport const isRegisterKind = (value: unknown): value is RegisterKind =>\n\ttypeof value === \"string\" && (REGISTER_KINDS as readonly string[]).includes(value);\n\n/** Register codes: lowercase letters, digits and underscores, starting with a letter. */\nexport const REGISTER_CODE = /^[a-z][a-z0-9_]*$/;\n\n/** A column name as `registers.json` takes one. */\nexport const REGISTER_COLUMN_NAME = /^[a-z_][a-z0-9_]*$/;\n\n/** How much one cached row of a `totals` cache covers. */\nexport type RegisterTotalsPeriod = \"day\" | \"month\" | \"quarter\" | \"year\";\n\nexport const REGISTER_TOTALS_PERIODS: readonly RegisterTotalsPeriod[] = [\"day\", \"month\", \"quarter\", \"year\"];\n\n/** A measured column of the register: one shared column, or one per side on an accounting register. */\nexport interface RegisterResourceDef {\n\t/** The column, on the state entity / journal / record entity. Not with `debitField` / `creditField`. */\n\tfield?: string;\n\t/** Debit-side column, on an accounting register. Declared with `creditField`. */\n\tdebitField?: string;\n\tcreditField?: string;\n\t/** Every binding must map it (default true). False for a resource only some writers supply. */\n\trequired?: boolean;\n\t/** Under `recordChangesOnly`: what the resource compares as before any record is in effect. */\n\tabsentAs?: number | string | boolean;\n\tdescription?: string;\n}\n\n/** The per-period cache (registers.md §8). */\nexport interface RegisterTotalsDef {\n\tentity: string;\n\tperiod: RegisterTotalsPeriod;\n\t/** Column holding the first date of the period a row covers. */\n\tperiodField: string;\n\t/** Account column; required on an accounting register, refused elsewhere. */\n\taccountField?: string;\n\t/** Which dimensions the cache is grouped by; accounting only; all of them when omitted. */\n\tdimensions?: string[];\n\t/** Every resource of the register: `inField` + `outField`, or `debitField` + `creditField` on an accounting register. */\n\tresources: Record<string, { inField?: string; outField?: string; debitField?: string; creditField?: string }>;\n}\n\n/** Where the chart says which analytic each account carries in which slot. */\nexport interface RegisterCompositionDef {\n\tentity: string;\n\taccount: string;\n\tslot: string;\n\tanalytic: string;\n}\n\n/** One declaration of `registers.json`. */\nexport interface RegisterDef {\n\tkind: RegisterKind;\n\tlabel?: string;\n\tdescription?: string;\n\t/** State table (`balance`) or record table (`info`). */\n\tentity?: string;\n\t/** Movement / posting entity (`balance` optional, `turnover` and `accounting` required). */\n\tjournal?: string;\n\t/** Journal column stamped +1 / −1 (`balance`, `turnover`). */\n\tjournalDirection?: string;\n\t/** Journal column holding each movement's date. */\n\tjournalDate?: string;\n\t/** Record-entity column holding when a record takes effect (`info`). */\n\tperiod?: string;\n\t/** Record-entity jsonb column the writing document is stamped into (`info`); makes the register subordinate. */\n\trecorder?: string;\n\trecordChangesOnly?: boolean;\n\t/** Chart of accounts (`accounting`). */\n\tchart?: string;\n\tlineMode?: \"classic\" | \"compound\";\n\t/** Posting column pointing at the chart (classic). */\n\taccount?: string;\n\tdebitAccount?: string;\n\tcreditAccount?: string;\n\t/** Analytic slot columns in slot order (classic). */\n\tanalyticFields?: string[];\n\tdebitAnalyticFields?: string[];\n\tcreditAnalyticFields?: string[];\n\tcomposition?: RegisterCompositionDef;\n\t/** 1–8 column names: the key of the register, on its carrier. */\n\tdimensions: string[];\n\t/** Two-sided dimensions of a compound register, with the column per side. */\n\tsideDimensions?: Record<string, { debitField: string; creditField: string }>;\n\tresources: Record<string, RegisterResourceDef>;\n\ttotals?: RegisterTotalsDef;\n\t/** `balance`: may the state go below zero (default false). */\n\tallowNegative?: boolean;\n\t/** `balance`: create the state row on first movement (default true). */\n\tautoCreate?: boolean;\n}\n\n/** `registers.json`: register code → declaration. */\nexport type RegistersFile = Record<string, RegisterDef>;\n\n/** A map entry: a column of the row read, or on a compound ledger one column per side. */\nexport type RegisterMapValue = string | { debit?: string; credit?: string };\n\n/** `params` of an A / L column bound to a register (registers.md §4). */\nexport interface RegisterBindingParams {\n\t/** `module_cd.register_cd`. */\n\tregister?: string;\n\t/** The bool column recording the posted state, when not the column itself. */\n\tstateField?: string;\n\tdirection?: \"increase\" | \"decrease\";\n\t/** Per-row direction from a document value, instead of `direction`. */\n\tsign?: { field: string; positive?: unknown[]; negative?: unknown[] };\n\t/** Document column (or path / expression) supplying the journal date. */\n\tdateField?: string;\n\tdateTrunc?: \"day\" | \"month\" | \"year\";\n\tlineEntity?: string;\n\tlineField?: string;\n\tgroupLinesBy?: string[];\n\t/** Register dimension or resource name → column of the row read. */\n\tmap?: Record<string, RegisterMapValue>;\n\t/** On a line posting: register name → DOCUMENT column. */\n\theaderMap?: Record<string, string>;\n\tlockedFields?: string[];\n\tlockedLineFields?: string[];\n\tskipPostingWhenEmpty?: string[];\n\t/** Classic ledger: the line's account column. */\n\taccountField?: string;\n\tdebitAccountField?: string;\n\tcreditAccountField?: string;\n\tperiodDimField?: string;\n\t/** Analytic kind → line column (classic ledger). */\n\tanalytics?: Record<string, string>;\n\tdebitAnalytics?: Record<string, string>;\n\tcreditAnalytics?: Record<string, string>;\n\tmovementMapping?: Record<string, string>;\n\t/** Inline restatement of a register; refused alongside `register`. */\n\tregistries?: unknown;\n}\n","// Reading a declaration: the loose-JSON helpers every check shares, and what each\n// kind requires and refuses.\n\nimport type { RegisterDef, RegisterKind, RegisterResourceDef } from \"./types\";\n\nexport type Raw = Record<string, unknown>;\n\nexport const isRecord = (value: unknown): value is Raw =>\n\t!!value && typeof value === \"object\" && !Array.isArray(value);\n\nexport const safeTrim = (value: unknown): string | undefined => (typeof value === \"string\" && value.trim() ? value : undefined);\n\nexport const stringList = (value: unknown): string[] =>\n\tArray.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n\n/** A key present but undefined is an editor's draft, not a declaration. */\nexport const hasKey = (obj: Raw, key: string): boolean => obj[key] !== undefined;\n\n/** A declaration's dimension names, only the ones that are strings. */\nexport const registerDimensions = (def: RegisterDef | Raw): string[] => stringList(def.dimensions);\n\n/** A declaration's resources in file order, each with its definition object. */\nexport const registerResources = (def: RegisterDef | Raw): Array<{ name: string; def: Raw }> =>\n\t!isRecord(def.resources)\n\t\t? []\n\t\t: Object.entries(def.resources).map(([name, raw]) => ({ name, def: isRecord(raw) ? raw : {} }));\n\n/** The entity the dimensions, resources and stamps are columns of. */\nexport function registerCarrier(def: RegisterDef | Raw): string | undefined {\n\tswitch (def.kind) {\n\t\tcase \"balance\":\n\t\tcase \"info\":\n\t\t\treturn safeTrim(def.entity);\n\t\tcase \"turnover\":\n\t\tcase \"accounting\":\n\t\t\treturn safeTrim(def.journal);\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\n/** How a resource names its column: one shared column, or one per side. */\nexport const registerResourceShape = (resource: RegisterResourceDef | Raw): \"field\" | \"split\" =>\n\t\"debitField\" in resource || \"creditField\" in resource ? \"split\" : \"field\";\n\nexport const registerLineMode = (def: RegisterDef | Raw): \"classic\" | \"compound\" =>\n\tdef.lineMode === \"compound\" ? \"compound\" : \"classic\";\n\nconst INFO_PROPERTIES = [\"period\", \"recorder\", \"recordChangesOnly\"] as const;\nconst ACCOUNTING_PROPERTIES = [\n\t\"chart\", \"composition\", \"analyticFields\", \"lineMode\", \"account\",\n\t\"debitAccount\", \"creditAccount\", \"debitAnalyticFields\", \"creditAnalyticFields\", \"sideDimensions\",\n] as const;\nconst BALANCE_PROPERTIES = [\"allowNegative\", \"autoCreate\"] as const;\n\n/** Properties each kind refuses — the schema's `false` properties, which install fails on. */\nconst REFUSED: Record<RegisterKind, readonly string[]> = {\n\tbalance: [...INFO_PROPERTIES, ...ACCOUNTING_PROPERTIES],\n\tturnover: [\"entity\", ...INFO_PROPERTIES, ...ACCOUNTING_PROPERTIES, ...BALANCE_PROPERTIES],\n\tinfo: [\"journal\", \"journalDirection\", \"journalDate\", ...ACCOUNTING_PROPERTIES, \"totals\", ...BALANCE_PROPERTIES],\n\taccounting: [\"entity\", ...INFO_PROPERTIES, \"journalDirection\", ...BALANCE_PROPERTIES],\n};\n\n/** Properties each kind requires beside `kind`, `dimensions` and `resources`. */\nconst REQUIRED: Record<RegisterKind, readonly string[]> = {\n\tbalance: [\"entity\"],\n\tturnover: [\"journal\", \"journalDirection\", \"journalDate\"],\n\tinfo: [\"entity\"],\n\taccounting: [\"chart\", \"journal\", \"journalDate\", \"lineMode\"],\n};\n\nexport const registerRefusedProps = (kind: RegisterKind): readonly string[] => REFUSED[kind];\nexport const registerRequiredProps = (kind: RegisterKind): readonly string[] => REQUIRED[kind];\n\n/** \"A balance register\" / \"a balance register\", for the start and the middle of a message. */\nexport const KIND_LABEL: Record<RegisterKind, { sentence: string; inline: string }> = {\n\tbalance: { sentence: \"A balance register\", inline: \"a balance register\" },\n\tturnover: { sentence: \"A turnover register\", inline: \"a turnover register\" },\n\tinfo: { sentence: \"An information register\", inline: \"an information register\" },\n\taccounting: { sentence: \"An accounting register\", inline: \"an accounting register\" },\n};\n","// Resolving the entities and columns a declaration names, through the caller's lookup.\n\nimport { REGISTER_COLUMN_NAME } from \"./types\";\nimport type { IssuePath, RegisterReporter } from \"./issues\";\n\n/**\n * What an entity a declaration names looks like, as far as the caller knows.\n * `columns` is left undefined when they can't be listed (a dependency known\n * only from its contract), and no column rule runs against it.\n */\nexport interface RegisterEntityInfo {\n\tcolumns?: Iterable<string>;\n}\n\n/**\n * Resolves an entity code as `registers.json` writes it — bare for the module's\n * own, `module.entity` for another's. Returns `null` when nothing can be known\n * (the module named isn't available: the rule stands down) and `undefined` when\n * the module is known and has no such entity.\n */\nexport type RegisterEntityLookup = (code: string) => RegisterEntityInfo | null | undefined;\n\nexport type ResolvedRegisterEntity = RegisterEntityInfo | null | undefined;\n\nexport interface EntityChecks {\n\t/** Resolve `code`, reporting `unknown-entity` when the module is known and has no such entity. */\n\tentityAt: (path: IssuePath, code: string | undefined, what: string) => ResolvedRegisterEntity;\n\t/** Check a column name, and that `entity` stores it. A non-string or blank column is skipped. */\n\tcolumnAt: (path: IssuePath, entity: ResolvedRegisterEntity, entityCode: string | undefined, column: unknown, what: string) => void;\n}\n\nexport function createEntityChecks(\n\tlookup: RegisterEntityLookup | undefined,\n\terror: RegisterReporter,\n\twarning: RegisterReporter,\n): EntityChecks {\n\tconst resolved = new Map<string, ResolvedRegisterEntity>();\n\tconst columnSets = new WeakMap<RegisterEntityInfo, Set<string> | null>();\n\n\tconst entityAt: EntityChecks[\"entityAt\"] = (path, code, what) => {\n\t\tif (!code || !lookup) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!resolved.has(code)) {\n\t\t\tresolved.set(code, lookup(code));\n\t\t}\n\t\tconst found = resolved.get(code);\n\t\tif (found === undefined) {\n\t\t\terror(path, \"unknown-entity\", `${what} '${code}' isn't an entity this module can see.`);\n\t\t}\n\t\treturn found;\n\t};\n\n\tconst columnsOf = (entity: ResolvedRegisterEntity): Set<string> | null => {\n\t\tif (!entity) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!columnSets.has(entity)) {\n\t\t\tcolumnSets.set(entity, entity.columns ? new Set(entity.columns) : null);\n\t\t}\n\t\treturn columnSets.get(entity) ?? null;\n\t};\n\n\tconst columnAt: EntityChecks[\"columnAt\"] = (path, entity, entityCode, column, what) => {\n\t\tif (typeof column !== \"string\" || !column.trim()) {\n\t\t\treturn;\n\t\t}\n\t\tif (!REGISTER_COLUMN_NAME.test(column)) {\n\t\t\terror(path, \"column-name\", `${what} '${column}' isn't a column name (lowercase letters, digits and '_').`);\n\t\t\treturn;\n\t\t}\n\t\tconst columns = columnsOf(entity);\n\t\tif (columns && !columns.has(column)) {\n\t\t\twarning(path, \"unknown-column\", `${what} '${column}' isn't a stored column of ${entityCode}.`);\n\t\t}\n\t};\n\n\treturn { entityAt, columnAt };\n}\n","// Findings: what a check reports, and the reporters that collect them.\n\nexport type IssuePath = Array<string | number>;\n\n/** One finding, addressed by a JSON path relative to what was checked. */\nexport interface RegisterIssue {\n\tpath: IssuePath;\n\tmessage: string;\n\t/** `error`: install refuses it. `warning`: worth a look. */\n\tseverity: \"error\" | \"warning\";\n\t/** Stable id, namespaced `registers/*` for a declaration and `entity/register-*` for a binding. */\n\tcode: string;\n}\n\nexport type RegisterReporter = (path: IssuePath, code: string, message: string) => void;\n\nexport function createIssueReporter(\n\tissues: RegisterIssue[],\n\tnamespace: \"registers\" | \"entity\",\n\tseverity: RegisterIssue[\"severity\"],\n): RegisterReporter {\n\treturn (path, code, message) => {\n\t\tissues.push({ path, code: `${namespace}/${code}`, message, severity });\n\t};\n}\n","// Checking one declaration of `registers.json`, and the file as a whole.\n// Every rule FAILS OPEN: what cannot be resolved is not reported.\n\nimport { createEntityChecks } from \"./entities\";\nimport type { EntityChecks, RegisterEntityLookup, ResolvedRegisterEntity } from \"./entities\";\nimport { createIssueReporter } from \"./issues\";\nimport type { IssuePath, RegisterIssue, RegisterReporter } from \"./issues\";\nimport {\n\tKIND_LABEL, hasKey, isRecord, registerCarrier, registerDimensions, registerLineMode, registerRefusedProps,\n\tregisterRequiredProps, registerResourceShape, registerResources, safeTrim, stringList,\n} from \"./read\";\nimport type { Raw } from \"./read\";\nimport { REGISTER_CODE, REGISTER_COLUMN_NAME, REGISTER_TOTALS_PERIODS, isRegisterKind } from \"./types\";\nimport type { RegisterKind } from \"./types\";\n\nconst MAX_DIMENSIONS = 8;\nconst MAX_RESOURCES = 64;\nconst MAX_ANALYTIC_SLOTS = 8;\nconst MAX_REGISTERS = 100;\n\ninterface Context extends EntityChecks {\n\tdef: Raw;\n\tkind: RegisterKind;\n\terror: RegisterReporter;\n\twarning: RegisterReporter;\n\tjournalCode?: string;\n\tjournal: ResolvedRegisterEntity;\n\tcarrierCode?: string;\n\tcarrier: ResolvedRegisterEntity;\n\tdimensions: string[];\n\tsides: Raw;\n\tresources: ReturnType<typeof registerResources>;\n}\n\n/** Two columns that must both be named and must differ — a debit/credit pair. */\nfunction checkColumnPair(\n\terror: RegisterReporter,\n\tcode: string,\n\tfirst: string | undefined,\n\tsecond: string | undefined,\n\twhere: { missing: IssuePath; missingMessage: string; same: IssuePath; sameMessage: string },\n): void {\n\tif (!first || !second) {\n\t\terror(where.missing, code, where.missingMessage);\n\t}\n\tif (first && first === second) {\n\t\terror(where.same, code, where.sameMessage);\n\t}\n}\n\n/** Report each of `keys` the declaration carries, with the same message. */\nfunction refuseKeys(def: Raw, keys: readonly string[], error: RegisterReporter, code: string, message: (key: string) => string): void {\n\tfor (const key of keys) {\n\t\tif (hasKey(def, key)) {\n\t\t\terror([key], code, message(key));\n\t\t}\n\t}\n}\n\nfunction checkProperties({ def, kind, error }: Pick<Context, \"def\" | \"kind\" | \"error\">): void {\n\tfor (const key of registerRequiredProps(kind)) {\n\t\tif (safeTrim(def[key]) === undefined) {\n\t\t\terror([key], \"required\", `${KIND_LABEL[kind].sentence} needs '${key}'.`);\n\t\t}\n\t}\n\trefuseKeys(def, registerRefusedProps(kind), error, \"refused\", (key) => `'${key}' isn't allowed on ${KIND_LABEL[kind].inline}.`);\n\tif ((safeTrim(def.journalDirection) || safeTrim(def.journalDate)) && !safeTrim(def.journal)) {\n\t\terror([\"journal\"], \"journal-stamps\", \"'journalDirection' and 'journalDate' are columns of the journal — name the journal entity.\");\n\t}\n\tif (kind === \"info\" && def.recordChangesOnly === true && !safeTrim(def.recorder)) {\n\t\terror([\"recordChangesOnly\"], \"record-changes-only\", \"'recordChangesOnly' needs a 'recorder' — only a document's record can be compared with the slice in effect.\");\n\t}\n}\n\nfunction resolveEntities(\n\tdef: Raw,\n\tkind: RegisterKind,\n\tchecks: EntityChecks,\n): Pick<Context, \"journalCode\" | \"journal\" | \"carrierCode\" | \"carrier\"> {\n\tconst { entityAt, columnAt } = checks;\n\tconst entityCode = safeTrim(def.entity);\n\tconst journalCode = safeTrim(def.journal);\n\tconst carriedByEntity = kind === \"balance\" || kind === \"info\";\n\tconst entity = carriedByEntity ? entityAt([\"entity\"], entityCode, \"Entity\") : null;\n\tconst journal = kind !== \"info\" ? entityAt([\"journal\"], journalCode, \"Journal\") : null;\n\tif (kind === \"accounting\") {\n\t\tentityAt([\"chart\"], safeTrim(def.chart), \"Chart\");\n\t}\n\n\tcolumnAt([\"journalDirection\"], journal, journalCode, def.journalDirection, \"Journal direction column\");\n\tcolumnAt([\"journalDate\"], journal, journalCode, def.journalDate, \"Journal date column\");\n\tcolumnAt([\"period\"], entity, entityCode, def.period, \"Period column\");\n\tcolumnAt([\"recorder\"], entity, entityCode, def.recorder, \"Recorder column\");\n\n\treturn { journalCode, journal, carrierCode: registerCarrier(def), carrier: carriedByEntity ? entity : journal };\n}\n\nfunction checkDimensions({ def, dimensions, sides, carrier, carrierCode, columnAt, error }: Context): void {\n\tif (!Array.isArray(def.dimensions) || dimensions.length === 0) {\n\t\terror([\"dimensions\"], \"dimensions\", \"A register needs at least one dimension.\");\n\t}\n\tif (dimensions.length > MAX_DIMENSIONS) {\n\t\terror([\"dimensions\"], \"dimensions\", `At most ${MAX_DIMENSIONS} dimensions.`);\n\t}\n\tconst twice = dimensions.filter((name, index) => dimensions.indexOf(name) !== index);\n\tif (twice.length > 0) {\n\t\terror([\"dimensions\"], \"dimensions\", `Dimension listed more than once: ${[...new Set(twice)].join(\", \")}.`);\n\t}\n\tdimensions.forEach((name, i) => {\n\t\t// A two-sided dimension lives in its side columns, not under its own name.\n\t\tif (!(name in sides)) {\n\t\t\tcolumnAt([\"dimensions\", i], carrier, carrierCode, name, \"Dimension\");\n\t\t}\n\t});\n}\n\n/** The shape a resource's columns take, per kind and line mode. */\nfunction checkResourceShape(ctx: Context, name: string, resource: Raw, path: (key?: string) => IssuePath): void {\n\tconst { def, kind, error } = ctx;\n\tconst field = safeTrim(resource.field);\n\tconst debit = safeTrim(resource.debitField);\n\tconst credit = safeTrim(resource.creditField);\n\tconst split = registerResourceShape(resource) === \"split\";\n\tconst classic = kind === \"accounting\" && registerLineMode(def) === \"classic\";\n\tif (split && hasKey(resource, \"field\")) {\n\t\terror(path(\"field\"), \"resource-shape\", `Resource '${name}': either 'field' or 'debitField' + 'creditField', not both.`);\n\t}\n\tif (classic && !split) {\n\t\terror(path(), \"resource-shape\", `Resource '${name}': on a classic accounting register every resource is split — 'debitField' and 'creditField'.`);\n\t} else if (kind !== \"accounting\" && split) {\n\t\terror(path(), \"resource-shape\", `Resource '${name}': 'debitField' / 'creditField' belong to an accounting register — name one 'field'.`);\n\t} else if (split) {\n\t\tcheckColumnPair(error, \"resource-shape\", debit, credit, {\n\t\t\tmissing: path(),\n\t\t\tmissingMessage: `Resource '${name}' needs both 'debitField' and 'creditField'.`,\n\t\t\tsame: path(\"creditField\"),\n\t\t\tsameMessage: `Resource '${name}': the debit and credit columns must differ.`,\n\t\t});\n\t} else if (!field) {\n\t\terror(path(), \"resource-shape\", `Resource '${name}' needs its column ('field').`);\n\t}\n}\n\nfunction checkResources(ctx: Context): void {\n\tconst { def, kind, resources, dimensions, carrier, carrierCode, columnAt, error } = ctx;\n\tif (!isRecord(def.resources) || resources.length === 0) {\n\t\terror([\"resources\"], \"resources\", \"A register needs at least one resource.\");\n\t}\n\tif (resources.length > MAX_RESOURCES) {\n\t\terror([\"resources\"], \"resources\", `At most ${MAX_RESOURCES} resources.`);\n\t}\n\tconst takesAbsentAs = kind === \"info\" && !!safeTrim(def.recorder) && def.recordChangesOnly === true;\n\tconst storage = new Map<string, string>();\n\tfor (const { name, def: resource } of resources) {\n\t\tconst path = (key?: string): IssuePath => (key ? [\"resources\", name, key] : [\"resources\", name]);\n\t\tif (!REGISTER_COLUMN_NAME.test(name)) {\n\t\t\terror(path(), \"resource-name\", `Resource '${name}' isn't a name (lowercase letters, digits and '_').`);\n\t\t}\n\t\tcheckResourceShape(ctx, name, resource, path);\n\t\tfor (const key of [\"field\", \"debitField\", \"creditField\"] as const) {\n\t\t\tconst column = safeTrim(resource[key]);\n\t\t\tif (!column) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcolumnAt(path(key), carrier, carrierCode, column, `Resource '${name}' column`);\n\t\t\tconst other = storage.get(column);\n\t\t\tif (other && other !== name) {\n\t\t\t\terror(path(key), \"resource-column-shared\", `Resources '${other}' and '${name}' both store into '${column}' — posting would add both into one column.`);\n\t\t\t}\n\t\t\tstorage.set(column, name);\n\t\t}\n\t\tconst field = safeTrim(resource.field);\n\t\tif (field && dimensions.includes(field)) {\n\t\t\terror(path(\"field\"), \"resource-is-dimension\", `'${field}' is both a dimension and resource '${name}'.`);\n\t\t}\n\t\tif (hasKey(resource, \"absentAs\") && !takesAbsentAs) {\n\t\t\terror(path(\"absentAs\"), \"absent-as\", `Resource '${name}': 'absentAs' needs 'recordChangesOnly' on a subordinate information register.`);\n\t\t}\n\t}\n}\n\nfunction checkAnalyticSlotColumns(ctx: Context, key: string, columns: string[]): void {\n\tcolumns.forEach((column, i) => ctx.columnAt([key, i], ctx.journal, ctx.journalCode, column, \"Analytic slot column\"));\n}\n\nfunction checkSideDimensions(ctx: Context): void {\n\tconst { dimensions, sides, journal, journalCode, columnAt, error } = ctx;\n\tfor (const [name, side] of Object.entries(sides)) {\n\t\tconst base: IssuePath = [\"sideDimensions\", name];\n\t\tif (!dimensions.includes(name)) {\n\t\t\terror(base, \"side-dimension\", `Two-sided dimension '${name}' must also be listed in 'dimensions'.`);\n\t\t}\n\t\tconst sideDef = isRecord(side) ? side : {};\n\t\tconst debitField = safeTrim(sideDef.debitField);\n\t\tconst creditField = safeTrim(sideDef.creditField);\n\t\tcheckColumnPair(error, \"side-dimension\", debitField, creditField, {\n\t\t\tmissing: base,\n\t\t\tmissingMessage: `Two-sided dimension '${name}' needs 'debitField' and 'creditField'.`,\n\t\t\tsame: [...base, \"creditField\"],\n\t\t\tsameMessage: `Two-sided dimension '${name}': the two columns must differ.`,\n\t\t});\n\t\tcolumnAt([...base, \"debitField\"], journal, journalCode, debitField, `Dimension '${name}' debit column`);\n\t\tcolumnAt([...base, \"creditField\"], journal, journalCode, creditField, `Dimension '${name}' credit column`);\n\t}\n}\n\n/** Returns whether the register declares analytic slots. */\nfunction checkCompoundAccounts(ctx: Context): boolean {\n\tconst { def, journal, journalCode, columnAt, error } = ctx;\n\trefuseKeys(def, [\"account\", \"analyticFields\"], error, \"line-mode\", (key) => `'${key}' belongs to a classic register; a compound one names 'debitAccount' and 'creditAccount'.`);\n\tconst debit = safeTrim(def.debitAccount);\n\tconst credit = safeTrim(def.creditAccount);\n\tcheckColumnPair(error, \"accounts\", debit, credit, {\n\t\tmissing: [\"debitAccount\"],\n\t\tmissingMessage: \"A compound register needs 'debitAccount' and 'creditAccount'.\",\n\t\tsame: [\"creditAccount\"],\n\t\tsameMessage: \"'debitAccount' and 'creditAccount' must differ — one column would record no correspondence.\",\n\t});\n\tcolumnAt([\"debitAccount\"], journal, journalCode, debit, \"Debit account column\");\n\tcolumnAt([\"creditAccount\"], journal, journalCode, credit, \"Credit account column\");\n\n\tconst debitSlots = stringList(def.debitAnalyticFields);\n\tconst creditSlots = stringList(def.creditAnalyticFields);\n\tif (debitSlots.length > MAX_ANALYTIC_SLOTS || creditSlots.length > MAX_ANALYTIC_SLOTS) {\n\t\terror([\"debitAnalyticFields\"], \"analytic-slots\", `At most ${MAX_ANALYTIC_SLOTS} analytic slots per side.`);\n\t}\n\tconst shared = debitSlots.filter((column) => creditSlots.includes(column));\n\tif (shared.length > 0) {\n\t\terror([\"creditAnalyticFields\"], \"analytic-slots\", `Analytic slot column on both sides: ${shared.join(\", \")}.`);\n\t}\n\tcheckAnalyticSlotColumns(ctx, \"debitAnalyticFields\", debitSlots);\n\tcheckAnalyticSlotColumns(ctx, \"creditAnalyticFields\", creditSlots);\n\tconst needsComposition = debitSlots.length > 0 || creditSlots.length > 0;\n\tcheckSideDimensions(ctx);\n\treturn needsComposition;\n}\n\n/** Returns whether the register declares analytic slots. */\nfunction checkClassicAccounts(ctx: Context): boolean {\n\tconst { def, journal, journalCode, columnAt, error } = ctx;\n\trefuseKeys(def, [\"debitAccount\", \"creditAccount\", \"debitAnalyticFields\", \"creditAnalyticFields\", \"sideDimensions\"], error, \"line-mode\", (key) => `'${key}' belongs to a compound register.`);\n\tif (!safeTrim(def.account)) {\n\t\terror([\"account\"], \"accounts\", \"A classic register names the posting's account column ('account').\");\n\t}\n\tcolumnAt([\"account\"], journal, journalCode, safeTrim(def.account), \"Account column\");\n\tconst slots = stringList(def.analyticFields);\n\tif (slots.length > MAX_ANALYTIC_SLOTS) {\n\t\terror([\"analyticFields\"], \"analytic-slots\", `At most ${MAX_ANALYTIC_SLOTS} analytic slots.`);\n\t}\n\tcheckAnalyticSlotColumns(ctx, \"analyticFields\", slots);\n\treturn slots.length > 0;\n}\n\nfunction checkComposition(ctx: Context, needsComposition: boolean): void {\n\tconst { def, entityAt, columnAt, error } = ctx;\n\tconst composition = isRecord(def.composition) ? def.composition : undefined;\n\tif (needsComposition && !composition) {\n\t\terror([\"composition\"], \"composition\", \"Analytic slots need a 'composition' — where the chart says which analytic each account takes in which slot.\");\n\t}\n\tif (!needsComposition && composition) {\n\t\terror([\"composition\"], \"composition\", \"'composition' is for a register with analytic slots; this one declares none.\");\n\t}\n\tif (!composition) {\n\t\treturn;\n\t}\n\tfor (const key of [\"entity\", \"account\", \"slot\", \"analytic\"]) {\n\t\tif (!safeTrim(composition[key])) {\n\t\t\terror([\"composition\", key], \"composition\", `'composition' needs '${key}'.`);\n\t\t}\n\t}\n\tconst code = safeTrim(composition.entity);\n\tconst entity = entityAt([\"composition\", \"entity\"], code, \"Composition entity\");\n\tfor (const key of [\"account\", \"slot\", \"analytic\"]) {\n\t\tcolumnAt([\"composition\", key], entity, code, composition[key], `Composition '${key}' column`);\n\t}\n}\n\nfunction checkAccounting(ctx: Context): void {\n\tconst { def, kind, error } = ctx;\n\tif (kind !== \"accounting\") {\n\t\treturn;\n\t}\n\tconst mode = safeTrim(def.lineMode);\n\tif (mode && mode !== \"classic\" && mode !== \"compound\") {\n\t\terror([\"lineMode\"], \"line-mode\", \"'lineMode' is 'classic' or 'compound'.\");\n\t}\n\tcheckComposition(ctx, mode === \"compound\" ? checkCompoundAccounts(ctx) : checkClassicAccounts(ctx));\n}\n\nfunction checkTotals(ctx: Context): void {\n\tconst { def, kind, dimensions, resources, entityAt, columnAt, error } = ctx;\n\tconst totals = isRecord(def.totals) ? def.totals : undefined;\n\tif (!totals || kind === \"info\") {\n\t\treturn;\n\t}\n\tconst totalsCode = safeTrim(totals.entity);\n\tif (!totalsCode) {\n\t\terror([\"totals\", \"entity\"], \"totals\", \"The totals cache needs its entity.\");\n\t}\n\tconst totalsEntity = entityAt([\"totals\", \"entity\"], totalsCode, \"Totals entity\");\n\tconst period = safeTrim(totals.period);\n\tif (!period || !(REGISTER_TOTALS_PERIODS as readonly string[]).includes(period)) {\n\t\terror([\"totals\", \"period\"], \"totals\", \"The totals cache needs a period: day, month, quarter or year.\");\n\t}\n\tif (!safeTrim(totals.periodField)) {\n\t\terror([\"totals\", \"periodField\"], \"totals\", \"The totals cache needs 'periodField' — the column holding each period's first date.\");\n\t}\n\tcolumnAt([\"totals\", \"periodField\"], totalsEntity, totalsCode, safeTrim(totals.periodField), \"Totals period column\");\n\tif (kind === \"accounting\") {\n\t\tif (!safeTrim(totals.accountField)) {\n\t\t\terror([\"totals\", \"accountField\"], \"totals\", \"An accounting register's cache needs 'accountField'.\");\n\t\t}\n\t\tcolumnAt([\"totals\", \"accountField\"], totalsEntity, totalsCode, safeTrim(totals.accountField), \"Totals account column\");\n\t\tstringList(totals.dimensions).forEach((name, i) => {\n\t\t\tif (!dimensions.includes(name)) {\n\t\t\t\terror([\"totals\", \"dimensions\", i], \"totals\", `Totals dimension '${name}' isn't a dimension of the register.`);\n\t\t\t}\n\t\t});\n\t} else {\n\t\tif (hasKey(totals, \"accountField\")) {\n\t\t\terror([\"totals\", \"accountField\"], \"totals\", \"'accountField' belongs to an accounting register's cache.\");\n\t\t}\n\t\tif (hasKey(totals, \"dimensions\")) {\n\t\t\terror([\"totals\", \"dimensions\"], \"totals\", \"A cache grouped by fewer dimensions is for an accounting register only.\");\n\t\t}\n\t}\n\tconst cached = isRecord(totals.resources) ? totals.resources : {};\n\tconst pair = kind === \"accounting\" ? [\"debitField\", \"creditField\"] : [\"inField\", \"outField\"];\n\tfor (const { name } of resources) {\n\t\tconst entry = cached[name];\n\t\tif (!isRecord(entry)) {\n\t\t\terror([\"totals\", \"resources\"], \"totals\", `The totals cache must cover every resource — '${name}' is missing.`);\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const key of pair) {\n\t\t\tif (!safeTrim(entry[key])) {\n\t\t\t\terror([\"totals\", \"resources\", name], \"totals\", `Totals resource '${name}' needs '${key}'.`);\n\t\t\t}\n\t\t\tcolumnAt([\"totals\", \"resources\", name, key], totalsEntity, totalsCode, safeTrim(entry[key]), `Totals '${name}' ${key}`);\n\t\t}\n\t}\n\tfor (const name of Object.keys(cached)) {\n\t\tif (!resources.some((resource) => resource.name === name)) {\n\t\t\terror([\"totals\", \"resources\", name], \"totals\", `Totals cache names '${name}', which isn't a resource of the register.`);\n\t\t}\n\t}\n}\n\nfunction checkWarnings({ def, kind, journalCode, warning }: Context): void {\n\tif (!safeTrim(def.label)) {\n\t\twarning([\"label\"], \"label\", \"No label — the query builder shows the code.\");\n\t}\n\tif (kind === \"balance\") {\n\t\tif (!journalCode) {\n\t\t\twarning([\"journal\"], \"no-journal\", \"No journal: unposting has to recompute from the document, and as-of-date reads are refused.\");\n\t\t} else if (!safeTrim(def.journalDirection) || !safeTrim(def.journalDate)) {\n\t\t\twarning([\"journal\"], \"journal-stamps\", \"Declare both journal stamps (direction and date) from the start — rows journalled without them can't be backfilled.\");\n\t\t}\n\t}\n}\n\n/**\n * Check one declaration against the schema's per-kind rules and the installer's\n * — paths relative to the declaration. With a lookup, the entities it names are\n * checked to exist and the columns it names to be stored on them.\n */\nexport function checkRegister(raw: unknown, lookup?: RegisterEntityLookup): RegisterIssue[] {\n\tconst out: RegisterIssue[] = [];\n\tconst error = createIssueReporter(out, \"registers\", \"error\");\n\tconst warning = createIssueReporter(out, \"registers\", \"warning\");\n\tif (!isRecord(raw)) {\n\t\terror([], \"shape\", \"A register declaration is a JSON object.\");\n\t\treturn out;\n\t}\n\tconst def = raw;\n\tif (!isRegisterKind(def.kind)) {\n\t\terror([\"kind\"], \"kind\", \"Pick a kind: balance, turnover, info or accounting.\");\n\t\treturn out;\n\t}\n\tconst kind = def.kind;\n\tcheckProperties({ def, kind, error });\n\tconst checks = createEntityChecks(lookup, error, warning);\n\tconst ctx: Context = {\n\t\tdef, kind, error, warning, ...checks,\n\t\t...resolveEntities(def, kind, checks),\n\t\tdimensions: registerDimensions(def),\n\t\tsides: isRecord(def.sideDimensions) ? def.sideDimensions : {},\n\t\tresources: registerResources(def),\n\t};\n\tcheckDimensions(ctx);\n\tcheckResources(ctx);\n\tcheckAccounting(ctx);\n\tcheckTotals(ctx);\n\tcheckWarnings(ctx);\n\treturn out;\n}\n\n/**\n * Check a whole `registers.json`: every declaration (paths prefixed by its\n * code), plus the codes themselves and the file's limits.\n */\nexport function checkRegisters(file: unknown, lookup?: RegisterEntityLookup): RegisterIssue[] {\n\tconst out: RegisterIssue[] = [];\n\tconst error = createIssueReporter(out, \"registers\", \"error\");\n\tif (!isRecord(file)) {\n\t\terror([], \"shape\", \"registers.json is an object of register declarations.\");\n\t\treturn out;\n\t}\n\tconst codes = Object.keys(file).filter((code) => !code.startsWith(\"$\"));\n\tif (codes.length === 0) {\n\t\terror([], \"empty\", \"A module with no registers has no registers.json — the file needs at least one.\");\n\t}\n\tif (codes.length > MAX_REGISTERS) {\n\t\terror([], \"limit\", `A module declares at most ${MAX_REGISTERS} registers.`);\n\t}\n\tfor (const code of codes) {\n\t\tif (!REGISTER_CODE.test(code)) {\n\t\t\terror([code], \"code\", `'${code}': lowercase letters, digits and '_', starting with a letter — a binding names it as module.code.`);\n\t\t}\n\t\tfor (const issue of checkRegister(file[code], lookup)) {\n\t\t\tout.push({ ...issue, path: [code, ...issue.path] });\n\t\t}\n\t}\n\treturn out;\n}\n","// Checking an A / L column's `params` against the register it binds.\n\nimport { createIssueReporter } from \"./issues\";\nimport type { RegisterIssue, RegisterReporter } from \"./issues\";\nimport { hasKey, isRecord, registerResourceShape, safeTrim, stringList } from \"./read\";\nimport type { Raw } from \"./read\";\nimport type { RegisterDef } from \"./types\";\n\n/** What a binding is checked against: the parts of a declaration it has to agree with. */\nexport type RegisterBindingTarget = Pick<RegisterDef, \"kind\" | \"dimensions\" | \"resources\" | \"recorder\" | \"lineMode\" | \"sideDimensions\">;\n\n/** A reference path (`ref.column`), an expression (`=…`) or a quoted constant — not a plain column. */\nexport const isPlainColumnRef = (value: string): boolean => !value.startsWith(\"=\") && !value.startsWith(\"'\") && !value.includes(\".\");\n\ninterface BindingContext {\n\tparams: Raw;\n\tregister: RegisterBindingTarget;\n\tqualified: string;\n\tledger: boolean;\n\terror: RegisterReporter;\n}\n\nfunction checkPosting(params: Raw, ledger: boolean, error: RegisterReporter): void {\n\tconst lineEntity = safeTrim(params.lineEntity);\n\tconst locked = stringList(params.lockedFields);\n\tconst lockedLines = stringList(params.lockedLineFields);\n\n\tif (!Array.isArray(params.lockedFields) || locked.length === 0) {\n\t\terror([\"lockedFields\"], \"register-locked-fields\", \"'lockedFields' is required: the document columns frozen while posted (or [\\\"*\\\"] for the whole row).\");\n\t}\n\tif (lineEntity) {\n\t\tif (!safeTrim(params.lineField)) {\n\t\t\terror([\"lineField\"], \"register-line\", \"A line posting needs 'lineField' — the line's key to the document.\");\n\t\t}\n\t\tif (!Array.isArray(params.lockedLineFields) || lockedLines.length === 0) {\n\t\t\terror([\"lockedLineFields\"], \"register-locked-fields\", \"A line posting needs 'lockedLineFields' (or [\\\"*\\\"]).\");\n\t\t}\n\t} else {\n\t\tif (hasKey(params, \"lineField\")) {\n\t\t\terror([\"lineField\"], \"register-line\", \"'lineField' needs a 'lineEntity'.\");\n\t\t}\n\t\tif (hasKey(params, \"lockedLineFields\")) {\n\t\t\terror([\"lockedLineFields\"], \"register-line\", \"'lockedLineFields' is for a line posting — name the 'lineEntity'.\");\n\t\t}\n\t\tif (hasKey(params, \"headerMap\")) {\n\t\t\terror([\"headerMap\"], \"register-header-map\", \"'headerMap' is for a binding that posts from lines; a document posting reads every value from the document already.\");\n\t\t}\n\t\tif (hasKey(params, \"groupLinesBy\")) {\n\t\t\terror([\"groupLinesBy\"], \"register-group-lines\", \"'groupLinesBy' needs lines to group — name the 'lineEntity'.\");\n\t\t}\n\t}\n\tif (ledger) {\n\t\tif (!lineEntity) {\n\t\t\terror([\"lineEntity\"], \"register-line\", \"An L column posts lines: 'lineEntity' and 'lineField' are required.\");\n\t\t}\n\t\tfor (const key of [\"headerMap\", \"skipPostingWhenEmpty\", \"dateTrunc\", \"groupLinesBy\"]) {\n\t\t\tif (hasKey(params, key)) {\n\t\t\t\terror([key], \"register-ledger-only\", `'${key}' is read by A columns only.`);\n\t\t\t}\n\t\t}\n\t}\n\tconst dateField = safeTrim(params.dateField);\n\tif (hasKey(params, \"dateTrunc\") && !dateField) {\n\t\terror([\"dateTrunc\"], \"register-date\", \"'dateTrunc' needs a 'dateField'.\");\n\t}\n\tif (dateField && !locked.includes(\"*\") && isPlainColumnRef(dateField) && !locked.includes(dateField)) {\n\t\terror([\"dateField\"], \"register-date-unlocked\", `'${dateField}' must be in 'lockedFields': a posted document re-dated would leave what it posted under the old date.`);\n\t}\n}\n\nfunction checkInfoBinding({ params, register, qualified, error }: BindingContext): void {\n\tif (!safeTrim(register.recorder)) {\n\t\terror([\"register\"], \"register-info-independent\", `\"${qualified}\" is an independent information register (no 'recorder'): its records are written by data operations, not by posting — unposting could never find them.`);\n\t}\n\tfor (const key of [\"direction\", \"sign\"]) {\n\t\tif (hasKey(params, key)) {\n\t\t\terror([key], \"register-info-direction\", `'${key}' is refused on a binding to an information register, which records values rather than quantities.`);\n\t\t}\n\t}\n}\n\nfunction checkLedgerAccounts({ params, register, error }: BindingContext): void {\n\tif (register.lineMode === \"compound\") {\n\t\tif (hasKey(params, \"accountField\")) {\n\t\t\terror([\"accountField\"], \"register-accounts\", \"A compound register takes 'debitAccountField' and 'creditAccountField', not 'accountField'.\");\n\t\t}\n\t\tif (!safeTrim(params.debitAccountField) || !safeTrim(params.creditAccountField)) {\n\t\t\terror([\"register\"], \"register-accounts\", \"A binding to a compound register names 'debitAccountField' and 'creditAccountField'.\");\n\t\t}\n\t\treturn;\n\t}\n\tif (!safeTrim(params.accountField)) {\n\t\terror([\"register\"], \"register-accounts\", \"A binding to a classic register names the line's 'accountField'.\");\n\t}\n\tfor (const key of [\"debitAccountField\", \"creditAccountField\"]) {\n\t\tif (hasKey(params, key)) {\n\t\t\terror([key], \"register-accounts\", `'${key}' belongs to a compound register; a classic one takes 'accountField'.`);\n\t\t}\n\t}\n}\n\n/** A ledger `map` entry takes a column per side where the register keeps one per side. */\nfunction checkLedgerMapValue(ctx: BindingContext, key: string, value: unknown, resources: Raw): void {\n\tconst { register, error } = ctx;\n\tconst resource = resources[key];\n\tconst split = key in resources\n\t\t? isRecord(resource) && registerResourceShape(resource) === \"split\"\n\t\t: isRecord(register.sideDimensions) && key in register.sideDimensions;\n\tif (split) {\n\t\tif (!isRecord(value) || !safeTrim(value.debit) || !safeTrim(value.credit)) {\n\t\t\terror([\"map\", key], \"register-map-value\", `\"${key}\" has a column per side; map both 'debit' and 'credit' to line columns.`);\n\t\t}\n\t} else if (!safeTrim(value)) {\n\t\terror([\"map\", key], \"register-map-value\", `\"${key}\" is shared between the sides; map it to one line column.`);\n\t}\n}\n\n/** Returns the names the binding maps, from `map` and `headerMap` alike. */\nfunction checkMaps(ctx: BindingContext, dimensions: string[], resources: Raw): Set<string> {\n\tconst { params, qualified, ledger, register, error } = ctx;\n\tconst known = new Set([...dimensions, ...Object.keys(resources)]);\n\tconst mapped = new Set<string>();\n\tfor (const mapKey of [\"map\", \"headerMap\"] as const) {\n\t\tconst map = isRecord(params[mapKey]) ? params[mapKey] : null;\n\t\tif (!map) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const key of Object.keys(map)) {\n\t\t\tif (mapped.has(key)) {\n\t\t\t\terror([mapKey, key], \"register-map-key\", `\"${key}\" is in both 'map' and 'headerMap' — one value cannot come from two places.`);\n\t\t\t}\n\t\t\tmapped.add(key);\n\t\t\tif (!known.has(key)) {\n\t\t\t\terror([mapKey, key], \"register-map-key\", `\"${key}\" is neither a dimension nor a resource of ${qualified} (dimensions: ${dimensions.join(\", \") || \"none\"}; resources: ${Object.keys(resources).join(\", \") || \"none\"}).`);\n\t\t\t} else if (ledger && mapKey === \"map\") {\n\t\t\t\tcheckLedgerMapValue(ctx, key, map[key], resources);\n\t\t\t} else if (mapKey === \"headerMap\" && key in resources && register.kind !== \"info\") {\n\t\t\t\terror([mapKey, key], \"register-header-map\", `A resource (\"${key}\") can't come from the document on a line posting — it would be added once per line. Only an information register's records take it.`);\n\t\t\t}\n\t\t}\n\t}\n\treturn mapped;\n}\n\nfunction checkMapping(ctx: BindingContext): void {\n\tconst { params, register, qualified, error } = ctx;\n\tconst dimensions = stringList(register.dimensions);\n\tconst resources: Raw = isRecord(register.resources) ? register.resources : {};\n\tconst mapped = checkMaps(ctx, dimensions, resources);\n\n\tconst unmapped = Object.entries(resources)\n\t\t.filter(([resource, def]) => !mapped.has(resource) && !(isRecord(def) && def.required === false))\n\t\t.map(([resource]) => resource);\n\tif (unmapped.length > 0) {\n\t\terror(\n\t\t\t[hasKey(params, \"map\") ? \"map\" : \"register\"],\n\t\t\t\"register-unmapped-resource\",\n\t\t\t`Every required resource of ${qualified} must be mapped — missing: ${unmapped.join(\", \")}. A writer that omitted one would post a silent zero.`,\n\t\t);\n\t}\n\tstringList(params.skipPostingWhenEmpty).forEach((name, i) => {\n\t\tif (!dimensions.includes(name)) {\n\t\t\terror([\"skipPostingWhenEmpty\", i], \"register-skip\", `\"${name}\" isn't a dimension of ${qualified}; 'skipPostingWhenEmpty' names register dimensions.`);\n\t\t}\n\t});\n}\n\n/**\n * Check an A / L column's `params` against the register it binds — paths\n * relative to `params`. `register` is the resolved declaration, `undefined`\n * when the module named declares no such register, `null` when that module\n * can't be seen (every rule that needs the declaration then stands down).\n */\nexport function checkRegisterBinding(\n\tcolumnType: \"A\" | \"L\",\n\trawParams: unknown,\n\tregister: RegisterBindingTarget | null | undefined,\n\toptions: { moduleCode?: string } = {},\n): RegisterIssue[] {\n\tconst out: RegisterIssue[] = [];\n\tconst error = createIssueReporter(out, \"entity\", \"error\");\n\tif (!isRecord(rawParams)) {\n\t\treturn out;\n\t}\n\tconst params = rawParams;\n\tconst qualified = safeTrim(params.register);\n\tif (!qualified) {\n\t\treturn out;\n\t}\n\tconst ledger = columnType === \"L\";\n\n\tif (hasKey(params, \"registries\")) {\n\t\terror([\"registries\"], \"register-and-inline\", \"A column binds to a register OR restates one inline in 'registries' — keep one.\");\n\t}\n\tif (!qualified.includes(\".\")) {\n\t\terror([\"register\"], \"register-unqualified\", `A register is named as module.register, e.g. \"${options.moduleCode ?? \"module\"}.${qualified}\".`);\n\t\treturn out;\n\t}\n\tconst dot = qualified.indexOf(\".\");\n\tconst moduleCode = qualified.slice(0, dot);\n\tconst registerCode = qualified.slice(dot + 1);\n\n\t// Posting inputs are validated even when the target register cannot be resolved.\n\tcheckPosting(params, ledger, error);\n\n\tif (register === null) {\n\t\treturn out;\n\t}\n\tif (!register) {\n\t\terror([\"register\"], \"unknown-register\", `No register \"${qualified}\" — ${moduleCode}/registers.json doesn't declare \"${registerCode}\".`);\n\t\treturn out;\n\t}\n\tconst kind = register.kind;\n\tif (ledger && kind !== \"accounting\") {\n\t\terror([\"register\"], \"register-kind\", `An L column posts to an accounting register; \"${qualified}\" is a ${kind || \"?\"} register — use an A column.`);\n\t\treturn out;\n\t}\n\tif (!ledger && kind === \"accounting\") {\n\t\terror([\"register\"], \"register-kind\", `An A column posts to a balance, turnover or info register; \"${qualified}\" is an accounting register — use an L column.`);\n\t\treturn out;\n\t}\n\tconst ctx: BindingContext = { params, register, qualified, ledger, error };\n\tif (kind === \"info\") {\n\t\tcheckInfoBinding(ctx);\n\t}\n\t// No rule on a missing `direction`: the engine posts an increase by default\n\t// (gl's VAT binding ships without one).\n\tif (ledger) {\n\t\tcheckLedgerAccounts(ctx);\n\t}\n\tcheckMapping(ctx);\n\treturn out;\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, stored procedures, menus, folders,\n// roles, settings, registers, jobs, triggers, webhooks, print templates, seed data,\n// translations, actions, diagrams), mirrored from the JSON schemas under docs/schemas/;\n// • shared rules — the action-DSL analyzer (./dsl) and the register checks\n// (checkRegisters / checkRegisterBinding), run by editors and validators alike.\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\tEntityReaction,\n\tEntityReference,\n\tEntityViewDef,\n\tEntityViewColumnDef,\n\tNumberSequenceDef,\n\tPeriodConfig,\n\tAccumulationConfig,\n\tSignConfig,\n\tLedgerRegistryEntry,\n\tTraitCd,\n\tAuditHistoryLevel,\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 {\n\tStoredProceduresFile,\n\tStoredProcedureDef,\n\tSpParamDef,\n\tSpColumnDef,\n} from \"./stored-procedures\";\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 { DiagramFile, DiagramDef, DiagramEntity, DiagramDetail, DiagramRelation } from \"./diagrams\";\nexport type { RolesFile, RoleDef } from \"./roles\";\nexport type {\n\tTranslationsFile,\n\tTranslatedAction,\n\tTranslatedEntity,\n\tTranslatedMenu,\n\tTranslatedOption,\n\tTranslatedReport,\n\tTranslatedWithOptions,\n} from \"./translations\";\nexport { settingDefsOf } from \"./settings\";\nexport type { SettingsFile, SettingDef, SettingConstraintDef, SettingBaseDatatype } from \"./settings\";\nexport {\n\tREGISTER_KINDS,\n\tREGISTER_CODE,\n\tREGISTER_COLUMN_NAME,\n\tREGISTER_TOTALS_PERIODS,\n\tisRegisterKind,\n\tisPlainColumnRef,\n\tregisterDimensions,\n\tregisterResources,\n\tregisterCarrier,\n\tregisterResourceShape,\n\tregisterLineMode,\n\tregisterRefusedProps,\n\tregisterRequiredProps,\n\tcheckRegister,\n\tcheckRegisters,\n\tcheckRegisterBinding,\n\ttype RegisterKind,\n\ttype RegisterDef,\n\ttype RegistersFile,\n\ttype RegisterResourceDef,\n\ttype RegisterTotalsDef,\n\ttype RegisterTotalsPeriod,\n\ttype RegisterCompositionDef,\n\ttype RegisterBindingParams,\n\ttype RegisterMapValue,\n\ttype RegisterBindingTarget,\n\ttype RegisterIssue,\n\ttype RegisterEntityInfo,\n\ttype RegisterEntityLookup,\n} from \"./registers\";\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, SeedDataRepeat } 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.39",
3
+ "version": "0.0.41",
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",
@@ -94,8 +94,8 @@
94
94
  },
95
95
  "provenance": {
96
96
  "type": "string",
97
- "pattern": "^(extends:[^.]+\\.[^.]+|(ref|formula|action|view|report|print|role):.+)$",
98
- "description": "A usage token of the form '<kind>:<symbol>'. Kinds: ref:<entity.column> (a link column joins to the dependency; the entity is qualified <module>.<entity> when it's an extension host), extends:<module.entity> (the consumer extends the provider entity — MUST be fully qualified, e.g. extends:parties.party, never bare extends:party), formula:<entity.column> (a formula nav path reads it), action:<action_cd> (DSL reads/writes it, incl. raw query() SQL), view:<view_cd> (a data view's dataSource targets it), report:<report_cd> (a report queries it), print:<template_cd> (a print template navigates into it), role:<role_cd> (a security role grants rights on the entity). Note: 'display' is intentionally NOT a kind — the label is the provider's concern."
97
+ "pattern": "^(extends:[^.]+\\.[^.]+|(ref|formula|action|function|reaction|view|report|print|role):.+)$",
98
+ "description": "A usage token of the form '<kind>:<symbol>'. Kinds: ref:<entity.column> (a link column joins to the dependency; the entity is qualified <module>.<entity> when it's an extension host), extends:<module.entity> (the consumer extends the provider entity — MUST be fully qualified, e.g. extends:parties.party, never bare extends:party), formula:<entity.column> (a formula nav path reads it), action:<action_cd> (DSL reads/writes it, incl. raw query() SQL), function:<library> (a function in logic/functions/<library>.dsl reads/writes it), reaction:<code> (a field-change reaction the consumer's extension declares on the dependency entity reads or sets it), view:<view_cd> (a data view's dataSource targets it), report:<report_cd> (a report queries it), print:<template_cd> (a print template navigates into it), role:<role_cd> (a security role grants rights on the entity). Note: 'display' is intentionally NOT a kind — the label is the provider's concern."
99
99
  }
100
100
  }
101
101
  }
@@ -6,7 +6,8 @@
6
6
  "type": "object",
7
7
  "anyOf": [
8
8
  { "required": ["fields"] },
9
- { "required": ["traits"] }
9
+ { "required": ["traits"] },
10
+ { "required": ["extends", "reactions"] }
10
11
  ],
11
12
  "properties": {
12
13
  "extends": {
@@ -33,6 +34,73 @@
33
34
  "type": "boolean",
34
35
  "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."
35
36
  },
37
+ "reactions": {
38
+ "type": "array",
39
+ "description": "Field-change reactions: rules that suggest a stored, editable value when another field is edited (1C's ПриИзменении). Run in the UI only — a form, an editable grid, and bulk update per selected record; a write over the API, an import or a DSL update() runs none. A rule fires on the user's edit of an 'on' column and sets each 'set' column — from a formula evaluated on the client, or from what a server rule's 'call' function returns; a value it sets may fire further rules, each rule at most once per edit, and never over a column the user edited. A column the user may not edit is skipped. Pack and install check every column, formula and function. See docs/ideas/field-change-reactions.md.",
40
+ "items": {
41
+ "type": "object",
42
+ "required": ["code", "on", "set"],
43
+ "additionalProperties": false,
44
+ "properties": {
45
+ "code": {
46
+ "type": "string",
47
+ "pattern": "^[^.]+$",
48
+ "description": "Names the rule in errors; unique within the entity. No '.': a bridge module's rules are stored as <module>.<code>."
49
+ },
50
+ "on": {
51
+ "type": "array",
52
+ "minItems": 1,
53
+ "items": { "type": "string" },
54
+ "description": "The columns whose edit fires the rule — stored or reference columns, not computed ones."
55
+ },
56
+ "call": {
57
+ "type": "string",
58
+ "description": "Makes this a server rule: a top-level function of the module's libraries (logic/functions), or an imported one as namespace.name. It is called with the record as the user is editing it — function f(record) { … } — in a transaction that is always rolled back, and returns an object of suggested values by column. Its info() / warn() / error() reach the user."
59
+ },
60
+ "set": {
61
+ "description": "A formula rule: column → formula. A server rule: the columns its function may set, each a column or { column, ifEmpty } — what it returns beyond them is ignored, and a column it leaves out is unchanged. A target is a stored column, not the primary key, a reference (set its FK column instead) or a computed column. A formula uses the F-column syntax, [ref].[column] reads included.",
62
+ "oneOf": [
63
+ {
64
+ "type": "object",
65
+ "minProperties": 1,
66
+ "additionalProperties": {
67
+ "oneOf": [
68
+ { "type": "string" },
69
+ {
70
+ "type": "object",
71
+ "required": ["formula"],
72
+ "additionalProperties": false,
73
+ "properties": {
74
+ "formula": { "type": "string" },
75
+ "ifEmpty": { "type": "boolean", "description": "Fill only an empty field." }
76
+ }
77
+ }
78
+ ]
79
+ }
80
+ },
81
+ {
82
+ "type": "array",
83
+ "minItems": 1,
84
+ "items": {
85
+ "oneOf": [
86
+ { "type": "string" },
87
+ {
88
+ "type": "object",
89
+ "required": ["column"],
90
+ "additionalProperties": false,
91
+ "properties": {
92
+ "column": { "type": "string" },
93
+ "ifEmpty": { "type": "boolean", "description": "Fill only an empty field." }
94
+ }
95
+ }
96
+ ]
97
+ }
98
+ }
99
+ ]
100
+ }
101
+ }
102
+ }
103
+ },
36
104
  "auditHistory": {
37
105
  "type": "string",
38
106
  "enum": ["basic", "fields", "full"],
@@ -265,6 +333,17 @@
265
333
  "dateField": { "type": "string", "description": "This document's date column, supplying the date the register stamps on each journal row. Required once the register declares a 'journalDate' — one writer that supplied none would make every balance read as of a date quietly short, across all writers. On an accumulation (A) column it may be a one-hop reference path, '<reference column>.<target column>', or an expression over the document ('=...', see 'map')." },
266
334
  "dateTrunc": { "type": "string", "enum": ["day", "month", "year"], "description": "Takes the date 'dateField' supplies back to the start of its day, month or year before the register stamps it — for a document that carries a time while the register's records sit on whole days. The calendar is the value's as stored; no time zone is applied. Needs 'dateField'. Accumulation (A) columns only." },
267
335
  "skipPostingWhenEmpty": { "type": "array", "items": { "type": "string" }, "description": "THE EXCEPTION, not the normal way to model this — read the guide before declaring it. Register dimensions (by the register's names) whose empty value means THIS POSTING records nothing: the line, or the document when the binding posts from the document. One posting is skipped, never the register and never the document. When that is all the document would have recorded it is left unposted to this register, and the flag is per binding, so its other registers post as usual. An empty dimension NOT listed refuses the posting, naming the row and the dimension. It leaves a document fully posted while some of its lines produced no movement, which usually means the document is modelling two kinds of line in one table: since 'lineEntity' is declared per BINDING, prefer giving each register its own line table (goods lines post to stock, service lines do not) so that every line of each table posts. Declare this only when the distinction is not structural but lives in the data — a batch or serial number filled on some rows and not others — so there is no second table to put those rows in. Accumulation (A) columns only. Formerly skipWhenNull: a package using the old name is refused at install, while a tenant's already-stored params keep being read under it." },
336
+ "validation": {
337
+ "type": "object",
338
+ "description": "Per-line and per-entry checks a ledger (L) column runs before it posts. Accounting (L) columns only.",
339
+ "properties": {
340
+ "balanced": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true, "description": "Register resources whose total debit must equal total credit across the entry's lines — a list because an entry may balance its amount but not its quantity. Exclusive with requireBalanced; refused on a compound register, which balances by construction." },
341
+ "requireBalanced": { "type": "boolean", "default": false, "description": "Every resource must balance across the entry's lines. Exclusive with balanced; refused on a compound register." },
342
+ "storno": { "type": "boolean", "default": false, "description": "Red reversal (storno): an amount may be negative, posted on the SAME side as the entry it reverses, so turnovers stay on their side as negatives. The other per-line checks stay, read as non-zero: a classic line still carries one side per resource, and a line must still move something. Wins over requirePositiveAmounts: false." },
343
+ "requirePositiveAmounts": { "type": "boolean", "default": true, "deprecated": true, "description": "Deprecated — use storno. false switches off EVERY per-line check (the sign, both sides on one classic line, a line that moves nothing), not only the sign." }
344
+ },
345
+ "additionalProperties": true
346
+ },
268
347
  "periodEntity": { "type": "string", "description": "Period entity this registry closes against — one of this module's, or 'module.entity' from a dependency." },
269
348
  "periodDateField": { "type": "string", "description": "Header date column that resolves the period." },
270
349
  "periodKeyField": { "type": "string", "default": "period_key" },
package/src/deps.ts CHANGED
@@ -40,6 +40,8 @@ export type DepProvenanceKind =
40
40
  | "extends"
41
41
  | "formula"
42
42
  | "action"
43
+ | "function"
44
+ | "reaction"
43
45
  | "view"
44
46
  | "report"
45
47
  | "print"
@@ -47,7 +49,9 @@ export type DepProvenanceKind =
47
49
 
48
50
  /**
49
51
  * A usage token of the form `<kind>:<symbol>` — e.g. `ref:invoice.customer`,
50
- * `extends:party`, `action:check_credit`, `role:fin.admin`. Names the consuming
52
+ * `extends:parties.party`, `action:check_credit`, `function:wh_posting` (a function in
53
+ * `logic/functions/wh_posting.dsl`), `reaction:quote_line_pricing` (a field-change reaction
54
+ * an extension declares on the dependency entity), `role:fin.admin`. Names the consuming
51
55
  * symbol so the completeness lint can reconcile declared vs actual usage. Note:
52
56
  * `display` is intentionally not a kind — the label is the provider's concern.
53
57
  */
package/src/entity.ts CHANGED
@@ -155,6 +155,26 @@ export interface LedgerRegistryEntry {
155
155
  };
156
156
  }
157
157
 
158
+ /** One field-change reaction rule of an entity. */
159
+ export interface EntityReaction {
160
+ /** Names the rule in errors; unique within the entity. */
161
+ code: string;
162
+ /** The columns whose edit fires the rule. */
163
+ on: string[];
164
+ /**
165
+ * A server rule's function: one of the module's libraries (`name`), or an imported one
166
+ * (`namespace.name`). Called with the record being edited; returns the values by column.
167
+ */
168
+ call?: string;
169
+ /**
170
+ * A formula rule: column → formula, or `{ formula, ifEmpty }` to fill only an empty field.
171
+ * A server rule: the columns its function may set, each a column or `{ column, ifEmpty }`.
172
+ */
173
+ set:
174
+ | Record<string, string | { formula: string; ifEmpty?: boolean }>
175
+ | (string | { column: string; ifEmpty?: boolean })[];
176
+ }
177
+
158
178
  /** A single entity definition (`entities/<code>.json`). */
159
179
  export interface EntityDef {
160
180
  /** Qualified entity code to extend (e.g. 'fin.invoice') — turns this into an extension. */
@@ -170,6 +190,12 @@ export interface EntityDef {
170
190
  auditHistory?: AuditHistoryLevel;
171
191
  /** Records of this entity accept comments (composer + thread on the card). */
172
192
  comments?: boolean;
193
+ /**
194
+ * Field-change reactions: when the user edits an `on` column, each `set` column is filled from
195
+ * its formula, or from what a server rule's `call` function returns. UI only (form, grid, bulk
196
+ * update). See docs/ideas/field-change-reactions.md.
197
+ */
198
+ reactions?: EntityReaction[];
173
199
  /** Traits expanded at install time. */
174
200
  traits?: TraitCd[];
175
201
  /** Column definitions keyed by column code. Optional when a trait (e.g. `period`) supplies all columns. */
package/src/index.ts CHANGED
@@ -62,6 +62,7 @@ export { FILTER_GROUP_OPERATORS, FILTER_OPERATORS } from "./filter";
62
62
  // ── Module-file structural types ─────────────────────────────────────────────
63
63
  export type {
64
64
  EntityDef,
65
+ EntityReaction,
65
66
  EntityReference,
66
67
  EntityViewDef,
67
68
  EntityViewColumnDef,