@cosmicdrift/kumiko-framework 0.307.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 (161) 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 +73 -6
  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 +107 -13
  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 +51 -11
  28. package/src/api/server.ts +11 -0
  29. package/src/api/sse-broker.ts +61 -22
  30. package/src/changes.json +37 -0
  31. package/src/compliance/override-schema.ts +1 -1
  32. package/src/crypto/__tests__/event-pii.test.ts +1 -1
  33. package/src/crypto/kms-adapter.ts +1 -1
  34. package/src/db/__tests__/tenant-db-where-merge.integration.test.ts +1 -1
  35. package/src/db/queries/event-consumer.ts +98 -6
  36. package/src/derivatives/__tests__/derivatives-context.integration.test.ts +1 -1
  37. package/src/engine/__tests__/boot-validator-action-wiring.test.ts +1 -1
  38. package/src/engine/__tests__/boot-validator-dashboard.test.ts +1 -1
  39. package/src/engine/__tests__/boot-validator-nav-area.test.ts +1 -1
  40. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +1 -1
  41. package/src/engine/__tests__/boot-validator-projection-list.test.ts +1 -1
  42. package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +1 -1
  43. package/src/engine/__tests__/boot-validator-query-refs.test.ts +1 -1
  44. package/src/engine/__tests__/boot-validator-ref-entity.test.ts +1 -1
  45. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +1 -1
  46. package/src/engine/__tests__/boot-validator.test.ts +35 -1
  47. package/src/engine/__tests__/build-app-schema.test.ts +1 -1
  48. package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +1 -1
  49. package/src/engine/__tests__/define-feature-entity-mapping.test.ts +1 -1
  50. package/src/engine/__tests__/engine.test.ts +1 -1
  51. package/src/engine/__tests__/event-helpers.test.ts +1 -1
  52. package/src/engine/__tests__/event-migration-declarative.test.ts +1 -1
  53. package/src/engine/__tests__/extend-entity-projection.test.ts +1 -1
  54. package/src/engine/__tests__/extends-registrar.test.ts +1 -1
  55. package/src/engine/__tests__/hook-phases.test.ts +1 -1
  56. package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
  57. package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
  58. package/src/engine/__tests__/pipeline-handler.integration.test.ts +1 -1
  59. package/src/engine/__tests__/pipeline-observability.integration.test.ts +1 -1
  60. package/src/engine/__tests__/pipeline-performance.integration.test.ts +1 -1
  61. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
  62. package/src/engine/__tests__/post-query-hook.test.ts +1 -1
  63. package/src/engine/__tests__/projection-detail-actions.test.ts +1 -1
  64. package/src/engine/__tests__/projection-detail-singleton.test.ts +1 -1
  65. package/src/engine/__tests__/projection-detail-tabs.test.ts +1 -1
  66. package/src/engine/__tests__/read-steps-tenant-filter.integration.test.ts +1 -1
  67. package/src/engine/__tests__/registry.test.ts +1 -1
  68. package/src/engine/__tests__/screen.test.ts +1 -1
  69. package/src/engine/__tests__/validate-projection-allowlist.test.ts +1 -1
  70. package/src/engine/boot-validator/__tests__/access-declarations.test.ts +1 -1
  71. package/src/engine/boot-validator/__tests__/action-icon.test.ts +81 -0
  72. package/src/engine/boot-validator/__tests__/anonymous-rate-limit-required.test.ts +1 -1
  73. package/src/engine/boot-validator/__tests__/no-all-role-in-handler-access.test.ts +1 -1
  74. package/src/engine/boot-validator/__tests__/parent-ref.test.ts +1 -1
  75. package/src/engine/boot-validator/__tests__/rate-limit-disabled-reason.test.ts +1 -1
  76. package/src/engine/boot-validator/screens.ts +83 -0
  77. package/src/engine/define-handler.ts +2 -1
  78. package/src/engine/entity-handlers.ts +2 -1
  79. package/src/engine/feature-ast/__tests__/pattern-change-schema.test.ts +1 -1
  80. package/src/engine/feature-ast/pattern-change-schema.ts +1 -1
  81. package/src/engine/feature-builder-state.ts +2 -1
  82. package/src/engine/feature-config-events-jobs.ts +2 -1
  83. package/src/engine/feature-entity-handlers.ts +2 -1
  84. package/src/engine/schema-builder.ts +1 -1
  85. package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +1 -1
  86. package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +1 -1
  87. package/src/entrypoint/__tests__/split-deploy.integration.test.ts +1 -1
  88. package/src/env/__tests__/compose-env-schema.test.ts +1 -1
  89. package/src/env/__tests__/dry-run.test.ts +1 -1
  90. package/src/env/__tests__/parse-env-dry-run.test.ts +1 -1
  91. package/src/env/_zod-introspect.ts +1 -1
  92. package/src/env/dry-run.ts +1 -1
  93. package/src/env/index.ts +1 -1
  94. package/src/errors/__tests__/classes.test.ts +1 -1
  95. package/src/es-ops/__tests__/runner.integration.test.ts +1 -1
  96. package/src/event-store/__tests__/backfill-pii.integration.test.ts +1 -1
  97. package/src/event-store/__tests__/event-attribution.integration.test.ts +1 -1
  98. package/src/event-store/__tests__/upcaster.integration.test.ts +1 -1
  99. package/src/jobs/__tests__/job-backoff.integration.test.ts +1 -1
  100. package/src/jobs/__tests__/job-event-trigger.integration.test.ts +1 -1
  101. package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +1 -1
  102. package/src/jobs/__tests__/job-public-intake-origin.integration.test.ts +1 -1
  103. package/src/jobs/__tests__/job-retention.integration.test.ts +1 -1
  104. package/src/jobs/__tests__/job-write-as.integration.test.ts +1 -1
  105. package/src/jobs/__tests__/jobs.integration.test.ts +1 -1
  106. package/src/observability/__tests__/metrics-wiring.test.ts +1 -1
  107. package/src/observability/__tests__/observability.integration.test.ts +1 -1
  108. package/src/observability/metrics-wiring.ts +1 -1
  109. package/src/pipeline/__tests__/archive-stream.integration.test.ts +1 -1
  110. package/src/pipeline/__tests__/causation-chain.integration.test.ts +1 -1
  111. package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +1 -1
  112. package/src/pipeline/__tests__/ctx-systemdb.integration.test.ts +1 -1
  113. package/src/pipeline/__tests__/dispatcher.test.ts +1 -1
  114. package/src/pipeline/__tests__/domain-events-projections.integration.test.ts +1 -1
  115. package/src/pipeline/__tests__/escape-hatch-audit.integration.test.ts +1 -1
  116. package/src/pipeline/__tests__/escape-hatch.integration.test.ts +1 -1
  117. package/src/pipeline/__tests__/event-dedup.integration.test.ts +1 -1
  118. package/src/pipeline/__tests__/event-define-event-strict.integration.test.ts +1 -1
  119. package/src/pipeline/__tests__/event-dispatcher-batch-delivery.test.ts +178 -0
  120. package/src/pipeline/__tests__/event-dispatcher-batch-handler.integration.test.ts +143 -0
  121. package/src/pipeline/__tests__/event-dispatcher-idle.integration.test.ts +94 -0
  122. package/src/pipeline/__tests__/event-dispatcher-start-from.integration.test.ts +103 -3
  123. package/src/pipeline/__tests__/fetch-for-writing.integration.test.ts +1 -1
  124. package/src/pipeline/__tests__/idempotency-transient-failure.integration.test.ts +1 -1
  125. package/src/pipeline/__tests__/job-trigger-consumer.integration.test.ts +1 -1
  126. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +1 -1
  127. package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +1 -1
  128. package/src/pipeline/__tests__/msp-multi-hop.integration.test.ts +1 -1
  129. package/src/pipeline/__tests__/msp-rebuild.integration.test.ts +116 -1
  130. package/src/pipeline/__tests__/multi-stream-projection.integration.test.ts +1 -1
  131. package/src/pipeline/__tests__/pending-gap-ranges.test.ts +94 -0
  132. package/src/pipeline/__tests__/post-query-hook.integration.test.ts +1 -1
  133. package/src/pipeline/__tests__/public-intake-runtime-gate.integration.test.ts +1 -1
  134. package/src/pipeline/__tests__/query-projection.integration.test.ts +1 -1
  135. package/src/pipeline/__tests__/rate-limit-default.integration.test.ts +1 -1
  136. package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +1 -1
  137. package/src/pipeline/__tests__/search-consumer-named-events.integration.test.ts +1 -1
  138. package/src/pipeline/__tests__/system-identity-switch.integration.test.ts +1 -1
  139. package/src/pipeline/__tests__/try-append-event.integration.test.ts +1 -1
  140. package/src/pipeline/__tests__/unsafe-raw-escape-hatch.integration.test.ts +1 -1
  141. package/src/pipeline/dispatch-stream.ts +6 -1
  142. package/src/pipeline/event-dispatcher-delivery.ts +132 -37
  143. package/src/pipeline/event-dispatcher.ts +96 -9
  144. package/src/pipeline/index.ts +1 -0
  145. package/src/pipeline/msp-rebuild.ts +39 -1
  146. package/src/pipeline/pending-gap-ranges.ts +48 -0
  147. package/src/pipeline/system-hooks.ts +223 -68
  148. package/src/pipeline/write-origin.ts +1 -1
  149. package/src/rate-limit/__tests__/dispatcher-l3.integration.test.ts +1 -1
  150. package/src/search/__tests__/derive-search-adapter-config.test.ts +65 -0
  151. package/src/search/__tests__/meilisearch-adapter.integration.test.ts +91 -0
  152. package/src/search/__tests__/search-consumer-batch.integration.test.ts +306 -0
  153. package/src/search/derive-search-adapter-config.ts +27 -0
  154. package/src/search/index.ts +1 -0
  155. package/src/search/meilisearch-adapter.ts +96 -14
  156. package/src/secrets/__tests__/contains-secret.test.ts +1 -1
  157. package/src/stack/__tests__/request-helper.integration.test.ts +1 -1
  158. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +1 -1
  159. package/src/stack/__tests__/setup-test-stack-metrics.integration.test.ts +1 -1
  160. package/src/testing/__tests__/e2e-generator.test.ts +1 -1
  161. package/src/testing/e2e-generator.ts +1 -1
@@ -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
 
@@ -4,7 +4,10 @@
4
4
  // into validateColumnRendererForm, splitting them would create a
5
5
  // same-folder require cycle.
6
6
 
7
+ import { resolveActionIcon } from "@cosmicdrift/kumiko-types/action-icon";
7
8
  import { NO_WIDGET_FIELD_TYPES } from "@cosmicdrift/kumiko-types/fields";
9
+ import type { IconKey } from "@cosmicdrift/kumiko-types/nav-icon";
10
+ import { NAV_ICON_KEYS } from "@cosmicdrift/kumiko-types/nav-icon";
8
11
  import { rowMetaFieldNames } from "../../db/table-builder";
9
12
  import { LIST_ROW_META_COLUMNS } from "../../ui-types/list-row-meta";
10
13
  import { parseRefTarget } from "../parse-ref-target";
@@ -319,6 +322,41 @@ function validateWizardLayout(
319
322
  // are NOT validated here — the r.uiComponent registry that would resolve
320
323
  // them ships in M4/M5. Until then those are kept opaque on purpose.
321
324
 
325
+ // every action rendered in a Card title row (screen-level
326
+ // `actions`, section-level `actions`, `emptyState.action`) must draw an
327
+ // icon — declared or id-derived (resolveActionIcon, same resolver the
328
+ // renderer uses). Uncovered here: entityList/projectionList rowActions —
329
+ // those render inside a DataTable row, not a Card title row, so they keep
330
+ // today's "icon optional" behavior.
331
+ //
332
+ // Registration check (not just "resolved is not undefined"): action.icon is
333
+ // typed IconKey, a closed union — but that only protects call sites this
334
+ // repo's own `tsc --build` actually covers (samples and external consumers
335
+ // aren't in that project graph). The renderer's actionIconFor
336
+ // (renderer-web/src/primitives/index.tsx) already falls back to "no icon"
337
+ // for an unregistered key instead of crashing — this check makes the same
338
+ // gap fail loudly at boot instead of silently at render.
339
+ function isRegisteredIcon(icon: IconKey): boolean {
340
+ return (NAV_ICON_KEYS as readonly string[]).includes(icon);
341
+ }
342
+
343
+ export function validateActionHasIcon(
344
+ featureName: string,
345
+ screenId: string,
346
+ screenType: "projectionDetail" | "entityEdit",
347
+ context: string,
348
+ action: { readonly id: string; readonly icon?: IconKey },
349
+ ): void {
350
+ const resolved = resolveActionIcon(action.id, action.icon);
351
+ if (resolved === undefined || !isRegisteredIcon(resolved)) {
352
+ throw new Error(
353
+ `[Feature ${featureName}] Screen "${screenId}" (${screenType}) ${context} "${action.id}" has no ` +
354
+ `resolvable icon — set icon to a key registered in NAV_ICONS, or use an id that resolves one of ` +
355
+ `its kebab segments via the default icon map (resolveActionIcon).`,
356
+ );
357
+ }
358
+ }
359
+
322
360
  // Tier 2.7e-3: deklarative Feld-Referenzen einer Action gegen die Entity-
323
361
  // Felder pinnen — ein Tippfehler in pick/map-Quellfeldern oder
324
362
  // visible.field erzeugte sonst still `undefined` im Payload bzw. dauerhaft
@@ -1137,6 +1175,17 @@ export function validateScreens(
1137
1175
  }
1138
1176
  if (screen.layout.mode === "tabs") {
1139
1177
  validateTabSections(feature.name, screenId, "projectionDetail", screen.layout.sections);
1178
+ for (const section of screen.layout.sections) {
1179
+ const isFieldsSection = section.kind === undefined || section.kind === "fields";
1180
+ if (isFieldsSection && section.groups !== undefined && section.actions !== undefined) {
1181
+ throw new Error(
1182
+ `[Feature ${feature.name}] Screen "${screenId}" (projectionDetail) section "${section.id}" ` +
1183
+ `declares both groups and actions in tabs mode — actions has no single group's Card to sit ` +
1184
+ `in, and an extra title-less Card around the group cards would nest a Card inside a Card. ` +
1185
+ `Move the actions onto one group's title, or drop groups for a flat fields grid.`,
1186
+ );
1187
+ }
1188
+ }
1140
1189
  }
1141
1190
  if (screen.metrics !== undefined) {
1142
1191
  for (const metric of screen.metrics) {
@@ -1166,6 +1215,27 @@ export function validateScreens(
1166
1215
  }
1167
1216
  }
1168
1217
  for (const section of screen.layout.sections) {
1218
+ const sectionLabel = section.title ?? section.id ?? "(untitled)";
1219
+ if (section.actions !== undefined) {
1220
+ for (const action of section.actions) {
1221
+ validateActionHasIcon(
1222
+ feature.name,
1223
+ screenId,
1224
+ "projectionDetail",
1225
+ `section "${sectionLabel}" action`,
1226
+ action,
1227
+ );
1228
+ }
1229
+ }
1230
+ if (section.kind === "relatedList" && section.emptyState?.action !== undefined) {
1231
+ validateActionHasIcon(
1232
+ feature.name,
1233
+ screenId,
1234
+ "projectionDetail",
1235
+ `section "${sectionLabel}" emptyState action`,
1236
+ section.emptyState.action,
1237
+ );
1238
+ }
1169
1239
  if (isExtensionEditSection(section)) {
1170
1240
  // projectionDetail is read-only (no composed form submit) — an
1171
1241
  // extension that persists through the host's Save button has
@@ -1402,6 +1472,7 @@ export function validateScreens(
1402
1472
  // a detail screen has no row to click.
1403
1473
  if (screen.actions !== undefined) {
1404
1474
  for (const action of screen.actions) {
1475
+ validateActionHasIcon(feature.name, screenId, "projectionDetail", "action", action);
1405
1476
  if (action.kind === "navigate" && action.rowClick === true) {
1406
1477
  throw new Error(
1407
1478
  `[Feature ${feature.name}] Screen "${qualifyEntityName(feature.name, "screen", screenId)}" ` +
@@ -1902,6 +1973,18 @@ export function validateScreens(
1902
1973
  );
1903
1974
  }
1904
1975
  for (const section of screen.layout.sections) {
1976
+ if (section.actions !== undefined) {
1977
+ const sectionLabel = section.title ?? section.id ?? "(untitled)";
1978
+ for (const action of section.actions) {
1979
+ validateActionHasIcon(
1980
+ feature.name,
1981
+ screenId,
1982
+ "entityEdit",
1983
+ `section "${sectionLabel}" action`,
1984
+ action,
1985
+ );
1986
+ }
1987
+ }
1905
1988
  if (isExtensionEditSection(section)) {
1906
1989
  if (section.component?.react === undefined && section.component?.native === undefined) {
1907
1990
  throw new Error(
@@ -1,6 +1,7 @@
1
1
  import type { CursorResult } from "@cosmicdrift/kumiko-types/cursor-types";
2
2
  import { ENTITY_CONVENTION_QUERY_BRAND } from "@cosmicdrift/kumiko-types/handlers";
3
- import type { ZodType, z } from "zod";
3
+ import type * as z from "zod";
4
+ import type { ZodType } from "zod";
4
5
  import type { ContainsSecret } from "../secrets/types";
5
6
  import { runPipeline } from "./run-pipeline";
6
7
  import type { HandlerContext, KumikoEventTypeMap, WriteEvent, WriteResult } from "./types";
@@ -1,5 +1,6 @@
1
1
  import { ENTITY_CONVENTION_QUERY_BRAND } from "@cosmicdrift/kumiko-types/handlers";
2
- import { type ZodType, z } from "zod";
2
+ import type { ZodType } from "zod";
3
+ import * as z from "zod";
3
4
  import type { DbRow } from "../db/connection";
4
5
  import {
5
6
  collectReferenceFields,
@@ -6,7 +6,7 @@
6
6
  import { describe, expect, test } from "bun:test";
7
7
  import { resolve } from "node:path";
8
8
  import { Project, type SourceFile } from "ts-morph";
9
- import type { z } from "zod";
9
+ import type * as z from "zod";
10
10
  import { parseFeatureFile, parseSourceFile } from "../parse";
11
11
  import { applyChanges, type PatternId } from "../patch";
12
12
  import {
@@ -22,7 +22,7 @@
22
22
  // additionally checked for field-type membership via the shared
23
23
  // `findUnknownEntityFieldTypes` helper (kept in sync with `extractEntity`).
24
24
 
25
- import { z } from "zod";
25
+ import * as z from "zod";
26
26
  import { type LifecycleHookType, LifecycleHookTypes } from "../constants";
27
27
  import type {
28
28
  ConfigKeyDefinition,
@@ -1,4 +1,5 @@
1
- import type { ZodType, z } from "zod";
1
+ import type * as z from "zod";
2
+ import type { ZodType } from "zod";
2
3
  import { LifecycleHookTypes } from "./constants";
3
4
  import type {
4
5
  AuthClaimsFn,
@@ -1,5 +1,6 @@
1
1
  import { normalizeEventPiiSubject } from "@cosmicdrift/kumiko-types/handlers";
2
- import { ZodObject, type ZodType, type z } from "zod";
2
+ import type * as z from "zod";
3
+ import { ZodObject, type ZodType } from "zod";
3
4
  import type { FeatureBuilderState } from "./feature-builder-state";
4
5
  import { resolveName } from "./handler-helpers";
5
6
  import { splitNamedDefinition, unwrapArrayForm } from "./object-form";
@@ -1,5 +1,6 @@
1
1
  import { ENTITY_CONVENTION_QUERY_BRAND } from "@cosmicdrift/kumiko-types/handlers";
2
- import type { ZodType, z } from "zod";
2
+ import type * as z from "zod";
3
+ import type { ZodType } from "zod";
3
4
  import { toTableName } from "../db/table-builder";
4
5
  import {
5
6
  isEntityConventionQueryHandler,
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import { moneyPayloadToMinorUnits } from "../db/money";
3
3
  import { isValidIanaTimeZone } from "../time";
4
4
  import { assertUnreachable } from "../utils";
@@ -10,7 +10,7 @@
10
10
  // throws the #2043 stub).
11
11
 
12
12
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
13
- import { z } from "zod";
13
+ import * as z from "zod";
14
14
  import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
15
15
  import { asRawClient } from "../../db/query";
16
16
  import { createRegistry, defineFeature } from "../../engine";
@@ -13,7 +13,7 @@
13
13
  // regressing to the Welle-2.5 state.
14
14
 
15
15
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
16
- import { z } from "zod";
16
+ import * as z from "zod";
17
17
  import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
18
18
  import { createRegistry, defineFeature } from "../../engine";
19
19
  import { createArchivedStreamsTable } from "../../event-store";
@@ -13,7 +13,7 @@
13
13
  // fires with systemConsumers explicitly disabled).
14
14
 
15
15
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
16
- import { z } from "zod";
16
+ import * as z from "zod";
17
17
  import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
18
18
  import { asRawClient } from "../../db/query";
19
19
  import { createRegistry, defineFeature } from "../../engine";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../../engine/define-feature";
4
4
  import {
5
5
  camelCase,
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { defineFeature } from "../../engine/define-feature";
4
4
  import { prometheusMetricsEnvSchema } from "../../observability/metrics-wiring";
5
5
  import { renderDryRun } from "../dry-run";
@@ -2,7 +2,7 @@
2
2
  // undefined. parseEnvDryRun liefert stattdessen ein ehrliches Partial.
3
3
 
4
4
  import { describe, expect, test } from "bun:test";
5
- import { z } from "zod";
5
+ import * as z from "zod";
6
6
  import { parseEnvDryRun } from "../index";
7
7
 
8
8
  const schema = z.object({
@@ -6,7 +6,7 @@
6
6
  // All helpers accept the *runtime* Zod instance — TypeScript wrapper
7
7
  // (`z.ZodType`) and core (`$ZodType`) are the same object at runtime.
8
8
 
9
- import type { z } from "zod";
9
+ import type * as z from "zod";
10
10
 
11
11
  export type ZodDef = {
12
12
  readonly innerType?: z.ZodType;
@@ -8,7 +8,7 @@
8
8
  // The same schema introspection (`classifyField`, `readKumikoMeta`) used
9
9
  // by parseEnv drives the output here — single source of truth.
10
10
 
11
- import type { z } from "zod";
11
+ import type * as z from "zod";
12
12
  import { zodShape } from "./_zod-introspect";
13
13
  import {
14
14
  type ComposedEnvSchema,
package/src/env/index.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  // Without meta, defaults: `camelCase(envVarName)` for the Pulumi key,
15
15
  // `<set-me>` placeholder for the value, no `--secret` flag.
16
16
 
17
- import { z } from "zod";
17
+ import * as z from "zod";
18
18
  import type { FeatureDefinition } from "../engine/types/feature";
19
19
  import { zodDef, zodDescription, zodMeta, zodShape, zodShapeField } from "./_zod-introspect";
20
20
 
@@ -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 {
4
4
  AccessDeniedError,
5
5
  buildErrorLog,
@@ -17,7 +17,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:tes
17
17
  import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
18
18
  import { tmpdir } from "node:os";
19
19
  import { join } from "node:path";
20
- import { z } from "zod";
20
+ import * as z from "zod";
21
21
  import { createEventStoreExecutor } from "../../db/event-store-executor";
22
22
  import { asRawClient, insertOne, selectMany } from "../../db/query";
23
23
  import { buildEntityTable } from "../../db/table-builder";
@@ -12,7 +12,7 @@ import {
12
12
  resetBlindIndexKeyForTests,
13
13
  resetPiiSubjectKmsForTests,
14
14
  } from "@cosmicdrift/kumiko-framework/testing";
15
- import { z } from "zod";
15
+ import * as z from "zod";
16
16
  import {
17
17
  configureBlindIndexKey,
18
18
  configurePiiSubjectKms,
@@ -15,7 +15,7 @@
15
15
 
16
16
  import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test";
17
17
  import { UNATTRIBUTED_ORIGIN, type WriteOrigin } from "@cosmicdrift/kumiko-types/event-store-types";
18
- import { z } from "zod";
18
+ import * as z from "zod";
19
19
  import { createEventStoreExecutor } from "../../db/event-store-executor";
20
20
  import { selectMany } from "../../db/query";
21
21
  import { buildEntityTable } from "../../db/table-builder";
@@ -9,7 +9,7 @@
9
9
  // can never silently hand half-migrated data to consumers.
10
10
 
11
11
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
12
- import { z } from "zod";
12
+ import * as z from "zod";
13
13
  import { integer as pgInteger, table as pgTable, text as pgText } from "../../db/dialect";
14
14
  import { createEventStoreExecutor } from "../../db/event-store-executor";
15
15
  import { asRawClient, insertOne, selectMany } from "../../db/query";
@@ -10,7 +10,7 @@
10
10
  // so a >= assertion is flake-free without inflating waitFor's budget.
11
11
 
12
12
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
13
- import { z } from "zod";
13
+ import * as z from "zod";
14
14
  import { defineFeature } from "../../engine";
15
15
  import { setupTestStack, type TestStack, TestUsers } from "../../stack";
16
16
  import { waitFor } from "../../testing";
@@ -1,6 +1,6 @@
1
1
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
2
  import type { Hono } from "hono";
3
- import { z } from "zod";
3
+ import * as z from "zod";
4
4
  import { buildServer, type JwtHelper } from "../../api";
5
5
  import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
6
6
  import { createRegistry, defineFeature, type SessionUser } from "../../engine";
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
10
10
  import type { Hono } from "hono";
11
- import { z } from "zod";
11
+ import * as z from "zod";
12
12
  import { buildServer, type JwtHelper } from "../../api";
13
13
  import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
14
14
  import { createRegistry, defineFeature, type SessionUser } from "../../engine";
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
4
4
  import { Queue } from "bullmq";
5
- import { z } from "zod";
5
+ import * as z from "zod";
6
6
  import type { SchemaTable } from "../../db";
7
7
  import { createEventStoreExecutor } from "../../db/event-store-executor";
8
8
  import { asRawClient, selectMany } from "../../db/query";
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
9
9
  import { Queue } from "bullmq";
10
- import { z } from "zod";
10
+ import * as z from "zod";
11
11
  import { createRegistry, defineFeature } from "../../engine";
12
12
  import type { AppContext, TenantId } from "../../engine/types";
13
13
  import { createTestRedis, type TestRedis } from "../../stack";
@@ -9,7 +9,7 @@
9
9
  // to the actor rather than to SYSTEM.
10
10
 
11
11
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
12
- import { z } from "zod";
12
+ import * as z from "zod";
13
13
  import { ROLES } from "../../auth/roles";
14
14
  import { type BunTestDb, createTestDb } from "../../bun-db/__tests__/bun-test-db";
15
15
  import { asRawClient } from "../../db/query";
@@ -1,6 +1,6 @@
1
1
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
2
  import { Queue } from "bullmq";
3
- import { z } from "zod";
3
+ import * as z from "zod";
4
4
  import { requestContext } from "../../api/request-context";
5
5
  import { tenantDbRunner } from "../../db/tenant-db-runner";
6
6
  import { createRegistry, defineFeature } from "../../engine";
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { composeEnvSchema, readKumikoMeta } from "../../env";
4
4
  import { prometheusMetricsEnvSchema, resolveObservabilityWiring } from "../metrics-wiring";
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import type { TableColumns } from "../../db/dialect";
4
4
  import { buildEntityTable } from "../../db/table-builder";
5
5
  import { createRegistry, defineFeature } from "../../engine";
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import { createNoopProvider } from "./noop-provider";
3
3
  import { createPrometheusMeter, type PrometheusMeter } from "./prometheus-meter";
4
4
  import type { ObservabilityProvider } from "./types";