@cosmicdrift/kumiko-types 0.381.0 → 0.383.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
@@ -3,7 +3,7 @@ import type { ConcurrencyMode } from "./concurrency-mode.ts";
3
3
  import type { ConfigScope } from "./config-scope.ts";
4
4
  import type { DbConnection } from "./db-connection.ts";
5
5
  import type { FieldDefinition, OptionsQueryPayload } from "./fields.ts";
6
- import type { EscapeHatchDeclaration, JobContext } from "./handlers.ts";
6
+ import type { EscapeHatchDeclaration, JobContext, JobDeferral } from "./handlers.ts";
7
7
  import type { PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn } from "./hooks.ts";
8
8
  import type { TenantId } from "./identifiers.ts";
9
9
  import type { NavIconKey } from "./nav-icon.ts";
@@ -139,7 +139,7 @@ export type ConfigResolver = {
139
139
  };
140
140
  export type RunIn = "api" | "worker" | "both";
141
141
  export type JobRunIn = Exclude<RunIn, "both">;
142
- export type JobHandlerFn = (payload: Record<string, unknown>, context: JobContext) => Promise<void>;
142
+ export type JobHandlerFn = (payload: Record<string, unknown>, context: JobContext) => Promise<void | JobDeferral>;
143
143
  export type JobTrigger = {
144
144
  readonly on: import("./handlers.ts").NameOrRef | readonly import("./handlers.ts").NameOrRef[];
145
145
  readonly where?: Readonly<Record<string, string | number | boolean>>;
@@ -166,6 +166,7 @@ export type JobDefinition = {
166
166
  readonly concurrency?: ConcurrencyMode | undefined;
167
167
  readonly maxPerTenant?: number | MaxPerTenantOptions | undefined;
168
168
  readonly debounceMs?: number | undefined;
169
+ readonly maxDefers?: number | undefined;
169
170
  readonly retries?: number | undefined;
170
171
  readonly backoff?: JobBackoff | undefined;
171
172
  readonly timeout?: number | undefined;
package/dist/fields.d.ts CHANGED
@@ -120,8 +120,9 @@ export type TextFieldDef = {
120
120
  * run that already read the compromised value. See irreversible-operation-gate. */
121
121
  readonly readAsInstruction?: boolean;
122
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";
123
+ * "month" stores `YYYY-MM`: month picker in the form, format enforced on write.
124
+ * "iban" is checked on write (shape plus mod-97); the value is stored as typed. */
125
+ readonly format?: "email" | "url" | "phone" | "password" | "month" | "iban";
125
126
  readonly default?: string;
126
127
  readonly access?: FieldAccess;
127
128
  /** Mehrzeiliger Text — DefaultInput rendert dann ein <textarea> statt
@@ -377,6 +377,10 @@ export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedCon
377
377
  readonly resolveActiveMembership: (userId: string, tenantId: TenantId) => Promise<ActiveMembershipResult>;
378
378
  readonly queryAsMember: MemberReader;
379
379
  };
380
+ export type JobDeferral = {
381
+ readonly kind: "job-deferral";
382
+ readonly delayMs: number;
383
+ };
380
384
  export type JobContext = SharedContextFields & {
381
385
  readonly db: TenantDb;
382
386
  readonly registry: Registry;
@@ -390,6 +394,7 @@ export type JobContext = SharedContextFields & {
390
394
  } | null;
391
395
  readonly attempt: number;
392
396
  readonly finalAttempt: boolean;
397
+ readonly defer: (delayMs: number) => JobDeferral;
393
398
  readonly systemDb?: UncheckedSystemDb;
394
399
  readonly crossTenantReads?: CrossTenantReads;
395
400
  readonly write: (qn: string, payload: unknown) => Promise<WriteResult>;
@@ -60,7 +60,7 @@ export type HttpRouteDefinition = {
60
60
  * Response (sync oder async). */
61
61
  readonly handler: HttpRouteHandler;
62
62
  /** Enforced before the route's guards through the server's rate-limit
63
- * resolver (fail-closed like the global L1 limiter: a limiter outage
64
- * answers 503 instead of letting the request through). */
63
+ * resolver (fail-open like the global L1 limiter: a limiter outage
64
+ * lets the request through, counted and logged). */
65
65
  readonly rateLimit?: HttpRouteRateLimit;
66
66
  };
package/dist/screen.d.ts CHANGED
@@ -631,12 +631,11 @@ export type MetricNavigate = {
631
631
  * instead of navigating away. */
632
632
  readonly tab?: string;
633
633
  };
634
- export type MetricSpec = string | {
634
+ export type MetricSpec = {
635
635
  readonly field: string;
636
- readonly label?: string;
636
+ readonly label: string;
637
637
  readonly navigate?: MetricNavigate;
638
638
  };
639
- export declare function metricField(metric: MetricSpec): string;
640
639
  export type ProjectionDetailScreenDefinition = {
641
640
  readonly id: string;
642
641
  readonly type: "projectionDetail";
@@ -682,8 +681,7 @@ export type ProjectionDetailScreenDefinition = {
682
681
  /** Record header above the layout — title/subtitle/status name columns
683
682
  * in the query row (not literals). Rendered only when set. */
684
683
  readonly header?: RecordHeaderSpec;
685
- /** Metric band above the layout — column names in the query row, labeled
686
- * via `fieldLabels` (string shorthand) or the object form's own `label`.
684
+ /** Metric band above the layout — column names in the query row, each with its own i18n `label`.
687
685
  * Rendered only when set. */
688
686
  readonly metrics?: readonly MetricSpec[];
689
687
  /** Parent list screen (kurze id) für eine "Zurück"-Navigation. */
@@ -1133,7 +1131,7 @@ export type EditFieldPrefill = {
1133
1131
  readonly property: "displayName" | "email" | "id";
1134
1132
  };
1135
1133
  export type EditSectionSpec = EditFieldsSection | EditExtensionSection | EditRelatedListSection | EditWriteFormSection;
1136
- export type EditFieldsSection = {
1134
+ type EditFieldsSectionBase = {
1137
1135
  /** Kebab-case tab id, used as the `?tab=` value. Required by the
1138
1136
  * boot-validator when the enclosing `EditLayout.mode` is "tabs". */
1139
1137
  readonly id?: string;
@@ -1149,17 +1147,6 @@ export type EditFieldsSection = {
1149
1147
  * (subtitle-only section). */
1150
1148
  readonly description?: string;
1151
1149
  readonly columns?: number;
1152
- /** Mutually exclusive with `groups` — pass `[]` when using `groups`; the
1153
- * boot-validator rejects both non-empty or both empty. */
1154
- readonly fields: readonly EditFieldSpec[];
1155
- /** Splits the section into multiple titled cards instead of one flat grid.
1156
- * Mutually exclusive with `fields`; fields named here still need to exist. */
1157
- readonly groups?: readonly {
1158
- readonly title: string;
1159
- readonly fields: readonly EditFieldSpec[];
1160
- /** Default 2. */
1161
- readonly columns?: number;
1162
- }[];
1163
1150
  /** Rendered left of the section title, `text-muted-foreground` — closed
1164
1151
  * IconKey vocabulary into the ICONS registry (renderer/web), analogous
1165
1152
  * to EditFieldSpec.icon. No title → no icon, and no heuristic derives
@@ -1180,6 +1167,21 @@ export type EditFieldsSection = {
1180
1167
  * (declared or id-derived); the boot-validator rejects one that doesn't. */
1181
1168
  readonly actions?: readonly RowAction[];
1182
1169
  };
1170
+ export type EditFieldGroup = {
1171
+ readonly title: string;
1172
+ readonly fields: readonly EditFieldSpec[];
1173
+ /** Default 2. */
1174
+ readonly columns?: number;
1175
+ };
1176
+ /** Either a flat `fields` grid or titled `groups` cards, never both: the groups branch has no
1177
+ * `fields` key, so a raw `.fields` read on a section does not compile — use `sectionFieldSpecs`. */
1178
+ export type EditFieldsSection = EditFieldsSectionBase & ({
1179
+ readonly fields: readonly EditFieldSpec[];
1180
+ } | {
1181
+ /** Splits the section into multiple titled cards instead of one flat grid; fields named
1182
+ * here still need to exist. */
1183
+ readonly groups: readonly EditFieldGroup[];
1184
+ });
1183
1185
  export type EditExtensionSection = {
1184
1186
  /** Kebab-case tab id, used as the `?tab=` value. Required by the
1185
1187
  * boot-validator when the enclosing `EditLayout.mode` is "tabs". */
package/dist/screen.js CHANGED
@@ -1,6 +1,3 @@
1
- export function metricField(metric) {
2
- return typeof metric === "string" ? metric : metric.field;
3
- }
4
1
  // Query result contract: flat record; `valueField` points at the
5
2
  // value (string is display-ready, number is locale-formatted, DashboardI18nText
6
3
  // is translated), `subField` optionally at a sub line (string | DashboardI18nText), `toneField` optionally at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.381.0",
3
+ "version": "0.383.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,18 @@
1
1
  [
2
+ {
3
+ "version": "0.383.0",
4
+ "type": "breaking",
5
+ "title": "MetricSpec requires the object form with a label",
6
+ "detail": "The plain string shorthand for projectionDetail `metrics` is removed and `label` (an i18n key) is required, so a metric can no longer render as a bare column name. `metricField()` is removed from the screen exports because it is trivial now; read `metric.field`. The `metrics` entry without label check in the Screen-Conventions guard is dropped, the type enforces it.",
7
+ "migration": "Replace `metrics: [\"balance\"]` with `metrics: [{ field: \"balance\", label: \"<i18n key>\" }]`, reusing the key you had in `fieldLabels` for that column. Replace `metricField(metric)` with `metric.field`."
8
+ },
9
+ {
10
+ "version": "0.383.0",
11
+ "type": "breaking",
12
+ "title": "EditFieldsSection is either fields or groups",
13
+ "detail": "EditFieldsSection is now a union of a section with `fields` and a section with `groups`; the groups branch has no `fields` key, so a raw `section.fields` read on a section does not compile and `fields: []` next to `groups` is no longer needed. The boot validator rejects a section that declares both, including `fields: []`. FieldsOrGroupsSection now has optional `fields` and `groups`. Read a section's fields through `sectionFieldSpecs`, which the Section-Fields-Raw guard used to enforce.",
14
+ "migration": "Remove `fields: []` from every section that declares `groups`. Code that reads `section.fields` or `section.groups` of a fields section uses `sectionFieldSpecs(section)` or narrows with `\"groups\" in section`."
15
+ },
2
16
  {
3
17
  "version": "0.379.0",
4
18
  "type": "breaking",