@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
@@ -2,7 +2,7 @@
2
2
  import { bigserial, index, instant, integer, jsonb, table as pgTable, sql, text, uniqueIndex, uuid, } from "../db/dialect.js";
3
3
  import { tableExists } from "../db/index.js";
4
4
  import { ensureIdempotencyKeyIndex, ensureMembershipPayloadTenantIndex, } from "../db/queries/event-store.js";
5
- import { unsafePushTables } from "../stack/index.js";
5
+ import { unsafePushTables } from "../db/table-push.js";
6
6
  import { createArchivedStreamsTable } from "./archive.js";
7
7
  import { createSnapshotsTable } from "./snapshot.js";
8
8
  // Event-store schema as a Drizzle table. The typed select/insert path handles
@@ -14,7 +14,7 @@
14
14
  // is a separate ops step — same stance as the upcaster dead-letter.
15
15
  import { bigint, index, jsonb, table as pgTable, text, timestamp, uuid } from "../db/dialect.js";
16
16
  import { tableExists } from "../db/schema-inspection.js";
17
- import { unsafePushTables } from "../stack/index.js";
17
+ import { unsafePushTables } from "../db/table-push.js";
18
18
  export const rebuildDeadLetterTable = pgTable("kumiko_rebuild_dead_letters", {
19
19
  // Surrogate PK — the same event can land here across multiple rebuild
20
20
  // attempts before the fix ships, without unique-violation noise.
@@ -47,7 +47,7 @@ export async function createRebuildDeadLetterTable(db) {
47
47
  // tx). Unqualified insert — during the rebuild the search_path points at the
48
48
  // shadow schema, and this table exists only in public, so it falls through.
49
49
  export async function recordRebuildDeadLetters(db, projectionName, skipped) {
50
- const { insertMany } = await import("../bun-db/query.js");
50
+ const { insertMany } = await import("../db/query.js");
51
51
  await insertMany(db, rebuildDeadLetterTable, skipped.map(({ event, error }) => ({
52
52
  projectionName,
53
53
  eventId: event.id,
@@ -61,7 +61,7 @@ export async function recordRebuildDeadLetters(db, projectionName, skipped) {
61
61
  }
62
62
  // Ops-side triage query, optionally scoped to one projection.
63
63
  export async function listRebuildDeadLetters(db, options = {}) {
64
- const { selectMany } = await import("../bun-db/query.js");
64
+ const { selectMany } = await import("../db/query.js");
65
65
  const limit = options.limit ?? 100;
66
66
  const where = options.projectionName !== undefined ? { projectionName: options.projectionName } : undefined;
67
67
  return selectMany(db, rebuildDeadLetterTable, where, {
@@ -4,7 +4,7 @@ import { index, instant, integer, jsonb, table as pgTable, primaryKey, sql, text
4
4
  import { ensureSnapshotVersionColumn, upsertSnapshot } from "../db/queries/event-store.js";
5
5
  import { selectMany } from "../db/query.js";
6
6
  import { tableExists } from "../db/schema-inspection.js";
7
- import { unsafePushTables } from "../stack/index.js";
7
+ import { unsafePushTables } from "../db/table-push.js";
8
8
  import { isStreamArchived } from "./archive.js";
9
9
  import { loadEventsAfterVersion } from "./event-store.js";
10
10
  // Marten-aligned snapshot store. A snapshot is a point-in-time materialised
@@ -13,7 +13,7 @@
13
13
  // separate CLI step — not implemented here, tracked as follow-up.
14
14
  import { bigint, index, integer, jsonb, table as pgTable, text, timestamp, uuid, } from "../db/dialect.js";
15
15
  import { tableExists } from "../db/schema-inspection.js";
16
- import { unsafePushTables } from "../stack/index.js";
16
+ import { unsafePushTables } from "../db/table-push.js";
17
17
  export const upcasterDeadLetterTable = pgTable("kumiko_upcaster_dead_letters", {
18
18
  // Surrogate PK. We don't reuse eventId so a single event can land
19
19
  // here multiple times (retry attempts across deploys before the fix
@@ -49,7 +49,7 @@ export async function createUpcasterDeadLetterTable(db) {
49
49
  // ops tooling uses it for correlate-and-replay flows.
50
50
  export async function recordUpcasterDeadLetter(db, args) {
51
51
  const message = args.error instanceof Error ? args.error.message : String(args.error);
52
- const { insertOne } = await import("../bun-db/query.js");
52
+ const { insertOne } = await import("../db/query.js");
53
53
  await insertOne(db, upcasterDeadLetterTable, {
54
54
  eventId: args.event.id,
55
55
  tenantId: args.event.tenantId,
@@ -65,7 +65,7 @@ export async function recordUpcasterDeadLetter(db, args) {
65
65
  // Ops-side query. Loads recent failures, optionally scoped by event-type
66
66
  // to triage a single broken migration without pulling the full table.
67
67
  export async function listDeadLetters(db, options = {}) {
68
- const { selectMany } = await import("../bun-db/query.js");
68
+ const { selectMany } = await import("../db/query.js");
69
69
  const limit = options.limit ?? 100;
70
70
  const where = options.eventType !== undefined ? { eventType: options.eventType } : undefined;
71
71
  const rows = await selectMany(db, upcasterDeadLetterTable, where, {
@@ -1,4 +1,4 @@
1
- import { fetchOne } from "../bun-db/query.js";
1
+ import { fetchOne } from "../db/query.js";
2
2
  import { FrameworkReasons, InternalError, UnprocessableError, writeFailure, } from "../errors/index.js";
3
3
  import { createFileRefRow, DEFAULT_MAX_UPLOAD_SIZE, storeFileAsFileRef, writeAcceptedFileBytes, } from "./create-file-ref.js";
4
4
  import { fileRefsTable } from "./file-ref-table.js";
@@ -2,8 +2,8 @@ import { Hono } from "hono";
2
2
  import { bodyLimit } from "hono/body-limit";
3
3
  import { getUser } from "../api/auth-middleware.js";
4
4
  import { requestContext } from "../api/request-context.js";
5
- import { selectMany } from "../bun-db/index.js";
6
5
  import { collectPiiSubjectFields, configuredPiiSubjectKms, decryptPiiFieldValues, isPiiCiphertext, PII_ERASED_SENTINEL, } from "../crypto/index.js";
6
+ import { selectMany } from "../db/query.js";
7
7
  import { createProjectingTenantDb } from "../db/tenant-db.js";
8
8
  import { createDerivativesContext, resolveFieldVariant, resolveRenderer, } from "../derivatives/index.js";
9
9
  import { SYSTEM_USER_ID } from "../engine/system-user.js";
@@ -16,10 +16,9 @@ export const SETTINGS_HUB_I18N = {
16
16
  "config.settings.tenant": { en: "Tenant" },
17
17
  "config.settings.user": { en: "Personal" },
18
18
  "config.settings.extensionSelectorHint": {
19
- en: "Choose a provider and save. Its settings appear below.",
19
+ en: "Choose a provider. Its settings appear below and are saved together with it.",
20
20
  },
21
21
  "config.settings.provider": { en: "Provider" },
22
- "config.settings.saveProvider": { en: "Save provider" },
23
22
  "config.settings.audience.system": {
24
23
  en: "Applies to every tenant unless a tenant sets its own value.",
25
24
  },
@@ -0,0 +1,12 @@
1
+ export { createDbConnection } from "../db/connection.js";
2
+ export { createDatabase, databaseExists, dropDatabaseIfExists, truncateTablesRestartIdentity, } from "../db/queries/test-stack.js";
3
+ export { type AnyDb, isClosedConnectionError } from "../db/query.js";
4
+ export { columnNamesOf } from "../db/schema-inspection.js";
5
+ export { schemaTableName } from "../db/table-introspection.js";
6
+ export { addMissingColumns, addMissingIndexes, tableToMeta, unsafeCreateEntityTable, unsafeEnsureEntityTable, unsafePushTables, } from "../db/table-push.js";
7
+ export { SEARCHABLE_FALSE_WHITELIST } from "../engine/boot-validator/entity-list-screens.js";
8
+ export { validateNavBootSubset } from "../engine/boot-validator/index.js";
9
+ export { validateOwnershipBoot } from "../engine/boot-validator/ownership.js";
10
+ export { featureHasI18nSurface, requiredKeysFromFeature } from "../i18n/required-surface-keys.js";
11
+ export type { Lifecycle } from "../lifecycle/index.js";
12
+ export { createRateLimitResolver } from "../rate-limit/index.js";
@@ -0,0 +1,12 @@
1
+ // @runtime dev
2
+ export { createDbConnection } from "../db/connection.js";
3
+ export { createDatabase, databaseExists, dropDatabaseIfExists, truncateTablesRestartIdentity, } from "../db/queries/test-stack.js";
4
+ export { isClosedConnectionError } from "../db/query.js";
5
+ export { columnNamesOf } from "../db/schema-inspection.js";
6
+ export { schemaTableName } from "../db/table-introspection.js";
7
+ export { addMissingColumns, addMissingIndexes, tableToMeta, unsafeCreateEntityTable, unsafeEnsureEntityTable, unsafePushTables, } from "../db/table-push.js";
8
+ export { SEARCHABLE_FALSE_WHITELIST } from "../engine/boot-validator/entity-list-screens.js";
9
+ export { validateNavBootSubset } from "../engine/boot-validator/index.js";
10
+ export { validateOwnershipBoot } from "../engine/boot-validator/ownership.js";
11
+ export { featureHasI18nSurface, requiredKeysFromFeature } from "../i18n/required-surface-keys.js";
12
+ export { createRateLimitResolver } from "../rate-limit/index.js";
@@ -0,0 +1,4 @@
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";
@@ -0,0 +1,5 @@
1
+ // @runtime test
2
+ export { resetBlindIndexKeyForTests } from "../crypto/blind-index.js";
3
+ export { resetEventPiiCatalogForTests } from "../crypto/event-pii.js";
4
+ export { resetPiiSubjectKmsForTests } from "../crypto/pii-field-encryption.js";
5
+ export { resetEntityFieldEncryptionCacheForTests } from "../db/entity-field-encryption.js";
@@ -6,6 +6,7 @@ import { Redis } from "ioredis";
6
6
  import { requestContext } from "../api/request-context.js";
7
7
  import { createTenantDb, createUncheckedSystemDb } from "../db/tenant-db.js";
8
8
  import { createDerivativesContext } from "../derivatives/derivatives-context.js";
9
+ import { bridgeStub } from "../engine/bridge-stub.js";
9
10
  import { qnScope } from "../engine/qualified-name.js";
10
11
  import { createSystemUser } from "../engine/system-user.js";
11
12
  import { SYSTEM_TENANT_ID, } from "../engine/types/index.js";
@@ -19,7 +20,6 @@ import { RedisKeys } from "../pipeline/redis-keys.js";
19
20
  import { createGatedIdentitySwitch } from "../pipeline/system-identity-switch.js";
20
21
  import { TENANT_TIMEZONE_CONFIG_KEY } from "../pipeline/tenant-timezone-config-key.js";
21
22
  import { buildPersonalDataGate, isPersonalDataGated, parseWriteOrigin, } from "../pipeline/write-origin.js";
22
- import { bridgeStub } from "../testing/handler-context.js";
23
23
  import { createTzContext, isValidIanaTimeZone } from "../time/index.js";
24
24
  import { cronRepeatOptions } from "./cron-repeat-options.js";
25
25
  import { resolveMaxPerTenant } from "./max-per-tenant.js";
@@ -429,7 +429,7 @@ export function createJobRunner(options) {
429
429
  return undefined;
430
430
  return async () => {
431
431
  const systemUser = createSystemUser(SYSTEM_TENANT_ID);
432
- const systemModeDb = createTenantDb(db, SYSTEM_TENANT_ID, "system");
432
+ const systemModeDb = createTenantDb({ db, tenantId: SYSTEM_TENANT_ID, mode: "system" });
433
433
  const reportEscapeHatch = createEscapeHatchReporter({
434
434
  handler: ACTIVE_TENANT_IDS_QUERY_NAME,
435
435
  tenantId: SYSTEM_TENANT_ID,
@@ -922,9 +922,14 @@ export function createJobRunner(options) {
922
922
  // unchecked cross-tenant escape hatch for such a job. Gated like jobDb so
923
923
  // ctx.systemDb, built from it, is gated too.
924
924
  const tenantScopedDb = configDb
925
- ? createTenantDb(configDb, tenantId, "system", undefined, undefined, undefined, {
926
- projectionRegistry: registry,
927
- ...(jobPersonalDataGate && { personalDataGate: jobPersonalDataGate }),
925
+ ? createTenantDb({
926
+ db: configDb,
927
+ tenantId,
928
+ mode: "system",
929
+ grants: {
930
+ projectionRegistry: registry,
931
+ ...(jobPersonalDataGate && { personalDataGate: jobPersonalDataGate }),
932
+ },
928
933
  })
929
934
  : undefined;
930
935
  const isSystemJob = registry.isJobSystemScoped(jobName);
@@ -945,12 +950,19 @@ export function createJobRunner(options) {
945
950
  ? createUncheckedSystemDb(tenantScopedDb, undefined, reportEscapeHatch)
946
951
  : undefined;
947
952
  const jobDb = configDb
948
- ? createTenantDb(configDb, tenantId, "tenant", context.tracer, context.meter, undefined, {
949
- unsafeRaw: escapeHatchFor(jobDef.escapeHatch, "unsafeRaw"),
950
- globalWrites: escapeHatchFor(jobDef.escapeHatch, "globalWrites"),
951
- report: reportEscapeHatch,
952
- projectionRegistry: registry,
953
- ...(jobPersonalDataGate && { personalDataGate: jobPersonalDataGate }),
953
+ ? createTenantDb({
954
+ db: configDb,
955
+ tenantId,
956
+ mode: "tenant",
957
+ tracer: context.tracer,
958
+ meter: context.meter,
959
+ grants: {
960
+ unsafeRaw: escapeHatchFor(jobDef.escapeHatch, "unsafeRaw"),
961
+ globalWrites: escapeHatchFor(jobDef.escapeHatch, "globalWrites"),
962
+ report: reportEscapeHatch,
963
+ projectionRegistry: registry,
964
+ ...(jobPersonalDataGate && { personalDataGate: jobPersonalDataGate }),
965
+ },
954
966
  })
955
967
  : undefined;
956
968
  const crossTenantReadsReason = escapeHatchFor(jobDef.escapeHatch, "unsafeRaw")?.reason;
@@ -14,9 +14,9 @@ import { instant, table as pgTable, sql, text } from "../db/dialect.js";
14
14
  import { deleteMany, selectMany, upsertOnConflict } from "../db/query.js";
15
15
  import { readRebuildMarker } from "../db/rebuild-marker.js";
16
16
  import { tableExists } from "../db/schema-inspection.js";
17
+ import { unsafePushTables } from "../db/table-push.js";
17
18
  import { createFallbackLogger } from "../logging/utils.js";
18
19
  import { rebuildMultiStreamProjection, rebuildProjection, } from "../pipeline/index.js";
19
- import { unsafePushTables } from "../stack/index.js";
20
20
  import { buildProjectionTableIndex } from "./projection-table-index.js";
21
21
  export const pendingRebuildsTable = pgTable("kumiko_pending_rebuilds", {
22
22
  tableName: text("table_name").primaryKey(),
@@ -18,8 +18,8 @@ import { alterTableAddColumn } from "../db/queries/ddl.js";
18
18
  import { persistPiiBackfillState, readPiiBackfillState, } from "../db/queries/pii-backfill-state.js";
19
19
  import { upsertOnConflict } from "../db/query.js";
20
20
  import { tableExists } from "../db/schema-inspection.js";
21
+ import { unsafePushTables } from "../db/table-push.js";
21
22
  import { createFallbackLogger } from "../logging/utils.js";
22
- import { unsafePushTables } from "../stack/index.js";
23
23
  import { clearPendingRebuilds, createPendingRebuildsTable, listPendingRebuildRows, pendingRebuildsTable, rebuildProjectionOrMultiStream, } from "./pending-rebuilds.js";
24
24
  import { buildProjectionTableIndex } from "./projection-table-index.js";
25
25
  const PII_EVENT_BACKFILL_LOCK_NAMESPACE = 0x70_69_69_62; // 'piib'
@@ -71,7 +71,7 @@ export function piiAnnotationEntries(registry) {
71
71
  // Bump when the backfill gains a transform stage: completed states keyed by the
72
72
  // old fingerprint must run one more full pass. Kept out of piiAnnotationEntries,
73
73
  // whose emptiness gates no_pii.
74
- const PII_BACKFILL_TRANSFORM_VERSION = "owner-rekey-v1";
74
+ const PII_BACKFILL_TRANSFORM_VERSION = "owner-rekey-v2";
75
75
  export function piiAnnotationFingerprint(registry) {
76
76
  return createHash("sha256")
77
77
  .update([...piiAnnotationEntries(registry), PII_BACKFILL_TRANSFORM_VERSION].join("\n"))
@@ -289,16 +289,24 @@ readOnlyForTenant = false) {
289
289
  });
290
290
  const identitySwitchGrantReason = identitySwitchEscapeHatch?.reason ??
291
291
  (isSystem ? `r.systemScope() feature "${featureName ?? "unknown"}"` : undefined);
292
- const buildTenantScopedDb = (source, signal, foreignTenantId, denyWrites = false) => createTenantDb(source, foreignTenantId ?? user.tenantId, isSystem && foreignTenantId === undefined ? "system" : "tenant", context.tracer, context.meter, signal, {
293
- ...(foreignTenantId === undefined && {
294
- globalWrites: escapeHatchFor(writeEscapeHatch, "globalWrites"),
295
- unsafeRaw: escapeHatchFor(handlerEscapeHatch, "unsafeRaw"),
296
- }),
297
- report: reportEscapeHatch,
298
- memberReadOnly: isMemberResolutionPrincipal(user),
299
- personalDataGate: buildPersonalDataGate(registry, origin),
300
- projectionRegistry: registry,
301
- ...(denyWrites && { writesDeniedReason: "without an open transaction" }),
292
+ const buildTenantScopedDb = (source, signal, foreignTenantId, denyWrites = false) => createTenantDb({
293
+ db: source,
294
+ tenantId: foreignTenantId ?? user.tenantId,
295
+ mode: isSystem && foreignTenantId === undefined ? "system" : "tenant",
296
+ tracer: context.tracer,
297
+ meter: context.meter,
298
+ signal,
299
+ grants: {
300
+ ...(foreignTenantId === undefined && {
301
+ globalWrites: escapeHatchFor(writeEscapeHatch, "globalWrites"),
302
+ unsafeRaw: escapeHatchFor(handlerEscapeHatch, "unsafeRaw"),
303
+ }),
304
+ report: reportEscapeHatch,
305
+ memberReadOnly: isMemberResolutionPrincipal(user),
306
+ personalDataGate: buildPersonalDataGate(registry, origin),
307
+ projectionRegistry: registry,
308
+ ...(denyWrites && { writesDeniedReason: "without an open transaction" }),
309
+ },
302
310
  });
303
311
  // Propagate the request's AbortSignal so every TenantDb query throws when
304
312
  // the client has disconnected — handlers with many sequential queries skip
@@ -410,7 +418,14 @@ readOnlyForTenant = false) {
410
418
  const targetSystemUser = createSystemUser(targetTenantId);
411
419
  return configFactory({
412
420
  user: { id: targetSystemUser.id, tenantId: targetTenantId },
413
- db: createTenantDb(dbSource, targetTenantId, "tenant", context.tracer, context.meter, undefined, { projectionRegistry: registry }),
421
+ db: createTenantDb({
422
+ db: dbSource,
423
+ tenantId: targetTenantId,
424
+ mode: "tenant",
425
+ tracer: context.tracer,
426
+ meter: context.meter,
427
+ grants: { projectionRegistry: registry },
428
+ }),
414
429
  secrets: context.secrets,
415
430
  });
416
431
  }
@@ -886,9 +901,7 @@ readOnlyForTenant = false) {
886
901
  // Finger an typo-resistenten ctx.user-Patterns. Identisch zum
887
902
  // event.user-Wert; Identity-Switches nutzen weiterhin queryAs/writeAs.
888
903
  user,
889
- _userId: user.id,
890
904
  _tenantId: user.tenantId,
891
- _handlerType: type,
892
905
  ...(hasLiveCommitSink && { scheduleAfterCommit }),
893
906
  ...(includeDeleted && { includeDeleted: true }),
894
907
  ...bridge,
@@ -1219,16 +1232,23 @@ function buildAuthClaimsContext(ctx, user) {
1219
1232
  message: "dispatcher.resolveAuthClaims requires a database connection — none is configured.",
1220
1233
  });
1221
1234
  }
1222
- const db = createTenantDb(dbSource, user.tenantId, "tenant", context.tracer, context.meter, undefined, {
1223
- report: createEscapeHatchReporter({
1224
- handler: "r.authClaims hook",
1225
- tenantId: user.tenantId,
1226
- actor: user.id,
1227
- sink: context._escapeHatchAuditSink,
1228
- log: context.log ?? createFallbackLogger("dispatcher"),
1229
- window: ctx.escapeHatchReportWindow,
1230
- }),
1231
- projectionRegistry: ctx.registry,
1235
+ const db = createTenantDb({
1236
+ db: dbSource,
1237
+ tenantId: user.tenantId,
1238
+ mode: "tenant",
1239
+ tracer: context.tracer,
1240
+ meter: context.meter,
1241
+ grants: {
1242
+ report: createEscapeHatchReporter({
1243
+ handler: "r.authClaims hook",
1244
+ tenantId: user.tenantId,
1245
+ actor: user.id,
1246
+ sink: context._escapeHatchAuditSink,
1247
+ log: context.log ?? createFallbackLogger("dispatcher"),
1248
+ window: ctx.escapeHatchReportWindow,
1249
+ }),
1250
+ projectionRegistry: ctx.registry,
1251
+ },
1232
1252
  });
1233
1253
  const configAccessor = context._configAccessorFactory
1234
1254
  ? context._configAccessorFactory({
@@ -163,7 +163,7 @@ async function isParentRowHiddenFromCaller(ctx, parentEntityName, parentId, user
163
163
  const source = resolveDbSource(ctx, tx);
164
164
  if (!table || !source)
165
165
  return true;
166
- const callerDb = createTenantDb(source, user.tenantId, "tenant");
166
+ const callerDb = createTenantDb({ db: source, tenantId: user.tenantId, mode: "tenant" });
167
167
  const visibleRow = await callerDb.fetchOne(table, { id: parentId });
168
168
  if (visibleRow === undefined)
169
169
  return true;
@@ -181,7 +181,11 @@ async function completeParentRow(ctx, parentEntityName, parentEntity, parentRow,
181
181
  const source = resolveDbSource(ctx, tx);
182
182
  if (!table || !source || typeof parentRow["id"] !== "string")
183
183
  return parentRow;
184
- const stored = await createTenantDb(source, user.tenantId, "tenant").fetchOne(table, {
184
+ const stored = await createTenantDb({
185
+ db: source,
186
+ tenantId: user.tenantId,
187
+ mode: "tenant",
188
+ }).fetchOne(table, {
185
189
  id: parentRow["id"],
186
190
  });
187
191
  return stored ? { ...stored, ...parentRow } : parentRow;
@@ -143,10 +143,14 @@ export function resolveType(type) {
143
143
  // A custom write losing an append race (ctx.appendEvent, stream.append) is a
144
144
  // retryable 409 like the CRUD executor's, not an internal error. Mapped here
145
145
  // because errors/ is client code and must not import the event store.
146
- // currentVersion -1 is the executor's "not looked up" sentinel.
146
+ // currentVersion -1 is the "not looked up" sentinel for a bare 23505 fallback.
147
147
  export function wrapToKumiko(e) {
148
148
  if (e instanceof EventStoreVersionConflictError) {
149
- return new VersionConflictError({ entityId: e.aggregateId, expectedVersion: e.expectedVersion, currentVersion: -1 }, { cause: e });
149
+ return new VersionConflictError({
150
+ entityId: e.aggregateId,
151
+ expectedVersion: e.expectedVersion,
152
+ currentVersion: e.currentVersion ?? -1,
153
+ }, { cause: e });
150
154
  }
151
155
  return toKumikoError(e);
152
156
  }
@@ -1,7 +1,7 @@
1
1
  import { bigint, index, instant, integer, jsonb, table as pgTable, primaryKey, sql, text, } from "../db/dialect.js";
2
2
  import { alterTableAddColumn } from "../db/queries/ddl.js";
3
3
  import { tableExists } from "../db/schema-inspection.js";
4
- import { unsafePushTables } from "../stack/index.js";
4
+ import { unsafePushTables } from "../db/table-push.js";
5
5
  // Reserved sentinel used in the instance_id column for consumers whose
6
6
  // delivery is "shared" — i.e. one cursor across all dispatcher instances
7
7
  // (the default, pre-Welle-2.7 behaviour). Per-instance consumers store
@@ -2,6 +2,7 @@
2
2
  import type { DbConnection } from "../db/connection.js";
3
3
  import type { Registry } from "../engine/types/index.js";
4
4
  import type { Meter } from "../observability/types/metric.js";
5
+ export declare const REBUILD_BATCH_SIZE = 1000;
5
6
  export type RebuildResult = {
6
7
  readonly projection: string;
7
8
  readonly eventsProcessed: number;
@@ -8,7 +8,7 @@ import { projectionStateTable } from "./projection-state.js";
8
8
  // Events replayed per catch-up batch. Each batch is a fresh READ COMMITTED
9
9
  // SELECT, so a batch shorter than this means the currently-committed tail is
10
10
  // drained.
11
- const REBUILD_BATCH_SIZE = 1000;
11
+ export const REBUILD_BATCH_SIZE = 1000;
12
12
  // Cap on UNLOCKED catch-up batches before forcing the cutover fence. Bounds the
13
13
  // lock-free phase under sustained writes that never momentarily quiesce; the
14
14
  // fenced final drain then always terminates (no new event can commit once the
@@ -1,6 +1,6 @@
1
1
  import { bigint, index, instant, table as pgTable, sql, text } from "../db/dialect.js";
2
2
  import { tableExists } from "../db/schema-inspection.js";
3
- import { unsafePushTables } from "../stack/index.js";
3
+ import { unsafePushTables } from "../db/table-push.js";
4
4
  // Framework-level state for every registered projection. One row per qualified
5
5
  // projection name. Written by the rebuild machinery; read by the CLI + any
6
6
  // status dashboard. Lives alongside the events table as framework infra —
@@ -206,7 +206,7 @@ async function readProjectionRowForSearch(ctx, registry, entityName, entity, ten
206
206
  if (entity.softDelete && table["isDeleted"]) {
207
207
  where["isDeleted"] = false;
208
208
  }
209
- const row = await createTenantDb(baseDb, tenantId).fetchOne(table, where);
209
+ const row = await createTenantDb({ db: baseDb, tenantId }).fetchOne(table, where);
210
210
  if (!row)
211
211
  return undefined;
212
212
  const state = rehydrateCompoundTypes(row, entity);
@@ -303,7 +303,7 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
303
303
  out.err(` ${migrationsDir} fehlt — erst schema generate <name>.`);
304
304
  return 1;
305
305
  }
306
- const { db, close } = createDbConnection(dbUrl);
306
+ const { db, close } = createDbConnection({ url: dbUrl });
307
307
  let wiring;
308
308
  try {
309
309
  const result = await runMigrationsFromDir(db, migrationsDir);
@@ -386,7 +386,7 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
386
386
  out.err(` ${migrationsDir} fehlt — erst schema generate <name>.`);
387
387
  return 1;
388
388
  }
389
- const { db, close } = createDbConnection(dbUrl);
389
+ const { db, close } = createDbConnection({ url: dbUrl });
390
390
  try {
391
391
  const result = await baselineMigrations(db, loadMigrationsFromDir(migrationsDir));
392
392
  out.log("");
@@ -418,7 +418,7 @@ export async function runSchemaCli(argv, appCwd, out, options = {}) {
418
418
  return 0;
419
419
  }
420
420
  const local = loadMigrationsFromDir(migrationsDir);
421
- const { db, close } = createDbConnection(dbUrl);
421
+ const { db, close } = createDbConnection({ url: dbUrl });
422
422
  try {
423
423
  // Frische DB ohne je gelaufenes `kumiko schema apply` → tracking-table
424
424
  // fehlt = "nichts applied". Connection-/Permission-Fehler dagegen
@@ -9,7 +9,7 @@
9
9
  // that still carry the subject key in encrypted columns.
10
10
  import { quoteIdent, subjectCiphertextLikePattern } from "../crypto/ciphertext-pattern.js";
11
11
  import { isSelfPiiField } from "../crypto/is-self-pii-field.js";
12
- import { collectSearchableSubjectFields } from "../crypto/subject-resolver.js";
12
+ import { collectSearchableSubjectFields, isRecordKeyedField } from "../crypto/subject-resolver.js";
13
13
  import { resolveTableName } from "../db/entity-table-meta.js";
14
14
  import { executeRawQueryRead } from "../db/queries/raw-sql.js";
15
15
  import { columnNamesOf, tableExists } from "../db/schema-inspection.js";
@@ -63,20 +63,30 @@ function tenantOwnershipPredicates(entity, searchableFields, tenantId, nextParam
63
63
  return finalizePredicateParts(parts, params);
64
64
  }
65
65
  function recordOwnershipPredicates(entity, searchableFields, entityName, subjectEntity, subjectId, nextParam) {
66
- const parts = [];
67
- const params = [];
68
- let recordIdN;
66
+ const ownerlessColumns = new Set();
67
+ let hasRecordOwnedField = false;
69
68
  for (const fieldName of searchableFields) {
70
69
  const field = entity.fields[fieldName];
71
- if (!field)
70
+ if (!field || entityName !== subjectEntity || !isRecordKeyedField(field))
72
71
  continue;
73
- const isMatchingRecordField = entityName === subjectEntity && "recordOwned" in field && field.recordOwned === true;
74
- if (isMatchingRecordField && recordIdN === undefined) {
75
- recordIdN = nextParam();
76
- params.push(subjectId);
77
- parts.push(`${quoteIdent("id")} = $${recordIdN}`);
72
+ if ("recordOwned" in field && field.recordOwned === true) {
73
+ hasRecordOwnedField = true;
74
+ }
75
+ else if ("userOwned" in field && field.userOwned !== undefined) {
76
+ ownerlessColumns.add(toSnakeCase(field.userOwned.ownerField));
78
77
  }
79
78
  }
79
+ if (!hasRecordOwnedField && ownerlessColumns.size === 0)
80
+ return null;
81
+ const idN = nextParam();
82
+ const parts = [];
83
+ if (hasRecordOwnedField)
84
+ parts.push(`${quoteIdent("id")} = $${idN}`);
85
+ // whenAbsent: "self" keys a row on itself only while its owner is empty
86
+ for (const col of ownerlessColumns) {
87
+ parts.push(`(${quoteIdent("id")} = $${idN} AND (${quoteIdent(col)} IS NULL OR ${quoteIdent(col)}::text = ''))`);
88
+ }
89
+ const params = [subjectId];
80
90
  return finalizePredicateParts(parts, params);
81
91
  }
82
92
  /** Build OR predicates for rows owned by `subject` (id / ownerField / tenant_id). */
@@ -1,2 +1,3 @@
1
1
  export { type EventStoreSeedExisting, type RunEventStoreSeedOptions, runEventStoreSeed, } from "./entity-seed.js";
2
+ export { createTestUser, TestUsers, testTenantId, testUserId } from "./seed-users.js";
2
3
  export { DEFAULT_SEED_IF_EXISTS, type SeedIfExists } from "./types.js";
@@ -1,2 +1,3 @@
1
1
  export { runEventStoreSeed, } from "./entity-seed.js";
2
+ export { createTestUser, TestUsers, testTenantId, testUserId } from "./seed-users.js";
2
3
  export { DEFAULT_SEED_IF_EXISTS } from "./types.js";
@@ -1,9 +1,10 @@
1
- export { type CreateTestDbOptions, createTestDb, type TestDb, } from "./db.js";
2
- export { drainEventConsumers } from "./drain-event-consumers.js";
3
- export { createEventCollector, type EventCollector } from "./event-collector.js";
4
- export { pushEntityProjectionTables } from "./push-entity-projection-tables.js";
5
- export { createTestRedis, type TestRedis } from "./redis.js";
6
- export { createRequestHelper, type RequestHelper } from "./request-helper.js";
7
- export { resetEventStore, unsafeCreateEntityTable, unsafeEnsureEntityTable, unsafePushTables, } from "./table-helpers.js";
8
- export { setupTestStack, type TestStack, type TestStackOptions } from "./test-stack.js";
9
- export { createTestUser, TestUsers, testTenantId, testUserId, } from "./test-users.js";
1
+ /**
2
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/stack`. 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/stack";
6
+ /**
7
+ * @deprecated Moved to `@cosmicdrift/kumiko-framework/seeding`. Run `bun kumiko-upgrade --apply`
8
+ * to rewrite imports. Removed in the first release after 2026-10-29.
9
+ */
10
+ export { createTestUser, TestUsers, testTenantId, testUserId } from "../seeding/seed-users.js";
@@ -1,19 +1,11 @@
1
- // Runtime-safe Stack-Builder. Was hier liegt, wird vom dev-server zum Hochfahren
2
- // einer kompletten Kumiko-Instanz genutzt — DB, Redis, Hono-App, Dispatcher,
3
- // SSE-Broker. Die Files heißen historisch `test*` (createTestDb,
4
- // setupTestStack, TestUsers, …), bedienen aber heute Dev- UND Test-Code: das
5
- // ist genau derselbe Hochfahr-Pfad, nur einmal mit ephemeral-DB (test) und
6
- // einmal mit persistent-DB (dev).
7
- //
8
- // Wichtig: dieses Modul darf KEINE vitest-Imports enthalten und keine
9
- // Vitest-only Helper transitiv ziehen — sonst crasht jedes Tooling, das den
10
- // dev-server unter Node lädt (drizzle-kit, build-scripts).
11
- export { createTestDb, } from "./db.js";
12
- export { drainEventConsumers } from "./drain-event-consumers.js";
13
- export { createEventCollector } from "./event-collector.js";
14
- export { pushEntityProjectionTables } from "./push-entity-projection-tables.js";
15
- export { createTestRedis } from "./redis.js";
16
- export { createRequestHelper } from "./request-helper.js";
17
- export { resetEventStore, unsafeCreateEntityTable, unsafeEnsureEntityTable, unsafePushTables, } from "./table-helpers.js";
18
- export { setupTestStack } from "./test-stack.js";
19
- export { createTestUser, TestUsers, testTenantId, testUserId, } from "./test-users.js";
1
+ // @runtime test
2
+ /**
3
+ * @deprecated Moved to `@cosmicdrift/kumiko-testing/stack`. Run `bun kumiko-upgrade --apply`
4
+ * to rewrite imports. Removed in the first release after 2026-10-29.
5
+ */
6
+ export * from "@cosmicdrift/kumiko-testing/stack";
7
+ /**
8
+ * @deprecated Moved to `@cosmicdrift/kumiko-framework/seeding`. Run `bun kumiko-upgrade --apply`
9
+ * to rewrite imports. Removed in the first release after 2026-10-29.
10
+ */
11
+ export { createTestUser, TestUsers, testTenantId, testUserId } from "../seeding/seed-users.js";