@cosmicdrift/kumiko-types 0.197.0 → 0.198.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/config.ts +7 -6
- package/src/derivatives-types.ts +6 -4
- package/src/handlers.ts +33 -1
- package/src/identifiers.ts +9 -3
- package/src/nav-icon.ts +56 -0
- package/src/nav.ts +8 -5
- package/src/screen.ts +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.198.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>",
|
|
@@ -162,6 +162,10 @@
|
|
|
162
162
|
"types": "./src/nav.ts",
|
|
163
163
|
"default": "./src/nav.ts"
|
|
164
164
|
},
|
|
165
|
+
"./nav-icon": {
|
|
166
|
+
"types": "./src/nav-icon.ts",
|
|
167
|
+
"default": "./src/nav-icon.ts"
|
|
168
|
+
},
|
|
165
169
|
"./ownership": {
|
|
166
170
|
"types": "./src/ownership.ts",
|
|
167
171
|
"default": "./src/ownership.ts"
|
package/src/config.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ConcurrencyMode } from "./concurrency-mode";
|
|
|
3
3
|
import type { ConfigScope } from "./config-scope";
|
|
4
4
|
import type { DbConnection } from "./db-connection";
|
|
5
5
|
import type { FieldDefinition } from "./fields";
|
|
6
|
-
import type {
|
|
6
|
+
import type { JobContext } from "./handlers";
|
|
7
7
|
import type {
|
|
8
8
|
PostDeleteHookFn,
|
|
9
9
|
PostSaveHookFn,
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
PreSaveHookFn,
|
|
13
13
|
} from "./hooks";
|
|
14
14
|
import type { TenantId } from "./identifiers";
|
|
15
|
+
import type { NavIconKey } from "./nav-icon";
|
|
15
16
|
import type { TenantDb } from "./tenant-db-types";
|
|
16
17
|
|
|
17
18
|
// --- Config ---
|
|
@@ -154,12 +155,12 @@ export type ConfigKeyDefinition<T extends ConfigKeyType = ConfigKeyType> = {
|
|
|
154
155
|
readonly group?: string;
|
|
155
156
|
};
|
|
156
157
|
|
|
157
|
-
// Label
|
|
158
|
-
//
|
|
159
|
-
//
|
|
158
|
+
// Label carrier for the settings hub. `title` is an i18n key (no literal —
|
|
159
|
+
// guarded), `icon` a NavIconKey for the nav entry, `order` the sort weight
|
|
160
|
+
// within its audience group.
|
|
160
161
|
export type ConfigMask = {
|
|
161
162
|
readonly title: string;
|
|
162
|
-
readonly icon?:
|
|
163
|
+
readonly icon?: NavIconKey;
|
|
163
164
|
readonly order?: number;
|
|
164
165
|
};
|
|
165
166
|
|
|
@@ -335,7 +336,7 @@ export type JobRunIn = Exclude<RunIn, "both">;
|
|
|
335
336
|
|
|
336
337
|
// --- Jobs ---
|
|
337
338
|
|
|
338
|
-
export type JobHandlerFn = (payload: Record<string, unknown>, context:
|
|
339
|
+
export type JobHandlerFn = (payload: Record<string, unknown>, context: JobContext) => Promise<void>;
|
|
339
340
|
|
|
340
341
|
export type JobTrigger =
|
|
341
342
|
// `on` akzeptiert ein einzelnes Handler-Ref ODER eine Liste. Multi-
|
package/src/derivatives-types.ts
CHANGED
|
@@ -35,10 +35,12 @@ export type VariantSpec = {
|
|
|
35
35
|
//
|
|
36
36
|
// The renderer must produce `spec.format` when set, and the source's own
|
|
37
37
|
// format otherwise. It never reports back what it produced: the caller
|
|
38
|
-
// derives the output mimeType from the spec
|
|
39
|
-
// derivatives-context.ts), because a cache
|
|
40
|
-
//
|
|
41
|
-
//
|
|
38
|
+
// derives the output mimeType from the spec plus the source FileRef's
|
|
39
|
+
// mimeType (see outputMimeType in derivatives-context.ts), because a cache
|
|
40
|
+
// hit has no renderer run to read a mimeType off. When `spec.format` is
|
|
41
|
+
// unset, the source mimeType is client-controlled, so outputMimeType
|
|
42
|
+
// allowlists it against known-safe raster types instead of passing it
|
|
43
|
+
// through verbatim (#2021).
|
|
42
44
|
export type DerivativeRendererPlugin = {
|
|
43
45
|
readonly render: (
|
|
44
46
|
input: Uint8Array,
|
package/src/handlers.ts
CHANGED
|
@@ -187,6 +187,17 @@ export type JobRunnerRef = {
|
|
|
187
187
|
): Promise<void>;
|
|
188
188
|
};
|
|
189
189
|
|
|
190
|
+
// Minimal interface a JobRunner attaches via attachDispatcher() to reach the
|
|
191
|
+
// write pipeline — framework-owned, concrete type wraps the dispatcher's
|
|
192
|
+
// executeWrite/executeQuery. Unlike HandlerContext.write (identity implicit
|
|
193
|
+
// via closure), both take an explicit user: a JobRunner is a boot-time
|
|
194
|
+
// singleton, not bound to one caller's identity. Only write+queryAs — no
|
|
195
|
+
// .query/.stream/.command/.batch/.resolveAuthClaims, that stays Dispatcher-only.
|
|
196
|
+
export type DispatchWriteRef = {
|
|
197
|
+
readonly write: (user: SessionUser, qn: string, payload: unknown) => Promise<WriteResult>;
|
|
198
|
+
readonly queryAs: (user: SessionUser, qn: string, payload: unknown) => Promise<unknown>;
|
|
199
|
+
};
|
|
200
|
+
|
|
190
201
|
// Priority levels for notifications
|
|
191
202
|
export type NotifyPriority = "critical" | "normal" | "low";
|
|
192
203
|
|
|
@@ -544,13 +555,34 @@ export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedCon
|
|
|
544
555
|
readonly resolveAuthClaims: (user: SessionUser) => Promise<Record<string, unknown>>;
|
|
545
556
|
};
|
|
546
557
|
|
|
547
|
-
// Job execution: db + registry + systemUser + logging guaranteed
|
|
558
|
+
// Job execution: db + registry + systemUser + logging guaranteed, plus a
|
|
559
|
+
// write path into the same dispatcher pipeline write-handlers use.
|
|
560
|
+
//
|
|
561
|
+
// write/queryAs throw until JobRunner.attachDispatcher() has run — a call
|
|
562
|
+
// this early is a framework boot bug, not a job-author mistake.
|
|
563
|
+
//
|
|
564
|
+
// Identity is implicit: write runs as the job's own systemUser, tenant-
|
|
565
|
+
// scoped via the job's _tenantId (falls back to SYSTEM_TENANT_ID for jobs
|
|
566
|
+
// with no tenant context, e.g. plain cron jobs). A job that writes without
|
|
567
|
+
// fanning out per-tenant (`_perTenant:` trigger) silently writes into the
|
|
568
|
+
// system tenant instead of each tenant it may have meant to reach — no
|
|
569
|
+
// guard against this, only this warning.
|
|
548
570
|
export type JobContext = SharedContextFields & {
|
|
549
571
|
readonly db: DbConnection;
|
|
550
572
|
readonly registry: Registry;
|
|
551
573
|
readonly systemUser: SessionUser;
|
|
552
574
|
readonly log: Logger;
|
|
553
575
|
readonly triggeredBy: { readonly id: string; readonly tenantId: TenantId } | null;
|
|
576
|
+
readonly write: (qn: string, payload: unknown) => Promise<WriteResult>;
|
|
577
|
+
readonly queryAs: (user: SessionUser, qn: string, payload: unknown) => Promise<unknown>;
|
|
578
|
+
// Multi-trigger jobs (`on: [...]`) use this to tell which trigger fired —
|
|
579
|
+
// undefined for cron/manual jobs. Mirrors AppContext.triggerName.
|
|
580
|
+
readonly triggerName?: string;
|
|
581
|
+
// Fallback tenant/user when systemUser doesn't carry it (tenant-less cron
|
|
582
|
+
// jobs). Mirrors AppContext._tenantId/_userId — see the tenant-scoping
|
|
583
|
+
// footgun above.
|
|
584
|
+
readonly _tenantId?: TenantId;
|
|
585
|
+
readonly _userId?: string | undefined;
|
|
554
586
|
};
|
|
555
587
|
|
|
556
588
|
// --- Handler Functions ---
|
package/src/identifiers.ts
CHANGED
|
@@ -15,6 +15,14 @@ export type TenantId = string;
|
|
|
15
15
|
// the system boundary.
|
|
16
16
|
const TENANT_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
17
17
|
|
|
18
|
+
// Shared shape check for any UUID-formatted identifier crossing an
|
|
19
|
+
// untrusted boundary (fileRefId, entity ids, ...) — same loose,
|
|
20
|
+
// version-agnostic regex as TENANT_ID_REGEX (v4/v7/nil all match), kept as
|
|
21
|
+
// one predicate so call sites don't grow their own copy of the shape.
|
|
22
|
+
export function isUuid(value: unknown): value is string {
|
|
23
|
+
return typeof value === "string" && TENANT_ID_REGEX.test(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
// Validates a candidate string against the tenantId format and returns it
|
|
19
27
|
// as a TenantId, or `null` when it doesn't match. Use at every system
|
|
20
28
|
// boundary that admits untrusted input (HTTP headers, cookies, query
|
|
@@ -22,9 +30,7 @@ const TENANT_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-
|
|
|
22
30
|
// of the rejection shape — middleware returns 400, batch jobs may filter
|
|
23
31
|
// + log, and unit tests don't need a try/catch.
|
|
24
32
|
export function parseTenantId(value: unknown): TenantId | null {
|
|
25
|
-
|
|
26
|
-
if (!TENANT_ID_REGEX.test(value)) return null;
|
|
27
|
-
return value;
|
|
33
|
+
return isUuid(value) ? value : null;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
// "System-scope" tenant marker: handlers carry this tenantId when the event
|
package/src/nav-icon.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Closed vocabulary of nav icon keys. NavDefinition.icon, ScreenNavSugar.icon
|
|
2
|
+
// and ConfigMask.icon are all typed against this union, so an unregistered
|
|
3
|
+
// key is a compile error at the r.nav()/r.screen()/config-mask call site
|
|
4
|
+
// instead of a silent missing-icon at runtime.
|
|
5
|
+
//
|
|
6
|
+
// The renderer-web NAV_ICONS map (packages/renderer-web/src/layout/
|
|
7
|
+
// nav-tree.tsx) is checked against this same union via `satisfies`, so the
|
|
8
|
+
// two can't drift — add a key here only together with its lucide-react entry
|
|
9
|
+
// there, and vice versa.
|
|
10
|
+
export type NavIconKey =
|
|
11
|
+
| "dashboard"
|
|
12
|
+
| "layout-grid"
|
|
13
|
+
| "book-open"
|
|
14
|
+
| "clipboard-list"
|
|
15
|
+
| "package"
|
|
16
|
+
| "gauge"
|
|
17
|
+
| "list"
|
|
18
|
+
| "table"
|
|
19
|
+
| "layers"
|
|
20
|
+
| "building"
|
|
21
|
+
| "calculator"
|
|
22
|
+
| "wallet"
|
|
23
|
+
| "coins"
|
|
24
|
+
| "credit-card"
|
|
25
|
+
| "piggy-bank"
|
|
26
|
+
| "receipt"
|
|
27
|
+
| "chart"
|
|
28
|
+
| "bar-chart"
|
|
29
|
+
| "trending"
|
|
30
|
+
| "sparkles"
|
|
31
|
+
| "wand"
|
|
32
|
+
| "calendar"
|
|
33
|
+
| "file"
|
|
34
|
+
| "folder"
|
|
35
|
+
| "folder-open"
|
|
36
|
+
| "home"
|
|
37
|
+
| "bell"
|
|
38
|
+
| "shield"
|
|
39
|
+
| "settings"
|
|
40
|
+
| "users"
|
|
41
|
+
| "user"
|
|
42
|
+
| "search"
|
|
43
|
+
| "tag"
|
|
44
|
+
| "key"
|
|
45
|
+
| "link"
|
|
46
|
+
| "palette"
|
|
47
|
+
| "share"
|
|
48
|
+
| "server"
|
|
49
|
+
| "mail"
|
|
50
|
+
| "lock"
|
|
51
|
+
| "hash"
|
|
52
|
+
| "download"
|
|
53
|
+
| "upload"
|
|
54
|
+
| "rocket"
|
|
55
|
+
| "plus"
|
|
56
|
+
| "languages";
|
package/src/nav.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { AccessRule } from "./handlers";
|
|
2
|
+
import type { NavIconKey } from "./nav-icon";
|
|
2
3
|
import type { TargetRef } from "./target-ref";
|
|
3
4
|
import type { TreeAction } from "./tree-node";
|
|
4
5
|
|
|
6
|
+
export type { NavIconKey } from "./nav-icon";
|
|
7
|
+
|
|
5
8
|
// Nav entry declaration. Every feature that wants to appear in the app's
|
|
6
9
|
// navigation tree registers one or more entries via r.nav(). The engine
|
|
7
10
|
// keeps the list flat — ui-core's resolveNavigation assembles the parent/
|
|
@@ -22,10 +25,10 @@ export type NavDefinition = {
|
|
|
22
25
|
// i18n translation key. Resolved at render time by the renderer's
|
|
23
26
|
// useTranslation hook; engine keeps it opaque.
|
|
24
27
|
readonly label: string;
|
|
25
|
-
// Icon key
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
readonly icon?:
|
|
28
|
+
// Icon key from the closed NavIconKey vocabulary (see nav-icon.ts). Every
|
|
29
|
+
// renderer's icon map is checked against the same union, so a typo'd key
|
|
30
|
+
// is a compile error here rather than a missing icon at runtime.
|
|
31
|
+
readonly icon?: NavIconKey;
|
|
29
32
|
// Qualified name of a parent nav entry ("<feature>:nav:<id>"). Omit for
|
|
30
33
|
// top-level entries. Boot-validator rejects cycles + dangling refs.
|
|
31
34
|
readonly parent?: string;
|
|
@@ -113,7 +116,7 @@ export type ContentCollectionDefinition = {
|
|
|
113
116
|
readonly variableSchema?: Readonly<Record<string, string>>;
|
|
114
117
|
readonly nav: {
|
|
115
118
|
readonly label: string;
|
|
116
|
-
readonly icon?:
|
|
119
|
+
readonly icon?: NavIconKey;
|
|
117
120
|
readonly parent?: string;
|
|
118
121
|
readonly order?: number;
|
|
119
122
|
// Visibility of the nav node. Leave unset — it then follows the
|
package/src/screen.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FieldDefinition } from "./fields";
|
|
2
2
|
import type { AccessRule } from "./handlers";
|
|
3
|
+
import type { NavIconKey } from "./nav-icon";
|
|
3
4
|
|
|
4
5
|
// Screen definitions describe how a feature surfaces data to the user.
|
|
5
6
|
// Pure data — the engine stores these verbatim and ui-core / the renderer
|
|
@@ -704,6 +705,16 @@ export type CustomScreenDefinition = {
|
|
|
704
705
|
/** Parent list screen for breadcrumb when this detail is not in nav. */
|
|
705
706
|
readonly listScreenId?: string;
|
|
706
707
|
readonly access?: AccessRule;
|
|
708
|
+
/** This screen is registered without a self-owned `r.nav()` entry by
|
|
709
|
+
* design — an app opts in by navving it explicitly (e.g. a settings-area
|
|
710
|
+
* self-service screen). `createKumikoApp`'s boot diagnostic
|
|
711
|
+
* (kumiko-framework#2025) skips dormant screens instead of flagging
|
|
712
|
+
* every consumer that doesn't mount the client plugin as a false
|
|
713
|
+
* positive (kumiko-framework#2034). Only set this on screens the
|
|
714
|
+
* feature itself never navs — a screen the feature DOES nav still needs
|
|
715
|
+
* its client plugin mounted by every consumer, and should keep
|
|
716
|
+
* triggering the diagnostic if it's missing. */
|
|
717
|
+
readonly dormant?: boolean;
|
|
707
718
|
};
|
|
708
719
|
|
|
709
720
|
// --- configEdit ---
|
|
@@ -790,7 +801,7 @@ export type ScreenSlots = {
|
|
|
790
801
|
// declare a standalone `r.nav()` entry instead.
|
|
791
802
|
export type ScreenNavSugar = {
|
|
792
803
|
readonly label: string;
|
|
793
|
-
readonly icon?:
|
|
804
|
+
readonly icon?: NavIconKey;
|
|
794
805
|
readonly parent?: string;
|
|
795
806
|
readonly order?: number;
|
|
796
807
|
};
|