@cosmicdrift/kumiko-framework 0.258.0 → 0.259.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 (121) hide show
  1. package/package.json +3 -3
  2. package/src/__tests__/anonymous-access.integration.test.ts +3 -3
  3. package/src/__tests__/derived-fields.integration.test.ts +1 -1
  4. package/src/__tests__/entity-list-limits.integration.test.ts +1 -1
  5. package/src/__tests__/error-contract.integration.test.ts +1 -1
  6. package/src/__tests__/ownership-where-write-path.integration.test.ts +1 -1
  7. package/src/__tests__/ownership.integration.test.ts +7 -3
  8. package/src/__tests__/schema-cli-rebuild.integration.test.ts +2 -2
  9. package/src/__tests__/transition-guard.integration.test.ts +3 -3
  10. package/src/api/__tests__/api.test.ts +21 -3
  11. package/src/api/__tests__/batch.integration.test.ts +3 -3
  12. package/src/api/__tests__/body-limit.test.ts +7 -1
  13. package/src/api/__tests__/dispatcher-live.integration.test.ts +1 -1
  14. package/src/api/__tests__/http-route-rate-limit.integration.test.ts +7 -1
  15. package/src/api/__tests__/metrics-endpoint.test.ts +7 -1
  16. package/src/api/__tests__/nested-write.integration.test.ts +3 -3
  17. package/src/api/__tests__/server-boot-guards.test.ts +11 -4
  18. package/src/bun-db/__tests__/write-brand.test.ts +4 -1
  19. package/src/changes.json +6 -0
  20. package/src/db/__tests__/apply-entity-event-tenant.integration.test.ts +1 -1
  21. package/src/db/__tests__/assert-exists-in.integration.test.ts +1 -1
  22. package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +2 -2
  23. package/src/db/__tests__/collect-table-metas.test.ts +9 -5
  24. package/src/db/__tests__/compound-types.test.ts +1 -1
  25. package/src/db/__tests__/config-seed.integration.test.ts +3 -3
  26. package/src/db/__tests__/eagerload.integration.test.ts +7 -7
  27. package/src/db/__tests__/entity-table-from-registry.test.ts +8 -2
  28. package/src/db/__tests__/entity-table-meta-source.test.ts +7 -3
  29. package/src/db/__tests__/entity-table-types.test.ts +4 -4
  30. package/src/db/__tests__/event-store-executor-list.integration.test.ts +9 -2
  31. package/src/db/__tests__/event-store-executor-reference-search.integration.test.ts +4 -4
  32. package/src/db/__tests__/event-store-executor-reference-sort.integration.test.ts +3 -3
  33. package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +4 -4
  34. package/src/db/__tests__/event-store-executor.integration.test.ts +11 -6
  35. package/src/db/__tests__/event-store-system-stream.integration.test.ts +2 -2
  36. package/src/db/__tests__/implicit-projection-equivalence.integration.test.ts +6 -4
  37. package/src/db/__tests__/located-timestamp.test.ts +1 -1
  38. package/src/db/__tests__/money.test.ts +1 -1
  39. package/src/db/__tests__/multi-row-insert.integration.test.ts +3 -3
  40. package/src/db/__tests__/schema-migration.integration.test.ts +30 -14
  41. package/src/db/__tests__/source-shadow-create.integration.test.ts +1 -1
  42. package/src/db/__tests__/table-builder-indexes.test.ts +14 -12
  43. package/src/db/__tests__/table-builder-required.test.ts +2 -2
  44. package/src/db/__tests__/tenant-db-where-merge.integration.test.ts +1 -1
  45. package/src/db/__tests__/tenant-db-where-merge.test.ts +5 -2
  46. package/src/db/__tests__/tenant-db.integration.test.ts +2 -2
  47. package/src/db/__tests__/unique-violation-mapping.integration.test.ts +2 -2
  48. package/src/engine/__tests__/boot-validator-action-wiring.test.ts +50 -8
  49. package/src/engine/__tests__/boot-validator-dashboard.test.ts +138 -1
  50. package/src/engine/__tests__/boot-validator-detail-for.test.ts +28 -4
  51. package/src/engine/__tests__/boot-validator-entity-list.test.ts +20 -4
  52. package/src/engine/__tests__/boot-validator-i18n-keys.test.ts +9 -3
  53. package/src/engine/__tests__/boot-validator.test.ts +488 -91
  54. package/src/engine/__tests__/define-feature-entity-mapping.test.ts +2 -2
  55. package/src/engine/__tests__/engine.test.ts +128 -37
  56. package/src/engine/__tests__/entity-handlers.test.ts +3 -3
  57. package/src/engine/__tests__/entity-presave-wiring.integration.test.ts +4 -4
  58. package/src/engine/__tests__/entity-restore-tenant-isolation.integration.test.ts +1 -1
  59. package/src/engine/__tests__/entity-write-crosstenant.integration.test.ts +1 -1
  60. package/src/engine/__tests__/extend-entity-projection.test.ts +1 -1
  61. package/src/engine/__tests__/extends-registrar.test.ts +13 -3
  62. package/src/engine/__tests__/factories-long-text.test.ts +10 -6
  63. package/src/engine/__tests__/feature-crud-shorthand.test.ts +1 -1
  64. package/src/engine/__tests__/feature-crud-verb-access.integration.test.ts +1 -1
  65. package/src/engine/__tests__/multiselect-filter.integration.test.ts +1 -1
  66. package/src/engine/__tests__/nav.test.ts +1 -1
  67. package/src/engine/__tests__/pipeline-handler.integration.test.ts +1 -1
  68. package/src/engine/__tests__/pipeline-observability.integration.test.ts +1 -1
  69. package/src/engine/__tests__/pipeline-performance.integration.test.ts +1 -1
  70. package/src/engine/__tests__/projection-detail-actions.test.ts +6 -1
  71. package/src/engine/__tests__/projection-detail-singleton.test.ts +6 -1
  72. package/src/engine/__tests__/projection-detail-tabs.test.ts +6 -1
  73. package/src/engine/__tests__/projection-list-filter.integration.test.ts +7 -2
  74. package/src/engine/__tests__/projection.test.ts +1 -1
  75. package/src/engine/__tests__/registry.test.ts +13 -3
  76. package/src/engine/__tests__/schema-builder.test.ts +39 -11
  77. package/src/engine/__tests__/screen.test.ts +21 -15
  78. package/src/engine/__tests__/search-payload-extension.test.ts +15 -3
  79. package/src/engine/__tests__/store-table.test.ts +6 -3
  80. package/src/engine/__tests__/validate-projection-allowlist.test.ts +12 -4
  81. package/src/engine/__tests__/validation-hooks.test.ts +7 -1
  82. package/src/engine/boot-validator/__tests__/ownership.test.ts +2 -2
  83. package/src/engine/boot-validator/query-refs.ts +11 -0
  84. package/src/engine/boot-validator/screens.ts +45 -2
  85. package/src/engine/build-config-feature-schema.ts +2 -2
  86. package/src/engine/index.ts +2 -0
  87. package/src/engine/types/index.ts +2 -0
  88. package/src/es-ops/__tests__/runner.integration.test.ts +1 -1
  89. package/src/event-store/__tests__/upcaster.integration.test.ts +1 -1
  90. package/src/files/__tests__/file-field-pipeline.integration.test.ts +1 -1
  91. package/src/files/__tests__/files.integration.test.ts +1 -1
  92. package/src/files/file-ref-entity.ts +9 -9
  93. package/src/i18n/__tests__/i18n.test.ts +7 -1
  94. package/src/migrations/__tests__/kumiko-drift.integration.test.ts +3 -1
  95. package/src/migrations/__tests__/pending-rebuilds.integration.test.ts +2 -2
  96. package/src/pipeline/__tests__/archive-stream.integration.test.ts +1 -1
  97. package/src/pipeline/__tests__/cascade-handler.integration.test.ts +23 -8
  98. package/src/pipeline/__tests__/cascade-handler.test.ts +18 -3
  99. package/src/pipeline/__tests__/causation-chain.integration.test.ts +1 -1
  100. package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +5 -3
  101. package/src/pipeline/__tests__/dispatcher.test.ts +42 -6
  102. package/src/pipeline/__tests__/domain-events-projections.integration.test.ts +2 -2
  103. package/src/pipeline/__tests__/fetch-for-writing.integration.test.ts +1 -1
  104. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +42 -6
  105. package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +2 -2
  106. package/src/pipeline/__tests__/msp-multi-hop.integration.test.ts +1 -1
  107. package/src/pipeline/__tests__/msp-rebuild.integration.test.ts +6 -2
  108. package/src/pipeline/__tests__/multi-stream-projection.integration.test.ts +6 -2
  109. package/src/pipeline/__tests__/perf-rebuild.integration.test.ts +1 -1
  110. package/src/pipeline/__tests__/post-query-hook.integration.test.ts +3 -3
  111. package/src/pipeline/__tests__/projection-rebuild.integration.test.ts +3 -3
  112. package/src/pipeline/__tests__/query-projection.integration.test.ts +1 -1
  113. package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +2 -2
  114. package/src/pipeline/__tests__/search-consumer-named-events.integration.test.ts +13 -2
  115. package/src/pipeline/__tests__/try-append-event.integration.test.ts +1 -1
  116. package/src/search/__tests__/reindex-entity.integration.test.ts +6 -1
  117. package/src/search/__tests__/search-pii-derived-index.integration.test.ts +7 -1
  118. package/src/stack/__tests__/ownership-boot-guard.integration.test.ts +1 -1
  119. package/src/testing/__tests__/e2e-generator.test.ts +15 -5
  120. package/src/testing/shared-entities.ts +4 -2
  121. package/src/ui-types/index.ts +2 -0
@@ -3,7 +3,7 @@ import { z } from "zod";
3
3
  import { requiredKeysFromScreen } from "../../i18n/required-surface-keys";
4
4
  import { validateBoot } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
6
- import type { DashboardScreenDefinition } from "../types/screen";
6
+ import type { DashboardScreenDefinition, DashboardScreenPanel } from "../types/screen";
7
7
 
8
8
  const STAT_PANEL = {
9
9
  kind: "stat",
@@ -242,3 +242,140 @@ describe("validateBoot — dashboard screens", () => {
242
242
  expect(keys).not.toContain("custom-panel");
243
243
  });
244
244
  });
245
+
246
+ const catalogFeature = defineFeature("catalog", (r) => {
247
+ r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
248
+ access: { openToAll: true },
249
+ });
250
+ r.queryHandler("items:status", z.object({}), async () => ({ enabled: true }), {
251
+ access: { openToAll: true },
252
+ });
253
+ r.screen({
254
+ id: "items",
255
+ type: "projectionList",
256
+ query: "catalog:query:items:list",
257
+ columns: ["name"],
258
+ });
259
+ r.screen({
260
+ id: "items-overview",
261
+ type: "dashboard",
262
+ panels: [STAT_PANEL],
263
+ });
264
+ r.translations({
265
+ keys: {
266
+ "screen:items.title": { de: "Artikel", en: "Items" },
267
+ "screen:items-overview.title": { de: "Übersicht", en: "Overview" },
268
+ "demo:dashboard:panel:open-incidents": { de: "Offene Vorfälle", en: "Open incidents" },
269
+ },
270
+ });
271
+ });
272
+
273
+ function screenPanelFeature(panel: DashboardScreenPanel) {
274
+ return dashboardFeature([STAT_PANEL, panel]);
275
+ }
276
+
277
+ describe("validateBoot — dashboard screen panels (fw#2841)", () => {
278
+ test("accepts a cross-feature projectionList target with a registered visibleWhen query", () => {
279
+ const feature = screenPanelFeature({
280
+ kind: "screen",
281
+ id: "items",
282
+ screen: "catalog:screen:items",
283
+ label: "demo:dashboard:panel:latest",
284
+ visibleWhen: { query: "catalog:query:items:status", field: "enabled", eq: true },
285
+ });
286
+ expect(() => validateBoot([feature, catalogFeature])).not.toThrow();
287
+ });
288
+
289
+ test("accepts a same-feature short id", () => {
290
+ const feature = defineFeature("demo", (r) => {
291
+ r.queryHandler("items:list", z.object({}), async () => ({ rows: [], nextCursor: null }), {
292
+ access: { openToAll: true },
293
+ });
294
+ r.screen({
295
+ id: "items",
296
+ type: "projectionList",
297
+ query: "demo:query:items:list",
298
+ columns: ["name"],
299
+ });
300
+ r.screen({
301
+ id: "overview",
302
+ type: "dashboard",
303
+ panels: [{ kind: "screen", id: "items", screen: "items" }],
304
+ });
305
+ r.translations({
306
+ keys: {
307
+ "screen:items.title": { de: "Artikel", en: "Items" },
308
+ "screen:overview.title": { de: "Übersicht", en: "Overview" },
309
+ },
310
+ });
311
+ });
312
+ expect(() => validateBoot([feature])).not.toThrow();
313
+ });
314
+
315
+ test("rejects a target that resolves to no registered screen", () => {
316
+ const feature = screenPanelFeature({
317
+ kind: "screen",
318
+ id: "items",
319
+ screen: "catalog:screen:ghost",
320
+ });
321
+ expect(() => validateBoot([feature, catalogFeature])).toThrow(
322
+ /screen-panel "items" screen "catalog:screen:ghost" does not resolve/,
323
+ );
324
+ });
325
+
326
+ test("rejects a short id that only exists in another feature", () => {
327
+ const feature = screenPanelFeature({ kind: "screen", id: "items", screen: "items" });
328
+ expect(() => validateBoot([feature, catalogFeature])).toThrow(/checked "demo:screen:items"/);
329
+ });
330
+
331
+ test("rejects embedding a dashboard (no nesting)", () => {
332
+ const feature = screenPanelFeature({
333
+ kind: "screen",
334
+ id: "nested",
335
+ screen: "catalog:screen:items-overview",
336
+ });
337
+ expect(() => validateBoot([feature, catalogFeature])).toThrow(
338
+ /of type "dashboard", which can't be embedded/,
339
+ );
340
+ });
341
+
342
+ test("rejects a visibleWhen query that is not registered", () => {
343
+ const feature = screenPanelFeature({
344
+ kind: "screen",
345
+ id: "items",
346
+ screen: "catalog:screen:items",
347
+ visibleWhen: { query: "catalog:query:items:ghost", field: "enabled", eq: true },
348
+ });
349
+ expect(() => validateBoot([feature, catalogFeature])).toThrow(
350
+ /screen-panel "items" visibleWhen query "catalog:query:items:ghost" is not a registered query-handler/,
351
+ );
352
+ });
353
+
354
+ test("rejects a visibleWhen with an empty field", () => {
355
+ const feature = screenPanelFeature({
356
+ kind: "screen",
357
+ id: "items",
358
+ screen: "catalog:screen:items",
359
+ visibleWhen: { query: "catalog:query:items:status", field: "", eq: true },
360
+ });
361
+ expect(() => validateBoot([feature, catalogFeature])).toThrow(
362
+ /visibleWhen needs a non-empty query and field/,
363
+ );
364
+ });
365
+
366
+ test("requiredKeysFromScreen sammelt ein gesetztes Screen-Panel-Label", () => {
367
+ const screen: DashboardScreenDefinition = {
368
+ id: "overview",
369
+ type: "dashboard",
370
+ panels: [
371
+ {
372
+ kind: "screen",
373
+ id: "items",
374
+ screen: "catalog:screen:items",
375
+ label: "demo:dashboard:panel:latest",
376
+ },
377
+ ],
378
+ };
379
+ expect(requiredKeysFromScreen("demo", screen)).toContain("demo:dashboard:panel:latest");
380
+ });
381
+ });
@@ -6,7 +6,13 @@ import { createEntity, createTextField } from "../factories";
6
6
  describe("validateBoot — detailFor screens (fw#2163)", () => {
7
7
  test("two screens with the same detailFor fail boot, naming both screen ids", () => {
8
8
  const feature = defineFeature("demo", (r) => {
9
- r.entity("item", createEntity({ table: "Items", fields: { name: createTextField() } }));
9
+ r.entity(
10
+ "item",
11
+ createEntity({
12
+ table: "Items",
13
+ fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
14
+ }),
15
+ );
10
16
  r.screen({
11
17
  id: "item-detail-a",
12
18
  type: "custom",
@@ -34,7 +40,13 @@ describe("validateBoot — detailFor screens (fw#2163)", () => {
34
40
 
35
41
  test("detailFor on an unknown entity fails boot", () => {
36
42
  const feature = defineFeature("demo", (r) => {
37
- r.entity("item", createEntity({ table: "Items", fields: { name: createTextField() } }));
43
+ r.entity(
44
+ "item",
45
+ createEntity({
46
+ table: "Items",
47
+ fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
48
+ }),
49
+ );
38
50
  r.screen({
39
51
  id: "item-detail",
40
52
  type: "custom",
@@ -53,7 +65,13 @@ describe("validateBoot — detailFor screens (fw#2163)", () => {
53
65
 
54
66
  test("a valid detailFor on a custom screen passes boot", () => {
55
67
  const feature = defineFeature("demo", (r) => {
56
- r.entity("item", createEntity({ table: "Items", fields: { name: createTextField() } }));
68
+ r.entity(
69
+ "item",
70
+ createEntity({
71
+ table: "Items",
72
+ fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
73
+ }),
74
+ );
57
75
  r.screen({
58
76
  id: "item-detail",
59
77
  type: "custom",
@@ -72,7 +90,13 @@ describe("validateBoot — detailFor screens (fw#2163)", () => {
72
90
 
73
91
  test("an entity without any detail screen passes boot", () => {
74
92
  const feature = defineFeature("demo", (r) => {
75
- r.entity("item", createEntity({ table: "Items", fields: { name: createTextField() } }));
93
+ r.entity(
94
+ "item",
95
+ createEntity({
96
+ table: "Items",
97
+ fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
98
+ }),
99
+ );
76
100
  r.translations({
77
101
  keys: { "demo:entity:item:field:name": { de: "Name", en: "Name" } },
78
102
  });
@@ -10,7 +10,9 @@ describe("validateBoot — entityList screens", () => {
10
10
  "item",
11
11
  createEntity({
12
12
  table: "Items",
13
- fields: { name: createTextField({ sortable: true }) },
13
+ fields: {
14
+ name: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
15
+ },
14
16
  }),
15
17
  );
16
18
  r.screen({
@@ -31,7 +33,13 @@ describe("validateBoot — entityList screens", () => {
31
33
 
32
34
  test("rejects searchable:false on operator lists not on whitelist", () => {
33
35
  const feature = defineFeature("demo", (r) => {
34
- r.entity("item", createEntity({ table: "Items", fields: { name: createTextField() } }));
36
+ r.entity(
37
+ "item",
38
+ createEntity({
39
+ table: "Items",
40
+ fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
41
+ }),
42
+ );
35
43
  r.screen({
36
44
  id: "item-list",
37
45
  type: "entityList",
@@ -51,7 +59,13 @@ describe("validateBoot — entityList screens", () => {
51
59
 
52
60
  test("allows searchable:false on download-attempt-list whitelist", () => {
53
61
  const feature = defineFeature("demo", (r) => {
54
- r.entity("attempt", createEntity({ table: "Attempts", fields: { id: createTextField() } }));
62
+ r.entity(
63
+ "attempt",
64
+ createEntity({
65
+ table: "Attempts",
66
+ fields: { id: createTextField({ personal: false, reason: "test_fixture" }) },
67
+ }),
68
+ );
55
69
  r.screen({
56
70
  id: "download-attempt-list",
57
71
  type: "entityList",
@@ -75,7 +89,9 @@ describe("validateBoot — entityList screens", () => {
75
89
  "item",
76
90
  createEntity({
77
91
  table: "Items",
78
- fields: { name: createTextField({ sortable: true }) },
92
+ fields: {
93
+ name: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
94
+ },
79
95
  }),
80
96
  );
81
97
  r.screen({
@@ -10,7 +10,9 @@ describe("validateBoot — i18n surface keys", () => {
10
10
  "item",
11
11
  createEntity({
12
12
  table: "Items",
13
- fields: { name: createTextField({ sortable: true }) },
13
+ fields: {
14
+ name: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
15
+ },
14
16
  }),
15
17
  );
16
18
  r.screen({
@@ -36,7 +38,9 @@ describe("validateBoot — i18n surface keys", () => {
36
38
  "item",
37
39
  createEntity({
38
40
  table: "Items",
39
- fields: { name: createTextField({ sortable: true }) },
41
+ fields: {
42
+ name: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
43
+ },
40
44
  }),
41
45
  );
42
46
  r.screen({
@@ -63,7 +67,9 @@ describe("validateBoot — i18n surface keys", () => {
63
67
  "item",
64
68
  createEntity({
65
69
  table: "Items",
66
- fields: { name: createTextField({ sortable: true }) },
70
+ fields: {
71
+ name: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
72
+ },
67
73
  }),
68
74
  );
69
75
  r.screen({