@cosmicdrift/kumiko-bundled-features 0.14.0 → 0.16.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 (269) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/env-schemas.test.ts +1 -1
  3. package/src/__tests__/es-ops-e2e.integration.ts +10 -9
  4. package/src/audit/__tests__/audit.integration.ts +3 -3
  5. package/src/audit/handlers/list.query.ts +39 -51
  6. package/src/auth-email-password/__tests__/account-lockout-no-redis.integration.ts +4 -3
  7. package/src/auth-email-password/__tests__/account-lockout.integration.ts +4 -3
  8. package/src/auth-email-password/__tests__/auth-claims.integration.ts +5 -4
  9. package/src/auth-email-password/__tests__/auth.integration.ts +4 -3
  10. package/src/auth-email-password/__tests__/confirm-token-flow.test.ts +1 -1
  11. package/src/auth-email-password/__tests__/email-templates.test.ts +1 -1
  12. package/src/auth-email-password/__tests__/email-verification.integration.ts +7 -10
  13. package/src/auth-email-password/__tests__/identity-v3-hash.test.ts +1 -1
  14. package/src/auth-email-password/__tests__/identity-v3-login.integration.ts +4 -3
  15. package/src/auth-email-password/__tests__/invite-flow.integration.ts +16 -43
  16. package/src/auth-email-password/__tests__/multi-roles.integration.ts +6 -9
  17. package/src/auth-email-password/__tests__/password-reset.integration.ts +8 -7
  18. package/src/auth-email-password/__tests__/public-routes-rate-limit.integration.ts +4 -3
  19. package/src/auth-email-password/__tests__/seed-admin.integration.ts +19 -32
  20. package/src/auth-email-password/__tests__/session-callbacks.integration.ts +6 -5
  21. package/src/auth-email-password/__tests__/session-strict-mode.integration.ts +1 -1
  22. package/src/auth-email-password/__tests__/signed-token.test.ts +1 -1
  23. package/src/auth-email-password/__tests__/signup-flow.integration.ts +11 -15
  24. package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +26 -26
  25. package/src/auth-email-password/handlers/invite-accept.write.ts +24 -21
  26. package/src/auth-email-password/handlers/invite-create.write.ts +3 -8
  27. package/src/auth-email-password/handlers/invite-signup-complete.write.ts +20 -17
  28. package/src/auth-email-password/handlers/signup-confirm.write.ts +3 -7
  29. package/src/auth-email-password/seeding.ts +1 -1
  30. package/src/auth-email-password/web/__tests__/auth-gate.test.tsx +1 -2
  31. package/src/auth-email-password/web/__tests__/forgot-password-screen.test.tsx +10 -19
  32. package/src/auth-email-password/web/__tests__/login-screen.test.tsx +12 -18
  33. package/src/auth-email-password/web/__tests__/reset-password-screen.test.tsx +12 -17
  34. package/src/auth-email-password/web/__tests__/session-roles.test.ts +1 -1
  35. package/src/auth-email-password/web/__tests__/tenant-switcher.test.tsx +1 -8
  36. package/src/auth-email-password/web/__tests__/test-utils.tsx +4 -8
  37. package/src/auth-email-password/web/__tests__/user-menu.test.tsx +2 -8
  38. package/src/auth-email-password/web/__tests__/verify-email-screen.test.tsx +10 -15
  39. package/src/billing-foundation/__tests__/billing-foundation.integration.ts +1 -1
  40. package/src/billing-foundation/__tests__/feature.test.ts +1 -1
  41. package/src/billing-foundation/__tests__/webhook-handler.test.ts +6 -5
  42. package/src/billing-foundation/db/queries/subscription-projection.ts +15 -0
  43. package/src/billing-foundation/get-subscription-for-tenant.ts +2 -6
  44. package/src/billing-foundation/handlers/create-portal-session.write.ts +2 -2
  45. package/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -1
  46. package/src/billing-foundation/projection.ts +32 -13
  47. package/src/cap-counter/__tests__/cap-counter.integration.ts +1 -1
  48. package/src/cap-counter/__tests__/enforce-cap.test.ts +37 -32
  49. package/src/cap-counter/__tests__/with-cap-enforcement.integration.ts +1 -1
  50. package/src/cap-counter/enforce-cap.ts +14 -20
  51. package/src/cap-counter/handlers/get-counter.query.ts +7 -13
  52. package/src/cap-counter/handlers/increment.write.ts +2 -2
  53. package/src/cap-counter/handlers/mark-soft-warned.write.ts +2 -2
  54. package/src/channel-in-app/handlers/inbox.query.ts +7 -13
  55. package/src/channel-in-app/handlers/mark-all-read.write.ts +7 -9
  56. package/src/channel-in-app/handlers/mark-read.write.ts +8 -14
  57. package/src/channel-in-app/handlers/unread-count.query.ts +10 -9
  58. package/src/channel-in-app/in-app-channel.ts +10 -12
  59. package/src/channel-in-app/tables.ts +1 -1
  60. package/src/compliance-profiles/__tests__/compliance-profiles.integration.ts +1 -1
  61. package/src/compliance-profiles/__tests__/seeding.integration.ts +1 -1
  62. package/src/compliance-profiles/_internal/parse-override.ts +19 -0
  63. package/src/compliance-profiles/handlers/for-tenant.query.ts +10 -32
  64. package/src/compliance-profiles/handlers/needs-profile.query.ts +4 -7
  65. package/src/compliance-profiles/handlers/set-profile.write.ts +5 -7
  66. package/src/compliance-profiles/resolve-for-tenant.ts +11 -27
  67. package/src/compliance-profiles/schema/profile-selection.ts +2 -2
  68. package/src/compliance-profiles/seeding.ts +4 -7
  69. package/src/config/__tests__/app-overrides.test.ts +1 -1
  70. package/src/config/__tests__/cascade.integration.ts +1 -1
  71. package/src/config/__tests__/config.integration.ts +8 -27
  72. package/src/config/db/queries/resolver.ts +47 -0
  73. package/src/config/handlers/__tests__/prepare-config-write.test.ts +1 -1
  74. package/src/config/resolver.ts +14 -62
  75. package/src/config/table.ts +4 -4
  76. package/src/config/write-helpers.ts +7 -11
  77. package/src/custom-fields/__tests__/audit-integration.integration.ts +6 -6
  78. package/src/custom-fields/__tests__/custom-fields.integration.ts +7 -7
  79. package/src/custom-fields/__tests__/feature.test.ts +1 -1
  80. package/src/custom-fields/__tests__/field-access.integration.ts +6 -6
  81. package/src/custom-fields/__tests__/quota.integration.ts +6 -6
  82. package/src/custom-fields/__tests__/retention.integration.ts +12 -10
  83. package/src/custom-fields/__tests__/user-data-rights.integration.ts +27 -17
  84. package/src/custom-fields/__tests__/wire-for-entity.test.ts +5 -5
  85. package/src/custom-fields/db/queries/field-access.ts +16 -0
  86. package/src/custom-fields/db/queries/projection.ts +43 -0
  87. package/src/custom-fields/db/queries/quota.ts +14 -0
  88. package/src/custom-fields/db/queries/retention.ts +39 -0
  89. package/src/custom-fields/db/queries/user-data-rights.ts +54 -0
  90. package/src/custom-fields/lib/field-access.ts +2 -41
  91. package/src/custom-fields/lib/quota.ts +2 -25
  92. package/src/custom-fields/run-retention.ts +19 -21
  93. package/src/custom-fields/wire-for-entity.ts +30 -23
  94. package/src/custom-fields/wire-user-data-rights.ts +33 -85
  95. package/src/data-retention/__tests__/data-retention.integration.ts +1 -1
  96. package/src/data-retention/__tests__/keep-for.test.ts +1 -1
  97. package/src/data-retention/__tests__/override-schema.test.ts +1 -1
  98. package/src/data-retention/__tests__/policy-for.integration.ts +1 -1
  99. package/src/data-retention/__tests__/resolver.test.ts +1 -1
  100. package/src/data-retention/handlers/policy-for.query.ts +5 -8
  101. package/src/data-retention/resolve-for-tenant.ts +6 -8
  102. package/src/data-retention/schema/tenant-retention-override.ts +2 -2
  103. package/src/delivery/__tests__/delivery-events.integration.ts +8 -21
  104. package/src/delivery/__tests__/delivery.integration.ts +100 -190
  105. package/src/delivery/db/queries/preferences.ts +30 -0
  106. package/src/delivery/delivery-service.ts +8 -36
  107. package/src/delivery/feature.ts +10 -2
  108. package/src/delivery/handlers/log.query.ts +5 -7
  109. package/src/delivery/handlers/preferences.query.ts +2 -5
  110. package/src/delivery/tables.ts +26 -1
  111. package/src/delivery/upsert-preference.ts +8 -14
  112. package/src/feature-toggles/__tests__/feature-toggles.integration.ts +30 -30
  113. package/src/feature-toggles/__tests__/registered-system-tenant.test.ts +7 -6
  114. package/src/feature-toggles/db/queries/toggle-state.ts +25 -0
  115. package/src/feature-toggles/feature.ts +16 -2
  116. package/src/feature-toggles/global-feature-state-table.ts +1 -1
  117. package/src/feature-toggles/handlers/list.query.ts +9 -2
  118. package/src/feature-toggles/handlers/registered.query.ts +3 -7
  119. package/src/feature-toggles/handlers/set.write.ts +37 -25
  120. package/src/feature-toggles/toggle-runtime.ts +3 -6
  121. package/src/file-foundation/__tests__/feature.test.ts +1 -1
  122. package/src/file-foundation/__tests__/file-foundation.integration.ts +1 -1
  123. package/src/file-provider-inmemory/__tests__/feature.test.ts +1 -1
  124. package/src/file-provider-s3/__tests__/feature.test.ts +1 -1
  125. package/src/files/__tests__/files.integration.ts +18 -7
  126. package/src/files/schema/file-ref.ts +1 -1
  127. package/src/files-provider-s3/__tests__/env-helper.test.ts +1 -1
  128. package/src/files-provider-s3/__tests__/s3-provider.integration.ts +1 -1
  129. package/src/files-provider-s3/__tests__/s3-provider.test.ts +1 -1
  130. package/src/jobs/__tests__/job-system-user.integration.ts +1 -1
  131. package/src/jobs/__tests__/jobs-events.integration.ts +8 -21
  132. package/src/jobs/__tests__/jobs-feature.integration.ts +1 -1
  133. package/src/jobs/feature.ts +26 -15
  134. package/src/jobs/handlers/detail.query.ts +10 -8
  135. package/src/jobs/handlers/list.query.ts +9 -21
  136. package/src/jobs/handlers/retry.write.ts +2 -7
  137. package/src/jobs/job-run-logger.ts +3 -9
  138. package/src/jobs/job-run-table.ts +49 -17
  139. package/src/legal-pages/__tests__/legal-pages.integration.ts +1 -1
  140. package/src/mail-foundation/__tests__/feature.test.ts +1 -1
  141. package/src/mail-foundation/__tests__/mail-foundation.integration.ts +1 -1
  142. package/src/mail-transport-inmemory/__tests__/feature.test.ts +1 -1
  143. package/src/mail-transport-smtp/__tests__/feature.test.ts +1 -1
  144. package/src/rate-limiting/__tests__/rate-limiting.integration.ts +1 -1
  145. package/src/renderer-foundation/__tests__/api.test.ts +2 -2
  146. package/src/renderer-foundation/__tests__/collect-plugins.integration.ts +1 -1
  147. package/src/renderer-simple/__tests__/adapter.test.ts +2 -2
  148. package/src/renderer-simple/__tests__/simple-renderer.test.ts +1 -1
  149. package/src/secrets/__tests__/require-secrets-context.test.ts +6 -5
  150. package/src/secrets/__tests__/rotate.integration.ts +6 -9
  151. package/src/secrets/__tests__/secrets-events.integration.ts +6 -12
  152. package/src/secrets/__tests__/secrets.integration.ts +6 -11
  153. package/src/secrets/db/queries/read.ts +16 -0
  154. package/src/secrets/handlers/list.query.ts +16 -17
  155. package/src/secrets/handlers/rotate.job.ts +8 -12
  156. package/src/secrets/secrets-context.ts +9 -21
  157. package/src/secrets/table.ts +1 -1
  158. package/src/sessions/__tests__/cleanup.integration.ts +8 -6
  159. package/src/sessions/__tests__/password-auto-revoke.integration.ts +7 -6
  160. package/src/sessions/__tests__/sessions.integration.ts +23 -38
  161. package/src/sessions/__tests__/test-helpers.ts +1 -1
  162. package/src/sessions/db/queries/cleanup.ts +21 -0
  163. package/src/sessions/handlers/cleanup.job.ts +6 -29
  164. package/src/sessions/handlers/list.query.ts +24 -24
  165. package/src/sessions/handlers/mine.query.ts +24 -23
  166. package/src/sessions/handlers/revoke-all-for-user.write.ts +7 -11
  167. package/src/sessions/handlers/revoke-all-others.write.ts +7 -12
  168. package/src/sessions/handlers/revoke.write.ts +11 -18
  169. package/src/sessions/schema/user-session.ts +2 -2
  170. package/src/sessions/session-callbacks.ts +19 -21
  171. package/src/subscription-mollie/__tests__/feature.test.ts +1 -1
  172. package/src/subscription-mollie/__tests__/mollie-foundation.integration.ts +1 -1
  173. package/src/subscription-mollie/__tests__/verify-webhook.test.ts +8 -7
  174. package/src/subscription-stripe/__tests__/feature.test.ts +1 -1
  175. package/src/subscription-stripe/__tests__/plugin-methods.test.ts +14 -15
  176. package/src/subscription-stripe/__tests__/stripe-foundation.integration.ts +1 -1
  177. package/src/subscription-stripe/__tests__/verify-webhook.test.ts +14 -14
  178. package/src/subscription-stripe/verify-webhook.ts +1 -1
  179. package/src/template-resolver/__tests__/handlers.integration.ts +1 -1
  180. package/src/template-resolver/__tests__/template-resolver.integration.ts +3 -2
  181. package/src/template-resolver/api.ts +7 -13
  182. package/src/template-resolver/handlers/archive.write.ts +4 -7
  183. package/src/template-resolver/handlers/find-by-id.query.ts +4 -7
  184. package/src/template-resolver/handlers/list.query.ts +13 -21
  185. package/src/template-resolver/handlers/publish.write.ts +4 -7
  186. package/src/template-resolver/handlers/upsert-system.write.ts +7 -10
  187. package/src/template-resolver/handlers/upsert-tenant.write.ts +7 -10
  188. package/src/template-resolver/table.ts +2 -5
  189. package/src/tenant/__tests__/multi-tenant.integration.ts +1 -1
  190. package/src/tenant/__tests__/seed-testing.integration.ts +19 -45
  191. package/src/tenant/__tests__/tenant.integration.ts +1 -1
  192. package/src/tenant/handlers/active-tenant-ids.query.ts +3 -8
  193. package/src/tenant/handlers/add-member.write.ts +6 -8
  194. package/src/tenant/handlers/cancel-invitation.write.ts +5 -7
  195. package/src/tenant/handlers/invitations.query.ts +5 -10
  196. package/src/tenant/handlers/me.query.ts +2 -3
  197. package/src/tenant/handlers/members.query.ts +4 -5
  198. package/src/tenant/handlers/memberships.query.ts +2 -5
  199. package/src/tenant/handlers/remove-member.write.ts +6 -8
  200. package/src/tenant/handlers/resolve-user-ids.query.ts +6 -16
  201. package/src/tenant/handlers/update-member-roles.write.ts +6 -8
  202. package/src/tenant/invitation-table.ts +2 -5
  203. package/src/tenant/membership-table.ts +3 -6
  204. package/src/tenant/schema/tenant.ts +2 -2
  205. package/src/tenant/seeding.ts +12 -18
  206. package/src/text-content/README.md +1 -1
  207. package/src/text-content/__tests__/text-content.integration.ts +2 -2
  208. package/src/text-content/api.ts +2 -9
  209. package/src/text-content/handlers/by-slug.query.ts +6 -9
  210. package/src/text-content/handlers/by-tenant.query.ts +2 -2
  211. package/src/text-content/handlers/set.write.ts +7 -9
  212. package/src/text-content/seeding.ts +6 -9
  213. package/src/text-content/table.ts +2 -2
  214. package/src/text-content/web/__tests__/editor-read-only.test.tsx +31 -45
  215. package/src/text-content/web/__tests__/group-blocks.test.ts +1 -18
  216. package/src/text-content/web/client-plugin.tsx +11 -23
  217. package/src/tier-engine/__tests__/auto-default-tier.integration.ts +10 -16
  218. package/src/tier-engine/__tests__/compose-app.test.ts +1 -1
  219. package/src/tier-engine/__tests__/drift.test.ts +1 -1
  220. package/src/tier-engine/__tests__/resolver.integration.ts +6 -6
  221. package/src/tier-engine/__tests__/tier-engine.integration.ts +1 -1
  222. package/src/tier-engine/feature.ts +9 -16
  223. package/src/user/__tests__/seed-testing.integration.ts +10 -22
  224. package/src/user/__tests__/user-status.test.ts +1 -1
  225. package/src/user/__tests__/user.integration.ts +6 -5
  226. package/src/user/handlers/create.write.ts +5 -7
  227. package/src/user/handlers/find-for-auth.query.ts +5 -7
  228. package/src/user/schema/user.ts +2 -2
  229. package/src/user/seeding.ts +2 -3
  230. package/src/user-data-rights/__tests__/audit-log.integration.ts +24 -12
  231. package/src/user-data-rights/__tests__/cross-data-matrix.integration.ts +64 -37
  232. package/src/user-data-rights/__tests__/download.integration.ts +29 -46
  233. package/src/user-data-rights/__tests__/export-job-idempotency.integration.ts +35 -28
  234. package/src/user-data-rights/__tests__/export-job-schema.test.ts +2 -2
  235. package/src/user-data-rights/__tests__/policy-to-strategy.test.ts +1 -1
  236. package/src/user-data-rights/__tests__/request-cancel-deletion.integration.ts +11 -15
  237. package/src/user-data-rights/__tests__/request-deletion-callback.integration.ts +10 -12
  238. package/src/user-data-rights/__tests__/request-export.integration.ts +23 -16
  239. package/src/user-data-rights/__tests__/restriction-flow.integration.ts +24 -32
  240. package/src/user-data-rights/__tests__/run-export-jobs.integration.ts +142 -137
  241. package/src/user-data-rights/__tests__/run-forget-cleanup.integration.ts +46 -28
  242. package/src/user-data-rights/__tests__/run-user-export.integration.ts +20 -14
  243. package/src/user-data-rights/__tests__/token-helpers.test.ts +1 -1
  244. package/src/user-data-rights/__tests__/user-data-rights.integration.ts +1 -1
  245. package/src/user-data-rights/__tests__/zip-path.test.ts +1 -1
  246. package/src/user-data-rights/audit-download.ts +3 -3
  247. package/src/user-data-rights/db/queries/export-jobs.ts +23 -0
  248. package/src/user-data-rights/db/queries/forget-cleanup.ts +13 -0
  249. package/src/user-data-rights/handlers/cancel-deletion.write.ts +28 -22
  250. package/src/user-data-rights/handlers/download-by-job.query.ts +11 -21
  251. package/src/user-data-rights/handlers/download-by-token.query.ts +20 -35
  252. package/src/user-data-rights/handlers/export-status.query.ts +19 -33
  253. package/src/user-data-rights/handlers/lift-restriction.write.ts +7 -12
  254. package/src/user-data-rights/handlers/list-download-attempts.query.ts +14 -23
  255. package/src/user-data-rights/handlers/my-audit-log.query.ts +33 -23
  256. package/src/user-data-rights/handlers/request-deletion.write.ts +15 -15
  257. package/src/user-data-rights/handlers/request-export.write.ts +7 -11
  258. package/src/user-data-rights/handlers/restrict-account.write.ts +12 -12
  259. package/src/user-data-rights/run-export-jobs.ts +20 -60
  260. package/src/user-data-rights/run-forget-cleanup.ts +19 -33
  261. package/src/user-data-rights/run-user-export.ts +4 -6
  262. package/src/user-data-rights/schema/download-attempt.ts +2 -2
  263. package/src/user-data-rights/schema/download-token.ts +2 -2
  264. package/src/user-data-rights/schema/export-job.ts +2 -3
  265. package/src/user-data-rights-defaults/__tests__/user-data-rights-defaults.integration.ts +37 -30
  266. package/src/user-data-rights-defaults/db/queries/user-hook.ts +17 -0
  267. package/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +12 -27
  268. package/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +16 -18
  269. package/CHANGELOG.md +0 -689
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import type { S3ProviderConfig } from "../s3-provider";
3
3
  import { resolveForcePathStyle } from "../s3-provider";
4
4
 
@@ -1,3 +1,4 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
1
2
  import { randomBytes } from "node:crypto";
2
3
  import {
3
4
  createEncryptionProvider,
@@ -26,7 +27,6 @@ import {
26
27
  unsafePushTables,
27
28
  } from "@cosmicdrift/kumiko-framework/stack";
28
29
  import { bridgeStub, sleep } from "@cosmicdrift/kumiko-framework/testing";
29
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
30
30
  import { ConfigHandlers } from "../../config/constants";
31
31
  import { createConfigAccessor, createConfigFeature } from "../../config/feature";
32
32
  import { type ConfigResolver, createConfigResolver } from "../../config/resolver";
@@ -8,6 +8,8 @@
8
8
  // projection side (list + detail queries). This file covers the event
9
9
  // side — complementary coverage, minimal overlap.
10
10
 
11
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
12
+ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
11
13
  import { createRegistry, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
12
14
  import { createEventsTable, eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
13
15
  import {
@@ -17,8 +19,7 @@ import {
17
19
  type TestRedis,
18
20
  unsafePushTables,
19
21
  } from "@cosmicdrift/kumiko-framework/stack";
20
- import { eq } from "drizzle-orm";
21
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
22
+ import { resetTestTables } from "@cosmicdrift/kumiko-framework/testing";
22
23
  import { runCompletedSchema, runFailedSchema, runStartedSchema } from "../events";
23
24
  import { createJobsFeature } from "../feature";
24
25
  import {
@@ -48,9 +49,7 @@ afterAll(async () => {
48
49
  });
49
50
 
50
51
  beforeEach(async () => {
51
- await testDb.db.delete(eventsTable);
52
- await testDb.db.delete(jobRunsTable);
53
- await testDb.db.delete(jobRunLogsTable);
52
+ await resetTestTables(testDb.db, [eventsTable, jobRunsTable, jobRunLogsTable]);
54
53
  });
55
54
 
56
55
  describe("jobRun event shapes", () => {
@@ -69,10 +68,7 @@ describe("jobRun event shapes", () => {
69
68
  attempt: 1,
70
69
  });
71
70
 
72
- const events = await testDb.db
73
- .select()
74
- .from(eventsTable)
75
- .where(eq(eventsTable.type, JOB_RUN_STARTED_EVENT));
71
+ const events = await selectMany(testDb.db, eventsTable, { type: JOB_RUN_STARTED_EVENT });
76
72
 
77
73
  expect(events.length).toBe(1);
78
74
  const e = events[0];
@@ -95,10 +91,7 @@ describe("jobRun event shapes", () => {
95
91
  { level: "info", message: "done", timestamp: Temporal.Now.instant() },
96
92
  ]);
97
93
 
98
- const events = await testDb.db
99
- .select()
100
- .from(eventsTable)
101
- .where(eq(eventsTable.type, JOB_RUN_COMPLETED_EVENT));
94
+ const events = await selectMany(testDb.db, eventsTable, { type: JOB_RUN_COMPLETED_EVENT });
102
95
 
103
96
  expect(events.length).toBe(1);
104
97
  const p = runCompletedSchema.parse(events[0]?.payload);
@@ -113,10 +106,7 @@ describe("jobRun event shapes", () => {
113
106
  { level: "error", message: "kaboom", timestamp: Temporal.Now.instant() },
114
107
  ]);
115
108
 
116
- const events = await testDb.db
117
- .select()
118
- .from(eventsTable)
119
- .where(eq(eventsTable.type, JOB_RUN_FAILED_EVENT));
109
+ const events = await selectMany(testDb.db, eventsTable, { type: JOB_RUN_FAILED_EVENT });
120
110
 
121
111
  expect(events.length).toBe(1);
122
112
  const p = runFailedSchema.parse(events[0]?.payload);
@@ -131,10 +121,7 @@ describe("jobRun event shapes", () => {
131
121
  // Both events should share the same aggregateId — that's what makes
132
122
  // the jobRun a single stream and lets ctx.loadAggregate() reduce
133
123
  // them into a coherent state.
134
- const events = await testDb.db
135
- .select()
136
- .from(eventsTable)
137
- .where(eq(eventsTable.aggregateType, "jobRun"));
124
+ const events = await selectMany(testDb.db, eventsTable, { aggregateType: "jobRun" });
138
125
 
139
126
  expect(events.length).toBe(2);
140
127
  const ids = new Set(events.map((e) => e.aggregateId));
@@ -1,3 +1,4 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
1
2
  import { buildServer, type JwtHelper } from "@cosmicdrift/kumiko-framework/api";
2
3
  import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
3
4
  import {
@@ -18,7 +19,6 @@ import {
18
19
  } from "@cosmicdrift/kumiko-framework/stack";
19
20
  import { sleep } from "@cosmicdrift/kumiko-framework/testing";
20
21
  import type { Hono } from "hono";
21
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
22
22
  import { JobHandlers, JobQueries } from "../constants";
23
23
  import { createJobsFeature } from "../feature";
24
24
  import { createJobRunLogger } from "../job-run-logger";
@@ -1,9 +1,9 @@
1
+ import { insertMany, insertOne, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
1
2
  import {
2
3
  defineApply,
3
4
  defineFeature,
4
5
  type FeatureDefinition,
5
6
  } from "@cosmicdrift/kumiko-framework/engine";
6
- import { eq } from "drizzle-orm";
7
7
  import type { z } from "zod";
8
8
  // Event-payload schemas live in a sibling module so the logger can import
9
9
  // them without the cycle jobs-feature ↔ job-run-logger. The logger parses
@@ -19,11 +19,14 @@ import {
19
19
  JOB_RUN_FAILED_EVENT,
20
20
  JOB_RUN_STARTED_EVENT,
21
21
  } from "./job-run-logger";
22
- import { jobRunLogsTable, jobRunsTable } from "./job-run-table";
22
+ import { jobRunLogsTable, jobRunLogsTableMeta, jobRunsTable } from "./job-run-table";
23
23
 
24
24
  export function createJobsFeature(): FeatureDefinition {
25
25
  return defineFeature("jobs", (r) => {
26
26
  r.systemScope();
27
+ r.unmanagedTable(jobRunLogsTableMeta, {
28
+ reason: "read_side.job_run_logs",
29
+ });
27
30
  // Events-only aggregate: "jobRun" has no r.entity registration, because
28
31
  // the entire lifecycle is driven by BullMQ-callback → r.defineEvent
29
32
  // (no executor, no CRUD). The boot-validator accepts the two
@@ -44,7 +47,7 @@ export function createJobsFeature(): FeatureDefinition {
44
47
  [JOB_RUN_STARTED_EVENT]: defineApply<z.infer<typeof runStartedSchema>>(
45
48
  async (event, tx) => {
46
49
  const p = event.payload;
47
- await tx.insert(jobRunsTable).values({
50
+ await insertOne(tx, jobRunsTable, {
48
51
  id: event.aggregateId,
49
52
  tenantId: event.tenantId,
50
53
  version: event.version,
@@ -63,24 +66,27 @@ export function createJobsFeature(): FeatureDefinition {
63
66
  [JOB_RUN_COMPLETED_EVENT]: defineApply<z.infer<typeof runCompletedSchema>>(
64
67
  async (event, tx) => {
65
68
  const p = event.payload;
66
- await tx
67
- .update(jobRunsTable)
68
- .set({
69
+ await updateMany(
70
+ tx,
71
+ jobRunsTable,
72
+ {
69
73
  status: "completed",
70
74
  duration: p.duration,
71
75
  finishedAt: Temporal.Instant.from(p.finishedAt),
72
76
  version: event.version,
73
77
  modifiedAt: event.createdAt,
74
78
  modifiedById: event.metadata?.userId ?? "system",
75
- })
76
- .where(eq(jobRunsTable.id, event.aggregateId));
79
+ },
80
+ { id: event.aggregateId },
81
+ );
77
82
  },
78
83
  ),
79
84
  [JOB_RUN_FAILED_EVENT]: defineApply<z.infer<typeof runFailedSchema>>(async (event, tx) => {
80
85
  const p = event.payload;
81
- await tx
82
- .update(jobRunsTable)
83
- .set({
86
+ await updateMany(
87
+ tx,
88
+ jobRunsTable,
89
+ {
84
90
  status: "failed",
85
91
  error: p.error,
86
92
  duration: p.duration,
@@ -88,8 +94,9 @@ export function createJobsFeature(): FeatureDefinition {
88
94
  version: event.version,
89
95
  modifiedAt: event.createdAt,
90
96
  modifiedById: event.metadata?.userId ?? "system",
91
- })
92
- .where(eq(jobRunsTable.id, event.aggregateId));
97
+ },
98
+ { id: event.aggregateId },
99
+ );
93
100
  }),
94
101
  },
95
102
  });
@@ -109,7 +116,9 @@ export function createJobsFeature(): FeatureDefinition {
109
116
  // to insert; the completed-event alone already updated the run's
110
117
  // status via the sibling job-runs projection.
111
118
  if (p.logs.length === 0) return;
112
- await tx.insert(jobRunLogsTable).values(
119
+ await insertMany(
120
+ tx,
121
+ jobRunLogsTable,
113
122
  p.logs.map((log) => ({
114
123
  runId: event.aggregateId,
115
124
  level: log.level,
@@ -123,7 +132,9 @@ export function createJobsFeature(): FeatureDefinition {
123
132
  const p = event.payload;
124
133
  // skip: empty log batch — the worker ran silent (mirror of completed)
125
134
  if (p.logs.length === 0) return;
126
- await tx.insert(jobRunLogsTable).values(
135
+ await insertMany(
136
+ tx,
137
+ jobRunLogsTable,
127
138
  p.logs.map((log) => ({
128
139
  runId: event.aggregateId,
129
140
  level: log.level,
@@ -1,6 +1,5 @@
1
- import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
1
+ import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
3
- import { eq } from "drizzle-orm";
4
3
  import { z } from "zod";
5
4
  import { jobRunLogsTable, jobRunsTable } from "../job-run-table";
6
5
 
@@ -15,15 +14,18 @@ export const detailQuery = defineQueryHandler({
15
14
  handler: async (query, ctx) => {
16
15
  const db = ctx.db;
17
16
 
18
- const row = await fetchOne(db, jobRunsTable, eq(jobRunsTable.id, query.payload.runId));
17
+ const row = await fetchOne(db, jobRunsTable, { id: query.payload.runId });
19
18
 
20
19
  if (!row) return null;
21
20
 
22
- const logs = await db
23
- .select()
24
- .from(jobRunLogsTable)
25
- .where(eq(jobRunLogsTable.runId, query.payload.runId))
26
- .orderBy(jobRunLogsTable.id);
21
+ const logs = await selectMany(
22
+ db,
23
+ jobRunLogsTable,
24
+ { runId: query.payload.runId },
25
+ {
26
+ orderBy: { col: "id", direction: "asc" },
27
+ },
28
+ );
27
29
 
28
30
  return { ...row, logs };
29
31
  },
@@ -1,7 +1,7 @@
1
+ import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
1
2
  import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
2
- import { and, desc, eq, type SQL } from "drizzle-orm";
3
3
  import { z } from "zod";
4
- import { type JobRunStatus, jobRunsTable } from "../job-run-table";
4
+ import { jobRunsTable } from "../job-run-table";
5
5
 
6
6
  export const listQuery = defineQueryHandler({
7
7
  name: "list",
@@ -12,25 +12,13 @@ export const listQuery = defineQueryHandler({
12
12
  }),
13
13
  access: { roles: ["SystemAdmin"] },
14
14
  handler: async (query, ctx) => {
15
- const db = ctx.db;
16
- const conditions: SQL[] = [];
17
-
18
- if (query.payload.jobName) {
19
- conditions.push(eq(jobRunsTable.jobName, query.payload.jobName));
20
- }
21
- if (query.payload.status) {
22
- conditions.push(eq(jobRunsTable.status, query.payload.status as JobRunStatus)); // @cast-boundary engine-payload
23
- }
24
-
25
- const limit = query.payload.limit ?? 50;
26
-
27
- const rows = await db
28
- .select()
29
- .from(jobRunsTable)
30
- .where(conditions.length > 0 ? and(...conditions) : undefined)
31
- .orderBy(desc(jobRunsTable.id))
32
- .limit(limit);
33
-
15
+ const where: WhereObject = {};
16
+ if (query.payload.jobName) where["jobName"] = query.payload.jobName;
17
+ if (query.payload.status) where["status"] = query.payload.status;
18
+ const rows = await selectMany(ctx.db, jobRunsTable, where, {
19
+ orderBy: { col: "id", direction: "desc" },
20
+ limit: query.payload.limit ?? 50,
21
+ });
34
22
  return { rows };
35
23
  },
36
24
  });
@@ -1,4 +1,4 @@
1
- import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
1
+ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import {
4
4
  NotFoundError,
@@ -7,7 +7,6 @@ import {
7
7
  } from "@cosmicdrift/kumiko-framework/errors";
8
8
  import type { JobRunner } from "@cosmicdrift/kumiko-framework/jobs";
9
9
  import { parseJsonOrThrow } from "@cosmicdrift/kumiko-framework/utils";
10
- import { eq } from "drizzle-orm";
11
10
  import { z } from "zod";
12
11
  import { JobErrors } from "../constants";
13
12
  import { jobRunsTable } from "../job-run-table";
@@ -29,11 +28,7 @@ export const retryWrite = defineWriteHandler({
29
28
  // @cast-boundary engine-payload — JobRunner attached by app-boot via ctx-extension
30
29
  const jobRunner = ctx["jobRunner"] as JobRunner;
31
30
 
32
- const run = await fetchOne<JobRunRow>(
33
- db,
34
- jobRunsTable,
35
- eq(jobRunsTable.id, event.payload.runId),
36
- );
31
+ const run = await fetchOne<JobRunRow>(db, jobRunsTable, { id: event.payload.runId });
37
32
 
38
33
  if (!run) {
39
34
  return writeFailure(
@@ -1,10 +1,10 @@
1
+ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
1
2
  import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
2
3
  import { type Registry, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
3
4
  import { append, getStreamVersion } from "@cosmicdrift/kumiko-framework/event-store";
4
5
  import type { JobLogEntry, JobMeta, JobRunnerOptions } from "@cosmicdrift/kumiko-framework/jobs";
5
6
  import { runProjectionsForEvent } from "@cosmicdrift/kumiko-framework/pipeline";
6
7
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
7
- import { eq } from "drizzle-orm";
8
8
  import { runCompletedSchema, runFailedSchema, runStartedSchema } from "./events";
9
9
  import { jobRunsTable } from "./job-run-table";
10
10
 
@@ -86,10 +86,7 @@ export function createJobRunLogger(opts: JobRunLoggerOptions): JobRunLoggerCallb
86
86
  async function resolveRunId(bullJobId: string): Promise<string | undefined> {
87
87
  const cached = cacheGet(bullJobId);
88
88
  if (cached) return cached;
89
- const [row] = await db
90
- .select({ id: jobRunsTable.id })
91
- .from(jobRunsTable)
92
- .where(eq(jobRunsTable.bullJobId, bullJobId));
89
+ const row = await fetchOne<{ id: string | number }>(db, jobRunsTable, { bullJobId });
93
90
  // buildBaseColumns's signature types `id` as `string | number` because
94
91
  // it returns both branches of the idType union. We know this table
95
92
  // was built with idType: "uuid" (see job-run-table.ts), so narrowing
@@ -181,10 +178,7 @@ export function createJobRunLogger(opts: JobRunLoggerOptions): JobRunLoggerCallb
181
178
  // symmetrically to onJobComplete (which gets duration from the
182
179
  // worker). The projection already has started_at from the
183
180
  // run-started inline-apply.
184
- const [row] = await db
185
- .select({ startedAt: jobRunsTable.startedAt })
186
- .from(jobRunsTable)
187
- .where(eq(jobRunsTable.id, runId));
181
+ const row = await fetchOne<{ startedAt: Temporal.Instant }>(db, jobRunsTable, { id: runId });
188
182
  const now = Temporal.Now.instant();
189
183
  const duration = row ? Number(now.since(row.startedAt).total({ unit: "millisecond" })) : 0;
190
184
  const payload = runFailedSchema.parse({
@@ -1,11 +1,18 @@
1
1
  import {
2
- buildBaseColumns,
2
+ buildEntityTable,
3
+ defineUnmanagedTable,
4
+ type EntityTableMeta,
3
5
  instant,
4
- integer,
5
6
  table as pgTable,
6
7
  serial,
7
8
  text,
8
9
  } from "@cosmicdrift/kumiko-framework/db";
10
+ import {
11
+ createEntity,
12
+ createNumberField,
13
+ createTextField,
14
+ createTimestampField,
15
+ } from "@cosmicdrift/kumiko-framework/engine";
9
16
 
10
17
  export type JobRunStatus = "queued" | "running" | "completed" | "failed";
11
18
  export type JobLogLevel = "info" | "warn" | "error";
@@ -24,23 +31,30 @@ export type JobLogLevel = "info" | "warn" | "error";
24
31
  // expands the batch into N rows in jobRunLogsTable, keeping the pre-ES
25
32
  // detail-query-shape intact.
26
33
  //
27
- // No r.entity is registered for `jobRun` the boot-validator accepts
28
- // events-only projection sources where every apply-key is a registered
29
- // domain-event (see registry.ts).
30
- export const jobRunsTable = pgTable("read_job_runs", {
31
- ...buildBaseColumns(false, "uuid"),
32
- jobName: text("job_name").notNull(),
33
- bullJobId: text("bull_job_id").notNull(),
34
- status: text("status").notNull().$type<JobRunStatus>(),
35
- payload: text("payload"),
36
- error: text("error"),
37
- attempt: integer("attempt").default(1).notNull(),
38
- startedAt: instant("started_at").notNull(),
39
- finishedAt: instant("finished_at"),
40
- duration: integer("duration"),
41
- triggeredById: text("triggered_by_id"),
34
+ // Entity-derived table Phase 3b of drizzle-replacement. Earlier this was
35
+ // a hand-written pgTable; the entity-form is the single source for both
36
+ // the drizzle-table (query API) and the future EntityTableMeta-based
37
+ // migration generator. status/$type<JobRunStatus> ist nicht im entity-
38
+ // schema modelliert — Drizzle's column-type ist text mit CHECK-Constraint
39
+ // als App-Boundary (gleicher Pattern wie template-resolver kind/scope).
40
+ export const jobRunEntity = createEntity({
41
+ table: "read_job_runs",
42
+ fields: {
43
+ jobName: createTextField({ required: true }),
44
+ bullJobId: createTextField({ required: true }),
45
+ status: createTextField({ required: true }),
46
+ payload: createTextField(),
47
+ error: createTextField(),
48
+ attempt: createNumberField({ required: true, default: 1 }),
49
+ startedAt: createTimestampField({ required: true }),
50
+ finishedAt: createTimestampField(),
51
+ duration: createNumberField(),
52
+ triggeredById: createTextField(),
53
+ },
42
54
  });
43
55
 
56
+ export const jobRunsTable = buildEntityTable("job-run", jobRunEntity);
57
+
44
58
  // Child projection keyed by the jobRun aggregate id. Pre-ES used a serial
45
59
  // PK + integer runId; post-ES runId is still exposed but now holds the
46
60
  // uuid of the parent jobRun. Existing detail-query callers treat it as an
@@ -53,3 +67,21 @@ export const jobRunLogsTable = pgTable("read_job_run_logs", {
53
67
  message: text("message").notNull(),
54
68
  timestamp: instant("timestamp").notNull(),
55
69
  });
70
+
71
+ // **Unmanaged table** — bewusst KEIN createEntity. Begründung:
72
+ // - serial PK (kein uuid) — pre-ES legacy, kompatibilität mit existing rows
73
+ // - KEIN tenant_id — child-Tabelle von jobRun, tenant-context lebt am parent
74
+ // - keine base-columns (kein version/inserted_at/inserted_by_id) — append-
75
+ // only log, kein in-place-update, keine Audit-Spalten gewünscht
76
+ // pgTable bleibt source-of-truth für Query-API; Phase 4 leitet das pgTable
77
+ // aus dieser Meta ab.
78
+ export const jobRunLogsTableMeta: EntityTableMeta = defineUnmanagedTable({
79
+ tableName: "read_job_run_logs",
80
+ columns: [
81
+ { name: "id", pgType: "serial", notNull: true, primaryKey: true },
82
+ { name: "run_id", pgType: "text", notNull: true },
83
+ { name: "level", pgType: "text", notNull: true },
84
+ { name: "message", pgType: "text", notNull: true },
85
+ { name: "timestamp", pgType: "timestamptz", notNull: true },
86
+ ],
87
+ });
@@ -1,3 +1,4 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
1
2
  import type { TextContentApi } from "@cosmicdrift/kumiko-bundled-features/text-content";
2
3
  import {
3
4
  createTextContentApi,
@@ -13,7 +14,6 @@ import {
13
14
  type TestStack,
14
15
  unsafeCreateEntityTable,
15
16
  } from "@cosmicdrift/kumiko-framework/stack";
16
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
17
17
  import { createLegalPagesFeature, runLegalPagesBootCheck } from "../feature";
18
18
  import { renderMarkdownToHtml, wrapInLayout } from "../markdown";
19
19
 
@@ -6,7 +6,7 @@
6
6
  // declares an extension-point + a single selector config-key, nothing
7
7
  // provider-concrete.
8
8
 
9
- import { describe, expect, test } from "vitest";
9
+ import { describe, expect, test } from "bun:test";
10
10
  import { mailFoundationFeature } from "../feature";
11
11
 
12
12
  describe("mailFoundationFeature — shape", () => {
@@ -8,6 +8,7 @@
8
8
  // it saw. That's the cheapest way to get a real `HandlerContext` in a
9
9
  // test without re-implementing the dispatcher.
10
10
 
11
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
11
12
  import { randomBytes } from "node:crypto";
12
13
  import { createEncryptionProvider, type DbConnection } from "@cosmicdrift/kumiko-framework/db";
13
14
  import { defineFeature, defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
@@ -25,7 +26,6 @@ import {
25
26
  createMutableMasterKeyProvider,
26
27
  type MutableMasterKeyProvider,
27
28
  } from "@cosmicdrift/kumiko-framework/testing";
28
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
29
29
  import { z } from "zod";
30
30
  import { createConfigFeature } from "../../config";
31
31
  import { ConfigHandlers } from "../../config/constants";
@@ -1,6 +1,6 @@
1
1
  // feature.ts contract tests for mail-transport-inmemory.
2
2
 
3
- import { describe, expect, test } from "vitest";
3
+ import { describe, expect, test } from "bun:test";
4
4
  import { clearInbox, getInbox, mailTransportInMemoryFeature } from "../feature";
5
5
 
6
6
  describe("mailTransportInMemoryFeature — shape", () => {
@@ -3,7 +3,7 @@
3
3
  // Plugin-registration shape is also pinned (drift-pin: name "smtp",
4
4
  // build-fn presence).
5
5
 
6
- import { describe, expect, test } from "vitest";
6
+ import { describe, expect, test } from "bun:test";
7
7
  import { mailTransportSmtpFeature, SMTP_PASSWORD } from "../feature";
8
8
 
9
9
  describe("mailTransportSmtpFeature — shape", () => {
@@ -5,9 +5,9 @@
5
5
  // L3 dispatcher hook + resolver wiring are tested in framework-side
6
6
  // suites; here we only verify the feature's own surface area.
7
7
 
8
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
8
9
  import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
9
10
  import { setupTestStack, type TestStack, TestUsers } from "@cosmicdrift/kumiko-framework/stack";
10
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
11
11
  import { z } from "zod";
12
12
  import { createRateLimitingFeature } from "../feature";
13
13
 
@@ -1,5 +1,5 @@
1
+ import { describe, expect, test } from "bun:test";
1
2
  import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
- import { describe, expect, test } from "vitest";
3
3
  import { createRendererFoundationApi } from "../api";
4
4
  import {
5
5
  type RendererContext,
@@ -132,7 +132,7 @@ describe("renderer-foundation :: Plugin-Selection", () => {
132
132
  const api = createRendererFoundationApi([]);
133
133
  try {
134
134
  api.createRendererForTenant({ tenantId: TENANT, kind: "notification" });
135
- expect.fail("expected RendererError");
135
+ throw new Error("expected RendererError");
136
136
  } catch (e) {
137
137
  expect(e).toBeInstanceOf(RendererError);
138
138
  expect((e as RendererError).code).toBe("no_plugin_for_kind");
@@ -1,6 +1,6 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
1
2
  import { defineFeature, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
2
3
  import { setupTestStack, type TestStack } from "@cosmicdrift/kumiko-framework/stack";
3
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
4
4
  import { createTemplateResolverFeature } from "../../template-resolver/feature";
5
5
  import { createRendererFoundationApi } from "../api";
6
6
  import { collectRendererPlugins, createRendererFoundationFeature } from "../feature";
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { RendererError } from "../../renderer-foundation";
3
3
  import { adaptToFoundation } from "../feature";
4
4
 
@@ -41,7 +41,7 @@ describe("renderer-simple :: adaptToFoundation", () => {
41
41
  kind: "document-pdf",
42
42
  payload: { content: "test", contentFormat: "markdown" },
43
43
  });
44
- expect.fail("expected RendererError");
44
+ throw new Error("expected RendererError");
45
45
  } catch (e) {
46
46
  expect(e).toBeInstanceOf(RendererError);
47
47
  expect((e as RendererError).code).toBe("invalid_payload");
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "vitest";
1
+ import { describe, expect, test } from "bun:test";
2
2
  import { simpleRenderer } from "../simple-renderer";
3
3
 
4
4
  describe("simple renderer", () => {
@@ -12,16 +12,16 @@
12
12
  // + FileProviderContext-shape direkt) und prueft dass beide den happy-path
13
13
  // + den fehlt-_userId-throw durchlaufen.
14
14
 
15
+ import { describe, expect, mock, test } from "bun:test";
15
16
  import { SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
16
17
  import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
17
- import { describe, expect, test, vi } from "vitest";
18
18
  import { requireSecretsContext } from "../feature";
19
19
 
20
20
  function makeRawSecretsContext(): SecretsContext {
21
21
  return {
22
- get: vi.fn(),
23
- set: vi.fn(),
24
- delete: vi.fn(),
22
+ get: mock(),
23
+ set: mock(),
24
+ delete: mock(),
25
25
  };
26
26
  }
27
27
 
@@ -72,7 +72,8 @@ describe("requireSecretsContext :: FileProviderContext surface", () => {
72
72
  "any-key" as unknown as Parameters<SecretsContext["get"]>[1],
73
73
  );
74
74
  // Erste-Aufruf-args: [tenantId, key, audit-Object]
75
- const audit = vi.mocked(raw.get).mock.calls[0]?.[2];
75
+ // biome-ignore lint/suspicious/noExplicitAny: Bun mock API requires any cast
76
+ const audit = (raw.get as any).mock.calls[0]?.[2];
76
77
  expect(audit).toEqual({
77
78
  userId: SYSTEM_USER_ID,
78
79
  handlerName: "user-data-rights:run-export-jobs",
@@ -3,7 +3,9 @@
3
3
  // asserts the job bails after maxFailures instead of spraying the log
4
4
  // with every row's identical error.
5
5
 
6
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
6
7
  import { randomBytes } from "node:crypto";
8
+ import { deleteMany, insertOne, selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
7
9
  import type { AppContext } from "@cosmicdrift/kumiko-framework/engine";
8
10
  import {
9
11
  createEnvMasterKeyProvider,
@@ -16,8 +18,6 @@ import {
16
18
  type TestStack,
17
19
  unsafePushTables,
18
20
  } from "@cosmicdrift/kumiko-framework/stack";
19
- import { eq, sql } from "drizzle-orm";
20
- import { afterAll, beforeAll, describe, expect, test } from "vitest";
21
21
  import { createSecretsFeature } from "../feature";
22
22
  import { rotateJob } from "../handlers/rotate.job";
23
23
  import { createSecretsContext } from "../secrets-context";
@@ -83,7 +83,7 @@ beforeAll(async () => {
83
83
  // Seed 20 V1 rows directly — too many for any maxFailures default.
84
84
  for (let i = 0; i < 20; i++) {
85
85
  const envelope = await encryptValue(`secret-${i}`, seedProvider);
86
- await stack.db.insert(tenantSecretsTable).values({
86
+ await insertOne(stack.db, tenantSecretsTable, {
87
87
  tenantId: admin.tenantId,
88
88
  key: `test:secret:k-${i}`,
89
89
  envelope: {
@@ -100,7 +100,7 @@ beforeAll(async () => {
100
100
 
101
101
  afterAll(async () => {
102
102
  // Clean up the seeded fixtures so downstream suites don't see them.
103
- await stack.db.delete(tenantSecretsTable).where(eq(tenantSecretsTable.tenantId, admin.tenantId));
103
+ await deleteMany(stack.db, tenantSecretsTable, { tenantId: admin.tenantId });
104
104
  await stack.cleanup();
105
105
  });
106
106
 
@@ -128,11 +128,8 @@ function jobCtx(provider: MasterKeyProvider): Parameters<typeof rotateJob>[1] {
128
128
  }
129
129
 
130
130
  async function countV1Rows(): Promise<number> {
131
- const rows = await stack.db
132
- .select({ kekVersion: tenantSecretsTable.kekVersion })
133
- .from(tenantSecretsTable)
134
- .where(sql`${tenantSecretsTable.tenantId} = ${admin.tenantId}`);
135
- return rows.filter((r) => r.kekVersion === 1).length;
131
+ const rows = await selectMany(stack.db, tenantSecretsTable, { tenantId: admin.tenantId });
132
+ return rows.filter((r: Record<string, unknown>) => r["kekVersion"] === 1).length;
136
133
  }
137
134
 
138
135
  describe("rotate-job circuit-breaker", () => {