@cosmicdrift/kumiko-types 0.333.0 → 0.335.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
@@ -2,7 +2,7 @@ import type { ZodType } from "zod";
2
2
  import type { ConcurrencyMode } from "./concurrency-mode.js";
3
3
  import type { ConfigScope } from "./config-scope.js";
4
4
  import type { DbConnection } from "./db-connection.js";
5
- import type { FieldDefinition } from "./fields.js";
5
+ import type { FieldDefinition, OptionsQueryPayload } from "./fields.js";
6
6
  import type { EscapeHatchDeclaration, JobContext } from "./handlers.js";
7
7
  import type { PostDeleteHookFn, PostSaveHookFn, PreDeleteHookFn, PreQueryHookFn, PreSaveHookFn } from "./hooks.js";
8
8
  import type { TenantId } from "./identifiers.js";
@@ -46,12 +46,14 @@ export type ConfigKeyDefinition<T extends ConfigKeyType = ConfigKeyType> = {
46
46
  readonly piiEncrypted?: boolean;
47
47
  readonly options?: readonly string[];
48
48
  /** Only on `type: "select"`: loads the options from this query (QN), contract
49
- * `{ rows: { value, label }[] }`, labels shown verbatim. Mutually exclusive with
49
+ * `{ rows: { value, label, description?, group? }[] }`, labels shown verbatim. Mutually exclusive with
50
50
  * `options` and `allowPerRequest`. The write side does not check the value against
51
51
  * the query result; the consuming feature validates it where it uses it. */
52
52
  readonly optionsQuery?: string;
53
- /** Static payload sent with every `optionsQuery` call. Requires `optionsQuery`. */
54
- readonly optionsQueryPayload?: Readonly<Record<string, string | number | boolean>>;
53
+ /** Payload sent with every `optionsQuery` call. Requires `optionsQuery`. `{ field }`
54
+ * names another config key (short name) of the owner feature, resolved on the
55
+ * generated settings screen. */
56
+ readonly optionsQueryPayload?: OptionsQueryPayload;
55
57
  readonly bounds?: ConfigBounds;
56
58
  readonly pattern?: {
57
59
  readonly regex: string;
@@ -68,6 +70,8 @@ export type ConfigKeyDefinition<T extends ConfigKeyType = ConfigKeyType> = {
68
70
  };
69
71
  export type ConfigMask = {
70
72
  readonly title: string;
73
+ /** i18n key for help text shown under the field label. */
74
+ readonly description?: string;
71
75
  readonly icon?: NavIconKey;
72
76
  readonly order?: number;
73
77
  };
package/dist/feature.d.ts CHANGED
@@ -473,6 +473,7 @@ export type Registry = {
473
473
  isHandlerSystemScoped(qualifiedHandler: string): boolean;
474
474
  isJobSystemScoped(qualifiedJobName: string): boolean;
475
475
  getHandlerFeature(qualifiedHandler: string): string | undefined;
476
+ getJobFeature(qualifiedJobName: string): string | undefined;
476
477
  hasRateLimitedHandler(): boolean;
477
478
  getAllMetrics(): ReadonlyMap<string, FeatureMetricDef & {
478
479
  readonly featureName: string;
package/dist/fields.d.ts CHANGED
@@ -89,6 +89,20 @@ export type TextFieldDef = {
89
89
  readonly filterable?: boolean;
90
90
  readonly encrypted?: boolean;
91
91
  readonly sensitive?: boolean;
92
+ /** Write-only secret (API key): the value is never returned to clients. Reads
93
+ * (list/detail/write responses) show `true` = set, `null` = empty. Writes:
94
+ * `""` or omitted = unchanged, `null` = clear, string = new value.
95
+ * Server-side: executor reads (detail/list in server handlers) and
96
+ * postSave/postQuery hooks see the plaintext; ctx.query, dispatcher.query,
97
+ * agent tools and `_refs` see true/null; ctx.db sees the ciphertext.
98
+ * Known limit: masking runs for entity-bound query handlers with array /
99
+ * {rows} / flat results; an unbound custom query handler that returns
100
+ * executor rows must call maskWriteOnlyFields itself (same scope limit as
101
+ * access.read). Requires `sensitive` (via `find: "secret"`); forbidden with
102
+ * default/searchable/sortable/filterable/lookupable and on inline form fields
103
+ * (actionForm/wizard). Allowed on configEdit text fields only over an
104
+ * encrypted-at-rest config key, where it is also set automatically. */
105
+ readonly writeOnly?: boolean;
92
106
  /** A later run reads this field's value as an instruction — prompt, rule,
93
107
  * template. Every create/update whose payload writes it requires agent.risk
94
108
  * "high" on the directly-dispatched entry handler; a revert doesn't undo a
@@ -158,6 +172,16 @@ export type BooleanFieldDef = {
158
172
  readonly access?: FieldAccess;
159
173
  };
160
174
  export type SelectOptionTone = "ok" | "warn" | "bad" | "neutral";
175
+ /** One `optionsQueryPayload` entry: a literal, or `{ field }` = the current value of
176
+ * a sibling field of the same form (config keys: another key of the owner feature). */
177
+ export type OptionsQueryPayloadValue = string | number | boolean | {
178
+ readonly field: string;
179
+ };
180
+ export type OptionsQueryPayload = Readonly<Record<string, OptionsQueryPayloadValue>>;
181
+ export declare function isOptionsQueryFieldRef(value: OptionsQueryPayloadValue): value is {
182
+ readonly field: string;
183
+ };
184
+ export declare function optionsQueryFieldRefs(payload: OptionsQueryPayload | undefined): string[];
161
185
  export type SelectFieldDef<TOptions extends readonly string[] = readonly string[]> = {
162
186
  readonly type: "select";
163
187
  readonly description?: string;
@@ -180,13 +204,15 @@ export type SelectFieldDef<TOptions extends readonly string[] = readonly string[
180
204
  readonly optionTones?: {
181
205
  readonly [K in TOptions[number]]?: SelectOptionTone;
182
206
  };
183
- /** Loads the options from this query (QN). Contract: `{ rows: { value, label }[] }`,
184
- * labels are shown verbatim (not translated). `options` must be empty when set.
207
+ /** Loads the options from this query (QN). Contract:
208
+ * `{ rows: { value, label, description?, group? }[] }`, labels are shown verbatim
209
+ * (not translated); description is a muted second line, group a heading. `options` must be empty when set.
185
210
  * Only valid on configEdit/actionForm screen fields and config keys, not on entity
186
211
  * fields: the stored value would not be checked against the query result. */
187
212
  readonly optionsQuery?: string;
188
- /** Static payload sent with every `optionsQuery` call. */
189
- readonly optionsQueryPayload?: Readonly<Record<string, string | number | boolean>>;
213
+ /** Payload sent with every `optionsQuery` call. `{ field }` entries re-load the
214
+ * options when that sibling field changes. */
215
+ readonly optionsQueryPayload?: OptionsQueryPayload;
190
216
  } & ResolvedPiiFlags;
191
217
  export type MultiSelectFieldDef<TOptions extends readonly string[] = readonly string[]> = {
192
218
  readonly type: "multiSelect";
package/dist/fields.js CHANGED
@@ -1,4 +1,12 @@
1
1
  // --- Field Types ---
2
+ export function isOptionsQueryFieldRef(value) {
3
+ return typeof value === "object" && value !== null;
4
+ }
5
+ export function optionsQueryFieldRefs(payload) {
6
+ return Object.values(payload ?? {})
7
+ .filter(isOptionsQueryFieldRef)
8
+ .map((ref) => ref.field);
9
+ }
2
10
  /** Qualified config key a `{ kind: "tenant" }` MoneyCurrencySource resolves
3
11
  * against — the tenant-settings bundle's per-tenant currency. Lives here
4
12
  * (client-safe) rather than framework/engine so renderer code can read it
package/dist/screen.d.ts CHANGED
@@ -115,12 +115,14 @@ export type ListColumnSpec = string | {
115
115
  readonly hideOnNarrow?: boolean;
116
116
  /** Dashboard list panels only. "bar": field holds a 0..1 fraction, drawn
117
117
  * as bar + percent. "badge": field holds a label, drawn as a pill whose
118
- * tone comes from `badgeToneField`. Other list renderers ignore it. */
118
+ * tone comes from `badgeToneField`. "datetime": field holds an ISO string or
119
+ * epoch ms, shown in the user's locale and time zone. Other list renderers
120
+ * ignore it. */
119
121
  readonly display?: ListColumnDisplay;
120
122
  /** Row field naming the badge tone; only with `display: "badge"`. */
121
123
  readonly badgeToneField?: string;
122
124
  };
123
- export type ListColumnDisplay = "bar" | "badge";
125
+ export type ListColumnDisplay = "bar" | "badge" | "datetime";
124
126
  export type ListBadgeTone = "positive" | "warn" | "negative" | "neutral";
125
127
  export type ListPaginationMode = "pages" | "infinite" | false;
126
128
  export type ListSortDir = "asc" | "desc";
@@ -194,6 +196,14 @@ export type RowActionWriteHandler = {
194
196
  readonly icon?: IconKey;
195
197
  /** Inline render form in the row-action column — see `RowActionDisplay`. */
196
198
  readonly display?: RowActionDisplay;
199
+ /** Where to go after the write succeeded instead of staying on the shown
200
+ * record. Same forms as entityEdit `redirect`. Honored on record actions
201
+ * (projectionDetail header/section actions, entityEdit header/section
202
+ * actions); a valid `returnTo` wins under the same rule as
203
+ * entityEdit.redirect. Not used by list row actions. Without it, an action
204
+ * deleting the shown record (`<feature>:write:<entity>:delete` with that
205
+ * record's id) leaves to `returnTo`/the list; any other action refetches. */
206
+ readonly redirect?: string | ActionFormRedirect;
197
207
  };
198
208
  export type RowActionNavigateBase = {
199
209
  readonly kind: "navigate";
@@ -735,6 +745,11 @@ export type DashboardScreenPanel = {
735
745
  readonly screen: string;
736
746
  readonly label?: string;
737
747
  readonly visibleWhen?: DashboardPanelVisibility;
748
+ /** Render the embedded form without its own card frame and padding, aligned to
749
+ * the dashboard's page grid like a standalone settings screen. Default false
750
+ * (framed card); set for panels whose body is a settings-list form, where a
751
+ * card inside the page would double the inset. */
752
+ readonly chromeless?: boolean;
738
753
  };
739
754
  export type DashboardPanelDefinition = DashboardStatPanel | DashboardStatGroupPanel | DashboardChartPanel | DashboardListPanel | DashboardFeedPanel | DashboardProgressListPanel | DashboardCustomPanel | DashboardScreenPanel;
740
755
  export type DashboardFilterDefinition = {
@@ -781,6 +796,10 @@ export type DashboardScreenDefinition = {
781
796
  readonly timeRange?: DashboardTimeRangeDefinition;
782
797
  /** Marks the screen as spanning more than the caller's own scope. */
783
798
  readonly scope?: DashboardScopeDefinition;
799
+ /** Show the "Updated at" timestamp in the toolbar. Default true; turn off for
800
+ * dashboards that host forms rather than live metrics, where a load time
801
+ * next to the form reads as stale data. */
802
+ readonly showUpdatedAt?: boolean;
784
803
  readonly slots?: ScreenSlots;
785
804
  readonly access?: AccessRule;
786
805
  };
@@ -1062,6 +1081,12 @@ export type EditLayout = {
1062
1081
  * boot-validator rejects it on entityEdit, see the check for the reason);
1063
1082
  * requires >= 2 sections, each with a title and an `id`. */
1064
1083
  readonly mode?: "single" | "wizard" | "tabs";
1084
+ /** "settings-list" lays each section out as a band with a title column on
1085
+ * the left and one hairline-separated row per field (label + help left,
1086
+ * control right) — the familiar settings pattern, for screens that are a
1087
+ * list of independent preferences rather than one record form. Default
1088
+ * "form" keeps the grid of cells. */
1089
+ readonly variant?: "form" | "settings-list";
1065
1090
  /** Persists in-progress wizard state as a resumable draft instead of
1066
1091
  * discarding it on navigation away. */
1067
1092
  readonly draft?: boolean;
@@ -1424,6 +1449,11 @@ export type ConfigEditScreenDefinition = {
1424
1449
  * ohne es unter der `__config-edit__`-Konvention zu duplizieren.
1425
1450
  * Fehlt ein Eintrag, gilt die Konvention. */
1426
1451
  readonly fieldLabels?: Readonly<Record<string, string>>;
1452
+ /** Per-field help text i18n key (Field-Name → Key), from `mask.description`. */
1453
+ readonly fieldDescriptions?: Readonly<Record<string, string>>;
1454
+ /** Field names whose config key declares `required: true` — drives the
1455
+ * "Not set. Required." origin line, not form validation. */
1456
+ readonly requiredFields?: readonly string[];
1427
1457
  /** i18n-key für den Submit-Button. Default: "kumiko.actions.save". */
1428
1458
  readonly submitLabel?: string;
1429
1459
  readonly slots?: ScreenSlots;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.333.0",
3
+ "version": "0.335.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,11 @@
1
1
  [
2
+ {
3
+ "version": "0.335.0",
4
+ "type": "improvement",
5
+ "title": "RowActionWriteHandler gets an optional redirect for record actions",
6
+ "detail": "`redirect` takes the same forms as entityEdit `redirect` and is honored on projectionDetail and entityEdit header and section actions, not on list row actions.",
7
+ "migration": "No code change needed."
8
+ },
2
9
  {
3
10
  "version": "0.328.1",
4
11
  "type": "improvement",