@cosmicdrift/kumiko-types 0.386.0 → 0.387.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/LICENSE CHANGED
@@ -1,57 +1,21 @@
1
- Business Source License 1.1
2
-
3
- Parameters
4
-
5
- Licensor: Marc Frost
6
-
7
- Licensed Work: @cosmicdrift/kumiko-types
8
- The Licensed Work is © 2026 Marc Frost.
9
-
10
- Additional Use Grant:
11
- You may use the Licensed Work in production for any purpose, including
12
- commercially, EXCEPT for the Restricted Use.
13
-
14
- "Restricted Use" is defined as using the Licensed Work to provide a platform
15
- or service to third parties that allows them to host, deploy, or run their
16
- own applications built with the Licensed Work. This includes, but is not
17
- limited to: managed hosting services, software-as-a-service (SaaS) platforms,
18
- platform-as-a-service (PaaS), developer platforms, or any multi-tenant
19
- managed offering of the Licensed Work.
20
-
21
- This restriction does not apply to the Licensor, any entity controlled by,
22
- controlling, or under common control with the Licensor ("Affiliates"), or
23
- contractors acting on their behalf. The Licensor remains free to use the
24
- Licensed Work for any purpose, including for the operation of kumiko.rocks.
25
-
26
- Change Date: 2030-05-05
27
- Change License: Apache License, Version 2.0
28
-
29
-
30
- Terms
31
-
32
- The Licensor hereby grants you the right to copy, modify, create derivative works,
33
- redistribute, and make non-production use of the Licensed Work. The Licensor may
34
- make an Additional Use Grant, above, permitting limited production use.
35
-
36
- Effective on the Change Date, or the fourth anniversary of the first publicly
37
- available distribution of the Licensed Work under this License, whichever comes
38
- first, this License will convert to the Change License.
39
-
40
- This Business Source License governs use of the Licensed Work in all cases, except
41
- as to any use that is explicitly granted in the Additional Use Grant above or
42
- under the Change License after the Change Date.
43
-
44
- If your use of the Licensed Work does not comply with the requirements of this
45
- License, you must cease use of the Licensed Work immediately.
46
-
47
- All copies of the Licensed Work, and all derivative works thereof, must include
48
- this License.
49
-
50
- This License does not grant you any right, title, or interest in any trademark,
51
- logo, or branding of the Licensor, except as required to comply with this License.
52
-
53
- TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
54
- “AS IS” BASIS. LICENSOR DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
55
- WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
56
- TITLE, AND NON-INFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY DAMAGES
57
- ARISING OUT OF OR RELATED TO THIS LICENSE OR THE USE OF THE LICENSED WORK.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marc Frost
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @cosmicdrift/kumiko-types
2
2
 
3
- [![License: BUSL-1.1](https://img.shields.io/badge/License-BUSL--1.1-blue.svg)](../../LICENSE)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](../../LICENSE)
4
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
5
5
 
6
6
  Framework type definitions for Kumiko — `FeatureDefinition`, boot-check types,
package/dist/config.d.ts CHANGED
@@ -50,7 +50,7 @@ export type ConfigKeyDefinition<T extends ConfigKeyType = ConfigKeyType> = {
50
50
  * encrypted under the DEK of the scope actually written to (tenant-row
51
51
  * → tenant subject, user-row → user subject). Only on `type: "text"`,
52
52
  * `scope !== "system"` (no subject there), and mutually exclusive with
53
- * `encrypted` (kumiko-platform#231/#459). */
53
+ * `encrypted`. */
54
54
  readonly piiEncrypted?: boolean;
55
55
  readonly options?: readonly string[];
56
56
  /** Only on `type: "select"`: loads the options from this query (QN), contract
@@ -2,6 +2,6 @@
2
2
  // Plain-data types for EntityTableMeta — split from the runtime
3
3
  // (deriveEntityTableMeta, resolveTableName, defineUnmanagedTable) in
4
4
  // entity-table-meta.ts. Prep step for the types-only package extraction
5
- // (#1283) — this file must have ONLY `import type`, no value imports
5
+ // — this file must have ONLY `import type`, no value imports
6
6
  // (crypto/DB deps).
7
7
  export {};
@@ -64,7 +64,7 @@ export type EventStoreExecutor<Row extends Record<string, unknown> = Record<stri
64
64
  readonly value: unknown;
65
65
  }> | undefined;
66
66
  }, user: SessionUser, db: TenantDb,
67
- /** Tier 2.7e audit fix: per-call SearchAdapter override. When the
67
+ /** Per-call SearchAdapter override. When the
68
68
  * executor didn't get a SearchAdapter via Options at build time
69
69
  * (defaultEntityQueryHandler path), the caller (handler) can pass
70
70
  * one from ctx.searchAdapter here at runtime.
@@ -3,5 +3,5 @@ export function isPersonalDataGated(origin) {
3
3
  }
4
4
  // Stamped when an append runs outside any attributed scope (seed scripts,
5
5
  // tests, direct event-store use). A distinct value, not an omission —
6
- // omitted would be indistinguishable from a pre-#3043 row.
6
+ // omitted would be indistinguishable from an earlier row.
7
7
  export const UNATTRIBUTED_ORIGIN = "unknown";
@@ -1,18 +1,18 @@
1
- // Cross-Feature Compile-Time-Type-Map.
1
+ // Cross-Feature Compile-Time Type Map.
2
2
  //
3
- // Zweck: ctx.appendEvent / ctx.queryProjection / dispatcher.write gegen ein
4
- // statisch bekanntes Schema-Bild prüfen, statt erst zur Boot- oder Runtime
5
- // (zod-validate) zu scheitern. Designer/AI-Layer profitiert dadurch sofort:
6
- // Autocomplete kennt alle Event-Typen aller geladenen Features, payload-
7
- // Shape-Mismatches werden im Editor angezeigt, nicht erst beim Boot.
3
+ // Purpose: check ctx.appendEvent / ctx.queryProjection / dispatcher.write against a
4
+ // statically known schema image instead of failing only at boot or runtime
5
+ // (zod-validate). The Designer/AI layer benefits immediately: autocomplete knows all
6
+ // event types from all loaded features, and payload-shape mismatches appear in the
7
+ // editor rather than only at boot.
8
8
  //
9
- // Befüllung erfolgt per Feature über `declare module "@cosmicdrift/kumiko-framework/engine"`
10
- // — entweder hand-geschrieben (für stabile Frameworks-Internals) oder vom
11
- // Codegen-Skript erzeugt (für apps/bundled-features). Empty defaults sind
12
- // kein Bug: ein Feature ohne Augmentation ist runtime-pluggable und nutzt
13
- // die Fallback-Overload mit `unknown` payload.
9
+ // Each feature populates the map through `declare module "@cosmicdrift/kumiko-framework/engine"`
10
+ // — either manually (for stable framework internals) or through the code-generation
11
+ // script (for apps/bundled-features). Empty defaults are not a bug: a feature without
12
+ // augmentation is runtime-pluggable and uses the fallback overload with `unknown`
13
+ // payload.
14
14
  //
15
- // Pattern für hand-geschriebene Augmentation am File-Top:
15
+ // Pattern for a manually written augmentation at the top of the file:
16
16
  //
17
17
  // declare module "@cosmicdrift/kumiko-framework/engine" {
18
18
  // interface KumikoEventTypeMap {
package/dist/feature.d.ts CHANGED
@@ -79,7 +79,7 @@ export type StoreTableOptions = {
79
79
  /** Direct-write stores skip the executor, so the executor's PII
80
80
  * encryption never runs for them — a feature whose meta carries
81
81
  * piiSubjectFields must encrypt those fields itself before every
82
- * insert/update and declare that here, or boot fails (#820). */
82
+ * insert/update and declare that here, or boot fails. */
83
83
  readonly piiEncryptedOnWrite?: true;
84
84
  };
85
85
  /** Per-feature store-table registration. `meta` is the `EntityTableMeta`
@@ -152,17 +152,17 @@ export type FeatureDefinition = {
152
152
  readonly extensionSelectors: readonly ExtensionSelectorDef[];
153
153
  /**
154
154
  * Cross-feature API names this feature exposes via `r.exposesApi(name)`.
155
- * Pure Marker-Deklaration — die echte Implementation wird als
156
- * Query-/Write-Handler unter dem QN-Pattern registriert (z.B.
157
- * `compliance-profiles:query:effective-profile`). Boot-Validator prüft
158
- * dass jedes `r.usesApi(name)` einen passenden Exposer hier findet —
159
- * Tippfehler oder Drop-Refactorings werden zu Boot-Fail statt Runtime-Crash.
155
+ * Pure marker declaration — the implementation is registered separately as
156
+ * a query or write handler under the QN pattern (for example,
157
+ * `compliance-profiles:query:effective-profile`). The boot validator checks
158
+ * that every `r.usesApi(name)` finds a matching exposer, so typos or dropped
159
+ * refactors fail at boot instead of causing a runtime crash.
160
160
  */
161
161
  readonly exposedApis: ReadonlySet<string>;
162
162
  /**
163
- * Cross-feature API names this feature calls. Pflicht-Boot-Check:
164
- * jeder Eintrag muss in `exposedApis` irgendeines Features auftauchen
165
- * UND das Provider-Feature muss in requires/optionalRequires sein.
163
+ * Cross-feature API names this feature calls. The boot check requires every
164
+ * entry to appear in some feature's `exposedApis` and the provider feature
165
+ * to be listed in requires/optionalRequires.
166
166
  */
167
167
  readonly usedApis: ReadonlySet<string>;
168
168
  /**
@@ -406,30 +406,30 @@ export type FeatureRegistrar<TFeature extends string = string> = {
406
406
  readonly panels?: readonly ExtensionSelectorPanel[];
407
407
  }): void;
408
408
  /**
409
- * Marker-Deklaration: dieses Feature stellt eine Cross-Feature-API
410
- * unter dem genannten Namen bereit. Die eigentliche Implementation
411
- * wird separat als Query- oder Write-Handler unter dem QN-Pattern
412
- * registriert; `r.exposesApi` ist reine Boot-Check-Surface.
409
+ * Marker declaration: this feature provides a cross-feature API under the
410
+ * given name. The implementation is registered separately as a query or
411
+ * write handler under the QN pattern; `r.exposesApi` is only a boot-check
412
+ * surface.
413
413
  *
414
- * Boot-Validator prüft, dass jedes `r.usesApi(name)` einen passenden
415
- * Exposer findet, dass das Exposer-Feature in requires/optionalRequires
416
- * gelisted ist und dass kein API-Name doppelt exposed wird.
414
+ * The boot validator checks that every `r.usesApi(name)` finds a matching
415
+ * exposer, that the exposer feature is listed in requires/optionalRequires,
416
+ * and that no API name is exposed more than once.
417
417
  *
418
418
  * ```ts
419
419
  * defineFeature("compliance-profiles", (r) => {
420
420
  * r.exposesApi("compliance.forTenant");
421
421
  * r.queryHandler({
422
422
  * name: "compliance:query:for-tenant",
423
- * // ... echte Implementation
423
+ * // ... actual implementation
424
424
  * });
425
425
  * });
426
426
  * ```
427
427
  */
428
428
  exposesApi(apiName: string): void;
429
429
  /**
430
- * Declares that this feature calls a cross-feature API. Boot-Validator
431
- * checkt dass irgendein anderes Feature `r.exposesApi(apiName)` macht
432
- * und dass dieses Feature `r.requires/optionalRequires` darauf hat.
430
+ * Declares that this feature calls a cross-feature API. The boot validator
431
+ * checks that another feature provides `r.exposesApi(apiName)` and that
432
+ * this feature lists it in `r.requires/optionalRequires`.
433
433
  *
434
434
  * ```ts
435
435
  * defineFeature("user-data-rights", (r) => {
@@ -447,9 +447,9 @@ export type FeatureRegistrar<TFeature extends string = string> = {
447
447
  * Use for cross-feature invariants that `r.requires` can't express
448
448
  * because they're conditional (only fail when the feature has a
449
449
  * specific shape), e.g. "if this feature has PII entities, some
450
- * user-data-hook feature must be mounted" (the prompt-store trap,
451
- * kumiko-enterprise#229 — a UserData feature was written but never
452
- * mounted, and nothing caught it at boot):
450
+ * user-data-hook feature must be mounted" (the prompt-store trap —
451
+ * a UserData feature was written but never mounted, and nothing caught
452
+ * it at boot):
453
453
  *
454
454
  * ```ts
455
455
  * defineFeature("prompt-store", (r) => {
package/dist/fields.d.ts CHANGED
@@ -17,11 +17,10 @@ export type ResolvedPiiFlags = {
17
17
  readonly recordOwned?: true;
18
18
  readonly anonymize?: () => unknown | Promise<unknown>;
19
19
  readonly allowPlaintext?: string;
20
- /** Equality-Lookups (fetchOne/filter eq) bleiben trotz Verschluesselung
21
- * moeglich: generierte `<snake>_bidx`-Spalte traegt einen HMAC-Blind-
22
- * Index, Query-Compiler matchen `(col = $1 OR col_bidx = $2)`. Nur auf
23
- * text-Feldern MIT Subject-Annotation erlaubt (Boot-Validator).
24
- * Siehe docs/plans/datenschutz/blind-index.md (kumiko-framework#818). */
20
+ /** Equality lookups (`fetchOne`/`filter eq`) remain possible despite encryption:
21
+ * the generated `<snake>_bidx` column carries an HMAC blind index, and the
22
+ * query compiler matches `(col = $1 OR col_bidx = $2)`. Allowed only on text
23
+ * fields with subject annotations, as enforced by the boot validator. */
25
24
  readonly lookupable?: true;
26
25
  /** Foreign key into the `user` entity (authorId, assigneeId, ...) with no
27
26
  * annotated content of its own. Triggers no encryption — feeds only the
@@ -94,9 +93,9 @@ export type TextFieldDef = {
94
93
  readonly required?: boolean;
95
94
  readonly searchable?: boolean;
96
95
  readonly sortable?: boolean;
97
- /** Author erlaubt Screen-Filter auf diesem Feld (Tier 2.7c).
98
- * Boot-Validator weist Filter mit `filterable !== true` zurück.
99
- * Default: false — analog zu `sortable`, opt-in. */
96
+ /** Allows screen filters on this field.
97
+ * The boot validator rejects filters with `filterable !== true`.
98
+ * Default: false, analogous to `sortable`, and therefore opt-in. */
100
99
  readonly filterable?: boolean;
101
100
  readonly encrypted?: boolean;
102
101
  readonly sensitive?: boolean;
@@ -125,40 +124,38 @@ export type TextFieldDef = {
125
124
  readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
126
125
  readonly default?: string;
127
126
  readonly access?: FieldAccess;
128
- /** Mehrzeiliger Text — DefaultInput rendert dann ein <textarea> statt
129
- * <input type="text">. `true` = Default 4 Zeilen, `{ rows: N }` =
130
- * explizite Höhe. Search/sort/encrypt verhalten sich unverändert
131
- * identisch zu single-line — nur die Render-Surface wechselt. */
127
+ /** Multiline text causes DefaultInput to render a <textarea> instead of
128
+ * <input type="text">. `true` uses four rows by default; `{ rows: N }`
129
+ * sets an explicit height. Search, sort, and encryption remain unchanged;
130
+ * only the rendering surface differs. */
132
131
  readonly multiline?: boolean | {
133
132
  readonly rows?: number;
134
133
  };
135
134
  } & ResolvedPiiFlags;
136
135
  /**
137
- * Long-form text content — source-code, markdown, blog-posts, email-
138
- * templates, anything that can be megabytes large. Bewusst MINIMALE
139
- * Surface gegen `text`:
136
+ * Long-form text content — source code, markdown, blog posts, email
137
+ * templates, and anything that can be megabytes large. Its surface is
138
+ * intentionally minimal compared with `text`:
140
139
  *
141
- * - **Kein `sortable`**: ORDER BY auf 100 KB-Strings kostet I/O ohne
142
- * sinnvolles UX-Outcome (lex-Sortierung von Code ist Nonsense).
143
- * - **Kein `searchable`**: ILIKE/Substring-Suche auf langen Texten
144
- * skaliert nicht. Wer wirklich Volltextsuche will, nimmt den
145
- * SearchAdapter (Meilisearch) — der hat eine eigene Pipeline mit
146
- * Tokenizer + Index, NICHT diesen field-flag.
147
- * - **Kein `filterable`**: WHERE auf langen Strings same Story wie
148
- * sortable.
149
- * - **Kein `format`**: email/url/phone sind kurz definierte Inputs,
150
- * longText ist per Definition unstrukturiert.
140
+ * - **No `sortable`**: ordering 100 KB strings costs I/O without useful UX;
141
+ * lexicographic ordering of code is not meaningful.
142
+ * - **No `searchable`**: ILIKE/substring searches do not scale on long text.
143
+ * Full-text search should use the SearchAdapter (Meilisearch), which has
144
+ * its own tokenizer and index rather than this field flag.
145
+ * - **No `filterable`**: filtering long strings has the same issue as sorting.
146
+ * - **No `format`**: email/url/phone are short, structured inputs, whereas
147
+ * longText is unstructured by definition.
151
148
  *
152
- * Type-level enforcement statt convention: wer sortable/searchable
153
- * braucht, nimmt `text` (mit den entsprechenden Skalierungs-Trade-offs).
154
- * DB-mapping ist identisch zu text (Postgres `text` ist unbounded).
149
+ * Type-level enforcement means callers who need sorting or searching must use
150
+ * `text` and accept its scaling trade-offs. Database mapping is identical to
151
+ * `text` (Postgres `text` is unbounded).
155
152
  */
156
153
  export type LongTextFieldDef = {
157
154
  readonly type: "longText";
158
155
  readonly description?: string;
159
- /** Optionale soft-Cap. Default unbounded (= Postgres-text-limit, 1 GB).
160
- * Nützlich für defensive Caps wie 1 MB damit ein verirrter Browser-
161
- * Paste nicht die DB sprengt. */
156
+ /** Optional soft cap. Default is unbounded (= Postgres text limit, 1 GB).
157
+ * Useful for defensive limits such as 1 MB so an accidental browser paste
158
+ * does not overwhelm the database. */
162
159
  readonly maxLength?: number;
163
160
  readonly required?: boolean;
164
161
  readonly encrypted?: boolean;
@@ -265,7 +262,7 @@ export type MultiSelectFieldDef<TOptions extends readonly string[] = readonly st
265
262
  readonly required?: boolean;
266
263
  readonly filterable?: boolean;
267
264
  readonly sensitive?: boolean;
268
- /** Default-Auswahl. Jeder Eintrag muss in `options` sein (Boot-Validator). */
265
+ /** Default selection. Every entry must occur in `options` (boot validator). */
269
266
  readonly default?: readonly TOptions[number][];
270
267
  readonly access?: FieldAccess;
271
268
  /** Renderer metadata: `"checkboxes"` renders every option as a visible
@@ -321,16 +318,15 @@ export type NumberFieldDef = {
321
318
  readonly grouping?: boolean;
322
319
  } & ResolvedPiiFlags;
323
320
  /**
324
- * 64-bit-Integer-Spalte fuer Audit-Counter, Byte-Sizes, Event-IDs und
325
- * andere Werte die >2^31 (~2.1 Mrd) wandern koennen. Storage als
326
- * Postgres `bigint`, JS-Round-trip als `number` (mode:"number" — sicher
327
- * bis 2^53 ≈ 9 PB, JSON-serialisierbar). Wer >2^53 braucht (rare),
328
- * nutzt einen `text`-Field mit eigenem Codec.
321
+ * 64-bit integer field for audit counters, byte sizes, event IDs, and other
322
+ * values that can exceed 2^31 (~2.1 billion). Stored as Postgres `bigint`,
323
+ * with a JavaScript `number` round trip (`mode:"number"`, safe through 2^53
324
+ * ≈ 9 PB and JSON-serializable). Values above 2^53 should use a `text` field
325
+ * with a custom codec.
329
326
  *
330
- * Vorrang vor `NumberFieldDef`-(integer 32-bit-Cap, ~2.1 GB) immer dann
331
- * wenn der Wert physisch ueber dieses Limit klettern kann: Bytes,
332
- * Events, Counters in High-Throughput-Apps, Cumulative-Sums. Money
333
- * hat dafuer den eigenen `MoneyFieldDef` (mit Currency-Spalte).
327
+ * Prefer this over `NumberFieldDef`'s 32-bit integer cap whenever a value can
328
+ * physically exceed that limit: bytes, events, high-throughput counters, and
329
+ * cumulative sums. Money has its own `MoneyFieldDef` with a currency column.
334
330
  */
335
331
  export type BigIntFieldDef = {
336
332
  readonly type: "bigInt";
@@ -368,9 +364,9 @@ export type DecimalFieldDef = {
368
364
  } & ResolvedPiiFlags;
369
365
  /** Where a money field's currency comes from. `"tenant"` is the
370
366
  * tenant-settings bundle's per-tenant currency (`tenant-settings:config:
371
- * currency`, kumiko-framework#2933); `"literal"` names a fixed ISO code and
372
- * is checked against the app's `currencies` list at boot
373
- * (kumiko-framework#2839). Discriminated union so a `{ kind: "field", of }`
367
+ * currency`); `"literal"` names a fixed ISO code and
368
+ * is checked against the app's `currencies` list at boot.
369
+ * Discriminated union so a `{ kind: "field", of }`
374
370
  * variant (amount and currency-picker side by side) can still be added
375
371
  * without a breaking change, once a multi-currency form actually exists. */
376
372
  export type MoneyCurrencySource = {
@@ -382,7 +378,7 @@ export type MoneyCurrencySource = {
382
378
  /** Qualified config key a `{ kind: "tenant" }` MoneyCurrencySource resolves
383
379
  * against — the tenant-settings bundle's per-tenant currency. Lives here
384
380
  * (client-safe) rather than framework/engine so renderer code can read it
385
- * without a runtime-only import (kumiko-framework#2937). */
381
+ * without a runtime-only import. */
386
382
  export declare const TENANT_CURRENCY_CONFIG_KEY = "tenant-settings:config:currency";
387
383
  export type MoneyFieldDef = {
388
384
  readonly type: "money";
@@ -406,27 +402,27 @@ export type ReferenceFieldDef = {
406
402
  readonly filterable?: boolean;
407
403
  readonly sensitive?: boolean;
408
404
  readonly access?: FieldAccess;
409
- /** Welches Feld der referenced Entity als Display-Label im
410
- * Select-Dropdown erscheint. Default: "id". Boot-Validator pinst
411
- * dass das Feld auf der referenced Entity existiert. */
405
+ /** Field of the referenced entity displayed as the Select dropdown label.
406
+ * Default: "id". The boot validator ensures that the field exists on the
407
+ * referenced entity. */
412
408
  readonly labelField?: string;
413
- /** Multi-Reference (Tier 2.7e-Multi): Wert ist ein Array von UUIDs
414
- * statt single UUID. Storage als jsonb-Array<uuid>. UI rendert
415
- * Multi-Select-Combobox mit Tag-Anzeige der gewählten Items. */
409
+ /** Multi-reference: the value is an array of UUIDs rather than a single UUID.
410
+ * Stored as a jsonb array of UUIDs; the UI renders a multi-select combobox
411
+ * with tags for the selected items. */
416
412
  readonly multiple?: boolean;
417
413
  /** Include a row in this entity's result set on a text-search hit against
418
414
  * the referenced entity's `labelField`, instead of only matching the UUID
419
- * column itself (fw#2660). Requires an explicit `labelField` — the boot
415
+ * column itself. Requires an explicit `labelField` — the boot
420
416
  * validator rejects it otherwise, since the default "id" is a UUID column
421
417
  * and ILIKE against it would crash at runtime. */
422
418
  readonly searchable?: true;
423
419
  /** Order a list by the referenced entity's `labelField` instead of by the raw
424
- * UUID column (fw#2741). Requires an explicit, non-"id" `labelField` — the
420
+ * UUID column. Requires an explicit, non-"id" `labelField` — the
425
421
  * boot validator rejects it otherwise, since ordering by a UUID produces a
426
422
  * sequence that looks meaningful to the user but is arbitrary. */
427
423
  readonly sortable?: true;
428
424
  /** Fill the picker from this query handler instead of the referenced
429
- * entity's own list handler (fw#2780) — for targets whose identity is
425
+ * entity's own list handler — for targets whose identity is
430
426
  * composed from joined rows, where no single column of the referenced
431
427
  * entity is a readable label. Fully-qualified QN
432
428
  * ("<feature>:query:<short>"), boot-validated against the registered
@@ -481,7 +477,7 @@ export type EmbeddedSubFieldDef = (EmbeddedSubFieldBase & {
481
477
  readonly type: "reference";
482
478
  readonly entity: string;
483
479
  readonly labelField?: string;
484
- /** Same contract as `ReferenceFieldDef.optionsQuery` (fw#2780). */
480
+ /** Same contract as `ReferenceFieldDef.optionsQuery`. */
485
481
  readonly optionsQuery?: string;
486
482
  });
487
483
  /** A cell computed from other cells of the same row. `from` names the
@@ -535,7 +531,7 @@ export type EmbeddedFieldDef = {
535
531
  * field's amount. Validated both client- and server-side (schema-builder
536
532
  * superRefine). Only meaningful with `multiple: true`. Compares against
537
533
  * rounded `derived` cells, i.e. "sum-of-rounded" not "round-of-sum" —
538
- * see kumiko-framework#1866. */
534
+ * see the embedded-list totals-matching behavior. */
539
535
  readonly totalsMatch?: Readonly<Record<string, string>>;
540
536
  } & ResolvedPiiFlags;
541
537
  export type JsonbFieldDef = {
@@ -557,13 +553,13 @@ export type DateFieldDef = {
557
553
  readonly filterable?: boolean;
558
554
  readonly sensitive?: boolean;
559
555
  readonly access?: FieldAccess;
560
- /** Erlaubte Datumsgrenzen als ISO `yyyy-mm-dd` (z.B. Geburtsdatum nicht
561
- * in der Zukunft: `max` = heute). Begrenzt den Picker und wird vom
562
- * Zod-Schema beim Write durchgesetzt. */
556
+ /** Permitted date bounds as ISO `yyyy-mm-dd` (for example, a birth date
557
+ * cannot be in the future when `max` is today). The bounds constrain the
558
+ * picker and are enforced by the Zod schema on write. */
563
559
  readonly min?: string;
564
560
  readonly max?: string;
565
- /** Format/Locale-Override für Anzeige und Eingabe-Parsing (z.B.
566
- * "de-DE"). Default = App-Locale. */
561
+ /** Format/locale override for display and input parsing (for example,
562
+ * "de-DE"). Default: app locale. */
567
563
  readonly locale?: string;
568
564
  } & ResolvedPiiFlags;
569
565
  export type TimestampFieldDef = {
@@ -575,21 +571,20 @@ export type TimestampFieldDef = {
575
571
  readonly sensitive?: boolean;
576
572
  readonly access?: FieldAccess;
577
573
  /**
578
- * Marker: dieses Timestamp-Feld ist Wall-Clock-Zeit an einem Ort.
579
- * Wert ist der Name des begleitenden tz-Felds (IANA-Zone).
574
+ * Marker: this timestamp field represents wall-clock time at a location.
575
+ * The value is the name of the accompanying tz field (IANA zone).
580
576
  *
581
- * Beispiel: manuelles Pair
577
+ * Example: manual pair
582
578
  * { pickupAt: { type: "timestamp", locatedBy: "pickupTz" }, pickupTz: { type: "tz" } }
583
- * — bevorzuge stattdessen `createLocatedTimestampField()`.
579
+ * — prefer `createLocatedTimestampField()` instead.
584
580
  */
585
581
  readonly locatedBy?: string;
586
- /** Erlaubte Grenzen als ISO-Datetime. Begrenzt den Picker auf
587
- * Tages-Granularität; die exakte Uhrzeit-Grenze setzt das Zod-Schema
588
- * beim Write durch. */
582
+ /** Permitted bounds as ISO datetime values. The picker is limited to day
583
+ * granularity; the Zod schema enforces the exact time bound on write. */
589
584
  readonly min?: string;
590
585
  readonly max?: string;
591
- /** Format/Locale-Override für Anzeige und Eingabe-Parsing. Default =
592
- * App-Locale. */
586
+ /** Format/locale override for display and input parsing. Default: app
587
+ * locale. */
593
588
  readonly locale?: string;
594
589
  } & ResolvedPiiFlags;
595
590
  export type TzFieldDef = {
@@ -607,13 +602,13 @@ export type LocatedTimestampFieldDef = {
607
602
  readonly filterable?: boolean;
608
603
  readonly sensitive?: boolean;
609
604
  readonly access?: FieldAccess;
610
- /** Erlaubte Grenzen als ISO-Datetime (Wall-Clock). Begrenzt den Picker
611
- * auf Tages-Granularität; die exakte Uhrzeit-Grenze setzt das Zod-Schema
612
- * beim Write durch. */
605
+ /** Permitted bounds as ISO datetime values (wall-clock time). The picker is
606
+ * limited to day granularity; the Zod schema enforces the exact time bound
607
+ * on write. */
613
608
  readonly min?: string;
614
609
  readonly max?: string;
615
- /** Format/Locale-Override für Anzeige und Eingabe-Parsing. Default =
616
- * App-Locale. */
610
+ /** Format/locale override for display and input parsing. Default: app
611
+ * locale. */
617
612
  readonly locale?: string;
618
613
  } & ResolvedPiiFlags;
619
614
  export type FileFieldDef = {
@@ -673,7 +668,7 @@ export declare const FIELD_TYPE_NAMES: readonly FieldDefinition["type"][];
673
668
  * secretMint) may declare it: a `money` field there has no entity to borrow
674
669
  * `defaultCurrency` from, so it must name its own currency source, or the
675
670
  * form seeds a bare `0` that the handler's zod schema then rejects
676
- * (kumiko-framework#2839). Entity fields keep the optional `currency` —
671
+ * . Entity fields keep the optional `currency` —
677
672
  * `entity.defaultCurrency` is already boot-enforced for them. */
678
673
  export type FormFieldDefinition = Exclude<FieldDefinition, MoneyFieldDef> | (MoneyFieldDef & {
679
674
  readonly currency: MoneyCurrencySource;
@@ -707,35 +702,35 @@ export type DerivedFieldsMap = Readonly<Record<string, DerivedFieldDef>>;
707
702
  * DerivedFieldDef` cast lying about a field that's actually missing. */
708
703
  export type ClientDerivedFieldDef = Pick<DerivedFieldDef, "valueType">;
709
704
  export type TransitionMap = Readonly<Record<string, readonly string[]>>;
710
- /** Composite-Index auf einer Entity. Spalten werden via field-Name
711
- * referenziert (camelCase). buildEntityTable mapped sie auf snake_case-
712
- * Spaltennamen und benennt den Index nach Convention:
705
+ /** Composite index on an entity. Columns are referenced by field name
706
+ * (camelCase). `buildEntityTable` maps them to snake_case column names and
707
+ * names the index by convention:
713
708
  *
714
709
  * <table>_<col1>_<col2>_idx (non-unique)
715
710
  * <table>_<col1>_<col2>_unique (unique)
716
711
  *
717
- * Eine `name`-Override ist erlaubt — Convention-Bruch in Bestandscode
718
- * vermeidet Migration-Churn beim Refactor.
712
+ * A `name` override is allowed to avoid migration churn when refactoring
713
+ * existing code.
719
714
  *
720
- * Single-column indices über `tenantId` sind redundant (buildEntityTable
721
- * legt die immer automatisch an); die Boot-Validation warnt (außer
722
- * `{ unique: true }` — semantische 1:1-Constraint, kein Performance-Hint). */
715
+ * Single-column indexes over `tenantId` are redundant because
716
+ * `buildEntityTable` creates them automatically; boot validation warns about
717
+ * them unless `{ unique: true }`, which expresses a semantic 1:1 constraint
718
+ * rather than a performance hint. */
723
719
  export type EntityIndexDef = {
724
720
  readonly columns: readonly [string, ...string[]];
725
721
  readonly unique?: boolean;
726
722
  readonly name?: string;
727
723
  /**
728
- * Optional SQL-Fragment fuer Partial-Index — `CREATE [UNIQUE] INDEX
729
- * ... WHERE <condition>`. Postgres-Pattern fuer "Index nur unter
730
- * bestimmten Bedingungen", typisches Beispiel: ExportJob-Idempotency
731
- * `UNIQUE(userId) WHERE status IN ('pending', 'running')`.
724
+ * Optional SQL fragment for a partial index — `CREATE [UNIQUE] INDEX
725
+ * ... WHERE <condition>`. This is PostgreSQL's pattern for indexing only
726
+ * rows that meet specified conditions; a typical example is export-job
727
+ * idempotency: `UNIQUE(userId) WHERE status IN ('pending', 'running')`.
732
728
  *
733
- * Caller baut das Fragment via drizzle-orm `sql\`...\``-Tagged-
734
- * Template. table-builder.ts emittiert `.where(def.where)` auf den
735
- * Drizzle-IndexBuilder — wirkt sowohl fuer unique- als auch fuer
736
- * non-unique-Indexes (PG erlaubt beides; non-unique partial nutzt
737
- * man z.B. fuer scharfe BTREE-Indexes nur auf einer Status-Teilmenge
738
- * statt voller Tabelle).
729
+ * Callers build the fragment with the drizzle-orm `sql\`...\`` tagged
730
+ * template. table-builder.ts emits `.where(def.where)` on the Drizzle
731
+ * IndexBuilder. It applies to both unique and non-unique indexes; a
732
+ * non-unique partial index can target a status subset instead of the full
733
+ * table.
739
734
  */
740
735
  readonly where?: unknown;
741
736
  };
@@ -800,17 +795,20 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTe
800
795
  readonly lockedWhen?: FieldCondition;
801
796
  /** Allowed state transitions per field. Boot validates against select options. */
802
797
  readonly transitions?: Readonly<Record<string, TransitionMap>>;
803
- /** Composite-Indices über mehrere Felder. Single-column FK-Indices und
804
- * der tenant_id-Index werden weiterhin automatisch von buildEntityTable
805
- * angelegt — diese Liste ist nur für Custom-Indices die der Author
806
- * explizit deklariert (z.B. `{ unique: true, columns: ["key", "tenantId", "userId"] }`). */
798
+ /** Composite indexes over multiple fields. Single-column foreign-key indexes
799
+ * and the tenant_id index are still created automatically by
800
+ * `buildEntityTable`; this list is only for custom indexes explicitly
801
+ * declared by the author (for example,
802
+ * `{ unique: true, columns: ["key", "tenantId", "userId"] }`). */
807
803
  readonly indexes?: readonly EntityIndexDef[];
808
804
  /**
809
- * PK-Typ der Entity.
810
- * - `"serial"` (default): bigserial integer — schneller, kompakter, perfekt für klassische CRUD-Entities.
811
- * - `"uuid"`: uuid mit `gen_random_uuid()` default — verpflichtend für Entities deren `id` als
812
- * Foreign-Key-Wert in multi-tenant Kontexten reist (z.B. `tenant.id` IS der `tenantId`). Auch für
813
- * ES-Aggregate (Phase 2+) notwendig, da Events per UUID aggregiert werden.
805
+ * Entity primary-key type.
806
+ * - `"serial"` (default): bigserial integer — faster and more compact, suitable
807
+ * for classic CRUD entities.
808
+ * - `"uuid"`: UUID with a `gen_random_uuid()` default — required for entities
809
+ * whose `id` travels as a foreign-key value in multi-tenant contexts (for
810
+ * example, `tenant.id` is the `tenantId`). Also required for event-sourced
811
+ * aggregates because events are grouped by UUID.
814
812
  */
815
813
  readonly idType?: "serial" | "uuid";
816
814
  /**
@@ -837,19 +835,19 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTe
837
835
  */
838
836
  readonly parentRef?: ParentRefDef;
839
837
  /**
840
- * Default-Retention-Policy fuer diese Entity. Tenant-Admin kann via
841
- * Compliance-Profile + Tenant-Override (Sprint 2) uebersteuern.
842
- * Cleanup-Job (Sprint 2) verarbeitet die Strategy:
838
+ * Default retention policy for this entity. Tenant administrators can
839
+ * override it through compliance profiles and tenant overrides.
840
+ * The cleanup job applies these strategies:
843
841
  *
844
- * - "hardDelete" → Row physisch weg nach keepFor
845
- * - "softDelete" → deletedAt = now() (mit core-soft-delete-Feature)
846
- * - "anonymize" → Felder mit `anonymize`-Funktion ueberschrieben,
847
- * Row bleibt
848
- * - "blockDelete" → Cleanup-Job ignoriert; User-Forget loest
849
- * stattdessen anonymize aus. Buchhaltung, Mandate,
850
- * Patientenakten.
842
+ * - "hardDelete" → physically remove the row after `keepFor`
843
+ * - "softDelete" → set `deletedAt = now()` (with the core-soft-delete feature)
844
+ * - "anonymize" → overwrite fields with an `anonymize` function while the
845
+ * row remains
846
+ * - "blockDelete" → the cleanup job skips the row; user forget instead
847
+ * triggers anonymization. Accounting records, mandates,
848
+ * and patient records.
851
849
  *
852
- * Siehe docs/plans/features/core-data-retention.md.
850
+ * See docs/plans/features/core-data-retention.md.
853
851
  */
854
852
  readonly retention?: RetentionDef;
855
853
  /**
@@ -862,7 +860,7 @@ export type EntityDefinition<F extends FieldsMap = FieldsMap, T extends EntityTe
862
860
  readonly description?: string;
863
861
  /**
864
862
  * Opts this entity's rows into the `tenant-handover` bundled feature
865
- * (kumiko-framework#3035): a row-bound-grant-legitimated ownership change
863
+ *: a row-bound-grant-legitimated ownership change
866
864
  * from an anonymous/public tenant to a freshly signed-up account's tenant.
867
865
  * An entity reachable from a transfer's root — the root itself, or any
868
866
  * entity whose `parentRef` names the root's type — that is NOT declared
package/dist/fields.js CHANGED
@@ -12,7 +12,7 @@ export function optionsQueryFieldRefs(payload) {
12
12
  /** Qualified config key a `{ kind: "tenant" }` MoneyCurrencySource resolves
13
13
  * against — the tenant-settings bundle's per-tenant currency. Lives here
14
14
  * (client-safe) rather than framework/engine so renderer code can read it
15
- * without a runtime-only import (kumiko-framework#2937). */
15
+ * without a runtime-only import. */
16
16
  export const TENANT_CURRENCY_CONFIG_KEY = "tenant-settings:config:currency";
17
17
  // Exhaustive catalogue of `FieldDefinition["type"]` values, derived from a
18
18
  // `Record` so an added/removed union member fails to compile here instead of
@@ -41,13 +41,16 @@ const FIELD_TYPE_NAME_FLAGS = {
41
41
  files: true,
42
42
  images: true,
43
43
  };
44
- export const FIELD_TYPE_NAMES = Object.keys(FIELD_TYPE_NAME_FLAGS);
44
+ function isFieldTypeName(name) {
45
+ return Object.hasOwn(FIELD_TYPE_NAME_FLAGS, name);
46
+ }
47
+ export const FIELD_TYPE_NAMES = Object.keys(FIELD_TYPE_NAME_FLAGS).filter(isFieldTypeName);
45
48
  // Field types with no bound, editable widget on the auto-wired entityEdit
46
49
  // path (render-field.tsx renders a read-only banner instead) — shared by
47
50
  // the boot validator (packages/framework/src/engine/boot-validator/screens.ts)
48
51
  // and the client-side required-field check (packages/renderer/src/app/form-schema.ts).
49
52
  // Embedded LIST fields (`multiple: true`) are the one exception — they get
50
- // their own EmbeddedListField grid widget (#1838) — callers must check that
53
+ // their own EmbeddedListField grid widget — callers must check that
51
54
  // separately, this array alone doesn't distinguish plain embedded from embedded lists.
52
55
  // A plain array, not a Set: kumiko-types stays free of module-scope runtime
53
56
  // identity (guard-types-class-free.test.ts) since a consumer may resolve two
@@ -268,23 +268,23 @@ export type AppContext = SharedContextFields & {
268
268
  readonly id: string;
269
269
  readonly tenantId: TenantId;
270
270
  } | null;
271
- /** Bei Job-Handler-Aufrufen die aus einem Event-Trigger heraus laufen
272
- * (r.job mit `trigger: { on: ... }`): der Name des Handlers der das
273
- * Event ausgelöst hat. Bei Multi-Trigger-Jobs (`on: [...]`) ist das
274
- * die einzige Möglichkeit für den Handler zu wissen WELCHER Trigger
275
- * gefeuert hat. Cron- und manual-Jobs lassen das Feld undefined. */
271
+ /** For job-handler calls triggered by an event
272
+ * (`r.job` with `trigger: { on: ... }`): the name of the handler that
273
+ * emitted the event. For multi-trigger jobs (`on: [...]`), this is the
274
+ * only way for the handler to know which trigger fired. Cron and manual
275
+ * jobs leave the field undefined. */
276
276
  readonly triggerName?: string;
277
- /** Optionaler Geo→Zone-Adapter. Wenn gesetzt (via buildServer-context oder
278
- * runProdApp/runDevApp extraContext), reicht der Dispatcher ihn an
279
- * ctx.tz.fromCoordinates / fromAddress weiter. Ohne Provider werfen diese. */
277
+ /** Optional Geo→zone adapter. When set (via buildServer context or
278
+ * `runProdApp`/`runDevApp` `extraContext`), the dispatcher passes it to
279
+ * `ctx.tz.fromCoordinates` / `fromAddress`. These methods throw without
280
+ * a provider. */
280
281
  readonly geoTzProvider?: GeoTzProvider;
281
282
  /** Overrides the system clock behind ctx.tz in handlers and jobs; tests inject a fixed time. */
282
283
  readonly clock?: Clock;
283
- /** Tenant des aktuellen Pipeline-Calls. Wird vom Dispatcher beim Bauen
284
- * des HandlerContext aus `user.tenantId` gespiegelt, damit lifecycle-
285
- * pipeline + system-hooks den Wert ohne Zugriff auf user-Object haben.
286
- * Sprint-8a Tier-Composition: `effectiveFeatures(ctx._tenantId)`-call
287
- * in den hook-filter-Stellen. */
284
+ /** Tenant of the current pipeline call. The dispatcher mirrors
285
+ * `user.tenantId` here when building HandlerContext so the lifecycle
286
+ * pipeline and system hooks can access it without the user object.
287
+ * `effectiveFeatures(ctx._tenantId)` uses it at hook-filter points. */
288
288
  readonly _tenantId?: TenantId;
289
289
  /** Boot-configured default locale (BCP-47). Last fallback in ctx.locale's
290
290
  * Request-Locale → Boot-Default chain (dispatch-shared.ts) — used when
@@ -307,13 +307,11 @@ export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedCon
307
307
  readonly systemDb?: UncheckedSystemDb;
308
308
  readonly crossTenantReads?: CrossTenantReads;
309
309
  readonly registry: Registry;
310
- /** Aktiver SessionUser des Handler-Aufrufs — Convenience-Alias zu
311
- * `event.user`. Existiert weil Handler intuitiv `ctx.user.tenantId`
312
- * schreiben (Context = "kennt seinen User") und der Pfad sonst nur
313
- * über `event.user` läuft, was typo-anfällig ist und stillschweigend
314
- * zu `internal_error` führt wenn der falsche Pfad gewählt wird.
315
- * Identisch zum event.user-Wert; Identity-Switches nutzen
316
- * weiterhin queryAs/writeAs. */
310
+ /** The active SessionUser for the handler call, exposed as a convenience
311
+ * alias for `event.user`. Handlers naturally write `ctx.user.tenantId`;
312
+ * without the alias that path does not exist and silently ends in `internal_error`.
313
+ * The value is identical to `event.user`; identity switches still use
314
+ * `queryAs`/`writeAs`. */
317
315
  readonly user: SessionUser;
318
316
  readonly systemUser?: SessionUser;
319
317
  readonly log?: Logger;
@@ -676,7 +674,7 @@ export type QueryHandlerDef = {
676
674
  * the boot-validator skips any column-reference check that would need
677
675
  * it (screens.ts projectionList/relatedList `columns`, projectionDetail
678
676
  * `header`/`metrics`, dashboard `valueField`/`subField`/etc.) rather
679
- * than requiring it retroactively. See fw#2493. */
677
+ * than requiring it retroactively. */
680
678
  readonly outputSchema?: ZodType;
681
679
  /** Entities whose changes this query reflects. Anonymous callers with
682
680
  * access to this query receive /api/sse change signals (no field values)
@@ -1,19 +1,19 @@
1
1
  import type { Context } from "hono";
2
2
  import type { RateLimitOption } from "./handlers.ts";
3
- /** Subset von HTTP-Methoden den wir aktiv unterstützen. Hono spricht
4
- * alle, aber das hier sind die einzigen die ein Feature-Author
5
- * realistisch deklariert. */
3
+ /** Subset of HTTP methods actively supported. Hono supports all methods,
4
+ * but these are the only ones a feature author is expected to declare. */
6
5
  export type HttpRouteMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS";
7
- /** Dependencies die der Handler vom Framework bekommt. App-Author kann
8
- * die App selbst aufrufen (`deps.app.fetch(...)` für intern-call) oder
9
- * direkt per dispatcher Daten ziehen. Db/Redis sind die rohen Connections
10
- * — wer Tenant-Scope braucht muss durch dispatcher.query gehen.
6
+ /** Dependencies provided to the handler by the framework. The app can be
7
+ * called directly (`deps.app.fetch(...)` for internal calls), or data can
8
+ * be fetched through the dispatcher. Db/Redis are raw connections — tenant
9
+ * scoping requires going through dispatcher.query.
11
10
  *
12
- * Hono-typing: `Context<any, any>` weil das Hono-Type-Param-Setup nur
13
- * intern relevant ist. Concrete Hono-app wird im Boot-Path zugewiesen. */
11
+ * Hono typing: `Context<any, any>` is used because Hono's type-parameter
12
+ * setup is only relevant internally. The concrete Hono app is assigned
13
+ * during boot. */
14
14
  export type HttpRouteHandlerDeps = {
15
- /** Die Hono-app — Handler kann via app.fetch(...) interne Routes
16
- * ansprechen (z.B. /api/query mit der vollen Auth-/Anonymous-Chain). */
15
+ /** The Hono app — the handler can use app.fetch(...) to call internal routes
16
+ * (e.g. /api/query with the full auth/anonymous chain). */
17
17
  readonly app: import("hono").Hono<any, any>;
18
18
  /** Run a query handler in-process, forcing a SPECIFIC tenant — WITHOUT
19
19
  * going through the public /api/query HTTP layer (no header parsing, no
@@ -45,9 +45,9 @@ export type HttpRouteRateLimit = Omit<RateLimitOption, "per"> & {
45
45
  readonly per: "ip" | "ip+handler";
46
46
  };
47
47
  export type HttpRouteDefinition = {
48
- /** HTTP-Methode — bei Hono-Mount via app.{get,post,...}(path). */
48
+ /** HTTP method — mounted in Hono via app.{get,post,...}(path). */
49
49
  readonly method: HttpRouteMethod;
50
- /** URL-Pfad (Hono-Pattern, z.B. "/feed.xml" oder "/og/:tenantId.png"). */
50
+ /** URL path (Hono pattern, e.g. "/feed.xml" or "/og/:tenantId.png"). */
51
51
  readonly path: string;
52
52
  /** true = public, no session required. false = mounted behind the
53
53
  * session auth chain (global IP rate limit when configured, PII
@@ -56,8 +56,8 @@ export type HttpRouteDefinition = {
56
56
  * `/api/*` chain only. A request without a session gets 401. The
57
57
  * handler reads the caller via getUser(c). */
58
58
  readonly anonymous: boolean;
59
- /** Hono-Handler. Bekommt Hono-Context + Framework-Deps; returnt
60
- * Response (sync oder async). */
59
+ /** Hono handler. Receives the Hono context and framework dependencies;
60
+ * returns a Response synchronously or asynchronously. */
61
61
  readonly handler: HttpRouteHandler;
62
62
  /** Enforced before the route's guards through the server's rate-limit
63
63
  * resolver (fail-open like the global L1 limiter: a limiter outage
@@ -4,7 +4,7 @@
4
4
  // feature author controls the wire format themselves.
5
5
  //
6
6
  // Pattern symmetric to r.queryHandler / r.writeHandler: definition as
7
- // part of the feature (not the app bootstrapping). Phase-3 multi-tenant
7
+ // part of the feature (not the app bootstrapping). Multi-tenant support
8
8
  // becomes trivial because tenant context is picked up via host resolution.
9
9
  //
10
10
  // Escape-hatch stays: runProdApp.extraRoutes (declarative list with
@@ -55,7 +55,7 @@ export type ActionFormScreenDefinition = {
55
55
  * validator).
56
56
  *
57
57
  * A `money` field here must name its own currency source: this screen
58
- * has no entity whose `defaultCurrency` it could inherit (fw#2839). */
58
+ * has no entity whose `defaultCurrency` it could inherit. */
59
59
  readonly fields: Readonly<Record<string, FormFieldDefinition>>;
60
60
  /** Fields computed from other fields, shown read-only and recomputed on every
61
61
  * change. A `listSum` over an empty list without `whenEmpty` leaves the field
@@ -109,7 +109,7 @@ export type ActionFormScreenDefinition = {
109
109
  * The object form additionally names the success-payload field the
110
110
  * navigation id comes from (`ActionFormRedirect.idFrom`) — needed when
111
111
  * the handler creates a child record but the target screen is the
112
- * parent's detail screen (fw#2670). */
112
+ * parent's detail screen. */
113
113
  readonly redirect?: string | ActionFormRedirect;
114
114
  /** Links shown in the success box after a successful submit while the
115
115
  * screen stays put. Targets resolve like `redirect` (the id comes from the
@@ -164,7 +164,7 @@ export type ActionFormFooterAction = {
164
164
  * id. The write-handler reports the id of what it wrote (`data.id`); when
165
165
  * that record is a child and the target screen shows its parent, the
166
166
  * parent id has to be read from a different field instead of forcing the
167
- * handler to misreport its own result (fw#2670). */
167
+ * handler to misreport its own result. */
168
168
  export type ActionFormRedirect = {
169
169
  /** Same target forms as the string `redirect`: short screen ID
170
170
  * (same-feature) or a fully-qualified cross-feature screen QN. */
@@ -21,14 +21,13 @@ export type CustomScreenDefinition = {
21
21
  readonly access?: AccessRule;
22
22
  /** This screen is registered without a self-owned `r.nav()` entry by
23
23
  * design — an app opts in by navving it explicitly (e.g. a settings-area
24
- * self-service screen). `createKumikoApp`'s boot diagnostic
25
- * (kumiko-framework#2025) skips dormant screens instead of flagging
26
- * every consumer that doesn't mount the client plugin as a false
27
- * positive (kumiko-framework#2034). Only set this on screens the
28
- * feature itself never navs — a screen the feature DOES nav still needs
29
- * its client plugin mounted by every consumer, and should keep
30
- * triggering the diagnostic if it's missing. Also gates the
31
- * boot-validator's nav-area check, same as `dormant` on other screens. */
24
+ * self-service screen). `createKumikoApp`'s boot diagnostic skips dormant
25
+ * screens instead of flagging every consumer that doesn't mount the client
26
+ * plugin as a false positive. Only set this on screens the feature itself
27
+ * never navs — a screen the feature DOES nav still needs its client plugin
28
+ * mounted by every consumer, and should keep triggering the diagnostic if
29
+ * it's missing. Also gates the boot-validator's nav-area check, same as
30
+ * `dormant` on other screens. */
32
31
  readonly dormant?: boolean;
33
32
  /** UI-only gate, also on direct URL access; handlers still enforce access. Unmet or failed renders `fallback`. */
34
33
  readonly visibleWhen?: DashboardPanelVisibility;
@@ -149,7 +149,7 @@ export type EditExtensionSection = {
149
149
  * surrounding form's Save button should activate for its dirty state.
150
150
  * Omit/false for extensions that persist through their own dispatcher
151
151
  * writes (e.g. notes-history NotesSection) — otherwise a read-only form
152
- * shows a Save button that does nothing (fw#2359). */
152
+ * shows a Save button that does nothing. */
153
153
  readonly contributesToFormSubmit?: boolean;
154
154
  /** Overrides the host-derived entity name passed to the mounted component.
155
155
  * Required on `projectionDetail` (which has no real entity — the host
@@ -105,7 +105,7 @@ export type RowActionWriteHandler = {
105
105
  readonly kind?: "writeHandler";
106
106
  /** Stable id per screen — kebab-case, unique within the action set. */
107
107
  readonly id: string;
108
- /** Anzeige-Text (i18n-Key). */
108
+ /** Display text (i18n key). */
109
109
  readonly label: string;
110
110
  /** Qualified name of the server handler, e.g.
111
111
  * "publicstatus:write:maintenance:start". Dispatched via
@@ -188,7 +188,7 @@ export type RowActionNavigate = RowActionNavigateBase & ({
188
188
  readonly screen: string;
189
189
  readonly entity?: never;
190
190
  } | {
191
- /** Entity name instead of screen id (fw#2228) — resolves at boot time to
191
+ /** Entity name instead of screen id — resolves at boot time to
192
192
  * the screen that declares `detailFor: "<entity>"` (see ObjectTarget/
193
193
  * resolveTarget in @cosmicdrift/kumiko-renderer's nav.tsx). */
194
194
  readonly entity: string;
@@ -86,7 +86,7 @@ export type ProjectionListScreenDefinition = {
86
86
  * param present). Same type serves author and wire schema (no separate
87
87
  * client-facing screen type exists), so this stays structurally
88
88
  * writable — validateProjectionListScreens rejects any hand-authored
89
- * value at boot (fw#2165). */
89
+ * value at boot. */
90
90
  readonly sortable?: boolean;
91
91
  /** Derived by buildAppSchema from the query handler's Zod schema (`cursor`
92
92
  * or `offset` param present). See `sortable` doc for why this is
@@ -212,6 +212,6 @@ export type ProjectionDetailScreenDefinition = {
212
212
  /** How head fields render. Default "text" — plain text instead of a
213
213
  * disabled Input, since every field on this screen type is forced
214
214
  * readOnly anyway (there is no write path, see the shim doc above).
215
- * Set "form" to opt back into the disabled-Input look (fw#2245). */
215
+ * Set "form" to opt back into the disabled-Input look. */
216
216
  readonly valueDisplay?: "form" | "text";
217
217
  };
package/dist/screen.d.ts CHANGED
@@ -91,7 +91,7 @@ export type ScreenSlots = {
91
91
  readonly sidebar?: PlatformComponent;
92
92
  readonly footer?: PlatformComponent;
93
93
  /** entityEdit wizard: marks `footer` as the sticky-footer primary action
94
- * (fw#1918's pinned `max-sm:fixed` group on mobile) instead of the
94
+ * (pinned `max-sm:fixed` group on mobile) instead of the
95
95
  * non-sticky secondary bucket it falls into by default alongside
96
96
  * Back/Cancel. Set this when `footer` renders the screen's actual
97
97
  * submit-type action (e.g. a custom "Publish" button standing in for, or
package/dist/step.d.ts CHANGED
@@ -39,7 +39,7 @@ export type PipelineCtx<TPayload = unknown, TMap extends object = KumikoEventTyp
39
39
  */
40
40
  readonly retryAttempt?: number;
41
41
  /**
42
- * Q7 Snapshot-at-Start fingerprint of the workflow definition that
42
+ * Snapshot-at-Start fingerprint of the workflow definition that
43
43
  * started this run. Propagated into every suspension event payload so
44
44
  * the resume-loop can detect library-upgrades that changed the closure
45
45
  * source and surface them as a typed failure instead of running a
@@ -1,16 +1,13 @@
1
- // TargetRef — runtime-Repräsentation eines typed buildTarget-Outputs.
2
- // Wird vom Visual-Tree-Component (renderer/web) an einen Target-Resolver
3
- // dispatcht; der Resolver findet die Editor-Maske via featureId.
1
+ // TargetRef — runtime representation of a typed buildTarget output.
2
+ // It is dispatched by the Visual Tree component (renderer/web) to a target
3
+ // resolver, which finds the editor form via featureId.
4
4
  //
5
- // **Compile-time-Safety:** TargetRef wird niemals hand-getippt. Stattdessen
6
- // erzeugt der typed buildTarget-Builder (engine/build-target.ts) einen
7
- // TargetRef, dessen action + args gegen die treeActions-Map des Ziel-
8
- // Features validiert sind.
5
+ // **Compile-time safety:** TargetRef is never written by hand. Instead, the
6
+ // typed buildTarget builder (engine/build-target.ts) creates a TargetRef whose
7
+ // action + args are validated against the target feature's treeActions map.
9
8
  //
10
- // **Runtime:** args sind hier untyped (Record<string, unknown>), weil
11
- // TargetRef die erased-runtime-Version ist. Der Resolver kennt das
12
- // Ziel-Feature und kann args entsprechend casten — ähnlich wie Event-
13
- // Payloads im Event-Store.
14
- //
15
- // Siehe docs/plans/architecture/visual-tree.md A5.
9
+ // **Runtime:** args are untyped here (Record<string, unknown>) because
10
+ // TargetRef is the erased runtime version. The resolver knows the target
11
+ // feature and can cast args accordingly, similar to event payloads in the
12
+ // event store.
16
13
  export {};
package/dist/tree-node.js CHANGED
@@ -1,22 +1,22 @@
1
- // TreeNode — single Knoten im Client-navProvider-Tree. Provider liefern
2
- // entweder statische readonly TreeNode[] oder dynamische TreeChildrenSubscribe.
1
+ // TreeNode — a single node in the client nav-provider tree. Providers return
2
+ // either static readonly TreeNode[] or dynamic TreeChildrenSubscribe.
3
3
  //
4
- // **Mental-Modell** (VS-Code-Explorer):
4
+ // **Mental model** (VS Code Explorer):
5
5
  // [icon] [label] [...hover-actions]
6
- // optional ein target zum Klicken (öffnet Editor-Maske via
7
- // Target-Resolver) und optional children als nested tree.
6
+ // An optional target makes the node clickable (opening an editor form via
7
+ // the target resolver), and optional children form a nested tree.
8
8
  //
9
- // **State** markiert Visual-Modus für Skeleton-Pattern:
10
- // - "filled" (default) — schwarz, Knoten hat Inhalt
11
- // - "stub" — hellgrau, existing aber leer (Designer-Stub-File)
12
- // - "empty" — Platzhalter für "+ create"-Affordance
13
- // - "loading" — Children werden gerade aufgelöst
14
- // - "error" — Provider hat Fehler emittiert
15
- // Provider die kein Skeleton-Pattern brauchen müssen state nicht setzen.
9
+ // **State** selects the visual mode for the skeleton pattern:
10
+ // - "filled" (default) — black; the node has content
11
+ // - "stub" — light gray; existing but empty (designer stub file)
12
+ // - "empty" — placeholder for the "+ create" affordance
13
+ // - "loading" — children are currently being resolved
14
+ // - "error" — the provider emitted an error
15
+ // Providers that do not need the skeleton pattern can omit state.
16
16
  //
17
- // **Subscribe-Form** für dynamic Children: Provider erhält emit(),
18
- // gibt unsubscribe() zurück. Initial-Emit synchron oder async, weitere
19
- // Emits beliebig oft (z.B. wenn Entity-Row neu erscheint via SSE).
20
- // Spielt natürlich mit existing SSE-Frame: ein Provider kann intern
21
- // auf Entity-Update-Events abonnieren und bei Änderung emit() aufrufen.
17
+ // **Subscribe form** for dynamic children: the provider receives emit()
18
+ // and returns unsubscribe(). The initial emit may be synchronous or async;
19
+ // further emits may occur arbitrarily often (for example, when an entity row
20
+ // appears through SSE). This works with the existing SSE frame: a provider
21
+ // can subscribe internally to entity-update events and call emit() on change.
22
22
  export {};
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.386.0",
3
+ "version": "0.387.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
- "license": "BUSL-1.1",
5
+ "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
7
7
  "repository": {
8
8
  "type": "git",