@cosmicdrift/kumiko-framework 0.399.0 → 0.400.0

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.
Files changed (133) hide show
  1. package/agents/api/framework-engine.md +9 -3
  2. package/agents/api/framework-ui-types.md +10 -5
  3. package/agents/api/renderer-web.md +2 -1
  4. package/agents/api/types.md +242 -80
  5. package/agents/api.md +2 -2
  6. package/agents/examples/derived-fields-sql.md +251 -0
  7. package/agents/examples/documents.md +161 -0
  8. package/agents/examples/inspection-checklist.md +807 -0
  9. package/agents/examples.md +3 -0
  10. package/agents/features-index.md +2 -0
  11. package/agents/features.md +8 -0
  12. package/dist/api/entity-read-access.d.ts +2 -0
  13. package/dist/api/entity-read-access.js +46 -2
  14. package/dist/api/server-boot-checks.js +2 -2
  15. package/dist/api/server-routes.js +3 -1
  16. package/dist/db/derived-field-sql.d.ts +23 -0
  17. package/dist/db/derived-field-sql.js +115 -0
  18. package/dist/db/derived-related-sql.d.ts +31 -0
  19. package/dist/db/derived-related-sql.js +137 -0
  20. package/dist/db/driver-row-coercion.d.ts +1 -0
  21. package/dist/db/driver-row-coercion.js +13 -0
  22. package/dist/db/eagerload.d.ts +3 -1
  23. package/dist/db/eagerload.js +4 -6
  24. package/dist/db/event-store-executor-context.js +1 -1
  25. package/dist/db/event-store-executor-read-paging.js +1 -1
  26. package/dist/db/event-store-executor-read-search.d.ts +3 -0
  27. package/dist/db/event-store-executor-read-search.js +17 -9
  28. package/dist/db/event-store-executor-read-sort.d.ts +9 -2
  29. package/dist/db/event-store-executor-read-sort.js +27 -14
  30. package/dist/db/event-store-executor-read-where.d.ts +0 -5
  31. package/dist/db/event-store-executor-read-where.js +3 -9
  32. package/dist/db/event-store-executor-read.js +23 -4
  33. package/dist/db/event-store-executor-write-steps.d.ts +6 -1
  34. package/dist/db/event-store-executor-write-steps.js +20 -1
  35. package/dist/db/event-store-executor-write.js +8 -8
  36. package/dist/db/money.d.ts +4 -0
  37. package/dist/db/money.js +34 -29
  38. package/dist/db/parent-ref-clause.d.ts +1 -0
  39. package/dist/db/parent-ref-clause.js +18 -8
  40. package/dist/db/read-gate-clause.d.ts +7 -0
  41. package/dist/db/read-gate-clause.js +188 -0
  42. package/dist/db/read-ownership.d.ts +9 -0
  43. package/dist/db/read-ownership.js +12 -0
  44. package/dist/engine/boot-validator/boot-check.d.ts +3 -1
  45. package/dist/engine/boot-validator/boot-check.js +7 -11
  46. package/dist/engine/boot-validator/boot-context.d.ts +26 -0
  47. package/dist/engine/boot-validator/boot-context.js +57 -0
  48. package/dist/engine/boot-validator/boot-options.d.ts +33 -0
  49. package/dist/engine/boot-validator/boot-options.js +1 -0
  50. package/dist/engine/boot-validator/boot-phases-app.d.ts +5 -0
  51. package/dist/engine/boot-validator/boot-phases-app.js +103 -0
  52. package/dist/engine/boot-validator/boot-phases.d.ts +11 -0
  53. package/dist/engine/boot-validator/boot-phases.js +140 -0
  54. package/dist/engine/boot-validator/boot-report.d.ts +6 -0
  55. package/dist/engine/boot-validator/boot-report.js +47 -0
  56. package/dist/engine/boot-validator/boot-rule.d.ts +12 -0
  57. package/dist/engine/boot-validator/boot-rule.js +14 -0
  58. package/dist/engine/boot-validator/boot-warnings.d.ts +2 -0
  59. package/dist/engine/boot-validator/boot-warnings.js +20 -0
  60. package/dist/engine/boot-validator/derived-sql-fields.d.ts +4 -0
  61. package/dist/engine/boot-validator/derived-sql-fields.js +184 -0
  62. package/dist/engine/boot-validator/entity-handler.d.ts +3 -0
  63. package/dist/engine/boot-validator/entity-handler.js +6 -3
  64. package/dist/engine/boot-validator/entity-list-screens.js +3 -1
  65. package/dist/engine/boot-validator/field-conditions.d.ts +2 -0
  66. package/dist/engine/boot-validator/field-conditions.js +178 -0
  67. package/dist/engine/boot-validator/i18n-keys.d.ts +2 -2
  68. package/dist/engine/boot-validator/i18n-keys.js +1 -3
  69. package/dist/engine/boot-validator/index.d.ts +5 -34
  70. package/dist/engine/boot-validator/index.js +24 -204
  71. package/dist/engine/boot-validator/pii-retention.js +2 -7
  72. package/dist/engine/boot-validator/projection-list-screens.js +19 -1
  73. package/dist/engine/boot-validator/read-gate.d.ts +2 -0
  74. package/dist/engine/boot-validator/read-gate.js +54 -0
  75. package/dist/engine/boot-validator/screens-forms.js +19 -6
  76. package/dist/engine/boot-validator/screens-kind-entity-list.js +8 -6
  77. package/dist/engine/boot-validator/screens-lists.js +8 -0
  78. package/dist/engine/boot-validator/zod-shape.js +4 -2
  79. package/dist/engine/build-app-schema.js +11 -5
  80. package/dist/engine/embedded-derived.d.ts +8 -6
  81. package/dist/engine/embedded-derived.js +25 -7
  82. package/dist/engine/entity-handlers.d.ts +1 -1
  83. package/dist/engine/entity-handlers.js +17 -18
  84. package/dist/engine/entity-list-runtime-options.d.ts +2 -1
  85. package/dist/engine/entity-list-runtime-options.js +86 -5
  86. package/dist/engine/factories.d.ts +5 -5
  87. package/dist/engine/factories.js +3 -5
  88. package/dist/engine/field-access.d.ts +2 -0
  89. package/dist/engine/field-access.js +48 -1
  90. package/dist/engine/field-state.d.ts +12 -0
  91. package/dist/engine/field-state.js +67 -0
  92. package/dist/engine/handler-helpers.d.ts +3 -0
  93. package/dist/engine/handler-helpers.js +7 -1
  94. package/dist/engine/index.d.ts +2 -2
  95. package/dist/engine/index.js +2 -2
  96. package/dist/engine/ownership.d.ts +1 -1
  97. package/dist/engine/ownership.js +9 -5
  98. package/dist/engine/registry-state.d.ts +1 -0
  99. package/dist/engine/registry-state.js +1 -1
  100. package/dist/engine/schema-builder.js +3 -0
  101. package/dist/engine/strip-unreadable-related-lists.d.ts +4 -0
  102. package/dist/engine/strip-unreadable-related-lists.js +80 -0
  103. package/dist/files/assert-file-refs-belong-to-tenant.js +26 -6
  104. package/dist/files/bind-pending-file-refs.d.ts +3 -2
  105. package/dist/files/bind-pending-file-refs.js +11 -12
  106. package/dist/files/bind-referenced-files.write.js +5 -5
  107. package/dist/files/create-file-ref-from-bytes.d.ts +2 -0
  108. package/dist/files/create-file-ref-from-bytes.js +2 -0
  109. package/dist/files/create-file-ref.d.ts +2 -0
  110. package/dist/files/create-file-ref.js +39 -22
  111. package/dist/files/dispose-removed-file-refs.js +5 -8
  112. package/dist/files/file-field-slots.d.ts +10 -0
  113. package/dist/files/file-field-slots.js +53 -0
  114. package/dist/files/file-ref-entity.d.ts +1 -1
  115. package/dist/files/file-ref-table.d.ts +1 -1
  116. package/dist/files/file-routes.js +12 -3
  117. package/dist/files/index.d.ts +1 -0
  118. package/dist/files/index.js +1 -0
  119. package/dist/files/load-save-file-refs.js +3 -4
  120. package/dist/files/record-read-check.js +2 -30
  121. package/dist/files/record-write-check.d.ts +2 -0
  122. package/dist/files/record-write-check.js +18 -3
  123. package/dist/files/upload-field-resolution.d.ts +2 -1
  124. package/dist/files/upload-field-resolution.js +33 -13
  125. package/dist/pipeline/dispatch-batch.js +6 -5
  126. package/dist/pipeline/dispatch-write.js +2 -1
  127. package/dist/pipeline/write-result-masking.js +2 -1
  128. package/dist/schema-cli/validate-command.js +46 -12
  129. package/dist/schema-cli.js +1 -1
  130. package/dist/ui-types/index.d.ts +2 -1
  131. package/dist/ui-types/index.js +2 -1
  132. package/package.json +3 -3
  133. package/src/changes.json +55 -0
@@ -374,6 +374,7 @@ type EagerLoadEntityResolver = (entityName: string) => EntityDefinition | undefi
374
374
  type EagerLoadViewer = {
375
375
  user: SessionUser;
376
376
  parentVisibility: ParentVisibilityOption;
377
+ resolveTable?: (entityName: string) => ReturnType<typeof buildEntityTable>;
377
378
  };
378
379
  ```
379
380
 
@@ -896,6 +897,12 @@ type WorkflowTrigger = // Trigger configuration for a workflow.
896
897
  ;
897
898
  ```
898
899
 
900
+ ## packages/framework/src/engine/embedded-derived.ts
901
+
902
+ ```ts
903
+ type NumericDerivedOp = Exclude<EmbeddedDerivedCellDef["op"], "inRange">;
904
+ ```
905
+
899
906
  ## packages/framework/src/engine/event-helpers.ts
900
907
 
901
908
  ```ts
@@ -981,6 +988,14 @@ type BuildManifestOptions = {
981
988
  };
982
989
  ```
983
990
 
991
+ ## packages/framework/src/engine/field-state.ts
992
+
993
+ ```ts
994
+ type RequiredState = FieldStateConditions & {
995
+ required?: boolean;
996
+ };
997
+ ```
998
+
984
999
  ## packages/framework/src/engine/handler-field-references.ts
985
1000
 
986
1001
  ```ts
@@ -1767,6 +1782,78 @@ type DataTableViewOptions = {
1767
1782
  };
1768
1783
  ```
1769
1784
 
1785
+ ## packages/renderer/src/embedded-list-types.ts
1786
+
1787
+ ```ts
1788
+ type EmbeddedListCellType = "text" | "number" | "boolean" | "date" | "money" | "decimal" | "select" | "reference" | "timestamp" | "images" | "files"; // Cell type for one column of an embedded-list field.
1789
+ type EmbeddedListColumn = { // One column of an embedded-list table (one entry per key of the source EmbeddedFieldDef's `schema`).
1790
+ field: string;
1791
+ label: string;
1792
+ type: EmbeddedListCellType;
1793
+ required: boolean;
1794
+ derived: boolean; // Read-only, value supplied by the caller (already computed from sibling cells) — not directly user-editable.
1795
+ readOnly?: boolean; // Shown but not editable by the user; unlike `derived` it is not computed.
1796
+ hiddenInCard?: boolean; // Phone cards skip this column; the table still shows it.
1797
+ cardLimits?: {
1798
+ min: string;
1799
+ max: string;
1800
+ }; // Phone cards show the row's min/max (sub-field names) as a hint under this column's control.
1801
+ cardAttachedTo?: string; // Phone cards render this column unlabeled beside the named column's control.
1802
+ options?: string[]; // Only for `type: "select"`.
1803
+ optionLabels?: Readonly<Record<string, string>>;
1804
+ display?: "radio" | "dropdown"; // Only for `type: "select"`; `"radio"` renders segmented buttons, the default is a dropdown.
1805
+ referenceOptions?: { value: string; label: string; }[]; // Only for `type: "reference"` — pre-fetched by the caller, ONE query per column shared across every row (not one query per cell).
1806
+ referenceLoading?: boolean;
1807
+ optionTones?: Readonly<Partial<Record<string, SelectOptionTone>>>; // Only for `type: "select"`; tone per option (selected segment, derived badge).
1808
+ capture?: "user" | "environment"; // Only for `type: "images" | "files"`.
1809
+ maxCount?: number;
1810
+ accept?: string[];
1811
+ isHiddenInRow?: (row: Readonly<Record<string, unknown>>) => boolean; // Per-row state, evaluated by the caller (conditions are engine logic the primitive must not import).
1812
+ isRequiredInRow?: (row: Readonly<Record<string, unknown>>) => boolean;
1813
+ isDerivedInRow?: (row: Readonly<Record<string, unknown>>) => boolean; // Overrides `derived` per row: a conditional derived cell is editable on rows where it does not apply.
1814
+ };
1815
+ type EmbeddedListInputProps = { // Row-array + totals table for a `createEmbeddedListField` (invoice- positions-style) field — structurally too different from `Input`'s value/onChange union to fit there, so it's its own primitive, same as `DataTable`.
1816
+ id: string;
1817
+ columns: EmbeddedListColumn[];
1818
+ rows: Readonly<Record<string, unknown>>[];
1819
+ totals?: EmbeddedListTotal[];
1820
+ currency?: string; // Currency for money cells and the totals row — one value for the whole list (currency lives on the head aggregate, not the row).
1821
+ disabled?: boolean; // Whole-list read-only (e.g. a released invoice) — hides row-mutation affordances and disables every cell.
1822
+ fixedRows?: boolean; // Rows are fixed: no add, remove, duplicate or move; cells stay editable.
1823
+ minItems?: number;
1824
+ maxItems?: number;
1825
+ uploadTarget?: {
1826
+ entityType: string;
1827
+ fieldName: string;
1828
+ entityId?: string;
1829
+ }; // Upload target of file cells: `entityType`/`entityId`/`fieldName` of the list, the column name is the `subField`.
1830
+ listIssues?: FieldIssue[]; // Shown under the whole list (e.g. under the totals row).
1831
+ rowIssues?: Readonly<Record<number, FieldIssue[]>>; // Keyed by row index.
1832
+ cellIssues?: Readonly<Record<string, FieldIssue[]>>; // Keyed `${rowIndex}.${field}`.
1833
+ onCellChange: (rowIndex: number, field: string, value: unknown) => void;
1834
+ onAddRow: () => void;
1835
+ onRemoveRow: (rowIndex: number) => void;
1836
+ onDuplicateRow: (rowIndex: number) => void;
1837
+ onMoveRow: (fromIndex: number, toIndex: number) => void;
1838
+ onPasteCells?: (rowIndex: number, columnIndex: number, grid: (string[])[]) => void; // Tab/newline-delimited clipboard paste starting at (rowIndex, columnIndex) — parsed to a 2D string grid by the Web impl (paste events are a browser-only concept); undefined caller = no paste handling wired up.
1839
+ addLabel: string;
1840
+ actionsLabel: string; // Accessible name of the (otherwise headerless) row-actions column.
1841
+ removeLabel: string;
1842
+ duplicateLabel: string;
1843
+ moveUpLabel: string;
1844
+ moveDownLabel: string;
1845
+ emptyLabel: string;
1846
+ emptyCtaLabel: string;
1847
+ testId?: string;
1848
+ };
1849
+ type EmbeddedListTotal = { // One entry of an embedded-list totals row (e.g. an invoice's total amount, summed across all rows by the caller).
1850
+ field: string;
1851
+ label: string;
1852
+ value: number;
1853
+ money?: boolean; // Set for labelled multi-column totals, which have no single column to read the money type from.
1854
+ };
1855
+ ```
1856
+
1770
1857
  ## packages/renderer/src/headless/contracts/locale.ts
1771
1858
 
1772
1859
  ```ts
@@ -1986,6 +2073,7 @@ type EditFieldViewModel = {
1986
2073
  payload: OptionsQueryPayload;
1987
2074
  }; // Set for `type: "select"` / `"multiSelect"` when optionsAvailabilityQuery is set: the renderer loads `{ rows: { value, disabled?, hint?
1988
2075
  display?: "checkboxes" | "dropdown" | "radio"; // Set for `type: "multiSelect"` when MultiSelectFieldDef.display is "checkboxes" — the renderer shows a checkbox grid with a select-all toggle instead of the default combobox dropdown.
2076
+ optionTones?: Readonly<Partial<Record<string, SelectOptionTone>>>; // `type: "select"` only: tone per option, shown on the selected radio segment.
1989
2077
  columns?: 1 | 2 | 3 | 4; // Set for `type: "multiSelect"` when MultiSelectFieldDef.columns is defined — column count for the checkbox grid at the widest breakpoint.
1990
2078
  maxRows?: number; // Set for `type: "multiSelect"` when MultiSelectFieldDef.maxRows is defined — grid rows visible before the checkbox grid becomes scrollable.
1991
2079
  multiline?: boolean | { rows?: number; }; // For `type: "text"`, set when TextFieldDef.multiline is true — the renderer then renders a textarea instead of a single-line input.
@@ -2021,9 +2109,14 @@ type EditFieldViewModel = {
2021
2109
  embeddedListCells?: EmbeddedListCellViewModel[]; // Only set for an embedded-LIST field (`multiple: true`) — the per-cell metadata the renderer needs to draw each column/cell.
2022
2110
  embeddedListMinItems?: number; // Minimum row count for the embedded list.
2023
2111
  embeddedListMaxItems?: number; // Maximum row count for the embedded list.
2024
- embeddedListDerived?: Readonly<Record<string, { op: "multiply" | "sum" | "subtract" | "percent"; from: string[]; }>>; // Sub-field name → how to compute its cell from other sub-fields of the same row.
2112
+ embeddedListDerived?: Readonly<Record<string, EmbeddedDerivedCellDef>>; // Sub-field name → how to compute its cell from other sub-fields of the same row.
2025
2113
  embeddedListTotals?: (string | { label: string; from: string[]; })[]; // Footer totals: a sub-field name, or a labelled sum of several.
2026
2114
  embeddedListFixedRows?: boolean; // No add, remove, duplicate or move; cells stay editable.
2115
+ embeddedListUploadTarget?: {
2116
+ entityType: string;
2117
+ fieldName: string;
2118
+ entityId?: string;
2119
+ }; // Where photos of a row's file cells are uploaded: the entity and the top-level list field; the cell name travels as `subField`.
2027
2120
  embeddedListCurrency?: string; // Currency for the whole embedded list's money cells and totals row — currency lives on the head aggregate (EntityDefinition.defaultCurrency), not per row.
2028
2121
  currency?: string; // Only set for a plain (non-list, non-embedded) `type: "money"` field — same source as embeddedListCurrency (EntityDefinition.defaultCurrency, falls back to "EUR").
2029
2122
  unit?: string | { field: string; }; // Only set for `type: "number"` when NumberFieldDef.unit is set — a static suffix string, or a sibling-field reference the renderer resolves against the live row values.
@@ -2031,12 +2124,19 @@ type EditFieldViewModel = {
2031
2124
  type EmbeddedListCellViewModel = {
2032
2125
  field: string;
2033
2126
  label: string;
2034
- type: "text" | "number" | "boolean" | "date" | "money" | "decimal" | "select" | "reference" | "timestamp";
2127
+ type: "text" | "number" | "boolean" | "date" | "money" | "decimal" | "select" | "reference" | "timestamp" | "images" | "files";
2035
2128
  required: boolean;
2129
+ visibleWhen?: Exclude<FieldCondition, boolean>; // Per-row state: hidden / required for a row where the condition holds.
2130
+ requiredWhen?: Exclude<FieldCondition, boolean>;
2036
2131
  options?: string[]; // Only for `type: "select"`.
2132
+ optionTones?: Readonly<Partial<Record<string, SelectOptionTone>>>; // Only for `type: "select"`; tone of each option's badge or selected segment.
2133
+ capture?: "user" | "environment"; // Only for `type: "images" | "files"`.
2134
+ maxCount?: number;
2135
+ accept?: string[];
2037
2136
  optionLabels?: Readonly<Record<string, string>>;
2038
2137
  display?: "radio" | "dropdown"; // Only for `type: "select"`; `"radio"` renders segmented buttons instead of a dropdown.
2039
2138
  readOnly?: boolean; // Cell is shown but not editable (`EditFieldSpec.embeddedList.readOnlyCells`).
2139
+ hiddenInCard?: boolean; // Not shown on phone cards (`EditFieldSpec.embeddedList.cardHiddenCells`).
2040
2140
  refEntity?: string; // Only for `type: "reference"`.
2041
2141
  refFeature?: string;
2042
2142
  refLabelField?: string;
@@ -2222,55 +2322,6 @@ type DrawerProps = { // Side panel for hosting an existing self-contained form/w
2222
2322
  children: ReactNode;
2223
2323
  testId?: string;
2224
2324
  };
2225
- type EmbeddedListCellType = "text" | "number" | "boolean" | "date" | "money" | "decimal" | "select" | "reference" | "timestamp"; // Cell type for one column of an embedded-list field.
2226
- type EmbeddedListColumn = { // One column of an embedded-list table (one entry per key of the source EmbeddedFieldDef's `schema`).
2227
- field: string;
2228
- label: string;
2229
- type: EmbeddedListCellType;
2230
- required: boolean;
2231
- derived: boolean; // Read-only, value supplied by the caller (already computed from sibling cells) — not directly user-editable.
2232
- readOnly?: boolean; // Shown but not editable by the user; unlike `derived` it is not computed.
2233
- options?: string[]; // Only for `type: "select"`.
2234
- optionLabels?: Readonly<Record<string, string>>;
2235
- display?: "radio" | "dropdown"; // Only for `type: "select"`; `"radio"` renders segmented buttons, the default is a dropdown.
2236
- referenceOptions?: { value: string; label: string; }[]; // Only for `type: "reference"` — pre-fetched by the caller, ONE query per column shared across every row (not one query per cell).
2237
- referenceLoading?: boolean;
2238
- };
2239
- type EmbeddedListInputProps = { // Row-array + totals table for a `createEmbeddedListField` (invoice- positions-style) field — structurally too different from `Input`'s value/onChange union to fit there, so it's its own primitive, same as `DataTable`.
2240
- id: string;
2241
- columns: EmbeddedListColumn[];
2242
- rows: Readonly<Record<string, unknown>>[];
2243
- totals?: EmbeddedListTotal[];
2244
- currency?: string; // Currency for money cells and the totals row — one value for the whole list (currency lives on the head aggregate, not the row).
2245
- disabled?: boolean; // Whole-list read-only (e.g. a released invoice) — hides row-mutation affordances and disables every cell.
2246
- fixedRows?: boolean; // Rows are fixed: no add, remove, duplicate or move; cells stay editable.
2247
- minItems?: number;
2248
- maxItems?: number;
2249
- listIssues?: FieldIssue[]; // Shown under the whole list (e.g. under the totals row).
2250
- rowIssues?: Readonly<Record<number, FieldIssue[]>>; // Keyed by row index.
2251
- cellIssues?: Readonly<Record<string, FieldIssue[]>>; // Keyed `${rowIndex}.${field}`.
2252
- onCellChange: (rowIndex: number, field: string, value: unknown) => void;
2253
- onAddRow: () => void;
2254
- onRemoveRow: (rowIndex: number) => void;
2255
- onDuplicateRow: (rowIndex: number) => void;
2256
- onMoveRow: (fromIndex: number, toIndex: number) => void;
2257
- onPasteCells?: (rowIndex: number, columnIndex: number, grid: (string[])[]) => void; // Tab/newline-delimited clipboard paste starting at (rowIndex, columnIndex) — parsed to a 2D string grid by the Web impl (paste events are a browser-only concept); undefined caller = no paste handling wired up.
2258
- addLabel: string;
2259
- actionsLabel: string; // Accessible name of the (otherwise headerless) row-actions column.
2260
- removeLabel: string;
2261
- duplicateLabel: string;
2262
- moveUpLabel: string;
2263
- moveDownLabel: string;
2264
- emptyLabel: string;
2265
- emptyCtaLabel: string;
2266
- testId?: string;
2267
- };
2268
- type EmbeddedListTotal = { // One entry of an embedded-list totals row (e.g. an invoice's total amount, summed across all rows by the caller).
2269
- field: string;
2270
- label: string;
2271
- value: number;
2272
- money?: boolean; // Set for labelled multi-column totals, which have no single column to read the money type from.
2273
- };
2274
2325
  type FieldCellWidth = "text" | "number" | "money" | "date" | "timestamp" | "select" | "full" | "toggle"; // Width class of a field cell inside a `flow` grid: sized by what the field holds.
2275
2326
  type FileValueListProps = { // Read-only list of stored files (FileRef ids): name, download link and, for PDFs, an inline preview.
2276
2327
  fileIds: string[];
@@ -2500,6 +2551,7 @@ type FileUploadFormDataInput = {
2500
2551
  entityType?: string | undefined;
2501
2552
  entityId?: string | undefined;
2502
2553
  fieldName?: string | undefined;
2554
+ subField?: string | undefined; // File sub-field of the embedded `fieldName`.
2503
2555
  actionForm?: string | undefined; // Screen QN of an actionForm: the upload is pending for that form's field, not for an entity.
2504
2556
  };
2505
2557
  ```
@@ -3097,6 +3149,26 @@ type RegisterEntityCrudOptions = {
3097
3149
  };
3098
3150
  ```
3099
3151
 
3152
+ ## packages/types/src/entity-row-visibility.ts
3153
+
3154
+ ```ts
3155
+ type ParentRefDef = {
3156
+ entityTypeField: string; // Field holding the name of the host entity this row hangs off.
3157
+ entityIdField: string; // Field holding the host row's id.
3158
+ allowedTypes?: string[]; // Narrow which registered entities may act as host.
3159
+ };
3160
+ type ReadGateDef = {
3161
+ anyLink?: {
3162
+ entity: string;
3163
+ field: string;
3164
+ }; // Row readable only if at least one live row of `entity` with `field` = row.id is readable through that entity's own parentRef gate (host visibility).
3165
+ fileOwner?: {
3166
+ field: string;
3167
+ }; // Row readable only if the file_ref in `field` is live and readable through its owner: owner row readable (ownership, parentRef, handler gate) and the field read access on the file_ref's fieldName passes.
3168
+ linkExemptRoles?: string[]; // Roles that skip `anyLink` (they also see unlinked rows).
3169
+ };
3170
+ ```
3171
+
3100
3172
  ## packages/types/src/entity-table-meta-types.ts
3101
3173
 
3102
3174
  ```ts
@@ -3155,12 +3227,21 @@ type ListRuntimeOptions = {
3155
3227
  referenceSearch?: {
3156
3228
  fields: ReadonlyArray<{ fieldName: string; targetEntityName: string; labelField: string; }>;
3157
3229
  resolveEntity: (entityName: string) => EntityDefinition | undefined;
3230
+ resolveTable?: (entityName: string) => SchemaTable;
3158
3231
  };
3159
3232
  referenceSort?: {
3160
3233
  fields: ReadonlyArray<{ fieldName: string; targetEntityName: string; labelField: string; }>;
3161
3234
  resolveEntity: (entityName: string) => EntityDefinition | undefined;
3235
+ resolveTable?: (entityName: string) => SchemaTable;
3162
3236
  };
3163
3237
  parentVisibility?: ParentVisibilityOption;
3238
+ derivedSql?: {
3239
+ resolveEntity: (entityName: string) => EntityDefinition | undefined;
3240
+ resolveTable: (entityName: string) => SchemaTable;
3241
+ canReadEntity: (entityName: string) => boolean;
3242
+ today: string;
3243
+ now: string;
3244
+ };
3164
3245
  };
3165
3246
  type LockGuardOption = {
3166
3247
  unsafeSkipLockGuard?: boolean;
@@ -3171,6 +3252,8 @@ type ParentVisibilityGateOption = {
3171
3252
  };
3172
3253
  type ParentVisibilityOption = {
3173
3254
  entities: ReadonlyMap<string, EntityDefinition>;
3255
+ canReadEntity?: (entityName: string) => boolean;
3256
+ resolveTable?: (entityName: string) => SchemaTable;
3174
3257
  };
3175
3258
  type PreSaveRunner = (changes: Record<string, unknown>, previous: Readonly<Record<string, unknown>>, isNew: boolean) => Promise<Record<string, unknown>>;
3176
3259
  type StreamTenantOption = {
@@ -3291,21 +3374,103 @@ type UiHints = {
3291
3374
  };
3292
3375
  ```
3293
3376
 
3377
+ ## packages/types/src/field-state-conditions.ts
3378
+
3379
+ ```ts
3380
+ type EmbeddedFilesSubFieldProps = { // Value is an array of FileRef ids; `file_refs.fieldName` stays the top-level embedded field name.
3381
+ maxSize?: string;
3382
+ accept?: string[];
3383
+ maxCount?: number;
3384
+ };
3385
+ type EmbeddedImagesSubFieldProps = EmbeddedFilesSubFieldProps & {
3386
+ capture?: "environment" | "user";
3387
+ };
3388
+ type FieldStateConditions = {
3389
+ visibleWhen?: Exclude<FieldCondition, boolean>; // Field is shown only while the condition holds on the record values.
3390
+ requiredWhen?: Exclude<FieldCondition, boolean>; // Field is required while the condition holds (and the field is visible).
3391
+ };
3392
+ ```
3393
+
3294
3394
  ## packages/types/src/fields-derived.ts
3295
3395
 
3296
3396
  ```ts
3297
- type DerivedFieldDef = {
3397
+ type DerivedCondition =
3398
+ | {
3399
+ field: string;
3400
+ op: "eq" | "ne" | "lt" | "gt" | "lte" | "gte" | "in";
3401
+ value: DerivedConditionValue;
3402
+ }
3403
+ | {
3404
+ activeOn: {
3405
+ from: string;
3406
+ to: string;
3407
+ };
3408
+ }
3409
+ ;
3410
+ type DerivedConditionValue =
3411
+ | string
3412
+ | number
3413
+ | boolean
3414
+ | null
3415
+ | (string | number)[]
3416
+ | {
3417
+ today: true;
3418
+ }
3419
+ | {
3420
+ now: true;
3421
+ }
3422
+ ;
3423
+ type DerivedFieldDef = JsDerivedFieldDef | SqlDerivedFieldDef;
3424
+ type DerivedFieldsMap = Readonly<Record<string, DerivedFieldDef>>;
3425
+ type DerivedRelatedMeasure =
3426
+ | {
3427
+ kind: "count";
3428
+ }
3429
+ | {
3430
+ kind: "countDistinct";
3431
+ field: string;
3432
+ }
3433
+ | {
3434
+ kind: "first";
3435
+ field: string;
3436
+ orderBy: string;
3437
+ direction: "asc" | "desc";
3438
+ label?: boolean; // Only for a reference field with labelField: yields the referenced row's label.
3439
+ }
3440
+ ;
3441
+ type DerivedSqlSpec =
3442
+ | {
3443
+ kind: "coalesce";
3444
+ fields: string[];
3445
+ }
3446
+ | {
3447
+ kind: "related";
3448
+ entity: string; // Same convention as a reference field's `entity` ("feature:entity").
3449
+ on: {
3450
+ field: string;
3451
+ outer?: string;
3452
+ }; // related.<field> = outer.<outer ??
3453
+ measure: DerivedRelatedMeasure;
3454
+ where?: DerivedCondition[]; // AND-ed.
3455
+ }
3456
+ ;
3457
+ type DerivedValueType = "text" | "number" | "decimal" | "boolean" | "date" | "timestamp" | "money"; // Display type a derived value formats as — drives the column's renderer choice in the view-model, parallel to FieldDefinition["type"].
3458
+ type JsDerivedFieldDef = {
3298
3459
  valueType: DerivedValueType;
3299
3460
  derive: (row: Readonly<Record<string, unknown>>, ctx: DeriveContext) => unknown; // Pure function of the stored row + clock.
3300
3461
  };
3301
- type DerivedFieldsMap = Readonly<Record<string, DerivedFieldDef>>;
3302
- type DerivedValueType = "text" | "number" | "decimal" | "boolean" | "date" | "timestamp"; // Display type a derived value formats as — drives the column's renderer choice in the view-model, parallel to FieldDefinition["type"].
3462
+ type SqlDerivedFieldDef = {
3463
+ valueType: DerivedValueType;
3464
+ sortable?: boolean;
3465
+ sql: DerivedSqlSpec;
3466
+ derive?: undefined;
3467
+ };
3303
3468
  ```
3304
3469
 
3305
3470
  ## packages/types/src/fields.ts
3306
3471
 
3307
3472
  ```ts
3308
- type BigIntFieldDef = ResolvedPiiFlags & { // 64-bit integer field for audit counters, byte sizes, event IDs, and other values that can exceed 2^31 (~2.1 billion).
3473
+ type BigIntFieldDef = PiiAndFieldState & { // 64-bit integer field for audit counters, byte sizes, event IDs, and other values that can exceed 2^31 (~2.1 billion).
3309
3474
  type: "bigInt";
3310
3475
  description?: string;
3311
3476
  required?: boolean;
@@ -3316,7 +3481,7 @@ type BigIntFieldDef = ResolvedPiiFlags & { // 64-bit integer field for audit cou
3316
3481
  default?: number;
3317
3482
  access?: FieldAccess;
3318
3483
  };
3319
- type BooleanFieldDef = {
3484
+ type BooleanFieldDef = FieldStateConditions & {
3320
3485
  type: "boolean";
3321
3486
  description?: string;
3322
3487
  required?: boolean;
@@ -3327,7 +3492,7 @@ type BooleanFieldDef = {
3327
3492
  default?: boolean;
3328
3493
  access?: FieldAccess;
3329
3494
  };
3330
- type DateFieldDef = ResolvedPiiFlags & {
3495
+ type DateFieldDef = PiiAndFieldState & {
3331
3496
  type: "date";
3332
3497
  description?: string;
3333
3498
  required?: boolean;
@@ -3340,7 +3505,7 @@ type DateFieldDef = ResolvedPiiFlags & {
3340
3505
  max?: string;
3341
3506
  locale?: string; // Format/locale override for display and input parsing (for example, "de-DE").
3342
3507
  };
3343
- type DecimalFieldDef = ResolvedPiiFlags & { // Exact decimal — Postgres `numeric(precision, scale)`.
3508
+ type DecimalFieldDef = PiiAndFieldState & { // Exact decimal — Postgres `numeric(precision, scale)`.
3344
3509
  type: "decimal";
3345
3510
  description?: string;
3346
3511
  precision: number;
@@ -3353,7 +3518,7 @@ type DecimalFieldDef = ResolvedPiiFlags & { // Exact decimal — Postgres `numer
3353
3518
  default?: number;
3354
3519
  access?: FieldAccess;
3355
3520
  };
3356
- type EmbeddedFieldDef = ResolvedPiiFlags & {
3521
+ type EmbeddedFieldDef = PiiAndFieldState & {
3357
3522
  type: "embedded";
3358
3523
  description?: string;
3359
3524
  required?: boolean;
@@ -3367,12 +3532,12 @@ type EmbeddedFieldDef = ResolvedPiiFlags & {
3367
3532
  totals?: EmbeddedTotalDef[]; // Renderer metadata: footer totals.
3368
3533
  totalsMatch?: Readonly<Record<string, string>>; // Maps an embedded-list subfield name to a sibling top-level money field name — the sum of that subfield across all rows must equal the sibling field's amount.
3369
3534
  };
3370
- type EmbeddedSubFieldBase = {
3535
+ type EmbeddedSubFieldBase = FieldStateConditions & {
3371
3536
  required?: boolean;
3372
3537
  searchable?: boolean;
3373
3538
  access?: FieldAccess;
3374
3539
  };
3375
- type EmbeddedSubFieldDef = (EmbeddedSubFieldBase & { type: "text" | "number" | "boolean" | "date" | "money" | "timestamp"; }) | (EmbeddedSubFieldBase & { type: "decimal"; scale: number; }) | (EmbeddedSubFieldBase & { type: "select"; options: string[]; display?: "radio" | "dropdown"; }) | (EmbeddedSubFieldBase & { type: "reference"; entity: string; labelField?: string; optionsQuery?: string; }); // `money` is a signed integer amount in minor units (cents); which currency it means is defined by the owning aggregate (a head-level field or tenant config), not per row.
3540
+ type EmbeddedSubFieldDef = (EmbeddedSubFieldBase & { type: "text" | "number" | "boolean" | "date" | "money" | "timestamp"; }) | (EmbeddedSubFieldBase & { type: "decimal"; scale: number; }) | (EmbeddedSubFieldBase & { type: "select"; options: string[]; display?: "radio" | "dropdown"; optionTones?: { [option: string]: SelectOptionTone; }; }) | (EmbeddedSubFieldBase & { type: "reference"; entity: string; labelField?: string; optionsQuery?: string; }) | (EmbeddedSubFieldBase & EmbeddedImagesSubFieldProps …; // `money` is a signed integer amount in minor units (cents); which currency it means is defined by the owning aggregate (a head-level field or tenant config), not per row.
3376
3541
  type EmbeddedTotalDef =
3377
3542
  | string
3378
3543
  | {
@@ -3400,7 +3565,7 @@ type FieldAccess = {
3400
3565
  write?: OwnershipMap | string[];
3401
3566
  };
3402
3567
  type FieldsMap = Readonly<Record<string, EntityFieldDefinition>>;
3403
- type FileFieldDef = ResolvedPiiFlags & {
3568
+ type FileFieldDef = PiiAndFieldState & {
3404
3569
  type: "file";
3405
3570
  description?: string;
3406
3571
  required?: boolean;
@@ -3409,7 +3574,7 @@ type FileFieldDef = ResolvedPiiFlags & {
3409
3574
  accept?: string[];
3410
3575
  access?: FieldAccess;
3411
3576
  };
3412
- type FilesFieldDef = ResolvedPiiFlags & {
3577
+ type FilesFieldDef = PiiAndFieldState & {
3413
3578
  type: "files";
3414
3579
  description?: string;
3415
3580
  maxSize?: string;
@@ -3421,7 +3586,7 @@ type FilesFieldDef = ResolvedPiiFlags & {
3421
3586
  };
3422
3587
  type Findability = "exact" | "fuzzy" | "none" | "secret";
3423
3588
  type FormFieldDefinition = Exclude<FieldDefinition, MoneyFieldDef> | (MoneyFieldDef & { currency: MoneyCurrencySource; }); // FieldDefinition as an entity-less inline form screen (actionForm, secretMint) may declare it: a `money` field there has no entity to borrow `defaultCurrency` from, so it must name its own currency source, or the form seeds a bare `0` that the handler's zod schema then rejects .
3424
- type ImageFieldDef = ResolvedPiiFlags & {
3589
+ type ImageFieldDef = PiiAndFieldState & {
3425
3590
  type: "image";
3426
3591
  description?: string;
3427
3592
  required?: boolean;
@@ -3432,7 +3597,7 @@ type ImageFieldDef = ResolvedPiiFlags & {
3432
3597
  capture?: "environment" | "user"; // Forwarded to the file input's `capture` attribute so a phone opens the camera instead of the file picker.
3433
3598
  access?: FieldAccess;
3434
3599
  };
3435
- type ImagesFieldDef = ResolvedPiiFlags & {
3600
+ type ImagesFieldDef = PiiAndFieldState & {
3436
3601
  type: "images";
3437
3602
  description?: string;
3438
3603
  maxSize?: string;
@@ -3445,7 +3610,7 @@ type ImagesFieldDef = ResolvedPiiFlags & {
3445
3610
  access?: FieldAccess;
3446
3611
  };
3447
3612
  type JsonbEmptyValue = "object" | "array";
3448
- type LocatedTimestampFieldDef = ResolvedPiiFlags & {
3613
+ type LocatedTimestampFieldDef = PiiAndFieldState & {
3449
3614
  type: "locatedTimestamp";
3450
3615
  description?: string;
3451
3616
  required?: boolean;
@@ -3458,7 +3623,7 @@ type LocatedTimestampFieldDef = ResolvedPiiFlags & {
3458
3623
  max?: string;
3459
3624
  locale?: string; // Format/locale override for display and input parsing.
3460
3625
  };
3461
- type LongTextFieldDef = ResolvedPiiFlags & { // Long-form text content — source code, markdown, blog posts, email templates, and anything that can be megabytes large.
3626
+ type LongTextFieldDef = PiiAndFieldState & { // Long-form text content — source code, markdown, blog posts, email templates, and anything that can be megabytes large.
3462
3627
  type: "longText";
3463
3628
  description?: string;
3464
3629
  maxLength?: number; // Optional soft cap.
@@ -3480,7 +3645,7 @@ type MoneyCurrencySource = // Where a money field's currency comes from.
3480
3645
  code: string;
3481
3646
  }
3482
3647
  ;
3483
- type MoneyFieldDef = {
3648
+ type MoneyFieldDef = FieldStateConditions & {
3484
3649
  type: "money";
3485
3650
  description?: string;
3486
3651
  required?: boolean;
@@ -3491,7 +3656,7 @@ type MoneyFieldDef = {
3491
3656
  access?: FieldAccess;
3492
3657
  currency?: MoneyCurrencySource; // Declares where this field's currency comes from for a currently-empty value in an entityEdit form.
3493
3658
  };
3494
- type MultiSelectFieldDef<TOptions extends string[] = string[]> = ResolvedPiiFlags & {
3659
+ type MultiSelectFieldDef<TOptions extends string[] = string[]> = PiiAndFieldState & {
3495
3660
  type: "multiSelect";
3496
3661
  description?: string;
3497
3662
  options: TOptions;
@@ -3511,7 +3676,7 @@ type NoPersonalAnnotation = {
3511
3676
  reason?: never;
3512
3677
  anonymize?: never;
3513
3678
  };
3514
- type NumberFieldDef = ResolvedPiiFlags & { // Storage: `integer` flag decides the Postgres column type, not just Zod validation.
3679
+ type NumberFieldDef = PiiAndFieldState & { // Storage: `integer` flag decides the Postgres column type, not just Zod validation.
3515
3680
  type: "number";
3516
3681
  description?: string;
3517
3682
  required?: boolean;
@@ -3535,11 +3700,6 @@ type OptionsQueryPayloadValue = // One `optionsQueryPayload` entry: a literal, o
3535
3700
  field: string;
3536
3701
  }
3537
3702
  ;
3538
- type ParentRefDef = {
3539
- entityTypeField: string; // Field holding the name of the host entity this row hangs off.
3540
- entityIdField: string; // Field holding the host row's id.
3541
- allowedTypes?: string[]; // Narrow which registered entities may act as host.
3542
- };
3543
3703
  type PersonalAnnotations =
3544
3704
  | {
3545
3705
  personal: PersonalSubject;
@@ -3593,7 +3753,8 @@ type PersonalSubject =
3593
3753
  }
3594
3754
  | "tenant"
3595
3755
  ;
3596
- type ReferenceFieldDef = ResolvedPiiFlags & {
3756
+ type PiiAndFieldState = ResolvedPiiFlags & FieldStateConditions;
3757
+ type ReferenceFieldDef = PiiAndFieldState & {
3597
3758
  type: "reference";
3598
3759
  description?: string;
3599
3760
  entity: string;
@@ -3633,7 +3794,7 @@ type SelectConditionalOptions<TOption extends string = string> = { // Options of
3633
3794
  options: TOption[];
3634
3795
  when: Exclude<FieldCondition, boolean>;
3635
3796
  };
3636
- type SelectFieldDef<TOptions extends string[] = string[]> = ResolvedPiiFlags & {
3797
+ type SelectFieldDef<TOptions extends string[] = string[]> = PiiAndFieldState & {
3637
3798
  type: "select";
3638
3799
  description?: string;
3639
3800
  options: TOptions;
@@ -3652,7 +3813,7 @@ type SelectFieldDef<TOptions extends string[] = string[]> = ResolvedPiiFlags & {
3652
3813
  conditionalOptions?: SelectConditionalOptions<TOptions[number]>[]; // Options that are only available while `when` holds for the row values; options in no rule are always available.
3653
3814
  };
3654
3815
  type SortNulls = "first" | "last"; // Where rows without a value sort in a list, in both directions.
3655
- type TimestampFieldDef = ResolvedPiiFlags & {
3816
+ type TimestampFieldDef = PiiAndFieldState & {
3656
3817
  type: "timestamp";
3657
3818
  description?: string;
3658
3819
  required?: boolean;
@@ -3667,7 +3828,7 @@ type TimestampFieldDef = ResolvedPiiFlags & {
3667
3828
  locale?: string; // Format/locale override for display and input parsing.
3668
3829
  };
3669
3830
  type TransitionMap = Readonly<Record<string, string[]>>;
3670
- type TzFieldDef = ResolvedPiiFlags & {
3831
+ type TzFieldDef = PiiAndFieldState & {
3671
3832
  type: "tz";
3672
3833
  description?: string;
3673
3834
  required?: boolean;
@@ -4060,6 +4221,7 @@ type HookMap = {
4060
4221
  type HookOwner = {
4061
4222
  featureName?: string;
4062
4223
  };
4224
+ type LifecycleResult = SaveContext | DeleteContext;
4063
4225
  type OwnedFn<TFn> = HookOwner & {
4064
4226
  fn: TFn;
4065
4227
  };
@@ -4565,7 +4727,7 @@ type ScreenFilterOp = "eq" | "ne" | "lt" | "gt" | "lte" | "gte" | "in";
4565
4727
  ## packages/types/src/screen-expandable-row.ts
4566
4728
 
4567
4729
  ```ts
4568
- type EntityListExpandableRow = Omit<EditRelatedListSection, "id" | "countField" | "table" | "bulkActions"> & { // Expandable area of an `entityList` row: a relatedList of the row's record.
4730
+ type EntityListExpandableRow = Omit<EditRelatedListSection, "id" | "countField" | "count" | "table" | "bulkActions"> & { // Expandable area of an `entityList` row: a relatedList of the row's record.
4569
4731
  table?: {
4570
4732
  grouping?: ExpandableRowGrouping;
4571
4733
  };
package/agents/api.md CHANGED
@@ -8,9 +8,9 @@ Format: one line per function or constant, object and options types as property
8
8
 
9
9
  | Import path | File | Exports |
10
10
  | --- | --- | --- |
11
- | `@cosmicdrift/kumiko-framework/engine` | `api/framework-engine.md` | 409 |
11
+ | `@cosmicdrift/kumiko-framework/engine` | `api/framework-engine.md` | 411 |
12
12
  | `@cosmicdrift/kumiko-framework/errors` | `api/framework-errors.md` | 25 |
13
- | `@cosmicdrift/kumiko-framework/ui-types` | `api/framework-ui-types.md` | 149 |
13
+ | `@cosmicdrift/kumiko-framework/ui-types` | `api/framework-ui-types.md` | 154 |
14
14
  | `@cosmicdrift/kumiko-framework/db` | `api/framework-db.md` | 150 |
15
15
  | `@cosmicdrift/kumiko-framework/event-store` | `api/framework-event-store.md` | 38 |
16
16
  | `@cosmicdrift/kumiko-framework/seeding` | `api/framework-seeding.md` | 6 |