@cosmicdrift/kumiko-framework 0.146.4 → 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.
Files changed (68) hide show
  1. package/package.json +6 -2
  2. package/src/api/auth-routes.ts +32 -67
  3. package/src/api/routes.ts +1 -3
  4. package/src/bun-db/__tests__/PATTERN.md +0 -1
  5. package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +29 -3
  6. package/src/db/__tests__/schema-inspection.test.ts +7 -0
  7. package/src/db/event-store-executor-context.ts +398 -0
  8. package/src/db/event-store-executor-read.ts +276 -0
  9. package/src/db/event-store-executor-write.ts +615 -0
  10. package/src/db/event-store-executor.ts +29 -1166
  11. package/src/db/queries/shadow-swap.ts +3 -1
  12. package/src/db/schema-inspection.ts +1 -1
  13. package/src/engine/__tests__/boot-validator-dashboard.test.ts +10 -0
  14. package/src/engine/__tests__/engine.test.ts +45 -1
  15. package/src/engine/__tests__/event-migration-declarative.test.ts +6 -0
  16. package/src/engine/__tests__/membership-roles.test.ts +4 -10
  17. package/src/engine/__tests__/screen.test.ts +26 -0
  18. package/src/engine/boot-validator/entity-list-screens.ts +1 -1
  19. package/src/engine/boot-validator/gdpr-storage.ts +1 -1
  20. package/src/engine/boot-validator/index.ts +12 -8
  21. package/src/engine/boot-validator/nav.ts +120 -0
  22. package/src/engine/boot-validator/{screens-nav.ts → screens.ts} +12 -190
  23. package/src/engine/boot-validator/workspaces.ts +68 -0
  24. package/src/engine/define-feature.ts +77 -1011
  25. package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/feature.ts +9 -0
  26. package/src/engine/feature-ast/__tests__/fixtures/cross-file-registrar/screens.ts +4 -0
  27. package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +18 -8
  28. package/src/engine/feature-ast/__tests__/parse.test.ts +291 -2
  29. package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
  30. package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +73 -0
  31. package/src/engine/feature-ast/extractors/round1.ts +3 -3
  32. package/src/engine/feature-ast/extractors/round4.ts +45 -10
  33. package/src/engine/feature-ast/extractors/shared.ts +64 -6
  34. package/src/engine/feature-ast/parse.ts +123 -10
  35. package/src/engine/feature-ast/patterns.ts +14 -7
  36. package/src/engine/feature-ast/render.ts +28 -7
  37. package/src/engine/feature-builder-state.ts +165 -0
  38. package/src/engine/feature-config-events-jobs.ts +303 -0
  39. package/src/engine/feature-entity-handlers.ts +161 -0
  40. package/src/engine/feature-ui-extensions.ts +413 -0
  41. package/src/engine/index.ts +0 -2
  42. package/src/engine/pattern-library/library.ts +44 -1131
  43. package/src/engine/pattern-library/mixed-schemas.ts +484 -0
  44. package/src/engine/pattern-library/opaque-schemas.ts +124 -0
  45. package/src/engine/pattern-library/shared-fields.ts +80 -0
  46. package/src/engine/pattern-library/static-schemas.ts +456 -0
  47. package/src/engine/registry-facade.ts +349 -0
  48. package/src/engine/registry-ingest.ts +473 -0
  49. package/src/engine/registry-state.ts +388 -0
  50. package/src/engine/registry-validate.ts +660 -0
  51. package/src/engine/registry.ts +79 -1695
  52. package/src/engine/types/screen.ts +4 -2
  53. package/src/engine/types/tree-node.ts +2 -5
  54. package/src/event-store/snapshot.ts +7 -7
  55. package/src/i18n/required-surface-keys.ts +2 -0
  56. package/src/jobs/job-runner.ts +1 -1
  57. package/src/observability/standard-metrics.ts +4 -3
  58. package/src/pipeline/dispatch-batch.ts +3 -3
  59. package/src/pipeline/dispatch-shared.ts +17 -10
  60. package/src/pipeline/event-dispatcher-admin.ts +289 -0
  61. package/src/pipeline/event-dispatcher-delivery.ts +264 -0
  62. package/src/pipeline/event-dispatcher.ts +28 -540
  63. package/src/pipeline/projection-rebuild.ts +1 -1
  64. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +95 -0
  65. package/src/stack/test-stack.ts +46 -1
  66. package/src/testing/boot-validator-fixture.ts +1 -2
  67. package/src/engine/__tests__/deep-link.test.ts +0 -35
  68. 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
+ }