@cosmicdrift/kumiko-types 0.306.0 → 0.308.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 +5 -1
- package/src/action-icon.ts +57 -0
- package/src/changes.json +7 -0
- package/src/define-handler.ts +2 -1
- package/src/event-store-types.ts +15 -0
- package/src/feature.ts +2 -1
- package/src/fields.ts +4 -0
- package/src/handlers.ts +15 -2
- package/src/nav-icon.ts +91 -82
- package/src/screen.ts +36 -3
- package/src/search-adapter.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.308.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"
|
|
@@ -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",
|
package/src/define-handler.ts
CHANGED
package/src/event-store-types.ts
CHANGED
|
@@ -45,8 +45,23 @@ export type EventMetadata = {
|
|
|
45
45
|
// every new row carries both, falling back to UNATTRIBUTED_ORIGIN.
|
|
46
46
|
readonly feature?: string;
|
|
47
47
|
readonly handler?: string;
|
|
48
|
+
// Only set under an anonymous root without public-intake, so event-triggered
|
|
49
|
+
// jobs keep the personal-data gate.
|
|
50
|
+
readonly writeOrigin?: WriteOrigin;
|
|
48
51
|
};
|
|
49
52
|
|
|
53
|
+
export type WriteOrigin = {
|
|
54
|
+
readonly rootHandler: string;
|
|
55
|
+
readonly anonymousRoot: boolean;
|
|
56
|
+
// Only a write-handler root can declare public-intake; a query/stream root never does.
|
|
57
|
+
readonly publicIntake: boolean;
|
|
58
|
+
readonly viaJob?: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function isPersonalDataGated(origin: WriteOrigin): boolean {
|
|
62
|
+
return origin.anonymousRoot && !origin.publicIntake;
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
// Stamped when an append runs outside any attributed scope (seed scripts,
|
|
51
66
|
// tests, direct event-store use). A distinct value, not an omission —
|
|
52
67
|
// omitted would be indistinguishable from a pre-#3043 row.
|
package/src/feature.ts
CHANGED
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/handlers.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { ConfigAccessor, ConfigAccessorFactory, ConfigResolver } from "./co
|
|
|
4
4
|
import type { DbConnection } from "./db-connection";
|
|
5
5
|
import type { DerivativesContext } from "./derivatives-types";
|
|
6
6
|
import type { EntityCache } from "./entity-cache";
|
|
7
|
+
import type { WriteOrigin } from "./event-store-types";
|
|
7
8
|
import type { KumikoEventTypeMap } from "./event-type-map";
|
|
8
9
|
import type { FileContext } from "./file-handle-types";
|
|
9
10
|
import type { FileProviderResolver } from "./file-provider-resolver-types";
|
|
@@ -260,8 +261,20 @@ export type JobRunnerRef = {
|
|
|
260
261
|
// singleton, not bound to one caller's identity. Only write+queryAs — no
|
|
261
262
|
// .query/.stream/.command/.batch/.resolveAuthClaims, that stays Dispatcher-only.
|
|
262
263
|
export type DispatchWriteRef = {
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
// A job under a gated anonymous origin passes it, else the target would
|
|
265
|
+
// reset to the job's own non-anonymous root.
|
|
266
|
+
readonly write: (
|
|
267
|
+
user: SessionUser,
|
|
268
|
+
qn: string,
|
|
269
|
+
payload: unknown,
|
|
270
|
+
inheritedOrigin?: WriteOrigin,
|
|
271
|
+
) => Promise<WriteResult>;
|
|
272
|
+
readonly queryAs: (
|
|
273
|
+
user: SessionUser,
|
|
274
|
+
qn: string,
|
|
275
|
+
payload: unknown,
|
|
276
|
+
inheritedOrigin?: WriteOrigin,
|
|
277
|
+
) => Promise<unknown>;
|
|
265
278
|
// Builds a tenant-scoped MemberReader — one per JobContext.queryAsMember
|
|
266
279
|
// caller (job-runner.ts lazily creates one per job run).
|
|
267
280
|
readonly createMemberReader: (tenantId: TenantId) => MemberReader;
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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).
|
package/src/search-adapter.ts
CHANGED
|
@@ -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>;
|