@cosmicdrift/kumiko-bundled-features 0.388.0 → 0.390.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 (415) hide show
  1. package/dist/agent-tools/index.d.ts +1 -1
  2. package/dist/agent-tools/index.js +1 -1
  3. package/dist/agent-tools/tool-dispatch.d.ts +8 -3
  4. package/dist/agent-tools/tool-dispatch.js +36 -25
  5. package/dist/audit/feature.js +2 -1
  6. package/dist/auth-email-password/auth-user-row.js +1 -1
  7. package/dist/auth-email-password/bootstrap.js +6 -1
  8. package/dist/auth-email-password/feature.js +1 -1
  9. package/dist/auth-email-password/handlers/change-password.write.js +7 -7
  10. package/dist/auth-email-password/handlers/confirm-account-unlock.write.js +3 -3
  11. package/dist/auth-email-password/handlers/confirm-token-flow.js +4 -4
  12. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +8 -7
  13. package/dist/auth-email-password/handlers/invite-accept.write.js +10 -7
  14. package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
  15. package/dist/auth-email-password/handlers/invite-info.query.js +2 -2
  16. package/dist/auth-email-password/handlers/invite-signup-complete.write.js +8 -7
  17. package/dist/auth-email-password/handlers/login.write.js +17 -15
  18. package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -0
  19. package/dist/auth-email-password/handlers/request-account-unlock.write.js +1 -0
  20. package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -0
  21. package/dist/auth-email-password/handlers/request-email-verification.write.js +1 -0
  22. package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -0
  23. package/dist/auth-email-password/handlers/request-password-reset.write.js +1 -0
  24. package/dist/auth-email-password/handlers/reset-password.write.js +2 -2
  25. package/dist/auth-email-password/handlers/signup-confirm.write.js +12 -12
  26. package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -0
  27. package/dist/auth-email-password/handlers/signup-request.write.js +16 -8
  28. package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +4 -2
  29. package/dist/auth-email-password/handlers/token-request-handler.d.ts +2 -0
  30. package/dist/auth-email-password/handlers/token-request-handler.js +7 -2
  31. package/dist/auth-email-password/handlers/verify-email.write.js +2 -2
  32. package/dist/auth-email-password/magic-link-mail.d.ts +2 -0
  33. package/dist/auth-email-password/magic-link-mail.js +7 -3
  34. package/dist/auth-email-password/seeding.d.ts +1 -1
  35. package/dist/auth-email-password/seeding.js +9 -8
  36. package/dist/auth-email-password/signup-activation.d.ts +1 -0
  37. package/dist/auth-email-password/signup-activation.js +1 -0
  38. package/dist/auth-email-password/web/auth-client.d.ts +2 -2
  39. package/dist/auth-email-password/web/auth-client.js +8 -4
  40. package/dist/auth-email-password/web/auth-redirect.d.ts +5 -1
  41. package/dist/auth-email-password/web/auth-redirect.js +20 -25
  42. package/dist/auth-email-password/web/default-auth-pages-gate.js +6 -3
  43. package/dist/auth-email-password/web/forgot-password-screen.d.ts +4 -1
  44. package/dist/auth-email-password/web/forgot-password-screen.js +7 -2
  45. package/dist/auth-email-password/web/index.d.ts +1 -0
  46. package/dist/auth-email-password/web/index.js +1 -0
  47. package/dist/auth-email-password/web/login-screen.js +11 -6
  48. package/dist/auth-email-password/web/reset-password-screen.js +7 -1
  49. package/dist/auth-email-password/web/return-path.d.ts +4 -0
  50. package/dist/auth-email-password/web/return-path.js +34 -0
  51. package/dist/auth-email-password/web/session.js +6 -1
  52. package/dist/auth-email-password/web/signup-complete-screen.js +11 -3
  53. package/dist/auth-email-password/web/signup-screen.d.ts +4 -1
  54. package/dist/auth-email-password/web/signup-screen.js +8 -3
  55. package/dist/auth-foundation/feature.js +12 -4
  56. package/dist/auth-mfa/config.d.ts +1 -1
  57. package/dist/auth-mfa/config.js +3 -7
  58. package/dist/auth-mfa/handlers/disable.write.js +5 -5
  59. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +19 -14
  60. package/dist/auth-mfa/handlers/enable-confirm.write.js +3 -3
  61. package/dist/auth-mfa/handlers/enable-start-preauth.write.js +6 -1
  62. package/dist/auth-mfa/handlers/enable-start.write.js +1 -1
  63. package/dist/auth-mfa/handlers/reencrypt.job.js +14 -9
  64. package/dist/auth-mfa/handlers/regenerate-recovery.write.js +4 -4
  65. package/dist/auth-mfa/handlers/verify.write.js +19 -14
  66. package/dist/auth-mfa/mfa-code-verifier.js +9 -4
  67. package/dist/auth-mfa/mfa-status-checker.js +13 -6
  68. package/dist/auth-mfa/verify-factor.js +1 -1
  69. package/dist/billing-foundation/billing-info-query.js +2 -2
  70. package/dist/billing-foundation/consumer-protection/consent-gate.js +1 -1
  71. package/dist/billing-foundation/consumer-protection/issue-confirmation.js +5 -5
  72. package/dist/billing-foundation/consumer-protection/register-consent.js +1 -1
  73. package/dist/billing-foundation/consumer-protection/register-termination.js +1 -1
  74. package/dist/billing-foundation/consumer-protection/termination-open-cancels.d.ts +4 -0
  75. package/dist/billing-foundation/consumer-protection/termination-open-cancels.js +14 -0
  76. package/dist/billing-foundation/consumer-protection/termination-provider-lock.d.ts +2 -0
  77. package/dist/billing-foundation/consumer-protection/termination-provider-lock.js +3 -0
  78. package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +4 -4
  79. package/dist/billing-foundation/consumer-protection/termination-record.js +11 -48
  80. package/dist/billing-foundation/consumer-protection/termination-request.js +10 -8
  81. package/dist/billing-foundation/consumer-protection/termination-revoke.js +10 -12
  82. package/dist/billing-foundation/events.js +1 -1
  83. package/dist/billing-foundation/feature.js +3 -3
  84. package/dist/billing-foundation/get-subscription-for-tenant.d.ts +1 -1
  85. package/dist/billing-foundation/handlers/process-event.write.js +5 -5
  86. package/dist/billing-foundation/handlers/process-payment-event.write.js +3 -3
  87. package/dist/billing-foundation/plan-catalog.js +2 -2
  88. package/dist/billing-foundation/projection.js +1 -1
  89. package/dist/billing-foundation/types.js +1 -1
  90. package/dist/cap-counter/aggregate-id.d.ts +1 -1
  91. package/dist/cap-counter/aggregate-id.js +1 -1
  92. package/dist/cap-counter/book-cap-usage.d.ts +2 -2
  93. package/dist/cap-counter/book-cap-usage.js +3 -3
  94. package/dist/cap-counter/cap-reservation.js +16 -7
  95. package/dist/cap-counter/constants.js +1 -1
  96. package/dist/cap-counter/feature.js +3 -2
  97. package/dist/cap-counter/handlers/increment-rolling.write.js +1 -1
  98. package/dist/cap-counter/reservation-chain.js +1 -1
  99. package/dist/cap-counter/with-cap-enforcement.d.ts +3 -3
  100. package/dist/cap-counter/with-cap-enforcement.js +1 -1
  101. package/dist/cap-overview/feature.js +1 -0
  102. package/dist/cap-overview/handlers/caps-usage.query.js +1 -1
  103. package/dist/cap-overview/handlers/tenant-caps-list.query.js +1 -1
  104. package/dist/compliance-profiles/seeding.d.ts +1 -1
  105. package/dist/compliance-profiles/seeding.js +9 -3
  106. package/dist/config/feature.js +6 -6
  107. package/dist/config/handlers/cascade.query.js +1 -1
  108. package/dist/config/handlers/readiness.query.js +1 -1
  109. package/dist/config/handlers/reencrypt.job.js +23 -13
  110. package/dist/config/handlers/reset.write.js +3 -3
  111. package/dist/config/handlers/selected-extensions.query.js +1 -1
  112. package/dist/config/handlers/set.write.js +15 -5
  113. package/dist/config/handlers/values.query.js +1 -1
  114. package/dist/crypto-shredding/handlers/forget-subject.write.js +9 -9
  115. package/dist/crypto-shredding/resolve-file-bytes-deleter.js +3 -3
  116. package/dist/custom-fields/handlers/clear-custom-field.write.js +1 -1
  117. package/dist/custom-fields/handlers/delete-system-field.write.js +1 -1
  118. package/dist/custom-fields/handlers/delete-tenant-field.write.js +1 -1
  119. package/dist/custom-fields/handlers/set-custom-field.write.js +1 -1
  120. package/dist/custom-fields/index.d.ts +1 -0
  121. package/dist/custom-fields/index.js +1 -0
  122. package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
  123. package/dist/data-retention/feature.js +1 -1
  124. package/dist/data-retention/run-retention-cleanup.js +12 -2
  125. package/dist/delivery/attempt-log.js +1 -1
  126. package/dist/delivery/delivery-service.js +5 -5
  127. package/dist/delivery/feature.js +3 -2
  128. package/dist/delivery/index.d.ts +1 -0
  129. package/dist/delivery/index.js +1 -0
  130. package/dist/delivery/jobs.js +11 -11
  131. package/dist/delivery/system-channel-context.d.ts +15 -0
  132. package/dist/delivery/system-channel-context.js +5 -0
  133. package/dist/document-ingest-foundation/append-missing-extract-event.d.ts +21 -0
  134. package/dist/document-ingest-foundation/append-missing-extract-event.js +42 -0
  135. package/dist/document-ingest-foundation/events.d.ts +11 -0
  136. package/dist/document-ingest-foundation/events.js +12 -1
  137. package/dist/document-ingest-foundation/index.d.ts +2 -1
  138. package/dist/document-ingest-foundation/index.js +2 -1
  139. package/dist/feature-toggles/feature.js +1 -0
  140. package/dist/feature-toggles/handlers/registered.query.js +1 -1
  141. package/dist/feature-toggles/handlers/set.write.js +1 -1
  142. package/dist/feature-toggles/toggle-sync-signal.js +1 -1
  143. package/dist/file-derivatives/feature.d.ts +1 -1
  144. package/dist/file-derivatives/feature.js +1 -1
  145. package/dist/file-derivatives/handlers/public-variant-by-file-ref.query.js +1 -1
  146. package/dist/file-derivatives/handlers/public-variant.query.js +5 -5
  147. package/dist/file-foundation/feature.js +1 -1
  148. package/dist/file-foundation/read-file-ref-content.d.ts +4 -2
  149. package/dist/file-foundation/read-file-ref-content.js +3 -3
  150. package/dist/file-provider-s3/feature.js +1 -1
  151. package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +8 -8
  152. package/dist/form-draft/feature.js +1 -1
  153. package/dist/form-draft/handlers/cleanup.job.js +22 -12
  154. package/dist/form-draft/handlers/discard.write.js +3 -3
  155. package/dist/form-draft/handlers/get.query.js +1 -1
  156. package/dist/foundation-shared/config-helpers.d.ts +1 -1
  157. package/dist/foundation-shared/config-helpers.js +1 -1
  158. package/dist/host-routing/validate-options.js +3 -0
  159. package/dist/inbound-mail-foundation/events.js +1 -1
  160. package/dist/inbound-mail-foundation/feature.js +2 -1
  161. package/dist/inbound-mail-foundation/handlers/account-state.d.ts +3 -1
  162. package/dist/inbound-mail-foundation/handlers/account-state.js +1 -1
  163. package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
  164. package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +1 -1
  165. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +10 -10
  166. package/dist/inbound-mail-foundation/handlers/update-account.write.js +1 -1
  167. package/dist/inbound-mail-foundation/projection.js +1 -1
  168. package/dist/inbound-mail-foundation/watch-supervisor.js +1 -1
  169. package/dist/inbound-provider-imap/feature.js +1 -1
  170. package/dist/jobs/events.js +1 -1
  171. package/dist/jobs/feature.js +1 -0
  172. package/dist/jobs/handlers/consumer.write.js +1 -1
  173. package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
  174. package/dist/jobs/handlers/reindex-entity.job.js +5 -5
  175. package/dist/jobs/handlers/retention-cleanup.job.js +1 -1
  176. package/dist/jobs/handlers/retry.write.js +1 -1
  177. package/dist/jobs/handlers/stale-run-sweep.job.js +1 -1
  178. package/dist/jobs/handlers/trigger.write.js +2 -2
  179. package/dist/jobs/job-run-logger.d.ts +1 -0
  180. package/dist/jobs/job-run-logger.js +38 -18
  181. package/dist/ledger/web/index.js +1 -1
  182. package/dist/legal-pages/lib/boot-check.d.ts +10 -4
  183. package/dist/legal-pages/lib/boot-check.js +2 -2
  184. package/dist/legal-pages/seed.d.ts +3 -1
  185. package/dist/legal-pages/seed.js +2 -2
  186. package/dist/mail-foundation/feature.d.ts +2 -2
  187. package/dist/mail-transport-smtp/config-keys.js +1 -1
  188. package/dist/mail-transport-smtp/feature.js +7 -6
  189. package/dist/managed-pages/constants.d.ts +1 -0
  190. package/dist/managed-pages/constants.js +1 -0
  191. package/dist/managed-pages/feature.js +3 -1
  192. package/dist/managed-pages/index.d.ts +1 -0
  193. package/dist/managed-pages/index.js +1 -0
  194. package/dist/managed-pages/seeding.d.ts +1 -1
  195. package/dist/managed-pages/seeding.js +10 -4
  196. package/dist/notes-history/handlers/add-note.write.js +2 -2
  197. package/dist/personal-access-tokens/feature.js +2 -1
  198. package/dist/personal-access-tokens/handlers/create.write.js +2 -2
  199. package/dist/personal-access-tokens/handlers/revoke.write.js +1 -1
  200. package/dist/rate-limiting/handlers/status.query.js +2 -2
  201. package/dist/readiness/handlers/status.query.js +1 -1
  202. package/dist/renderer-foundation/api.d.ts +4 -2
  203. package/dist/renderer-foundation/api.js +2 -2
  204. package/dist/secrets/announce-secret-change.d.ts +6 -2
  205. package/dist/secrets/announce-secret-change.js +3 -3
  206. package/dist/secrets/feature.d.ts +1 -1
  207. package/dist/secrets/feature.js +8 -8
  208. package/dist/secrets/handlers/delete.write.js +1 -1
  209. package/dist/secrets/handlers/rotate.job.js +13 -8
  210. package/dist/secrets/handlers/set.write.js +1 -1
  211. package/dist/secrets/secrets-context.js +16 -6
  212. package/dist/secrets/write-gate.js +8 -2
  213. package/dist/seo/feature.js +1 -1
  214. package/dist/sessions/feature.js +2 -1
  215. package/dist/sessions/handlers/cleanup.job.js +2 -2
  216. package/dist/sessions/handlers/detail.query.d.ts +1 -0
  217. package/dist/sessions/handlers/detail.query.js +5 -2
  218. package/dist/sessions/handlers/list.query.d.ts +1 -0
  219. package/dist/sessions/handlers/list.query.js +5 -2
  220. package/dist/sessions/handlers/mine.query.d.ts +1 -0
  221. package/dist/sessions/handlers/mine.query.js +5 -2
  222. package/dist/sessions/handlers/revoke-all-for-user.write.js +3 -3
  223. package/dist/sessions/handlers/revoke-all-others.write.js +1 -1
  224. package/dist/sessions/handlers/revoke.write.js +1 -1
  225. package/dist/sessions/index.d.ts +1 -1
  226. package/dist/sessions/index.js +1 -1
  227. package/dist/sessions/screens.js +8 -4
  228. package/dist/sessions/session-callbacks.d.ts +0 -2
  229. package/dist/sessions/session-callbacks.js +1 -3
  230. package/dist/sessions/session-revoked-event.js +1 -1
  231. package/dist/sessions/web/session-device-cell.js +10 -12
  232. package/dist/sessions/web/user-agent.d.ts +1 -0
  233. package/dist/sessions/web/user-agent.js +11 -0
  234. package/dist/shared/index.d.ts +2 -0
  235. package/dist/shared/index.js +2 -0
  236. package/dist/shared/purge-files-reaching-subject.d.ts +1 -0
  237. package/dist/shared/purge-files-reaching-subject.js +31 -4
  238. package/dist/shared/require-for-tenant.d.ts +1 -1
  239. package/dist/shared/require-for-tenant.js +6 -4
  240. package/dist/shared/run-in-sub-transaction.js +5 -5
  241. package/dist/shared/stream-tenant-backfill.d.ts +28 -0
  242. package/dist/shared/stream-tenant-backfill.js +119 -0
  243. package/dist/shared/stream-tenant-derived-state.d.ts +25 -0
  244. package/dist/shared/stream-tenant-derived-state.js +68 -0
  245. package/dist/step-dispatcher/deliver-dispatch-request.js +16 -3
  246. package/dist/step-dispatcher/feature.js +1 -1
  247. package/dist/step-dispatcher/mail-runner.d.ts +1 -0
  248. package/dist/subscription-dodo/plugin-methods.js +1 -1
  249. package/dist/subscription-dodo/runtime.js +4 -4
  250. package/dist/subscription-stripe/plugin-methods.js +3 -3
  251. package/dist/subscription-stripe/runtime.js +4 -4
  252. package/dist/tags/feature.js +1 -0
  253. package/dist/template-resolver/api.d.ts +4 -2
  254. package/dist/template-resolver/api.js +2 -2
  255. package/dist/template-resolver/document-ops.d.ts +5 -0
  256. package/dist/template-resolver/document-ops.js +18 -2
  257. package/dist/template-resolver/feature.js +2 -2
  258. package/dist/template-resolver/handlers/bundle-import.d.ts +4 -3
  259. package/dist/template-resolver/handlers/export-bundle.query.js +1 -1
  260. package/dist/template-resolver/handlers/layout-source.query.d.ts +4 -0
  261. package/dist/template-resolver/handlers/layout-source.query.js +26 -6
  262. package/dist/template-resolver/handlers/overview.query.js +17 -9
  263. package/dist/template-resolver/handlers/patch-draft.write.js +1 -1
  264. package/dist/template-resolver/handlers/send-test-shared.js +1 -1
  265. package/dist/template-resolver/handlers/send-test-status.query.d.ts +2 -1
  266. package/dist/template-resolver/handlers/send-test-status.query.js +18 -15
  267. package/dist/template-resolver/handlers/send-test.write.js +2 -2
  268. package/dist/template-resolver/handlers/tenant-override-selection.d.ts +3 -1
  269. package/dist/template-resolver/handlers/tenant-override-selection.js +1 -1
  270. package/dist/template-resolver/handlers/tenant-overrides.query.js +1 -1
  271. package/dist/template-resolver/handlers/usage.query.js +1 -1
  272. package/dist/template-resolver/seeding.d.ts +1 -0
  273. package/dist/template-resolver/seeding.js +17 -4
  274. package/dist/template-resolver/store-template-resource.d.ts +7 -6
  275. package/dist/template-resolver/store-template-resource.js +48 -8
  276. package/dist/template-resolver/template-resources.d.ts +2 -2
  277. package/dist/template-resolver/template-resources.js +2 -2
  278. package/dist/template-resolver/testing.js +2 -2
  279. package/dist/template-resolver/web/document-insert-fields.d.ts +4 -0
  280. package/dist/template-resolver/web/document-insert-fields.js +69 -0
  281. package/dist/template-resolver/web/document-insert-model.d.ts +43 -0
  282. package/dist/template-resolver/web/document-insert-model.js +106 -0
  283. package/dist/template-resolver/web/document-insert-panel.d.ts +13 -0
  284. package/dist/template-resolver/web/document-insert-panel.js +49 -0
  285. package/dist/template-resolver/web/document-tiptap-bubble.d.ts +4 -0
  286. package/dist/template-resolver/web/document-tiptap-bubble.js +85 -0
  287. package/dist/template-resolver/web/document-tiptap-content.d.ts +3 -0
  288. package/dist/template-resolver/web/document-tiptap-content.js +12 -0
  289. package/dist/template-resolver/web/document-tiptap-context.d.ts +41 -0
  290. package/dist/template-resolver/web/document-tiptap-context.js +43 -0
  291. package/dist/template-resolver/web/document-tiptap-editor.js +50 -495
  292. package/dist/template-resolver/web/document-tiptap-helpers.d.ts +44 -0
  293. package/dist/template-resolver/web/document-tiptap-helpers.js +129 -0
  294. package/dist/template-resolver/web/document-tiptap-layout.d.ts +4 -0
  295. package/dist/template-resolver/web/document-tiptap-layout.js +24 -0
  296. package/dist/template-resolver/web/document-tiptap-toolbar-groups.d.ts +6 -0
  297. package/dist/template-resolver/web/document-tiptap-toolbar-groups.js +33 -0
  298. package/dist/template-resolver/web/document-tiptap-toolbar.d.ts +3 -0
  299. package/dist/template-resolver/web/document-tiptap-toolbar.js +91 -0
  300. package/dist/template-resolver/web/editor-handle.d.ts +1 -0
  301. package/dist/template-resolver/web/editor-handle.js +15 -0
  302. package/dist/template-resolver/web/i18n.js +3 -0
  303. package/dist/template-resolver/web/placeholder-line-editor.js +16 -5
  304. package/dist/template-resolver/web/qualified-names.d.ts +1 -0
  305. package/dist/template-resolver/web/qualified-names.js +1 -0
  306. package/dist/template-resolver/web/use-document-tiptap-model.d.ts +54 -0
  307. package/dist/template-resolver/web/use-document-tiptap-model.js +96 -0
  308. package/dist/tenant/feature.js +1 -0
  309. package/dist/tenant/handlers/cancel-invitation.write.js +1 -1
  310. package/dist/tenant/handlers/memberships.query.js +1 -1
  311. package/dist/tenant/handlers/remove-member.write.js +2 -2
  312. package/dist/tenant/handlers/team-list.query.js +3 -3
  313. package/dist/tenant/handlers/update-member-roles.write.js +1 -1
  314. package/dist/tenant/index.d.ts +1 -0
  315. package/dist/tenant/index.js +1 -0
  316. package/dist/tenant/invited-membership.js +6 -1
  317. package/dist/tenant/schema/tenant.d.ts +2 -2
  318. package/dist/tenant/schema/tenant.js +4 -1
  319. package/dist/tenant/seeding.d.ts +2 -2
  320. package/dist/tenant/seeding.js +52 -52
  321. package/dist/tenant/stream-tenant-backfill.d.ts +21 -0
  322. package/dist/tenant/stream-tenant-backfill.js +63 -0
  323. package/dist/tenant/testing.d.ts +12 -0
  324. package/dist/tenant/testing.js +33 -0
  325. package/dist/tenant-handover/events.js +1 -1
  326. package/dist/tenant-handover/handlers/claim.write.js +1 -1
  327. package/dist/tenant-lifecycle/feature.js +5 -5
  328. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +14 -5
  329. package/dist/tenant-lifecycle/handlers/request-destruction.write.js +12 -4
  330. package/dist/tenant-lifecycle/run-tenant-destroy.js +23 -13
  331. package/dist/tenant-lifecycle/stages.js +28 -2
  332. package/dist/tenant-lifecycle/testing.d.ts +1 -1
  333. package/dist/tenant-lifecycle/testing.js +6 -5
  334. package/dist/tier-engine/entity.js +1 -1
  335. package/dist/tier-engine/feature.js +8 -3
  336. package/dist/tier-engine/handlers/active-tier.query.js +3 -3
  337. package/dist/tier-engine/handlers/get-tenant-tier.query.js +6 -1
  338. package/dist/tier-engine/handlers/set-tenant-tier.write.js +4 -4
  339. package/dist/user/db/queries/stream-tenant-backfill.d.ts +20 -1
  340. package/dist/user/db/queries/stream-tenant-backfill.js +40 -77
  341. package/dist/user/feature.js +1 -0
  342. package/dist/user/handlers/find-for-auth.query.js +2 -2
  343. package/dist/user/handlers/update-roles.js +2 -2
  344. package/dist/user/index.d.ts +1 -1
  345. package/dist/user/index.js +1 -1
  346. package/dist/user/invited-global-roles.js +6 -1
  347. package/dist/user/principal-status.js +1 -1
  348. package/dist/user/seeding.d.ts +1 -1
  349. package/dist/user/seeding.js +20 -5
  350. package/dist/user/testing.d.ts +3 -0
  351. package/dist/user/testing.js +10 -0
  352. package/dist/user-data-rights/audit-download.js +12 -2
  353. package/dist/user-data-rights/feature.js +30 -26
  354. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +1 -1
  355. package/dist/user-data-rights/handlers/deletion-grace-period.js +1 -1
  356. package/dist/user-data-rights/handlers/download-by-job.query.js +2 -2
  357. package/dist/user-data-rights/handlers/download-by-token.query.js +2 -2
  358. package/dist/user-data-rights/handlers/request-deletion.write.js +1 -1
  359. package/dist/user-data-rights/handlers/restrict-account.write.js +4 -4
  360. package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -6
  361. package/dist/user-data-rights/index.d.ts +1 -1
  362. package/dist/user-data-rights/index.js +1 -1
  363. package/dist/user-data-rights/lib/mail-transport-resolver.js +1 -1
  364. package/dist/user-data-rights/lib/storage-provider-resolver.js +1 -1
  365. package/dist/user-data-rights/lib/update-user-lifecycle.js +6 -1
  366. package/dist/user-data-rights/run-export-jobs.js +7 -2
  367. package/dist/user-data-rights/run-forget-cleanup.d.ts +1 -0
  368. package/dist/user-data-rights/run-forget-cleanup.js +9 -1
  369. package/dist/user-data-rights/run-user-export.js +7 -1
  370. package/dist/user-data-rights/web/constants.d.ts +14 -0
  371. package/dist/user-data-rights/web/constants.js +14 -0
  372. package/dist/user-data-rights/web/index.d.ts +1 -0
  373. package/dist/user-data-rights/web/index.js +1 -0
  374. package/dist/user-profile/handlers/change-email.write.js +8 -6
  375. package/dist/waitlist/feature.js +1 -0
  376. package/dist/waitlist/handlers/export-purge.write.js +1 -1
  377. package/dist/waitlist/handlers/invite.write.js +2 -2
  378. package/dist/waitlist/handlers/reject.write.js +1 -1
  379. package/dist/waitlist/handlers/submit.write.js +20 -11
  380. package/dist/waitlist/import.d.ts +2 -1
  381. package/dist/waitlist/import.js +22 -2
  382. package/dist/waitlist/legacy-migrate.js +8 -3
  383. package/dist/waitlist/link-on-signup.js +6 -1
  384. package/dist/waitlist/options.d.ts +2 -1
  385. package/dist/workflow-runner/db/queries/due-runs.js +1 -1
  386. package/dist/workflow-runner/event-subscriber.js +4 -3
  387. package/dist/workflow-runner/event-trigger.js +2 -1
  388. package/dist/workflow-runner/feature.js +2 -2
  389. package/dist/workflow-runner/handlers/resume-run.write.js +6 -6
  390. package/dist/workflow-runner/runner.d.ts +3 -1
  391. package/dist/workflow-runner/runner.js +3 -3
  392. package/package.json +41 -23
  393. package/src/agent-tools/changes.json +6 -0
  394. package/src/auth-email-password/changes.json +18 -0
  395. package/src/auth-mfa/changes.json +6 -0
  396. package/src/billing-foundation/changes.json +6 -0
  397. package/src/cap-counter/changes.json +12 -0
  398. package/src/config/changes.json +6 -0
  399. package/src/crypto-shredding/changes.json +12 -0
  400. package/src/custom-fields/changes.json +6 -0
  401. package/src/delivery/changes.json +12 -0
  402. package/src/document-ingest-foundation/changes.json +6 -0
  403. package/src/host-routing/changes.json +6 -0
  404. package/src/jobs/changes.json +6 -0
  405. package/src/mail-transport-smtp/changes.json +12 -0
  406. package/src/managed-pages/changes.json +6 -0
  407. package/src/secrets/changes.json +6 -0
  408. package/src/sessions/changes.json +19 -0
  409. package/src/step-dispatcher/changes.json +6 -0
  410. package/src/template-resolver/changes.json +54 -0
  411. package/src/tenant/changes.json +19 -0
  412. package/src/tenant-lifecycle/changes.json +6 -0
  413. package/src/user-data-rights/changes.json +36 -0
  414. package/src/waitlist/changes.json +18 -0
  415. package/src/workflow-runner/changes.json +6 -0
@@ -11,6 +11,8 @@ import { CONTRACT_TERMINATION_DECLARED_EVENT_QN, CONTRACT_TERMINATION_REVOKED_EV
11
11
  import { getSubscriptionForTenant } from "../get-subscription-for-tenant.js";
12
12
  import { SubscriptionFoundationHandlers } from "../web/constants.js";
13
13
  import { notifyOperator } from "./termination-notify.js";
14
+ import { hasOtherOpenProviderCancel } from "./termination-open-cancels.js";
15
+ import { TERMINATION_PROVIDER_LOCK_KEY, TERMINATION_PROVIDER_LOCK_NAMESPACE, } from "./termination-provider-lock.js";
14
16
  import { findRecordedTermination, hasRevocation, resumeAtProvider } from "./termination-record.js";
15
17
  export const REVOCATION_PATHS = {
16
18
  de: "/legal/kuendigen/zuruecknehmen",
@@ -66,7 +68,7 @@ export function createRevokeContractTerminationHandler() {
66
68
  handler: async (event, ctx) => {
67
69
  // @cast-boundary engine-payload — dispatcher-zod-validated payload
68
70
  const payload = event.payload;
69
- const result = await ctx.writeAs(createSystemUser(payload.tenantId), SubscriptionFoundationHandlers.recordContractTerminationRevocation, { requestId: payload.requestId, token: payload.token });
71
+ const result = await ctx.dispatch.writeAs(createSystemUser(payload.tenantId), SubscriptionFoundationHandlers.recordContractTerminationRevocation, { requestId: payload.requestId, token: payload.token });
70
72
  return {
71
73
  isSuccess: true,
72
74
  data: { outcome: result.isSuccess ? parseOutcome(result.data) : "invalid" },
@@ -84,11 +86,13 @@ export function createRecordContractTerminationRevocationHandler(options, consum
84
86
  // @cast-boundary engine-payload — dispatcher-zod-validated payload
85
87
  const payload = event.payload;
86
88
  const tenantId = event.user.tenantId;
89
+ // The provider cancel/resume and the stream append must not interleave across declarations of the same subscription.
90
+ await ctx.db.advisoryXactLock(TERMINATION_PROVIDER_LOCK_NAMESPACE, TERMINATION_PROVIDER_LOCK_KEY);
87
91
  const outcome = (value) => ({
88
92
  isSuccess: true,
89
93
  data: { outcome: value },
90
94
  });
91
- const stream = await ctx.fetchForWriting({
95
+ const stream = await ctx.events.fetchForWriting({
92
96
  aggregateId: subscriptionAggregateId(tenantId),
93
97
  aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
94
98
  });
@@ -111,22 +115,16 @@ export function createRecordContractTerminationRevocationHandler(options, consum
111
115
  return outcome("too-late");
112
116
  }
113
117
  const reasons = ["revoked"];
114
- if (requested?.providerCancel === "period-end") {
118
+ if (requested?.providerCancel === "period-end" &&
119
+ !hasOtherOpenProviderCancel(stream.events, payload.requestId)) {
115
120
  reasons.push(...(await resumeAtProvider(ctx, subscription)));
116
121
  }
117
122
  // Re-fetch after the external call, like record-contract-termination.
118
- const streamAfterProviderCall = await ctx.fetchForWriting({
123
+ const streamAfterProviderCall = await ctx.events.fetchForWriting({
119
124
  aggregateId: subscriptionAggregateId(tenantId),
120
125
  aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
121
126
  });
122
- // A cancel recorded between the first fetch and now was never resumed above.
123
- const requestedMeanwhile = requested === undefined
124
- ? findRecordedTermination(streamAfterProviderCall.events, payload.requestId)
125
- : undefined;
126
- if (requestedMeanwhile?.providerCancel === "period-end") {
127
- reasons.push(...(await resumeAtProvider(ctx, await getSubscriptionForTenant(ctx, tenantId))));
128
- }
129
- const providerCancel = (requested ?? requestedMeanwhile)?.providerCancel;
127
+ const providerCancel = requested?.providerCancel;
130
128
  const revoked = {
131
129
  requestId: payload.requestId,
132
130
  revokedAtIso: now.toString(),
@@ -16,7 +16,7 @@ import { CONSENT_LOCALES } from "./web/consumer-protection/consent-locale.js";
16
16
  // one stream; the subscriptionAggregateId helper provides the stream ID.
17
17
  export const SUBSCRIPTION_AGGREGATE_TYPE = "subscription";
18
18
  // Event-name constants — short form (for r.defineEvent) and qualified
19
- // FQN (for ctx.unsafeAppendEvent and projection-apply keys).
19
+ // FQN (for ctx.events.unsafeAppend and projection-apply keys).
20
20
  export const SUBSCRIPTION_CREATED_EVENT_SHORT = "subscription-created";
21
21
  export const SUBSCRIPTION_UPDATED_EVENT_SHORT = "subscription-updated";
22
22
  export const SUBSCRIPTION_CANCELED_EVENT_SHORT = "subscription-canceled";
@@ -122,7 +122,7 @@ export function createBillingFoundationFeature(options = {}) {
122
122
  // providerCustomerId is tenantOwned ciphertext, not plaintext personal data.
123
123
  r.defineEvent(PAYMENT_RECEIVED_EVENT_SHORT, paymentEventPayloadSchema, { piiFields: "none" });
124
124
  // Inline projection: materialized current state in `read_subscriptions`.
125
- // Apply runs in the same TX as ctx.unsafeAppendEvent — read-your-own-
125
+ // Apply runs in the same TX as ctx.events.unsafeAppend — read-your-own-
126
126
  // write without a dispatcher tick.
127
127
  r.projection({
128
128
  name: "subscription",
@@ -203,11 +203,11 @@ export function createBillingFoundationFeature(options = {}) {
203
203
  perTenant: true,
204
204
  runOnBoot: true,
205
205
  handler: async (_payload, ctx) => {
206
- const result = await ctx.write(SubscriptionFoundationHandlers.syncSubscription, {});
206
+ const result = await ctx.dispatch.write(SubscriptionFoundationHandlers.syncSubscription, {});
207
207
  if (!result.isSuccess) {
208
208
  throw new Error(`billing-foundation:sync-subscriptions: sync-subscription write failed: ${JSON.stringify(result.error)}`);
209
209
  }
210
- ctx.log.info(`[billing-foundation:sync-subscriptions] ${JSON.stringify(result.data)}`);
210
+ ctx.obs.log.info(`[billing-foundation:sync-subscriptions] ${JSON.stringify(result.data)}`);
211
211
  },
212
212
  });
213
213
  r.useExtension(EXT_TENANT_DATA, "subscription", {
@@ -18,4 +18,4 @@ export type SubscriptionView = {
18
18
  /** Returns the tenant's only subscription row (deterministic
19
19
  * aggregate ID), or null if the tenant has never subscribed. The status
20
20
  * may be active/canceled/past_due/etc.; the caller decides what to do. */
21
- export declare function getSubscriptionForTenant(ctx: HandlerContext, tenantId: string): Promise<SubscriptionView | null>;
21
+ export declare function getSubscriptionForTenant(ctx: Pick<HandlerContext, "db">, tenantId: string): Promise<SubscriptionView | null>;
@@ -9,7 +9,7 @@
9
9
  // with no second append.
10
10
  // 2. Type mapping: SubscriptionEvent.type (= normalized by the plugin)
11
11
  // → one of the 5 ES event types.
12
- // 3. ctx.unsafeAppendEvent — the inline projection materializes the
12
+ // 3. ctx.events.unsafeAppend — the inline projection materializes the
13
13
  // `read_subscriptions` row in the same TX.
14
14
  import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
15
15
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
@@ -126,7 +126,7 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
126
126
  // streams, optimize via snapshot or a per-tenant dedup table as the
127
127
  // idempotency anchor (like cap-counter).
128
128
  // ---------------------------------------------------------------
129
- const existingEvents = await ctx.loadAggregate(aggId);
129
+ const existingEvents = await ctx.events.load(aggId);
130
130
  const alreadySeen = existingEvents.some((e) => {
131
131
  const headers = e.metadata.headers ?? {};
132
132
  return (headers["providerEventId"] === payload.providerEventId &&
@@ -139,7 +139,7 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
139
139
  // late delivery: appending it would roll the row back (a canceled subscription
140
140
  // would come back active). Invoice events carry the live state, not a snapshot.
141
141
  if (isLateStateEvent(existingEvents, payload)) {
142
- ctx.log?.warn(`[billing-foundation:process-event] dropped late ${payload.type} ${payload.providerEventId}: a newer provider event is already stored`);
142
+ ctx.obs.log?.warn(`[billing-foundation:process-event] dropped late ${payload.type} ${payload.providerEventId}: a newer provider event is already stored`);
143
143
  return { duplicate: false, stale: true, subscriptionAggregateId: aggId };
144
144
  }
145
145
  // ---------------------------------------------------------------
@@ -196,7 +196,7 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
196
196
  providerEventId: payload.providerEventId,
197
197
  providerName: payload.providerName,
198
198
  };
199
- await ctx.unsafeAppendEvent({
199
+ await ctx.events.unsafeAppend({
200
200
  aggregateId: aggId,
201
201
  aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
202
202
  type: esEventType,
@@ -240,7 +240,7 @@ export function createProcessEventHandler(now) {
240
240
  const payload = event.payload;
241
241
  const current = await getSubscriptionForTenant(ctx, event.user.tenantId);
242
242
  if (isStaleSubscriptionEvent(current, payload, now())) {
243
- ctx.log?.warn(`[billing-foundation:process-event] dropped stale ${payload.type} ${payload.providerEventId}: tenant already tracks another subscription`);
243
+ ctx.obs.log?.warn(`[billing-foundation:process-event] dropped stale ${payload.type} ${payload.providerEventId}: tenant already tracks another subscription`);
244
244
  return {
245
245
  isSuccess: true,
246
246
  data: {
@@ -6,7 +6,7 @@
6
6
  // 1. Idempotency-check: loads the payment-stream and scans for an
7
7
  // already-seen `metadata.providerEventId`. A provider-replay with
8
8
  // the same event-id → duplicate=true, no second append.
9
- // 2. ctx.unsafeAppendEvent — inline-projection materializes a new
9
+ // 2. ctx.events.unsafeAppend — inline-projection materializes a new
10
10
  // `read_payments`-row (INSERT-once, not UPSERT — every payment is
11
11
  // its own fact, not a state-update).
12
12
  import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
@@ -41,7 +41,7 @@ export const processPaymentEventHandler = {
41
41
  // (a tenant with hundreds of purchases scans hundreds of events;
42
42
  // optimize via a per-tenant providerEventId-index if that bites).
43
43
  // ---------------------------------------------------------------
44
- const existingEvents = await ctx.loadAggregate(aggId);
44
+ const existingEvents = await ctx.events.load(aggId);
45
45
  const alreadySeen = existingEvents.some((e) => {
46
46
  const headers = e.metadata.headers ?? {};
47
47
  return (headers["providerEventId"] === payload.providerEventId &&
@@ -81,7 +81,7 @@ export const processPaymentEventHandler = {
81
81
  providerCheckoutId: payload.providerCheckoutId,
82
82
  }),
83
83
  };
84
- await ctx.unsafeAppendEvent({
84
+ await ctx.events.unsafeAppend({
85
85
  aggregateId: aggId,
86
86
  aggregateType: PAYMENT_AGGREGATE_TYPE,
87
87
  type: PAYMENT_RECEIVED_EVENT_QN,
@@ -50,7 +50,7 @@ export async function resolvePlanPrices(ctx, plugin, catalog) {
50
50
  }
51
51
  catch (error) {
52
52
  const message = error instanceof Error ? error.message : String(error);
53
- ctx.log?.warn("billing-foundation: retrievePrices threw, all plan prices unavailable", {
53
+ ctx.obs.log?.warn("billing-foundation: retrievePrices threw, all plan prices unavailable", {
54
54
  message,
55
55
  });
56
56
  return result;
@@ -67,7 +67,7 @@ export async function resolvePlanPrices(ctx, plugin, catalog) {
67
67
  result.set(tier, { priceId: chosen.priceId, price: chosen });
68
68
  }
69
69
  else if (active.length > 1) {
70
- ctx.log?.warn(`billing-foundation: tier "${tier}" has ${active.length} active flat-amount prices — expected exactly one, price stays unavailable`);
70
+ ctx.obs.log?.warn(`billing-foundation: tier "${tier}" has ${active.length} active flat-amount prices — expected exactly one, price stays unavailable`);
71
71
  }
72
72
  }
73
73
  return result;
@@ -1,7 +1,7 @@
1
1
  // Inline projection for `read_subscriptions`. Materializes the 5
2
2
  // subscription events into one row per tenant.
3
3
  //
4
- // Apply runs in the same transaction as ctx.unsafeAppendEvent, so the caller
4
+ // Apply runs in the same transaction as ctx.events.unsafeAppend, so the caller
5
5
  // sees its write state immediately (no dispatcher tick required). The PK is
6
6
  // event.aggregateId (= deterministic uuidv5 per tenant), so replays collide
7
7
  // on the PK instead of creating duplicate rows.
@@ -18,7 +18,7 @@
18
18
  // 2. **Post-tenant-resolution:** `createPortalSession` +
19
19
  // `cancelSubscription` are called from regular write handlers with
20
20
  // a fully resolved HandlerContext. The plugin can use ctx.config +
21
- // ctx.secrets for tenant-specific configuration (e.g. tenant-owned
21
+ // ctx.services.secrets for tenant-specific configuration (e.g. tenant-owned
22
22
  // customer-id mappings).
23
23
  //
24
24
  // Foundation uses only the common subset of provider functionality;
@@ -16,7 +16,7 @@ export declare function capCounterAggregateId(tenantId: string, capName: string,
16
16
  * different uuidv5 namespace, different output UUID. This also prevents an
17
17
  * accidental calendar increment from landing on the rolling stream.
18
18
  *
19
- * **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.write`) calls
19
+ * **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.dispatch.write`) calls
20
20
  * this with tenantId + capName and appends increment events to the stream.
21
21
  * Race-free because the event store appends with an auto-incrementing version.
22
22
  */
@@ -30,7 +30,7 @@ export function capCounterAggregateId(tenantId, capName, periodStartIso) {
30
30
  * different uuidv5 namespace, different output UUID. This also prevents an
31
31
  * accidental calendar increment from landing on the rolling stream.
32
32
  *
33
- * **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.write`) calls
33
+ * **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.dispatch.write`) calls
34
34
  * this with tenantId + capName and appends increment events to the stream.
35
35
  * Race-free because the event store appends with an auto-incrementing version.
36
36
  */
@@ -1,5 +1,5 @@
1
1
  import { type TenantDb } from "@cosmicdrift/kumiko-framework/db";
2
- import { type HandlerContext, type JobContext, type SessionUser, type TenantId, type WriteResult } from "@cosmicdrift/kumiko-framework/engine";
2
+ import { type HandlerContext, type JobContext, type ReservationContext, type SessionUser, type TenantId, type WriteResult } from "@cosmicdrift/kumiko-framework/engine";
3
3
  export declare function retryCounterWriteOnVersionConflict(writeAttempt: () => Promise<WriteResult>): Promise<WriteResult>;
4
4
  export declare function retryOnStreamVersionConflict<T>(attempt: () => Promise<T>): Promise<T>;
5
5
  export type BookCapUsageOptions = {
@@ -9,7 +9,7 @@ export type BookCapUsageOptions = {
9
9
  readonly outsideTransaction?: boolean;
10
10
  readonly guardCurrentValue?: (currentValue: number) => void;
11
11
  };
12
- export type CapCounterContext = HandlerContext | JobContext;
12
+ export type CapCounterContext = HandlerContext | JobContext | ReservationContext;
13
13
  export type CapCounterScope = {
14
14
  readonly user: SessionUser;
15
15
  readonly readDb: TenantDb;
@@ -60,7 +60,7 @@ function assertDbServesUser(db, user) {
60
60
  return db;
61
61
  }
62
62
  // Jobs write in place (their db is not transaction-bound); systemScope handlers reach both dbs via
63
- // systemDb, because their ctx.db / ctx.dbOutsideTransaction are fail-closed proxies.
63
+ // systemDb, because their ctx.db / ctx.db.outsideTransaction are fail-closed proxies.
64
64
  export function resolveCapCounterScope(ctx, outsideTransaction) {
65
65
  // A job context spread into a handler-shaped one (user set) is a handler context: its user, not
66
66
  // the job's systemUser, names the metered tenant.
@@ -83,10 +83,10 @@ export function resolveCapCounterScope(ctx, outsideTransaction) {
83
83
  : readDb;
84
84
  return { user, readDb, writeDb };
85
85
  }
86
- const writeDb = outsideTransaction ? ctx.dbOutsideTransaction : ctx.db;
86
+ const writeDb = outsideTransaction ? ctx.db.outsideTransaction : ctx.db;
87
87
  if (!writeDb) {
88
88
  throw new Error(outsideTransaction
89
- ? "cap-counter: outsideTransaction requested but ctx.dbOutsideTransaction is undefined"
89
+ ? "cap-counter: outsideTransaction requested but ctx.db.outsideTransaction is undefined"
90
90
  : "cap-counter: ctx.db missing — run inside a handler context");
91
91
  }
92
92
  return {
@@ -186,6 +186,16 @@ function bookCalendarDelta(txDb, user, period, delta) {
186
186
  return Promise.resolve({ isSuccess: true, data: {} });
187
187
  return applyCapDeltaOn(txDb, user, { ...period, amount: Math.abs(delta) }, delta > 0 ? "add" : "subtract");
188
188
  }
189
+ // Asked after a missed or lost claim. A released tombstone committed since the first read does not
190
+ // count: the caller's own tombstone insert then conflicts with it, and the retry claims that row.
191
+ async function hasSettledTombstone(txDb, user, id) {
192
+ const settled = await txDb.fetchOne(capReservationsTable, {
193
+ id,
194
+ tenantId: user.tenantId,
195
+ settledAt: { ne: null },
196
+ });
197
+ return settled !== undefined;
198
+ }
189
199
  async function settleInTransaction(txDb, user, id, options) {
190
200
  const ownRow = { id, tenantId: user.tenantId };
191
201
  const open = await txDb.fetchOne(capReservationsTable, {
@@ -205,12 +215,11 @@ async function settleInTransaction(txDb, user, id, options) {
205
215
  await txDb.updateMany(capReservationsTable, { settledAt: now, expiresAt: now.add({ minutes: CAP_SETTLED_TOMBSTONE_TTL_MINUTES }) }, ownRow);
206
216
  return booked;
207
217
  }
208
- // The claim lost to a concurrent settle or release: a tombstone means the amount is booked.
209
- if (await txDb.fetchOne(capReservationsTable, ownRow)) {
218
+ if (await hasSettledTombstone(txDb, user, id))
210
219
  return NOTHING_TO_RELEASE;
211
- }
212
220
  // Row swept or released: its amount was given back, so the full actual amount is booked and a
213
- // tombstone makes a repeated settle a no-op. A parallel settle inserting first fails this insert.
221
+ // tombstone makes a repeated settle a no-op. A parallel settle or consume inserting first fails
222
+ // this insert.
214
223
  // Booking first: a lost counter race returns a result instead of throwing, so the transaction
215
224
  // commits and a tombstone written before it would turn the retry into a no-op.
216
225
  const booked = await bookCalendarDelta(txDb, user, options, options.actualAmount);
@@ -286,7 +295,7 @@ async function consumeClaimedRow(txDb, user, claimed, options) {
286
295
  return NOTHING_TO_RELEASE;
287
296
  }
288
297
  // Row already expired away: the units are booked directly and a released tombstone remembers them
289
- // so a later settle subtracts them. A parallel consume inserting first fails this insert.
298
+ // so a later settle subtracts them. A parallel consume or settle inserting first fails this insert.
290
299
  async function consumeWithoutRow(txDb, user, id, options) {
291
300
  // Booking first, for the same reason as settle's missing-row path.
292
301
  const booked = await bookCalendarDelta(txDb, user, options, options.units);
@@ -320,7 +329,7 @@ async function consumeInTransaction(txDb, user, id, options) {
320
329
  const claimed = open && (await claimReservationRow(txDb, user, open));
321
330
  if (claimed)
322
331
  return consumeClaimedRow(txDb, user, claimed, options);
323
- if (await txDb.fetchOne(capReservationsTable, ownRow)) {
332
+ if (await hasSettledTombstone(txDb, user, id)) {
324
333
  throw new CapReservationSettledError(options.capName);
325
334
  }
326
335
  return consumeWithoutRow(txDb, user, id, options);
@@ -364,7 +373,7 @@ export async function releaseExpiredCapReservations(ctx, capName, outsideTransac
364
373
  await releaseCapReservation(ctx, id, outsideTransaction);
365
374
  }
366
375
  catch (error) {
367
- ctx.log?.error("releasing an expired cap reservation failed", { id, capName, error });
376
+ ctx.obs.log?.error("releasing an expired cap reservation failed", { id, capName, error });
368
377
  }
369
378
  }
370
379
  }
@@ -10,7 +10,7 @@ export const CAP_COUNTER_ROLLING_AGGREGATE_TYPE = "cap-counter-rolling";
10
10
  // Custom event types for rolling-window counters. The pair is symmetric:
11
11
  // _SHORT — matches `r.defineEvent(short, schema)` in the registrar
12
12
  // (the framework automatically prefixes it to the QN)
13
- // _QN — qualified form for `ctx.unsafeAppendEvent({type})`, the
13
+ // _QN — qualified form for `ctx.events.unsafeAppend({type})`, the
14
14
  // `events.type` column, and `registry.getEvent(qn)` lookups
15
15
  // Both MUST stay consistent; the feature test pins against drift.
16
16
  export const ROLLING_INCREMENTED_EVENT_SHORT = "rolling-incremented";
@@ -31,7 +31,7 @@
31
31
  // ai-foundation are BYOK by default and currently have no platform pool
32
32
  // to count. cap-counter is generic: an app owner who wants to use the
33
33
  // counter calls `bookCapUsage(...)` /
34
- // `ctx.write(CapCounterHandlers.incrementRolling, ...)` from their own handler.
34
+ // `ctx.dispatch.write(CapCounterHandlers.incrementRolling, ...)` from their own handler.
35
35
  // - **No reset cron for the calendar period.** It works without one: the
36
36
  // periodStartIso part of the aggregate id rolls over to a fresh counter
37
37
  // at the period boundary. Old rows remain for auditing.
@@ -54,6 +54,7 @@ import { CAP_COUNTER_I18N } from "./i18n.js";
54
54
  import { capReservationsTableMeta } from "./tables.js";
55
55
  export function createCapCounterFeature() {
56
56
  return defineFeature(CAP_COUNTER_FEATURE, (r) => {
57
+ r.adoptsBundledLists();
57
58
  r.describe("Tracks per-tenant usage against configurable limits using two complementary storage models: calendar-period counters (one projection row per tenant/capName/period, reset implicitly by period rollover) and rolling-window counters (append-only event stream, no projection). Use `enforceCap` / `enforceRollingCap` (or the `withCapEnforcement` / `withRollingCapEnforcement` handler wrappers) in your write-handlers to check limits with soft-warn and hard-block tolerances; call `enforceCapAndMaybeNotify` when you also want to trigger a delivery notification on soft-threshold hits.");
58
59
  r.uiHints({
59
60
  displayLabel: "Cap Counter · Usage Limits",
@@ -65,7 +66,7 @@ export function createCapCounterFeature() {
65
66
  reason: "coordination.cap_reservation_ledger",
66
67
  });
67
68
  // Custom domain event for the rolling counter. r.defineEvent registers the
68
- // schema with the registry; ctx.unsafeAppendEvent in the handler reuses the
69
+ // schema with the registry; ctx.events.unsafeAppend in the handler reuses the
69
70
  // same schema for append-time validation. QN after prefixing:
70
71
  // "cap-counter:event:rolling-incremented" (see
71
72
  // ROLLING_INCREMENTED_EVENT_QN).
@@ -63,7 +63,7 @@ export const incrementRollingCapHandler = {
63
63
  // unsafeAppendEvent: the strict appendEvent is bound to an app's own event map, which
64
64
  // bundled-features cannot see. Schema validation still runs because r.defineEvent
65
65
  // registered the schema.
66
- await ctx.unsafeAppendEvent({
66
+ await ctx.events.unsafeAppend({
67
67
  aggregateId,
68
68
  aggregateType: CAP_COUNTER_ROLLING_AGGREGATE_TYPE,
69
69
  type: ROLLING_INCREMENTED_EVENT_QN,
@@ -22,7 +22,7 @@ export function chainReservations(inner, own) {
22
22
  await innerHandle.release();
23
23
  }
24
24
  catch (releaseError) {
25
- ctx.log?.error("releasing the inner cap reservation failed", { error: releaseError });
25
+ ctx.obs.log?.error("releasing the inner cap reservation failed", { error: releaseError });
26
26
  }
27
27
  throw error;
28
28
  }
@@ -1,4 +1,4 @@
1
- import type { HandlerContext, WriteEvent, WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
1
+ import type { ReservationContext, WriteEvent, WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
2
2
  import { type CapToleranceProfileName, type SoftHitNotifier } from "./enforce-cap.ts";
3
3
  /**
4
4
  * Dynamic cap definition resolved for each handler invocation. A typical caller reads the tenant
@@ -20,7 +20,7 @@ export type CalendarCapDef = {
20
20
  * to compute the cap-spec for THIS request (e.g. limit derived from
21
21
  * tenant-tier). Sync OR async — async lets the caller fetch the
22
22
  * tier from DB. */
23
- export type CalendarCapResolver = (event: WriteEvent, ctx: HandlerContext) => Promise<CalendarCapDef> | CalendarCapDef;
23
+ export type CalendarCapResolver = (event: WriteEvent, ctx: ReservationContext) => Promise<CalendarCapDef> | CalendarCapDef;
24
24
  /**
25
25
  * Wrap a write-handler with calendar-period cap-enforcement.
26
26
  *
@@ -40,7 +40,7 @@ export type RollingCapDef = {
40
40
  readonly amount?: number;
41
41
  readonly notify: SoftHitNotifier;
42
42
  };
43
- export type RollingCapResolver = (event: WriteEvent, ctx: HandlerContext) => Promise<RollingCapDef> | RollingCapDef;
43
+ export type RollingCapResolver = (event: WriteEvent, ctx: ReservationContext) => Promise<RollingCapDef> | RollingCapDef;
44
44
  /**
45
45
  * Wrap a write-handler with rolling-window cap-enforcement.
46
46
  *
@@ -12,7 +12,7 @@
12
12
  // after a rollback, failure result or failed COMMIT only gives back what is still booked, so an
13
13
  // unknown COMMIT outcome cannot under-count. No connection is held across the handler. Rows a
14
14
  // crashed process left behind expire after CAP_RESERVATION_TTL_MINUTES and are given back before the
15
- // next reserve of the same cap. Reached through a nested ctx.write the cap is not reserved, so the
15
+ // next reserve of the same cap. Reached through a nested ctx.dispatch.write the cap is not reserved, so the
16
16
  // dispatcher rejects that call unless the top-level batch reserved the same handler.
17
17
  //
18
18
  // The wrapper spreads the wrapped handler and chains its reserveBeforeTransaction: inner reserves
@@ -36,6 +36,7 @@ export function createCapOverviewFeature(opts) {
36
36
  }
37
37
  }
38
38
  return defineFeature(CAP_OVERVIEW_FEATURE, (r) => {
39
+ r.adoptsBundledLists();
39
40
  r.describe("Read-only visibility into per-tenant tier assignment and cap usage. SystemAdmin gets a platform-wide tenant list with usage bars; tenant admins (or the roles in `usageVisibleTo`) get their own tenant's usage as dashboard cards. Reads tier-engine, billing-foundation, and tenant data plus app-owned usage tables via caller-supplied CapSpec callbacks — never writes.");
40
41
  r.uiHints({
41
42
  displayLabel: "Cap Overview · Tier & Usage Visibility",
@@ -51,7 +51,7 @@ export function createCapsUsageQuery(caps, usageRoles) {
51
51
  const tier = checkedRows[0]?.tier ?? "";
52
52
  // Override path: limits must read the TARGET tenant's config, not the
53
53
  // SystemAdmin caller's.
54
- const config = override === undefined ? ctx.config : ctx.configFor?.(targetTenantId);
54
+ const config = override === undefined ? ctx.config : ctx.tenants.config?.(targetTenantId);
55
55
  const rows = await Promise.all(caps.map(async (cap) => {
56
56
  const used = await cap.usage(db, targetTenantId);
57
57
  const limit = await cap.limit(tier, { config });
@@ -163,7 +163,7 @@ export function createTenantCapsListQuery(caps, listCaps) {
163
163
  const perTenantEntries = await Promise.all(pageTenantIds.map(async (tenantId) => [tenantId, await cap.usage(db, tenantId)]));
164
164
  usageByCap.set(cap.id, new Map(perTenantEntries));
165
165
  }));
166
- const limitByTenantAndCap = await resolveLimitsByTenantAndCap(listedCaps, page, ctx.configFor);
166
+ const limitByTenantAndCap = await resolveLimitsByTenantAndCap(listedCaps, page, ctx.tenants.config);
167
167
  const rows = page.map((row) => {
168
168
  const capFields = {};
169
169
  for (const cap of listedCaps) {
@@ -1,6 +1,6 @@
1
1
  import type { ComplianceProfileKey, ComplianceProfileOverride } from "@cosmicdrift/kumiko-framework/compliance";
2
2
  import { type DbConnection } from "@cosmicdrift/kumiko-framework/db";
3
- import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { type SessionUser, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
4
4
  import { type SeedIfExists } from "@cosmicdrift/kumiko-framework/seeding";
5
5
  export type SeedComplianceProfileOptions = {
6
6
  readonly tenantId: TenantId;
@@ -10,14 +10,20 @@
10
10
  //
11
11
  // The pattern matches seedTextBlock in template-resolver/seeding.ts.
12
12
  import { createEventStoreExecutor, createTenantDb, fetchOne, } from "@cosmicdrift/kumiko-framework/db";
13
- import { runEventStoreSeed, TestUsers, } from "@cosmicdrift/kumiko-framework/seeding";
13
+ import { createSystemUser, } from "@cosmicdrift/kumiko-framework/engine";
14
+ import { runEventStoreSeed } from "@cosmicdrift/kumiko-framework/seeding";
14
15
  import { tenantComplianceProfileEntity, tenantComplianceProfileTable, } from "./schema/profile-selection.js";
15
16
  const executor = createEventStoreExecutor(tenantComplianceProfileTable, tenantComplianceProfileEntity, { entityName: "tenant-compliance-profile" });
16
17
  export async function seedComplianceProfile(db, opts) {
17
18
  // user.tenantId must equal opts.tenantId so the Event Store stream
18
19
  // and projection remain in the same tenant bucket.
19
- const by = opts.by ?? { ...TestUsers.systemAdmin, tenantId: opts.tenantId };
20
- const tdb = createTenantDb({ db, tenantId: opts.tenantId, mode: "system" });
20
+ const by = opts.by ?? createSystemUser(opts.tenantId);
21
+ const tdb = createTenantDb({
22
+ db,
23
+ tenantId: opts.tenantId,
24
+ mode: "system",
25
+ reason: "seeding writes the compliance profile into an explicitly named tenant",
26
+ });
21
27
  const overrideJson = opts.override !== undefined ? JSON.stringify(opts.override) : null;
22
28
  const existing = (await fetchOne(db, tenantComplianceProfileTable, {
23
29
  tenantId: opts.tenantId,
@@ -64,14 +64,14 @@ export function createConfigAccessorFactory(registry, resolver) {
64
64
  // session reads "config feature not wired into AppContext" instead of a
65
65
  // generic "configResolver missing".
66
66
  export function requireConfigResolver(ctx, handlerName) {
67
- if (!ctx.configResolver) {
67
+ if (!ctx.services.configResolver) {
68
68
  throw new InternalError({
69
- message: `[${handlerName}] ctx.configResolver missing — pass createConfigAccessorFactory's ` +
69
+ message: `[${handlerName}] ctx.services.configResolver missing — pass createConfigAccessorFactory's ` +
70
70
  `output via extraContext.configAccessorFactory and the resolver via ` +
71
71
  `extraContext.configResolver at boot.`,
72
72
  });
73
73
  }
74
- return ctx.configResolver;
74
+ return ctx.services.configResolver;
75
75
  }
76
76
  // Config declares r.systemScope() (raw ctx.db is unfiltered across tenants
77
77
  // in that mode), so config's own query handlers must go through ctx.systemDb's
@@ -101,13 +101,13 @@ export function requireSystemDb(ctx, handlerName, tenantId) {
101
101
  // if a handler tries to write an encrypted key without the provider in
102
102
  // place, pointing at the exact wiring gap).
103
103
  export function requireConfigEncryption(ctx, handlerName) {
104
- if (!ctx.configEncryption) {
104
+ if (!ctx.services.configEncryption) {
105
105
  throw new InternalError({
106
- message: `[${handlerName}] ctx.configEncryption missing — at least one config key declares ` +
106
+ message: `[${handlerName}] ctx.services.configEncryption missing — at least one config key declares ` +
107
107
  `encrypted: true, so the boot wiring must pass an EnvelopeCipher via ` +
108
108
  `extraContext.configEncryption (run{Prod,Dev}App wire it automatically from ` +
109
109
  `KUMIKO_SECRETS_MASTER_KEY_V<n>; same instance the resolver was built with).`,
110
110
  });
111
111
  }
112
- return ctx.configEncryption;
112
+ return ctx.services.configEncryption;
113
113
  }
@@ -38,7 +38,7 @@ export const cascadeQuery = defineQueryHandler({
38
38
  keyDefs.set(key, keyDefAtScope(keyDef, query.payload.scope, query.user.roles));
39
39
  filteredKeys.push(key);
40
40
  }
41
- const cascades = await resolver.getCascadeBatch(filteredKeys, keyDefs, query.user.tenantId, query.user.id, db, ctx.secrets);
41
+ const cascades = await resolver.getCascadeBatch(filteredKeys, keyDefs, query.user.tenantId, query.user.id, db, ctx.services.secrets);
42
42
  const result = {};
43
43
  for (const [key, rawCascade] of cascades) {
44
44
  const keyDef = keyDefs.get(key);
@@ -58,7 +58,7 @@ export async function collectMissingRequiredConfig(ctx, callerQn, user, db, gate
58
58
  candidates.set(qualifiedKey, keyDef);
59
59
  }
60
60
  const missing = [];
61
- const cascades = await resolver.getCascadeBatch([...candidates.keys()], candidates, user.tenantId, user.id, db, ctx.secrets);
61
+ const cascades = await resolver.getCascadeBatch([...candidates.keys()], candidates, user.tenantId, user.id, db, ctx.services.secrets);
62
62
  for (const [qualifiedKey, keyDef] of candidates) {
63
63
  // Deliberately the unredacted cascade value: readiness asks "does this
64
64
  // tenant functionally have the value?", and an inheritedToTenant:false key