@cosmicdrift/kumiko-framework 0.367.0 → 0.369.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 (240) hide show
  1. package/AGENTS.md +1 -7
  2. package/README.md +1 -1
  3. package/agents/examples/access-control.md +2 -2
  4. package/agents/examples/anonymous-access-multitenant.md +2 -2
  5. package/agents/examples/anonymous-access.md +3 -3
  6. package/agents/examples/apex-landing.md +1 -1
  7. package/agents/examples/apex-surface-auth.md +6 -7
  8. package/agents/examples/api-worker-split.md +7 -8
  9. package/agents/examples/auth-claims.md +2 -1
  10. package/agents/examples/auth-foundation-providers.md +3 -3
  11. package/agents/examples/auth-mfa.md +3 -4
  12. package/agents/examples/basic-entity.md +3 -4
  13. package/agents/examples/chat-channels.md +4 -4
  14. package/agents/examples/compliance-profiles-demo.md +2 -3
  15. package/agents/examples/cross-feature-events.md +4 -4
  16. package/agents/examples/crypto-shredding-hr.md +14 -9
  17. package/agents/examples/currencies-global.md +3 -4
  18. package/agents/examples/currencies-per-tenant.md +3 -4
  19. package/agents/examples/custom-fields-basic.md +2 -3
  20. package/agents/examples/custom-handlers.md +3 -5
  21. package/agents/examples/dashboard-stats.md +2 -3
  22. package/agents/examples/delivery-notifications.md +5 -6
  23. package/agents/examples/e2e-generator.md +1 -1
  24. package/agents/examples/embedded-entity-form.md +2 -3
  25. package/agents/examples/embedded.md +2 -3
  26. package/agents/examples/encrypted-tenant-config.md +5 -6
  27. package/agents/examples/error-contract.md +3 -4
  28. package/agents/examples/event-sourcing.md +19 -23
  29. package/agents/examples/feature-toggles.md +4 -4
  30. package/agents/examples/field-access.md +3 -4
  31. package/agents/examples/files-post-processing.md +12 -13
  32. package/agents/examples/golden-path.md +208 -0
  33. package/agents/examples/idempotency.md +2 -2
  34. package/agents/examples/inbound-mail-triage.md +7 -7
  35. package/agents/examples/lane-routing.md +3 -3
  36. package/agents/examples/ledger-banking.md +2 -3
  37. package/agents/examples/ledger-invoicing.md +2 -3
  38. package/agents/examples/legal-pages.md +1 -1
  39. package/agents/examples/lifecycle-hooks.md +3 -3
  40. package/agents/examples/managed-config.md +3 -4
  41. package/agents/examples/managed-pages.md +2 -2
  42. package/agents/examples/notes-history-basic.md +4 -5
  43. package/agents/examples/ownership.md +3 -5
  44. package/agents/examples/pdf-audit-export.md +2 -3
  45. package/agents/examples/pipeline-basics.md +2 -2
  46. package/agents/examples/public-share-token.md +3 -3
  47. package/agents/examples/rate-limiting.md +2 -1
  48. package/agents/examples/realtime-sse.md +2 -3
  49. package/agents/examples/record-detail-layout.md +1 -1
  50. package/agents/examples/reference-data.md +3 -3
  51. package/agents/examples/relations.md +2 -2
  52. package/agents/examples/screens-nav.md +1 -1
  53. package/agents/examples/search.md +2 -2
  54. package/agents/examples/secrets-demo.md +8 -8
  55. package/agents/examples/security-baseline.md +2 -2
  56. package/agents/examples/session-revocation.md +7 -8
  57. package/agents/examples/state-machine.md +181 -165
  58. package/agents/examples/tags-basic.md +2 -3
  59. package/agents/examples/tenant-isolation.md +2 -3
  60. package/agents/examples/tenant-settings.md +2 -2
  61. package/agents/examples/tier-admin.md +3 -4
  62. package/agents/examples/timezones.md +3 -3
  63. package/agents/examples/user-data-rights.md +12 -8
  64. package/agents/examples/user-profile.md +3 -3
  65. package/agents/examples/webhook-step.md +2 -3
  66. package/agents/examples/wizard-form.md +4 -5
  67. package/agents/examples/workflow-engine.md +7 -3
  68. package/agents/examples/writeform-section.md +1 -1
  69. package/agents/examples.md +1 -0
  70. package/agents/features.md +4 -4
  71. package/dist/consumer-cli.js +2 -3
  72. package/dist/crypto/index.d.ts +1 -1
  73. package/dist/crypto/index.js +1 -1
  74. package/dist/crypto/owner-transition-rekey.js +2 -0
  75. package/dist/crypto/pg-kms-adapter.js +1 -1
  76. package/dist/crypto/subject-resolver.d.ts +2 -0
  77. package/dist/crypto/subject-resolver.js +30 -15
  78. package/dist/db/bun-db-deprecated.d.ts +10 -0
  79. package/dist/{bun-db/index.js → db/bun-db-deprecated.js} +4 -3
  80. package/dist/db/config-seed.js +1 -1
  81. package/dist/db/connection.d.ts +23 -5
  82. package/dist/db/connection.js +10 -5
  83. package/dist/db/dialect.js +1 -1
  84. package/dist/db/entity-field-encryption.d.ts +5 -0
  85. package/dist/db/entity-field-encryption.js +29 -0
  86. package/dist/db/entity-table-meta.js +6 -5
  87. package/dist/db/event-store-executor-read.js +26 -5
  88. package/dist/db/index.d.ts +4 -4
  89. package/dist/db/index.js +1 -1
  90. package/dist/db/migrate-runner.js +1 -1
  91. package/dist/db/queries/backfill-pii.js +16 -9
  92. package/dist/db/queries/ddl.js +1 -1
  93. package/dist/db/queries/event-store.d.ts +2 -0
  94. package/dist/db/queries/event-store.js +26 -0
  95. package/dist/db/queries/test-stack.js +1 -1
  96. package/dist/db/query.d.ts +105 -1
  97. package/dist/db/query.js +1450 -3
  98. package/dist/{stack/table-helpers.d.ts → db/table-push.d.ts} +5 -15
  99. package/dist/{stack/table-helpers.js → db/table-push.js} +6 -46
  100. package/dist/db/tenant-db.d.ts +11 -0
  101. package/dist/db/tenant-db.js +51 -12
  102. package/dist/derivatives/derivatives-context.d.ts +1 -1
  103. package/dist/derivatives/derivatives-context.js +1 -1
  104. package/dist/engine/boot-validator/gdpr-storage.js +3 -3
  105. package/dist/engine/boot-validator/pii-retention.js +3 -3
  106. package/dist/engine/boot-validator/record-owned.js +3 -3
  107. package/dist/engine/boot-validator/screens.js +37 -1
  108. package/dist/engine/bridge-stub.d.ts +4 -0
  109. package/dist/engine/bridge-stub.js +62 -0
  110. package/dist/engine/build-config-feature-schema.js +197 -65
  111. package/dist/engine/event-helpers.d.ts +2 -1
  112. package/dist/engine/event-helpers.js +1 -0
  113. package/dist/engine/extension-selector-plugins.d.ts +3 -0
  114. package/dist/engine/extension-selector-plugins.js +14 -0
  115. package/dist/engine/feature-config-events-jobs.d.ts +2 -0
  116. package/dist/engine/feature-config-events-jobs.js +3 -2
  117. package/dist/engine/index.d.ts +1 -0
  118. package/dist/engine/index.js +1 -0
  119. package/dist/es-ops/operations-schema.js +1 -1
  120. package/dist/event-store/admin-api.js +1 -1
  121. package/dist/event-store/archive.js +1 -1
  122. package/dist/event-store/errors.d.ts +2 -1
  123. package/dist/event-store/errors.js +6 -2
  124. package/dist/event-store/event-store.js +17 -9
  125. package/dist/event-store/events-schema.js +1 -1
  126. package/dist/event-store/rebuild-dead-letter.js +3 -3
  127. package/dist/event-store/snapshot.js +1 -1
  128. package/dist/event-store/upcaster-dead-letter.js +3 -3
  129. package/dist/files/create-file-ref-from-bytes.js +1 -1
  130. package/dist/files/file-routes.js +1 -1
  131. package/dist/i18n/settings-hub-keys.js +1 -2
  132. package/dist/internal/stack-support.d.ts +12 -0
  133. package/dist/internal/stack-support.js +12 -0
  134. package/dist/internal/testing.d.ts +4 -0
  135. package/dist/internal/testing.js +5 -0
  136. package/dist/jobs/job-runner.js +23 -11
  137. package/dist/migrations/pending-rebuilds.js +1 -1
  138. package/dist/migrations/pii-event-backfill.js +2 -2
  139. package/dist/pipeline/dispatch-shared.js +43 -23
  140. package/dist/pipeline/dispatch-write.js +6 -2
  141. package/dist/pipeline/dispatcher-utils.js +6 -2
  142. package/dist/pipeline/event-consumer-state.js +1 -1
  143. package/dist/pipeline/projection-rebuild.d.ts +1 -0
  144. package/dist/pipeline/projection-rebuild.js +1 -1
  145. package/dist/pipeline/projection-state.js +1 -1
  146. package/dist/pipeline/system-hooks.js +1 -1
  147. package/dist/schema-cli.js +3 -3
  148. package/dist/search/purge-subject.js +20 -10
  149. package/dist/seeding/index.d.ts +1 -0
  150. package/dist/seeding/index.js +1 -0
  151. package/dist/stack/index.d.ts +10 -9
  152. package/dist/stack/index.js +11 -19
  153. package/dist/testing/e2e-generator.d.ts +5 -49
  154. package/dist/testing/e2e-generator.js +5 -382
  155. package/dist/testing/handler-context.d.ts +5 -4
  156. package/dist/testing/handler-context.js +5 -69
  157. package/dist/testing/index.d.ts +10 -26
  158. package/dist/testing/index.js +10 -34
  159. package/dist/testing/real-providers.d.ts +5 -3
  160. package/dist/testing/real-providers.js +5 -21
  161. package/dist/upgrade-cli.js +6 -4
  162. package/package.json +37 -9
  163. package/src/changes.json +64 -0
  164. package/src/scripts/codemod/README.md +43 -0
  165. package/src/scripts/codemod/codemod-support.ts +276 -0
  166. package/src/scripts/codemod/db-factory.ts +810 -0
  167. package/src/scripts/codemod/feature-factory.ts +429 -0
  168. package/src/scripts/codemod/s3-imports.ts +274 -0
  169. package/src/scripts/codemod/tenant-db-options.ts +454 -0
  170. package/src/scripts/codemod/testing-move-targets.ts +161 -0
  171. package/src/scripts/codemod/testing-move.ts +520 -0
  172. package/dist/bun-db/connection.d.ts +0 -17
  173. package/dist/bun-db/connection.js +0 -40
  174. package/dist/bun-db/index.d.ts +0 -4
  175. package/dist/bun-db/query.d.ts +0 -106
  176. package/dist/bun-db/query.js +0 -1457
  177. package/dist/db/api.d.ts +0 -19
  178. package/dist/db/api.js +0 -23
  179. package/dist/db/bun-provider.d.ts +0 -2
  180. package/dist/db/bun-provider.js +0 -28
  181. package/dist/db/postgres-provider.d.ts +0 -2
  182. package/dist/db/postgres-provider.js +0 -21
  183. package/dist/db/query-api.d.ts +0 -2
  184. package/dist/db/query-api.js +0 -6
  185. package/dist/stack/db.d.ts +0 -22
  186. package/dist/stack/db.js +0 -67
  187. package/dist/stack/drain-event-consumers.d.ts +0 -4
  188. package/dist/stack/drain-event-consumers.js +0 -51
  189. package/dist/stack/drain-jobs.d.ts +0 -22
  190. package/dist/stack/drain-jobs.js +0 -141
  191. package/dist/stack/event-collector.d.ts +0 -9
  192. package/dist/stack/event-collector.js +0 -12
  193. package/dist/stack/push-entity-projection-tables.d.ts +0 -24
  194. package/dist/stack/push-entity-projection-tables.js +0 -74
  195. package/dist/stack/redis.d.ts +0 -17
  196. package/dist/stack/redis.js +0 -52
  197. package/dist/stack/request-helper.d.ts +0 -35
  198. package/dist/stack/request-helper.js +0 -144
  199. package/dist/stack/test-stack.d.ts +0 -192
  200. package/dist/stack/test-stack.js +0 -429
  201. package/dist/testing/access-assertions.d.ts +0 -2
  202. package/dist/testing/access-assertions.js +0 -13
  203. package/dist/testing/assertions.d.ts +0 -10
  204. package/dist/testing/assertions.js +0 -22
  205. package/dist/testing/boot-validator-fixture.d.ts +0 -3
  206. package/dist/testing/boot-validator-fixture.js +0 -101
  207. package/dist/testing/closed-connection-error.d.ts +0 -7
  208. package/dist/testing/closed-connection-error.js +0 -46
  209. package/dist/testing/db-cleanup.d.ts +0 -9
  210. package/dist/testing/db-cleanup.js +0 -31
  211. package/dist/testing/expect-error.d.ts +0 -2
  212. package/dist/testing/expect-error.js +0 -22
  213. package/dist/testing/file-provider-contract.d.ts +0 -2
  214. package/dist/testing/file-provider-contract.js +0 -102
  215. package/dist/testing/http-cookies.d.ts +0 -7
  216. package/dist/testing/http-cookies.js +0 -47
  217. package/dist/testing/late-bound.d.ts +0 -9
  218. package/dist/testing/late-bound.js +0 -31
  219. package/dist/testing/manual-clock.d.ts +0 -14
  220. package/dist/testing/manual-clock.js +0 -12
  221. package/dist/testing/multipart-helper.d.ts +0 -22
  222. package/dist/testing/multipart-helper.js +0 -77
  223. package/dist/testing/mutable-master-key-provider.d.ts +0 -7
  224. package/dist/testing/mutable-master-key-provider.js +0 -35
  225. package/dist/testing/observability-recorder.d.ts +0 -9
  226. package/dist/testing/observability-recorder.js +0 -29
  227. package/dist/testing/seed.d.ts +0 -9
  228. package/dist/testing/seed.js +0 -21
  229. package/dist/testing/shared-entities.d.ts +0 -286
  230. package/dist/testing/shared-entities.js +0 -56
  231. package/dist/testing/utils.d.ts +0 -1
  232. package/dist/testing/utils.js +0 -1
  233. package/dist/testing/wait-for.d.ts +0 -20
  234. package/dist/testing/wait-for.js +0 -47
  235. package/dist/testing/without-ambient-temporal.d.ts +0 -5
  236. package/dist/testing/without-ambient-temporal.js +0 -17
  237. package/dist/testing/zip-fixture.d.ts +0 -1
  238. package/dist/testing/zip-fixture.js +0 -33
  239. /package/dist/{stack/test-users.d.ts → seeding/seed-users.d.ts} +0 -0
  240. /package/dist/{stack/test-users.js → seeding/seed-users.js} +0 -0
@@ -1,49 +1,5 @@
1
- import type * as z from "zod";
2
- import { type Registry } from "../engine/index.js";
3
- export type E2ETestSpec = {
4
- readonly kind: "list-renders";
5
- readonly screenQn: string;
6
- readonly title: string;
7
- readonly urlPath: string;
8
- } | {
9
- readonly kind: "list-has-fixture-row";
10
- readonly screenQn: string;
11
- readonly title: string;
12
- readonly urlPath: string;
13
- readonly writeHandlerQn: string;
14
- readonly fixture: Readonly<Record<string, unknown>>;
15
- readonly identifyingValue: string;
16
- } | {
17
- readonly kind: "edit-validates-required";
18
- readonly screenQn: string;
19
- readonly title: string;
20
- readonly urlPath: string;
21
- readonly requiredFields: readonly string[];
22
- } | {
23
- readonly kind: "edit-save-persists";
24
- readonly screenQn: string;
25
- readonly title: string;
26
- readonly urlPath: string;
27
- readonly listUrlPath?: string;
28
- readonly fills: readonly EditFillOp[];
29
- readonly identifyingValue: string;
30
- readonly identifyingField: string;
31
- };
32
- export type EditFillOp = {
33
- readonly kind: "fill";
34
- readonly field: string;
35
- readonly value: string;
36
- } | {
37
- readonly kind: "check";
38
- readonly field: string;
39
- readonly value: boolean;
40
- } | {
41
- readonly kind: "select";
42
- readonly field: string;
43
- readonly value: string;
44
- };
45
- export type E2EGeneratorOptions = {
46
- readonly tenantPlaceholder?: string;
47
- };
48
- export declare function generateE2ESpec(registry: Registry, options?: E2EGeneratorOptions): readonly E2ETestSpec[];
49
- export declare function generateZodFixture(schema: z.ZodTypeAny): unknown;
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/helpers/e2e-generator`. Run
3
+ * `bun kumiko-upgrade --apply` to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export * from "@cosmicdrift/kumiko-testing/helpers/e2e-generator";
@@ -1,382 +1,5 @@
1
- // @runtime tooling
2
- //
3
- // E2E-Generator — leitet strukturierte TestSpecs aus der Registry ab.
4
- //
5
- // Grundidee: jede r.screen(...) ist eine testbare Oberfläche. Der Generator
6
- // iteriert getAllScreens() und baut pro Screen einen Satz TestSpecs (vier
7
- // Kinds, einer pro "was muss mindestens funktionieren"). Die Specs sind
8
- // JSON-serialisierbar — Consumer (Sample/Package-e2e) schreiben sie via
9
- // bun-Script als JSON auf Platte, und ein Playwright-globalSetup triggert
10
- // das vor jedem Run. Der eigentliche Playwright-Runner liest die JSON und
11
- // iteriert mit kind-spezifischen Handlern gegen den echten Renderer.
12
- //
13
- // Vorteil des 2-Prozess-Modells: die framework-runtime (Registry, drizzle,
14
- // ioredis-types etc.) lädt NIE im Playwright-Worker. Würde sie das, kollidierte
15
- // sie mit Playwrights Object.prototype-Symbolen ($$jest-matchers-object) und
16
- // der Worker würde beim spec-import crashen.
17
- //
18
- // Zwei Stufen, jede einzeln testbar:
19
- // 1. generateE2ESpec(registry) — Registry → TestSpec[]
20
- // 2. generateZodFixture(schema) — ZodType → plausibler Value
21
- //
22
- // Stufe 2 ist intentionally minimal (Strategy a aus dem Plan): string/number/
23
- // boolean/date/enum/uuid + ZodOptional/ZodDefault-Unwrap. Alles andere wirft
24
- // "not supported yet" — wir füllen nach, wenn ein echter Caller es braucht.
25
- // Fake-komplexe-Werte würden nur False-Green-Tests produzieren.
26
- import { isFieldsEditSection, normalizeEditField, normalizeListColumn, parseQn, qn, sectionFieldSpecs, toKebab, } from "../engine/index.js";
27
- // --- Stufe 1: Spec-Ableitung aus der Registry ---
28
- export function generateE2ESpec(registry, options = {}) {
29
- const tenant = options.tenantPlaceholder ?? "{tenant}";
30
- const specs = [];
31
- for (const [screenQn, screen] of registry.getAllScreens()) {
32
- if (screen.type === "custom")
33
- continue; // keine generische Annahme möglich
34
- // actionForm: kein generischer E2E-Spec — Author-defined Handler
35
- // braucht Author-defined Test-Daten, die der Generator nicht kennt.
36
- if (screen.type === "actionForm")
37
- continue;
38
- // secretMint: same reasoning — author-defined write handler, no generic spec.
39
- if (screen.type === "secretMint")
40
- continue;
41
- // configEdit: dito — die Werte werden über config:write:set
42
- // pro Field geschrieben, ohne CRUD-Zustand zu generieren wäre der
43
- // Spec wertlos. Branding/SMTP/etc. sind Author-spezifisch.
44
- if (screen.type === "configEdit")
45
- continue;
46
- // secretsEdit: dito — writes go through secrets:write:set with
47
- // plaintext values the generator has no author-defined test data for.
48
- if (screen.type === "secretsEdit")
49
- continue;
50
- // projectionList: query-getrieben, Author-spezifische Projection —
51
- // kein generischer CRUD-Spec ableitbar (wie actionForm/configEdit).
52
- if (screen.type === "projectionList")
53
- continue;
54
- // projectionDetail: dito — read-only single-row inspector, kein CRUD-Zustand.
55
- if (screen.type === "projectionDetail")
56
- continue;
57
- // dashboard: panel-getrieben (Stat/Chart/List-Queries) — dito, keine
58
- // generische CRUD-Annahme möglich.
59
- if (screen.type === "dashboard")
60
- continue;
61
- const { scope: feature, name: short } = parseQn(screenQn);
62
- const urlPath = `/t/${tenant}/${feature}/${short}`;
63
- const title = `${feature}/${short}`;
64
- if (screen.type === "entityList") {
65
- specs.push(...buildListSpecs(screen, screenQn, title, urlPath, registry));
66
- }
67
- else {
68
- specs.push(...buildEditSpecs(screen, screenQn, title, urlPath, registry, tenant));
69
- }
70
- }
71
- return specs;
72
- }
73
- function buildListSpecs(screen, screenQn, title, urlPath, registry) {
74
- const out = [{ kind: "list-renders", screenQn, title, urlPath }];
75
- const entity = registry.getEntity(screen.entity);
76
- if (!entity)
77
- return out;
78
- const writeHandlerQn = findCreateHandlerQn(registry, screen.entity);
79
- if (!writeHandlerQn)
80
- return out;
81
- const fixture = buildEntityFixture(entity);
82
- const identifyingValue = pickIdentifyingValue(fixture, screen, entity);
83
- if (identifyingValue === undefined)
84
- return out;
85
- out.push({
86
- kind: "list-has-fixture-row",
87
- screenQn,
88
- title,
89
- urlPath,
90
- writeHandlerQn,
91
- fixture,
92
- identifyingValue,
93
- });
94
- return out;
95
- }
96
- function buildEditSpecs(screen, screenQn, title, urlPath, registry, tenant) {
97
- const out = [];
98
- const entity = registry.getEntity(screen.entity);
99
- if (!entity)
100
- return out;
101
- const requiredFields = collectRequiredEditFields(screen, entity);
102
- if (requiredFields.length > 0) {
103
- out.push({ kind: "edit-validates-required", screenQn, title, urlPath, requiredFields });
104
- }
105
- const fixture = buildEntityFixture(entity);
106
- const listScreen = findListScreenForEntity(registry, screen.entity);
107
- const listUrlPath = listScreen ? buildScreenUrlPath(listScreen.qn, tenant) : undefined;
108
- const identifying = pickIdentifyingForEdit(fixture, screen, entity, listScreen?.def);
109
- if (identifying) {
110
- const fills = buildEditFillOps(screen, entity, fixture);
111
- out.push({
112
- kind: "edit-save-persists",
113
- screenQn,
114
- title,
115
- urlPath,
116
- listUrlPath,
117
- fills,
118
- identifyingValue: identifying.value,
119
- identifyingField: identifying.field,
120
- });
121
- }
122
- return out;
123
- }
124
- function buildScreenUrlPath(qn, tenant) {
125
- const { scope, name } = parseQn(qn);
126
- return `/t/${tenant}/${scope}/${name}`;
127
- }
128
- function findCreateHandlerQn(registry, entityName) {
129
- // feature.writeHandlers ist mit Short-Names gekeyt ("task:create"), der
130
- // Registry-Lookup braucht die qualified Form ("tasks:write:task:create").
131
- // Wir qualifizieren via qn() + toKebab wie die Registry selbst — das ist
132
- // der Vertrag aus qualified-name.ts.
133
- for (const feature of registry.features.values()) {
134
- for (const shortName of Object.keys(feature.writeHandlers ?? {})) {
135
- if (!shortName.endsWith(":create"))
136
- continue;
137
- const qualified = qn(toKebab(feature.name), "write", toKebab(shortName));
138
- if (registry.getHandlerEntity(qualified) === entityName)
139
- return qualified;
140
- }
141
- }
142
- return undefined;
143
- }
144
- function findListScreenForEntity(registry, entityName) {
145
- for (const [qn, screen] of registry.getAllScreens()) {
146
- if (screen.type === "entityList" && screen.entity === entityName) {
147
- return { qn, def: screen };
148
- }
149
- }
150
- return undefined;
151
- }
152
- function collectRequiredEditFields(screen, entity) {
153
- const out = [];
154
- for (const section of screen.layout.sections) {
155
- if (!isFieldsEditSection(section))
156
- continue;
157
- for (const rawField of sectionFieldSpecs(section)) {
158
- const { field } = normalizeEditField(rawField);
159
- const def = entity.fields[field];
160
- if (def && "required" in def && def.required === true) {
161
- out.push(field);
162
- }
163
- }
164
- }
165
- return out;
166
- }
167
- function pickIdentifyingValue(fixture, screen, entity) {
168
- // Erste Text-Spalte mit Fixture-Wert — das ist die visuell zuverlässigste
169
- // Identifikation in einer generischen Tabelle.
170
- for (const raw of screen.columns) {
171
- const { field } = normalizeListColumn(raw);
172
- if (entity.fields[field]?.type !== "text")
173
- continue;
174
- const v = fixture[field];
175
- if (typeof v === "string" && v.length > 0)
176
- return v;
177
- }
178
- return undefined;
179
- }
180
- function buildEditFillOps(screen, entity, fixture) {
181
- // Laufe die Layout-Reihenfolge — das Resultat spiegelt, was ein User
182
- // tatsächlich ausfüllt. Per Field-Typ entscheiden wir die Interaktions-
183
- // form; Felder ohne Fixture-Wert (file/image/…) werden übersprungen.
184
- const ops = [];
185
- for (const section of screen.layout.sections) {
186
- if (!isFieldsEditSection(section))
187
- continue;
188
- for (const raw of sectionFieldSpecs(section)) {
189
- const { field } = normalizeEditField(raw);
190
- const def = entity.fields[field];
191
- if (!def)
192
- continue;
193
- const v = fixture[field];
194
- if (v === undefined)
195
- continue;
196
- switch (def.type) {
197
- case "boolean":
198
- if (typeof v === "boolean")
199
- ops.push({ kind: "check", field, value: v });
200
- break;
201
- case "select":
202
- if (typeof v === "string")
203
- ops.push({ kind: "select", field, value: v });
204
- break;
205
- case "text":
206
- case "longText":
207
- case "number":
208
- case "date":
209
- case "tz":
210
- // date: ein Text-Input, akzeptiert getippte ISO-Werte direkt.
211
- ops.push({ kind: "fill", field, value: String(v) });
212
- break;
213
- // embedded/money/timestamp/locatedTimestamp/file/image/files/images:
214
- // keine generische Interaktion. timestamp rendert seit #369 zwei
215
- // Inputs (Datum + Uhrzeit), die ein einzelnes .fill() nicht bedienen
216
- // kann — der Test-Autor liefert einen Hand-Override oder überspringt.
217
- default:
218
- break;
219
- }
220
- }
221
- }
222
- return ops;
223
- }
224
- function pickIdentifyingForEdit(fixture, editScreen, entity, listScreen) {
225
- // Bevorzugt ein Feld das im List-Screen als Column auftaucht — sonst ist
226
- // die "taucht in Liste auf"-Assertion nicht durchführbar.
227
- const columns = listScreen
228
- ? new Set(listScreen.columns.map((c) => normalizeListColumn(c).field))
229
- : undefined;
230
- for (const section of editScreen.layout.sections) {
231
- if (!isFieldsEditSection(section))
232
- continue;
233
- for (const rawField of sectionFieldSpecs(section)) {
234
- const { field } = normalizeEditField(rawField);
235
- if (entity.fields[field]?.type !== "text")
236
- continue;
237
- if (columns && !columns.has(field))
238
- continue;
239
- const v = fixture[field];
240
- if (typeof v === "string" && v.length > 0)
241
- return { field, value: v };
242
- }
243
- }
244
- return undefined;
245
- }
246
- function readZodInternals(schema) {
247
- return schema._def;
248
- }
249
- export function generateZodFixture(schema) {
250
- const def = readZodInternals(schema);
251
- const typeName = def?.type;
252
- switch (typeName) {
253
- case "optional":
254
- case "nullable":
255
- case "default": {
256
- if (!def?.innerType)
257
- throw new Error(`zod ${typeName} without innerType`);
258
- return generateZodFixture(def.innerType);
259
- }
260
- case "string":
261
- return fixtureString(def?.format);
262
- case "number":
263
- return 1;
264
- case "boolean":
265
- return true;
266
- case "enum": {
267
- const first = def?.entries ? Object.values(def.entries)[0] : undefined;
268
- return first ?? "";
269
- }
270
- case "date":
271
- return new Date("2026-01-01T00:00:00Z");
272
- case "pipe": {
273
- if (!def?.out)
274
- throw new Error("zod pipe without out");
275
- return generateZodFixture(def.out);
276
- }
277
- default:
278
- throw new Error(`generateZodFixture: not supported yet: ${typeName ?? "<unknown>"}`);
279
- }
280
- }
281
- function fixtureString(format) {
282
- if (format === "email")
283
- return "e2e@example.com";
284
- if (format === "url")
285
- return "https://example.com";
286
- if (format === "uuid" || format === "guid")
287
- return "00000000-0000-4000-8000-000000000000";
288
- if (format === "datetime" || format === "date")
289
- return "2026-01-01T00:00:00Z";
290
- return "e2e-fixture";
291
- }
292
- // --- Fixture aus FieldDefinition (für Stufe 1 statt Zod-Schema) ---
293
- //
294
- // Bewusstes Duplicate zu generateZodFixture. Die beiden haben unterschiedliche
295
- // Aufgaben:
296
- // generateZodFixture — public, generisch, weiß nur Zod-Primitives
297
- // buildEntityFixture — intern, Kumiko-Domain, weiß über file-skip,
298
- // embedded-Shape, money/tz-Objekte und nutzt den
299
- // Feldnamen für lesbare Prefixes ("e2e-email@...")
300
- //
301
- // Ein Zusammenziehen über buildInsertSchema + generateZodFixture würde entweder
302
- // den Feldnamen-Hint verlieren (alle email-Fixtures bekämen denselben Wert —
303
- // Unique-Constraints wären inkonsistent) oder einen hint-Parameter in die
304
- // public API von generateZodFixture drücken, den externe Caller nie brauchen.
305
- function buildEntityFixture(entity) {
306
- const out = {};
307
- for (const [name, field] of Object.entries(entity.fields)) {
308
- const value = fieldToFixture(name, field);
309
- if (value !== undefined)
310
- out[name] = value;
311
- }
312
- return out;
313
- }
314
- function fieldToFixture(name, field) {
315
- switch (field.type) {
316
- case "text": {
317
- if (field.format === "email")
318
- return `e2e-${name}@example.com`;
319
- if (field.format === "url")
320
- return "https://example.com";
321
- return `e2e ${name}`;
322
- }
323
- case "longText":
324
- // longText hat keine format-Optionen — generic placeholder reicht.
325
- return `e2e ${name} (long-form content)`;
326
- case "boolean":
327
- return true;
328
- case "select": {
329
- const conditional = new Set(field.conditionalOptions?.flatMap((rule) => rule.options));
330
- return (field.default ??
331
- field.options.find((option) => !conditional.has(option)) ??
332
- field.options[0] ??
333
- "");
334
- }
335
- case "multiSelect": {
336
- const first = field.options[0];
337
- return first ? [first] : [];
338
- }
339
- case "number":
340
- return 1;
341
- case "money":
342
- return { amount: 1, currency: "EUR" };
343
- case "date":
344
- return "2026-01-01";
345
- case "timestamp":
346
- return field.locatedBy !== undefined ? "2026-01-01T00:00:00" : "2026-01-01T00:00:00Z";
347
- case "tz":
348
- return "Europe/Berlin";
349
- case "locatedTimestamp":
350
- return { at: "2026-01-01T00:00:00", tz: "Europe/Berlin" };
351
- case "embedded": {
352
- const sub = {};
353
- for (const [subName, subDef] of Object.entries(field.schema)) {
354
- sub[subName] =
355
- subDef.type === "text"
356
- ? `e2e ${subName}`
357
- : subDef.type === "number" || subDef.type === "decimal"
358
- ? 1
359
- : subDef.type === "money"
360
- ? 100
361
- : subDef.type === "boolean"
362
- ? true
363
- : "2026-01-01";
364
- }
365
- return field.multiple === true ? [sub] : sub;
366
- }
367
- case "jsonb":
368
- // Free-form jsonb — e2e-generator returns empty-object.
369
- return {};
370
- case "file":
371
- case "image":
372
- case "files":
373
- case "images":
374
- // File-Upload-Fixture ist noch nicht generisch: Playwright müsste
375
- // Dateiinhalte mitliefern, und die Write-API verlangt bereits ge-
376
- // uploadete fileRef-UUIDs. Lässt sich nachziehen, sobald Sample die
377
- // Pfade klärt (M4).
378
- return undefined;
379
- default:
380
- return undefined;
381
- }
382
- }
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/helpers/e2e-generator`. Run
3
+ * `bun kumiko-upgrade --apply` to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export * from "@cosmicdrift/kumiko-testing/helpers/e2e-generator";
@@ -1,4 +1,5 @@
1
- import type { HandlerContext, SessionUser } from "../engine/types/index.js";
2
- export declare function bridgeStub(opts?: {
3
- readonly user?: SessionUser;
4
- }): Pick<HandlerContext, "query" | "queryAs" | "write" | "writeAs" | "appendEvent" | "unsafeAppendEvent" | "tryAppendEvent" | "fetchForWriting" | "loadAggregate" | "archiveStream" | "restoreStream" | "isStreamArchived" | "snapshotAggregate" | "loadAggregateWithSnapshot" | "queryProjection" | "resolveAuthClaims" | "resolveActiveMembership" | "queryAsMember" | "hasFeature" | "metrics" | "metricsFor" | "tracer" | "tz" | "locale" | "user">;
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-framework/engine`. Run `bun kumiko-upgrade --apply`
3
+ * to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export { bridgeStub } from "../engine/bridge-stub.js";
@@ -1,69 +1,5 @@
1
- // @runtime runtime
2
- //
3
- // bridgeStub hands back a HandlerContext shape with throw-on-use bridge
4
- // methods (ctx.query/write/loadAggregate/...). Used by both test code AND
5
- // production services (delivery-service uses it to run cross-feature notify
6
- // calls without a real dispatcher). Hence the runtime classification despite
7
- // living under `testing/` — no vitest imports, no test side-effects.
8
- import { createAnonymousUser } from "../engine/system-user.js";
9
- import { DEFAULT_LOCALE } from "../i18n/request-locale.js";
10
- import { createNoopMetricsHandle, getFallbackTracer } from "../observability/index.js";
11
- import { createTzContext } from "../time/index.js";
12
- // Test/service helper: cross-feature bridge methods that throw on use.
13
- //
14
- // Production code always receives a full HandlerContext from the Dispatcher's
15
- // buildHandlerContext (with real query/write closures). Some internal services
16
- // and tests construct a mini-context manually (typically just `{ db, registry }`)
17
- // to invoke a single handler. Those call sites don't use ctx.query/write —
18
- // the stubs make the TypeScript shape match while still failing loudly if
19
- // anything downstream accidentally reaches for them.
20
- //
21
- // Use: `{ db, registry, ...bridgeStub() }`
22
- const notAvailable = (what) => async () => {
23
- throw new Error(`ctx.${what} not available in this context — use the dispatcher, not a stubbed handler context`);
24
- };
25
- // Noop observability — hand back the shared fallback tracer so ctx.tracer has
26
- // a valid Tracer shape. No allocations per call.
27
- const noopTracer = getFallbackTracer();
28
- export function bridgeStub(opts) {
29
- // ctx.user is a convenience alias for event.user (see HandlerContext
30
- // docs). Caller code expects the field; bridgeStub hands back a stub with
31
- // anonymous default values when no user is passed explicitly. Test code
32
- // that cares about identity passes its own SessionUser here and gets it
33
- // back on ctx.
34
- const stubUser = opts?.user ??
35
- createAnonymousUser("00000000-0000-0000-0000-000000000000"); // @cast-boundary engine-bridge
36
- return {
37
- user: stubUser,
38
- query: notAvailable("query"), // @cast-boundary engine-bridge
39
- queryAs: notAvailable("queryAs"),
40
- write: notAvailable("write"),
41
- writeAs: notAvailable("writeAs"),
42
- appendEvent: notAvailable("appendEvent"),
43
- unsafeAppendEvent: notAvailable("unsafeAppendEvent"),
44
- tryAppendEvent: notAvailable("tryAppendEvent"),
45
- fetchForWriting: notAvailable("fetchForWriting"),
46
- loadAggregate: notAvailable("loadAggregate"),
47
- archiveStream: notAvailable("archiveStream"),
48
- restoreStream: notAvailable("restoreStream"),
49
- isStreamArchived: notAvailable("isStreamArchived"),
50
- snapshotAggregate: notAvailable("snapshotAggregate"),
51
- loadAggregateWithSnapshot: notAvailable("loadAggregateWithSnapshot"),
52
- queryProjection: notAvailable("queryProjection"),
53
- resolveAuthClaims: notAvailable("resolveAuthClaims"),
54
- resolveActiveMembership: notAvailable("resolveActiveMembership"),
55
- queryAsMember: notAvailable("queryAsMember"),
56
- // Stub defaults to always-enabled — matches the dispatcher's behaviour
57
- // when no effectiveFeatures resolver is wired (tests without toggles).
58
- hasFeature: async () => true,
59
- metrics: createNoopMetricsHandle(),
60
- metricsFor: () => createNoopMetricsHandle(),
61
- tracer: noopTracer,
62
- // Real TzContext, not notAvailable — test code uses ctx.tz routinely,
63
- // it isn't a "bridge" concept. Defaults to UTC.
64
- tz: createTzContext(),
65
- // Same reasoning as tz above — ctx.locale is always-present, not a
66
- // bridge method. Defaults to DEFAULT_LOCALE.
67
- locale: DEFAULT_LOCALE,
68
- };
69
- }
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-framework/engine`. Run `bun kumiko-upgrade --apply`
3
+ * to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export { bridgeStub } from "../engine/bridge-stub.js";
@@ -1,26 +1,10 @@
1
- export { resetBlindIndexKeyForTests } from "../crypto/blind-index.js";
2
- export { resetEventPiiCatalogForTests } from "../crypto/event-pii.js";
3
- export { resetPiiSubjectKmsForTests } from "../crypto/pii-field-encryption.js";
4
- export { resetEntityFieldEncryptionCacheForTests } from "../db/entity-field-encryption.js";
5
- export { rolesOf } from "./access-assertions.js";
6
- export { expectError, expectSuccess } from "./assertions.js";
7
- export { withBootValidatorFixture } from "./boot-validator-fixture.js";
8
- export { captureClosedConnectionError } from "./closed-connection-error.js";
9
- export { type ClearableTable, clearTables, resetTestTables } from "./db-cleanup.js";
10
- export { type E2EGeneratorOptions, type E2ETestSpec, type EditFillOp, generateE2ESpec, generateZodFixture, } from "./e2e-generator.js";
11
- export { expectErrorIncludes } from "./expect-error.js";
12
- export { describeFileProviderContract } from "./file-provider-contract.js";
13
- export { bridgeStub } from "./handler-context.js";
14
- export { getSetCookieRaw, getSetCookies, getSetCookieValue, type ParsedSetCookie, } from "./http-cookies.js";
15
- export { createLateBoundHolder, type LateBoundHolder } from "./late-bound.js";
16
- export { createManualClock, type ManualClock } from "./manual-clock.js";
17
- export { buildMultipartBody, patchFileInstanceofForBunTest } from "./multipart-helper.js";
18
- export { createMutableMasterKeyProvider, createTestEnvelopeCipher, createTestMasterKeyProvider, type MutableMasterKeyProvider, } from "./mutable-master-key-provider.js";
19
- export { createRecordingProvider, type RecordingProvider, } from "./observability-recorder.js";
20
- export { isRealProviderRun, REAL_PROVIDERS_ENV, requireRealProviders } from "./real-providers.js";
21
- export { deleteRows, seedRow, seedRows, updateRows } from "./seed.js";
22
- export { sharedItemEntity, sharedItemTable, sharedUserEntity, sharedUserTable, sharedWidgetEntity, sharedWidgetTable, } from "./shared-entities.js";
23
- export { sleep } from "./utils.js";
24
- export { waitFor } from "./wait-for.js";
25
- export { withoutAmbientTemporal } from "./without-ambient-temporal.js";
26
- export { zipWithEntries } from "./zip-fixture.js";
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/helpers`. Run `bun kumiko-upgrade --apply`
3
+ * to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export * from "@cosmicdrift/kumiko-testing/helpers";
6
+ /**
7
+ * @deprecated Moved to `@cosmicdrift/kumiko-framework/engine`. Run `bun kumiko-upgrade --apply`
8
+ * to rewrite imports. Removed in the first release after 2026-10-29.
9
+ */
10
+ export { bridgeStub } from "../engine/bridge-stub.js";
@@ -1,34 +1,10 @@
1
- // Test assertions and domain test fixtures. Production code (dev-server, bin/)
2
- // must import nothing from this subpath — the stack builders live in
3
- // `@cosmicdrift/kumiko-framework/stack`.
4
- // The four cache/injection resets stay in their own modules (they close over
5
- // module-private state) and are only re-exported here — they are out of /crypto
6
- // and /db as of #1631. A production call to resetPiiSubjectKmsForTests() silently
7
- // switches the PII layer off, and subject-annotated fields are written in
8
- // plaintext from then on: no error, no log.
9
- export { resetBlindIndexKeyForTests } from "../crypto/blind-index.js";
10
- export { resetEventPiiCatalogForTests } from "../crypto/event-pii.js";
11
- export { resetPiiSubjectKmsForTests } from "../crypto/pii-field-encryption.js";
12
- export { resetEntityFieldEncryptionCacheForTests } from "../db/entity-field-encryption.js";
13
- export { rolesOf } from "./access-assertions.js";
14
- export { expectError, expectSuccess } from "./assertions.js";
15
- export { withBootValidatorFixture } from "./boot-validator-fixture.js";
16
- export { captureClosedConnectionError } from "./closed-connection-error.js";
17
- export { clearTables, resetTestTables } from "./db-cleanup.js";
18
- export { generateE2ESpec, generateZodFixture, } from "./e2e-generator.js";
19
- export { expectErrorIncludes } from "./expect-error.js";
20
- export { describeFileProviderContract } from "./file-provider-contract.js";
21
- export { bridgeStub } from "./handler-context.js";
22
- export { getSetCookieRaw, getSetCookies, getSetCookieValue, } from "./http-cookies.js";
23
- export { createLateBoundHolder } from "./late-bound.js";
24
- export { createManualClock } from "./manual-clock.js";
25
- export { buildMultipartBody, patchFileInstanceofForBunTest } from "./multipart-helper.js";
26
- export { createMutableMasterKeyProvider, createTestEnvelopeCipher, createTestMasterKeyProvider, } from "./mutable-master-key-provider.js";
27
- export { createRecordingProvider, } from "./observability-recorder.js";
28
- export { isRealProviderRun, REAL_PROVIDERS_ENV, requireRealProviders } from "./real-providers.js";
29
- export { deleteRows, seedRow, seedRows, updateRows } from "./seed.js";
30
- export { sharedItemEntity, sharedItemTable, sharedUserEntity, sharedUserTable, sharedWidgetEntity, sharedWidgetTable, } from "./shared-entities.js";
31
- export { sleep } from "./utils.js";
32
- export { waitFor } from "./wait-for.js";
33
- export { withoutAmbientTemporal } from "./without-ambient-temporal.js";
34
- export { zipWithEntries } from "./zip-fixture.js";
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/helpers`. Run `bun kumiko-upgrade --apply`
3
+ * to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export * from "@cosmicdrift/kumiko-testing/helpers";
6
+ /**
7
+ * @deprecated Moved to `@cosmicdrift/kumiko-framework/engine`. Run `bun kumiko-upgrade --apply`
8
+ * to rewrite imports. Removed in the first release after 2026-10-29.
9
+ */
10
+ export { bridgeStub } from "../engine/bridge-stub.js";
@@ -1,3 +1,5 @@
1
- export declare const REAL_PROVIDERS_ENV = "KUMIKO_REAL_PROVIDERS";
2
- export declare function isRealProviderRun(env?: Readonly<Record<string, string | undefined>>): boolean;
3
- export declare function requireRealProviders(): void;
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/helpers/real-providers`. Run
3
+ * `bun kumiko-upgrade --apply` to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export * from "@cosmicdrift/kumiko-testing/helpers/real-providers";
@@ -1,21 +1,5 @@
1
- // Playwright configs load this module under plain Node via
2
- // @cosmicdrift/kumiko-testing/e2e, so it must stay import-free.
3
- export const REAL_PROVIDERS_ENV = "KUMIKO_REAL_PROVIDERS";
4
- const CI_FLAG = "CI";
5
- function isCi() {
6
- const ci = process.env[CI_FLAG];
7
- return ci !== undefined && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
8
- }
9
- // Apps and the e2e/bunfig templates gate real-provider runs on this same
10
- // check instead of re-reading KUMIKO_REAL_PROVIDERS themselves.
11
- export function isRealProviderRun(env = process.env) {
12
- return env[REAL_PROVIDERS_ENV] === "1";
13
- }
14
- export function requireRealProviders() {
15
- if (isCi()) {
16
- throw new Error(`Real-provider tests never run in CI (CI is set). Run them locally with ${REAL_PROVIDERS_ENV}=1 via the test:real / e2e:real script.`);
17
- }
18
- if (!isRealProviderRun()) {
19
- throw new Error(`Real-provider test needs ${REAL_PROVIDERS_ENV}=1 (an API key alone never enables it). Run it via the test:real / e2e:real script.`);
20
- }
21
- }
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/helpers/real-providers`. Run
3
+ * `bun kumiko-upgrade --apply` to rewrite imports. Removed in the first release after 2026-10-29.
4
+ */
5
+ export * from "@cosmicdrift/kumiko-testing/helpers/real-providers";