@cosmicdrift/kumiko-types 0.395.0 → 0.397.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.
@@ -9,6 +9,9 @@ import type { TenantDb } from "./tenant-db-types.ts";
9
9
  export type ParentVisibilityOption = {
10
10
  readonly entities: ReadonlyMap<string, EntityDefinition>;
11
11
  };
12
+ export type ParentVisibilityGateOption = {
13
+ readonly parentVisibility?: ParentVisibilityOption;
14
+ };
12
15
  export type PreSaveRunner = (changes: Record<string, unknown>, previous: Readonly<Record<string, unknown>>, isNew: boolean) => Promise<Record<string, unknown>>;
13
16
  export type StreamTenantOption = {
14
17
  readonly streamTenantId?: TenantId | undefined;
@@ -62,7 +65,7 @@ export type EventStoreExecutor<Row extends Record<string, unknown> = Record<stri
62
65
  id: EntityId;
63
66
  version?: number | undefined;
64
67
  changes: Record<string, unknown>;
65
- }, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption & {
68
+ }, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption & ParentVisibilityGateOption & {
66
69
  skipOptimisticLock?: boolean;
67
70
  skipUnchanged?: boolean;
68
71
  includeDeleted?: boolean;
@@ -71,13 +74,13 @@ export type EventStoreExecutor<Row extends Record<string, unknown> = Record<stri
71
74
  }) => Promise<WriteResult<SaveContext>>;
72
75
  delete: (payload: {
73
76
  id: EntityId;
74
- }, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption) => Promise<WriteResult<DeleteContext>>;
77
+ }, user: SessionUser, db: TenantDb, options?: StreamTenantOption & LockGuardOption & ParentVisibilityGateOption) => Promise<WriteResult<DeleteContext>>;
75
78
  forget: (payload: {
76
79
  id: EntityId;
77
- }, user: SessionUser, db: TenantDb, options?: StreamTenantOption) => Promise<WriteResult<DeleteContext>>;
80
+ }, user: SessionUser, db: TenantDb, options?: StreamTenantOption & ParentVisibilityGateOption) => Promise<WriteResult<DeleteContext>>;
78
81
  restore: (payload: {
79
82
  id: EntityId;
80
- }, user: SessionUser, db: TenantDb, options?: StreamTenantOption) => Promise<WriteResult<SaveContext>>;
83
+ }, user: SessionUser, db: TenantDb, options?: StreamTenantOption & ParentVisibilityGateOption) => Promise<WriteResult<SaveContext>>;
81
84
  list: (payload: {
82
85
  cursor?: string | undefined;
83
86
  limit?: number | undefined;
@@ -0,0 +1,27 @@
1
+ /// <reference types="temporal-polyfill/global" preserve="true" />
2
+ /** Display type a derived value formats as — drives the column's renderer
3
+ * choice in the view-model, parallel to FieldDefinition["type"]. Single-column
4
+ * types only: `money` is excluded because it needs a `<name>Currency`
5
+ * companion column a derived field has no place to put — use `number`/
6
+ * `decimal` plus a `{ format: "currency" }` column renderer instead. */
7
+ export type DerivedValueType = "text" | "number" | "decimal" | "boolean" | "date" | "timestamp";
8
+ /** Clock injected into `derive` — never read `Temporal.Now`/`Date` inside a
9
+ * derive body (no-date-api guard + testability). The list-query handler passes
10
+ * the read-time instant; unit tests pass a fixed one. */
11
+ export type DeriveContext = {
12
+ readonly asOf: Temporal.Instant;
13
+ };
14
+ export type DerivedFieldDef = {
15
+ readonly valueType: DerivedValueType;
16
+ /** Pure function of the stored row + clock. Returns the JSON-safe display
17
+ * value (e.g. integer minor units for a currency column, ISO string for a
18
+ * `date`). */
19
+ readonly derive: (row: Readonly<Record<string, unknown>>, ctx: DeriveContext) => unknown;
20
+ };
21
+ export type DerivedFieldsMap = Readonly<Record<string, DerivedFieldDef>>;
22
+ /** Client-facing projection of DerivedFieldDef — `derive` is server-only and
23
+ * not JSON-safe (would trip the output-walk guard), so the browser schema
24
+ * only ever carries `valueType`. A real `Pick`, not a same-shape cast: TS
25
+ * itself proves `derive` isn't there instead of a `{ valueType } as
26
+ * DerivedFieldDef` cast lying about a field that's actually missing. */
27
+ export type ClientDerivedFieldDef = Pick<DerivedFieldDef, "valueType">;
@@ -0,0 +1,20 @@
1
+ /// <reference types="temporal-polyfill/global" preserve="true" />
2
+ // --- Derived (computed) fields ---
3
+ //
4
+ // A derived field is read-time only: its value is computed from the stored row
5
+ // (and the clock) when an entityList query runs (676/2 — NOT detail; only the
6
+ // "list" case in entity-handlers.ts calls augmentDerivedFields), never
7
+ // persisted. It lives in
8
+ // `EntityDefinition.derivedFields` — deliberately NOT in `fields`, so it
9
+ // produces no DB column, never enters a write schema, and can't be the target
10
+ // of an entityEdit. A declarative `entityList` can name it like any column and
11
+ // the view-model renders the appended value.
12
+ //
13
+ // LIMIT: derived columns are DISPLAY ONLY. A declarative `entityList` loads its
14
+ // rows server-side and a column-header sort round-trips to the server, where
15
+ // `executor.list` sorts/filters/searches over real SQL columns — so a derived
16
+ // field (no column) silently no-ops. There is no client-side sort path. Need a
17
+ // derived value sortable/searchable? Materialize it as a stored field; it then
18
+ // rides the existing `searchable`/`sortable` machinery. Time-dependent values
19
+ // (as-of-today) can't be materialized without a daily re-index anyway.
20
+ export {};
package/dist/fields.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  /// <reference types="temporal-polyfill/global" preserve="true" />
2
2
  import type { VariantSpec } from "./derivatives-types.ts";
3
+ import type { DerivedFieldsMap } from "./fields-derived.ts";
3
4
  import type { EventUpcastCtx } from "./handlers.ts";
4
5
  import type { OwnershipMap } from "./ownership.ts";
5
6
  import type { EntityTenancy } from "./tenancy-brand.ts";
7
+ export type { ClientDerivedFieldDef, DeriveContext, DerivedFieldDef, DerivedFieldsMap, DerivedValueType, } from "./fields-derived.ts";
6
8
  export type FieldAccess = {
7
9
  readonly read?: OwnershipMap | readonly string[];
8
10
  readonly write?: OwnershipMap | readonly string[];
@@ -78,6 +80,9 @@ export type RetentionDef = {
78
80
  readonly strategy: "hardDelete" | "softDelete" | "anonymize" | "blockDelete";
79
81
  readonly reference?: string;
80
82
  };
83
+ /** Where rows without a value sort in a list, in both directions. Without it Postgres decides:
84
+ * last ascending, first descending. */
85
+ export type SortNulls = "first" | "last";
81
86
  export type TextFieldDef = {
82
87
  readonly type: "text";
83
88
  /** Optional free-text semantics for the AI agent manifest — never rendered;
@@ -93,6 +98,7 @@ export type TextFieldDef = {
93
98
  readonly required?: boolean;
94
99
  readonly searchable?: boolean;
95
100
  readonly sortable?: boolean;
101
+ readonly sortNulls?: SortNulls;
96
102
  /** Allows screen filters on this field.
97
103
  * The boot validator rejects filters with `filterable !== true`.
98
104
  * Default: false, analogous to `sortable`, and therefore opt-in. */
@@ -176,6 +182,7 @@ export type BooleanFieldDef = {
176
182
  readonly description?: string;
177
183
  readonly required?: boolean;
178
184
  readonly sortable?: boolean;
185
+ readonly sortNulls?: SortNulls;
179
186
  readonly filterable?: boolean;
180
187
  readonly sensitive?: boolean;
181
188
  readonly default?: boolean;
@@ -217,6 +224,7 @@ export type SelectFieldDef<TOptions extends readonly string[] = readonly string[
217
224
  readonly options: TOptions;
218
225
  readonly required?: boolean;
219
226
  readonly sortable?: boolean;
227
+ readonly sortNulls?: SortNulls;
220
228
  readonly filterable?: boolean;
221
229
  readonly sensitive?: boolean;
222
230
  readonly default?: TOptions[number];
@@ -297,6 +305,7 @@ export type NumberFieldDef = {
297
305
  readonly description?: string;
298
306
  readonly required?: boolean;
299
307
  readonly sortable?: boolean;
308
+ readonly sortNulls?: SortNulls;
300
309
  readonly filterable?: boolean;
301
310
  readonly sensitive?: boolean;
302
311
  readonly default?: number;
@@ -333,6 +342,7 @@ export type BigIntFieldDef = {
333
342
  readonly description?: string;
334
343
  readonly required?: boolean;
335
344
  readonly sortable?: boolean;
345
+ readonly sortNulls?: SortNulls;
336
346
  readonly filterable?: boolean;
337
347
  readonly sensitive?: boolean;
338
348
  readonly default?: number;
@@ -357,6 +367,7 @@ export type DecimalFieldDef = {
357
367
  readonly scale: number;
358
368
  readonly required?: boolean;
359
369
  readonly sortable?: boolean;
370
+ readonly sortNulls?: SortNulls;
360
371
  readonly filterable?: boolean;
361
372
  readonly sensitive?: boolean;
362
373
  readonly default?: number;
@@ -385,6 +396,7 @@ export type MoneyFieldDef = {
385
396
  readonly description?: string;
386
397
  readonly required?: boolean;
387
398
  readonly sortable?: boolean;
399
+ readonly sortNulls?: SortNulls;
388
400
  readonly filterable?: boolean;
389
401
  readonly sensitive?: boolean;
390
402
  readonly access?: FieldAccess;
@@ -421,6 +433,7 @@ export type ReferenceFieldDef = {
421
433
  * boot validator rejects it otherwise, since ordering by a UUID produces a
422
434
  * sequence that looks meaningful to the user but is arbitrary. */
423
435
  readonly sortable?: true;
436
+ readonly sortNulls?: SortNulls;
424
437
  /** Fill the picker from this query handler instead of the referenced
425
438
  * entity's own list handler — for targets whose identity is
426
439
  * composed from joined rows, where no single column of the referenced
@@ -537,6 +550,9 @@ export type EmbeddedFieldDef = {
537
550
  export type JsonbFieldDef = {
538
551
  readonly type: "jsonb";
539
552
  readonly description?: string;
553
+ /** `"array"`: the field holds a JSON array, so a row that never wrote it reads `[]` and writes
554
+ * take an array. Default `"object"` (`{}`, writes take a plain object). */
555
+ readonly emptyValue?: JsonbEmptyValue;
540
556
  readonly sensitive?: boolean;
541
557
  /** A later run reads this field's value as an instruction — prompt, rule,
542
558
  * template. Every create/update whose payload writes it requires agent.risk
@@ -545,11 +561,13 @@ export type JsonbFieldDef = {
545
561
  readonly readAsInstruction?: boolean;
546
562
  readonly access?: FieldAccess;
547
563
  } & ResolvedPiiFlags;
564
+ export type JsonbEmptyValue = "object" | "array";
548
565
  export type DateFieldDef = {
549
566
  readonly type: "date";
550
567
  readonly description?: string;
551
568
  readonly required?: boolean;
552
569
  readonly sortable?: boolean;
570
+ readonly sortNulls?: SortNulls;
553
571
  readonly filterable?: boolean;
554
572
  readonly sensitive?: boolean;
555
573
  readonly access?: FieldAccess;
@@ -567,6 +585,7 @@ export type TimestampFieldDef = {
567
585
  readonly description?: string;
568
586
  readonly required?: boolean;
569
587
  readonly sortable?: boolean;
588
+ readonly sortNulls?: SortNulls;
570
589
  readonly filterable?: boolean;
571
590
  readonly sensitive?: boolean;
572
591
  readonly access?: FieldAccess;
@@ -599,6 +618,7 @@ export type LocatedTimestampFieldDef = {
599
618
  readonly description?: string;
600
619
  readonly required?: boolean;
601
620
  readonly sortable?: boolean;
621
+ readonly sortNulls?: SortNulls;
602
622
  readonly filterable?: boolean;
603
623
  readonly sensitive?: boolean;
604
624
  readonly access?: FieldAccess;
@@ -675,32 +695,6 @@ export type FormFieldDefinition = Exclude<FieldDefinition, MoneyFieldDef> | (Mon
675
695
  });
676
696
  export type AnyFileFieldDef = FileFieldDef | ImageFieldDef | FilesFieldDef | ImagesFieldDef;
677
697
  export declare const NO_WIDGET_FIELD_TYPES: readonly FieldDefinition["type"][];
678
- /** Display type a derived value formats as — drives the column's renderer
679
- * choice in the view-model, parallel to FieldDefinition["type"]. Single-column
680
- * types only: `money` is excluded because it needs a `<name>Currency`
681
- * companion column a derived field has no place to put — use `number`/
682
- * `decimal` plus a `{ format: "currency" }` column renderer instead. */
683
- export type DerivedValueType = "text" | "number" | "decimal" | "boolean" | "date" | "timestamp";
684
- /** Clock injected into `derive` — never read `Temporal.Now`/`Date` inside a
685
- * derive body (no-date-api guard + testability). The list-query handler passes
686
- * the read-time instant; unit tests pass a fixed one. */
687
- export type DeriveContext = {
688
- readonly asOf: Temporal.Instant;
689
- };
690
- export type DerivedFieldDef = {
691
- readonly valueType: DerivedValueType;
692
- /** Pure function of the stored row + clock. Returns the JSON-safe display
693
- * value (e.g. integer minor units for a currency column, ISO string for a
694
- * `date`). */
695
- readonly derive: (row: Readonly<Record<string, unknown>>, ctx: DeriveContext) => unknown;
696
- };
697
- export type DerivedFieldsMap = Readonly<Record<string, DerivedFieldDef>>;
698
- /** Client-facing projection of DerivedFieldDef — `derive` is server-only and
699
- * not JSON-safe (would trip the output-walk guard), so the browser schema
700
- * only ever carries `valueType`. A real `Pick`, not a same-shape cast: TS
701
- * itself proves `derive` isn't there instead of a `{ valueType } as
702
- * DerivedFieldDef` cast lying about a field that's actually missing. */
703
- export type ClientDerivedFieldDef = Pick<DerivedFieldDef, "valueType">;
704
698
  export type TransitionMap = Readonly<Record<string, readonly string[]>>;
705
699
  /** Composite index on an entity. Columns are referenced by field name
706
700
  * (camelCase). `buildEntityTable` maps them to snake_case column names and
@@ -867,4 +861,3 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTe
867
861
  * privilegedRoles do not bypass it, the uploader check stays. */
868
862
  readonly attachedFileAccess?: "record-only";
869
863
  };
870
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.395.0",
3
+ "version": "0.397.0",
4
4
  "description": "Framework-Type-Definitions für Kumiko — FeatureDefinition, BootCheck-Types und die reinen Engine-Types. Erlaubt Downstream-Konsumenten, gegen die Type-Contracts zu bauen, ohne das ganze Framework-Package zu importieren. Enthaelt keine identitaets-sensitiven Runtime-Werte mehr (Error-Klassen leben seit #1629 in kumiko-framework, Brand-Symbole nutzen Symbol.for) und ist deshalb eine plain dependency, keine peerDependency.",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",