@cosmicdrift/kumiko-types 0.270.0 → 0.273.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 +1 -1
- package/src/define-handler.ts +5 -5
- package/src/entity-handlers.ts +18 -15
- package/src/feature.ts +4 -4
- package/src/handlers.ts +42 -13
- package/src/screen.ts +93 -1
- package/src/step.ts +3 -1
- package/src/tenant-db-types.ts +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.273.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/define-handler.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
EscapeHatchDeclaration,
|
|
7
7
|
HandlerContext,
|
|
8
8
|
QueryEvent,
|
|
9
|
-
|
|
9
|
+
RateLimitDeclaration,
|
|
10
10
|
WriteEvent,
|
|
11
11
|
WriteResult,
|
|
12
12
|
} from "./handlers";
|
|
@@ -39,7 +39,7 @@ export type WriteHandlerDefinition<
|
|
|
39
39
|
readonly description?: string;
|
|
40
40
|
readonly agent?: AgentHandlerHints;
|
|
41
41
|
readonly unsafeSkipTransitionGuard?: boolean;
|
|
42
|
-
readonly rateLimit?:
|
|
42
|
+
readonly rateLimit?: RateLimitDeclaration;
|
|
43
43
|
readonly escapeHatch?: EscapeHatchDeclaration;
|
|
44
44
|
readonly handler: (
|
|
45
45
|
event: WriteEvent<z.infer<TSchema>>,
|
|
@@ -67,7 +67,7 @@ export type WriteHandlerInput<
|
|
|
67
67
|
readonly description?: string;
|
|
68
68
|
readonly agent?: AgentHandlerHints;
|
|
69
69
|
readonly unsafeSkipTransitionGuard?: boolean;
|
|
70
|
-
readonly rateLimit?:
|
|
70
|
+
readonly rateLimit?: RateLimitDeclaration;
|
|
71
71
|
readonly escapeHatch?: EscapeHatchDeclaration;
|
|
72
72
|
} & (
|
|
73
73
|
| {
|
|
@@ -96,7 +96,7 @@ export type QueryHandlerDefinition<
|
|
|
96
96
|
readonly access: AccessRule;
|
|
97
97
|
readonly description?: string;
|
|
98
98
|
readonly agent?: AgentHandlerHints;
|
|
99
|
-
readonly rateLimit?:
|
|
99
|
+
readonly rateLimit?: RateLimitDeclaration;
|
|
100
100
|
readonly escapeHatch?: EscapeHatchDeclaration;
|
|
101
101
|
readonly handler: (
|
|
102
102
|
query: QueryEvent<z.infer<TSchema>>,
|
|
@@ -125,7 +125,7 @@ export type StreamHandlerDefinition<
|
|
|
125
125
|
readonly name: TName;
|
|
126
126
|
readonly schema: TSchema;
|
|
127
127
|
readonly access: AccessRule;
|
|
128
|
-
readonly rateLimit?:
|
|
128
|
+
readonly rateLimit?: RateLimitDeclaration;
|
|
129
129
|
// Stream handlers can't reach db.global() (that gate is write-only), but
|
|
130
130
|
// they can still switch identity to SYSTEM via ctx.queryAs — this opts
|
|
131
131
|
// in, same contract as WriteHandlerDefinition.escapeHatch.
|
package/src/entity-handlers.ts
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import type { EntityDefinition } from "./fields";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
AccessRule,
|
|
4
|
+
AgentHandlerHints,
|
|
5
|
+
EscapeHatchDeclaration,
|
|
6
|
+
QueryHandlerDef,
|
|
7
|
+
WriteHandlerDef,
|
|
8
|
+
} from "./handlers";
|
|
3
9
|
|
|
4
10
|
export type EntityHandlerOptions = {
|
|
5
11
|
readonly access: AccessRule;
|
|
6
12
|
readonly description?: string;
|
|
7
13
|
readonly agent?: AgentHandlerHints;
|
|
8
|
-
/**
|
|
9
|
-
* SystemAdmin-only operator handler over an
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
* entity write-ownership rules that compare against the acting user's
|
|
20
|
-
* tenant, so `access` is the only remaining gate — grant it to operator
|
|
21
|
-
* roles only. */
|
|
14
|
+
/** Lifts row filtering for this ONE handler across every tenant instead of
|
|
15
|
+
* the caller's own — for a SystemAdmin-only operator handler over an
|
|
16
|
+
* otherwise tenant-scoped entity. Every use reports an
|
|
17
|
+
* `acknowledge-cross-tenant` escape-hatch audit event. On write handlers
|
|
18
|
+
* update/delete/restore address the target row's own tenant stream.
|
|
19
|
+
* `access` stays the only caller gate. Unlike `escapeHatch` on a
|
|
20
|
+
* hand-written handler this does NOT grant `ctx.db.unsafeRaw`,
|
|
21
|
+
* `db.global()` writes or identity switches. */
|
|
22
|
+
readonly escapeHatch?: EscapeHatchDeclaration;
|
|
23
|
+
/** @deprecated Use `escapeHatch: { reason }` — removed in a future release;
|
|
24
|
+
* run `scripts/codemod/migrate-cross-tenant.ts`. */
|
|
22
25
|
readonly crossTenant?: boolean;
|
|
23
26
|
};
|
|
24
27
|
|
package/src/feature.ts
CHANGED
|
@@ -45,7 +45,7 @@ import type {
|
|
|
45
45
|
QualifiedEventName,
|
|
46
46
|
QueryHandlerDef,
|
|
47
47
|
QueryHandlerFn,
|
|
48
|
-
|
|
48
|
+
RateLimitDeclaration,
|
|
49
49
|
StreamHandlerDef,
|
|
50
50
|
StreamHandlerFn,
|
|
51
51
|
WriteHandlerDef,
|
|
@@ -457,7 +457,7 @@ export type FeatureRegistrar<TFeature extends string = string> = {
|
|
|
457
457
|
handler: WriteHandlerFn<z.infer<TSchema>>,
|
|
458
458
|
options: {
|
|
459
459
|
access: AccessRule;
|
|
460
|
-
rateLimit?:
|
|
460
|
+
rateLimit?: RateLimitDeclaration;
|
|
461
461
|
description?: string;
|
|
462
462
|
agent?: AgentHandlerHints;
|
|
463
463
|
escapeHatch?: EscapeHatchDeclaration;
|
|
@@ -473,7 +473,7 @@ export type FeatureRegistrar<TFeature extends string = string> = {
|
|
|
473
473
|
handler: QueryHandlerFn<z.infer<TSchema>>,
|
|
474
474
|
options: {
|
|
475
475
|
access: AccessRule;
|
|
476
|
-
rateLimit?:
|
|
476
|
+
rateLimit?: RateLimitDeclaration;
|
|
477
477
|
outputSchema?: ZodType;
|
|
478
478
|
description?: string;
|
|
479
479
|
agent?: AgentHandlerHints;
|
|
@@ -490,7 +490,7 @@ export type FeatureRegistrar<TFeature extends string = string> = {
|
|
|
490
490
|
handler: StreamHandlerFn<z.infer<TSchema>>,
|
|
491
491
|
options: {
|
|
492
492
|
access: AccessRule;
|
|
493
|
-
rateLimit?:
|
|
493
|
+
rateLimit?: RateLimitDeclaration;
|
|
494
494
|
escapeHatch?: EscapeHatchDeclaration;
|
|
495
495
|
},
|
|
496
496
|
): HandlerRef;
|
package/src/handlers.ts
CHANGED
|
@@ -287,6 +287,29 @@ export type NotifyFn = (notificationType: string, options: NotifyOptions) => Pro
|
|
|
287
287
|
// Concrete implementation in bundled-features/delivery (cross-package boundary)
|
|
288
288
|
export type NotifyFactory = (user: SessionUser, tenantId: TenantId) => NotifyFn;
|
|
289
289
|
|
|
290
|
+
export type EscapeHatchKind =
|
|
291
|
+
| "unsafe-raw"
|
|
292
|
+
| "acknowledge-cross-tenant"
|
|
293
|
+
| "global-write"
|
|
294
|
+
| "identity-switch"
|
|
295
|
+
| "unsafe-all-tenants";
|
|
296
|
+
export type EscapeHatchTarget = { readonly id: string; readonly tenantId: TenantId };
|
|
297
|
+
export type EscapeHatchUseEvent = {
|
|
298
|
+
readonly handler: string;
|
|
299
|
+
readonly kind: EscapeHatchKind;
|
|
300
|
+
readonly reason: string;
|
|
301
|
+
readonly tenantId: TenantId;
|
|
302
|
+
readonly actor: string;
|
|
303
|
+
readonly target?: EscapeHatchTarget;
|
|
304
|
+
};
|
|
305
|
+
export type EscapeHatchAuditSink = (event: EscapeHatchUseEvent) => Promise<void>;
|
|
306
|
+
// Bound to handler/tenant/actor of one invocation; called once per actual escape-hatch use.
|
|
307
|
+
export type EscapeHatchReporter = (
|
|
308
|
+
kind: EscapeHatchKind,
|
|
309
|
+
reason: string,
|
|
310
|
+
target?: EscapeHatchTarget,
|
|
311
|
+
) => void;
|
|
312
|
+
|
|
290
313
|
// Shared optional fields across all execution contexts
|
|
291
314
|
type SharedContextFields = {
|
|
292
315
|
readonly redis?: Redis;
|
|
@@ -323,6 +346,8 @@ type SharedContextFields = {
|
|
|
323
346
|
readonly entityCache?: EntityCache;
|
|
324
347
|
readonly notify?: NotifyFn;
|
|
325
348
|
readonly _notifyFactory?: NotifyFactory;
|
|
349
|
+
// Wired at boot when the `audit` feature is mounted; absent → structured warn log.
|
|
350
|
+
readonly _escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
326
351
|
// Tenant-scoped secrets accessor. Present when the app wired a
|
|
327
352
|
// MasterKeyProvider at boot. Feature code reads ctx.secrets.get(...)
|
|
328
353
|
// to pull a plaintext secret; Secret<string> carries the brand that
|
|
@@ -595,8 +620,9 @@ export type HandlerContext<TMap extends object = KumikoEventTypeMap> = SharedCon
|
|
|
595
620
|
// name without the feature having to import the drizzle-table directly.
|
|
596
621
|
//
|
|
597
622
|
// Auto-applies tenant_id filter when the projection table has a tenant_id
|
|
598
|
-
// column
|
|
599
|
-
//
|
|
623
|
+
// column. { unsafeAllTenants: true } opts out but requires a grant —
|
|
624
|
+
// r.systemScope() or a declared escapeHatch on the handler (or hook).
|
|
625
|
+
// Unknown projection name throws.
|
|
600
626
|
readonly queryProjection: <T = Record<string, unknown>>(
|
|
601
627
|
qualifiedName: string,
|
|
602
628
|
options?: { readonly unsafeAllTenants?: boolean },
|
|
@@ -690,14 +716,10 @@ export type JobContext = SharedContextFields & {
|
|
|
690
716
|
readonly systemUser: SessionUser;
|
|
691
717
|
readonly log: Logger;
|
|
692
718
|
readonly triggeredBy: { readonly id: string; readonly tenantId: TenantId } | null;
|
|
693
|
-
// Only present for jobs whose owning feature declares r.systemScope()
|
|
694
|
-
//
|
|
695
|
-
//
|
|
696
|
-
//
|
|
697
|
-
// like reindexEntity() reaches through `.raw` on that TenantDb. `.raw`
|
|
698
|
-
// bypasses tenant filtering entirely, so it's only safe to hand to a
|
|
699
|
-
// helper that filters by tenantId itself (as reindexEntity does) — never
|
|
700
|
-
// pass it to code that trusts the connection to already be scoped.
|
|
719
|
+
// Only present for jobs whose owning feature declares r.systemScope().
|
|
720
|
+
// assertTenantMatch()/acknowledgeCrossTenant() return a TenantDb; a raw,
|
|
721
|
+
// tenant-unfiltered DbRunner instead comes from ctx.systemDb.unsafeRaw(reason)
|
|
722
|
+
// — only safe for a helper that filters by tenantId itself (e.g. reindexEntity).
|
|
701
723
|
readonly systemDb?: UncheckedSystemDb;
|
|
702
724
|
readonly write: (qn: string, payload: unknown) => Promise<WriteResult>;
|
|
703
725
|
readonly writeAs: (user: SessionUser, qn: string, payload: unknown) => Promise<WriteResult>;
|
|
@@ -1053,6 +1075,13 @@ export type RateLimitOption = {
|
|
|
1053
1075
|
readonly cost?: number;
|
|
1054
1076
|
};
|
|
1055
1077
|
|
|
1078
|
+
export type RateLimitDisabled = { readonly disabled: true; readonly reason: string };
|
|
1079
|
+
export type RateLimitDeclaration = RateLimitOption | RateLimitDisabled;
|
|
1080
|
+
|
|
1081
|
+
export function isRateLimitDisabled(v: RateLimitDeclaration | undefined): v is RateLimitDisabled {
|
|
1082
|
+
return v !== undefined && "disabled" in v && v.disabled === true;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1056
1085
|
export type AgentRisk = "low" | "mid" | "high";
|
|
1057
1086
|
|
|
1058
1087
|
/** Per-handler hints for the AI-agent manifest. `expose` overrides the
|
|
@@ -1075,7 +1104,7 @@ export type WriteHandlerDef = {
|
|
|
1075
1104
|
readonly description?: string;
|
|
1076
1105
|
readonly agent?: AgentHandlerHints;
|
|
1077
1106
|
readonly unsafeSkipTransitionGuard?: boolean;
|
|
1078
|
-
readonly rateLimit?:
|
|
1107
|
+
readonly rateLimit?: RateLimitDeclaration;
|
|
1079
1108
|
readonly escapeHatch?: EscapeHatchDeclaration;
|
|
1080
1109
|
// Set when the author wrote a `perform: stepsPipeline(...)` block. Boot-
|
|
1081
1110
|
// validators (projection-allowlist) and Designer/AI tooling read this
|
|
@@ -1104,7 +1133,7 @@ export type QueryHandlerDef = {
|
|
|
1104
1133
|
readonly access: AccessRule;
|
|
1105
1134
|
readonly description?: string;
|
|
1106
1135
|
readonly agent?: AgentHandlerHints;
|
|
1107
|
-
readonly rateLimit?:
|
|
1136
|
+
readonly rateLimit?: RateLimitDeclaration;
|
|
1108
1137
|
/** Zod schema of the handler's actual return value — the paged envelope
|
|
1109
1138
|
* `{ rows, nextCursor, total? }` for a `definePagedQueryHandler`, or the
|
|
1110
1139
|
* flat record (optionally `.nullable()`) for a plain query handler.
|
|
@@ -1125,7 +1154,7 @@ export type StreamHandlerDef = {
|
|
|
1125
1154
|
readonly schema: ZodType;
|
|
1126
1155
|
readonly handler: StreamHandlerFn;
|
|
1127
1156
|
readonly access: AccessRule;
|
|
1128
|
-
readonly rateLimit?:
|
|
1157
|
+
readonly rateLimit?: RateLimitDeclaration;
|
|
1129
1158
|
// Stream handlers can't reach db.global() (that gate is write-only), but
|
|
1130
1159
|
// they can still switch identity to SYSTEM via ctx.queryAs — this opts
|
|
1131
1160
|
// in, same contract as WriteHandlerDef.escapeHatch.
|
package/src/screen.ts
CHANGED
|
@@ -377,6 +377,32 @@ export type ToolbarAction =
|
|
|
377
377
|
readonly style?: "primary" | "secondary" | "danger";
|
|
378
378
|
};
|
|
379
379
|
|
|
380
|
+
// relatedList-only extension of ToolbarAction:
|
|
381
|
+
// a relatedList section's toolbar renders inside a projectionDetail, which
|
|
382
|
+
// has a record to evaluate against — a plain entityList/projectionList
|
|
383
|
+
// toolbar does not, so `visible`/`params` live here instead of on the base
|
|
384
|
+
// union. `visible` uses the same FieldCondition as header actions/
|
|
385
|
+
// RowAction; `params` reuses RowActionNavigate's RowFieldExtractor, applied
|
|
386
|
+
// to the enclosing record instead of a clicked row. Every field is optional,
|
|
387
|
+
// so a plain ToolbarAction is already a valid RelatedListToolbarAction —
|
|
388
|
+
// callers pass either type without a cast.
|
|
389
|
+
export type RelatedListToolbarAction =
|
|
390
|
+
| (Extract<ToolbarAction, { readonly kind: "navigate" }> & {
|
|
391
|
+
/** Conditional visibility, evaluated against the relatedList's parent
|
|
392
|
+
* record (the "Akte"). */
|
|
393
|
+
readonly visible?: FieldCondition;
|
|
394
|
+
/** Declarative URL search params extracted from the parent record,
|
|
395
|
+
* prefilling the target screen. Replaces the implicit
|
|
396
|
+
* `{ [parentParam]: parentId }` default when set. */
|
|
397
|
+
readonly params?: RowFieldExtractor;
|
|
398
|
+
})
|
|
399
|
+
| (Extract<ToolbarAction, { readonly kind: "writeHandler" }> & {
|
|
400
|
+
readonly visible?: FieldCondition;
|
|
401
|
+
})
|
|
402
|
+
| (Extract<ToolbarAction, { readonly kind: "drawer" }> & {
|
|
403
|
+
readonly visible?: FieldCondition;
|
|
404
|
+
});
|
|
405
|
+
|
|
380
406
|
export type EntityListScreenDefinition = {
|
|
381
407
|
readonly id: string;
|
|
382
408
|
readonly type: "entityList";
|
|
@@ -384,6 +410,9 @@ export type EntityListScreenDefinition = {
|
|
|
384
410
|
readonly detailFor?: string;
|
|
385
411
|
readonly description?: string;
|
|
386
412
|
readonly agent?: AgentHandlerHints;
|
|
413
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
414
|
+
* app); exempts it from the nav-area boot check. */
|
|
415
|
+
readonly dormant?: boolean;
|
|
387
416
|
readonly entity: string;
|
|
388
417
|
readonly columns: readonly ListColumnSpec[];
|
|
389
418
|
// Row renderer (Desktop) — when omitted, renderer draws the default table
|
|
@@ -455,6 +484,17 @@ export type ListFacetSpec =
|
|
|
455
484
|
readonly label: string;
|
|
456
485
|
readonly trueLabel: string;
|
|
457
486
|
readonly falseLabel: string;
|
|
487
|
+
}
|
|
488
|
+
| {
|
|
489
|
+
readonly field: string;
|
|
490
|
+
readonly type: "reference";
|
|
491
|
+
readonly label: string;
|
|
492
|
+
/** Entity name (same feature) or `feature:entity` (cross-feature), same
|
|
493
|
+
* convention as `ListColumnSpec.refEntity`. Options load at render time. */
|
|
494
|
+
readonly entity: string;
|
|
495
|
+
/** Row field on the referenced entity shown as the option label
|
|
496
|
+
* (default "id"). */
|
|
497
|
+
readonly labelField?: string;
|
|
458
498
|
};
|
|
459
499
|
|
|
460
500
|
export type ProjectionListScreenDefinition = {
|
|
@@ -464,6 +504,9 @@ export type ProjectionListScreenDefinition = {
|
|
|
464
504
|
readonly detailFor?: string;
|
|
465
505
|
readonly description?: string;
|
|
466
506
|
readonly agent?: AgentHandlerHints;
|
|
507
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
508
|
+
* app); exempts it from the nav-area boot check. */
|
|
509
|
+
readonly dormant?: boolean;
|
|
467
510
|
readonly query: string;
|
|
468
511
|
readonly columns: readonly ListColumnSpec[];
|
|
469
512
|
readonly rowRenderer?: PlatformComponent;
|
|
@@ -558,6 +601,9 @@ export type ProjectionDetailScreenDefinition = {
|
|
|
558
601
|
readonly detailFor?: string;
|
|
559
602
|
readonly description?: string;
|
|
560
603
|
readonly agent?: AgentHandlerHints;
|
|
604
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
605
|
+
* app); exempts it from the nav-area boot check. */
|
|
606
|
+
readonly dormant?: boolean;
|
|
561
607
|
readonly query: string;
|
|
562
608
|
/** Query-payload key for the row-id. Default "id". */
|
|
563
609
|
readonly idParam?: string;
|
|
@@ -763,6 +809,9 @@ export type DashboardScreenDefinition = {
|
|
|
763
809
|
readonly detailFor?: string;
|
|
764
810
|
readonly description?: string;
|
|
765
811
|
readonly agent?: AgentHandlerHints;
|
|
812
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
813
|
+
* app); exempts it from the nav-area boot check. */
|
|
814
|
+
readonly dormant?: boolean;
|
|
766
815
|
readonly panels: readonly DashboardPanelDefinition[];
|
|
767
816
|
readonly filter?: DashboardFilterDefinition;
|
|
768
817
|
readonly slots?: ScreenSlots;
|
|
@@ -835,7 +884,17 @@ export type EditFieldsSection = {
|
|
|
835
884
|
* (subtitle-only section). */
|
|
836
885
|
readonly description?: string;
|
|
837
886
|
readonly columns?: number;
|
|
887
|
+
/** Mutually exclusive with `groups` — pass `[]` when using `groups`; the
|
|
888
|
+
* boot-validator rejects both non-empty or both empty. */
|
|
838
889
|
readonly fields: readonly EditFieldSpec[];
|
|
890
|
+
/** Splits the section into multiple titled cards instead of one flat grid.
|
|
891
|
+
* Mutually exclusive with `fields`; fields named here still need to exist. */
|
|
892
|
+
readonly groups?: readonly {
|
|
893
|
+
readonly title: string;
|
|
894
|
+
readonly fields: readonly EditFieldSpec[];
|
|
895
|
+
/** Default 2. */
|
|
896
|
+
readonly columns?: number;
|
|
897
|
+
}[];
|
|
839
898
|
/** Rendered left of the section title, `text-muted-foreground` — closed
|
|
840
899
|
* IconKey vocabulary into the ICONS registry (renderer-web), analogous
|
|
841
900
|
* to EditFieldSpec.icon. No title → no icon, and no heuristic derives
|
|
@@ -889,6 +948,15 @@ export type EditRelatedListSection = {
|
|
|
889
948
|
readonly query: string;
|
|
890
949
|
/** Query-payload key the parent record's id is passed under. Default "id". */
|
|
891
950
|
readonly parentParam?: string;
|
|
951
|
+
/** Server-side WHERE clause pinning this section to the parent record —
|
|
952
|
+
* sent as `payload.filter: { field, op: "eq", value: parentId }`, kept
|
|
953
|
+
* out of the user-facet `filters` array so it can't be cleared by facet
|
|
954
|
+
* interaction. Lets a tab reuse the generic `<entity>:list` query instead
|
|
955
|
+
* of a bespoke child-rows handler. Mutually exclusive with `parentParam`
|
|
956
|
+
* (the boot-validator rejects both). `field` must be a real field on the
|
|
957
|
+
* entity behind `query`, and that query's Zod schema must accept
|
|
958
|
+
* `filter` (same requirement `filter`/`facets` already have). */
|
|
959
|
+
readonly parentFilter?: { readonly field: string };
|
|
892
960
|
readonly columns: readonly ListColumnSpec[];
|
|
893
961
|
readonly pageSize?: number;
|
|
894
962
|
/** Initial sort on mount, applied client-side over the already-loaded rows
|
|
@@ -921,6 +989,11 @@ export type EditRelatedListSection = {
|
|
|
921
989
|
* writeHandler action re-runs this section's own query, same as a
|
|
922
990
|
* projectionList row action re-running its list query. */
|
|
923
991
|
readonly rowActions?: readonly RowAction[];
|
|
992
|
+
/** Toolbar actions above the table — same type and dispatch semantics as
|
|
993
|
+
* `entityList`/`projectionList`'s `toolbarActions` ("+ Anlegen" etc.),
|
|
994
|
+
* plus `visible`/`params` evaluated against the parent record (see
|
|
995
|
+
* RelatedListToolbarAction). */
|
|
996
|
+
readonly toolbarActions?: readonly RelatedListToolbarAction[];
|
|
924
997
|
/** Record field rendered as a count badge in the tab label when the
|
|
925
998
|
* enclosing `EditLayout.mode` is "tabs" (e.g. an open-items counter).
|
|
926
999
|
* Ignored outside tabs mode or when the field's value is not a finite
|
|
@@ -995,6 +1068,9 @@ export type EntityEditScreenDefinition = {
|
|
|
995
1068
|
readonly detailFor?: string;
|
|
996
1069
|
readonly description?: string;
|
|
997
1070
|
readonly agent?: AgentHandlerHints;
|
|
1071
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
1072
|
+
* app); exempts it from the nav-area boot check. */
|
|
1073
|
+
readonly dormant?: boolean;
|
|
998
1074
|
/** Derived by buildAppSchema from the navigate `params` targeting this
|
|
999
1075
|
* screen — the only URL query keys the create form prefills. An authored
|
|
1000
1076
|
* value is overwritten. */
|
|
@@ -1092,6 +1168,9 @@ export type ActionFormScreenDefinition = {
|
|
|
1092
1168
|
readonly detailFor?: string;
|
|
1093
1169
|
readonly description?: string;
|
|
1094
1170
|
readonly agent?: AgentHandlerHints;
|
|
1171
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
1172
|
+
* app); exempts it from the nav-area boot check. */
|
|
1173
|
+
readonly dormant?: boolean;
|
|
1095
1174
|
/** Derived by buildAppSchema — see EntityEditScreenDefinition.urlPrefillFields. */
|
|
1096
1175
|
readonly urlPrefillFields?: readonly string[];
|
|
1097
1176
|
/** Write-Handler-QN der bei Submit gerufen wird. Form-Object landet
|
|
@@ -1104,6 +1183,9 @@ export type ActionFormScreenDefinition = {
|
|
|
1104
1183
|
/** Layout analog zu EntityEditScreen: sections mit fields aus dem
|
|
1105
1184
|
* fields-Map oben. */
|
|
1106
1185
|
readonly layout: EditLayout;
|
|
1186
|
+
/** Per-field label i18n key override, same type/semantics as
|
|
1187
|
+
* `EntityEditScreenDefinition.fieldLabels`. Falls back to the convention when absent. */
|
|
1188
|
+
readonly fieldLabels?: Readonly<Record<string, string>>;
|
|
1107
1189
|
/** i18n-key für den Submit-Button. Default: i18n-Default des
|
|
1108
1190
|
* Renderers (typischerweise "actions.submit"). */
|
|
1109
1191
|
readonly submitLabel?: string;
|
|
@@ -1223,6 +1305,9 @@ export type SecretMintScreenDefinition = {
|
|
|
1223
1305
|
readonly detailFor?: string;
|
|
1224
1306
|
readonly description?: string;
|
|
1225
1307
|
readonly agent?: AgentHandlerHints;
|
|
1308
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
1309
|
+
* app); exempts it from the nav-area boot check. */
|
|
1310
|
+
readonly dormant?: boolean;
|
|
1226
1311
|
/** Derived by buildAppSchema — see EntityEditScreenDefinition.urlPrefillFields. */
|
|
1227
1312
|
readonly urlPrefillFields?: readonly string[];
|
|
1228
1313
|
/** Write-handler QN dispatched on submit. */
|
|
@@ -1276,7 +1361,8 @@ export type CustomScreenDefinition = {
|
|
|
1276
1361
|
* positive (kumiko-framework#2034). Only set this on screens the
|
|
1277
1362
|
* feature itself never navs — a screen the feature DOES nav still needs
|
|
1278
1363
|
* its client plugin mounted by every consumer, and should keep
|
|
1279
|
-
* triggering the diagnostic if it's missing.
|
|
1364
|
+
* triggering the diagnostic if it's missing. Also gates the
|
|
1365
|
+
* boot-validator's nav-area check, same as `dormant` on other screens. */
|
|
1280
1366
|
readonly dormant?: boolean;
|
|
1281
1367
|
};
|
|
1282
1368
|
|
|
@@ -1320,6 +1406,9 @@ export type ConfigEditScreenDefinition = {
|
|
|
1320
1406
|
readonly detailFor?: string;
|
|
1321
1407
|
readonly description?: string;
|
|
1322
1408
|
readonly agent?: AgentHandlerHints;
|
|
1409
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
1410
|
+
* app); exempts it from the nav-area boot check. */
|
|
1411
|
+
readonly dormant?: boolean;
|
|
1323
1412
|
/** scope für config:write:set Calls. Muss zur Scope-Deklaration der
|
|
1324
1413
|
* in `configKeys` referenzierten Keys passen — Boot-Validator
|
|
1325
1414
|
* prüft das gegen die Registry. */
|
|
@@ -1364,6 +1453,9 @@ export type SecretsEditScreenDefinition = {
|
|
|
1364
1453
|
readonly detailFor?: string;
|
|
1365
1454
|
readonly description?: string;
|
|
1366
1455
|
readonly agent?: AgentHandlerHints;
|
|
1456
|
+
/** Screen has no nav entry by design (opened via link or navved by the
|
|
1457
|
+
* app); exempts it from the nav-area boot check. */
|
|
1458
|
+
readonly dormant?: boolean;
|
|
1367
1459
|
/** field id -> qualified secret name (`<feature>:secret:<kebab>`). */
|
|
1368
1460
|
readonly secretKeys: Readonly<Record<string, string>>;
|
|
1369
1461
|
/** field id -> i18n key for the label. */
|
package/src/step.ts
CHANGED
|
@@ -262,13 +262,14 @@ export type StepNamespace = {
|
|
|
262
262
|
readonly where: StepResolver<WhereObject>;
|
|
263
263
|
}) => StepInstance;
|
|
264
264
|
// Read sub-namespace — thin wrapper on selectMany/fetchOne (bun-db).
|
|
265
|
-
//
|
|
265
|
+
// Tenant-filtered like ctx.db; unsafeAllTenants needs escapeHatch on the handler (or systemScope).
|
|
266
266
|
readonly read: {
|
|
267
267
|
readonly findOne: (
|
|
268
268
|
name: string,
|
|
269
269
|
opts: {
|
|
270
270
|
readonly table: unknown;
|
|
271
271
|
readonly where: StepResolver<WhereObject | undefined>;
|
|
272
|
+
readonly unsafeAllTenants?: { readonly reason: string };
|
|
272
273
|
},
|
|
273
274
|
) => StepInstance;
|
|
274
275
|
readonly findMany: (
|
|
@@ -277,6 +278,7 @@ export type StepNamespace = {
|
|
|
277
278
|
readonly table: unknown;
|
|
278
279
|
readonly where?: StepResolver<WhereObject | undefined>;
|
|
279
280
|
readonly limit?: number;
|
|
281
|
+
readonly unsafeAllTenants?: { readonly reason: string };
|
|
280
282
|
},
|
|
281
283
|
) => StepInstance;
|
|
282
284
|
};
|
package/src/tenant-db-types.ts
CHANGED
|
@@ -53,15 +53,6 @@ export type TenantDbMode = "tenant" | "system";
|
|
|
53
53
|
export type TenantDb = {
|
|
54
54
|
readonly tenantId: TenantId;
|
|
55
55
|
readonly mode: TenantDbMode;
|
|
56
|
-
/**
|
|
57
|
-
* Underlying DbRunner. Framework-internal use (event-store, migrations) —
|
|
58
|
-
* bypasses tenant-filter. Feature code uses the typed helpers above so the
|
|
59
|
-
* automatic scoping stays intact.
|
|
60
|
-
* @deprecated Use `ctx.db.unsafeRaw(reason)` / `db.global(table)` (method-
|
|
61
|
-
* form) instead — both make the cross-tenant intent an explicit, named
|
|
62
|
-
* declaration instead of a silent unfiltered escape hatch. Removal fw#2860.
|
|
63
|
-
*/
|
|
64
|
-
readonly raw: DbRunner;
|
|
65
56
|
/**
|
|
66
57
|
* Unfiltered DbRunner escape hatch for handlers/hooks that declare `escapeHatch: { reason }`.
|
|
67
58
|
* Throws `AccessDeniedError` when ungranted, or `Error` when `reason` is empty.
|