@cosmicdrift/kumiko-framework 0.146.3 → 0.147.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 +6 -2
- package/src/api/auth-routes.ts +32 -67
- package/src/api/routes.ts +1 -3
- package/src/bun-db/__tests__/PATTERN.md +0 -1
- package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +29 -3
- package/src/db/__tests__/schema-inspection.test.ts +7 -0
- package/src/db/event-store-executor-context.ts +398 -0
- package/src/db/event-store-executor-read.ts +276 -0
- package/src/db/event-store-executor-write.ts +615 -0
- package/src/db/event-store-executor.ts +29 -1166
- package/src/db/queries/shadow-swap.ts +3 -1
- package/src/db/schema-inspection.ts +1 -1
- package/src/engine/__tests__/boot-validator-dashboard.test.ts +10 -0
- package/src/engine/__tests__/engine.test.ts +45 -1
- package/src/engine/__tests__/event-migration-declarative.test.ts +6 -0
- package/src/engine/__tests__/membership-roles.test.ts +4 -10
- package/src/engine/__tests__/screen.test.ts +26 -0
- package/src/engine/boot-validator/entity-list-screens.ts +1 -1
- package/src/engine/boot-validator/gdpr-storage.ts +1 -1
- package/src/engine/boot-validator/index.ts +12 -8
- package/src/engine/boot-validator/nav.ts +120 -0
- package/src/engine/boot-validator/{screens-nav.ts → screens.ts} +12 -190
- package/src/engine/boot-validator/workspaces.ts +68 -0
- package/src/engine/define-feature.ts +77 -1011
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/feature.ts +9 -0
- package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/screens.ts +4 -0
- package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +18 -8
- package/src/engine/feature-ast/__tests__/parse.test.ts +291 -2
- package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +73 -0
- package/src/engine/feature-ast/extractors/round1.ts +3 -3
- package/src/engine/feature-ast/extractors/round4.ts +45 -10
- package/src/engine/feature-ast/extractors/shared.ts +64 -6
- package/src/engine/feature-ast/parse.ts +123 -10
- package/src/engine/feature-ast/patterns.ts +14 -7
- package/src/engine/feature-ast/render.ts +28 -7
- package/src/engine/feature-builder-state.ts +165 -0
- package/src/engine/feature-config-events-jobs.ts +303 -0
- package/src/engine/feature-entity-handlers.ts +161 -0
- package/src/engine/feature-ui-extensions.ts +413 -0
- package/src/engine/index.ts +0 -2
- package/src/engine/pattern-library/library.ts +44 -1131
- package/src/engine/pattern-library/mixed-schemas.ts +484 -0
- package/src/engine/pattern-library/opaque-schemas.ts +124 -0
- package/src/engine/pattern-library/shared-fields.ts +80 -0
- package/src/engine/pattern-library/static-schemas.ts +456 -0
- package/src/engine/registry-facade.ts +349 -0
- package/src/engine/registry-ingest.ts +473 -0
- package/src/engine/registry-state.ts +388 -0
- package/src/engine/registry-validate.ts +660 -0
- package/src/engine/registry.ts +79 -1695
- package/src/engine/types/screen.ts +4 -2
- package/src/engine/types/tree-node.ts +2 -5
- package/src/event-store/snapshot.ts +7 -7
- package/src/i18n/required-surface-keys.ts +2 -0
- package/src/jobs/job-runner.ts +1 -1
- package/src/observability/standard-metrics.ts +4 -3
- package/src/pipeline/dispatch-batch.ts +3 -3
- package/src/pipeline/dispatch-shared.ts +17 -10
- package/src/pipeline/event-dispatcher-admin.ts +289 -0
- package/src/pipeline/event-dispatcher-delivery.ts +264 -0
- package/src/pipeline/event-dispatcher.ts +28 -540
- package/src/pipeline/projection-rebuild.ts +1 -1
- package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +95 -0
- package/src/stack/test-stack.ts +46 -1
- package/src/testing/boot-validator-fixture.ts +1 -2
- package/src/engine/__tests__/deep-link.test.ts +0 -35
- package/src/engine/deep-link.ts +0 -23
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import type { IncomingRelation, RegistryState } from "./registry-state";
|
|
2
|
+
import { filterByPhase, filterOwned } from "./registry-state";
|
|
3
|
+
import type {
|
|
4
|
+
AuthClaimsHookDef,
|
|
5
|
+
ClaimKeyDefinition,
|
|
6
|
+
ConfigKeyDefinition,
|
|
7
|
+
ConfigSeedDef,
|
|
8
|
+
EntityDefinition,
|
|
9
|
+
EntityRelations,
|
|
10
|
+
EventDef,
|
|
11
|
+
FeatureDefinition,
|
|
12
|
+
HookPhase,
|
|
13
|
+
JobDefinition,
|
|
14
|
+
MultiStreamProjectionDefinition,
|
|
15
|
+
NavDefinition,
|
|
16
|
+
NotificationDefinition,
|
|
17
|
+
PostDeleteHookFn,
|
|
18
|
+
PostQueryHookFn,
|
|
19
|
+
PostSaveHookFn,
|
|
20
|
+
PreDeleteHookFn,
|
|
21
|
+
PreQueryHookFn,
|
|
22
|
+
PreSaveHookFn,
|
|
23
|
+
ProjectionDefinition,
|
|
24
|
+
QueryHandlerDef,
|
|
25
|
+
RawTableDef,
|
|
26
|
+
ReferenceDataDef,
|
|
27
|
+
RegistrarExtensionDef,
|
|
28
|
+
RegistrarExtensionRegistration,
|
|
29
|
+
Registry,
|
|
30
|
+
ScreenDefinition,
|
|
31
|
+
SearchPayloadContributorFn,
|
|
32
|
+
SecretKeyDefinition,
|
|
33
|
+
TranslationKeys,
|
|
34
|
+
TreeActionDef,
|
|
35
|
+
WorkspaceDefinition,
|
|
36
|
+
WriteHandlerDef,
|
|
37
|
+
} from "./types";
|
|
38
|
+
|
|
39
|
+
// Builds the public Registry surface (64 getters) bound to a specific
|
|
40
|
+
// RegistryState instance — pure move-diff from createRegistry's former
|
|
41
|
+
// return-object literal, only the closure-captured variable changed
|
|
42
|
+
// (state.X instead of bare X, done in the RegistryState-threading step).
|
|
43
|
+
export function buildRegistryFacade(state: RegistryState): Registry {
|
|
44
|
+
return {
|
|
45
|
+
features: state.featureMap,
|
|
46
|
+
|
|
47
|
+
getFeature(name: string): FeatureDefinition | undefined {
|
|
48
|
+
return state.featureMap.get(name);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
hasRateLimitedHandler(): boolean {
|
|
52
|
+
return state.hasRateLimitedHandlerCached;
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
getEntity(name: string): EntityDefinition | undefined {
|
|
56
|
+
return state.entityMap.get(name);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
getAllEntities(): ReadonlyMap<string, EntityDefinition> {
|
|
60
|
+
return state.entityMap;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
getWriteHandler(name: string): WriteHandlerDef | undefined {
|
|
64
|
+
return state.writeHandlerMap.get(name);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
getQueryHandler(name: string): QueryHandlerDef | undefined {
|
|
68
|
+
return state.queryHandlerMap.get(name);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
getSearchableFields(entityName: string): readonly string[] {
|
|
72
|
+
return state.searchableFieldsCache.get(entityName) ?? [];
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
getSortableFields(entityName: string): readonly string[] {
|
|
76
|
+
return state.sortableFieldsCache.get(entityName) ?? [];
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
getRelations(entityName: string): EntityRelations {
|
|
80
|
+
return (state.relationMap.get(entityName) ?? {}) as EntityRelations; // @cast-boundary schema-walk
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
getSearchIncludes(entityName: string): ReadonlyMap<string, readonly string[]> {
|
|
84
|
+
return state.searchIncludesCache.get(entityName) ?? new Map();
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
getIncomingRelations(entityName: string): readonly IncomingRelation[] {
|
|
88
|
+
return state.incomingRelationsCache.get(entityName) ?? [];
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
getPreSaveHooks(
|
|
92
|
+
name: string,
|
|
93
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
94
|
+
): readonly PreSaveHookFn[] {
|
|
95
|
+
return filterOwned(state.preSaveHooks.get(name), effectiveFeatures);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
getPostSaveHooks(
|
|
99
|
+
name: string,
|
|
100
|
+
phase?: HookPhase,
|
|
101
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
102
|
+
): readonly PostSaveHookFn[] {
|
|
103
|
+
return filterByPhase(state.postSaveHooks.get(name), phase, effectiveFeatures);
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
getPreDeleteHooks(
|
|
107
|
+
name: string,
|
|
108
|
+
phase?: HookPhase,
|
|
109
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
110
|
+
): readonly PreDeleteHookFn[] {
|
|
111
|
+
return filterByPhase(state.preDeleteHooks.get(name), phase, effectiveFeatures);
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
getPostDeleteHooks(
|
|
115
|
+
name: string,
|
|
116
|
+
phase?: HookPhase,
|
|
117
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
118
|
+
): readonly PostDeleteHookFn[] {
|
|
119
|
+
return filterByPhase(state.postDeleteHooks.get(name), phase, effectiveFeatures);
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
getPreQueryHooks(
|
|
123
|
+
name: string,
|
|
124
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
125
|
+
): readonly PreQueryHookFn[] {
|
|
126
|
+
return filterOwned(state.preQueryHooks.get(name), effectiveFeatures);
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
getPostQueryHooks(
|
|
130
|
+
name: string,
|
|
131
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
132
|
+
): readonly PostQueryHookFn[] {
|
|
133
|
+
return filterOwned(state.postQueryHooks.get(name), effectiveFeatures);
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// Entity hooks — fire for all writes on an entity
|
|
137
|
+
getEntityPostSaveHooks(
|
|
138
|
+
entityName: string,
|
|
139
|
+
phase?: HookPhase,
|
|
140
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
141
|
+
): readonly PostSaveHookFn[] {
|
|
142
|
+
return filterByPhase(state.entityPostSaveHooks.get(entityName), phase, effectiveFeatures);
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
getEntityPreDeleteHooks(
|
|
146
|
+
entityName: string,
|
|
147
|
+
phase?: HookPhase,
|
|
148
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
149
|
+
): readonly PreDeleteHookFn[] {
|
|
150
|
+
return filterByPhase(state.entityPreDeleteHooks.get(entityName), phase, effectiveFeatures);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
getEntityPostDeleteHooks(
|
|
154
|
+
entityName: string,
|
|
155
|
+
phase?: HookPhase,
|
|
156
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
157
|
+
): readonly PostDeleteHookFn[] {
|
|
158
|
+
return filterByPhase(state.entityPostDeleteHooks.get(entityName), phase, effectiveFeatures);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
getEntityPostQueryHooks(
|
|
162
|
+
entityName: string,
|
|
163
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
164
|
+
): readonly PostQueryHookFn[] {
|
|
165
|
+
return filterOwned(state.entityPostQueryHooks.get(entityName), effectiveFeatures);
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// F3 — Search-Payload-Extension contributors for an entity. Used by
|
|
169
|
+
// `buildSearchDocument` in system-hooks.ts to enrich the indexed payload.
|
|
170
|
+
// `effectiveFeatures` filters out contributors owned by feature-toggle-
|
|
171
|
+
// disabled features (parallel to getEntityPostQueryHooks etc.).
|
|
172
|
+
getSearchPayloadExtensions(
|
|
173
|
+
entityName: string,
|
|
174
|
+
effectiveFeatures?: ReadonlySet<string>,
|
|
175
|
+
): readonly SearchPayloadContributorFn[] {
|
|
176
|
+
return filterOwned(state.searchPayloadExtensions.get(entityName), effectiveFeatures);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
getAllTranslations(): TranslationKeys {
|
|
180
|
+
return state.mergedTranslations;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
getHandlerEntity(qualifiedHandler: string): string | undefined {
|
|
184
|
+
return state.handlerEntityMap.get(qualifiedHandler);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
isHandlerSystemScoped(qualifiedHandler: string): boolean {
|
|
188
|
+
const featureName = state.handlerFeatureMap.get(qualifiedHandler);
|
|
189
|
+
if (!featureName) return false;
|
|
190
|
+
return state.featureMap.get(featureName)?.systemScope ?? false;
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
getHandlerFeature(qualifiedHandler: string): string | undefined {
|
|
194
|
+
return state.handlerFeatureMap.get(qualifiedHandler);
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
getAllMetrics() {
|
|
198
|
+
return state.metricMap;
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
getAllSecretKeys(): ReadonlyMap<string, SecretKeyDefinition> {
|
|
202
|
+
return state.secretKeyMap;
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
getSecretKey(qualifiedName: string): SecretKeyDefinition | undefined {
|
|
206
|
+
return state.secretKeyMap.get(qualifiedName);
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
getConfigKey(qualifiedKey: string): ConfigKeyDefinition | undefined {
|
|
210
|
+
return state.configKeyMap.get(qualifiedKey);
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
getAllConfigKeys(): ReadonlyMap<string, ConfigKeyDefinition> {
|
|
214
|
+
return state.configKeyMap;
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
getJob(qualifiedName: string): JobDefinition | undefined {
|
|
218
|
+
return state.jobMap.get(qualifiedName);
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
getAllJobs(): ReadonlyMap<string, JobDefinition> {
|
|
222
|
+
return state.jobMap;
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
getEvent(qualifiedName: string): EventDef | undefined {
|
|
226
|
+
return state.eventMap.get(qualifiedName);
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
getEventUpcasters() {
|
|
230
|
+
return state.eventUpcasterMap;
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
getExtension(name: string): RegistrarExtensionDef | undefined {
|
|
234
|
+
return state.extensionMap.get(name);
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
getExtensionUsages(extensionName: string): readonly RegistrarExtensionRegistration[] {
|
|
238
|
+
return state.extensionUsages.filter((u) => u.extensionName === extensionName);
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
getAllExtensionSelectors(): ReadonlyMap<string, string> {
|
|
242
|
+
return state.extensionSelectorMap;
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
getAllNotifications(): ReadonlyMap<string, NotificationDefinition> {
|
|
246
|
+
return state.notificationMap;
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
getAllReferenceData(): readonly ReferenceDataDef[] {
|
|
250
|
+
return state.allReferenceData;
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
getAllConfigSeeds(): readonly ConfigSeedDef[] {
|
|
254
|
+
return state.allConfigSeeds;
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
getProjectionsForSource(entityName: string): readonly ProjectionDefinition[] {
|
|
258
|
+
return state.projectionsBySource.get(entityName) ?? [];
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
getAllProjections(): ReadonlyMap<string, ProjectionDefinition> {
|
|
262
|
+
return state.projectionMap;
|
|
263
|
+
},
|
|
264
|
+
|
|
265
|
+
getAllRawTables(): ReadonlyMap<string, RawTableDef> {
|
|
266
|
+
return state.rawTableMap;
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
getAllMultiStreamProjections(): ReadonlyMap<string, MultiStreamProjectionDefinition> {
|
|
270
|
+
return state.multiStreamProjectionMap;
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
getMultiStreamProjectionFeature(qualifiedName: string): string | undefined {
|
|
274
|
+
return state.multiStreamProjectionFeatureMap.get(qualifiedName);
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
getAuthClaimsHooks(): readonly AuthClaimsHookDef[] {
|
|
278
|
+
return state.authClaimsHooks;
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
getAllClaimKeys(): ReadonlyMap<string, ClaimKeyDefinition> {
|
|
282
|
+
return state.claimKeyMap;
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
getClaimKey(qualifiedName: string): ClaimKeyDefinition | undefined {
|
|
286
|
+
return state.claimKeyMap.get(qualifiedName);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
getAllScreens(): ReadonlyMap<string, ScreenDefinition> {
|
|
290
|
+
return state.screenMap;
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
getScreen(qualifiedName: string): ScreenDefinition | undefined {
|
|
294
|
+
return state.screenMap.get(qualifiedName);
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
getScreenFeature(qualifiedName: string): string | undefined {
|
|
298
|
+
return state.screenFeatureMap.get(qualifiedName);
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
getScreensByEntity(entityName: string): readonly ScreenDefinition[] {
|
|
302
|
+
return state.screensByEntity.get(entityName) ?? [];
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
getAllNavs(): ReadonlyMap<string, NavDefinition> {
|
|
306
|
+
return state.navMap;
|
|
307
|
+
},
|
|
308
|
+
|
|
309
|
+
getNav(qualifiedName: string): NavDefinition | undefined {
|
|
310
|
+
return state.navMap.get(qualifiedName);
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
getNavFeature(qualifiedName: string): string | undefined {
|
|
314
|
+
return state.navFeatureMap.get(qualifiedName);
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
getNavsByParent(parentQualifiedName: string): readonly NavDefinition[] {
|
|
318
|
+
return state.navsByParent.get(parentQualifiedName) ?? [];
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
getTopLevelNavs(): readonly NavDefinition[] {
|
|
322
|
+
return state.topLevelNavs;
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
getAllWorkspaces(): ReadonlyMap<string, WorkspaceDefinition> {
|
|
326
|
+
return state.workspaceMap;
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
getWorkspace(qualifiedName: string): WorkspaceDefinition | undefined {
|
|
330
|
+
return state.workspaceMap.get(qualifiedName);
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
getWorkspaceFeature(qualifiedName: string): string | undefined {
|
|
334
|
+
return state.workspaceFeatureMap.get(qualifiedName);
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
getWorkspaceNavs(workspaceQualifiedName: string): readonly string[] {
|
|
338
|
+
return state.navsByWorkspace.get(workspaceQualifiedName) ?? [];
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
getDefaultWorkspace(): WorkspaceDefinition | undefined {
|
|
342
|
+
return state.defaultWorkspace;
|
|
343
|
+
},
|
|
344
|
+
|
|
345
|
+
getTreeActions(featureName: string): Readonly<Record<string, TreeActionDef>> | undefined {
|
|
346
|
+
return state.treeActionsMap.get(featureName);
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
}
|