@cosmicdrift/kumiko-framework 0.399.0 → 0.401.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.
- package/AGENTS.md +4 -3
- package/agents/api/framework-engine.md +16 -8
- package/agents/api/framework-ui-types.md +10 -5
- package/agents/api/renderer-web.md +2 -1
- package/agents/api/testing-stack.md +1 -0
- package/agents/api/types.md +256 -80
- package/agents/api.md +3 -3
- package/agents/examples/derived-fields-sql.md +251 -0
- package/agents/examples/documents.md +161 -0
- package/agents/examples/inspection-checklist.md +807 -0
- package/agents/examples/relations.md +13 -3
- package/agents/examples.md +3 -0
- package/agents/features-index.md +2 -0
- package/agents/features.md +8 -0
- package/dist/api/entity-read-access.d.ts +2 -0
- package/dist/api/entity-read-access.js +46 -2
- package/dist/api/server-boot-checks.js +2 -2
- package/dist/api/server-routes.js +3 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/db/derived-field-sql.d.ts +23 -0
- package/dist/db/derived-field-sql.js +115 -0
- package/dist/db/derived-related-sql.d.ts +31 -0
- package/dist/db/derived-related-sql.js +137 -0
- package/dist/db/driver-row-coercion.d.ts +1 -0
- package/dist/db/driver-row-coercion.js +13 -0
- package/dist/db/eagerload.d.ts +3 -1
- package/dist/db/eagerload.js +4 -6
- package/dist/db/event-store-executor-context.js +1 -1
- package/dist/db/event-store-executor-read-paging.js +1 -1
- package/dist/db/event-store-executor-read-search.d.ts +3 -0
- package/dist/db/event-store-executor-read-search.js +17 -9
- package/dist/db/event-store-executor-read-sort.d.ts +9 -2
- package/dist/db/event-store-executor-read-sort.js +27 -14
- package/dist/db/event-store-executor-read-where.d.ts +0 -5
- package/dist/db/event-store-executor-read-where.js +3 -9
- package/dist/db/event-store-executor-read.js +23 -4
- package/dist/db/event-store-executor-write-steps.d.ts +6 -1
- package/dist/db/event-store-executor-write-steps.js +20 -1
- package/dist/db/event-store-executor-write.js +8 -8
- package/dist/db/money.d.ts +4 -0
- package/dist/db/money.js +34 -29
- package/dist/db/parent-ref-clause.d.ts +1 -0
- package/dist/db/parent-ref-clause.js +18 -8
- package/dist/db/read-gate-clause.d.ts +7 -0
- package/dist/db/read-gate-clause.js +201 -0
- package/dist/db/read-ownership.d.ts +9 -0
- package/dist/db/read-ownership.js +12 -0
- package/dist/engine/boot-validator/boot-check.d.ts +3 -1
- package/dist/engine/boot-validator/boot-check.js +7 -11
- package/dist/engine/boot-validator/boot-context.d.ts +26 -0
- package/dist/engine/boot-validator/boot-context.js +57 -0
- package/dist/engine/boot-validator/boot-options.d.ts +38 -0
- package/dist/engine/boot-validator/boot-options.js +1 -0
- package/dist/engine/boot-validator/boot-phases-app.d.ts +5 -0
- package/dist/engine/boot-validator/boot-phases-app.js +107 -0
- package/dist/engine/boot-validator/boot-phases.d.ts +11 -0
- package/dist/engine/boot-validator/boot-phases.js +140 -0
- package/dist/engine/boot-validator/boot-report.d.ts +6 -0
- package/dist/engine/boot-validator/boot-report.js +47 -0
- package/dist/engine/boot-validator/boot-rule.d.ts +12 -0
- package/dist/engine/boot-validator/boot-rule.js +14 -0
- package/dist/engine/boot-validator/boot-warnings.d.ts +2 -0
- package/dist/engine/boot-validator/boot-warnings.js +20 -0
- package/dist/engine/boot-validator/derived-sql-fields.d.ts +4 -0
- package/dist/engine/boot-validator/derived-sql-fields.js +184 -0
- package/dist/engine/boot-validator/entity-handler.d.ts +3 -0
- package/dist/engine/boot-validator/entity-handler.js +6 -3
- package/dist/engine/boot-validator/entity-list-screens.js +3 -1
- package/dist/engine/boot-validator/field-conditions.d.ts +2 -0
- package/dist/engine/boot-validator/field-conditions.js +196 -0
- package/dist/engine/boot-validator/i18n-keys.d.ts +3 -2
- package/dist/engine/boot-validator/i18n-keys.js +18 -4
- package/dist/engine/boot-validator/index.d.ts +5 -34
- package/dist/engine/boot-validator/index.js +24 -204
- package/dist/engine/boot-validator/pii-retention.js +15 -9
- package/dist/engine/boot-validator/projection-list-screens.js +19 -1
- package/dist/engine/boot-validator/read-gate.d.ts +2 -0
- package/dist/engine/boot-validator/read-gate.js +59 -0
- package/dist/engine/boot-validator/screens-forms.js +19 -6
- package/dist/engine/boot-validator/screens-kind-entity-list.js +8 -6
- package/dist/engine/boot-validator/screens-lists.js +8 -0
- package/dist/engine/boot-validator/zod-shape.js +4 -2
- package/dist/engine/build-app-schema.js +11 -5
- package/dist/engine/embedded-derived.d.ts +8 -6
- package/dist/engine/embedded-derived.js +25 -7
- package/dist/engine/entity-handlers.d.ts +13 -6
- package/dist/engine/entity-handlers.js +17 -18
- package/dist/engine/entity-list-runtime-options.d.ts +2 -1
- package/dist/engine/entity-list-runtime-options.js +86 -5
- package/dist/engine/factories.d.ts +18 -6
- package/dist/engine/factories.js +21 -5
- package/dist/engine/field-access.d.ts +2 -0
- package/dist/engine/field-access.js +48 -1
- package/dist/engine/field-state.d.ts +12 -0
- package/dist/engine/field-state.js +67 -0
- package/dist/engine/handler-helpers.d.ts +3 -0
- package/dist/engine/handler-helpers.js +7 -1
- package/dist/engine/index.d.ts +3 -3
- package/dist/engine/index.js +3 -3
- package/dist/engine/ownership.d.ts +1 -1
- package/dist/engine/ownership.js +9 -5
- package/dist/engine/registry-state.d.ts +1 -0
- package/dist/engine/registry-state.js +1 -1
- package/dist/engine/schema-builder.js +3 -0
- package/dist/engine/strip-unreadable-related-lists.d.ts +4 -0
- package/dist/engine/strip-unreadable-related-lists.js +80 -0
- package/dist/engine/user-entity-reference.d.ts +2 -0
- package/dist/engine/user-entity-reference.js +7 -0
- package/dist/files/assert-file-refs-belong-to-tenant.js +26 -6
- package/dist/files/bind-pending-file-refs.d.ts +3 -2
- package/dist/files/bind-pending-file-refs.js +11 -12
- package/dist/files/bind-referenced-files.write.js +5 -5
- package/dist/files/create-file-ref-from-bytes.d.ts +2 -0
- package/dist/files/create-file-ref-from-bytes.js +2 -0
- package/dist/files/create-file-ref.d.ts +2 -0
- package/dist/files/create-file-ref.js +39 -22
- package/dist/files/dispose-removed-file-refs.js +5 -8
- package/dist/files/file-field-slots.d.ts +10 -0
- package/dist/files/file-field-slots.js +53 -0
- package/dist/files/file-ref-entity.d.ts +1 -1
- package/dist/files/file-ref-table.d.ts +1 -1
- package/dist/files/file-routes.js +12 -3
- package/dist/files/index.d.ts +3 -0
- package/dist/files/index.js +2 -0
- package/dist/files/load-save-file-refs.js +3 -4
- package/dist/files/rebind-file-ref.d.ts +41 -0
- package/dist/files/rebind-file-ref.js +170 -0
- package/dist/files/record-read-check.js +2 -30
- package/dist/files/record-write-check.d.ts +2 -0
- package/dist/files/record-write-check.js +18 -3
- package/dist/files/upload-field-resolution.d.ts +2 -1
- package/dist/files/upload-field-resolution.js +33 -13
- package/dist/i18n/required-surface-keys.d.ts +6 -0
- package/dist/i18n/required-surface-keys.js +61 -0
- package/dist/pipeline/dispatch-batch.js +6 -5
- package/dist/pipeline/dispatch-write.js +2 -1
- package/dist/pipeline/write-result-masking.js +2 -1
- package/dist/schema-cli/validate-boot-input.js +2 -0
- package/dist/schema-cli/validate-command.js +46 -12
- package/dist/schema-cli.js +1 -1
- package/dist/ui-types/index.d.ts +2 -1
- package/dist/ui-types/index.js +2 -1
- package/package.json +3 -3
- package/src/changes.json +68 -0
package/AGENTS.md
CHANGED
|
@@ -113,8 +113,9 @@ the field types reject a text field without that stance.
|
|
|
113
113
|
`personal: false, reason: "is_business_data"` (a snake_case reason) for
|
|
114
114
|
business data. For personal data name the subject, `personal: "self"`,
|
|
115
115
|
`personal: { of: "<ownerField>" }` or `personal: "tenant"`, plus
|
|
116
|
-
`find: "exact" | "fuzzy" | "none" | "secret"`. A
|
|
117
|
-
is `
|
|
116
|
+
`find: "exact" | "fuzzy" | "none" | "secret"`. A foreign key to a user
|
|
117
|
+
is `createReferenceField({ entity: "user:user" })`, which defaults to
|
|
118
|
+
`personal: "ref"`; `personal: false` on it fails at boot. `personal: "self"` is for the row that is the user.
|
|
118
119
|
When the record itself is the person, such as a contact or a customer that
|
|
119
120
|
is not a user, use `personal: { of: "id" }`; the entity then needs a uuid
|
|
120
121
|
`id`. The framework encrypts personal fields per subject and
|
|
@@ -306,7 +307,7 @@ as bundled features.
|
|
|
306
307
|
| Business logic beyond CRUD | `r.writeHandler` / `r.queryHandler` with `createEntityExecutor` | `agents/examples/custom-handlers.md` |
|
|
307
308
|
| Filtered lists and counts | Executor `list` with `filter` and `totalCount` | `agents/examples/custom-handlers.md` |
|
|
308
309
|
| Key figures on a dashboard | `dashboard` screen with `stat` / `stat-group` panels and a query handler | `agents/examples/dashboard-stats.md` |
|
|
309
|
-
| Relations between entities | `r.relation(entity, name, { type, target, foreignKey, onDelete })` | `agents/examples/relations.md` |
|
|
310
|
+
| Relations between entities | `createReferenceField({ entity })` for the foreign key (user ids: `entity: "user:user"`), `r.relation(entity, name, { type, target, foreignKey, onDelete })` | `agents/examples/relations.md` |
|
|
310
311
|
| Structured sub-objects in a field | Embedded fields | `agents/examples/embedded.md` |
|
|
311
312
|
| List, edit, custom screens and navs | `r.screen`, `r.nav` | `agents/examples/screens-nav.md` |
|
|
312
313
|
| A record page with header, metrics, tabs | `projectionDetail` with `header`, `metrics`, `layout.mode: "tabs"` | `agents/examples/record-detail-layout.md` |
|
|
@@ -30,10 +30,10 @@ function createBigIntField<R extends true | false = false>(overrides?: Partial<O
|
|
|
30
30
|
function createBooleanField<R extends true | false = false>(overrides?: Partial<Omit<BooleanFieldDef, "type" | "required">> & { required?: R; }): BooleanFieldDef & { required: R; default: boolean; };
|
|
31
31
|
function createDateField<R extends true | false = false>(overrides?: Partial<Omit<DateFieldDef, "type" | "required" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind & { required?: R; }): DateFieldDef & { required: R; };
|
|
32
32
|
function createDecimalField<R extends true | false = false>(config: { precision: number; scale: number; } & Partial<Omit<DecimalFieldDef, "type" | "precision" | "scale" | "required" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind & { required?: R; }): DecimalFieldDef & { required: R; };
|
|
33
|
-
function createDerivedField(spec: DerivedFieldDef): DerivedFieldDef; // Read-time computed field for `EntityDefinition.derivedFields` (NOT `fields`).
|
|
33
|
+
function createDerivedField(spec: DerivedFieldDef): DerivedFieldDef; // Read-time computed field for `EntityDefinition.derivedFields` (NOT `fields`), never persisted.
|
|
34
34
|
function createEmbeddedField(schema: EmbeddedFieldDef["schema"], overrides?: Partial<Omit<EmbeddedFieldDef, "type" | "schema" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind): EmbeddedFieldDef;
|
|
35
35
|
function createEmbeddedListField(schema: EmbeddedFieldDef["schema"], overrides?: Partial<Omit<EmbeddedFieldDef, "type" | "schema" | "multiple" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind): EmbeddedFieldDef & { multiple: true; };
|
|
36
|
-
function createEntity<const F, const T extends EntityTenancy = "tenant">(def: { table?: string; fields: F; description?: string; softDelete?: boolean; systemStream?: boolean; eventVersion?: number; eventMigrations?: EntityDefinition["eventMigrations"]; searchWeight?: number; defaultCurrency?: string; transitions?: Readonly<Record<string, Readonly<Record<string, string[]>>>>; lockedWhen?: EntityDefinition["lockedWhen"]; indexes?: EntityIndexDef[]; idType?: "serial" | "uuid"; access?: EntityDefinition["access"]; parentRef?: EntityDefinition["parentRef"]; retention?: RetentionDef; derivedFields?: EntityDefinition["derivedFields"]; tenancy?: T; transferable?: boolean; attachedFileAccess?: EntityDefinition["attachedFileAccess"]; }): F extends FieldsMap ? EntityDefinition<F, T> : never;
|
|
36
|
+
function createEntity<const F, const T extends EntityTenancy = "tenant">(def: { table?: string; fields: F; description?: string; softDelete?: boolean; systemStream?: boolean; eventVersion?: number; eventMigrations?: EntityDefinition["eventMigrations"]; searchWeight?: number; defaultCurrency?: string; transitions?: Readonly<Record<string, Readonly<Record<string, string[]>>>>; lockedWhen?: EntityDefinition["lockedWhen"]; indexes?: EntityIndexDef[]; idType?: "serial" | "uuid"; access?: EntityDefinition["access"]; parentRef?: EntityDefinition["parentRef"]; readGate?: EntityDefinition["readGate"]; retention?: RetentionDef; derivedFields?: EntityDefinition["derivedFields"]; tenancy?: T; transferable?: boolean; attachedFileAccess?: EntityDefinition["attachedFileAccess"]; }): F extends FieldsMap ? EntityDefinition<F, T> : never;
|
|
37
37
|
function createEntityExecutor<E extends EntityDefinition>(entityName: string, entity: E): { table: EntityTable; executor: EventStoreExecutor<EntityRow<E>>; };
|
|
38
38
|
function createFileField(overrides?: Partial<Omit<FileFieldDef, "type" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind): FileFieldDef;
|
|
39
39
|
function createFilesField(overrides?: Partial<Omit<FilesFieldDef, "type" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind): FilesFieldDef;
|
|
@@ -45,6 +45,7 @@ function createLongTextField<R extends true | false = false>(overrides: Partial<
|
|
|
45
45
|
function createMoneyField<R extends true | false = false>(overrides?: Partial<Omit<MoneyFieldDef, "type" | "required">> & { required?: R; }): MoneyFieldDef & { required: R; };
|
|
46
46
|
function createMultiSelectField<const TOptions extends string[]>(opts: { options: TOptions; } & Partial<Omit<MultiSelectFieldDef<TOptions>, "type" | "options" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind): MultiSelectFieldDef<TOptions>; // Multi-select field — N values from a fixed option list.
|
|
47
47
|
function createNumberField<R extends true | false = false>(overrides?: Partial<Omit<NumberFieldDef, "type" | "required" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind & { required?: R; }): NumberFieldDef & { required: R; }; // Numeric field — `double precision` column by default (fractional values allowed end to end).
|
|
48
|
+
function createReferenceField<R extends true | false = false>(opts: { entity: string; } & Partial<Omit<ReferenceFieldDef, "type" | "entity" | "required" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind & { required?: R; }): ReferenceFieldDef & { required: R; }; // Foreign key to another entity (`"customer"` or `"feature:entity"`), stored as a UUID.
|
|
48
49
|
function createRegistry(rawFeatures: FeatureDefinition[]): Registry;
|
|
49
50
|
function createSeed(opts: CreateSeedOptions): ConfigSeedDef;
|
|
50
51
|
function createSelectField<const TOptions extends string[], R extends true | false = false>(opts: { options: TOptions; } & Partial<Omit<SelectFieldDef<NoInfer<TOptions>>, "type" | "options" | "required" | keyof ResolvedPiiFlags>> & PersonalAnnotationsNoFind & { required?: R; }): SelectFieldDef<TOptions> & { required: R; };
|
|
@@ -60,14 +61,14 @@ function createUserConfig<T extends ConfigKeyType>(type: T, opts?: ConfigKeyOpti
|
|
|
60
61
|
function declareEscapeHatch(declaration: EscapeHatchDeclaration): void;
|
|
61
62
|
function dedupeFeatures(features: FeatureDefinition[]): FeatureDefinition[];
|
|
62
63
|
function defineApply<TPayload = Record<string, unknown>>(fn: (event: StoredEvent<TPayload>, tx: ExecutorManagedRunner, table: ProjectionTable) => Promise<void>): SingleStreamApplyFn;
|
|
63
|
-
function defineEntityCreateHandler(entityName: string, entity: EntityDefinition, options: EntityWriteHandlerOptions):
|
|
64
|
-
function defineEntityDeleteHandler(entityName: string, entity: EntityDefinition, options: EntityHandlerOptions):
|
|
64
|
+
function defineEntityCreateHandler(entityName: string, entity: EntityDefinition, options: EntityWriteHandlerOptions): EntityWriteHandlerDef;
|
|
65
|
+
function defineEntityDeleteHandler(entityName: string, entity: EntityDefinition, options: EntityHandlerOptions): EntityWriteHandlerDef;
|
|
65
66
|
function defineEntityDetailHandler(entityName: string, entity: EntityDefinition, options: EntityQueryHandlerOptions): QueryHandlerDef;
|
|
66
67
|
function defineEntityListHandler(entityName: string, entity: EntityDefinition, options: EntityQueryHandlerOptions): QueryHandlerDef;
|
|
67
68
|
function defineEntityQueryHandler(name: string, entity: EntityDefinition, options: EntityQueryHandlerOptions): QueryHandlerDef;
|
|
68
|
-
function defineEntityRestoreHandler(entityName: string, entity: EntityDefinition, options: EntityHandlerOptions):
|
|
69
|
-
function defineEntityUpdateHandler(entityName: string, entity: EntityDefinition, options: EntityWriteHandlerOptions):
|
|
70
|
-
function defineEntityWriteHandler(name: string, entity: EntityDefinition, options: EntityWriteHandlerOptions):
|
|
69
|
+
function defineEntityRestoreHandler(entityName: string, entity: EntityDefinition, options: EntityHandlerOptions): EntityWriteHandlerDef;
|
|
70
|
+
function defineEntityUpdateHandler(entityName: string, entity: EntityDefinition, options: EntityWriteHandlerOptions): EntityWriteHandlerDef;
|
|
71
|
+
function defineEntityWriteHandler(name: string, entity: EntityDefinition, options: EntityWriteHandlerOptions): EntityWriteHandlerDef;
|
|
71
72
|
function defineFeature<const TName extends string, TExports = undefined>(name: TName, setup: (r: FeatureRegistrar<TName>) => TExports, settings?: DefineFeatureSettings): FeatureDefinition & { exports: TExports; };
|
|
72
73
|
function defineMspApply<TPayload = Record<string, unknown>>(fn: (event: StoredEvent<TPayload>, tx: ExecutorManagedRunner, ctx: MultiStreamApplyContext) => Promise<void>): MultiStreamApplyFn;
|
|
73
74
|
function definePagedQueryHandler<const TName extends string, TSchema extends ZodType, TRow = unknown, TMap extends object = KumikoEventTypeMap>(def: QueryHandlerDefinition<TName, TSchema, CursorResult<TRow>, TMap>, ..._noSecretInResponse: true extends ContainsSecret<CursorResult<TRow>> ? [ secretLeak: "A handler response must not contain a Secret<> — call .reveal() and return the plaintext, or drop the field." ] : [ ]): PagedQueryHandlerDefinition<TName, TSchema, TRow, TMap>;
|
|
@@ -120,6 +121,7 @@ function normalizeEditField(f: EditFieldSpec): Exclude<EditFieldSpec, string>;
|
|
|
120
121
|
function normalizeListColumn(c: ListColumnSpec): Exclude<ListColumnSpec, string>;
|
|
121
122
|
function pageRows<TRow extends Readonly<Record<TSortField, PagedListRowValue>> & { id: string; }, TSortField extends string>(rows: TRow[], request: PagedListRequest<TSortField>, options: { searchFields: TSortField[]; defaultSort: TSortField; }): CursorResult<TRow>;
|
|
122
123
|
function pagedListSchema<const TSortField extends string>(sortableFields: [ TSortField, ...TSortField[] ]): …;
|
|
124
|
+
function parentVisibilityFor(ctx: HandlerContext, user: SessionUser): ParentVisibilityOption;
|
|
123
125
|
function parseChangesetChanges(markdown: string, source: string): PendingChange[]; // Parse structured upgrade metadata embedded in a Changeset body.
|
|
124
126
|
function parseFeatureChangelog(raw: string, featureName: string): FeatureChangelog | null; // Parse a changes.json body.
|
|
125
127
|
function parseQn(value: string): ParsedQn;
|
|
@@ -145,6 +147,7 @@ function tenantChannel(tenantId: TenantId): string;
|
|
|
145
147
|
function toKebab(input: string): string;
|
|
146
148
|
function typedPayload<TPayload>(event: { type: string; payload: Record<string, unknown>; }, eventDef: EventDef<TPayload>): TPayload;
|
|
147
149
|
function unwrapWorkflowStepError(error: unknown): unknown;
|
|
150
|
+
function userCanWriteFieldRow(user: SessionUser, accessMap: OwnershipMap | undefined, oldRow: Readonly<Record<string, unknown>>, newRow: Readonly<Record<string, unknown>>): boolean;
|
|
148
151
|
function validateAppCustomScreenWriteQns(appRoot: string, knownQns: ReadonlySet<string>): void; // Boot-time scan of app `src/**` for `dispatcher.write("…")` string literals.
|
|
149
152
|
function validateBoot(rawFeatures: FeatureDefinition[], options?: ValidateBootOptions): void; // Validates all feature configurations at boot time.
|
|
150
153
|
function validateBundledListsOptions(options: unknown): void;
|
|
@@ -482,6 +485,7 @@ type EditRelatedListSection = {
|
|
|
482
485
|
id?: string; // Kebab-case tab id, used as the `?tab=` value.
|
|
483
486
|
kind: "relatedList";
|
|
484
487
|
title: string;
|
|
488
|
+
readOnly?: boolean; // Rows are aggregates or other values with no record of their own, so they lead nowhere on purpose.
|
|
485
489
|
query: string; // Fully qualified query QN.
|
|
486
490
|
parentParam?: string; // Query-payload key the parent record's id is passed under.
|
|
487
491
|
parentFilter?: {
|
|
@@ -506,6 +510,7 @@ type EditRelatedListSection = {
|
|
|
506
510
|
rowTone?: RelatedListRowTone; // Tints rows whose field matches — a declarative rule, not a function, so the screen definition stays serializable.
|
|
507
511
|
toolbarActions?: RelatedListToolbarAction[]; // Toolbar actions above the table — same type and dispatch semantics as `entityList`/`projectionList`'s `toolbarActions` ("+ Anlegen" etc.), plus `visible`/`params` evaluated against the parent record (see RelatedListToolbarAction).
|
|
508
512
|
countField?: string; // Record field rendered as a count badge in the tab label when the enclosing `EditLayout.mode` is "tabs" (e.g. an open-items counter).
|
|
513
|
+
count?: true; // Tab badge counted from this section's own query (`limit: 1, totalCount: true` → `total`), so it matches the tab's rows and stays live.
|
|
509
514
|
actions?: RowAction[]; // Rendered top-right in the section's Card title row — same navigate/drawer/writeHandler dispatch as `ProjectionDetailScreenDefinition.actions`, evaluated against the parent record.
|
|
510
515
|
emptyState?: RelatedListEmptyStateSpec; // Rendered inside the section's Card in place of the table when this section's query returns zero rows.
|
|
511
516
|
};
|
|
@@ -532,6 +537,7 @@ type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTenancy =
|
|
|
532
537
|
write?: OwnershipMap;
|
|
533
538
|
}; // Row-level ownership rules (H.2).
|
|
534
539
|
parentRef?: ParentRefDef; // Join-row carrier reference.
|
|
540
|
+
readGate?: ReadGateDef; // Declarative read gate for rows readable only through other rows (a link to a visible host, a file whose owner is readable).
|
|
535
541
|
retention?: RetentionDef; // Default retention policy for this entity.
|
|
536
542
|
derivedFields?: DerivedFieldsMap; // Read-time computed fields, keyed by name.
|
|
537
543
|
description?: string; // Optional free-text semantics for the AI agent manifest — see TextFieldDef.description.
|
|
@@ -942,7 +948,7 @@ type JobTrigger =
|
|
|
942
948
|
manual: true;
|
|
943
949
|
}
|
|
944
950
|
;
|
|
945
|
-
type JsonbFieldDef =
|
|
951
|
+
type JsonbFieldDef = PiiAndFieldState & {
|
|
946
952
|
type: "jsonb";
|
|
947
953
|
description?: string;
|
|
948
954
|
emptyValue?: JsonbEmptyValue; // `"array"`: the field holds a JSON array, so a row that never wrote it reads `[]` and writes take an array.
|
|
@@ -1708,6 +1714,7 @@ type ValidateBootOptions = {
|
|
|
1708
1714
|
env?: Readonly<Record<string, string | undefined>>; // Env the encrypted-field master-key probe reads.
|
|
1709
1715
|
entityFieldCipher?: EnvelopeCipher; // The cipher the runner injects after validation (built from the app's `masterKey` or the env keyring).
|
|
1710
1716
|
requiredLocales?: string[]; // Locales every translation key must carry across all mounted features (locale packages count).
|
|
1717
|
+
requireFieldValueTranslations?: boolean; // Require translations for the values that entityList columns and entityEdit fields display: select/multiSelect option labels and the column headers and select options of embedded lists.
|
|
1711
1718
|
};
|
|
1712
1719
|
type ValidationError = {
|
|
1713
1720
|
field: string;
|
|
@@ -1798,6 +1805,7 @@ type WriteResult<TData = unknown> =
|
|
|
1798
1805
|
| {
|
|
1799
1806
|
isSuccess: true;
|
|
1800
1807
|
data: TData;
|
|
1808
|
+
lifecycle?: LifecycleResult; // The executor result behind a handler that answers with its own `data`.
|
|
1801
1809
|
}
|
|
1802
1810
|
| WriteFailure
|
|
1803
1811
|
;
|
|
@@ -10,13 +10,17 @@ Import from `@cosmicdrift/kumiko-framework/ui-types`. Types used here but not ex
|
|
|
10
10
|
function applyFormDerivations(values: unknown, screen: FormDerivationScreen): unknown; // Recomputes every derived head field of an action form from its inputs.
|
|
11
11
|
function availableSelectOptions(field: SelectOptionRules, values: Record<string, unknown>): string[]; // Options of a select field that are offered for the given row values, in declaration order.
|
|
12
12
|
function cardColumnRoles<C extends CardRoleColumn>(columns: C[]): CardColumnRoles<C>;
|
|
13
|
-
function computeDerivedCellValue(op:
|
|
13
|
+
function computeDerivedCellValue(op: NumericDerivedOp, values: (number | undefined)[]): number | undefined; // Computes a derived cell from its source values.
|
|
14
14
|
function evalFieldCondition(cond: FieldCondition, values: Record<string, unknown>): boolean; // Evaluates a declarative FieldCondition against the current row/form values.
|
|
15
|
+
function findMissingConditionalRequirements(fields: Readonly<Record<string, FieldDefinition>>, values: Record<string, unknown>, previous?: Record<string, unknown>): string[]; // Paths of fields (and embedded sub-fields) whose `requiredWhen` holds while the value is missing.
|
|
15
16
|
function findUnavailableSelectOptions(fields: Readonly<Record<string, FieldDefinition>>, values: Record<string, unknown>, previous?: Record<string, unknown>): UnavailableSelectOption[]; // Select values that violate a `conditionalOptions` rule.
|
|
16
17
|
function formalLocaleTag<Locale extends string>(locale: Locale): `${Locale}-x-formal`;
|
|
17
18
|
function isExtensionEditSection(section: EditSectionSpec): section is EditExtensionSection;
|
|
19
|
+
function isFieldRequired(def: RequiredState, values: Record<string, unknown>): boolean;
|
|
20
|
+
function isFieldVisible(def: FieldStateConditions, values: Record<string, unknown>): boolean;
|
|
18
21
|
function isFormDerivationInputMode(screen: FormDerivationScreen, values: unknown, field: string): boolean; // True when `field` is a `listSum` without `whenEmpty` whose list has no rows: the sum has nothing to add up, so the user types the value.
|
|
19
22
|
function isFormatSpec(r: unknown): r is FormatSpec;
|
|
23
|
+
function isMissingRequiredValue(value: unknown): boolean;
|
|
20
24
|
function isRecordLocked(lockedWhen: FieldCondition | undefined, row: Readonly<Record<string, unknown>>): boolean;
|
|
21
25
|
function isUiAccessGranted(access: AccessRule | undefined, userRoles: string[] | undefined): boolean;
|
|
22
26
|
function isUnchangedValue(a: unknown, b: unknown): boolean;
|
|
@@ -26,6 +30,7 @@ function resolveNavParentScreen(screens: ScreenDefinition[], detail: ScreenDefin
|
|
|
26
30
|
function resolveTranslationValue(value: TranslationValue, locale: string, params?: Readonly<Record<string, unknown>>): string; // Resolve a TranslationValue to display text: a plural entry picks its CLDR category via `Intl.PluralRules` from `params.count`, falling back to `other` when `count` is missing, non-finite, or `locale` is invalid.
|
|
27
31
|
function roundDerivedCellValue(value: number, target: DerivedCellRoundingTarget): number; // Rounds a derived cell's computed value to the precision its target sub-field declares — commercial rounding (round-half-away-from-zero, correct for signed minor-unit money).
|
|
28
32
|
function translationValueOtherText(value: TranslationValue): string; // Text-only reading of a TranslationValue — the CLDR `other` form for a plural entry, or the string as-is.
|
|
33
|
+
function withDerivedCells(row: unknown, derived: Readonly<Record<string, EmbeddedDerivedCellDef>>, targets: Readonly<Record<string, DerivedCellRoundingTarget>>): unknown; // Recomputes every derived cell of an embedded-list row from its raw values, overwriting whatever the client sent instead of merely checking it — the server is the authority for derived cells.
|
|
29
34
|
```
|
|
30
35
|
|
|
31
36
|
## Constants
|
|
@@ -331,12 +336,12 @@ type EditWriteFormSection = {
|
|
|
331
336
|
submitLabel?: string; // i18n-key for the submit button.
|
|
332
337
|
actions?: RowAction[]; // Rendered top-right in the section's Card title row — same navigate/drawer/writeHandler dispatch as `ProjectionDetailScreenDefinition.actions`, evaluated against the same record.
|
|
333
338
|
};
|
|
334
|
-
type EmbeddedDerivedCellDef = { // A cell computed from other cells of the same row.
|
|
335
|
-
|
|
336
|
-
from: string[];
|
|
339
|
+
type EmbeddedDerivedCellDef = ({ op: "multiply" | "sum" | "subtract" | "percent"; from: string[]; } | { op: "inRange"; from: string[]; inside: string; outside: string; }) & { // A cell computed from other cells of the same row.
|
|
340
|
+
when?: Exclude<FieldCondition, boolean>; // Row-level condition on a sibling sub-field.
|
|
337
341
|
};
|
|
338
342
|
type EmbeddedListEditSpec = {
|
|
339
343
|
readOnlyCells?: string[]; // Sub-fields shown as read-only cells; every other cell stays editable.
|
|
344
|
+
cardHiddenCells?: string[]; // Sub-fields that only steer a row (e.g. the kind that decides which controls appear) and are not shown on phone cards; the desktop table still shows them.
|
|
340
345
|
fixedRows?: boolean; // `true` keeps the rows as they are: no add, remove, duplicate, move or paste-appended rows.
|
|
341
346
|
};
|
|
342
347
|
type EntityEditAction = RowAction & {
|
|
@@ -507,7 +512,7 @@ type SecretsEditScreenDefinition = {
|
|
|
507
512
|
access?: AccessRule;
|
|
508
513
|
};
|
|
509
514
|
type SelectOptionTone = (typeof SELECT_OPTION_TONES)[number];
|
|
510
|
-
type TextFieldDef =
|
|
515
|
+
type TextFieldDef = PiiAndFieldState & {
|
|
511
516
|
type: "text";
|
|
512
517
|
description?: string; // Optional free-text semantics for the AI agent manifest — never rendered; the i18n label is what users see.
|
|
513
518
|
maxLength?: number;
|
|
@@ -499,7 +499,7 @@ interface FileFieldProps extends FieldBase {
|
|
|
499
499
|
entityType?: string; // Binds the upload to a specific entity field; the `/api/files` endpoint then validates `accept`/`maxSize` server-side against that field's definition.
|
|
500
500
|
fieldName?: string;
|
|
501
501
|
}
|
|
502
|
-
type FileRefTarget = Pick<FileUploadFormDataInput, "entityType" | "entityId" | "fieldName" | "actionForm">;
|
|
502
|
+
type FileRefTarget = Pick<FileUploadFormDataInput, "entityType" | "entityId" | "fieldName" | "subField" | "actionForm">;
|
|
503
503
|
type FloatingPanelGeometry = {
|
|
504
504
|
x: number;
|
|
505
505
|
y: number;
|
|
@@ -776,6 +776,7 @@ type InputProps =
|
|
|
776
776
|
hasError?: boolean;
|
|
777
777
|
radioVariant?: "list" | "card"; // `"card"` renders the radio group as bordered cards with label plus optional description per option and always uses the radio group, regardless of option count or `display`.
|
|
778
778
|
display?: "radio" | "dropdown"; // Requested presentation.
|
|
779
|
+
optionTones?: Readonly<Partial<Record<string, SelectOptionTone>>>; // Tone per option value; the selected segment of a radio presentation takes its colors.
|
|
779
780
|
}
|
|
780
781
|
| ({ kind: "combobox"; id: string; name: string; options: { value: string; label: string; }[]; disabled?: boolean; required?: boolean; hasError?: boolean; placeholder?: string; searchPlaceholder?: string; emptyText?: string; onSearchChange?: (q: string) => void; loading?: boolean; onCreate?: () => void; createLabel?: string; } & ({ multiple?: false; value: string; onChange: (v: string) => void; } | { multiple: true; value: string[]; onChange: (v: string[]) => void; }))
|
|
781
782
|
| {
|
|
@@ -13,6 +13,7 @@ function createRequestHelper(app: Hono, jwt: JwtHelper, options?: RequestHelperO
|
|
|
13
13
|
function createTestDb(arg?: string | CreateTestDbOptions): Promise<TestDb>;
|
|
14
14
|
function createTestRedis(opts?: CreateTestRedisOptions): Promise<TestRedis>;
|
|
15
15
|
function drainEventConsumers(stack: Pick<TestStack, "db" | "eventDispatcher">, consumerNames: [ string, ...string[] ], options?: { maxPasses?: number; }): Promise<void>;
|
|
16
|
+
function dropTestDb(baseUrl: string, dbName: string): Promise<void>;
|
|
16
17
|
function pushEntityProjectionTables(stack: DbHolder, registry: Registry): Promise<void>; // Push all implicit-projection tables — one per `r.entity()` — that the registry knows about.
|
|
17
18
|
function resetEventStore(stack: { db: unknown; eventDispatcher?: EventDispatcher; }, extraTables?: (unknown | string)[]): Promise<void>; // Wipes event store + framework-state + the given feature read-models in one TRUNCATE, then re-registers the event-consumer state rows.
|
|
18
19
|
function setupTestStack(options: TestStackOptions): Promise<TestStack>;
|