@cosmicdrift/kumiko-types 0.307.0 → 0.309.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-types",
3
- "version": "0.307.0",
3
+ "version": "0.309.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>",
@@ -174,6 +174,10 @@
174
174
  "types": "./src/nav-icon.ts",
175
175
  "default": "./src/nav-icon.ts"
176
176
  },
177
+ "./action-icon": {
178
+ "types": "./src/action-icon.ts",
179
+ "default": "./src/action-icon.ts"
180
+ },
177
181
  "./field-icon": {
178
182
  "types": "./src/field-icon.ts",
179
183
  "default": "./src/field-icon.ts"
@@ -249,12 +253,12 @@
249
253
  },
250
254
  "devDependencies": {
251
255
  "hono": "^4.13.1",
252
- "postgres": "^3.4.9",
256
+ "postgres": "npm:@bender0oo0/postgres@3.4.9-kumiko.1",
253
257
  "temporal-polyfill": "^0.3.2"
254
258
  },
255
259
  "peerDependencies": {
256
260
  "hono": "^4.13.1",
257
- "postgres": "^3.4.9",
261
+ "postgres": "npm:@bender0oo0/postgres@3.4.9-kumiko.1",
258
262
  "temporal-polyfill": "^0.3.2"
259
263
  },
260
264
  "publishConfig": {
@@ -0,0 +1,57 @@
1
+ import type { IconKey } from "./nav-icon";
2
+
3
+ // Part B (fw-ui-defaults): id-derived default icon for actions that never
4
+ // declared one — a screen author still gets a recognizable glyph instead of
5
+ // a bare label. Checked against the actually registered IconKey vocabulary
6
+ // (nav-icon.ts) — no entry for verbs without a matching icon (e.g. "start",
7
+ // "pause").
8
+ export const ACTION_ICON_BY_ID: Readonly<Partial<Record<string, IconKey>>> = {
9
+ delete: "trash",
10
+ deletion: "trash",
11
+ edit: "pencil",
12
+ create: "plus",
13
+ new: "plus",
14
+ add: "plus",
15
+ view: "eye",
16
+ open: "eye",
17
+ cancel: "x",
18
+ reject: "x",
19
+ complete: "check",
20
+ resolve: "check",
21
+ approve: "check",
22
+ archive: "archive",
23
+ publish: "upload",
24
+ duplicate: "copy",
25
+ copy: "copy",
26
+ download: "download",
27
+ refresh: "refresh",
28
+ retry: "refresh",
29
+ settings: "settings",
30
+ share: "share",
31
+ send: "send",
32
+ };
33
+
34
+ // Ids are kebab-case (RowAction.id doc): aggregate-object-verb
35
+ // ("order-ship") or verb-prefix ("add-item").
36
+ function kebabLastSegment(id: string): string {
37
+ const idx = id.lastIndexOf("-");
38
+ return idx === -1 ? id : id.slice(idx + 1);
39
+ }
40
+
41
+ function kebabFirstSegment(id: string): string {
42
+ const idx = id.indexOf("-");
43
+ return idx === -1 ? id : id.slice(0, idx);
44
+ }
45
+
46
+ // Resolution order: author-declared `icon` wins, then the id-derived
47
+ // default (full id, then its last kebab segment, then its first kebab
48
+ // segment). `declared` is `undefined` for ToolbarAction, which has no
49
+ // author-facing icon field.
50
+ export function resolveActionIcon(id: string, declared?: IconKey): IconKey | undefined {
51
+ if (declared !== undefined) return declared;
52
+ return (
53
+ ACTION_ICON_BY_ID[id] ??
54
+ ACTION_ICON_BY_ID[kebabLastSegment(id)] ??
55
+ ACTION_ICON_BY_ID[kebabFirstSegment(id)]
56
+ );
57
+ }
package/src/changes.json CHANGED
@@ -1,4 +1,11 @@
1
1
  [
2
+ {
3
+ "version": "0.308.0",
4
+ "type": "improvement",
5
+ "title": "EditFieldsSection/EditExtensionSection/EditRelatedListSection/EditWriteFormSection gain actions; EditRelatedListSection gains emptyState; number fields gain grouping (fw#3234)",
6
+ "detail": "All four Edit*Section spec types accept an optional `actions?: readonly RowAction[]`, resolved and rendered in that section's own title row. EditRelatedListSection additionally accepts `emptyState?: { title, description?, action? }` for its zero-rows state. A number field spec accepts `grouping?: boolean` (default true) — false renders without thousands separators. Every projectionDetail/entityEdit-tabs screen/section/emptyState action must resolve an icon (declared `icon`, or the framework's id-derived default via resolveActionIcon) — checked by the boot validator (see @cosmicdrift/kumiko-framework's changes.json).",
7
+ "migration": "Additive — every field is optional. An existing screen/section/emptyState action on a projectionDetail (or entityEdit's section-level actions) whose id resolves no icon and declares none itself now fails boot; give it an explicit `icon` or an id the shared action-icon map already resolves."
8
+ },
2
9
  {
3
10
  "version": "0.290.0",
4
11
  "type": "improvement",
@@ -1,4 +1,5 @@
1
- import type { ZodType, z } from "zod";
1
+ import type * as z from "zod";
2
+ import type { ZodType } from "zod";
2
3
  import type { KumikoEventTypeMap } from "./event-type-map";
3
4
  import type {
4
5
  AccessRule,
package/src/feature.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { ZodType, z } from "zod";
1
+ import type * as z from "zod";
2
+ import type { ZodType } from "zod";
2
3
  import type {
3
4
  ConfigKeyDefinition,
4
5
  ConfigKeyHandle,
package/src/fields.ts CHANGED
@@ -395,6 +395,10 @@ export type NumberFieldDef = {
395
395
  /** Display-only suffix: a static string, or a sibling field name whose
396
396
  * value supplies it. Never converts the stored number. */
397
397
  readonly unit?: string | { readonly field: string };
398
+ /** Thousands-grouping in the rendered display (Intl `useGrouping`).
399
+ * Default `true`. Set `false` for values that read wrong grouped (e.g. a
400
+ * model year: "2021", not "2.021"). Never affects the stored value. */
401
+ readonly grouping?: boolean;
398
402
  } & ResolvedPiiFlags;
399
403
 
400
404
  /**
package/src/nav-icon.ts CHANGED
@@ -8,88 +8,97 @@
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.
11
- export type NavIconKey =
12
- | "dashboard"
13
- | "layout-grid"
14
- | "book-open"
15
- | "clipboard-list"
16
- | "package"
17
- | "gauge"
18
- | "list"
19
- | "table"
20
- | "layers"
21
- | "building"
22
- | "calculator"
23
- | "wallet"
24
- | "coins"
25
- | "credit-card"
26
- | "piggy-bank"
27
- | "receipt"
28
- | "chart"
29
- | "bar-chart"
30
- | "trending"
31
- | "sparkles"
32
- | "wand"
33
- | "calendar"
34
- | "file"
35
- | "folder"
36
- | "folder-open"
37
- | "home"
38
- | "bell"
39
- | "shield"
40
- | "shield-check"
41
- | "send"
42
- | "settings"
43
- | "users"
44
- | "user"
45
- | "search"
46
- | "tag"
47
- | "key"
48
- | "link"
49
- | "palette"
50
- | "share"
51
- | "server"
52
- | "mail"
53
- | "lock"
54
- | "hash"
55
- | "download"
56
- | "upload"
57
- | "rocket"
58
- | "plus"
59
- | "languages"
60
- | "trash"
61
- | "x"
62
- | "check"
63
- | "arrow-left"
64
- | "arrow-right"
65
- | "copy"
66
- | "pencil"
67
- | "eye"
68
- | "eye-off"
69
- | "filter"
70
- | "refresh"
71
- | "more-horizontal"
72
- | "more-vertical"
73
- | "external-link"
74
- | "chevron-down"
75
- | "chevron-right"
76
- | "save"
77
- | "undo"
78
- | "archive"
79
- | "star"
80
- | "flag"
81
- | "clock"
82
- | "map-pin"
83
- | "phone"
84
- | "printer"
85
- | "alert-triangle"
86
- | "info"
87
- | "check-circle"
88
- | "x-circle"
89
- | "loader"
90
- | "mic"
91
- | "circle-stop"
92
- | "image";
11
+ //
12
+ // Declared as a runtime tuple (not just a type) so callers that sit outside
13
+ // this repo's `tsc --build` project graph (e.g. samples, external consumers
14
+ // resolving from a stale published type) still get a runtime-checkable
15
+ // vocabulary — the boot-validator's validateActionHasIcon checks a declared
16
+ // icon against NAV_ICON_KEYS instead of trusting "not undefined".
17
+ export const NAV_ICON_KEYS = [
18
+ "dashboard",
19
+ "layout-grid",
20
+ "book-open",
21
+ "clipboard-list",
22
+ "package",
23
+ "gauge",
24
+ "list",
25
+ "table",
26
+ "layers",
27
+ "building",
28
+ "calculator",
29
+ "wallet",
30
+ "coins",
31
+ "credit-card",
32
+ "piggy-bank",
33
+ "receipt",
34
+ "chart",
35
+ "bar-chart",
36
+ "trending",
37
+ "sparkles",
38
+ "wand",
39
+ "calendar",
40
+ "file",
41
+ "folder",
42
+ "folder-open",
43
+ "home",
44
+ "bell",
45
+ "shield",
46
+ "shield-check",
47
+ "send",
48
+ "settings",
49
+ "users",
50
+ "user",
51
+ "search",
52
+ "tag",
53
+ "key",
54
+ "link",
55
+ "palette",
56
+ "share",
57
+ "server",
58
+ "mail",
59
+ "lock",
60
+ "hash",
61
+ "download",
62
+ "upload",
63
+ "rocket",
64
+ "plus",
65
+ "languages",
66
+ "trash",
67
+ "x",
68
+ "check",
69
+ "arrow-left",
70
+ "arrow-right",
71
+ "copy",
72
+ "pencil",
73
+ "eye",
74
+ "eye-off",
75
+ "filter",
76
+ "refresh",
77
+ "more-horizontal",
78
+ "more-vertical",
79
+ "external-link",
80
+ "chevron-down",
81
+ "chevron-right",
82
+ "save",
83
+ "undo",
84
+ "archive",
85
+ "star",
86
+ "flag",
87
+ "clock",
88
+ "map-pin",
89
+ "phone",
90
+ "printer",
91
+ "alert-triangle",
92
+ "info",
93
+ "check-circle",
94
+ "x-circle",
95
+ "loader",
96
+ "mic",
97
+ "circle-stop",
98
+ "image",
99
+ ] as const;
100
+
101
+ export type NavIconKey = (typeof NAV_ICON_KEYS)[number];
93
102
 
94
103
  // Alias kept because the union stopped being nav-only — actions and fields
95
104
  // (added on this branch) also key their icons against it.
package/src/screen.ts CHANGED
@@ -55,9 +55,11 @@ export interface FieldFormatRegistry {
55
55
  boolean: { readonly trueLabel?: string; readonly falseLabel?: string };
56
56
  currency: { readonly symbol?: string };
57
57
  priority: { readonly emptyLabel?: string; readonly prefix?: string };
58
- number: { readonly locale?: string };
59
- decimal: { readonly locale?: string };
60
- bigInt: { readonly locale?: string };
58
+ /** `grouping: false` renders without thousands-separators (e.g. a model
59
+ * year: "2021", not "2.021"). Default `true`. */
60
+ number: { readonly locale?: string; readonly grouping?: boolean };
61
+ decimal: { readonly locale?: string; readonly grouping?: boolean };
62
+ bigInt: { readonly locale?: string; readonly grouping?: boolean };
61
63
  // Value is percent *points* (12 → "12 %"), not a 0..1 ratio — Intl
62
64
  // style:"unit"/unit:"percent" does NOT multiply by 100 (unlike style:"percent").
63
65
  unit: {
@@ -925,6 +927,11 @@ export type EditFieldsSection = {
925
927
  * Ignored outside tabs mode or when the field's value is not a finite
926
928
  * number. */
927
929
  readonly countField?: string;
930
+ /** Rendered top-right in the section's Card title row — same
931
+ * navigate/drawer/writeHandler dispatch as `ProjectionDetailScreenDefinition.actions`,
932
+ * evaluated against the same record. Every action must resolve an icon
933
+ * (declared or id-derived); the boot-validator rejects one that doesn't. */
934
+ readonly actions?: readonly RowAction[];
928
935
  };
929
936
 
930
937
  export type EditExtensionSection = {
@@ -951,6 +958,11 @@ export type EditExtensionSection = {
951
958
  * Ignored outside tabs mode or when the field's value is not a finite
952
959
  * number. */
953
960
  readonly countField?: string;
961
+ /** Rendered top-right in the section's Card title row — same
962
+ * navigate/drawer/writeHandler dispatch as `ProjectionDetailScreenDefinition.actions`,
963
+ * evaluated against the same record. Every action must resolve an icon
964
+ * (declared or id-derived); the boot-validator rejects one that doesn't. */
965
+ readonly actions?: readonly RowAction[];
954
966
  };
955
967
 
956
968
  // Read-only list of related records, driven by its own query — for a
@@ -1019,6 +1031,22 @@ export type EditRelatedListSection = {
1019
1031
  * Ignored outside tabs mode or when the field's value is not a finite
1020
1032
  * number. */
1021
1033
  readonly countField?: string;
1034
+ /** Rendered top-right in the section's Card title row — same
1035
+ * navigate/drawer/writeHandler dispatch as `ProjectionDetailScreenDefinition.actions`,
1036
+ * evaluated against the parent record. Every action must resolve an icon
1037
+ * (declared or id-derived); the boot-validator rejects one that doesn't.
1038
+ * Distinct from `toolbarActions` (rendered inside the DataTable toolbar) —
1039
+ * this is the Card-level title-row action. */
1040
+ readonly actions?: readonly RowAction[];
1041
+ /** Rendered inside the section's Card in place of the table when this
1042
+ * section's query returns zero rows. Omitted: the DataTable default
1043
+ * ("kumiko.list.no-entries") applies. `action`'s icon must resolve, same
1044
+ * as `actions` above. */
1045
+ readonly emptyState?: {
1046
+ readonly title: string;
1047
+ readonly description?: string;
1048
+ readonly action?: RowAction;
1049
+ };
1022
1050
  };
1023
1051
 
1024
1052
  // A declarative, self-persisting form section for `projectionDetail`
@@ -1057,6 +1085,11 @@ export type EditWriteFormSection = {
1057
1085
  readonly handler: string;
1058
1086
  /** i18n-key for the submit button. Default: "kumiko.actions.save". */
1059
1087
  readonly submitLabel?: string;
1088
+ /** Rendered top-right in the section's Card title row — same
1089
+ * navigate/drawer/writeHandler dispatch as `ProjectionDetailScreenDefinition.actions`,
1090
+ * evaluated against the same record. Every action must resolve an icon
1091
+ * (declared or id-derived); the boot-validator rejects one that doesn't. */
1092
+ readonly actions?: readonly RowAction[];
1060
1093
  };
1061
1094
 
1062
1095
  // Max width of the form container (see FormScreenShell in renderer-web).
@@ -24,6 +24,12 @@ export type SearchOptions = {
24
24
 
25
25
  export type SearchAdapter = {
26
26
  configure(tenantId: TenantId, config: SearchAdapterConfig): Promise<void>;
27
+ // The adapter is built before the registry exists (app boot order), so it
28
+ // can't derive this itself. The framework hands it the registry-derived
29
+ // config once the registry is available; the adapter uses it to configure
30
+ // each tenant index lazily on that tenant's first access instead of
31
+ // requiring every app to call `configure` per tenant up front.
32
+ setDefaultConfig?(config: SearchAdapterConfig): void;
27
33
  index(tenantId: TenantId, doc: SearchDocument): Promise<void>;
28
34
  search(tenantId: TenantId, query: string, options?: SearchOptions): Promise<SearchResult[]>;
29
35
  remove(tenantId: TenantId, entityType: string, entityId: EntityId): Promise<void>;