@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,5 +1,7 @@
1
1
  // S2.U7 — my-audit-log + invalid-attempt-audit + list-download-attempts.
2
2
 
3
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
4
+ import { asRawClient, insertMany, insertOne } from "@cosmicdrift/kumiko-framework/bun-db";
3
5
  import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
4
6
  import {
5
7
  createTestUser,
@@ -8,8 +10,6 @@ import {
8
10
  testTenantId,
9
11
  unsafeCreateEntityTable,
10
12
  } from "@cosmicdrift/kumiko-framework/stack";
11
- import { sql } from "drizzle-orm";
12
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
13
13
  import {
14
14
  createComplianceProfilesFeature,
15
15
  tenantComplianceProfileEntity,
@@ -50,14 +50,14 @@ afterAll(async () => {
50
50
  });
51
51
 
52
52
  beforeEach(async () => {
53
- await stack.db.delete(userTable);
54
- await stack.db.execute(sql`DELETE FROM read_tenant_compliance_profiles`);
55
- await stack.db.execute(sql`DELETE FROM read_download_attempts`);
56
- await stack.db.execute(sql`DELETE FROM kumiko_events`);
53
+ await asRawClient(stack.db).unsafe(`DELETE FROM "${userTable.tableName}"`);
54
+ await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_compliance_profiles`);
55
+ await asRawClient(stack.db).unsafe(`DELETE FROM read_download_attempts`);
56
+ await asRawClient(stack.db).unsafe(`DELETE FROM kumiko_events`);
57
57
  });
58
58
 
59
59
  async function seedUser(u: typeof alice, email: string): Promise<void> {
60
- await stack.db.insert(userTable).values({
60
+ await insertOne(stack.db, userTable, {
61
61
  id: u.id,
62
62
  tenantId: u.tenantId,
63
63
  email,
@@ -78,12 +78,24 @@ async function seedEvent(
78
78
  payload: object,
79
79
  ): Promise<void> {
80
80
  _eventVersion += 1;
81
- await stack.db.execute(sql`
81
+ await asRawClient(stack.db).unsafe(
82
+ `
82
83
  INSERT INTO kumiko_events
83
84
  (tenant_id, aggregate_type, aggregate_id, version, type, payload, metadata, created_at, created_by)
84
- VALUES (${tenantId}, ${"test-aggregate"}, ${"00000000-0000-4000-8000-00000000aaaa"},
85
- ${_eventVersion}, ${type}, ${JSON.stringify(payload)}, ${"{}"}, now(), ${createdBy})
86
- `);
85
+ VALUES ($1, $2, $3,
86
+ $4, $5, $6, $7, now(), $8)
87
+ `,
88
+ [
89
+ tenantId,
90
+ "test-aggregate",
91
+ "00000000-0000-4000-8000-00000000aaaa",
92
+ _eventVersion,
93
+ type,
94
+ JSON.stringify(payload),
95
+ "{}",
96
+ createdBy,
97
+ ],
98
+ );
87
99
  }
88
100
 
89
101
  describe("my-audit-log", () => {
@@ -161,7 +173,7 @@ describe("list-download-attempts (DPO operator-query)", () => {
161
173
  // Direct-INSERT in attempts (simuliert was die download-handler schreiben).
162
174
  const T = await import("@cosmicdrift/kumiko-framework/time");
163
175
  const now = T.getTemporal().Now.instant();
164
- await stack.db.insert(downloadAttemptsTable).values([
176
+ await insertMany(stack.db, downloadAttemptsTable, [
165
177
  {
166
178
  id: "11111111-1111-4111-8111-111111111111",
167
179
  tenantId: tenantA,
@@ -12,6 +12,8 @@
12
12
  // - Other-User-Isolation: Bobs notes/files bleiben unangetastet bei
13
13
  // Alices Forget; Bobs Daten landen NICHT in Alices Export-Bundle.
14
14
 
15
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
16
+ import { asRawClient, insertOne } from "@cosmicdrift/kumiko-framework/bun-db";
15
17
  import {
16
18
  defineFeature,
17
19
  EXT_USER_DATA,
@@ -24,8 +26,6 @@ import {
24
26
  unsafeCreateEntityTable,
25
27
  } from "@cosmicdrift/kumiko-framework/stack";
26
28
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
27
- import { sql } from "drizzle-orm";
28
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
29
29
  import {
30
30
  createComplianceProfilesFeature,
31
31
  tenantComplianceProfileEntity,
@@ -59,11 +59,14 @@ const PAST = (): Instant => getTemporal().Instant.fromEpochMilliseconds(Date.now
59
59
  // Stellvertretend fuer App-spezifische Entities (Chat-Message, Blog-Post
60
60
  // etc.), die ueber EXT_USER_DATA sauber in die Pipeline integrieren.
61
61
  const exportNotes: UserDataExportHook = async (ctx) => {
62
- const result = await ctx.db.execute(sql`
62
+ const result = await asRawClient(ctx.db).unsafe(
63
+ `
63
64
  SELECT id, title, body
64
65
  FROM test_notes
65
- WHERE tenant_id = ${ctx.tenantId} AND author_id = ${ctx.userId}
66
- `);
66
+ WHERE tenant_id = $1 AND author_id = $2
67
+ `,
68
+ [ctx.tenantId, ctx.userId],
69
+ );
67
70
  // biome-ignore lint/suspicious/noExplicitAny: drizzle execute typing
68
71
  const rows = ((result as any).rows ?? result) as Array<{
69
72
  id: string;
@@ -78,10 +81,13 @@ const exportNotes: UserDataExportHook = async (ctx) => {
78
81
  };
79
82
 
80
83
  const deleteNotes: UserDataDeleteHook = async (ctx, _strategy) => {
81
- await ctx.db.execute(sql`
84
+ await asRawClient(ctx.db).unsafe(
85
+ `
82
86
  DELETE FROM test_notes
83
- WHERE tenant_id = ${ctx.tenantId} AND author_id = ${ctx.userId}
84
- `);
87
+ WHERE tenant_id = $1 AND author_id = $2
88
+ `,
89
+ [ctx.tenantId, ctx.userId],
90
+ );
85
91
  };
86
92
 
87
93
  const testNotesFeature = defineFeature("test-notes", (r) => {
@@ -107,7 +113,7 @@ beforeAll(async () => {
107
113
  await unsafeCreateEntityTable(stack.db, userEntity);
108
114
  await unsafeCreateEntityTable(stack.db, tenantRetentionOverrideEntity);
109
115
  await unsafeCreateEntityTable(stack.db, tenantComplianceProfileEntity);
110
- await stack.db.execute(sql`
116
+ await asRawClient(stack.db).unsafe(`
111
117
  CREATE TABLE IF NOT EXISTS read_tenant_memberships (
112
118
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
113
119
  tenant_id UUID NOT NULL,
@@ -124,7 +130,7 @@ beforeAll(async () => {
124
130
  UNIQUE(user_id, tenant_id)
125
131
  )
126
132
  `);
127
- await stack.db.execute(sql`
133
+ await asRawClient(stack.db).unsafe(`
128
134
  CREATE TABLE IF NOT EXISTS file_refs (
129
135
  id UUID PRIMARY KEY,
130
136
  tenant_id UUID NOT NULL,
@@ -139,7 +145,7 @@ beforeAll(async () => {
139
145
  inserted_by_id TEXT
140
146
  )
141
147
  `);
142
- await stack.db.execute(sql`
148
+ await asRawClient(stack.db).unsafe(`
143
149
  CREATE TABLE IF NOT EXISTS test_notes (
144
150
  id UUID PRIMARY KEY,
145
151
  tenant_id UUID NOT NULL,
@@ -155,10 +161,10 @@ afterAll(async () => {
155
161
  });
156
162
 
157
163
  beforeEach(async () => {
158
- await stack.db.delete(userTable);
159
- await stack.db.execute(sql`DELETE FROM read_tenant_memberships`);
160
- await stack.db.execute(sql`DELETE FROM file_refs`);
161
- await stack.db.execute(sql`DELETE FROM test_notes`);
164
+ await asRawClient(stack.db).unsafe(`DELETE FROM "${userTable.tableName}"`);
165
+ await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_memberships`);
166
+ await asRawClient(stack.db).unsafe(`DELETE FROM file_refs`);
167
+ await asRawClient(stack.db).unsafe(`DELETE FROM test_notes`);
162
168
  });
163
169
 
164
170
  async function seedUser(
@@ -170,7 +176,7 @@ async function seedUser(
170
176
  displayName?: string;
171
177
  } = {},
172
178
  ): Promise<void> {
173
- await stack.db.insert(userTable).values({
179
+ await insertOne(stack.db, userTable, {
174
180
  id,
175
181
  tenantId: TENANT_SYSTEM,
176
182
  email: overrides.email ?? `user-${id}@example.com`,
@@ -185,11 +191,14 @@ async function seedUser(
185
191
  }
186
192
 
187
193
  async function seedMembership(userId: string, tenantId: string): Promise<void> {
188
- await stack.db.execute(sql`
194
+ await asRawClient(stack.db).unsafe(
195
+ `
189
196
  INSERT INTO read_tenant_memberships (tenant_id, user_id, roles)
190
- VALUES (${tenantId}, ${userId}, '["Member"]')
197
+ VALUES ($1, $2, '["Member"]')
191
198
  ON CONFLICT (user_id, tenant_id) DO NOTHING
192
- `);
199
+ `,
200
+ [tenantId, userId],
201
+ );
193
202
  }
194
203
 
195
204
  async function seedFileRef(
@@ -198,10 +207,13 @@ async function seedFileRef(
198
207
  userId: string,
199
208
  name: string,
200
209
  ): Promise<void> {
201
- await stack.db.execute(sql`
210
+ await asRawClient(stack.db).unsafe(
211
+ `
202
212
  INSERT INTO file_refs (id, tenant_id, storage_key, file_name, mime_type, size, inserted_by_id)
203
- VALUES (${id}, ${tenantId}, ${`storage/${id}`}, ${name}, 'application/pdf', 1024, ${userId})
204
- `);
213
+ VALUES ($1, $2, $3, $4, 'application/pdf', 1024, $5)
214
+ `,
215
+ [id, tenantId, `storage/${id}`, name, userId],
216
+ );
205
217
  }
206
218
 
207
219
  async function seedNote(
@@ -210,16 +222,22 @@ async function seedNote(
210
222
  userId: string,
211
223
  title: string,
212
224
  ): Promise<void> {
213
- await stack.db.execute(sql`
225
+ await asRawClient(stack.db).unsafe(
226
+ `
214
227
  INSERT INTO test_notes (id, tenant_id, author_id, title, body)
215
- VALUES (${id}, ${tenantId}, ${userId}, ${title}, ${`body for ${title}`})
216
- `);
228
+ VALUES ($1, $2, $3, $4, $5)
229
+ `,
230
+ [id, tenantId, userId, title, `body for ${title}`],
231
+ );
217
232
  }
218
233
 
219
234
  async function fetchNotes(tenantId: string, userId: string): Promise<unknown[]> {
220
- const result = await stack.db.execute(sql`
221
- SELECT id, title FROM test_notes WHERE tenant_id = ${tenantId} AND author_id = ${userId}
222
- `);
235
+ const result = await asRawClient(stack.db).unsafe(
236
+ `
237
+ SELECT id, title FROM test_notes WHERE tenant_id = $1 AND author_id = $2
238
+ `,
239
+ [tenantId, userId],
240
+ );
223
241
  // biome-ignore lint/suspicious/noExplicitAny: drizzle execute typing
224
242
  return ((result as any).rows ?? result) as unknown[];
225
243
  }
@@ -319,24 +337,33 @@ describe("Cross-Data-Matrix :: Forget cleant 3 Provider-Features cross-tenant",
319
337
  expect(await fetchNotes(TENANT_B, ALICE_ID)).toHaveLength(0);
320
338
 
321
339
  // Alices fileRef in Tenant A weg
322
- const aliceFiles = await stack.db.execute(sql`
323
- SELECT id FROM file_refs WHERE tenant_id = ${TENANT_A} AND inserted_by_id = ${ALICE_ID}
324
- `);
340
+ const aliceFiles = await asRawClient(stack.db).unsafe(
341
+ `
342
+ SELECT id FROM file_refs WHERE tenant_id = $1 AND inserted_by_id = $2
343
+ `,
344
+ [TENANT_A, ALICE_ID],
345
+ );
325
346
  // biome-ignore lint/suspicious/noExplicitAny: drizzle execute typing
326
347
  expect((((aliceFiles as any).rows ?? aliceFiles) as unknown[]).length).toBe(0);
327
348
 
328
349
  // Bobs notes + files unangetastet
329
350
  expect(await fetchNotes(TENANT_A, BOB_ID)).toHaveLength(1);
330
- const bobFiles = await stack.db.execute(sql`
331
- SELECT id FROM file_refs WHERE tenant_id = ${TENANT_A} AND inserted_by_id = ${BOB_ID}
332
- `);
351
+ const bobFiles = await asRawClient(stack.db).unsafe(
352
+ `
353
+ SELECT id FROM file_refs WHERE tenant_id = $1 AND inserted_by_id = $2
354
+ `,
355
+ [TENANT_A, BOB_ID],
356
+ );
333
357
  // biome-ignore lint/suspicious/noExplicitAny: drizzle execute typing
334
358
  expect((((bobFiles as any).rows ?? bobFiles) as unknown[]).length).toBe(1);
335
359
 
336
360
  // Alice-User-Row anonymisiert (DSGVO-Kern: PII raus, Sentinel-Email).
337
- const aliceRow = await stack.db.execute(sql`
338
- SELECT email, status FROM read_users WHERE id = ${ALICE_ID}
339
- `);
361
+ const aliceRow = await asRawClient(stack.db).unsafe(
362
+ `
363
+ SELECT email, status FROM read_users WHERE id = $1
364
+ `,
365
+ [ALICE_ID],
366
+ );
340
367
  // biome-ignore lint/suspicious/noExplicitAny: drizzle execute typing
341
368
  const aliceRows = ((aliceRow as any).rows ?? aliceRow) as Array<{
342
369
  email: string;
@@ -7,7 +7,9 @@
7
7
  // Plus Audit-Updates (useCount, lastUsedAt, IP, UA), TTL-checks,
8
8
  // cross-user-isolation, cross-tenant-same-user.
9
9
 
10
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
10
11
  import { randomBytes } from "node:crypto";
12
+ import { asRawClient, selectMany, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
11
13
  import { createEncryptionProvider } from "@cosmicdrift/kumiko-framework/db";
12
14
  import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
13
15
  import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
@@ -24,8 +26,6 @@ import {
24
26
  unsafePushTables,
25
27
  } from "@cosmicdrift/kumiko-framework/stack";
26
28
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
27
- import { sql } from "drizzle-orm";
28
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
29
29
  import {
30
30
  createComplianceProfilesFeature,
31
31
  tenantComplianceProfileEntity,
@@ -127,7 +127,7 @@ beforeAll(async () => {
127
127
  await unsafeCreateEntityTable(stack.db, tenantComplianceProfileEntity);
128
128
  await unsafePushTables(stack.db, { configValuesTable });
129
129
  await createEventsTable(stack.db);
130
- await stack.db.execute(sql`
130
+ await asRawClient(stack.db).unsafe(`
131
131
  CREATE TABLE IF NOT EXISTS read_tenant_memberships (
132
132
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
133
133
  tenant_id UUID NOT NULL,
@@ -151,12 +151,12 @@ afterAll(async () => {
151
151
  });
152
152
 
153
153
  beforeEach(async () => {
154
- await stack.db.delete(exportDownloadTokensTable);
155
- await stack.db.delete(exportJobsTable);
156
- await stack.db.execute(sql`DELETE FROM kumiko_events`);
157
- await stack.db.execute(sql`DELETE FROM read_tenant_compliance_profiles`);
158
- await stack.db.execute(sql`DELETE FROM read_tenant_memberships`);
159
- await stack.db.execute(sql`DELETE FROM ${configValuesTable}`);
154
+ await asRawClient(stack.db).unsafe(`DELETE FROM "${exportDownloadTokensTable.tableName}"`);
155
+ await asRawClient(stack.db).unsafe(`DELETE FROM "${exportJobsTable.tableName}"`);
156
+ await asRawClient(stack.db).unsafe(`DELETE FROM kumiko_events`);
157
+ await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_compliance_profiles`);
158
+ await asRawClient(stack.db).unsafe(`DELETE FROM read_tenant_memberships`);
159
+ await asRawClient(stack.db).unsafe(`DELETE FROM $1`, [configValuesTable]);
160
160
  providerPerTenant = new Map();
161
161
 
162
162
  // Setup file-foundation provider="inmemory" pro Tenant.
@@ -242,10 +242,7 @@ describe("download-by-token :: happy path", () => {
242
242
  expect(result.expiresAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
243
243
 
244
244
  // Audit-Update: useCount=1, lastUsedAt set, IP+UA persistiert
245
- const tokenRows = (await stack.db
246
- .select()
247
- .from(exportDownloadTokensTable)
248
- .where(sql`job_id = ${jobId}`)) as Array<{
245
+ const tokenRows = (await selectMany(stack.db, exportDownloadTokensTable, { jobId })) as Array<{
249
246
  useCount: number;
250
247
  lastUsedAt: { toString(): string } | null;
251
248
  lastUsedFromIp: string | null;
@@ -271,10 +268,9 @@ describe("download-by-token :: happy path", () => {
271
268
  aliceUser,
272
269
  );
273
270
 
274
- const [row] = (await stack.db
275
- .select()
276
- .from(exportDownloadTokensTable)
277
- .where(sql`job_id = ${jobId}`)) as Array<{ useCount: number }>;
271
+ const [row] = (await selectMany(stack.db, exportDownloadTokensTable, { jobId })) as Array<{
272
+ useCount: number;
273
+ }>;
278
274
  expect(row?.useCount).toBe(2);
279
275
  });
280
276
  });
@@ -298,10 +294,7 @@ describe("download-by-token :: error paths", () => {
298
294
  const longAgo = getTemporal().Instant.fromEpochMilliseconds(
299
295
  Date.now() - 365 * 24 * 60 * 60 * 1000,
300
296
  );
301
- await stack.db
302
- .update(exportDownloadTokensTable)
303
- .set({ expiresAt: longAgo })
304
- .where(sql`job_id = ${jobId}`);
297
+ await updateMany(stack.db, exportDownloadTokensTable, { expiresAt: longAgo }, { jobId });
305
298
 
306
299
  const res = await stack.http.query(
307
300
  "user-data-rights:query:download-by-token",
@@ -315,7 +308,7 @@ describe("download-by-token :: error paths", () => {
315
308
 
316
309
  test("failed Job → 404 download.unavailable", async () => {
317
310
  const { jobId, plainToken } = await seedDoneJobWithToken();
318
- await stack.db.update(exportJobsTable).set({ status: "failed" }).where(sql`id = ${jobId}`);
311
+ await updateMany(stack.db, exportJobsTable, { status: "failed" }, { id: jobId });
319
312
 
320
313
  const res = await stack.http.query(
321
314
  "user-data-rights:query:download-by-token",
@@ -329,10 +322,7 @@ describe("download-by-token :: error paths", () => {
329
322
 
330
323
  test("storage cleared → 404 download.expired", async () => {
331
324
  const { jobId, plainToken } = await seedDoneJobWithToken();
332
- await stack.db
333
- .update(exportJobsTable)
334
- .set({ downloadStorageKey: null })
335
- .where(sql`id = ${jobId}`);
325
+ await updateMany(stack.db, exportJobsTable, { downloadStorageKey: null }, { id: jobId });
336
326
 
337
327
  const res = await stack.http.query(
338
328
  "user-data-rights:query:download-by-token",
@@ -368,10 +358,9 @@ describe("download-by-token :: error paths", () => {
368
358
  expect(body.error.i18nKey).toBe("userDataRights.errors.download.signedUrlNotSupported");
369
359
 
370
360
  // Sanity: Job-Row ist immer noch done — Operator-Bug aendert nicht den Job-State
371
- const [row] = (await stack.db
372
- .select()
373
- .from(exportJobsTable)
374
- .where(sql`id = ${jobId}`)) as Array<{ status: string }>;
361
+ const [row] = (await selectMany(stack.db, exportJobsTable, { id: jobId })) as Array<{
362
+ status: string;
363
+ }>;
375
364
  expect(row?.status).toBe("done");
376
365
  });
377
366
  });
@@ -392,10 +381,7 @@ describe("download-by-job :: happy path", () => {
392
381
  expect(result.url).toMatch(/^memory:\/\//);
393
382
 
394
383
  // UI-Klick zaehlt auch als Use → audit-row updated
395
- const [row] = (await stack.db
396
- .select()
397
- .from(exportDownloadTokensTable)
398
- .where(sql`job_id = ${jobId}`)) as Array<{
384
+ const [row] = (await selectMany(stack.db, exportDownloadTokensTable, { jobId })) as Array<{
399
385
  useCount: number;
400
386
  lastUsedFromIp: string | null;
401
387
  }>;
@@ -407,7 +393,7 @@ describe("download-by-job :: happy path", () => {
407
393
  // Symmetrisch zum token-Test: gleicher Code-Pfad muss auch im job-
408
394
  // handler 404 + unavailable raus, nicht 500.
409
395
  const { jobId } = await seedDoneJobWithToken();
410
- await stack.db.update(exportJobsTable).set({ status: "failed" }).where(sql`id = ${jobId}`);
396
+ await updateMany(stack.db, exportJobsTable, { status: "failed" }, { id: jobId });
411
397
 
412
398
  const res = await stack.http.query(
413
399
  "user-data-rights:query:download-by-job",
@@ -421,10 +407,7 @@ describe("download-by-job :: happy path", () => {
421
407
 
422
408
  test("storage cleared (downloadStorageKey null) → 404 download.expired (job-Pfad)", async () => {
423
409
  const { jobId } = await seedDoneJobWithToken();
424
- await stack.db
425
- .update(exportJobsTable)
426
- .set({ downloadStorageKey: null })
427
- .where(sql`id = ${jobId}`);
410
+ await updateMany(stack.db, exportJobsTable, { downloadStorageKey: null }, { id: jobId });
428
411
 
429
412
  const res = await stack.http.query(
430
413
  "user-data-rights:query:download-by-job",
@@ -488,10 +471,7 @@ describe("r.httpRoute :: /user-export/by-token (Magic-Link e2e)", () => {
488
471
  }),
489
472
  );
490
473
 
491
- const [row] = (await stack.db
492
- .select()
493
- .from(exportDownloadTokensTable)
494
- .where(sql`job_id = ${jobId}`)) as Array<{
474
+ const [row] = (await selectMany(stack.db, exportDownloadTokensTable, { jobId })) as Array<{
495
475
  useCount: number;
496
476
  lastUsedFromIp: string | null;
497
477
  lastUsedUserAgent: string | null;
@@ -549,11 +529,14 @@ describe("download-by-job :: cross-user + cross-tenant", () => {
549
529
  roles: ["Member"],
550
530
  });
551
531
  // Membership in Tenant B persisten damit auth-stack User akzeptiert
552
- await stack.db.execute(sql`
532
+ await asRawClient(stack.db).unsafe(
533
+ `
553
534
  INSERT INTO read_tenant_memberships (tenant_id, user_id)
554
- VALUES (${tenantB}, ${String(aliceUser.id)})
535
+ VALUES ($1, $2)
555
536
  ON CONFLICT (user_id, tenant_id) DO NOTHING
556
- `);
537
+ `,
538
+ [tenantB, String(aliceUser.id)],
539
+ );
557
540
 
558
541
  const result = await stack.http.queryOk<{ url: string }>(
559
542
  "user-data-rights:query:download-by-job",
@@ -9,14 +9,19 @@
9
9
  // in Atom 1a's pure unit-Test absichtlich ausgelassen wurde weil er
10
10
  // reale Postgres + Drizzle-customType-Codec-Path braucht.
11
11
 
12
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
13
+ import {
14
+ asRawClient,
15
+ insertOne,
16
+ selectMany,
17
+ updateMany,
18
+ } from "@cosmicdrift/kumiko-framework/bun-db";
12
19
  import {
13
20
  setupTestStack,
14
21
  type TestStack,
15
22
  unsafeCreateEntityTable,
16
23
  } from "@cosmicdrift/kumiko-framework/stack";
17
24
  import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
18
- import { eq, sql } from "drizzle-orm";
19
- import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
20
25
  import { createComplianceProfilesFeature } from "../../compliance-profiles";
21
26
  import { createDataRetentionFeature } from "../../data-retention";
22
27
  import { createUserFeature } from "../../user";
@@ -52,7 +57,7 @@ afterAll(async () => {
52
57
  });
53
58
 
54
59
  beforeEach(async () => {
55
- await stack.db.delete(exportJobsTable);
60
+ await asRawClient(stack.db).unsafe(`DELETE FROM "${exportJobsTable.tableName}"`);
56
61
  });
57
62
 
58
63
  const NOW = () => getTemporal().Now.instant();
@@ -101,7 +106,7 @@ async function insertJob(
101
106
  if (overrides.bytesWritten !== undefined) {
102
107
  values["bytesWritten"] = overrides.bytesWritten;
103
108
  }
104
- await stack.db.insert(exportJobsTable).values(values);
109
+ await insertOne(stack.db, exportJobsTable, values);
105
110
  return id;
106
111
  }
107
112
 
@@ -114,7 +119,7 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
114
119
  IDEMPOTENCY_CONSTRAINT,
115
120
  );
116
121
 
117
- const rows = await stack.db.select().from(exportJobsTable);
122
+ const rows = await selectMany(stack.db, exportJobsTable);
118
123
  expect(rows).toHaveLength(1);
119
124
  });
120
125
 
@@ -128,7 +133,7 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
128
133
  IDEMPOTENCY_CONSTRAINT,
129
134
  );
130
135
 
131
- const rows = await stack.db.select().from(exportJobsTable);
136
+ const rows = await selectMany(stack.db, exportJobsTable);
132
137
  expect(rows).toHaveLength(1);
133
138
  });
134
139
 
@@ -136,7 +141,7 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
136
141
  await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Pending);
137
142
  await insertJob(BOB_ID, EXPORT_JOB_STATUS.Pending);
138
143
 
139
- const rows = await stack.db.select().from(exportJobsTable);
144
+ const rows = await selectMany(stack.db, exportJobsTable);
140
145
  expect(rows).toHaveLength(2);
141
146
  });
142
147
 
@@ -147,7 +152,7 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
147
152
  // erfolgreichem Download
148
153
  await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Pending);
149
154
 
150
- const rows = await stack.db.select().from(exportJobsTable);
155
+ const rows = await selectMany(stack.db, exportJobsTable);
151
156
  expect(rows).toHaveLength(2);
152
157
  });
153
158
 
@@ -155,7 +160,7 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
155
160
  await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Failed);
156
161
  await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Pending);
157
162
 
158
- const rows = await stack.db.select().from(exportJobsTable);
163
+ const rows = await selectMany(stack.db, exportJobsTable);
159
164
  expect(rows).toHaveLength(2);
160
165
  });
161
166
 
@@ -163,7 +168,7 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
163
168
  await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Done);
164
169
  await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Done);
165
170
 
166
- const rows = await stack.db.select().from(exportJobsTable);
171
+ const rows = await selectMany(stack.db, exportJobsTable);
167
172
  expect(rows).toHaveLength(2);
168
173
  });
169
174
 
@@ -171,10 +176,12 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
171
176
  // Alice pending insert
172
177
  const aliceJobId = await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Pending);
173
178
  // Worker pickt auf — Status flip
174
- await stack.db
175
- .update(exportJobsTable)
176
- .set({ status: EXPORT_JOB_STATUS.Running })
177
- .where(eq(exportJobsTable.id, aliceJobId));
179
+ await updateMany(
180
+ stack.db,
181
+ exportJobsTable,
182
+ { status: EXPORT_JOB_STATUS.Running },
183
+ { id: aliceJobId },
184
+ );
178
185
 
179
186
  // Zweiter pending-Insert fuer Alice → faellt weiter, weil bestehender
180
187
  // Job in running auch im Index-Filter ist.
@@ -184,13 +191,15 @@ describe("ExportJob :: Partial-UNIQUE-Index", () => {
184
191
  );
185
192
 
186
193
  // Aber wenn der running-Job fertig wird (done), darf User wieder pending starten
187
- await stack.db
188
- .update(exportJobsTable)
189
- .set({ status: EXPORT_JOB_STATUS.Done })
190
- .where(eq(exportJobsTable.id, aliceJobId));
194
+ await updateMany(
195
+ stack.db,
196
+ exportJobsTable,
197
+ { status: EXPORT_JOB_STATUS.Done },
198
+ { id: aliceJobId },
199
+ );
191
200
 
192
201
  await insertJob(ALICE_ID, EXPORT_JOB_STATUS.Pending);
193
- const rows = await stack.db.select().from(exportJobsTable);
202
+ const rows = await selectMany(stack.db, exportJobsTable);
194
203
  expect(rows).toHaveLength(2);
195
204
  });
196
205
  });
@@ -202,10 +211,9 @@ describe("ExportJob :: bigInt bytesWritten DB-Roundtrip", () => {
202
211
  bytesWritten: TWO_GB_PLUS,
203
212
  });
204
213
 
205
- const [row] = await stack.db
206
- .select({ bytesWritten: exportJobsTable["bytesWritten"] })
207
- .from(exportJobsTable)
208
- .where(eq(exportJobsTable["id"], id));
214
+ const [row] = (await selectMany(stack.db, exportJobsTable, { id })) as Array<{
215
+ bytesWritten: number;
216
+ }>;
209
217
 
210
218
  expect(row?.bytesWritten).toBe(TWO_GB_PLUS);
211
219
  });
@@ -216,10 +224,9 @@ describe("ExportJob :: bigInt bytesWritten DB-Roundtrip", () => {
216
224
  bytesWritten: ONE_PB_PLUS,
217
225
  });
218
226
 
219
- const [row] = await stack.db
220
- .select({ bytesWritten: exportJobsTable["bytesWritten"] })
221
- .from(exportJobsTable)
222
- .where(eq(exportJobsTable["id"], id));
227
+ const [row] = (await selectMany(stack.db, exportJobsTable, { id })) as Array<{
228
+ bytesWritten: number;
229
+ }>;
223
230
 
224
231
  expect(row?.bytesWritten).toBe(ONE_PB_PLUS);
225
232
  });
@@ -232,7 +239,7 @@ describe("ExportJob :: bigInt bytesWritten DB-Roundtrip", () => {
232
239
  // Driver-Mapping — wenn jemand `case "bigInt"` zu `integer` zurueck-
233
240
  // refactored, faellt dieser Test um obwohl die JS-Werte sich
234
241
  // numerisch verhalten.
235
- const result = await stack.db.execute(sql`
242
+ const result = await asRawClient(stack.db).unsafe(`
236
243
  SELECT data_type FROM information_schema.columns
237
244
  WHERE table_name = 'read_export_jobs' AND column_name = 'bytes_written'
238
245
  `);
@@ -13,8 +13,8 @@
13
13
  // Schema-Snapshot, kein Behavior-Test. Behavior-Tests kommen mit Atom 2
14
14
  // (request-export.write.ts) + Atom 3 (Worker).
15
15
 
16
+ import { describe, expect, test } from "bun:test";
16
17
  import { COMPLIANCE_PROFILES } from "@cosmicdrift/kumiko-framework/compliance";
17
- import { describe, expect, test } from "vitest";
18
18
  import { EXPORT_JOB_STATUS, exportJobEntity } from "../schema/export-job";
19
19
 
20
20
  describe("EXPORT_JOB_STATUS Drift-Guard", () => {
@@ -32,7 +32,7 @@ describe("EXPORT_JOB_STATUS Drift-Guard", () => {
32
32
  // alphabetische Sortierung — die State-Machine kuemmert sich nicht
33
33
  // um Sort-Order, ein Re-Order der Konstanten waere kein Bug.
34
34
  for (const value of Object.values(EXPORT_JOB_STATUS)) {
35
- expect(value).toBe(value.toLowerCase());
35
+ expect(value as string).toBe(value.toLowerCase());
36
36
  expect(value).toMatch(/^[a-z]+$/);
37
37
  }
38
38
  });
@@ -4,7 +4,7 @@
4
4
  // werden. Memory `feedback_no_fake_tests`: das Mapping IST die Logik,
5
5
  // nicht ein Detail.
6
6
 
7
- import { describe, expect, test } from "vitest";
7
+ import { describe, expect, test } from "bun:test";
8
8
  import { policyToStrategy } from "../run-forget-cleanup";
9
9
 
10
10
  describe("policyToStrategy", () => {