@cosmicdrift/kumiko-framework 0.159.1 → 0.161.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 +3 -3
- package/src/api/__tests__/api.test.ts +65 -0
- package/src/api/__tests__/auth-routes-cookie.test.ts +1 -0
- package/src/api/__tests__/auth-routes-invalid-body-invite.test.ts +237 -0
- package/src/api/__tests__/auth-routes-mfa-verify.test.ts +1 -0
- package/src/api/__tests__/dispatcher-live.integration.test.ts +74 -0
- package/src/api/__tests__/login-rate-limiter-sweep.test.ts +41 -0
- package/src/api/__tests__/server-boot-guards.test.ts +71 -0
- package/src/api/api-constants.ts +1 -0
- package/src/api/auth-middleware.ts +17 -44
- package/src/api/auth-routes.ts +6 -2
- package/src/api/index.ts +1 -0
- package/src/api/routes.ts +57 -0
- package/src/api/server.ts +5 -4
- package/src/bun-db/query.ts +12 -25
- package/src/crypto/kms-adapter.ts +2 -118
- package/src/db/__tests__/build-filter-where.test.ts +34 -0
- package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +91 -0
- package/src/db/cursor.ts +1 -18
- package/src/db/dialect.ts +8 -19
- package/src/db/entity-table-meta-types.ts +2 -92
- package/src/db/event-store-executor.ts +4 -96
- package/src/db/table-builder.ts +2 -19
- package/src/db/tenant-db.ts +6 -55
- package/src/engine/__tests__/boot-validator.test.ts +46 -0
- package/src/engine/__tests__/codemod-pipeline.test.ts +139 -10
- package/src/engine/__tests__/engine.test.ts +28 -0
- package/src/engine/__tests__/registry-facade-sweep.test.ts +80 -0
- package/src/engine/__tests__/registry.test.ts +40 -0
- package/src/engine/__tests__/tier-resolver-extension.test.ts +19 -1
- package/src/engine/boot-validator/entity-handler.ts +10 -1
- package/src/engine/define-feature.ts +1 -0
- package/src/engine/define-handler.ts +1 -0
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +11 -1
- package/src/engine/feature-ast/__tests__/parse.test.ts +983 -3
- package/src/engine/feature-ast/__tests__/patch.test.ts +168 -0
- package/src/engine/feature-ast/__tests__/patcher.test.ts +7 -0
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +9 -0
- package/src/engine/feature-ast/extractors/handlers.ts +19 -2
- package/src/engine/feature-ast/extractors/index.ts +1 -0
- package/src/engine/feature-ast/index.ts +2 -0
- package/src/engine/feature-ast/parse.ts +3 -0
- package/src/engine/feature-ast/patch.ts +2 -0
- package/src/engine/feature-ast/patcher.ts +21 -0
- package/src/engine/feature-ast/patterns.ts +16 -0
- package/src/engine/feature-ast/render.ts +15 -0
- package/src/engine/feature-builder-state.ts +3 -0
- package/src/engine/feature-entity-handlers.ts +35 -1
- package/src/engine/index.ts +3 -0
- package/src/engine/pattern-library/__tests__/library.test.ts +9 -0
- package/src/engine/pattern-library/library.ts +2 -0
- package/src/engine/pattern-library/mixed-schemas.ts +37 -0
- package/src/engine/registry-facade.ts +9 -0
- package/src/engine/registry-ingest.ts +10 -0
- package/src/engine/registry-state.ts +3 -0
- package/src/engine/types/config.ts +2 -497
- package/src/engine/types/define-handler.ts +2 -94
- package/src/engine/types/entity-handlers.ts +2 -30
- package/src/engine/types/feature.ts +2 -1021
- package/src/engine/types/fields.ts +2 -685
- package/src/engine/types/handlers.ts +2 -820
- package/src/engine/types/hooks.ts +2 -170
- package/src/engine/types/index.ts +44 -36
- package/src/engine/types/nav.ts +2 -67
- package/src/engine/types/ownership.ts +2 -83
- package/src/engine/types/projection.ts +2 -165
- package/src/engine/types/screen.ts +2 -747
- package/src/engine/types/step.ts +2 -334
- package/src/engine/types/workspace.ts +2 -42
- package/src/errors/write-error-info.ts +6 -22
- package/src/event-store/errors.ts +2 -35
- package/src/event-store/event-store.ts +2 -21
- package/src/event-store/snapshot.ts +11 -35
- package/src/event-store/types.ts +2 -22
- package/src/files/provider-resolver.ts +3 -5
- package/src/files/types.ts +5 -54
- package/src/jobs/__tests__/jobs.integration.test.ts +102 -1
- package/src/pipeline/__tests__/dispatcher.test.ts +96 -0
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +208 -0
- package/src/pipeline/dispatch-shared.ts +39 -1
- package/src/pipeline/dispatch-stream.ts +74 -0
- package/src/pipeline/dispatcher-utils.ts +1 -1
- package/src/pipeline/dispatcher.ts +7 -0
- package/src/pipeline/multi-stream-apply-context.ts +4 -42
- package/src/rate-limit/resolver.ts +10 -30
- package/src/secrets/envelope-cipher.ts +4 -6
- package/src/secrets/types.ts +2 -177
- package/src/stack/request-helper.ts +19 -2
- package/src/stack/test-stack.ts +33 -14
- package/src/time/tz-context.ts +9 -56
|
@@ -1,170 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { AppContext } from "./handlers";
|
|
4
|
-
import type { EntityId } from "./identifiers";
|
|
5
|
-
|
|
6
|
-
// --- Validation ---
|
|
7
|
-
|
|
8
|
-
export type ValidationError = {
|
|
9
|
-
readonly field: string;
|
|
10
|
-
readonly error: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export type ValidationHookFn = (
|
|
14
|
-
data: Readonly<Record<string, unknown>>,
|
|
15
|
-
) => readonly ValidationError[] | null;
|
|
16
|
-
|
|
17
|
-
// --- Save/Delete Context (what hooks receive) ---
|
|
18
|
-
|
|
19
|
-
export type SaveContext = {
|
|
20
|
-
readonly kind: "save";
|
|
21
|
-
readonly id: EntityId;
|
|
22
|
-
readonly data: Readonly<Record<string, unknown>>;
|
|
23
|
-
readonly changes: Readonly<Record<string, unknown>>;
|
|
24
|
-
readonly previous: Readonly<Record<string, unknown>>;
|
|
25
|
-
readonly isNew: boolean;
|
|
26
|
-
readonly entityName?: string | undefined;
|
|
27
|
-
// The event that produced this save. Populated by the event-store-executor;
|
|
28
|
-
// the pipeline uses it to drive projections inside the same transaction.
|
|
29
|
-
// Optional because hand-crafted SaveContexts (tests, custom executors) may
|
|
30
|
-
// not have an event — projections just skip in that case.
|
|
31
|
-
readonly event?: StoredEvent | undefined;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type DeleteContext = {
|
|
35
|
-
readonly kind: "delete";
|
|
36
|
-
readonly id: EntityId;
|
|
37
|
-
readonly data: Readonly<Record<string, unknown>>;
|
|
38
|
-
readonly entityName?: string | undefined;
|
|
39
|
-
// See SaveContext.event — same semantics.
|
|
40
|
-
readonly event?: StoredEvent | undefined;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export type LifecycleResult = SaveContext | DeleteContext;
|
|
44
|
-
|
|
45
|
-
// --- Lifecycle Hooks ---
|
|
46
|
-
|
|
47
|
-
export type PreSaveHookFn = (
|
|
48
|
-
changes: Record<string, unknown>,
|
|
49
|
-
context: AppContext & {
|
|
50
|
-
readonly previous: Readonly<Record<string, unknown>>;
|
|
51
|
-
readonly isNew: boolean;
|
|
52
|
-
},
|
|
53
|
-
) => Promise<Record<string, unknown>>;
|
|
54
|
-
|
|
55
|
-
export type PostSaveHookFn = (result: SaveContext, context: AppContext) => Promise<void>;
|
|
56
|
-
|
|
57
|
-
// Batch-variant: called once at the end of a dispatcher batch with every
|
|
58
|
-
// successful SaveContext. The per-save PostSaveHookFn still fires for
|
|
59
|
-
// side-effects that need per-entity semantics (SSE); PostSaveBatch exists
|
|
60
|
-
// for adapters that can amortise work across the whole batch (e.g. search
|
|
61
|
-
// index batch-writes, bulk webhook fanout).
|
|
62
|
-
export type PostSaveBatchHookFn = (
|
|
63
|
-
results: readonly SaveContext[],
|
|
64
|
-
context: AppContext,
|
|
65
|
-
) => Promise<void>;
|
|
66
|
-
|
|
67
|
-
export type PreDeleteHookFn = (payload: DeleteContext, context: AppContext) => Promise<void>;
|
|
68
|
-
|
|
69
|
-
export type PostDeleteHookFn = (payload: DeleteContext, context: AppContext) => Promise<void>;
|
|
70
|
-
|
|
71
|
-
export type PostDeleteBatchHookFn = (
|
|
72
|
-
payloads: readonly DeleteContext[],
|
|
73
|
-
context: AppContext,
|
|
74
|
-
) => Promise<void>;
|
|
75
|
-
|
|
76
|
-
export type PreQueryHookFn = (
|
|
77
|
-
payload: Record<string, unknown>,
|
|
78
|
-
context: AppContext,
|
|
79
|
-
) => Promise<Record<string, unknown>>;
|
|
80
|
-
|
|
81
|
-
// postQuery — fires after query-handler-execute, before field-access-read-filter.
|
|
82
|
-
// Hook receives normalized rows + entityName + can mutate rows (e.g., merge
|
|
83
|
-
// custom-fields, add computed-counts, attach related-data). Mutation result
|
|
84
|
-
// replaces original rows. Hook is responsible for its own field-access-logic
|
|
85
|
-
// on added fields (field-access-filter only knows entity's stammfields).
|
|
86
|
-
export type PostQueryHookFn = (
|
|
87
|
-
result: {
|
|
88
|
-
// undefined for standalone queries (no-colon handler names like
|
|
89
|
-
// "ns:dashboard") — those have no backing entity, but handler-keyed
|
|
90
|
-
// postQuery hooks still fire on them.
|
|
91
|
-
readonly entityName: string | undefined;
|
|
92
|
-
readonly rows: ReadonlyArray<Record<string, unknown>>;
|
|
93
|
-
},
|
|
94
|
-
context: AppContext,
|
|
95
|
-
) => Promise<{ rows: ReadonlyArray<Record<string, unknown>> }>;
|
|
96
|
-
|
|
97
|
-
export type LifecycleHookFn =
|
|
98
|
-
| PreSaveHookFn
|
|
99
|
-
| PostSaveHookFn
|
|
100
|
-
| PreDeleteHookFn
|
|
101
|
-
| PostDeleteHookFn
|
|
102
|
-
| PreQueryHookFn
|
|
103
|
-
| PostQueryHookFn;
|
|
104
|
-
|
|
105
|
-
export type HookPhase = (typeof HookPhases)[keyof typeof HookPhases];
|
|
106
|
-
|
|
107
|
-
// Owner-tag shared across every hook structure. The lifecycle pipeline uses
|
|
108
|
-
// it to skip hooks whose owning feature is globally disabled:
|
|
109
|
-
// - A concrete feature name like "orders" → subject to the feature-toggle
|
|
110
|
-
// filter (skipped when "orders" is disabled).
|
|
111
|
-
// - "*" (star) → invariant plumbing, never filtered. Reserved for
|
|
112
|
-
// extension-provided hooks and framework-internal hooks that belong to
|
|
113
|
-
// the pipeline itself, not a feature.
|
|
114
|
-
// - Omitted (undefined) → treated as "*". Supports tests that hand-build
|
|
115
|
-
// HookMap objects without caring about ownership.
|
|
116
|
-
export type HookOwner = { readonly featureName?: string };
|
|
117
|
-
|
|
118
|
-
export type PhasedHook<TFn> = {
|
|
119
|
-
readonly fn: TFn;
|
|
120
|
-
readonly phase: HookPhase;
|
|
121
|
-
} & HookOwner;
|
|
122
|
-
|
|
123
|
-
// Flat (non-phased) hook — preSave, preQuery. Same owner contract, no
|
|
124
|
-
// phase semantics because these hooks run exactly once per handler pass
|
|
125
|
-
// before/around the DB transaction.
|
|
126
|
-
export type OwnedFn<TFn> = {
|
|
127
|
-
readonly fn: TFn;
|
|
128
|
-
} & HookOwner;
|
|
129
|
-
|
|
130
|
-
// --- Hook Maps ---
|
|
131
|
-
|
|
132
|
-
// Slots are optional: defineFeature materializes every slot, but hand-built
|
|
133
|
-
// FeatureDefinitions at system boundaries (test fixtures, partial boots —
|
|
134
|
-
// see registry.test.ts "slot robustness") legitimately omit them, and the
|
|
135
|
-
// registry merge paths tolerate undefined. The type mirrors that contract.
|
|
136
|
-
export type HookMap = {
|
|
137
|
-
readonly validation?: Readonly<Record<string, ValidationHookFn>>;
|
|
138
|
-
readonly preSave?: Readonly<Record<string, readonly OwnedFn<PreSaveHookFn>[]>>;
|
|
139
|
-
readonly postSave?: Readonly<Record<string, readonly PhasedHook<PostSaveHookFn>[]>>;
|
|
140
|
-
readonly preDelete?: Readonly<Record<string, readonly PhasedHook<PreDeleteHookFn>[]>>;
|
|
141
|
-
readonly postDelete?: Readonly<Record<string, readonly PhasedHook<PostDeleteHookFn>[]>>;
|
|
142
|
-
readonly preQuery?: Readonly<Record<string, readonly OwnedFn<PreQueryHookFn>[]>>;
|
|
143
|
-
readonly postQuery?: Readonly<Record<string, readonly OwnedFn<PostQueryHookFn>[]>>;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export type EntityHookMap = {
|
|
147
|
-
readonly postSave?: Readonly<Record<string, readonly PhasedHook<PostSaveHookFn>[]>>;
|
|
148
|
-
readonly preDelete?: Readonly<Record<string, readonly PhasedHook<PreDeleteHookFn>[]>>;
|
|
149
|
-
readonly postDelete?: Readonly<Record<string, readonly PhasedHook<PostDeleteHookFn>[]>>;
|
|
150
|
-
readonly postQuery?: Readonly<Record<string, readonly OwnedFn<PostQueryHookFn>[]>>;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
// Search-Payload-Extension (F3) — contributor function that adds flat
|
|
154
|
-
// fields to an entity's search-document. Fires synchronously during
|
|
155
|
-
// buildSearchDocument (in `system-hooks.ts`), receives current entity
|
|
156
|
-
// state, returns extra fields to merge into the search-index payload.
|
|
157
|
-
//
|
|
158
|
-
// Use-cases: custom-fields-bundle (merge customFields-jsonb-keys flat
|
|
159
|
-
// into index), tags-bundle (project tags-array as searchable), computed-
|
|
160
|
-
// fields (denormalize related-counts).
|
|
161
|
-
//
|
|
162
|
-
// IMPORTANT: contributor must be deterministic per (entityName, entityId,
|
|
163
|
-
// state). Async-allowed for future-proofing but discouraged — the
|
|
164
|
-
// indexing path runs once per entity-write, sync extension is
|
|
165
|
-
// near-zero-cost.
|
|
166
|
-
export type SearchPayloadContributorFn = (args: {
|
|
167
|
-
readonly entityName: string;
|
|
168
|
-
readonly entityId: EntityId;
|
|
169
|
-
readonly state: Record<string, unknown>;
|
|
170
|
-
}) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
1
|
+
// Legacy path — re-exported for callers still importing this module directly.
|
|
2
|
+
export * from "@cosmicdrift/kumiko-types/hooks";
|
|
@@ -1,23 +1,8 @@
|
|
|
1
|
-
// Barrel: re-exports all types from
|
|
1
|
+
// Barrel: re-exports all types from @cosmicdrift/kumiko-types, plus the
|
|
2
|
+
// runtime helpers below that stay framework-side.
|
|
2
3
|
// Duplicate types (OnDeleteStrategy, ConfigScope, ConcurrencyMode, LifecycleHookType)
|
|
3
4
|
// are defined ONLY in constants.ts — re-exported here for backwards compatibility.
|
|
4
5
|
|
|
5
|
-
// Re-export types that were duplicated in types.ts but are canonical in constants.ts
|
|
6
|
-
export type {
|
|
7
|
-
ConcurrencyMode,
|
|
8
|
-
ConfigScope,
|
|
9
|
-
LifecycleHookType,
|
|
10
|
-
OnDeleteStrategy,
|
|
11
|
-
} from "../constants";
|
|
12
|
-
export { DEFAULT_CURRENCIES, isFileField } from "../field-helpers";
|
|
13
|
-
export { resolveName, withResponseData } from "../handler-helpers";
|
|
14
|
-
export { HookPhases } from "../hook-helpers";
|
|
15
|
-
export {
|
|
16
|
-
isExtensionEditSection,
|
|
17
|
-
isFormatSpec,
|
|
18
|
-
normalizeEditField,
|
|
19
|
-
normalizeListColumn,
|
|
20
|
-
} from "../screen-helpers";
|
|
21
6
|
export type {
|
|
22
7
|
ConfigAccessor,
|
|
23
8
|
ConfigAccessorFactory,
|
|
@@ -62,26 +47,27 @@ export type {
|
|
|
62
47
|
TranslationKeys,
|
|
63
48
|
TranslationsDef,
|
|
64
49
|
UiExtensionDef,
|
|
65
|
-
} from "
|
|
50
|
+
} from "@cosmicdrift/kumiko-types/config";
|
|
66
51
|
export type {
|
|
67
52
|
QueryHandlerDefinition,
|
|
53
|
+
StreamHandlerDefinition,
|
|
68
54
|
WriteHandlerDefinition,
|
|
69
55
|
WriteHandlerInput,
|
|
70
|
-
} from "
|
|
56
|
+
} from "@cosmicdrift/kumiko-types/define-handler";
|
|
71
57
|
export type {
|
|
72
58
|
EntityCrudRegistrar,
|
|
73
59
|
EntityCrudVerb,
|
|
74
60
|
EntityHandlerOptions,
|
|
75
61
|
EntityQueryHandlerOptions,
|
|
76
62
|
RegisterEntityCrudOptions,
|
|
77
|
-
} from "
|
|
63
|
+
} from "@cosmicdrift/kumiko-types/entity-handlers";
|
|
78
64
|
// Cross-Feature Compile-Time-Type-Map — features extend per declare-module.
|
|
79
65
|
export type {
|
|
80
66
|
KumikoEntityTypeMap,
|
|
81
67
|
KumikoEventTypeMap,
|
|
82
68
|
KumikoHandlerPayloadMap,
|
|
83
69
|
KumikoHandlerResultMap,
|
|
84
|
-
} from "
|
|
70
|
+
} from "@cosmicdrift/kumiko-types/event-type-map";
|
|
85
71
|
export type {
|
|
86
72
|
BootCheckContext,
|
|
87
73
|
BootCheckFn,
|
|
@@ -99,7 +85,7 @@ export type {
|
|
|
99
85
|
StoreTableOptions,
|
|
100
86
|
UiHintOption,
|
|
101
87
|
UiHints,
|
|
102
|
-
} from "
|
|
88
|
+
} from "@cosmicdrift/kumiko-types/feature";
|
|
103
89
|
export type {
|
|
104
90
|
AnyFileFieldDef,
|
|
105
91
|
BigIntFieldDef,
|
|
@@ -137,7 +123,7 @@ export type {
|
|
|
137
123
|
TimestampFieldDef,
|
|
138
124
|
TransitionMap,
|
|
139
125
|
TzFieldDef,
|
|
140
|
-
} from "
|
|
126
|
+
} from "@cosmicdrift/kumiko-types/fields";
|
|
141
127
|
export type {
|
|
142
128
|
AccessRule,
|
|
143
129
|
AggregateStreamHandle,
|
|
@@ -176,6 +162,8 @@ export type {
|
|
|
176
162
|
RateLimitOption,
|
|
177
163
|
RateLimitPer,
|
|
178
164
|
SessionUser,
|
|
165
|
+
StreamHandlerDef,
|
|
166
|
+
StreamHandlerFn,
|
|
179
167
|
TryAppendEventFn,
|
|
180
168
|
TryAppendEventResult,
|
|
181
169
|
UnsafeAppendEventFn,
|
|
@@ -183,7 +171,7 @@ export type {
|
|
|
183
171
|
WriteHandlerDef,
|
|
184
172
|
WriteHandlerFn,
|
|
185
173
|
WriteResult,
|
|
186
|
-
} from "
|
|
174
|
+
} from "@cosmicdrift/kumiko-types/handlers";
|
|
187
175
|
export type {
|
|
188
176
|
DeleteContext,
|
|
189
177
|
EntityHookMap,
|
|
@@ -206,17 +194,21 @@ export type {
|
|
|
206
194
|
SearchPayloadContributorFn,
|
|
207
195
|
ValidationError,
|
|
208
196
|
ValidationHookFn,
|
|
209
|
-
} from "
|
|
197
|
+
} from "@cosmicdrift/kumiko-types/hooks";
|
|
210
198
|
export type {
|
|
211
199
|
HttpRouteDefinition,
|
|
212
200
|
HttpRouteHandler,
|
|
213
201
|
HttpRouteHandlerDeps,
|
|
214
202
|
HttpRouteMethod,
|
|
215
|
-
} from "
|
|
203
|
+
} from "@cosmicdrift/kumiko-types/http-route";
|
|
216
204
|
// Domain-identifier type aliases — see identifiers.ts for rationale.
|
|
217
|
-
export type { EntityId, TenantId } from "
|
|
218
|
-
export {
|
|
219
|
-
|
|
205
|
+
export type { EntityId, TenantId } from "@cosmicdrift/kumiko-types/identifiers";
|
|
206
|
+
export {
|
|
207
|
+
isSystemTenant,
|
|
208
|
+
parseTenantId,
|
|
209
|
+
SYSTEM_TENANT_ID,
|
|
210
|
+
} from "@cosmicdrift/kumiko-types/identifiers";
|
|
211
|
+
export type { NavDefinition } from "@cosmicdrift/kumiko-types/nav";
|
|
220
212
|
export type {
|
|
221
213
|
FromRule,
|
|
222
214
|
FromRuleKind,
|
|
@@ -227,7 +219,7 @@ export type {
|
|
|
227
219
|
SqlFragment,
|
|
228
220
|
WhereRule,
|
|
229
221
|
WhereRuleContext,
|
|
230
|
-
} from "
|
|
222
|
+
} from "@cosmicdrift/kumiko-types/ownership";
|
|
231
223
|
export type {
|
|
232
224
|
EntityProjectionExtension,
|
|
233
225
|
MspErrorMode,
|
|
@@ -237,14 +229,14 @@ export type {
|
|
|
237
229
|
ProjectionDefinition,
|
|
238
230
|
ProjectionTable,
|
|
239
231
|
SingleStreamApplyFn,
|
|
240
|
-
} from "
|
|
232
|
+
} from "@cosmicdrift/kumiko-types/projection";
|
|
241
233
|
export type {
|
|
242
234
|
BelongsToRelation,
|
|
243
235
|
EntityRelations,
|
|
244
236
|
HasManyRelation,
|
|
245
237
|
ManyToManyRelation,
|
|
246
238
|
RelationDefinition,
|
|
247
|
-
} from "
|
|
239
|
+
} from "@cosmicdrift/kumiko-types/relations";
|
|
248
240
|
export type {
|
|
249
241
|
ActionFormScreenDefinition,
|
|
250
242
|
ConfigEditScreenDefinition,
|
|
@@ -283,8 +275,8 @@ export type {
|
|
|
283
275
|
ScreenFilterOp,
|
|
284
276
|
ScreenSlots,
|
|
285
277
|
ToolbarAction,
|
|
286
|
-
} from "
|
|
287
|
-
export type { TargetRef } from "
|
|
278
|
+
} from "@cosmicdrift/kumiko-types/screen";
|
|
279
|
+
export type { TargetRef } from "@cosmicdrift/kumiko-types/target-ref";
|
|
288
280
|
export type {
|
|
289
281
|
Subscribe,
|
|
290
282
|
TreeAction,
|
|
@@ -293,5 +285,21 @@ export type {
|
|
|
293
285
|
TreeChildrenSubscribe,
|
|
294
286
|
TreeNode,
|
|
295
287
|
TreeNodeState,
|
|
296
|
-
} from "
|
|
297
|
-
export type { WorkspaceDefinition } from "
|
|
288
|
+
} from "@cosmicdrift/kumiko-types/tree-node";
|
|
289
|
+
export type { WorkspaceDefinition } from "@cosmicdrift/kumiko-types/workspace";
|
|
290
|
+
// Re-export types that were duplicated in types.ts but are canonical in constants.ts
|
|
291
|
+
export type {
|
|
292
|
+
ConcurrencyMode,
|
|
293
|
+
ConfigScope,
|
|
294
|
+
LifecycleHookType,
|
|
295
|
+
OnDeleteStrategy,
|
|
296
|
+
} from "../constants";
|
|
297
|
+
export { DEFAULT_CURRENCIES, isFileField } from "../field-helpers";
|
|
298
|
+
export { resolveName, withResponseData } from "../handler-helpers";
|
|
299
|
+
export { HookPhases } from "../hook-helpers";
|
|
300
|
+
export {
|
|
301
|
+
isExtensionEditSection,
|
|
302
|
+
isFormatSpec,
|
|
303
|
+
normalizeEditField,
|
|
304
|
+
normalizeListColumn,
|
|
305
|
+
} from "../screen-helpers";
|
package/src/engine/types/nav.ts
CHANGED
|
@@ -1,67 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import type { TreeAction } from "./tree-node";
|
|
4
|
-
|
|
5
|
-
// Nav entry declaration. Every feature that wants to appear in the app's
|
|
6
|
-
// navigation tree registers one or more entries via r.nav(). The engine
|
|
7
|
-
// keeps the list flat — ui-core's resolveNavigation assembles the parent/
|
|
8
|
-
// child tree at render time, so changes (toggles, access-gating) don't
|
|
9
|
-
// require re-indexing a tree shape server-side.
|
|
10
|
-
//
|
|
11
|
-
// Cross-feature references are allowed: `screen` may point at any
|
|
12
|
-
// registered screen QN, `parent` at any registered nav QN. The boot
|
|
13
|
-
// validator checks both references exist + rejects parent cycles.
|
|
14
|
-
export type NavDefinition = {
|
|
15
|
-
// Feature author writes the feature-local short id ("catalog"); the
|
|
16
|
-
// registry overwrites `id` with the qualified name ("shop:nav:catalog")
|
|
17
|
-
// in its stored copy. Callers of `registry.getNav(qn)` /
|
|
18
|
-
// `getTopLevelNavs()` / `getNavsByParent(...)` always see the qualified
|
|
19
|
-
// id — no parallel reverse index needed. `feature.navs[shortId]` on the
|
|
20
|
-
// unregistered FeatureDefinition keeps the short form.
|
|
21
|
-
readonly id: string;
|
|
22
|
-
// i18n translation key. Resolved at render time by the renderer's
|
|
23
|
-
// useTranslation hook; engine keeps it opaque.
|
|
24
|
-
readonly label: string;
|
|
25
|
-
// Icon key — whatever the icon registry of the active renderer understands.
|
|
26
|
-
// Engine doesn't validate; unknown icons surface as a missing icon on screen,
|
|
27
|
-
// not a boot failure.
|
|
28
|
-
readonly icon?: string;
|
|
29
|
-
// Qualified name of a parent nav entry ("<feature>:nav:<id>"). Omit for
|
|
30
|
-
// top-level entries. Boot-validator rejects cycles + dangling refs.
|
|
31
|
-
readonly parent?: string;
|
|
32
|
-
// Sort weight within the parent's children (lower = earlier). Ties are
|
|
33
|
-
// broken by registration order — features registered later appear lower.
|
|
34
|
-
readonly order?: number;
|
|
35
|
-
// Qualified name of the screen this entry navigates to
|
|
36
|
-
// ("<feature>:screen:<id>"). Omit for pure grouping entries (a parent-only
|
|
37
|
-
// nav node that renders a sub-tree but has no target screen itself).
|
|
38
|
-
readonly screen?: string;
|
|
39
|
-
// Polymorphes Klick-Ziel (öffnet die EditorPanel-Maske via Target-
|
|
40
|
-
// Resolver) — Alternative zu `screen`. Ein Knoten trägt screen XOR
|
|
41
|
-
// target; der Renderer dispatcht das target statt einen Route-Link zu
|
|
42
|
-
// rendern. Gespiegelt aus dem alten Visual-Tree (TreeNode.target).
|
|
43
|
-
readonly target?: TargetRef;
|
|
44
|
-
// Hover-Actions rechts in der Zeile (VS-Code-Pattern) — erst bei Hover
|
|
45
|
-
// sichtbar. Reihenfolge wie deklariert.
|
|
46
|
-
readonly actions?: readonly TreeAction[];
|
|
47
|
-
// „+"-Affordance am Knoten. Klick dispatcht createAction.target; der
|
|
48
|
-
// Provider weiß was „leer befüllen" für ihn heißt (neuer Page-Slug etc.).
|
|
49
|
-
readonly createAction?: TreeAction;
|
|
50
|
-
// Children kommen zur Laufzeit aus einem registrierten nav-provider
|
|
51
|
-
// (lazy beim Ausklappen, SSE-live via treeEntities), keyed auf diese
|
|
52
|
-
// Nav-QN. Macht den Knoten expandable auch ohne statische children.
|
|
53
|
-
readonly provider?: boolean;
|
|
54
|
-
// Role / openToAll gate. The nav resolver hides entries the user can't
|
|
55
|
-
// reach; leave unset to always show (engine stays un-opinionated about
|
|
56
|
-
// who sees what — apps that need default-deny can set { roles: [] }).
|
|
57
|
-
// If `screen` is set and `access` is left unset, the client-side nav
|
|
58
|
-
// builder (buildNavRegistrySliceForApp) fills this in from the target
|
|
59
|
-
// screen's own `access` — a nav entry never invites a role into a 403.
|
|
60
|
-
// An explicit `access` here always wins over the screen's.
|
|
61
|
-
readonly access?: AccessRule;
|
|
62
|
-
// Workspace QNs this entry self-assigns to. Merged at boot with any
|
|
63
|
-
// r.workspace({ nav: [...] }) explicit lists. Omit to leave workspace
|
|
64
|
-
// membership decided solely by the workspace's nav list (or both empty
|
|
65
|
-
// → entry belongs to no workspace).
|
|
66
|
-
readonly workspaces?: readonly string[];
|
|
67
|
-
};
|
|
1
|
+
// Legacy path — re-exported for callers still importing this module directly.
|
|
2
|
+
export * from "@cosmicdrift/kumiko-types/nav";
|
|
@@ -1,83 +1,2 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
// matchesRule(), buildOwnershipClause()) stays in engine/ownership.ts, which
|
|
4
|
-
// re-exports these for backwards compatibility.
|
|
5
|
-
|
|
6
|
-
import type { SessionUser } from "./handlers";
|
|
7
|
-
|
|
8
|
-
// Parameterised SQL fragment — produced by buildOwnershipClause + by the
|
|
9
|
-
// WhereRule escape-hatch. Caller weaves `sqlText` into a larger statement,
|
|
10
|
-
// renumbering placeholders if needed (shiftParams in engine/ownership.ts).
|
|
11
|
-
export type SqlFragment = {
|
|
12
|
-
readonly sqlText: string;
|
|
13
|
-
readonly params: readonly unknown[];
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// Reference spec supported by `from()`:
|
|
17
|
-
// "user:id" → user.id
|
|
18
|
-
// "user:tenantId" → user.tenantId (rarely needed — TenantDb scopes anyway)
|
|
19
|
-
// "claim:<featureName>:<key>" → user.claims["<featureName>:<key>"]
|
|
20
|
-
//
|
|
21
|
-
// The string form is keyed so the framework can look up the referenced
|
|
22
|
-
// Registry entry at boot (Claim-QN exists? Column type compatible?). A typed
|
|
23
|
-
// object form would force features to import each other's handles — the
|
|
24
|
-
// whole point of H.2's unified path is string-based references, no imports.
|
|
25
|
-
export type OwnershipRef = string;
|
|
26
|
-
|
|
27
|
-
// Resolved during `from()` — the parser eagerly splits the prefix so the
|
|
28
|
-
// runtime evaluator avoids string-parsing on every row. `kind` drives the
|
|
29
|
-
// evaluator branch; the rest is the resolved metadata.
|
|
30
|
-
export type FromRuleKind = "user" | "claim";
|
|
31
|
-
|
|
32
|
-
export type FromRule = {
|
|
33
|
-
readonly kind: "from";
|
|
34
|
-
readonly refKind: FromRuleKind;
|
|
35
|
-
// For "user:id" → "id"; for "user:tenantId" → "tenantId".
|
|
36
|
-
// For "claim:<featureName>:<key>" → "<featureName>:<key>" (the full QN,
|
|
37
|
-
// which is exactly the key under which the JWT stores the value).
|
|
38
|
-
readonly refPath: string;
|
|
39
|
-
// Row-column to match against. For claim rules defaults to the claim's
|
|
40
|
-
// shortName (second segment of the claim QN). For user-rules the column
|
|
41
|
-
// is always explicit.
|
|
42
|
-
readonly column: string;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Context passed to a WhereRule escape-hatch. The author returns a SqlFragment
|
|
46
|
-
// whose placeholders start at `paramStart` ($N, $N+1, ...); the framework
|
|
47
|
-
// concatenates the fragment into the larger query.
|
|
48
|
-
export type WhereRuleContext<TTable = unknown> = {
|
|
49
|
-
readonly table: TTable;
|
|
50
|
-
readonly tableName: string;
|
|
51
|
-
readonly paramStart: number;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type WhereRule<TTable = unknown> = {
|
|
55
|
-
readonly kind: "where";
|
|
56
|
-
readonly where: (user: SessionUser, ctx: WhereRuleContext<TTable>) => SqlFragment;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// "all" collapses to a primitive so map authors can write `Admin: "all"`
|
|
60
|
-
// without importing a helper.
|
|
61
|
-
export type OwnershipRule = "all" | FromRule | WhereRule;
|
|
62
|
-
|
|
63
|
-
// Per-role map: every key is a role name, value is the rule that role
|
|
64
|
-
// satisfies to pass the access check.
|
|
65
|
-
export type OwnershipMap = Readonly<Record<string, OwnershipRule>>;
|
|
66
|
-
|
|
67
|
-
// Result of buildOwnershipClause. The discriminant lets the caller handle
|
|
68
|
-
// the three outcomes without inspecting SQL internals:
|
|
69
|
-
//
|
|
70
|
-
// "pass" → user is unrestricted. Run the query as-is.
|
|
71
|
-
// "empty" → user has a role mapped but no rule accepts any row (missing
|
|
72
|
-
// claim, empty array, role not in map). Skip the DB call entirely
|
|
73
|
-
// — returning [] is equivalent and avoids a pointless roundtrip.
|
|
74
|
-
// "sql" → apply the parameterised fragment as an AND on the query.
|
|
75
|
-
// Caller is responsible for renumbering placeholders when
|
|
76
|
-
// concatenating with other fragments (see `shiftParams`).
|
|
77
|
-
//
|
|
78
|
-
// "empty" vs. "pass" is the critical distinction for a safe default:
|
|
79
|
-
// undefined/pass = allow, empty = deny-by-construction.
|
|
80
|
-
export type OwnershipClause =
|
|
81
|
-
| { readonly kind: "pass" }
|
|
82
|
-
| { readonly kind: "empty" }
|
|
83
|
-
| { readonly kind: "sql"; readonly sqlText: string; readonly params: readonly unknown[] };
|
|
1
|
+
// Legacy path — re-exported for callers still importing this module directly.
|
|
2
|
+
export * from "@cosmicdrift/kumiko-types/ownership";
|