@cosmicdrift/kumiko-bundled-features 0.307.0 → 0.308.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/package.json +9 -9
  2. package/src/__tests__/access-invalidation-consumer.integration.test.ts +107 -0
  3. package/src/__tests__/access-invalidation-stream.integration.test.ts +1 -1
  4. package/src/__tests__/credential-scoped-sse-revoke.integration.test.ts +296 -0
  5. package/src/agent-tools/__tests__/agent-doc-lint.test.ts +1 -1
  6. package/src/agent-tools/__tests__/agent-manifest.test.ts +1 -1
  7. package/src/agent-tools/__tests__/agent-optout.test.ts +1 -1
  8. package/src/agent-tools/__tests__/agent-tools.integration.test.ts +1 -1
  9. package/src/agent-tools/__tests__/feature.test.ts +1 -1
  10. package/src/agent-tools/__tests__/tool-catalog.test.ts +1 -1
  11. package/src/agent-tools/agent-manifest.ts +1 -1
  12. package/src/audit/__tests__/escape-hatch-audit.integration.test.ts +1 -1
  13. package/src/audit/escape-hatch-audit-sink.ts +1 -1
  14. package/src/audit/handlers/details.query.ts +1 -1
  15. package/src/audit/handlers/list.query.ts +1 -1
  16. package/src/auth-email-password/__tests__/query-as-member.integration.test.ts +1 -1
  17. package/src/auth-email-password/feature.ts +1 -1
  18. package/src/auth-email-password/handlers/change-password.write.ts +1 -1
  19. package/src/auth-email-password/handlers/confirm-account-unlock.write.ts +1 -1
  20. package/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
  21. package/src/auth-email-password/handlers/invite-accept.write.ts +1 -1
  22. package/src/auth-email-password/handlers/invite-create.write.ts +1 -1
  23. package/src/auth-email-password/handlers/invite-signup-complete.write.ts +1 -1
  24. package/src/auth-email-password/handlers/login.write.ts +1 -1
  25. package/src/auth-email-password/handlers/logout.write.ts +1 -1
  26. package/src/auth-email-password/handlers/reset-password.write.ts +1 -1
  27. package/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
  28. package/src/auth-email-password/handlers/signup-confirm.write.ts +1 -1
  29. package/src/auth-email-password/handlers/signup-request.write.ts +1 -1
  30. package/src/auth-email-password/handlers/token-request-handler.ts +1 -1
  31. package/src/auth-email-password/handlers/verify-email.write.ts +1 -1
  32. package/src/auth-email-password/password-policy.ts +1 -1
  33. package/src/auth-mfa/__tests__/session-scoped-sse-revoke.integration.test.ts +1 -1
  34. package/src/auth-mfa/handlers/disable.write.ts +1 -1
  35. package/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +1 -1
  36. package/src/auth-mfa/handlers/enable-confirm.write.ts +1 -1
  37. package/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
  38. package/src/auth-mfa/handlers/enable-start.write.ts +1 -1
  39. package/src/auth-mfa/handlers/regenerate-recovery.write.ts +1 -1
  40. package/src/auth-mfa/handlers/status.query.ts +1 -1
  41. package/src/auth-mfa/handlers/verify.write.ts +1 -1
  42. package/src/billing-foundation/billing-info-query.ts +1 -1
  43. package/src/billing-foundation/events.ts +1 -1
  44. package/src/billing-foundation/handlers/create-checkout-session.write.ts +1 -1
  45. package/src/billing-foundation/handlers/create-portal-session.write.ts +1 -1
  46. package/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
  47. package/src/billing-foundation/handlers/process-event.write.ts +1 -1
  48. package/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
  49. package/src/cap-counter/__tests__/cap-counter.integration.test.ts +1 -1
  50. package/src/cap-counter/__tests__/stock-cap-guard.integration.test.ts +1 -1
  51. package/src/cap-counter/__tests__/with-cap-enforcement.integration.test.ts +1 -1
  52. package/src/cap-counter/book-cap-usage.ts +1 -1
  53. package/src/cap-counter/handlers/get-counter.query.ts +1 -1
  54. package/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
  55. package/src/cap-counter/handlers/increment.write.ts +1 -1
  56. package/src/cap-counter/handlers/mark-soft-warned.write.ts +1 -1
  57. package/src/cap-overview/handlers/caps-usage.query.ts +1 -1
  58. package/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  59. package/src/cap-overview/handlers/tenant-options.query.ts +1 -1
  60. package/src/channel-in-app/handlers/inbox.query.ts +1 -1
  61. package/src/channel-in-app/handlers/mark-all-read.write.ts +1 -1
  62. package/src/channel-in-app/handlers/mark-read.write.ts +1 -1
  63. package/src/channel-in-app/handlers/unread-count.query.ts +1 -1
  64. package/src/compliance-profiles/handlers/for-tenant.query.ts +1 -1
  65. package/src/compliance-profiles/handlers/list-profiles.query.ts +1 -1
  66. package/src/compliance-profiles/handlers/needs-profile.query.ts +1 -1
  67. package/src/compliance-profiles/handlers/set-profile.write.ts +1 -1
  68. package/src/compliance-profiles/handlers/sub-processors.query.ts +1 -1
  69. package/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +1 -1
  70. package/src/config/__tests__/backing-secrets.integration.test.ts +1 -1
  71. package/src/config/__tests__/config.integration.test.ts +1 -1
  72. package/src/config/__tests__/tz-resolution.integration.test.ts +1 -1
  73. package/src/config/handlers/cascade.query.ts +1 -1
  74. package/src/config/handlers/readiness.query.ts +1 -1
  75. package/src/config/handlers/reset.write.ts +1 -1
  76. package/src/config/handlers/schema.query.ts +1 -1
  77. package/src/config/handlers/set.write.ts +1 -1
  78. package/src/config/handlers/values.query.ts +1 -1
  79. package/src/crypto-shredding/handlers/forget-subject.write.ts +1 -1
  80. package/src/custom-fields/__tests__/audit-integration.integration.test.ts +1 -1
  81. package/src/custom-fields/__tests__/cross-tenant-field-delete.integration.test.ts +1 -1
  82. package/src/custom-fields/__tests__/cross-tenant-set-write.integration.test.ts +1 -1
  83. package/src/custom-fields/__tests__/custom-fields.integration.test.ts +1 -1
  84. package/src/custom-fields/__tests__/field-access.integration.test.ts +1 -1
  85. package/src/custom-fields/__tests__/quota.integration.test.ts +1 -1
  86. package/src/custom-fields/__tests__/retention.integration.test.ts +1 -1
  87. package/src/custom-fields/__tests__/user-data-rights.integration.test.ts +1 -1
  88. package/src/custom-fields/events.ts +1 -1
  89. package/src/custom-fields/feature.ts +1 -1
  90. package/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
  91. package/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
  92. package/src/custom-fields/lib/value-schema.ts +1 -1
  93. package/src/custom-fields/schemas.ts +1 -1
  94. package/src/data-retention/handlers/policy-for.query.ts +1 -1
  95. package/src/data-retention/override-schema.ts +1 -1
  96. package/src/delivery/__tests__/delivery-pii-kms.integration.test.ts +1 -1
  97. package/src/delivery/__tests__/delivery.integration.test.ts +1 -1
  98. package/src/delivery/events.ts +1 -1
  99. package/src/delivery/feature.ts +1 -1
  100. package/src/delivery/handlers/log.query.ts +1 -1
  101. package/src/delivery/handlers/preferences.query.ts +1 -1
  102. package/src/delivery/handlers/set-preference.write.ts +1 -1
  103. package/src/delivery/jobs.ts +1 -1
  104. package/src/delivery/unsubscribe.ts +1 -1
  105. package/src/derivatives-sharp/__tests__/derivatives-sharp.integration.test.ts +1 -1
  106. package/src/document-ingest-foundation/events.ts +1 -1
  107. package/src/document-ingest-foundation/feature.ts +1 -1
  108. package/src/feature-toggles/__tests__/feature-toggles.integration.test.ts +1 -1
  109. package/src/feature-toggles/events.ts +1 -1
  110. package/src/feature-toggles/handlers/list.query.ts +1 -1
  111. package/src/feature-toggles/handlers/registered.query.ts +1 -1
  112. package/src/feature-toggles/handlers/set.write.ts +1 -1
  113. package/src/file-derivatives/handlers/public-variant.query.ts +1 -1
  114. package/src/file-foundation/__tests__/file-foundation.integration.test.ts +1 -1
  115. package/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +1 -1
  116. package/src/folders/handlers/delete-folder.write.ts +1 -1
  117. package/src/folders/schemas.ts +1 -1
  118. package/src/form-draft/schemas.ts +1 -1
  119. package/src/inbound-mail-foundation/events.ts +1 -1
  120. package/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
  121. package/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
  122. package/src/inbound-mail-foundation/handlers/ingest-message.write.ts +1 -1
  123. package/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
  124. package/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
  125. package/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
  126. package/src/inbound-provider-imap/credential-document.ts +1 -1
  127. package/src/jobs/__tests__/jobs-catalog.integration.test.ts +1 -1
  128. package/src/jobs/__tests__/tenant-job-failures.integration.test.ts +1 -1
  129. package/src/jobs/events.ts +1 -1
  130. package/src/jobs/handlers/catalog.query.ts +1 -1
  131. package/src/jobs/handlers/detail.query.ts +1 -1
  132. package/src/jobs/handlers/list.query.ts +1 -1
  133. package/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  134. package/src/jobs/handlers/reindex-entity.job.ts +1 -1
  135. package/src/jobs/handlers/retry.write.ts +1 -1
  136. package/src/jobs/handlers/tenant-failures.query.ts +1 -1
  137. package/src/jobs/handlers/trigger.write.ts +1 -1
  138. package/src/ledger/handlers/reports.query.ts +1 -1
  139. package/src/ledger/schemas.ts +1 -1
  140. package/src/mail-foundation/__tests__/mail-foundation.integration.test.ts +1 -1
  141. package/src/managed-pages/handlers/branding.query.ts +1 -1
  142. package/src/managed-pages/handlers/by-slug.query.ts +1 -1
  143. package/src/managed-pages/handlers/by-tenant-published.query.ts +1 -1
  144. package/src/managed-pages/handlers/set.write.ts +1 -1
  145. package/src/notes-history/changes.json +7 -0
  146. package/src/notes-history/schemas.ts +1 -1
  147. package/src/notes-history/web/notes-section.tsx +37 -36
  148. package/src/personal-access-tokens/feature.ts +10 -0
  149. package/src/personal-access-tokens/handlers/available-scopes.query.ts +1 -1
  150. package/src/personal-access-tokens/handlers/create.write.ts +1 -1
  151. package/src/personal-access-tokens/handlers/list.query.ts +1 -1
  152. package/src/personal-access-tokens/handlers/revoke.write.ts +18 -2
  153. package/src/personal-access-tokens/index.ts +7 -0
  154. package/src/personal-access-tokens/pat-revoked-event.ts +23 -0
  155. package/src/personal-access-tokens/revoke-for-user.ts +31 -0
  156. package/src/rate-limiting/__tests__/rate-limiting.integration.test.ts +1 -1
  157. package/src/rate-limiting/handlers/status.query.ts +1 -1
  158. package/src/readiness/handlers/status.query.ts +1 -1
  159. package/src/secrets/feature.ts +1 -1
  160. package/src/secrets/handlers/delete.write.ts +1 -1
  161. package/src/secrets/handlers/list.query.ts +1 -1
  162. package/src/secrets/handlers/set.write.ts +1 -1
  163. package/src/secrets/secrets-context.ts +1 -1
  164. package/src/seo/handlers/seo-config.query.ts +1 -1
  165. package/src/sessions/handlers/detail.query.ts +1 -1
  166. package/src/sessions/handlers/list.query.ts +1 -1
  167. package/src/sessions/handlers/mine.query.ts +1 -1
  168. package/src/sessions/handlers/revoke-all-for-user.write.ts +1 -1
  169. package/src/sessions/handlers/revoke-all-others.write.ts +1 -1
  170. package/src/sessions/handlers/revoke.write.ts +1 -1
  171. package/src/sessions/session-callbacks.ts +25 -1
  172. package/src/sessions/session-revoked-event.ts +6 -1
  173. package/src/shared/__tests__/row-bound-grant.integration.test.ts +1 -1
  174. package/src/step-dispatcher/mail-runner.ts +1 -1
  175. package/src/step-dispatcher/webhook-runner.ts +1 -1
  176. package/src/subscription-mollie/feature.ts +1 -1
  177. package/src/subscription-stripe/feature.ts +1 -1
  178. package/src/tags/schemas.ts +1 -1
  179. package/src/template-resolver/handlers/by-slug.query.ts +1 -1
  180. package/src/template-resolver/handlers/by-tenant.query.ts +1 -1
  181. package/src/template-resolver/handlers/collection-item.query.ts +1 -1
  182. package/src/template-resolver/handlers/collection-list.query.ts +1 -1
  183. package/src/template-resolver/handlers/collection-set.write.ts +1 -1
  184. package/src/template-resolver/handlers/find-by-id.query.ts +1 -1
  185. package/src/template-resolver/handlers/list.query.ts +1 -1
  186. package/src/template-resolver/handlers/set.write.ts +1 -1
  187. package/src/template-resolver/handlers/shared.ts +1 -1
  188. package/src/template-resolver/handlers/toggle-status.write.ts +1 -1
  189. package/src/template-resolver/handlers/upsert-tenant.write.ts +1 -1
  190. package/src/tenant/__tests__/tenant-timezone-boot.integration.test.ts +1 -1
  191. package/src/tenant/handlers/active-tenant-ids.query.ts +1 -1
  192. package/src/tenant/handlers/add-member.write.ts +1 -1
  193. package/src/tenant/handlers/cancel-invitation.write.ts +1 -1
  194. package/src/tenant/handlers/create.write.ts +1 -1
  195. package/src/tenant/handlers/invitations.query.ts +1 -1
  196. package/src/tenant/handlers/list.query.ts +1 -1
  197. package/src/tenant/handlers/me.query.ts +1 -1
  198. package/src/tenant/handlers/member-directory.query.ts +1 -1
  199. package/src/tenant/handlers/members.query.ts +1 -1
  200. package/src/tenant/handlers/memberships.query.ts +1 -1
  201. package/src/tenant/handlers/remove-member.write.ts +1 -1
  202. package/src/tenant/handlers/resolve-user-ids.query.ts +1 -1
  203. package/src/tenant/handlers/team-list.query.ts +1 -1
  204. package/src/tenant/handlers/tenant-directory.query.ts +1 -1
  205. package/src/tenant/handlers/toggle-enabled.write.ts +1 -1
  206. package/src/tenant/handlers/update-member-roles.write.ts +1 -1
  207. package/src/tenant/handlers/update.write.ts +1 -1
  208. package/src/tenant-handover/events.ts +1 -1
  209. package/src/tenant-handover/handlers/claim.write.ts +1 -1
  210. package/src/tenant-lifecycle/events.ts +1 -1
  211. package/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +1 -1
  212. package/src/tenant-lifecycle/handlers/request-destruction.write.ts +1 -1
  213. package/src/tenant-settings/tenant-defaults.ts +1 -1
  214. package/src/tier-engine/__tests__/tier-resolver.integration.test.ts +1 -1
  215. package/src/tier-engine/feature.ts +1 -1
  216. package/src/tier-engine/handlers/active-tier.query.ts +1 -1
  217. package/src/tier-engine/handlers/get-tenant-tier.query.ts +1 -1
  218. package/src/tier-engine/handlers/set-tenant-tier.write.ts +1 -1
  219. package/src/user/__tests__/user-global-tenancy.integration.test.ts +1 -1
  220. package/src/user/handlers/create.write.ts +1 -1
  221. package/src/user/handlers/find-for-auth.query.ts +1 -1
  222. package/src/user/handlers/me.query.ts +1 -1
  223. package/src/user/handlers/update.write.ts +1 -1
  224. package/src/user/roles-input-schema.ts +1 -1
  225. package/src/user-data-rights/__tests__/boot-checks.test.ts +1 -1
  226. package/src/user-data-rights/changes.json +6 -0
  227. package/src/user-data-rights/feature.ts +1 -0
  228. package/src/user-data-rights/handlers/cancel-deletion.write.ts +1 -1
  229. package/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
  230. package/src/user-data-rights/handlers/download-by-job.query.ts +1 -1
  231. package/src/user-data-rights/handlers/download-by-token.query.ts +1 -1
  232. package/src/user-data-rights/handlers/export-status.query.ts +1 -1
  233. package/src/user-data-rights/handlers/lift-restriction.write.ts +1 -1
  234. package/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -1
  235. package/src/user-data-rights/handlers/my-audit-log.query.ts +1 -1
  236. package/src/user-data-rights/handlers/request-deletion-by-email.write.ts +1 -1
  237. package/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
  238. package/src/user-data-rights/handlers/request-export.write.ts +1 -1
  239. package/src/user-data-rights/handlers/restrict-account.write.ts +1 -1
  240. package/src/user-data-rights/handlers/run-forget-cleanup.write.ts +1 -1
  241. package/src/user-profile/handlers/change-email.write.ts +1 -1
  242. package/src/workflow-runner/__tests__/pending-projection.integration.test.ts +1 -1
  243. package/src/workflow-runner/handlers/resume-run.write.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-bundled-features",
3
- "version": "0.307.0",
3
+ "version": "0.308.0",
4
4
  "description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -133,12 +133,12 @@
133
133
  "./workflow-runner": "./src/workflow-runner/index.ts"
134
134
  },
135
135
  "dependencies": {
136
- "@cosmicdrift/kumiko-dispatcher-live": "0.307.0",
137
- "@cosmicdrift/kumiko-framework": "0.307.0",
138
- "@cosmicdrift/kumiko-headless": "0.307.0",
139
- "@cosmicdrift/kumiko-renderer": "0.307.0",
140
- "@cosmicdrift/kumiko-renderer-web": "0.307.0",
141
- "@cosmicdrift/kumiko-types": "0.307.0",
136
+ "@cosmicdrift/kumiko-dispatcher-live": "0.308.0",
137
+ "@cosmicdrift/kumiko-framework": "0.308.0",
138
+ "@cosmicdrift/kumiko-headless": "0.308.0",
139
+ "@cosmicdrift/kumiko-renderer": "0.308.0",
140
+ "@cosmicdrift/kumiko-renderer-web": "0.308.0",
141
+ "@cosmicdrift/kumiko-types": "0.308.0",
142
142
  "@mollie/api-client": "^4.5.0",
143
143
  "@node-rs/argon2": "^2.0.2",
144
144
  "@types/mailparser": "^3.4.6",
@@ -167,8 +167,8 @@
167
167
  ],
168
168
  "devDependencies": {
169
169
  "@testing-library/user-event": "^14.6.1",
170
- "@cosmicdrift/kumiko-locale-de": "0.307.0",
171
- "@cosmicdrift/kumiko-locale-es": "0.307.0",
170
+ "@cosmicdrift/kumiko-locale-de": "0.308.0",
171
+ "@cosmicdrift/kumiko-locale-es": "0.308.0",
172
172
  "jsqr": "^1.4.0"
173
173
  }
174
174
  }
@@ -44,6 +44,11 @@ import { createAuthEmailPasswordFeature } from "../auth-email-password/feature";
44
44
  import { createConfigFeature } from "../config";
45
45
  import { createConfigResolver } from "../config/resolver";
46
46
  import { configValuesTable } from "../config/table";
47
+ import { PatHandlers } from "../personal-access-tokens/constants";
48
+ import { createPersonalAccessTokensFeature } from "../personal-access-tokens/feature";
49
+ import { revokeAllPatTokensForUser } from "../personal-access-tokens/revoke-for-user";
50
+ import { apiTokenEntity, apiTokenTable } from "../personal-access-tokens/schema/api-token";
51
+ import type { PatScopeConfig } from "../personal-access-tokens/scopes";
47
52
  import { makeSessionHelpers } from "../sessions/__tests__/test-helpers";
48
53
  import { SessionHandlers } from "../sessions/constants";
49
54
  import { createSessionsFeature } from "../sessions/feature";
@@ -66,6 +71,10 @@ const callbacks = createLateBoundHolder<SessionCallbacks>("session-callbacks");
66
71
  const encryptionKey = randomBytes(32).toString("base64");
67
72
  const TENANT = testTenantId(1);
68
73
 
74
+ const PAT_SCOPES: PatScopeConfig = {
75
+ probe: { label: "Probe", read: ["personal-access-tokens:query:mine"] },
76
+ };
77
+
69
78
  // Collects every userId the consumer pushed an invalidation for during the
70
79
  // running test, in push order. Reset in beforeEach.
71
80
  let invalidated: string[];
@@ -90,6 +99,10 @@ beforeAll(async () => {
90
99
  createAuthEmailPasswordFeature(),
91
100
  authFoundationFeature,
92
101
  createSessionsFeature({ autoRevokeOnPasswordChange: bound.asMassRevoker() }),
102
+ createPersonalAccessTokensFeature({
103
+ scopes: PAT_SCOPES,
104
+ autoRevokeOnPasswordChange: (userId) => revokeAllPatTokensForUser(stack.db, userId),
105
+ }),
93
106
  ],
94
107
  extraContext: { configResolver: resolver, configEncryption: encryption },
95
108
  authConfig: {
@@ -107,6 +120,7 @@ beforeAll(async () => {
107
120
  await unsafeCreateEntityTable(stack.db, userEntity);
108
121
  await unsafeCreateEntityTable(stack.db, tenantEntity);
109
122
  await unsafeCreateEntityTable(stack.db, userSessionEntity);
123
+ await unsafeCreateEntityTable(stack.db, apiTokenEntity);
110
124
  await unsafePushTables(stack.db, { configValuesTable, tenantMembershipsTable });
111
125
  });
112
126
 
@@ -119,6 +133,7 @@ beforeEach(async () => {
119
133
  userTable,
120
134
  tenantMembershipsTable,
121
135
  userSessionTable,
136
+ apiTokenTable,
122
137
  eventsTable,
123
138
  ]);
124
139
  invalidated = [];
@@ -309,4 +324,96 @@ describe("access-invalidation event consumer", () => {
309
324
  expect(sessionRow).toBeTruthy();
310
325
  expect(sessionRow?.revokedAt).not.toBeNull();
311
326
  });
327
+
328
+ test("session-revoked via logout (sessionRevoker, streamScope: revoked-sessions) still pushes an invalidation for that user", async () => {
329
+ const { userId } = await h.seedUser("logout-invalidates@example.com", "pw-long-enough");
330
+ const { token } = await h.login("logout-invalidates@example.com", "pw-long-enough");
331
+ trackInvalidation(userId);
332
+
333
+ // The raw sessionRevoker callback behind /api/auth/logout used to append
334
+ // no event at all — it now sets streamScope: "revoked-sessions"
335
+ // instead of the old userwide default, but a broad
336
+ // subscribeAccessInvalidation listener (no credential passed here, same
337
+ // as every other test in this file) still fires: this proves the new
338
+ // narrow scope still reaches the consumer, not just the pre-existing
339
+ // userwide paths above.
340
+ const logoutRes = await h.authedPost("/api/auth/logout", token);
341
+ expect(logoutRes.status).toBe(200);
342
+
343
+ const events = await selectMany(stack.db, eventsTable, {
344
+ type: "sessions:event:session-revoked",
345
+ });
346
+ expect(events.length).toBeGreaterThan(0);
347
+
348
+ await stack.eventDispatcher?.runOnce();
349
+
350
+ expect(invalidated).toEqual([userId]);
351
+ });
352
+
353
+ test("pat-revoked (self-service PAT revoke) pushes an invalidation for that user", async () => {
354
+ const { userId } = await h.seedUser("pat-revoke-invalidates@example.com", "pw-long-enough");
355
+ const { token } = await h.login("pat-revoke-invalidates@example.com", "pw-long-enough");
356
+ trackInvalidation(userId);
357
+
358
+ const mint = await h.authedPost("/api/write", token, {
359
+ type: PatHandlers.create,
360
+ payload: {
361
+ name: "probe",
362
+ scopes: ["probe:read"],
363
+ currentPassword: "pw-long-enough",
364
+ },
365
+ });
366
+ expect(mint.status).toBe(200);
367
+ const { data } = (await mint.json()) as { data: { id: string } };
368
+
369
+ const revokeRes = await h.authedPost("/api/write", token, {
370
+ type: PatHandlers.revoke,
371
+ payload: { id: data.id },
372
+ });
373
+ expect(revokeRes.status).toBe(200);
374
+
375
+ const events = await selectMany(stack.db, eventsTable, {
376
+ type: "personal-access-tokens:event:pat-revoked",
377
+ });
378
+ expect(events.length).toBeGreaterThan(0);
379
+
380
+ await stack.eventDispatcher?.runOnce();
381
+
382
+ expect(invalidated).toEqual([userId]);
383
+ });
384
+
385
+ test("revokeAllPatTokensForUser (password-change auto-revoke) pushes an invalidation for that user", async () => {
386
+ const { userId } = await h.seedUser("pat-mass-invalidates@example.com", "old-pw-long-enough");
387
+ const { token } = await h.login("pat-mass-invalidates@example.com", "old-pw-long-enough");
388
+ trackInvalidation(userId);
389
+
390
+ const mint = await h.authedPost("/api/write", token, {
391
+ type: PatHandlers.create,
392
+ payload: {
393
+ name: "probe",
394
+ scopes: ["probe:read"],
395
+ currentPassword: "old-pw-long-enough",
396
+ },
397
+ });
398
+ expect(mint.status).toBe(200);
399
+
400
+ const res = await h.authedPost("/api/write", token, {
401
+ type: AuthHandlers.changePassword,
402
+ payload: { oldPassword: "old-pw-long-enough", newPassword: "new-pw-long-enough" },
403
+ });
404
+ expect(res.status).toBe(200);
405
+
406
+ const events = await selectMany(stack.db, eventsTable, {
407
+ type: "personal-access-tokens:event:pat-revoked",
408
+ });
409
+ expect(events.length).toBeGreaterThan(0);
410
+
411
+ await stack.eventDispatcher?.runOnce();
412
+
413
+ // A password change fires both sessionMassRevoker (userwide,
414
+ // session-revoked) and this suite's PAT autoRevokeOnPasswordChange
415
+ // (pat-tokens-scoped, pat-revoked) — the broad listener here (no
416
+ // credential passed) fires once per publish, so twice for the same user.
417
+ expect(invalidated).toEqual([userId, userId]);
418
+ });
312
419
  });
@@ -26,7 +26,7 @@ import {
26
26
  createTestEnvelopeCipher,
27
27
  resetTestTables,
28
28
  } from "@cosmicdrift/kumiko-framework/testing";
29
- import { z } from "zod";
29
+ import * as z from "zod";
30
30
  import { AuthHandlers } from "../auth-email-password/constants";
31
31
  import { createAuthEmailPasswordFeature } from "../auth-email-password/feature";
32
32
  import { createConfigFeature } from "../config";
@@ -0,0 +1,296 @@
1
+ // Credential-scoped SSE revoke.
2
+ //
3
+ // Logout/tenant-switch (sessionRevoker, session-callbacks.ts) and PAT revoke
4
+ // (revoke.write.ts / revokeAllPatTokensForUser) used to make no event at
5
+ // all, so the access-invalidation consumer never heard about them and an
6
+ // already-open SSE stream on that exact credential survived until its
7
+ // JWT/token expired on its own. Both now append a scoped session-revoked /
8
+ // pat-revoked event and the consumer closes exactly the matching stream(s),
9
+ // leaving every other credential's stream of the same user open.
10
+ //
11
+ // Real stack throughout: setupTestStack + real HTTP (createLiveDispatcher for
12
+ // the SSE streams, stack.http/h.authedPost for the writes), never
13
+ // createTestDispatcher.
14
+
15
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
16
+ import { randomBytes } from "node:crypto";
17
+ import {
18
+ authFoundationFeature,
19
+ resolveTokenVerifier,
20
+ } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";
21
+ import { createLiveDispatcher } from "@cosmicdrift/kumiko-dispatcher-live";
22
+ import type { SessionCreator } from "@cosmicdrift/kumiko-framework/api";
23
+ import { createInMemoryLoginRateLimiter, generateToken } from "@cosmicdrift/kumiko-framework/api";
24
+ import { defineFeature } from "@cosmicdrift/kumiko-framework/engine";
25
+ import { eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
26
+ import {
27
+ setupTestStack,
28
+ type TestStack,
29
+ testTenantId,
30
+ unsafeCreateEntityTable,
31
+ unsafePushTables,
32
+ } from "@cosmicdrift/kumiko-framework/stack";
33
+ import {
34
+ createLateBoundHolder,
35
+ createTestEnvelopeCipher,
36
+ resetTestTables,
37
+ } from "@cosmicdrift/kumiko-framework/testing";
38
+ import * as z from "zod";
39
+ import { AuthHandlers } from "../auth-email-password/constants";
40
+ import { createAuthEmailPasswordFeature } from "../auth-email-password/feature";
41
+ import { createConfigFeature } from "../config";
42
+ import { createConfigResolver } from "../config/resolver";
43
+ import { configValuesTable } from "../config/table";
44
+ import { PatHandlers } from "../personal-access-tokens/constants";
45
+ import { createPersonalAccessTokensFeature } from "../personal-access-tokens/feature";
46
+ import { revokeAllPatTokensForUser } from "../personal-access-tokens/revoke-for-user";
47
+ import { apiTokenEntity, apiTokenTable } from "../personal-access-tokens/schema/api-token";
48
+ import type { PatScopeConfig } from "../personal-access-tokens/scopes";
49
+ import { makeSessionHelpers } from "../sessions/__tests__/test-helpers";
50
+ import { createSessionsFeature } from "../sessions/feature";
51
+ import { userSessionEntity, userSessionTable } from "../sessions/schema/user-session";
52
+ import { createSessionCallbacks, type SessionCallbacks } from "../sessions/session-callbacks";
53
+ import { sessionCallbacksFromLateBound } from "../sessions/testing";
54
+ import { createTenantFeature } from "../tenant";
55
+ import { tenantMembershipsTable } from "../tenant/membership-table";
56
+ import { tenantEntity } from "../tenant/schema/tenant";
57
+ import { createUserFeature } from "../user/feature";
58
+ import { userEntity, userTable } from "../user/schema/user";
59
+
60
+ let stack: TestStack;
61
+ let h: ReturnType<typeof makeSessionHelpers>;
62
+ const callbacks = createLateBoundHolder<SessionCallbacks>("session-callbacks");
63
+
64
+ const encryptionKey = randomBytes(32).toString("base64");
65
+ const TENANT = testTenantId(1);
66
+
67
+ const SCOPES: PatScopeConfig = {
68
+ probe: { label: "Probe stream", read: ["stream-probe:stream:probe:tail"] },
69
+ };
70
+
71
+ const chunkGates = new Map<string, { promise: Promise<void>; release: () => void }>();
72
+
73
+ function createGate(gateId: string): () => void {
74
+ let release!: () => void;
75
+ const promise = new Promise<void>((resolve) => {
76
+ release = resolve;
77
+ });
78
+ chunkGates.set(gateId, { promise, release });
79
+ return release;
80
+ }
81
+
82
+ const streamProbeFeature = defineFeature("stream-probe", (r) => {
83
+ r.streamHandler(
84
+ "probe:tail",
85
+ z.object({ gateId: z.string().min(1) }),
86
+ async function* (event) {
87
+ yield { phase: "open" as const };
88
+ const gate = chunkGates.get(event.payload.gateId);
89
+ if (!gate) throw new Error(`missing gate ${event.payload.gateId}`);
90
+ await gate.promise;
91
+ yield { phase: "survived" as const };
92
+ },
93
+ { access: { roles: ["User"] } },
94
+ );
95
+ });
96
+
97
+ function buildLiveFetch(bearerToken: string): {
98
+ readonly fetch: typeof fetch;
99
+ readonly csrfToken: string;
100
+ } {
101
+ const csrfToken = generateToken();
102
+ const fetchImpl = (async (url: unknown, init: RequestInit | undefined) => {
103
+ const reqInit: RequestInit = {
104
+ ...(init ?? {}),
105
+ headers: {
106
+ ...(init?.headers ?? {}),
107
+ Authorization: `Bearer ${bearerToken}`,
108
+ },
109
+ };
110
+ return stack.app.request(String(url), reqInit);
111
+ }) as unknown as typeof fetch;
112
+ return { fetch: fetchImpl, csrfToken };
113
+ }
114
+
115
+ async function openProbeStream(bearerToken: string): Promise<{
116
+ iter: AsyncIterator<{ phase: string }>;
117
+ release: () => void;
118
+ }> {
119
+ const gateId = generateToken();
120
+ const release = createGate(gateId);
121
+ const { fetch, csrfToken } = buildLiveFetch(bearerToken);
122
+ const dispatcher = createLiveDispatcher({ fetch, readCsrf: () => csrfToken });
123
+ const iter = dispatcher
124
+ .stream<{ phase: string }>("stream-probe:stream:probe:tail", { gateId })
125
+ [Symbol.asyncIterator]();
126
+ const first = await iter.next();
127
+ expect(first.done).toBe(false);
128
+ expect(first.value).toEqual({ phase: "open" });
129
+ return { iter, release };
130
+ }
131
+
132
+ // The append's NOTIFY can wake a dispatcher pass before the test's own
133
+ // runOnce(), and runOnce() skips a consumer that pass is still busy with.
134
+ // Draining first makes the invalidation land before the stream assertions.
135
+ async function deliverPendingEvents(): Promise<void> {
136
+ await stack.eventDispatcher?.drain();
137
+ await stack.eventDispatcher?.runOnce();
138
+ }
139
+
140
+ async function mintPat(cookieToken: string): Promise<{ id: string; token: string }> {
141
+ const res = await h.authedPost("/api/write", cookieToken, {
142
+ type: PatHandlers.create,
143
+ payload: {
144
+ name: "probe-token",
145
+ scopes: ["probe:read"],
146
+ currentPassword: "pw-long-enough",
147
+ },
148
+ });
149
+ expect(res.status).toBe(200);
150
+ const body = (await res.json()) as { data: { id: string; token: string } };
151
+ return body.data;
152
+ }
153
+
154
+ beforeAll(async () => {
155
+ const encryption = createTestEnvelopeCipher(encryptionKey);
156
+ const resolver = createConfigResolver({ cipher: encryption });
157
+ const bound = sessionCallbacksFromLateBound(callbacks);
158
+
159
+ stack = await setupTestStack({
160
+ features: [
161
+ createConfigFeature(),
162
+ createUserFeature(),
163
+ createTenantFeature(),
164
+ createAuthEmailPasswordFeature(),
165
+ authFoundationFeature,
166
+ createSessionsFeature({ autoRevokeOnPasswordChange: bound.asMassRevoker() }),
167
+ createPersonalAccessTokensFeature({
168
+ scopes: SCOPES,
169
+ autoRevokeOnPasswordChange: (userId) => revokeAllPatTokensForUser(stack.db, userId),
170
+ }),
171
+ streamProbeFeature,
172
+ ],
173
+ extraContext: { configResolver: resolver, configEncryption: encryption },
174
+ authConfig: {
175
+ ...bound.asAuthConfig(),
176
+ membershipQuery: "tenant:query:memberships",
177
+ loginHandler: AuthHandlers.login,
178
+ tokenVerifier: (raw: string) =>
179
+ resolveTokenVerifier({ db: stack.db, registry: stack.registry }, raw),
180
+ patRateLimiter: createInMemoryLoginRateLimiter(100, 60_000),
181
+ },
182
+ });
183
+ callbacks.set(createSessionCallbacks({ db: stack.db }));
184
+ h = makeSessionHelpers(stack, TENANT, bound.asAuthConfig().sessionCreator as SessionCreator);
185
+
186
+ await unsafeCreateEntityTable(stack.db, userEntity);
187
+ await unsafeCreateEntityTable(stack.db, tenantEntity);
188
+ await unsafeCreateEntityTable(stack.db, userSessionEntity);
189
+ await unsafeCreateEntityTable(stack.db, apiTokenEntity);
190
+ await unsafePushTables(stack.db, { configValuesTable, tenantMembershipsTable });
191
+ });
192
+
193
+ afterAll(async () => {
194
+ await stack.cleanup();
195
+ });
196
+
197
+ beforeEach(async () => {
198
+ await resetTestTables(stack.db, [
199
+ userTable,
200
+ tenantMembershipsTable,
201
+ userSessionTable,
202
+ apiTokenTable,
203
+ eventsTable,
204
+ ]);
205
+ chunkGates.clear();
206
+ });
207
+
208
+ // Tests release a stream's gate BEFORE asserting it closed: the invalidation
209
+ // already fired during runOnce(), so a revoked stream still rejects, while a
210
+ // stream that was wrongly left open yields its next chunk and fails the
211
+ // assertion instead of hanging on a gate nobody releases.
212
+ describe("logout closes only that cookie session's SSE stream", () => {
213
+ test("session 1 logs out, session 1's stream closes, session 2's stream survives", async () => {
214
+ await h.seedUser("logout-scope@example.com", "pw-long-enough");
215
+ const session1 = await h.login("logout-scope@example.com", "pw-long-enough");
216
+ const session2 = await h.login("logout-scope@example.com", "pw-long-enough");
217
+
218
+ const stream1 = await openProbeStream(session1.token);
219
+ const stream2 = await openProbeStream(session2.token);
220
+
221
+ const logoutRes = await h.authedPost("/api/auth/logout", session1.token);
222
+ expect(logoutRes.status).toBe(200);
223
+
224
+ await deliverPendingEvents();
225
+
226
+ stream1.release();
227
+
228
+ await expect(stream1.iter.next()).rejects.toMatchObject({ code: "access_denied" });
229
+
230
+ stream2.release();
231
+ const survivedChunk = await stream2.iter.next();
232
+ expect(survivedChunk.done).toBe(false);
233
+ expect(survivedChunk.value).toEqual({ phase: "survived" });
234
+ });
235
+ });
236
+
237
+ describe("PAT revoke closes only that token's SSE stream", () => {
238
+ test("revoking PAT1 closes PAT1's stream, PAT2's stream and the cookie stream survive", async () => {
239
+ await h.seedUser("pat-scope@example.com", "pw-long-enough");
240
+ const session = await h.login("pat-scope@example.com", "pw-long-enough");
241
+ const pat1 = await mintPat(session.token);
242
+ const pat2 = await mintPat(session.token);
243
+
244
+ const cookieStream = await openProbeStream(session.token);
245
+ const pat1Stream = await openProbeStream(pat1.token);
246
+ const pat2Stream = await openProbeStream(pat2.token);
247
+
248
+ const revokeRes = await h.authedPost("/api/write", session.token, {
249
+ type: PatHandlers.revoke,
250
+ payload: { id: pat1.id },
251
+ });
252
+ expect(revokeRes.status).toBe(200);
253
+
254
+ await deliverPendingEvents();
255
+
256
+ pat1Stream.release();
257
+
258
+ await expect(pat1Stream.iter.next()).rejects.toMatchObject({ code: "access_denied" });
259
+
260
+ cookieStream.release();
261
+ pat2Stream.release();
262
+ const cookieSurvived = await cookieStream.iter.next();
263
+ expect(cookieSurvived.done).toBe(false);
264
+ expect(cookieSurvived.value).toEqual({ phase: "survived" });
265
+ const pat2Survived = await pat2Stream.iter.next();
266
+ expect(pat2Survived.done).toBe(false);
267
+ expect(pat2Survived.value).toEqual({ phase: "survived" });
268
+ });
269
+
270
+ test("revokeAllPatTokensForUser closes every PAT stream of the user, cookie stream survives", async () => {
271
+ const { userId } = await h.seedUser("pat-mass-scope@example.com", "pw-long-enough");
272
+ const session = await h.login("pat-mass-scope@example.com", "pw-long-enough");
273
+ const pat1 = await mintPat(session.token);
274
+ const pat2 = await mintPat(session.token);
275
+
276
+ const cookieStream = await openProbeStream(session.token);
277
+ const pat1Stream = await openProbeStream(pat1.token);
278
+ const pat2Stream = await openProbeStream(pat2.token);
279
+
280
+ const revokedCount = await revokeAllPatTokensForUser(stack.db, userId);
281
+ expect(revokedCount).toBe(2);
282
+
283
+ await deliverPendingEvents();
284
+
285
+ pat1Stream.release();
286
+
287
+ await expect(pat1Stream.iter.next()).rejects.toMatchObject({ code: "access_denied" });
288
+ pat2Stream.release();
289
+ await expect(pat2Stream.iter.next()).rejects.toMatchObject({ code: "access_denied" });
290
+
291
+ cookieStream.release();
292
+ const cookieSurvived = await cookieStream.iter.next();
293
+ expect(cookieSurvived.done).toBe(false);
294
+ expect(cookieSurvived.value).toEqual({ phase: "survived" });
295
+ });
296
+ });
@@ -4,7 +4,7 @@ import {
4
4
  defineWriteHandler,
5
5
  resolveAgentExposure,
6
6
  } from "@cosmicdrift/kumiko-framework/engine";
7
- import { z } from "zod";
7
+ import * as z from "zod";
8
8
  import { AgentDocGapKinds, findAgentDocGaps, formatAgentDocGap } from "../agent-doc-lint";
9
9
 
10
10
  const OPEN_ACCESS = {
@@ -7,7 +7,7 @@ import {
7
7
  defineFeature,
8
8
  } from "@cosmicdrift/kumiko-framework/engine";
9
9
  import type { ReferenceFieldDef, TextFieldDef } from "@cosmicdrift/kumiko-framework/engine/types";
10
- import { z } from "zod";
10
+ import * as z from "zod";
11
11
  import { buildAgentManifest } from "../agent-manifest";
12
12
 
13
13
  const gatedEntity = createEntity({
@@ -7,7 +7,7 @@ import {
7
7
  defineEntityListHandler,
8
8
  defineFeature,
9
9
  } from "@cosmicdrift/kumiko-framework/engine";
10
- import { z } from "zod";
10
+ import * as z from "zod";
11
11
  import { authFoundationFeature } from "../../auth-foundation";
12
12
  import { AuthMfaHandlers, AuthMfaQueries, createAuthMfaFeature } from "../../auth-mfa";
13
13
  import { createConfigFeature } from "../../config";
@@ -16,7 +16,7 @@ import {
16
16
  type TestStack,
17
17
  unsafeCreateEntityTable,
18
18
  } from "@cosmicdrift/kumiko-framework/stack";
19
- import { z } from "zod";
19
+ import * as z from "zod";
20
20
  import { buildAgentManifest } from "../agent-manifest";
21
21
  import { buildToolCatalog, toolNameForQn } from "../tool-catalog";
22
22
  import type { ToolDispatcher } from "../tool-dispatch";
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, spyOn, test } from "bun:test";
2
2
  import { defineFeature, validateBoot } from "@cosmicdrift/kumiko-framework/engine";
3
- import { z } from "zod";
3
+ import * as z from "zod";
4
4
  import { AGENT_TOOLS_FEATURE_NAME, createAgentToolsFeature } from "../feature";
5
5
 
6
6
  async function noopWriteHandler() {
@@ -6,7 +6,7 @@ import {
6
6
  createTextField,
7
7
  defineFeature,
8
8
  } from "@cosmicdrift/kumiko-framework/engine";
9
- import { z } from "zod";
9
+ import * as z from "zod";
10
10
  import { buildAgentManifest } from "../agent-manifest";
11
11
  import { buildToolCatalog, toolNameForQn } from "../tool-catalog";
12
12
  import type { AgentManifest, AgentToolMode, RegistrySearchView } from "../types";
@@ -15,7 +15,7 @@ import {
15
15
  isAgentVisibleScreen,
16
16
  resolveAgentExposure,
17
17
  } from "@cosmicdrift/kumiko-framework/engine";
18
- import { z } from "zod";
18
+ import * as z from "zod";
19
19
  import type {
20
20
  AgentManifest,
21
21
  AgentManifestEntity,
@@ -12,7 +12,7 @@ import {
12
12
  type TestStack,
13
13
  testTenantId,
14
14
  } from "@cosmicdrift/kumiko-framework/stack";
15
- import { z } from "zod";
15
+ import * as z from "zod";
16
16
  import { createConfigFeature } from "../../config";
17
17
  import { createTenantFeature } from "../../tenant";
18
18
  import { createUserFeature } from "../../user";
@@ -2,7 +2,7 @@ import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
2
2
  import type { EscapeHatchAuditSink, EscapeHatchKind } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import { append } from "@cosmicdrift/kumiko-framework/event-store";
4
4
  import { generateId } from "@cosmicdrift/kumiko-framework/utils";
5
- import { z } from "zod";
5
+ import * as z from "zod";
6
6
 
7
7
  export const ESCAPE_HATCH_USED_EVENT = "audit:event:escape-hatch-used";
8
8
  export const ESCAPE_HATCH_USE_AGGREGATE_TYPE = "escapeHatchUse";
@@ -5,7 +5,7 @@
5
5
  import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
6
6
  import { access, defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
7
7
  import { eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
8
- import { z } from "zod";
8
+ import * as z from "zod";
9
9
 
10
10
  export const detailsQuery = defineQueryHandler({
11
11
  name: "details",
@@ -15,7 +15,7 @@ import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-
15
15
  import { access, defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
16
16
  import { eventsTable } from "@cosmicdrift/kumiko-framework/event-store";
17
17
  import { Temporal } from "temporal-polyfill";
18
- import { z } from "zod";
18
+ import * as z from "zod";
19
19
 
20
20
  const MAX_LIMIT = 100;
21
21
 
@@ -31,7 +31,7 @@ import {
31
31
  updateRows,
32
32
  waitFor,
33
33
  } from "@cosmicdrift/kumiko-framework/testing";
34
- import { z } from "zod";
34
+ import * as z from "zod";
35
35
  import {
36
36
  createComplianceProfilesFeature,
37
37
  tenantComplianceProfileEntity,
@@ -1,5 +1,5 @@
1
1
  import { defineFeature, type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { MIN_HMAC_SECRET_LENGTH } from "./constants";
4
4
  import type { AuthMailLocale } from "./email-templates";
5
5
  import { changePasswordWrite } from "./handlers/change-password.write";
@@ -4,7 +4,7 @@ import {
4
4
  defineWriteHandler,
5
5
  SYSTEM_TENANT_ID,
6
6
  } from "@cosmicdrift/kumiko-framework/engine";
7
- import { z } from "zod";
7
+ import * as z from "zod";
8
8
  import { hashPassword, verifyPassword } from "../../shared";
9
9
  import { UserHandlers, UserQueries } from "../../user";
10
10
  import { invalidCredentials } from "../errors";
@@ -4,7 +4,7 @@ import {
4
4
  UnprocessableError,
5
5
  writeFailure,
6
6
  } from "@cosmicdrift/kumiko-framework/errors";
7
- import { z } from "zod";
7
+ import * as z from "zod";
8
8
  import { AuthErrors } from "../constants";
9
9
  import { invalidUnlockToken } from "../errors";
10
10
  import { clearLockoutState } from "../lockout-store";
@@ -31,7 +31,7 @@ import {
31
31
  type TenantId,
32
32
  } from "@cosmicdrift/kumiko-framework/engine";
33
33
  import { InternalError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
34
- import { z } from "zod";
34
+ import * as z from "zod";
35
35
  import { decryptStoredPii, sessionLocaleField, sessionTimezoneField } from "../../shared";
36
36
  // kumiko-lint-ignore cross-feature-import invite-flow
37
37
  import {
@@ -23,7 +23,7 @@ import {
23
23
  type TenantId,
24
24
  } from "@cosmicdrift/kumiko-framework/engine";
25
25
  import { InternalError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
26
- import { z } from "zod";
26
+ import * as z from "zod";
27
27
  import { decryptStoredPii } from "../../shared";
28
28
  // kumiko-lint-ignore cross-feature-import invite-flow lebt in auth-email-password (Magic-Link), DB-row-owner ist tenant-feature
29
29
  import {