@cosmicdrift/kumiko-types 0.374.0 → 0.378.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/dist/config.d.ts CHANGED
@@ -219,7 +219,10 @@ export type TemplateListVariableDef = TemplateVariableCommon & {
219
219
  readonly item: Readonly<Record<string, TemplateScalarVariableDef>>;
220
220
  readonly example?: readonly Readonly<Record<string, unknown>>[];
221
221
  };
222
- export type TemplateVariableDef = TemplateScalarVariableDef | TemplateListVariableDef;
222
+ export type TemplateImagesVariableDef = TemplateVariableCommon & {
223
+ readonly type: "images";
224
+ };
225
+ export type TemplateVariableDef = TemplateScalarVariableDef | TemplateListVariableDef | TemplateImagesVariableDef;
223
226
  export type TemplateVariables = Readonly<Record<string, TemplateVariableDef>>;
224
227
  export type NotificationTemplateMeta = {
225
228
  readonly subject: string;
package/dist/feature.d.ts CHANGED
@@ -116,10 +116,12 @@ export type BootCheckContext = {
116
116
  };
117
117
  export type BootCheckFn = (ctx: BootCheckContext) => void;
118
118
  /** runProdApp options a stack can depend on; runProdApp checks the set at boot. */
119
- export type BootOptionName = "mail" | "kmsMode" | "fileStorage" | "billing" | "demoMode" | "seeds" | "signals" | "rateLimit";
119
+ export type BootOptionName = "mail" | "kmsMode" | "fileStorage" | "billing" | "featureToggles" | "demoMode" | "seeds" | "signals" | "rateLimit";
120
120
  export type StackBootRequirement = {
121
121
  readonly stack: string;
122
122
  readonly options: readonly BootOptionName[];
123
+ /** `"warn"`: a missing option logs a boot warning instead of aborting. Default `"error"`. */
124
+ readonly severity?: "error" | "warn";
123
125
  };
124
126
  export type FeatureDefinition = {
125
127
  readonly name: string;
package/dist/fields.d.ts CHANGED
@@ -119,8 +119,9 @@ export type TextFieldDef = {
119
119
  * "high" on the directly-dispatched entry handler; a revert doesn't undo a
120
120
  * run that already read the compromised value. See irreversible-operation-gate. */
121
121
  readonly readAsInstruction?: boolean;
122
- /** "password" is a pure render hint (masked input) — no storage semantics. */
123
- readonly format?: "email" | "url" | "phone" | "password";
122
+ /** "password" is a pure render hint (masked input) — no storage semantics.
123
+ * "month" stores `YYYY-MM`: month picker in the form, format enforced on write. */
124
+ readonly format?: "email" | "url" | "phone" | "password" | "month";
124
125
  readonly default?: string;
125
126
  readonly access?: FieldAccess;
126
127
  /** Mehrzeiliger Text — DefaultInput rendert dann ein <textarea> statt
@@ -488,7 +489,8 @@ export type EmbeddedSubFieldDef = (EmbeddedSubFieldBase & {
488
489
  * precision its target sub-field in `schema` declares (money → integer,
489
490
  * decimal → `scale`) before it's validated. */
490
491
  export type EmbeddedDerivedCellDef = {
491
- readonly op: "multiply" | "sum" | "subtract";
492
+ /** `percent` = `from[0] * from[1] / 100` (exactly two sources, e.g. amount and rate). */
493
+ readonly op: "multiply" | "sum" | "subtract" | "percent";
492
494
  readonly from: readonly string[];
493
495
  };
494
496
  export type EmbeddedFieldDef = {
@@ -230,6 +230,9 @@ type SharedContextFields = {
230
230
  * absent for r.systemScope() handlers and without a db.
231
231
  */
232
232
  readonly forTenant?: (tenantId: TenantId) => TenantWriteTarget;
233
+ /** Mints the per-request `ctx.config`; set by the boot (`addConfigAccessorFactory`) from the effective `configResolver`, overriding an app-supplied value. */
234
+ readonly configAccessorFactory?: ConfigAccessorFactory;
235
+ /** @deprecated Use `configAccessorFactory`; the underscore name is removed in the next major. */
233
236
  readonly _configAccessorFactory?: ConfigAccessorFactory;
234
237
  readonly configEncryption?: import("./envelope-cipher-types.ts").EnvelopeCipher;
235
238
  readonly rateLimit?: import("./rate-limit-types.ts").RateLimitResolver;
@@ -290,7 +293,18 @@ export type AppContext = SharedContextFields & {
290
293
  };
291
294
  export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedContextFields & {
292
295
  readonly db: TenantDb;
296
+ /**
297
+ * Same tenant scoping as `db`, but never bound to the handler's tx: writes through it survive
298
+ * a rollback. `undefined` only in the denial phases (member-resolution, before-transaction)
299
+ * and in platform-metering tenant mode; narrow with `hasOutsideTransactionDb(ctx)` or take it
300
+ * via `requireOutsideTransactionDb(ctx)` (`@cosmicdrift/kumiko-framework/db`). For
301
+ * r.systemScope() handlers it is a fail-closed Proxy; use `systemDb.outsideTransaction`.
302
+ */
293
303
  readonly dbOutsideTransaction: TenantDb | undefined;
304
+ /**
305
+ * @deprecated Use `runInOwnTransaction(ctx.dbOutsideTransaction, fn)` from
306
+ * `@cosmicdrift/kumiko-framework/db`; removed in the next major.
307
+ */
294
308
  readonly outsideTransaction?: <T>(fn: (db: TenantDb) => Promise<T>) => Promise<T>;
295
309
  readonly systemDb?: UncheckedSystemDb;
296
310
  readonly registry: Registry;
@@ -320,8 +334,21 @@ export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedCon
320
334
  readonly write: (qn: string, payload: unknown) => Promise<WriteResult>;
321
335
  readonly writeAs: (user: SessionUser, qn: string, payload: unknown) => Promise<WriteResult>;
322
336
  readonly hasFeature: (featureName: string) => Promise<boolean>;
337
+ /**
338
+ * Default append: a typed domain event on one aggregate stream in the current tx. The runtime
339
+ * bumps the stream version and fires matching projections in the same transaction. A version
340
+ * conflict throws and poisons the transaction.
341
+ */
323
342
  readonly appendEvent: AppendEventFn<TMap>;
343
+ /**
344
+ * Untyped append (`payload: unknown`) with the same runtime as `appendEvent`. Only for
345
+ * migrations and upcasters, where the event type is not knowable at compile time.
346
+ */
324
347
  readonly unsafeAppendEvent: UnsafeAppendEventFn;
348
+ /**
349
+ * Optimistic append: runs in a savepoint and returns a version conflict as a result instead of
350
+ * throwing, so the handler can continue after losing a race on the same aggregate stream.
351
+ */
325
352
  readonly tryAppendEvent: TryAppendEventFn;
326
353
  readonly fetchForWriting: (args: FetchForWritingArgs) => Promise<AggregateStreamHandle>;
327
354
  readonly loadAggregate: (aggregateId: string, options?: {
@@ -375,8 +402,13 @@ export type JobContext = SharedContextFields & {
375
402
  readonly triggerName?: string;
376
403
  readonly triggerEvent?: JobTriggerEvent;
377
404
  readonly _tenantId?: TenantId;
378
- readonly _userId?: string | undefined;
379
405
  };
406
+ /**
407
+ * What a provider plugin (`build`, `capabilitiesFor`, ...) may read. Both write/query handlers
408
+ * and jobs can produce one (`providerPluginContextFromHandler`, `providerPluginContextFromJob`),
409
+ * so a plugin never depends on a request-bound field.
410
+ */
411
+ export type ProviderPluginContext = Pick<HandlerContext, "db" | "dbOutsideTransaction" | "systemDb" | "registry" | "user" | "log" | "triggeredBy" | "config" | "secrets">;
380
412
  export type WriteHandlerFn<TPayload = unknown, TData = unknown> = (event: WriteEvent<TPayload>, context: HandlerContext) => Promise<WriteResult<TData>>;
381
413
  export type QueryHandlerFn<TPayload = unknown, TResult = unknown> = (query: QueryEvent<TPayload>, context: HandlerContext) => Promise<TResult>;
382
414
  export type StreamHandlerFn<TPayload = unknown, TChunk = unknown> = (query: QueryEvent<TPayload>, context: HandlerContext) => AsyncGenerator<TChunk>;
package/dist/nav-icon.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // r.nav()/r.screen()/config-mask/button call site instead of a silent
5
5
  // missing-icon at runtime.
6
6
  //
7
- // The renderer-web NAV_ICONS map (packages/renderer-web/src/icons.tsx) is
7
+ // The renderer/web NAV_ICONS map (packages/renderer/src/web/icons.tsx) is
8
8
  // checked against this same union via `satisfies`, so the two can't drift —
9
9
  // add a key here only together with its lucide-react entry there, and vice
10
10
  // versa.
package/dist/screen.d.ts CHANGED
@@ -63,8 +63,9 @@ export interface FieldFormatRegistry {
63
63
  image: {
64
64
  readonly variant?: string;
65
65
  };
66
- /** List cell: FileRef id (or id array) as a download link with the file name.
67
- * Outside list cells the raw value is shown. */
66
+ /** FileRef id (or id array) as a download link with the file name. On a
67
+ * read-only detail field it renders a file list with download links and an
68
+ * inline preview for PDFs; elsewhere the raw value is shown. */
68
69
  file: object;
69
70
  }
70
71
  export type FormatSpec = {
@@ -200,7 +201,7 @@ export type RowActionWriteHandler = {
200
201
  readonly style?: "primary" | "secondary" | "danger";
201
202
  /** Overrides the id-based default icon (see ACTION_ICON_BY_ID in
202
203
  * kumiko-renderer) — closed IconKey vocabulary into the ICONS registry
203
- * (renderer-web), analogous to EditFieldSpec.icon. */
204
+ * (renderer/web), analogous to EditFieldSpec.icon. */
204
205
  readonly icon?: IconKey;
205
206
  /** Inline render form in the row-action column — see `RowActionDisplay`. */
206
207
  readonly display?: RowActionDisplay;
@@ -244,7 +245,7 @@ export type RowActionNavigateBase = {
244
245
  readonly rowClick?: boolean;
245
246
  /** Overrides the id-based default icon (see ACTION_ICON_BY_ID in
246
247
  * kumiko-renderer) — closed IconKey vocabulary into the ICONS registry
247
- * (renderer-web), analogous to EditFieldSpec.icon. */
248
+ * (renderer/web), analogous to EditFieldSpec.icon. */
248
249
  readonly icon?: IconKey;
249
250
  /** Inline render form in the row-action column — see `RowActionDisplay`. */
250
251
  readonly display?: RowActionDisplay;
@@ -282,7 +283,7 @@ export type RowActionDrawer = {
282
283
  readonly style?: "primary" | "secondary" | "danger";
283
284
  /** Overrides the id-based default icon (see ACTION_ICON_BY_ID in
284
285
  * kumiko-renderer) — closed IconKey vocabulary into the ICONS registry
285
- * (renderer-web), analogous to EditFieldSpec.icon. */
286
+ * (renderer/web), analogous to EditFieldSpec.icon. */
286
287
  readonly icon?: IconKey;
287
288
  /** Inline render form in the row-action column — see `RowActionDisplay`. */
288
289
  readonly display?: RowActionDisplay;
@@ -1056,7 +1057,7 @@ export type EditFieldSpec = string | {
1056
1057
  * rendering are unchanged. */
1057
1058
  readonly submit?: false;
1058
1059
  /** Prefix icon on the input — closed FieldIconKey vocabulary into
1059
- * the FIELD_ICONS registry (renderer-web), analogous to
1060
+ * the FIELD_ICONS registry (renderer/web), analogous to
1060
1061
  * `ScreenNavSugar.icon` / NavIconKey. Only takes effect for
1061
1062
  * single-line `type: "text"` and `"number"` — a `text` field with
1062
1063
  * `multiline` routes to a textarea (no icon slot), and other input
@@ -1151,7 +1152,7 @@ export type EditFieldsSection = {
1151
1152
  readonly columns?: number;
1152
1153
  }[];
1153
1154
  /** Rendered left of the section title, `text-muted-foreground` — closed
1154
- * IconKey vocabulary into the ICONS registry (renderer-web), analogous
1155
+ * IconKey vocabulary into the ICONS registry (renderer/web), analogous
1155
1156
  * to EditFieldSpec.icon. No title → no icon, and no heuristic derives
1156
1157
  * one from the title (titles are free i18n strings). */
1157
1158
  readonly icon?: IconKey;
@@ -1522,6 +1523,21 @@ export type EntityEditActionPlacement = "inline" | "menu";
1522
1523
  export type EntityEditAction = RowAction & {
1523
1524
  readonly placement?: EntityEditActionPlacement;
1524
1525
  };
1526
+ /** A form field computed from other fields of the same form. `listSum` adds one
1527
+ * numeric cell over all rows of an embedded list field; its `whenEmpty`
1528
+ * fallback applies while the list has no rows (it must not be a `listSum`). */
1529
+ export type FormFieldDerivation = {
1530
+ readonly op: "sum" | "subtract" | "multiply" | "percent";
1531
+ readonly from: readonly string[];
1532
+ } | {
1533
+ readonly op: "listSum";
1534
+ readonly list: string;
1535
+ readonly cell: string;
1536
+ readonly whenEmpty?: FormFieldDerivationWithoutList;
1537
+ };
1538
+ export type FormFieldDerivationWithoutList = Extract<FormFieldDerivation, {
1539
+ from: unknown;
1540
+ }>;
1525
1541
  export type ActionFormScreenDefinition = {
1526
1542
  readonly id: string;
1527
1543
  readonly type: "actionForm";
@@ -1560,6 +1576,13 @@ export type ActionFormScreenDefinition = {
1560
1576
  * A `money` field here must name its own currency source: this screen
1561
1577
  * has no entity whose `defaultCurrency` it could inherit (fw#2839). */
1562
1578
  readonly fields: Readonly<Record<string, FormFieldDefinition>>;
1579
+ /** Fields computed from other fields, shown read-only and recomputed on every
1580
+ * change. A `listSum` over an empty list without `whenEmpty` leaves the field
1581
+ * a normal input, and then the client value counts. Otherwise the client value
1582
+ * is never trusted: the handler must wrap its schema with
1583
+ * `withFormDerivations(schema, screen)`, which the boot validator enforces.
1584
+ * A field read only as `whenEmpty` operand is read-only while every such list has rows. */
1585
+ readonly derive?: Readonly<Record<string, FormFieldDerivation>>;
1563
1586
  /** Layout analog zu EntityEditScreen: sections mit fields aus dem
1564
1587
  * fields-Map oben. */
1565
1588
  readonly layout: EditLayout;
@@ -1612,6 +1635,10 @@ export type ActionFormScreenDefinition = {
1612
1635
  * handler's success payload); a link without a string id is hidden.
1613
1636
  * Mutually exclusive with `redirect`. */
1614
1637
  readonly successLinks?: readonly ActionFormSuccessLink[];
1638
+ /** Key figures from the handler's success payload, shown in the success box
1639
+ * while the screen stays put (e.g. charged/skipped/failed of a batch run).
1640
+ * Mutually exclusive with `redirect`. */
1641
+ readonly successFacts?: readonly ActionFormSuccessFact[];
1615
1642
  /** Reset the form to its initial values after a successful submit (prefill
1616
1643
  * kept, typed values cleared). Only applies while the screen stays put,
1617
1644
  * i.e. without `redirect`. */
@@ -1677,6 +1704,15 @@ export type ActionFormLoad = {
1677
1704
  /** Payload key carrying the record id (default "id"). */
1678
1705
  readonly idParam?: string;
1679
1706
  };
1707
+ /** One key figure of the success box, read from the handler's success payload. */
1708
+ export type ActionFormSuccessFact = {
1709
+ /** Flat field name in the handler's success payload. A number or string is
1710
+ * shown as one value, an array a comma-separated list (numbers, strings and
1711
+ * `{ label }` objects). A missing field hides the fact. */
1712
+ readonly field: string;
1713
+ /** Label (i18n key). */
1714
+ readonly label: string;
1715
+ };
1680
1716
  /** A redirect target rendered as a link in the success box. */
1681
1717
  export type ActionFormSuccessLink = Omit<ActionFormRedirect, "idFrom"> & {
1682
1718
  /** Success-payload field carrying the navigation id. Required for a record
@@ -1,5 +1,5 @@
1
1
  // TargetRef — runtime-Repräsentation eines typed buildTarget-Outputs.
2
- // Wird vom Visual-Tree-Component (renderer-web) an einen Target-Resolver
2
+ // Wird vom Visual-Tree-Component (renderer/web) an einen Target-Resolver
3
3
  // dispatcht; der Resolver findet die Editor-Maske via featureId.
4
4
  //
5
5
  // **Compile-time-Safety:** TargetRef wird niemals hand-getippt. Stattdessen
@@ -46,6 +46,11 @@ export type TenantDb = {
46
46
  selectMany<T = Record<string, unknown>>(table: SchemaTable | EntityTableMeta, where?: WhereObject, options?: SelectOptions): Promise<readonly T[]>;
47
47
  fetchOne<T = Record<string, unknown>>(table: SchemaTable | EntityTableMeta, where: WhereObject): Promise<T | undefined>;
48
48
  count(table: SchemaTable | EntityTableMeta, where?: WhereObject): Promise<number>;
49
+ /**
50
+ * Transaction-scoped advisory lock keyed on (namespace, this handle's tenantId + key); released at
51
+ * commit or rollback. Keys of different tenants never contend. Only serialises inside a transaction.
52
+ */
53
+ advisoryXactLock(namespace: number, key: string): Promise<void>;
49
54
  aggregate(table: SchemaTable | EntityTableMeta, spec: AggregateSpec, where?: WhereObject): Promise<readonly AggregateRow[]>;
50
55
  insertOne<T = Record<string, unknown>>(table: WritableTable, values: Record<string, unknown>): Promise<T | undefined>;
51
56
  updateMany<T = Record<string, unknown>>(table: WritableTable, set: Record<string, unknown>, where: WhereObject): Promise<readonly T[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.374.0",
3
+ "version": "0.378.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": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
package/src/changes.json CHANGED
@@ -1,4 +1,24 @@
1
1
  [
2
+ {
3
+ "version": "0.377.0",
4
+ "type": "improvement",
5
+ "title": "outsideTransaction deprecated, ProviderPluginContext added",
6
+ "detail": "HandlerContext.outsideTransaction is marked deprecated; use runInOwnTransaction(ctx.dbOutsideTransaction, fn). ProviderPluginContext is a Pick of HandlerContext for provider plugins. SharedContextFields gains configAccessorFactory; _configAccessorFactory stays as a deprecated alias. dbOutsideTransaction stays TenantDb | undefined (undefined only in the member-resolution and before-transaction denial phases and in platform-metering tenant mode); hasOutsideTransactionDb(ctx) narrows it and requireOutsideTransactionDb(ctx) throws when it is missing.",
7
+ "migration": "Replace ctx.outsideTransaction(fn) with runInOwnTransaction(ctx.dbOutsideTransaction, fn). Read configAccessorFactory instead of _configAccessorFactory."
8
+ },
9
+ {
10
+ "version": "0.377.0",
11
+ "type": "breaking",
12
+ "title": "JobContext._userId removed",
13
+ "detail": "The job runner never set JobContext._userId; only user-data-rights read it, falling back to SYSTEM_USER_ID. Hand-built JobContext objects that still set it fail to type-check, and code that read it must use SYSTEM_USER_ID or the job's system user.",
14
+ "migration": "Drop _userId from hand-built JobContext objects (tests, job helpers) and replace reads of ctx._userId with SYSTEM_USER_ID. Known hits - money-horse data-export integration test, offlot-app channel-text generate-texts.job.ts."
15
+ },
16
+ {
17
+ "version": "0.376.0",
18
+ "type": "improvement",
19
+ "title": "StackBootRequirement severity and the featureToggles option name",
20
+ "detail": "StackBootRequirement takes severity \"warn\" for requirements that only log, and BootOptionName lists featureToggles."
21
+ },
2
22
  {
3
23
  "version": "0.370.0",
4
24
  "type": "breaking",