@cosmicdrift/kumiko-framework 0.306.0 → 0.308.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 (178) hide show
  1. package/package.json +4 -4
  2. package/src/__tests__/anonymous-access.integration.test.ts +1 -1
  3. package/src/__tests__/error-contract.integration.test.ts +1 -1
  4. package/src/__tests__/field-access.integration.test.ts +1 -1
  5. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.expected.ts +1 -1
  6. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.input.ts +1 -1
  7. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.expected.ts +1 -1
  8. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.input.ts +1 -1
  9. package/src/__tests__/full-stack.integration.test.ts +1 -1
  10. package/src/__tests__/transition-guard.integration.test.ts +1 -1
  11. package/src/api/__tests__/api.test.ts +1 -1
  12. package/src/api/__tests__/batch.integration.test.ts +1 -1
  13. package/src/api/__tests__/body-limit.test.ts +1 -1
  14. package/src/api/__tests__/dispatcher-live.integration.test.ts +1 -1
  15. package/src/api/__tests__/extra-routes.integration.test.ts +1 -1
  16. package/src/api/__tests__/http-route-rate-limit.integration.test.ts +1 -1
  17. package/src/api/__tests__/nested-write-ownership.integration.test.ts +1 -1
  18. package/src/api/__tests__/nested-write.integration.test.ts +1 -1
  19. package/src/api/__tests__/pii-leak-guard.integration.test.ts +1 -1
  20. package/src/api/__tests__/redis-sse-broker.integration.test.ts +133 -0
  21. package/src/api/__tests__/request-locale.integration.test.ts +1 -1
  22. package/src/api/__tests__/server-boot-guards.test.ts +1 -1
  23. package/src/api/__tests__/server-error-logging.test.ts +1 -1
  24. package/src/api/__tests__/sse-broker.test.ts +144 -1
  25. package/src/api/__tests__/stream-escape-hatch.integration.test.ts +1 -1
  26. package/src/api/auth-routes.ts +1 -1
  27. package/src/api/redis-sse-broker.ts +57 -3
  28. package/src/api/request-context.ts +24 -0
  29. package/src/api/server.ts +11 -0
  30. package/src/api/sse-broker.ts +69 -12
  31. package/src/changes.json +63 -0
  32. package/src/compliance/override-schema.ts +1 -1
  33. package/src/crypto/__tests__/event-pii.test.ts +1 -1
  34. package/src/crypto/kms-adapter.ts +1 -1
  35. package/src/db/__tests__/tenant-db-where-merge.integration.test.ts +1 -1
  36. package/src/db/queries/event-consumer.ts +155 -9
  37. package/src/db/queries/event-store.ts +69 -0
  38. package/src/db/tenant-db.ts +43 -5
  39. package/src/derivatives/__tests__/derivatives-context.integration.test.ts +1 -1
  40. package/src/engine/__tests__/boot-validator-action-wiring.test.ts +1 -1
  41. package/src/engine/__tests__/boot-validator-dashboard.test.ts +1 -1
  42. package/src/engine/__tests__/boot-validator-nav-area.test.ts +1 -1
  43. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +1 -1
  44. package/src/engine/__tests__/boot-validator-projection-list.test.ts +1 -1
  45. package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +1 -1
  46. package/src/engine/__tests__/boot-validator-query-refs.test.ts +1 -1
  47. package/src/engine/__tests__/boot-validator-ref-entity.test.ts +1 -1
  48. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +1 -1
  49. package/src/engine/__tests__/boot-validator.test.ts +35 -1
  50. package/src/engine/__tests__/build-app-schema.test.ts +1 -1
  51. package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +1 -1
  52. package/src/engine/__tests__/define-feature-entity-mapping.test.ts +1 -1
  53. package/src/engine/__tests__/engine.test.ts +1 -1
  54. package/src/engine/__tests__/event-helpers.test.ts +1 -1
  55. package/src/engine/__tests__/event-migration-declarative.test.ts +1 -1
  56. package/src/engine/__tests__/extend-entity-projection.test.ts +1 -1
  57. package/src/engine/__tests__/extends-registrar.test.ts +1 -1
  58. package/src/engine/__tests__/hook-phases.test.ts +1 -1
  59. package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
  60. package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
  61. package/src/engine/__tests__/pipeline-handler.integration.test.ts +1 -1
  62. package/src/engine/__tests__/pipeline-observability.integration.test.ts +1 -1
  63. package/src/engine/__tests__/pipeline-performance.integration.test.ts +1 -1
  64. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
  65. package/src/engine/__tests__/post-query-hook.test.ts +1 -1
  66. package/src/engine/__tests__/projection-detail-actions.test.ts +1 -1
  67. package/src/engine/__tests__/projection-detail-singleton.test.ts +1 -1
  68. package/src/engine/__tests__/projection-detail-tabs.test.ts +1 -1
  69. package/src/engine/__tests__/read-steps-tenant-filter.integration.test.ts +1 -1
  70. package/src/engine/__tests__/registry.test.ts +1 -1
  71. package/src/engine/__tests__/screen.test.ts +1 -1
  72. package/src/engine/__tests__/validate-projection-allowlist.test.ts +1 -1
  73. package/src/engine/boot-validator/__tests__/access-declarations.test.ts +1 -1
  74. package/src/engine/boot-validator/__tests__/action-icon.test.ts +81 -0
  75. package/src/engine/boot-validator/__tests__/anonymous-rate-limit-required.test.ts +1 -1
  76. package/src/engine/boot-validator/__tests__/no-all-role-in-handler-access.test.ts +1 -1
  77. package/src/engine/boot-validator/__tests__/parent-ref.test.ts +1 -1
  78. package/src/engine/boot-validator/__tests__/rate-limit-disabled-reason.test.ts +1 -1
  79. package/src/engine/boot-validator/screens.ts +83 -0
  80. package/src/engine/define-handler.ts +2 -1
  81. package/src/engine/entity-handlers.ts +2 -1
  82. package/src/engine/feature-ast/__tests__/pattern-change-schema.test.ts +1 -1
  83. package/src/engine/feature-ast/pattern-change-schema.ts +1 -1
  84. package/src/engine/feature-builder-state.ts +2 -1
  85. package/src/engine/feature-config-events-jobs.ts +2 -1
  86. package/src/engine/feature-entity-handlers.ts +2 -1
  87. package/src/engine/schema-builder.ts +1 -1
  88. package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +1 -1
  89. package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +1 -1
  90. package/src/entrypoint/__tests__/split-deploy.integration.test.ts +1 -1
  91. package/src/env/__tests__/compose-env-schema.test.ts +1 -1
  92. package/src/env/__tests__/dry-run.test.ts +1 -1
  93. package/src/env/__tests__/parse-env-dry-run.test.ts +1 -1
  94. package/src/env/_zod-introspect.ts +1 -1
  95. package/src/env/dry-run.ts +1 -1
  96. package/src/env/index.ts +1 -1
  97. package/src/errors/__tests__/classes.test.ts +1 -1
  98. package/src/es-ops/__tests__/runner.integration.test.ts +1 -1
  99. package/src/event-store/__tests__/backfill-pii.integration.test.ts +1 -1
  100. package/src/event-store/__tests__/event-attribution.integration.test.ts +54 -4
  101. package/src/event-store/__tests__/upcaster.integration.test.ts +1 -1
  102. package/src/event-store/admin-api.ts +5 -0
  103. package/src/event-store/event-store.ts +16 -7
  104. package/src/jobs/__tests__/job-backoff.integration.test.ts +1 -1
  105. package/src/jobs/__tests__/job-event-trigger.integration.test.ts +1 -1
  106. package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +1 -1
  107. package/src/jobs/__tests__/job-public-intake-origin.integration.test.ts +536 -0
  108. package/src/jobs/__tests__/job-retention.integration.test.ts +1 -1
  109. package/src/jobs/__tests__/job-write-as.integration.test.ts +1 -1
  110. package/src/jobs/__tests__/jobs.integration.test.ts +1 -1
  111. package/src/jobs/job-runner.ts +61 -6
  112. package/src/observability/__tests__/metrics-wiring.test.ts +1 -1
  113. package/src/observability/__tests__/observability.integration.test.ts +1 -1
  114. package/src/observability/metrics-wiring.ts +1 -1
  115. package/src/pipeline/__tests__/archive-stream.integration.test.ts +1 -1
  116. package/src/pipeline/__tests__/causation-chain.integration.test.ts +1 -1
  117. package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +1 -1
  118. package/src/pipeline/__tests__/ctx-systemdb.integration.test.ts +1 -1
  119. package/src/pipeline/__tests__/dispatcher-utils.test.ts +8 -0
  120. package/src/pipeline/__tests__/dispatcher.test.ts +1 -1
  121. package/src/pipeline/__tests__/domain-events-projections.integration.test.ts +1 -1
  122. package/src/pipeline/__tests__/escape-hatch-audit.integration.test.ts +1 -1
  123. package/src/pipeline/__tests__/escape-hatch.integration.test.ts +1 -1
  124. package/src/pipeline/__tests__/event-dedup.integration.test.ts +1 -1
  125. package/src/pipeline/__tests__/event-define-event-strict.integration.test.ts +1 -1
  126. package/src/pipeline/__tests__/event-dispatcher-batch-delivery.test.ts +178 -0
  127. package/src/pipeline/__tests__/event-dispatcher-batch-handler.integration.test.ts +143 -0
  128. package/src/pipeline/__tests__/event-dispatcher-commit-order.integration.test.ts +278 -0
  129. package/src/pipeline/__tests__/event-dispatcher-delivery-max-attempts.test.ts +1 -0
  130. package/src/pipeline/__tests__/event-dispatcher-idle.integration.test.ts +94 -0
  131. package/src/pipeline/__tests__/event-dispatcher-lifecycle.integration.test.ts +6 -6
  132. package/src/pipeline/__tests__/event-dispatcher-per-consumer-turns.integration.test.ts +126 -0
  133. package/src/pipeline/__tests__/event-dispatcher-start-from.integration.test.ts +103 -3
  134. package/src/pipeline/__tests__/fetch-for-writing.integration.test.ts +1 -1
  135. package/src/pipeline/__tests__/idempotency-transient-failure.integration.test.ts +1 -1
  136. package/src/pipeline/__tests__/job-trigger-consumer.integration.test.ts +1 -1
  137. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +1 -1
  138. package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +1 -1
  139. package/src/pipeline/__tests__/msp-multi-hop.integration.test.ts +1 -1
  140. package/src/pipeline/__tests__/msp-rebuild.integration.test.ts +116 -1
  141. package/src/pipeline/__tests__/multi-stream-projection.integration.test.ts +1 -1
  142. package/src/pipeline/__tests__/pending-gap-ranges.test.ts +94 -0
  143. package/src/pipeline/__tests__/post-query-hook.integration.test.ts +1 -1
  144. package/src/pipeline/__tests__/public-intake-runtime-gate.integration.test.ts +262 -3
  145. package/src/pipeline/__tests__/query-projection.integration.test.ts +1 -1
  146. package/src/pipeline/__tests__/rate-limit-default.integration.test.ts +1 -1
  147. package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +1 -1
  148. package/src/pipeline/__tests__/search-consumer-named-events.integration.test.ts +1 -1
  149. package/src/pipeline/__tests__/system-identity-switch.integration.test.ts +1 -1
  150. package/src/pipeline/__tests__/try-append-event.integration.test.ts +1 -1
  151. package/src/pipeline/__tests__/unsafe-raw-escape-hatch.integration.test.ts +1 -1
  152. package/src/pipeline/dispatch-batch.ts +49 -19
  153. package/src/pipeline/dispatch-stream.ts +12 -3
  154. package/src/pipeline/dispatcher-utils.ts +21 -2
  155. package/src/pipeline/dispatcher.ts +71 -6
  156. package/src/pipeline/event-consumer-state.ts +26 -0
  157. package/src/pipeline/event-dispatcher-admin.ts +32 -5
  158. package/src/pipeline/event-dispatcher-delivery.ts +212 -65
  159. package/src/pipeline/event-dispatcher.ts +253 -49
  160. package/src/pipeline/index.ts +1 -0
  161. package/src/pipeline/msp-rebuild.ts +39 -1
  162. package/src/pipeline/pending-gap-ranges.ts +120 -0
  163. package/src/pipeline/system-hooks.ts +226 -64
  164. package/src/pipeline/write-origin.ts +31 -10
  165. package/src/rate-limit/__tests__/dispatcher-l3.integration.test.ts +1 -1
  166. package/src/search/__tests__/derive-search-adapter-config.test.ts +65 -0
  167. package/src/search/__tests__/meilisearch-adapter.integration.test.ts +91 -0
  168. package/src/search/__tests__/search-consumer-batch.integration.test.ts +306 -0
  169. package/src/search/derive-search-adapter-config.ts +27 -0
  170. package/src/search/index.ts +1 -0
  171. package/src/search/meilisearch-adapter.ts +96 -14
  172. package/src/secrets/__tests__/contains-secret.test.ts +1 -1
  173. package/src/stack/__tests__/request-helper.integration.test.ts +1 -1
  174. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +1 -1
  175. package/src/stack/__tests__/setup-test-stack-metrics.integration.test.ts +1 -1
  176. package/src/stack/test-stack.ts +1 -1
  177. package/src/testing/__tests__/e2e-generator.test.ts +1 -1
  178. package/src/testing/e2e-generator.ts +1 -1
@@ -51,6 +51,41 @@ const declaredUnsafeRawRunners = new WeakMap<
51
51
  // (withUnsafeRawGrant, acknowledgeConventionCrossTenant) carry it too.
52
52
  const personalDataGates = new WeakMap<TenantDb, PersonalDataGate>();
53
53
 
54
+ // Lets createTenantDb(ctx.db.unsafeRaw(reason), ...) inherit the gate. Keyed by a
55
+ // per-grant proxy, never the shared pool/tx: tagging that would gate every sibling TenantDb.
56
+ const runnerPersonalDataGates = new WeakMap<DbRunner, PersonalDataGate>();
57
+
58
+ function gatedRunner(runner: DbRunner, gate: PersonalDataGate): DbRunner {
59
+ const proxy = new Proxy(runner as object, {
60
+ // Tagged-template calls need the real driver object as `this`.
61
+ apply(target, _thisArg, args) {
62
+ return Reflect.apply(target as (...callArgs: unknown[]) => unknown, target, args);
63
+ },
64
+ get(target, prop, _receiver) {
65
+ const value = Reflect.get(target, prop, target);
66
+ if (typeof value !== "function") return value;
67
+ if (prop === "begin" || prop === "savepoint") {
68
+ // createTenantDb(tx, ...) inside the callback must inherit the gate too.
69
+ return (...args: unknown[]) => {
70
+ const callback = args[args.length - 1];
71
+ if (typeof callback !== "function") {
72
+ return Reflect.apply(value, target, args);
73
+ }
74
+ const gatedArgs = [
75
+ ...args.slice(0, -1),
76
+ (tx: unknown) => callback(gatedRunner(tx as DbRunner, gate)),
77
+ ];
78
+ return Reflect.apply(value, target, gatedArgs);
79
+ };
80
+ }
81
+ return value.bind(target);
82
+ },
83
+ // @cast-boundary proxy-erasure — Proxy<object> re-tags as the wrapped DbRunner shape.
84
+ }) as DbRunner;
85
+ runnerPersonalDataGates.set(proxy, gate);
86
+ return proxy;
87
+ }
88
+
54
89
  // The executor passes its entity so the check does not depend on the table-name lookup.
55
90
  export function assertPersonalDataWrite(
56
91
  db: TenantDb,
@@ -151,7 +186,9 @@ function buildUncheckedSystemDb(
151
186
  });
152
187
  }
153
188
  report("unsafe-raw", reason);
154
- return tenantDbRunner(db);
189
+ const runner = tenantDbRunner(db);
190
+ const personalDataGate = personalDataGates.get(db);
191
+ return personalDataGate ? gatedRunner(runner, personalDataGate) : runner;
155
192
  }
156
193
 
157
194
  const uncheckedSystemDb: UncheckedSystemDb = {
@@ -352,6 +389,7 @@ export function createTenantDb(
352
389
  ): TenantDb {
353
390
  if (meter) registerStandardMetrics(meter);
354
391
  const report = grants?.report ?? fallbackEscapeHatchReporter(tenantId);
392
+ const personalDataGate = grants?.personalDataGate ?? runnerPersonalDataGates.get(db);
355
393
 
356
394
  function withDbSpan<T>(
357
395
  operation: "select" | "insert" | "update" | "delete",
@@ -452,9 +490,9 @@ export function createTenantDb(
452
490
  table: Table | EntityTableMeta,
453
491
  keys: readonly string[],
454
492
  ): AccessDeniedError | undefined {
455
- if (!grants?.personalDataGate) return undefined;
493
+ if (!personalDataGate) return undefined;
456
494
  try {
457
- grants.personalDataGate(tableNameOf(table), keys);
495
+ personalDataGate(tableNameOf(table), keys);
458
496
  return undefined;
459
497
  } catch (e) {
460
498
  if (e instanceof AccessDeniedError) return e;
@@ -553,7 +591,7 @@ export function createTenantDb(
553
591
  });
554
592
  }
555
593
  report("unsafe-raw", reason);
556
- return db;
594
+ return personalDataGate ? gatedRunner(db, personalDataGate) : db;
557
595
  }
558
596
 
559
597
  const tenantDb: TenantDb = {
@@ -648,7 +686,7 @@ export function createTenantDb(
648
686
  return createTenantDb(db, tenantId, "system", tracer, meter, signal, grants);
649
687
  });
650
688
  bindTenantDbRunner(tenantDb, db);
651
- if (grants?.personalDataGate) personalDataGates.set(tenantDb, grants.personalDataGate);
689
+ if (personalDataGate) personalDataGates.set(tenantDb, personalDataGate);
652
690
  return tenantDb;
653
691
  }
654
692
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
8
8
  import type { DerivativeRendererPlugin } from "@cosmicdrift/kumiko-types/derivatives-types";
9
- import { z } from "zod";
9
+ import * as z from "zod";
10
10
  import { defineFeature, EXT_DERIVATIVE_RENDERER } from "../../engine";
11
11
  import { InternalError, NotFoundError, writeFailure } from "../../errors";
12
12
  import { createFilesFeature } from "../../files/feature";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { validateBoot } from "../boot-validator";
4
4
  import { defineFeature } from "../define-feature";
5
5
  import { createEntity, createTextField } from "../factories";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as 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";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
@@ -15,7 +15,7 @@
15
15
  // allowPlaintext-Marker unterdrückt Heuristik-Warnings.
16
16
 
17
17
  import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
18
- import { z } from "zod";
18
+ import * as z from "zod";
19
19
  import { validateBoot } from "../boot-validator";
20
20
  import { defineFeature } from "../define-feature";
21
21
  import {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { validateBoot } from "../boot-validator";
4
4
  import { defineFeature } from "../define-feature";
5
5
  import { createEntity, createTextField } from "../factories";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
@@ -14,7 +14,7 @@
14
14
  // + handler-access mit ROLES.TenantAdmin
15
15
 
16
16
  import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
17
- import { z } from "zod";
17
+ import * as z from "zod";
18
18
  import { ROLES } from "../../auth";
19
19
  import { validateBoot } from "../boot-validator";
20
20
  import { defineFeature } from "../define-feature";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, spyOn, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import type { SchemaTable } from "../../db/dialect";
4
4
  import { table, text } from "../../db/dialect";
5
5
  import { rowMetaFieldNames } from "../../db/table-builder";
@@ -4673,6 +4673,40 @@ describe("boot-validator", () => {
4673
4673
  );
4674
4674
  });
4675
4675
 
4676
+ test("projectionDetail tabs-mode fields section with both groups and actions → throw", () => {
4677
+ const feature = defineFeature("shop", (r) => {
4678
+ r.screen({
4679
+ id: "order-detail",
4680
+ type: "projectionDetail",
4681
+ query: "shop:query:order-detail",
4682
+ layout: {
4683
+ mode: "tabs",
4684
+ sections: [
4685
+ {
4686
+ id: "billing",
4687
+ title: "Billing",
4688
+ fields: [],
4689
+ groups: [{ title: "Amounts", fields: ["name"] }],
4690
+ actions: [
4691
+ { kind: "navigate", id: "edit", label: "actions.edit", screen: "order-detail" },
4692
+ ],
4693
+ },
4694
+ {
4695
+ id: "positions",
4696
+ kind: "relatedList",
4697
+ title: "Positions",
4698
+ query: "shop:query:order-positions",
4699
+ columns: [{ field: "name" }],
4700
+ },
4701
+ ],
4702
+ },
4703
+ });
4704
+ });
4705
+ expect(() => validateBoot([feature])).toThrow(
4706
+ /section "billing" declares both groups and actions in tabs mode/,
4707
+ );
4708
+ });
4709
+
4676
4710
  test("projectionDetail relatedList defaultSort.field not a listed column → throw", () => {
4677
4711
  const feature = defineFeature("shop", (r) => {
4678
4712
  r.screen({
@@ -8,7 +8,7 @@
8
8
  // im Browser-Bundle.
9
9
 
10
10
  import { describe, expect, test } from "bun:test";
11
- import { z } from "zod";
11
+ import * as z from "zod";
12
12
  import { buildAppSchema, findNonJsonSafePath } from "../build-app-schema";
13
13
  import { defineFeature } from "../define-feature";
14
14
  import { createRegistry } from "../registry";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { buildConfigFeatureSchema } from "../build-config-feature-schema";
4
4
  import { createTenantConfig } from "../config-helpers";
5
5
  import { defineFeature } from "../define-feature";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../define-feature";
4
4
  import { createEntity, createTextField } from "../factories";
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { createTestUser } from "../../stack";
4
4
  import { rolesOf } from "../../testing/access-assertions";
5
5
  import { hasAccess } from "../access";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, mock, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { emitEvent, typedPayload } from "../event-helpers";
4
4
  import type { EventDef } from "../types/handlers";
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../define-feature";
4
4
  import type { DeclarativeEventMigration, EventUpcastCtx } from "../types";
5
5
 
@@ -3,7 +3,7 @@
3
3
  // custom-fields/__tests__/custom-fields.integration.test.ts.
4
4
 
5
5
  import { describe, expect, test } from "bun:test";
6
- import { z } from "zod";
6
+ import * as z from "zod";
7
7
  import { defineFeature } from "../define-feature";
8
8
  import { createEntity, createTextField } from "../factories";
9
9
  import { createRegistry } from "../registry";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, mock, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { AccessDeniedError } from "../../errors";
4
4
  import { createGatedIdentitySwitch, type QueryAsFn } from "../../pipeline/system-identity-switch";
5
5
  import {
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { createEntity, createRegistry, defineFeature, HookPhases } from "../index";
4
4
  import type { AppContext, PostSaveHookFn, SaveContext } from "../types";
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { createEntity, createRegistry, defineFeature } from "../index";
4
4
  import type { PostSaveHookFn, PreDeleteHookFn, PreSaveHookFn, SaveContext } from "../types";
5
5
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { describe, expect, it } from "bun:test";
7
7
  import { randomUUID } from "node:crypto";
8
- import { z } from "zod";
8
+ import * as z from "zod";
9
9
  import { TestUsers } from "../../stack";
10
10
  import { defineWriteHandler } from "../define-handler";
11
11
  import { defineStep } from "../define-step";
@@ -44,7 +44,7 @@
44
44
  // surface against an empty ctx mock; this file is the real-stack gate.
45
45
 
46
46
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
47
- import { z } from "zod";
47
+ import * as z from "zod";
48
48
  import { table, text, timestamp, uuid } from "../../db/dialect";
49
49
  import { createEventStoreExecutor } from "../../db/event-store-executor";
50
50
  import { asRawClient, selectMany } from "../../db/query";
@@ -14,7 +14,7 @@
14
14
  // and we wouldn't notice until a prod incident lacks the breadcrumbs.
15
15
 
16
16
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
17
- import { z } from "zod";
17
+ import * as z from "zod";
18
18
  import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
19
19
  import { createRecordingProvider, type RecordingProvider } from "../../testing";
20
20
  import { defineFeature } from "../define-feature";
@@ -23,7 +23,7 @@
23
23
  // here with a longer-pipeline handler variant.
24
24
 
25
25
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
26
- import { z } from "zod";
26
+ import * as z from "zod";
27
27
  import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
28
28
  import { defineFeature } from "../define-feature";
29
29
  import { defineWriteHandler } from "../define-handler";
@@ -6,7 +6,7 @@
6
6
  // hygiene, error-propagation and build-time guards.
7
7
 
8
8
  import { describe, expect, it } from "bun:test";
9
- import { z } from "zod";
9
+ import * as z from "zod";
10
10
  import { TestUsers } from "../../stack";
11
11
  import { defineWriteHandler } from "../define-handler";
12
12
  import { stepsPipeline } from "../pipeline";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { createEntity, createRegistry, defineFeature } from "../index";
4
4
  import type { AppContext, PostQueryHookFn } from "../types";
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { defineFeature } from "../define-feature";
@@ -8,7 +8,7 @@
8
8
  // escape-hatch-audit.integration.test.ts.
9
9
 
10
10
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
11
- import { z } from "zod";
11
+ import * as z from "zod";
12
12
  import { table, text, uuid } from "../../db/dialect";
13
13
  import { insertOne } from "../../db/query";
14
14
  import { defineFeature, defineWriteHandler } from "../../engine";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { createTenantConfig } from "../config-helpers";
4
4
  import { defineFeature } from "../define-feature";
5
5
  import { createEntity, createTextField } from "../factories";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { withBootValidatorFixture } from "../../testing/boot-validator-fixture";
4
4
  import { validateBoot as validateBootRaw } from "../boot-validator";
5
5
  import { createTenantConfig } from "../config-helpers";
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { describe, expect, it } from "bun:test";
7
7
  import { randomUUID } from "node:crypto";
8
- import { z } from "zod";
8
+ import * as z from "zod";
9
9
  import { table as pgTable, text, uuid } from "../../db/dialect";
10
10
  import { defineFeature } from "../define-feature";
11
11
  import { defineWriteHandler } from "../define-handler";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../../define-feature";
4
4
  import { createEntity, createTextField } from "../../factories";
5
5
  import { from } from "../../ownership";
@@ -0,0 +1,81 @@
1
+ // validateActionHasIcon is the single icon-resolvability check shared by
2
+ // every projectionDetail/entityEdit action rendered in a Card title row:
3
+ // screen-level `actions`, section-level `actions`, and relatedList
4
+ // `emptyState.action` (see screens.ts call sites). Tested directly here
5
+ // rather than only via a full feature boot, since all three call sites
6
+ // share this one function.
7
+
8
+ import { describe, expect, test } from "bun:test";
9
+ import { validateActionHasIcon } from "../screens";
10
+
11
+ describe("validateActionHasIcon", () => {
12
+ test("rejects a screen-level action with no declared icon and an id that resolves none", () => {
13
+ expect(() =>
14
+ validateActionHasIcon("orders", "order-detail", "projectionDetail", "action", {
15
+ id: "custom-thing",
16
+ }),
17
+ ).toThrow(/no resolvable icon/);
18
+ });
19
+
20
+ test("rejects a section action, naming the section in the error", () => {
21
+ expect(() =>
22
+ validateActionHasIcon(
23
+ "orders",
24
+ "order-detail",
25
+ "projectionDetail",
26
+ 'section "Payments" action',
27
+ { id: "custom-thing" },
28
+ ),
29
+ ).toThrow(/section "Payments" action "custom-thing" has no resolvable icon/);
30
+ });
31
+
32
+ test("rejects a relatedList emptyState action, naming it as such in the error", () => {
33
+ expect(() =>
34
+ validateActionHasIcon(
35
+ "orders",
36
+ "order-detail",
37
+ "projectionDetail",
38
+ 'section "Payments" emptyState action',
39
+ { id: "custom-thing" },
40
+ ),
41
+ ).toThrow(/emptyState action "custom-thing" has no resolvable icon/);
42
+ });
43
+
44
+ test("passes when the action declares an explicit icon", () => {
45
+ expect(() =>
46
+ validateActionHasIcon("orders", "order-detail", "entityEdit", "action", {
47
+ id: "custom-thing",
48
+ icon: "lock",
49
+ }),
50
+ ).not.toThrow();
51
+ });
52
+
53
+ test("passes when the id resolves a default icon via a kebab segment (no explicit icon needed)", () => {
54
+ // "deletion" -> "trash" in the shared ACTION_ICON_BY_ID map.
55
+ expect(() =>
56
+ validateActionHasIcon("user-data-rights", "privacy-center", "projectionDetail", "action", {
57
+ id: "request-deletion",
58
+ }),
59
+ ).not.toThrow();
60
+ });
61
+
62
+ // action.icon is typed IconKey (a closed union) — but that compile-time
63
+ // guarantee only covers call sites inside this repo's `tsc --build`
64
+ // project graph. Samples and external consumers aren't in it, so an
65
+ // unregistered literal (a typo, or a value copied from a different icon
66
+ // set) can reach here as plain runtime data. The renderer's actionIconFor
67
+ // (renderer-web/src/primitives/index.tsx) already falls back to "no icon"
68
+ // for such a value instead of crashing — this must throw here instead of
69
+ // silently accepting it, or the renderer/validator resolvers diverge.
70
+ test("rejects a declared icon that is not a registered NAV_ICON key, even though it resolves to a value", () => {
71
+ expect(() =>
72
+ validateActionHasIcon("orders", "order-detail", "projectionDetail", "action", {
73
+ id: "refresh-order",
74
+ // @ts-expect-error — exercising a value outside the closed IconKey
75
+ // union, exactly like an untypechecked caller (a sample, or a JS
76
+ // consumer) would produce at runtime.
77
+ icon: "refresh-cw",
78
+ }),
79
+ ).toThrow(/no resolvable icon/);
80
+ });
81
+ });
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../../define-feature";
4
4
  import { validateHandlerAccess } from "../entity-handler";
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../../define-feature";
4
4
  import { validateHandlerAccess } from "../entity-handler";
5
5
 
@@ -5,7 +5,7 @@
5
5
  // handler on a parentRef entity must fail boot rather than leak silently.
6
6
 
7
7
  import { describe, expect, test } from "bun:test";
8
- import { z } from "zod";
8
+ import * as z from "zod";
9
9
  import { defineFeature } from "../../define-feature";
10
10
  import { defineEntityDetailHandler, defineEntityListHandler } from "../../entity-handlers";
11
11
  import { createEntity, createTextField } from "../../factories";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../../define-feature";
4
4
  import { validateHandlerAccess } from "../entity-handler";
5
5