@cosmicdrift/kumiko-samples 0.389.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 (296) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +25 -13
  3. package/packages/bundled-features/src/audit/feature.ts +1 -1
  4. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +1 -1
  5. package/packages/bundled-features/src/auth-email-password/bootstrap.ts +13 -1
  6. package/packages/bundled-features/src/auth-email-password/changes.json +6 -0
  7. package/packages/bundled-features/src/auth-email-password/feature.ts +2 -2
  8. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +7 -7
  9. package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.write.ts +3 -3
  10. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  11. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +9 -7
  12. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +11 -7
  13. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -4
  14. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +2 -2
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +9 -7
  16. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +23 -16
  17. package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +3 -2
  18. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +12 -12
  19. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +12 -8
  20. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +4 -2
  21. package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +2 -2
  22. package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
  23. package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
  24. package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
  25. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +5 -5
  26. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +24 -14
  27. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +9 -3
  28. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +6 -1
  29. package/packages/bundled-features/src/auth-mfa/handlers/enable-start.write.ts +3 -1
  30. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +14 -9
  31. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -4
  32. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +25 -14
  33. package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +10 -4
  34. package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +9 -3
  35. package/packages/bundled-features/src/auth-mfa/verify-factor.ts +1 -1
  36. package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +2 -2
  37. package/packages/bundled-features/src/billing-foundation/changes.json +6 -0
  38. package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +1 -1
  39. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +5 -5
  40. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +10 -7
  41. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +15 -12
  42. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-open-cancels.ts +22 -0
  43. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-provider-lock.ts +4 -0
  44. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +4 -4
  45. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +17 -58
  46. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +10 -8
  47. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +18 -15
  48. package/packages/bundled-features/src/billing-foundation/events.ts +1 -1
  49. package/packages/bundled-features/src/billing-foundation/feature.ts +8 -3
  50. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +1 -1
  51. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +5 -5
  52. package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +3 -3
  53. package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +2 -2
  54. package/packages/bundled-features/src/billing-foundation/projection.ts +1 -1
  55. package/packages/bundled-features/src/billing-foundation/types.ts +1 -1
  56. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +1 -1
  57. package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +5 -4
  58. package/packages/bundled-features/src/cap-counter/cap-reservation.ts +1 -1
  59. package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
  60. package/packages/bundled-features/src/cap-counter/feature.ts +2 -2
  61. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
  62. package/packages/bundled-features/src/cap-counter/reservation-chain.ts +1 -1
  63. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +4 -4
  64. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -1
  65. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  66. package/packages/bundled-features/src/compliance-profiles/seeding.ts +12 -7
  67. package/packages/bundled-features/src/config/feature.ts +6 -6
  68. package/packages/bundled-features/src/config/handlers/cascade.query.ts +1 -1
  69. package/packages/bundled-features/src/config/handlers/readiness.query.ts +1 -1
  70. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +23 -13
  71. package/packages/bundled-features/src/config/handlers/reset.write.ts +3 -3
  72. package/packages/bundled-features/src/config/handlers/selected-extensions.query.ts +1 -1
  73. package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
  74. package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
  75. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +8 -8
  76. package/packages/bundled-features/src/crypto-shredding/resolve-file-bytes-deleter.ts +3 -3
  77. package/packages/bundled-features/src/custom-fields/changes.json +6 -0
  78. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
  79. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +1 -1
  80. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -1
  81. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
  82. package/packages/bundled-features/src/custom-fields/index.ts +4 -0
  83. package/packages/bundled-features/src/data-retention/feature.ts +1 -1
  84. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +12 -2
  85. package/packages/bundled-features/src/delivery/attempt-log.ts +1 -1
  86. package/packages/bundled-features/src/delivery/delivery-service.ts +6 -5
  87. package/packages/bundled-features/src/delivery/feature.ts +2 -2
  88. package/packages/bundled-features/src/delivery/jobs.ts +11 -11
  89. package/packages/bundled-features/src/delivery/testing.ts +1 -1
  90. package/packages/bundled-features/src/document-ingest-foundation/events.ts +1 -1
  91. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +1 -1
  92. package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +1 -1
  93. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  94. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  95. package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +5 -1
  96. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +7 -5
  97. package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
  98. package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -5
  99. package/packages/bundled-features/src/file-provider-s3/feature.ts +4 -1
  100. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +8 -8
  101. package/packages/bundled-features/src/form-draft/feature.ts +1 -1
  102. package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +22 -12
  103. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +3 -3
  104. package/packages/bundled-features/src/form-draft/handlers/get.query.ts +1 -1
  105. package/packages/bundled-features/src/foundation-shared/config-helpers.ts +1 -1
  106. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +1 -1
  107. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
  108. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +2 -2
  109. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
  110. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
  111. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +10 -10
  112. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
  113. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +1 -1
  114. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
  115. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +4 -1
  116. package/packages/bundled-features/src/jobs/changes.json +6 -0
  117. package/packages/bundled-features/src/jobs/events.ts +1 -1
  118. package/packages/bundled-features/src/jobs/handlers/consumer.write.ts +7 -4
  119. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  120. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +11 -5
  121. package/packages/bundled-features/src/jobs/handlers/retention-cleanup.job.ts +1 -1
  122. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  123. package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +1 -1
  124. package/packages/bundled-features/src/jobs/handlers/trigger.write.ts +2 -2
  125. package/packages/bundled-features/src/jobs/job-run-logger.ts +139 -90
  126. package/packages/bundled-features/src/legal-pages/README.md +1 -1
  127. package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +12 -8
  128. package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
  129. package/packages/bundled-features/src/mail-transport-smtp/changes.json +6 -0
  130. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +4 -1
  131. package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
  132. package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +1 -1
  133. package/packages/bundled-features/src/personal-access-tokens/feature.ts +1 -1
  134. package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +2 -2
  135. package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
  136. package/packages/bundled-features/src/rate-limiting/handlers/status.query.ts +2 -2
  137. package/packages/bundled-features/src/readiness/handlers/status.query.ts +4 -1
  138. package/packages/bundled-features/src/renderer-foundation/api.ts +3 -3
  139. package/packages/bundled-features/src/secrets/announce-secret-change.ts +4 -4
  140. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  141. package/packages/bundled-features/src/secrets/handlers/delete.write.ts +4 -3
  142. package/packages/bundled-features/src/secrets/handlers/rotate.job.ts +13 -8
  143. package/packages/bundled-features/src/secrets/handlers/set.write.ts +4 -3
  144. package/packages/bundled-features/src/secrets/secrets-context.ts +16 -6
  145. package/packages/bundled-features/src/secrets/write-gate.ts +1 -1
  146. package/packages/bundled-features/src/seo/feature.ts +1 -1
  147. package/packages/bundled-features/src/sessions/changes.json +12 -0
  148. package/packages/bundled-features/src/sessions/feature.ts +1 -1
  149. package/packages/bundled-features/src/sessions/handlers/cleanup.job.ts +2 -2
  150. package/packages/bundled-features/src/sessions/handlers/detail.query.ts +5 -2
  151. package/packages/bundled-features/src/sessions/handlers/list.query.ts +5 -2
  152. package/packages/bundled-features/src/sessions/handlers/mine.query.ts +5 -2
  153. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +3 -3
  154. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +1 -1
  155. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  156. package/packages/bundled-features/src/sessions/screens.ts +8 -4
  157. package/packages/bundled-features/src/sessions/session-revoked-event.ts +1 -1
  158. package/packages/bundled-features/src/sessions/web/session-device-cell.tsx +10 -12
  159. package/packages/bundled-features/src/sessions/web/user-agent.ts +12 -0
  160. package/packages/bundled-features/src/shared/index.ts +15 -0
  161. package/packages/bundled-features/src/shared/purge-files-reaching-subject.ts +12 -2
  162. package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -5
  163. package/packages/bundled-features/src/shared/stream-tenant-backfill.ts +201 -0
  164. package/packages/bundled-features/src/shared/stream-tenant-derived-state.ts +107 -0
  165. package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
  166. package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +1 -1
  167. package/packages/bundled-features/src/subscription-dodo/runtime.ts +7 -7
  168. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +5 -3
  169. package/packages/bundled-features/src/subscription-stripe/runtime.ts +7 -7
  170. package/packages/bundled-features/src/template-resolver/api.ts +3 -3
  171. package/packages/bundled-features/src/template-resolver/changes.json +18 -0
  172. package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +5 -4
  173. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +2 -2
  174. package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +1 -1
  175. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +2 -2
  176. package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
  177. package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +2 -2
  178. package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +6 -2
  179. package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +4 -2
  180. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +1 -1
  181. package/packages/bundled-features/src/template-resolver/seeding.ts +12 -2
  182. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +18 -13
  183. package/packages/bundled-features/src/template-resolver/template-resources.ts +4 -4
  184. package/packages/bundled-features/src/template-resolver/testing.ts +2 -2
  185. package/packages/bundled-features/src/template-resolver/web/document-insert-fields.tsx +222 -0
  186. package/packages/bundled-features/src/template-resolver/web/document-insert-model.ts +164 -0
  187. package/packages/bundled-features/src/template-resolver/web/document-insert-panel.tsx +115 -0
  188. package/packages/bundled-features/src/template-resolver/web/document-tiptap-bubble.tsx +177 -0
  189. package/packages/bundled-features/src/template-resolver/web/document-tiptap-content.tsx +38 -0
  190. package/packages/bundled-features/src/template-resolver/web/document-tiptap-context.ts +119 -0
  191. package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +51 -1098
  192. package/packages/bundled-features/src/template-resolver/web/document-tiptap-helpers.ts +161 -0
  193. package/packages/bundled-features/src/template-resolver/web/document-tiptap-layout.tsx +83 -0
  194. package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar-groups.tsx +153 -0
  195. package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar.tsx +182 -0
  196. package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +18 -0
  197. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +19 -6
  198. package/packages/bundled-features/src/template-resolver/web/use-document-tiptap-model.ts +138 -0
  199. package/packages/bundled-features/src/tenant/changes.json +19 -0
  200. package/packages/bundled-features/src/tenant/handlers/cancel-invitation.write.ts +1 -1
  201. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  202. package/packages/bundled-features/src/tenant/handlers/remove-member.write.ts +2 -2
  203. package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -1
  204. package/packages/bundled-features/src/tenant/index.ts +5 -0
  205. package/packages/bundled-features/src/tenant/invited-membership.ts +6 -1
  206. package/packages/bundled-features/src/tenant/schema/tenant.ts +4 -1
  207. package/packages/bundled-features/src/tenant/seeding.ts +65 -62
  208. package/packages/bundled-features/src/tenant/stream-tenant-backfill.ts +115 -0
  209. package/packages/bundled-features/src/tenant/testing.ts +77 -0
  210. package/packages/bundled-features/src/tenant-handover/events.ts +1 -1
  211. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  212. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +5 -5
  213. package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +22 -10
  214. package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +22 -11
  215. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +23 -12
  216. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +7 -3
  217. package/packages/bundled-features/src/tenant-lifecycle/testing.ts +9 -8
  218. package/packages/bundled-features/src/tier-engine/entity.ts +1 -1
  219. package/packages/bundled-features/src/tier-engine/feature.ts +8 -3
  220. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +3 -3
  221. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +7 -1
  222. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +3 -3
  223. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +71 -97
  224. package/packages/bundled-features/src/user/handlers/find-for-auth.query.ts +2 -2
  225. package/packages/bundled-features/src/user/handlers/update-roles.ts +2 -2
  226. package/packages/bundled-features/src/user/index.ts +2 -0
  227. package/packages/bundled-features/src/user/invited-global-roles.ts +6 -1
  228. package/packages/bundled-features/src/user/principal-status.ts +1 -1
  229. package/packages/bundled-features/src/user/seeding.ts +24 -6
  230. package/packages/bundled-features/src/user/testing.ts +22 -0
  231. package/packages/bundled-features/src/user-data-rights/COMPLIANCE.md +1 -1
  232. package/packages/bundled-features/src/user-data-rights/README.md +1 -1
  233. package/packages/bundled-features/src/user-data-rights/audit-download.ts +12 -2
  234. package/packages/bundled-features/src/user-data-rights/changes.json +24 -0
  235. package/packages/bundled-features/src/user-data-rights/feature.ts +29 -24
  236. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
  237. package/packages/bundled-features/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
  238. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +2 -2
  239. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +2 -2
  240. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
  241. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +4 -4
  242. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +12 -6
  243. package/packages/bundled-features/src/user-data-rights/index.ts +2 -0
  244. package/packages/bundled-features/src/user-data-rights/lib/mail-transport-resolver.ts +1 -1
  245. package/packages/bundled-features/src/user-data-rights/lib/storage-provider-resolver.ts +1 -1
  246. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +6 -1
  247. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +8 -2
  248. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +14 -3
  249. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +11 -1
  250. package/packages/bundled-features/src/user-data-rights/web/constants.ts +10 -0
  251. package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -1
  252. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
  253. package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +1 -1
  254. package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +2 -2
  255. package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
  256. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +9 -4
  257. package/packages/bundled-features/src/waitlist/legacy-migrate.ts +6 -1
  258. package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -1
  259. package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
  260. package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +1 -1
  261. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -3
  262. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
  263. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  264. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +8 -8
  265. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -4
  266. package/samples/apps/cap-billing-demo/src/feature.ts +19 -5
  267. package/samples/apps/config-demo/src/app/server.ts +1 -1
  268. package/samples/apps/styleguide/src/app/server.ts +2 -2
  269. package/samples/apps/use-all-bundled/feature-manifest.json +1 -1
  270. package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
  271. package/samples/apps/user-data-rights-demo/README.md +1 -1
  272. package/samples/apps/user-data-rights-demo/src/feature.ts +3 -2
  273. package/samples/recipes/api-worker-split/README.md +2 -1
  274. package/samples/recipes/api-worker-split/src/feature.ts +2 -1
  275. package/samples/recipes/cross-feature-events/README.md +5 -5
  276. package/samples/recipes/cross-feature-events/package.json +1 -1
  277. package/samples/recipes/cross-feature-events/src/feature.ts +4 -4
  278. package/samples/recipes/event-sourcing/README.md +5 -5
  279. package/samples/recipes/event-sourcing/package.json +1 -1
  280. package/samples/recipes/event-sourcing/src/feature.ts +15 -15
  281. package/samples/recipes/files-post-processing/README.md +1 -1
  282. package/samples/recipes/files-post-processing/package.json +1 -1
  283. package/samples/recipes/files-post-processing/src/feature.ts +6 -4
  284. package/samples/recipes/lane-routing/package.json +1 -1
  285. package/samples/recipes/legal-pages/README.md +2 -2
  286. package/samples/recipes/lifecycle-hooks/README.md +2 -2
  287. package/samples/recipes/lifecycle-hooks/src/feature.ts +1 -1
  288. package/samples/recipes/managed-config/README.md +3 -3
  289. package/samples/recipes/search/README.md +1 -1
  290. package/samples/recipes/search/src/feature.ts +3 -3
  291. package/samples/recipes/secrets-demo/README.md +1 -1
  292. package/samples/recipes/secrets-demo/src/feature.ts +5 -2
  293. package/samples/recipes/state-machine/src/events.ts +1 -1
  294. package/samples/recipes/state-machine/src/reducer.ts +1 -1
  295. package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
  296. package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
@@ -18,7 +18,17 @@ import {
18
18
  updateMany,
19
19
  upsertOnConflict,
20
20
  } from "@cosmicdrift/kumiko-framework/db";
21
- import { type Registry, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
21
+ import {
22
+ parseTenantId,
23
+ type Registry,
24
+ SYSTEM_TENANT_ID,
25
+ } from "@cosmicdrift/kumiko-framework/engine";
26
+ import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
27
+ import {
28
+ isRowLevelSecurityViolation,
29
+ runInRowLevelSecurityScope,
30
+ tenantRowLevelSecurityScope,
31
+ } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
22
32
  import type {
23
33
  JobDroppedMeta,
24
34
  JobLogEntry,
@@ -77,6 +87,16 @@ const DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
77
87
  // Run rows hang on SYSTEM_TENANT_ID and a null trigger is a cron/system run;
78
88
  // job-run-table.ts declares whenAbsent: "tenant" for exactly that case, so the
79
89
  // logger must resolve the same subject the entity chain would.
90
+ // Job callbacks run outside any request scope; the tenant tables enforce their rows per tenant, so each write
91
+ // declares the tenant it belongs to.
92
+ function inTenantScope<T>(tenantId: string, work: () => Promise<T>): Promise<T> {
93
+ const parsed = parseTenantId(tenantId);
94
+ if (parsed === null) {
95
+ throw new InternalError({ message: `job run logger got a malformed tenant id "${tenantId}"` });
96
+ }
97
+ return runInRowLevelSecurityScope(tenantRowLevelSecurityScope(parsed), work);
98
+ }
99
+
80
100
  function jobRunSubject(triggeredById: string | null): SubjectId {
81
101
  return triggeredById === null
82
102
  ? { kind: "tenant", tenantId: SYSTEM_TENANT_ID }
@@ -182,7 +202,7 @@ const TENANT_RUN_CONFLICT_KEYS = ["tenantId", "jobName", "bullJobId"] as const;
182
202
  function tenantJobFailureTarget(
183
203
  jobName: string,
184
204
  outcome: JobOutcomeMeta | undefined,
185
- ): Record<string, unknown> | null {
205
+ ): { tenantId: string; jobName: string; subject: string | null } | null {
186
206
  if (!outcome?.tenantVisible || outcome.tenantId === SYSTEM_TENANT_ID) return null;
187
207
  return { tenantId: outcome.tenantId, jobName, subject: outcome.tenantVisible.subject };
188
208
  }
@@ -202,19 +222,21 @@ async function recordTenantJobFailure(
202
222
  // partial unique indexes (subject NULL vs. set) are no ON CONFLICT target.
203
223
  // The lock makes the later of two racing final failures replace the earlier
204
224
  // one; the indexes are the backstop against any writer that skips the lock.
205
- await transaction(db, async (tx) => {
206
- await acquireNamespacedAdvisoryLock(
207
- tx,
208
- TENANT_JOB_FAILURE_LOCK_NAMESPACE,
209
- JSON.stringify([where["tenantId"], where["jobName"], where["subject"]]),
210
- );
211
- await deleteMany(tx, tenantJobFailuresTable, where);
212
- await insertOne(tx, tenantJobFailuresTable, {
213
- ...where,
214
- messageKey,
215
- failedAt: Temporal.Now.instant(),
216
- });
217
- });
225
+ await inTenantScope(where.tenantId, () =>
226
+ transaction(db, async (tx) => {
227
+ await acquireNamespacedAdvisoryLock(
228
+ tx,
229
+ TENANT_JOB_FAILURE_LOCK_NAMESPACE,
230
+ JSON.stringify([where["tenantId"], where["jobName"], where["subject"]]),
231
+ );
232
+ await deleteMany(tx, tenantJobFailuresTable, where);
233
+ await insertOne(tx, tenantJobFailuresTable, {
234
+ ...where,
235
+ messageKey,
236
+ failedAt: Temporal.Now.instant(),
237
+ });
238
+ }),
239
+ );
218
240
  }
219
241
 
220
242
  async function clearTenantJobFailure(
@@ -225,7 +247,7 @@ async function clearTenantJobFailure(
225
247
  const where = tenantJobFailureTarget(jobName, outcome);
226
248
  // skip: no tenant-visible target — nothing was ever recorded
227
249
  if (!where) return;
228
- await deleteMany(db, tenantJobFailuresTable, where);
250
+ await inTenantScope(where.tenantId, () => deleteMany(db, tenantJobFailuresTable, where));
229
251
  }
230
252
 
231
253
  // Tenant-visible run state. One row per BullMQ job in
@@ -260,28 +282,30 @@ async function recordTenantRunQueued(
260
282
  // skip: system-tenant run, no tenant-scoped query reads it
261
283
  if (!key) return;
262
284
  const now = Temporal.Now.instant();
263
- await transaction(db, async (tx) => {
264
- // A finished row under the same id is left over from before a Redis reset
265
- // (ids restart at 1), not this job. Only rows queued before this BullMQ job
266
- // was created qualify: a fast worker can finish before the hook runs (no
267
- // queuedAt), and a deduplicated add of an already finished job re-announces
268
- // it with a creation time older than its own row. Both rows must stay.
269
- await deleteMany(tx, tenantJobRunsTable, {
270
- tenantId: key.tenantId,
271
- jobName,
272
- bullJobId,
273
- status: { in: ["completed", "failed"] },
274
- queuedAt: { lt: Temporal.Instant.fromEpochMilliseconds(meta.jobCreatedAtMs) },
275
- });
276
- // DO NOTHING: a fast worker can report the start before this enqueue hook
277
- // returns, and a repeated jobId (debounce, fan-out dedup) is the same job.
278
- await insertOnConflictDoNothing(
279
- tx,
280
- tenantJobRunsTable,
281
- { ...key, bullJobId, status: "queued", queuedAt: now, updatedAt: now },
282
- { conflictKeys: [...TENANT_RUN_CONFLICT_KEYS] },
283
- );
284
- });
285
+ await inTenantScope(key.tenantId, () =>
286
+ transaction(db, async (tx) => {
287
+ // A finished row under the same id is left over from before a Redis reset
288
+ // (ids restart at 1), not this job. Only rows queued before this BullMQ job
289
+ // was created qualify: a fast worker can finish before the hook runs (no
290
+ // queuedAt), and a deduplicated add of an already finished job re-announces
291
+ // it with a creation time older than its own row. Both rows must stay.
292
+ await deleteMany(tx, tenantJobRunsTable, {
293
+ tenantId: key.tenantId,
294
+ jobName,
295
+ bullJobId,
296
+ status: { in: ["completed", "failed"] },
297
+ queuedAt: { lt: Temporal.Instant.fromEpochMilliseconds(meta.jobCreatedAtMs) },
298
+ });
299
+ // DO NOTHING: a fast worker can report the start before this enqueue hook
300
+ // returns, and a repeated jobId (debounce, fan-out dedup) is the same job.
301
+ await insertOnConflictDoNothing(
302
+ tx,
303
+ tenantJobRunsTable,
304
+ { ...key, bullJobId, status: "queued", queuedAt: now, updatedAt: now },
305
+ { conflictKeys: [...TENANT_RUN_CONFLICT_KEYS] },
306
+ );
307
+ }),
308
+ );
285
309
  }
286
310
 
287
311
  async function recordTenantRunStarted(
@@ -296,14 +320,16 @@ async function recordTenantRunStarted(
296
320
  // skip: system-tenant run, no tenant-scoped query reads it
297
321
  if (!key) return;
298
322
  const now = Temporal.Now.instant();
299
- await upsertOnConflict(
300
- db,
301
- tenantJobRunsTable,
302
- { ...key, bullJobId, status: "running", startedAt: now, updatedAt: now },
303
- {
304
- conflictKeys: [...TENANT_RUN_CONFLICT_KEYS],
305
- update: { status: "running", startedAt: now, finishedAt: null, updatedAt: now },
306
- },
323
+ await inTenantScope(key.tenantId, () =>
324
+ upsertOnConflict(
325
+ db,
326
+ tenantJobRunsTable,
327
+ { ...key, bullJobId, status: "running", startedAt: now, updatedAt: now },
328
+ {
329
+ conflictKeys: [...TENANT_RUN_CONFLICT_KEYS],
330
+ update: { status: "running", startedAt: now, finishedAt: null, updatedAt: now },
331
+ },
332
+ ),
307
333
  );
308
334
  }
309
335
 
@@ -323,11 +349,13 @@ async function recordTenantRunOutcome(
323
349
  // A failed attempt BullMQ will retry goes back to waiting: the tenant keeps
324
350
  // seeing pending work, not a failure the next attempt may still resolve.
325
351
  if (result === "failed" && !outcome.finalAttempt) {
326
- await updateMany(
327
- db,
328
- tenantJobRunsTable,
329
- { status: "queued", startedAt: null, finishedAt: null, updatedAt: now },
330
- { tenantId: key.tenantId, jobName, bullJobId },
352
+ await inTenantScope(key.tenantId, () =>
353
+ updateMany(
354
+ db,
355
+ tenantJobRunsTable,
356
+ { status: "queued", startedAt: null, finishedAt: null, updatedAt: now },
357
+ { tenantId: key.tenantId, jobName, bullJobId },
358
+ ),
331
359
  );
332
360
  // skip: retry pending, the row stays active until the final attempt
333
361
  return;
@@ -336,30 +364,32 @@ async function recordTenantRunOutcome(
336
364
  // one it replaces. The per-key lock keeps two runs of the same subject that
337
365
  // finish together from each missing the other's uncommitted row and leaving
338
366
  // both behind.
339
- await transaction(db, async (tx) => {
340
- await acquireNamespacedAdvisoryLock(
341
- tx,
342
- TENANT_JOB_RUN_LOCK_NAMESPACE,
343
- JSON.stringify([key.tenantId, key.jobName, key.subject]),
344
- );
345
- // Read under the lock: the run that finishes last in lock order must also carry the
346
- // latest finishedAt, otherwise a stale timestamp makes it the row that gets pruned.
347
- const finishedAt = Temporal.Now.instant();
348
- await upsertOnConflict(
349
- tx,
350
- tenantJobRunsTable,
351
- { ...key, bullJobId, status: result, finishedAt, updatedAt: finishedAt },
352
- {
353
- conflictKeys: [...TENANT_RUN_CONFLICT_KEYS],
354
- update: { status: result, finishedAt, updatedAt: finishedAt },
355
- },
356
- );
357
- await deleteMany(tx, tenantJobRunsTable, {
358
- ...key,
359
- status: { in: ["completed", "failed"] },
360
- bullJobId: { ne: bullJobId },
361
- });
362
- });
367
+ await inTenantScope(key.tenantId, () =>
368
+ transaction(db, async (tx) => {
369
+ await acquireNamespacedAdvisoryLock(
370
+ tx,
371
+ TENANT_JOB_RUN_LOCK_NAMESPACE,
372
+ JSON.stringify([key.tenantId, key.jobName, key.subject]),
373
+ );
374
+ // Read under the lock: the run that finishes last in lock order must also carry the
375
+ // latest finishedAt, otherwise a stale timestamp makes it the row that gets pruned.
376
+ const finishedAt = Temporal.Now.instant();
377
+ await upsertOnConflict(
378
+ tx,
379
+ tenantJobRunsTable,
380
+ { ...key, bullJobId, status: result, finishedAt, updatedAt: finishedAt },
381
+ {
382
+ conflictKeys: [...TENANT_RUN_CONFLICT_KEYS],
383
+ update: { status: result, finishedAt, updatedAt: finishedAt },
384
+ },
385
+ );
386
+ await deleteMany(tx, tenantJobRunsTable, {
387
+ ...key,
388
+ status: { in: ["completed", "failed"] },
389
+ bullJobId: { ne: bullJobId },
390
+ });
391
+ }),
392
+ );
363
393
  }
364
394
 
365
395
  async function requeueTenantRun(
@@ -373,11 +403,13 @@ async function requeueTenantRun(
373
403
  const key = tenantRunKey(jobName, meta.tenantVisibleRun.tenantId, meta.tenantVisibleRun.subject);
374
404
  // skip: system-tenant run, no tenant-scoped query reads it
375
405
  if (!key) return;
376
- await updateMany(
377
- db,
378
- tenantJobRunsTable,
379
- { status: "queued", startedAt: null, finishedAt: null, updatedAt: Temporal.Now.instant() },
380
- { tenantId: key.tenantId, jobName, bullJobId },
406
+ await inTenantScope(key.tenantId, () =>
407
+ updateMany(
408
+ db,
409
+ tenantJobRunsTable,
410
+ { status: "queued", startedAt: null, finishedAt: null, updatedAt: Temporal.Now.instant() },
411
+ { tenantId: key.tenantId, jobName, bullJobId },
412
+ ),
381
413
  );
382
414
  }
383
415
 
@@ -387,20 +419,22 @@ async function dropTenantRunQueued(
387
419
  bullJobId: string,
388
420
  meta: JobDroppedMeta,
389
421
  ): Promise<void> {
390
- await deleteMany(db, tenantJobRunsTable, {
391
- tenantId: meta.tenantId,
392
- jobName,
393
- bullJobId,
394
- status: "queued",
395
- });
422
+ await inTenantScope(meta.tenantId, () =>
423
+ deleteMany(db, tenantJobRunsTable, {
424
+ tenantId: meta.tenantId,
425
+ jobName,
426
+ bullJobId,
427
+ status: "queued",
428
+ }),
429
+ );
396
430
  }
397
431
 
398
432
  const log = createFallbackLogger("job-run-logger");
399
433
 
400
434
  // The tenant run state is an optional side write: a throw here must not
401
435
  // fail the enqueue, skip the run-row write or turn a completed run into a
402
- // BullMQ retry.
403
- async function isolateTenantRunWrite(
436
+ // BullMQ retry. A row-level-security rejection is logged louder (a missing or wrong tenant scope), never rethrown.
437
+ export async function isolateTenantRunWrite(
404
438
  write: () => Promise<void>,
405
439
  jobName: string,
406
440
  bullJobId: string,
@@ -408,6 +442,13 @@ async function isolateTenantRunWrite(
408
442
  try {
409
443
  await write();
410
444
  } catch (e) {
445
+ if (isRowLevelSecurityViolation(e)) {
446
+ log.error(
447
+ "tenant job run state write rejected by row level security: missing or wrong tenant scope",
448
+ { jobName, bullJobId },
449
+ );
450
+ return;
451
+ }
411
452
  log.error("tenant job run state write failed", {
412
453
  jobName,
413
454
  bullJobId,
@@ -417,7 +458,8 @@ async function isolateTenantRunWrite(
417
458
  }
418
459
 
419
460
  // The tenant failure record is an optional side write: a throw here must not
420
- // skip the run-row update or turn a completed run into a BullMQ retry.
461
+ // skip the run-row update or turn a completed run into a BullMQ retry. A row-level-security rejection is
462
+ // logged louder (a missing or wrong tenant scope), never rethrown.
421
463
  async function isolateTenantFailureWrite(
422
464
  write: () => Promise<void>,
423
465
  jobName: string,
@@ -426,6 +468,13 @@ async function isolateTenantFailureWrite(
426
468
  try {
427
469
  await write();
428
470
  } catch (e) {
471
+ if (isRowLevelSecurityViolation(e)) {
472
+ log.error(
473
+ "tenant job failure record write rejected by row level security: missing or wrong tenant scope",
474
+ { jobName, bullJobId },
475
+ );
476
+ return;
477
+ }
429
478
  log.error("tenant job failure record write failed", {
430
479
  jobName,
431
480
  bullJobId,
@@ -544,7 +593,7 @@ export function createJobRunLogger(opts: JobRunLoggerOptions): JobRunLoggerCallb
544
593
  const triggeredById = meta.triggeredById ?? null;
545
594
  cachePut(bullJobId, runId, triggeredById);
546
595
  // Parse against the registered schema so out-of-dispatcher writes
547
- // get the same validation guarantee as ctx.appendEvent. A shape
596
+ // get the same validation guarantee as ctx.events.append. A shape
548
597
  // drift between feature + logger fails loudly at the source
549
598
  // instead of silently landing on the table.
550
599
  const payload = runStartedSchema.parse({
@@ -164,7 +164,7 @@ A throw rejects the boot, so the process never reports ready.
164
164
 
165
165
  | Slug + locale | What happens when missing |
166
166
  |---|---|
167
- | `imprint` / `de` | **Production:** `throw new Error(...)` blocks app start. **Dev:** `ctx.log.warn(...)` |
167
+ | `imprint` / `de` | **Production:** `throw new Error(...)` blocks app start. **Dev:** `ctx.obs.log.warn(...)` |
168
168
  | `privacy` / `de` | as above |
169
169
 
170
170
  EN versions are **not** boot-fail-relevant by default (`LEGAL_OPTIONAL_BLOCKS`).
@@ -6,13 +6,17 @@ import {
6
6
  import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
7
7
  import { LEGAL_REQUIRED_BLOCKS, type LegalRequiredBlock } from "../web/constants.ts";
8
8
 
9
- // Only the fields the check reads, so HandlerContext, AppContext or a hand-built
10
- // object in a unit test all fit.
9
+ // Only the fields the check reads, so a HandlerContext or a hand-built object in a unit test
10
+ // fits.
11
11
  export type LegalPagesBootCheckCtx = {
12
- readonly templateResolver?: TemplateResolverApi;
13
- readonly log?: {
14
- readonly info?: (msg: string) => void;
15
- readonly warn?: (msg: string) => void;
12
+ readonly services: {
13
+ readonly extensions: { readonly templateResolver?: TemplateResolverApi };
14
+ };
15
+ readonly obs?: {
16
+ readonly log?: {
17
+ readonly info?: (msg: string) => void;
18
+ readonly warn?: (msg: string) => void;
19
+ };
16
20
  };
17
21
  };
18
22
 
@@ -42,7 +46,7 @@ export async function runLegalPagesBootCheck(
42
46
  }
43
47
 
44
48
  if (missing.length === 0) {
45
- ctx.log?.info?.("legal-pages boot-check: alle Pflicht-Blocks vorhanden");
49
+ ctx.obs?.log?.info?.("legal-pages boot-check: alle Pflicht-Blocks vorhanden");
46
50
  } else {
47
51
  const message =
48
52
  `legal-pages: missing ${missing.length} required text-block(s) in SYSTEM_TENANT: ` +
@@ -52,6 +56,6 @@ export async function runLegalPagesBootCheck(
52
56
  if (process.env["NODE_ENV"] === "production") {
53
57
  throw new Error(`Boot-Validation failed: ${message}`);
54
58
  }
55
- ctx.log?.warn?.(message);
59
+ ctx.obs?.log?.warn?.(message);
56
60
  }
57
61
  }
@@ -64,7 +64,7 @@ export const MAIL_TRANSPORT_EXTENSION = "mailTransport" as const;
64
64
  * **Why not the full HandlerContext?** On the worker path (transport builds
65
65
  * triggered by r.job, such as the user-data-rights forget/export cron) there
66
66
  * is no per-request `config`/`tx`/`actor`; the wrapper builds the per-tenant
67
- * ConfigAccessor from `ctx.configResolver`. A plugin reading `ctx.tx` or other
67
+ * ConfigAccessor from `ctx.services.configResolver`. A plugin reading `ctx.tx` or other
68
68
  * request-only fields would break the worker path at runtime, and that would
69
69
  * only show up with real SMTP in production. Casting
70
70
  * `ctx as unknown as HandlerContext` satisfies the compiler but fails at
@@ -82,7 +82,7 @@ export const MAIL_TRANSPORT_EXTENSION = "mailTransport" as const;
82
82
  export type MailTransportContext = {
83
83
  readonly config?: ConfigAccessor;
84
84
  readonly registry?: Registry;
85
- readonly secrets?: import("@cosmicdrift/kumiko-framework/secrets").SecretsContext;
85
+ readonly secrets: import("@cosmicdrift/kumiko-framework/secrets").SecretsContext | undefined;
86
86
  readonly auditUserId?: string | undefined;
87
87
  };
88
88
 
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.390.0",
4
+ "type": "fix",
5
+ "title": "nodemailer 10.2.1 fixes the address-parser advisories",
6
+ "detail": "nodemailer 10.0.13 mis-parsed quoted semicolons, angle brackets and nested comments in addresses (GHSA-m943-m63m-w62w, GHSA-85x2-q946-cp5h, GHSA-8c8m-p3m8-cjg8). The dependency is now 10.2.1, and the overrides for ip-address (10.7.4) and fast-copy (4.2.0) remove the remaining audit findings."
7
+ },
2
8
  {
3
9
  "version": "0.389.0",
4
10
  "type": "improvement",
@@ -231,7 +231,10 @@ async function buildSmtpTransport(
231
231
  }
232
232
 
233
233
  async function readPassword(ctx: MailTransportContext, tenantId: string): Promise<string> {
234
- const secrets = requireSecretsContext(ctx, FEATURE_NAME);
234
+ const secrets = requireSecretsContext(
235
+ { secrets: ctx.secrets, auditUserId: ctx.auditUserId },
236
+ FEATURE_NAME,
237
+ );
235
238
  const branded = await secrets.get(tenantId, SMTP_PASSWORD);
236
239
  return requireSecretSet(branded, FEATURE_NAME, SMTP_PASSWORD.name).reveal();
237
240
  }
@@ -8,12 +8,12 @@ import {
8
8
  type DbConnection,
9
9
  fetchOne,
10
10
  } from "@cosmicdrift/kumiko-framework/db";
11
- import type { SessionUser, TenantId } from "@cosmicdrift/kumiko-framework/engine";
12
11
  import {
13
- runEventStoreSeed,
14
- type SeedIfExists,
15
- TestUsers,
16
- } from "@cosmicdrift/kumiko-framework/seeding";
12
+ createSystemUser,
13
+ type SessionUser,
14
+ type TenantId,
15
+ } from "@cosmicdrift/kumiko-framework/engine";
16
+ import { runEventStoreSeed, type SeedIfExists } from "@cosmicdrift/kumiko-framework/seeding";
17
17
 
18
18
  import { type PageRow, pageEntity, pagesTable } from "./table.ts";
19
19
 
@@ -35,9 +35,14 @@ export type SeedPageOptions = {
35
35
  export async function seedPage(db: DbConnection, opts: SeedPageOptions): Promise<{ id: string }> {
36
36
  // The default user must have user.tenantId === opts.tenantId; otherwise the
37
37
  // event-store stream ends up in the wrong tenant bucket → version_conflict
38
- // on subsequent real writes. TestUsers.systemAdmin is testTenantId(1).
39
- const by = opts.by ?? { ...TestUsers.systemAdmin, tenantId: opts.tenantId };
40
- const tdb = createTenantDb({ db, tenantId: opts.tenantId, mode: "system" });
38
+ // on subsequent real writes.
39
+ const by = opts.by ?? createSystemUser(opts.tenantId);
40
+ const tdb = createTenantDb({
41
+ db,
42
+ tenantId: opts.tenantId,
43
+ mode: "system",
44
+ reason: "seeding writes managed pages into an explicitly named tenant",
45
+ });
41
46
 
42
47
  const existing = await fetchOne<PageRow>(db, pagesTable, {
43
48
  tenantId: opts.tenantId,
@@ -80,7 +80,7 @@ export function createAddNoteHandler(
80
80
  return decryptStoredPii(userRow.displayName, "displayName", "notes-history:add-note");
81
81
  });
82
82
  } catch (e) {
83
- ctx.log?.warn("notes-history: authorName lookup failed", {
83
+ ctx.obs.log?.warn("notes-history: authorName lookup failed", {
84
84
  error: e,
85
85
  userId: event.user.id,
86
86
  });
@@ -136,7 +136,7 @@ export function createPersonalAccessTokensFeature(
136
136
 
137
137
  // Custom domain-event for cross-instance access-invalidation,
138
138
  // mirroring sessions' SESSION_REVOKED_EVENT_SHORT. r.defineEvent
139
- // registers the schema so ctx.unsafeAppendEvent (revoke) enforces it at
139
+ // registers the schema so ctx.events.unsafeAppend (revoke) enforces it at
140
140
  // append time. revoke-for-user.ts appends via the low-level append()
141
141
  // instead (needs to anchor on SYSTEM_TENANT_ID) — same guarantee via a
142
142
  // different path. No projection: the payload IS the read, consumed
@@ -68,7 +68,7 @@ export function createPatCreateHandler(opts: CreatePatOptions = {}) {
68
68
  escapeHatch: {
69
69
  grants: ["systemIdentity"],
70
70
  reason:
71
- "Reads the caller's own passwordHash (privileged-only field) via ctx.queryAs(SYSTEM, " +
71
+ "Reads the caller's own passwordHash (privileged-only field) via ctx.dispatch.queryAs(SYSTEM, " +
72
72
  "user:findForAuth) to re-verify the current password before minting the token. Also " +
73
73
  "reads the MFA enrollment of the user being re-authenticated via the mfaVerifier callback.",
74
74
  },
@@ -79,7 +79,7 @@ export function createPatCreateHandler(opts: CreatePatOptions = {}) {
79
79
  agent: { expose: false },
80
80
  handler: async (event, ctx) => {
81
81
  const systemUser = createSystemUser(event.user.tenantId);
82
- const me = (await ctx.queryAs(systemUser, UserQueries.findForAuth, {
82
+ const me = (await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, {
83
83
  id: event.user.id,
84
84
  })) as { passwordHash: string | null } | null; // @cast-boundary db-runner
85
85
  if (!me?.passwordHash) return reauthFailed();
@@ -39,7 +39,7 @@ export const revokePatWrite = defineWriteHandler({
39
39
  // see feature.ts). Without this, the access-invalidation consumer
40
40
  // never hears about the revoke and an already-open SSE stream
41
41
  // authenticated by this exact token survives it.
42
- await ctx.unsafeAppendEvent({
42
+ await ctx.events.unsafeAppend({
43
43
  aggregateId: generateId(),
44
44
  aggregateType: PAT_REVOKED_AGGREGATE_TYPE,
45
45
  type: PAT_REVOKED_EVENT_QN,
@@ -37,12 +37,12 @@ export const rateLimitStatus = defineQueryHandler({
37
37
  // about the wiring either.
38
38
  const denied = bucketAccessDenied(query.payload.bucket, ctx.user);
39
39
  if (denied) throw denied;
40
- if (!ctx.rateLimit) {
40
+ if (!ctx.services.rateLimit) {
41
41
  throw new UnprocessableError(RateLimitErrors.resolverUnavailable, {
42
42
  i18nKey: "rateLimiting.errors.resolverUnavailable",
43
43
  });
44
44
  }
45
- const decision = await ctx.rateLimit.peek(query.payload.bucket, {
45
+ const decision = await ctx.services.rateLimit.peek(query.payload.bucket, {
46
46
  limit: query.payload.limit,
47
47
  windowSeconds: query.payload.windowSeconds,
48
48
  });
@@ -36,7 +36,10 @@ export const statusQuery = defineQueryHandler({
36
36
 
37
37
  // has() is metadata-only: no decryption, no read-audit event — a
38
38
  // readiness probe must not pollute the credential-read trail.
39
- const secrets = requireSecretsContext(ctx, ReadinessQueries.status);
39
+ const secrets = requireSecretsContext(
40
+ { secrets: ctx.services.secrets, user: ctx.user },
41
+ ReadinessQueries.status,
42
+ );
40
43
  const missingSecrets: ReadinessMissingSecret[] = [];
41
44
  for (const [qualifiedName, keyDef] of ctx.registry.getAllSecretKeys()) {
42
45
  if (keyDef.required !== true) continue;
@@ -87,12 +87,12 @@ function indexByKind(plugins: ReadonlyArray<RendererPlugin>): Map<RenderKind, Re
87
87
  // Single source of truth for handlers that require renderer-foundation.
88
88
  // The pattern mirrors requireTemplateResolver + requireTextContent.
89
89
  export function requireRendererFoundation(
90
- ctx: { readonly rendererFoundation?: RendererFoundationApi } | object,
90
+ ctx: { readonly services: { readonly extensions: Readonly<Record<string, unknown>> } },
91
91
  callerName: string,
92
92
  ): RendererFoundationApi {
93
93
  // @cast-boundary engine-bridge — rendererFoundation kommt per extraContext
94
- // from the app bootstrap.
95
- const api = (ctx as { rendererFoundation?: RendererFoundationApi }).rendererFoundation;
94
+ // from the app bootstrap and lands in services.extensions; the framework does not know its type.
95
+ const api = ctx.services.extensions["rendererFoundation"] as RendererFoundationApi | undefined;
96
96
  if (!api) {
97
97
  throw new InternalError({
98
98
  message:
@@ -6,8 +6,8 @@ import {
6
6
  } from "@cosmicdrift/kumiko-framework/redis";
7
7
 
8
8
  type AnnouncingContext = {
9
- readonly cacheSync?: CacheSyncBus;
10
- readonly scheduleAfterCommit?: (hook: () => Promise<void>) => void;
9
+ readonly services: { readonly cacheSync?: CacheSyncBus };
10
+ readonly lifecycle: { readonly afterCommit?: (hook: () => Promise<void>) => void };
11
11
  };
12
12
 
13
13
  // Secret writes bypass config:write:*, yet provider caches (e.g. the S3 secret
@@ -18,13 +18,13 @@ export function announceSecretChange(
18
18
  tenantId: TenantId,
19
19
  key: string,
20
20
  ): void {
21
- const bus = ctx.cacheSync;
21
+ const bus = ctx.services.cacheSync;
22
22
  // skip: no bus wired (single-process setup), nothing to announce
23
23
  if (!bus) return;
24
24
  const message: TenantConfigSyncMessage = { tenantId, key };
25
25
  const publish = async (): Promise<void> => {
26
26
  bus.publish(CACHE_SYNC_TOPICS.tenantConfig, message);
27
27
  };
28
- if (ctx.scheduleAfterCommit) ctx.scheduleAfterCommit(publish);
28
+ if (ctx.lifecycle.afterCommit) ctx.lifecycle.afterCommit(publish);
29
29
  else bus.publish(CACHE_SYNC_TOPICS.tenantConfig, message);
30
30
  }
@@ -70,19 +70,19 @@ export { type StoredEnvelope, type StoredMetadata, tenantSecretsTable } from "./
70
70
  export const JOIN_REQUEST_TRANSACTION_REASON =
71
71
  "the secret write runs on the request transaction so a failing batch rolls it back together with the config writes";
72
72
 
73
- // AppContext carries ctx.secrets via extraContext. requireSecretsContext
73
+ // AppContext carries ctx.services.secrets via extraContext. requireSecretsContext
74
74
  // wraps that raw context so every `.get(...)` call automatically includes
75
75
  // the current user and handler as audit metadata, preventing feature code
76
76
  // from silently bypassing read auditing.
77
77
  //
78
- // The context shape stays minimal: HandlerContext paths (Set/Delete handlers)
79
- // and FileProviderContext paths (the S3 plugin in the worker) provide the same
80
- // two fields, so importing the full HandlerContext is unnecessary.
81
- // Handler contexts carry the caller as ctx.user; provider contexts set
82
- // auditUserId, which takes precedence.
78
+ // The source shape stays minimal: handler paths pass `{ secrets: ctx.services.secrets,
79
+ // user: ctx.user }`, provider paths (the S3 plugin in the worker) pass `{ secrets, auditUserId }`.
80
+ // `secrets` is a required key on purpose: a whole HandlerContext or JobContext does not carry
81
+ // it flat, so handing one over is a type error instead of a silently undefined secret store.
82
+ // Provider contexts set auditUserId, which takes precedence over the caller in `user`.
83
83
  export function requireSecretsContext(
84
84
  ctx: {
85
- readonly secrets?: SecretsContext;
85
+ readonly secrets: SecretsContext | undefined;
86
86
  readonly auditUserId?: string | undefined;
87
87
  readonly user?: { readonly id: string };
88
88
  },
@@ -91,7 +91,7 @@ export function requireSecretsContext(
91
91
  if (!ctx.secrets) {
92
92
  throw new InternalError({
93
93
  message:
94
- `[${handlerName}] ctx.secrets missing — pass ` +
94
+ `[${handlerName}] ctx.services.secrets missing — pass ` +
95
95
  "createSecretsContext({db, masterKeyProvider, registry}) via extraContext.secrets at boot.",
96
96
  });
97
97
  }
@@ -187,7 +187,7 @@ export function createSecretsFeature(
187
187
  // Read-audit domain-event. Registered here so ops tools + MSPs can
188
188
  // discover the type; secrets-context.get parses payloads against
189
189
  // `secretReadSchema` at write time because the low-level append() path
190
- // skips ctx.appendEvent's schema-validation guard.
190
+ // skips ctx.events.append's schema-validation guard.
191
191
  r.defineEvent("read", secretReadSchema, { piiFields: "none" });
192
192
 
193
193
  // Per-tenant handlers (set/delete/list) run in the default tenant-scope,