@cosmicdrift/kumiko-samples 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 (385) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +25 -13
  3. package/packages/bundled-features/src/agent-tools/changes.json +6 -0
  4. package/packages/bundled-features/src/agent-tools/index.ts +1 -1
  5. package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +42 -26
  6. package/packages/bundled-features/src/audit/feature.ts +2 -1
  7. package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +1 -1
  8. package/packages/bundled-features/src/auth-email-password/bootstrap.ts +13 -1
  9. package/packages/bundled-features/src/auth-email-password/changes.json +18 -0
  10. package/packages/bundled-features/src/auth-email-password/feature.ts +2 -2
  11. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +7 -7
  12. package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.write.ts +3 -3
  13. package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
  14. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +9 -7
  15. package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +11 -7
  16. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -4
  17. package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +2 -2
  18. package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +9 -7
  19. package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +23 -16
  20. package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +1 -0
  21. package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +1 -0
  22. package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +1 -0
  23. package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +3 -2
  24. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +12 -12
  25. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +16 -8
  26. package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +4 -2
  27. package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +10 -2
  28. package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
  29. package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +16 -3
  30. package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
  31. package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -0
  32. package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +8 -2
  33. package/packages/bundled-features/src/auth-email-password/web/auth-redirect.ts +23 -21
  34. package/packages/bundled-features/src/auth-email-password/web/default-auth-pages-gate.tsx +6 -3
  35. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -1
  36. package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -0
  37. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +16 -5
  38. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +7 -1
  39. package/packages/bundled-features/src/auth-email-password/web/return-path.ts +35 -0
  40. package/packages/bundled-features/src/auth-email-password/web/session.tsx +6 -1
  41. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +11 -3
  42. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +12 -2
  43. package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
  44. package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
  45. package/packages/bundled-features/src/auth-mfa/config.ts +3 -7
  46. package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +5 -5
  47. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +24 -14
  48. package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +9 -3
  49. package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +6 -1
  50. package/packages/bundled-features/src/auth-mfa/handlers/enable-start.write.ts +3 -1
  51. package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +14 -9
  52. package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -4
  53. package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +25 -14
  54. package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +10 -4
  55. package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +17 -8
  56. package/packages/bundled-features/src/auth-mfa/verify-factor.ts +1 -1
  57. package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +2 -2
  58. package/packages/bundled-features/src/billing-foundation/changes.json +6 -0
  59. package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +1 -1
  60. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +5 -5
  61. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +10 -7
  62. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +15 -12
  63. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-open-cancels.ts +22 -0
  64. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-provider-lock.ts +4 -0
  65. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +4 -4
  66. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +17 -58
  67. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +10 -8
  68. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +18 -15
  69. package/packages/bundled-features/src/billing-foundation/events.ts +1 -1
  70. package/packages/bundled-features/src/billing-foundation/feature.ts +8 -3
  71. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +1 -1
  72. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +5 -5
  73. package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +3 -3
  74. package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +2 -2
  75. package/packages/bundled-features/src/billing-foundation/projection.ts +1 -1
  76. package/packages/bundled-features/src/billing-foundation/types.ts +1 -1
  77. package/packages/bundled-features/src/cap-counter/aggregate-id.ts +1 -1
  78. package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +5 -4
  79. package/packages/bundled-features/src/cap-counter/cap-reservation.ts +21 -8
  80. package/packages/bundled-features/src/cap-counter/changes.json +12 -0
  81. package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
  82. package/packages/bundled-features/src/cap-counter/feature.ts +3 -2
  83. package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
  84. package/packages/bundled-features/src/cap-counter/reservation-chain.ts +1 -1
  85. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +4 -4
  86. package/packages/bundled-features/src/cap-overview/feature.ts +1 -0
  87. package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -1
  88. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
  89. package/packages/bundled-features/src/compliance-profiles/seeding.ts +12 -7
  90. package/packages/bundled-features/src/config/changes.json +6 -0
  91. package/packages/bundled-features/src/config/feature.ts +6 -6
  92. package/packages/bundled-features/src/config/handlers/cascade.query.ts +1 -1
  93. package/packages/bundled-features/src/config/handlers/readiness.query.ts +1 -1
  94. package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +23 -13
  95. package/packages/bundled-features/src/config/handlers/reset.write.ts +3 -3
  96. package/packages/bundled-features/src/config/handlers/selected-extensions.query.ts +1 -1
  97. package/packages/bundled-features/src/config/handlers/set.write.ts +22 -5
  98. package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
  99. package/packages/bundled-features/src/crypto-shredding/changes.json +12 -0
  100. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +9 -9
  101. package/packages/bundled-features/src/crypto-shredding/resolve-file-bytes-deleter.ts +3 -3
  102. package/packages/bundled-features/src/custom-fields/changes.json +6 -0
  103. package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
  104. package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +1 -1
  105. package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -1
  106. package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
  107. package/packages/bundled-features/src/custom-fields/index.ts +4 -0
  108. package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
  109. package/packages/bundled-features/src/data-retention/feature.ts +1 -1
  110. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +12 -2
  111. package/packages/bundled-features/src/delivery/attempt-log.ts +1 -1
  112. package/packages/bundled-features/src/delivery/changes.json +12 -0
  113. package/packages/bundled-features/src/delivery/delivery-service.ts +6 -5
  114. package/packages/bundled-features/src/delivery/feature.ts +3 -2
  115. package/packages/bundled-features/src/delivery/index.ts +5 -0
  116. package/packages/bundled-features/src/delivery/jobs.ts +11 -11
  117. package/packages/bundled-features/src/delivery/system-channel-context.ts +33 -0
  118. package/packages/bundled-features/src/delivery/testing.ts +1 -1
  119. package/packages/bundled-features/src/document-ingest-foundation/append-missing-extract-event.ts +84 -0
  120. package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
  121. package/packages/bundled-features/src/document-ingest-foundation/events.ts +16 -1
  122. package/packages/bundled-features/src/document-ingest-foundation/index.ts +11 -0
  123. package/packages/bundled-features/src/feature-toggles/feature.ts +1 -0
  124. package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +1 -1
  125. package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +1 -1
  126. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
  127. package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
  128. package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +5 -1
  129. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +7 -5
  130. package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
  131. package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -5
  132. package/packages/bundled-features/src/file-provider-s3/feature.ts +4 -1
  133. package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +8 -8
  134. package/packages/bundled-features/src/form-draft/feature.ts +1 -1
  135. package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +22 -12
  136. package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +3 -3
  137. package/packages/bundled-features/src/form-draft/handlers/get.query.ts +1 -1
  138. package/packages/bundled-features/src/foundation-shared/config-helpers.ts +1 -1
  139. package/packages/bundled-features/src/host-routing/changes.json +6 -0
  140. package/packages/bundled-features/src/host-routing/validate-options.ts +5 -0
  141. package/packages/bundled-features/src/inbound-mail-foundation/events.ts +1 -1
  142. package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +2 -1
  143. package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +2 -2
  144. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
  145. package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
  146. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +10 -10
  147. package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
  148. package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +1 -1
  149. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
  150. package/packages/bundled-features/src/inbound-provider-imap/feature.ts +4 -1
  151. package/packages/bundled-features/src/jobs/changes.json +6 -0
  152. package/packages/bundled-features/src/jobs/events.ts +1 -1
  153. package/packages/bundled-features/src/jobs/feature.ts +1 -0
  154. package/packages/bundled-features/src/jobs/handlers/consumer.write.ts +7 -4
  155. package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
  156. package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +11 -5
  157. package/packages/bundled-features/src/jobs/handlers/retention-cleanup.job.ts +1 -1
  158. package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
  159. package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +1 -1
  160. package/packages/bundled-features/src/jobs/handlers/trigger.write.ts +2 -2
  161. package/packages/bundled-features/src/jobs/job-run-logger.ts +139 -90
  162. package/packages/bundled-features/src/ledger/web/index.ts +1 -1
  163. package/packages/bundled-features/src/legal-pages/README.md +1 -1
  164. package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +12 -8
  165. package/packages/bundled-features/src/legal-pages/seed.ts +5 -2
  166. package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
  167. package/packages/bundled-features/src/mail-transport-smtp/changes.json +12 -0
  168. package/packages/bundled-features/src/mail-transport-smtp/config-keys.ts +1 -1
  169. package/packages/bundled-features/src/mail-transport-smtp/feature.ts +13 -12
  170. package/packages/bundled-features/src/managed-pages/changes.json +6 -0
  171. package/packages/bundled-features/src/managed-pages/constants.ts +1 -0
  172. package/packages/bundled-features/src/managed-pages/feature.ts +3 -1
  173. package/packages/bundled-features/src/managed-pages/index.ts +1 -0
  174. package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
  175. package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +2 -2
  176. package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -1
  177. package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +2 -2
  178. package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
  179. package/packages/bundled-features/src/rate-limiting/handlers/status.query.ts +2 -2
  180. package/packages/bundled-features/src/readiness/handlers/status.query.ts +4 -1
  181. package/packages/bundled-features/src/renderer-foundation/api.ts +3 -3
  182. package/packages/bundled-features/src/secrets/announce-secret-change.ts +4 -4
  183. package/packages/bundled-features/src/secrets/changes.json +6 -0
  184. package/packages/bundled-features/src/secrets/feature.ts +9 -9
  185. package/packages/bundled-features/src/secrets/handlers/delete.write.ts +4 -3
  186. package/packages/bundled-features/src/secrets/handlers/rotate.job.ts +13 -8
  187. package/packages/bundled-features/src/secrets/handlers/set.write.ts +4 -3
  188. package/packages/bundled-features/src/secrets/secrets-context.ts +16 -6
  189. package/packages/bundled-features/src/secrets/write-gate.ts +10 -3
  190. package/packages/bundled-features/src/seo/feature.ts +1 -1
  191. package/packages/bundled-features/src/sessions/changes.json +19 -0
  192. package/packages/bundled-features/src/sessions/feature.ts +2 -1
  193. package/packages/bundled-features/src/sessions/handlers/cleanup.job.ts +2 -2
  194. package/packages/bundled-features/src/sessions/handlers/detail.query.ts +5 -2
  195. package/packages/bundled-features/src/sessions/handlers/list.query.ts +5 -2
  196. package/packages/bundled-features/src/sessions/handlers/mine.query.ts +5 -2
  197. package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +3 -3
  198. package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +1 -1
  199. package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
  200. package/packages/bundled-features/src/sessions/index.ts +1 -1
  201. package/packages/bundled-features/src/sessions/screens.ts +8 -4
  202. package/packages/bundled-features/src/sessions/session-callbacks.ts +1 -9
  203. package/packages/bundled-features/src/sessions/session-revoked-event.ts +1 -1
  204. package/packages/bundled-features/src/sessions/web/session-device-cell.tsx +10 -12
  205. package/packages/bundled-features/src/sessions/web/user-agent.ts +12 -0
  206. package/packages/bundled-features/src/shared/index.ts +15 -0
  207. package/packages/bundled-features/src/shared/purge-files-reaching-subject.ts +36 -4
  208. package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -5
  209. package/packages/bundled-features/src/shared/run-in-sub-transaction.ts +5 -5
  210. package/packages/bundled-features/src/shared/stream-tenant-backfill.ts +201 -0
  211. package/packages/bundled-features/src/shared/stream-tenant-derived-state.ts +107 -0
  212. package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
  213. package/packages/bundled-features/src/step-dispatcher/deliver-dispatch-request.ts +17 -3
  214. package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
  215. package/packages/bundled-features/src/step-dispatcher/mail-runner.ts +2 -1
  216. package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +1 -1
  217. package/packages/bundled-features/src/subscription-dodo/runtime.ts +7 -7
  218. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +5 -3
  219. package/packages/bundled-features/src/subscription-stripe/runtime.ts +7 -7
  220. package/packages/bundled-features/src/tags/feature.ts +1 -0
  221. package/packages/bundled-features/src/template-resolver/api.ts +3 -3
  222. package/packages/bundled-features/src/template-resolver/changes.json +54 -0
  223. package/packages/bundled-features/src/template-resolver/document-ops.ts +20 -1
  224. package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
  225. package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +5 -4
  226. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +2 -2
  227. package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +1 -1
  228. package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +36 -11
  229. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +32 -7
  230. package/packages/bundled-features/src/template-resolver/handlers/patch-draft.write.ts +1 -1
  231. package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
  232. package/packages/bundled-features/src/template-resolver/handlers/send-test-status.query.ts +22 -19
  233. package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +2 -2
  234. package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +6 -2
  235. package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +4 -2
  236. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +1 -1
  237. package/packages/bundled-features/src/template-resolver/seeding.ts +22 -5
  238. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +68 -19
  239. package/packages/bundled-features/src/template-resolver/template-resources.ts +4 -4
  240. package/packages/bundled-features/src/template-resolver/testing.ts +2 -2
  241. package/packages/bundled-features/src/template-resolver/web/document-insert-fields.tsx +222 -0
  242. package/packages/bundled-features/src/template-resolver/web/document-insert-model.ts +164 -0
  243. package/packages/bundled-features/src/template-resolver/web/document-insert-panel.tsx +115 -0
  244. package/packages/bundled-features/src/template-resolver/web/document-tiptap-bubble.tsx +177 -0
  245. package/packages/bundled-features/src/template-resolver/web/document-tiptap-content.tsx +38 -0
  246. package/packages/bundled-features/src/template-resolver/web/document-tiptap-context.ts +119 -0
  247. package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +51 -1098
  248. package/packages/bundled-features/src/template-resolver/web/document-tiptap-helpers.ts +161 -0
  249. package/packages/bundled-features/src/template-resolver/web/document-tiptap-layout.tsx +83 -0
  250. package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar-groups.tsx +153 -0
  251. package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar.tsx +182 -0
  252. package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +18 -0
  253. package/packages/bundled-features/src/template-resolver/web/i18n.ts +6 -0
  254. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +19 -6
  255. package/packages/bundled-features/src/template-resolver/web/qualified-names.ts +1 -0
  256. package/packages/bundled-features/src/template-resolver/web/use-document-tiptap-model.ts +138 -0
  257. package/packages/bundled-features/src/tenant/changes.json +19 -0
  258. package/packages/bundled-features/src/tenant/feature.ts +1 -0
  259. package/packages/bundled-features/src/tenant/handlers/cancel-invitation.write.ts +1 -1
  260. package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
  261. package/packages/bundled-features/src/tenant/handlers/remove-member.write.ts +2 -2
  262. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -3
  263. package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -1
  264. package/packages/bundled-features/src/tenant/index.ts +5 -0
  265. package/packages/bundled-features/src/tenant/invited-membership.ts +6 -1
  266. package/packages/bundled-features/src/tenant/schema/tenant.ts +4 -1
  267. package/packages/bundled-features/src/tenant/seeding.ts +65 -62
  268. package/packages/bundled-features/src/tenant/stream-tenant-backfill.ts +115 -0
  269. package/packages/bundled-features/src/tenant/testing.ts +77 -0
  270. package/packages/bundled-features/src/tenant-handover/events.ts +1 -1
  271. package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
  272. package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
  273. package/packages/bundled-features/src/tenant-lifecycle/feature.ts +5 -5
  274. package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +22 -10
  275. package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +22 -11
  276. package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +23 -12
  277. package/packages/bundled-features/src/tenant-lifecycle/stages.ts +42 -4
  278. package/packages/bundled-features/src/tenant-lifecycle/testing.ts +9 -8
  279. package/packages/bundled-features/src/tier-engine/entity.ts +1 -1
  280. package/packages/bundled-features/src/tier-engine/feature.ts +8 -3
  281. package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +3 -3
  282. package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +7 -1
  283. package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +3 -3
  284. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +71 -97
  285. package/packages/bundled-features/src/user/feature.ts +1 -0
  286. package/packages/bundled-features/src/user/handlers/find-for-auth.query.ts +2 -2
  287. package/packages/bundled-features/src/user/handlers/update-roles.ts +2 -2
  288. package/packages/bundled-features/src/user/index.ts +2 -0
  289. package/packages/bundled-features/src/user/invited-global-roles.ts +6 -1
  290. package/packages/bundled-features/src/user/principal-status.ts +1 -1
  291. package/packages/bundled-features/src/user/seeding.ts +24 -6
  292. package/packages/bundled-features/src/user/testing.ts +22 -0
  293. package/packages/bundled-features/src/user-data-rights/COMPLIANCE.md +1 -1
  294. package/packages/bundled-features/src/user-data-rights/README.md +1 -1
  295. package/packages/bundled-features/src/user-data-rights/audit-download.ts +12 -2
  296. package/packages/bundled-features/src/user-data-rights/changes.json +36 -0
  297. package/packages/bundled-features/src/user-data-rights/feature.ts +30 -24
  298. package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
  299. package/packages/bundled-features/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
  300. package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +2 -2
  301. package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +2 -2
  302. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
  303. package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +4 -4
  304. package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +12 -6
  305. package/packages/bundled-features/src/user-data-rights/index.ts +8 -1
  306. package/packages/bundled-features/src/user-data-rights/lib/mail-transport-resolver.ts +1 -1
  307. package/packages/bundled-features/src/user-data-rights/lib/storage-provider-resolver.ts +1 -1
  308. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +6 -1
  309. package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +8 -2
  310. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +14 -3
  311. package/packages/bundled-features/src/user-data-rights/run-user-export.ts +11 -1
  312. package/packages/bundled-features/src/user-data-rights/web/constants.ts +16 -0
  313. package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -0
  314. package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
  315. package/packages/bundled-features/src/waitlist/changes.json +18 -0
  316. package/packages/bundled-features/src/waitlist/feature.ts +1 -0
  317. package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +1 -1
  318. package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +2 -2
  319. package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
  320. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +34 -10
  321. package/packages/bundled-features/src/waitlist/import.ts +23 -3
  322. package/packages/bundled-features/src/waitlist/legacy-migrate.ts +8 -2
  323. package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -1
  324. package/packages/bundled-features/src/waitlist/options.ts +2 -1
  325. package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
  326. package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +1 -1
  327. package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -3
  328. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
  329. package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
  330. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +8 -8
  331. package/packages/bundled-features/src/workflow-runner/runner.ts +6 -4
  332. package/samples/apps/cap-billing-demo/src/feature.ts +19 -5
  333. package/samples/apps/config-demo/src/app/server.ts +1 -1
  334. package/samples/apps/styleguide/src/app/client.tsx +2 -0
  335. package/samples/apps/styleguide/src/app/seed.ts +6 -4
  336. package/samples/apps/styleguide/src/app/server.ts +6 -3
  337. package/samples/apps/styleguide/src/features/widgets/feature.ts +15 -4
  338. package/samples/apps/styleguide/src/features/workboard/feature.ts +185 -0
  339. package/samples/apps/styleguide/src/features/workboard/i18n.ts +27 -0
  340. package/samples/apps/styleguide/src/features/workboard/seed.ts +121 -0
  341. package/samples/apps/styleguide/src/features/workboard/web.ts +13 -0
  342. package/samples/apps/ui-walkthrough/src/features/rental/i18n.ts +2 -0
  343. package/samples/apps/ui-walkthrough/src/features/rental/screens.ts +9 -0
  344. package/samples/apps/use-all-bundled/bin/main.ts +2 -0
  345. package/samples/apps/use-all-bundled/feature-manifest.json +17 -1
  346. package/samples/apps/use-all-bundled/kumiko/schema.ts +2 -0
  347. package/samples/apps/use-all-bundled/kumiko.config.ts +2 -0
  348. package/samples/apps/use-all-bundled/package.json +2 -1
  349. package/samples/apps/use-all-bundled/src/app/dev-app-options.ts +2 -0
  350. package/samples/apps/use-all-bundled/src/app/notes-feature.ts +2 -0
  351. package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
  352. package/samples/apps/use-all-bundled/src/bundled-lists.ts +15 -0
  353. package/samples/apps/user-data-rights-demo/README.md +1 -1
  354. package/samples/apps/user-data-rights-demo/src/feature.ts +3 -2
  355. package/samples/recipes/api-worker-split/README.md +2 -1
  356. package/samples/recipes/api-worker-split/src/feature.ts +2 -1
  357. package/samples/recipes/cross-feature-events/README.md +5 -5
  358. package/samples/recipes/cross-feature-events/package.json +1 -1
  359. package/samples/recipes/cross-feature-events/src/feature.ts +4 -4
  360. package/samples/recipes/event-sourcing/README.md +5 -5
  361. package/samples/recipes/event-sourcing/package.json +1 -1
  362. package/samples/recipes/event-sourcing/src/feature.ts +15 -15
  363. package/samples/recipes/files-post-processing/README.md +1 -1
  364. package/samples/recipes/files-post-processing/package.json +1 -1
  365. package/samples/recipes/files-post-processing/src/feature.ts +6 -4
  366. package/samples/recipes/golden-path/src/feature.ts +2 -1
  367. package/samples/recipes/lane-routing/package.json +1 -1
  368. package/samples/recipes/legal-pages/README.md +2 -2
  369. package/samples/recipes/lifecycle-hooks/README.md +4 -0
  370. package/samples/recipes/lifecycle-hooks/src/feature.ts +26 -1
  371. package/samples/recipes/list-table-options/README.md +50 -0
  372. package/samples/recipes/list-table-options/package.json +11 -0
  373. package/samples/recipes/list-table-options/src/feature.ts +221 -0
  374. package/samples/recipes/managed-config/README.md +3 -3
  375. package/samples/recipes/pdf-audit-export/src/feature.ts +2 -1
  376. package/samples/recipes/search/README.md +1 -1
  377. package/samples/recipes/search/src/feature.ts +3 -3
  378. package/samples/recipes/secrets-demo/README.md +1 -1
  379. package/samples/recipes/secrets-demo/src/feature.ts +5 -2
  380. package/samples/recipes/state-machine/src/events.ts +4 -3
  381. package/samples/recipes/state-machine/src/reducer.ts +1 -1
  382. package/samples/recipes/tenant-isolation/README.md +1 -0
  383. package/samples/recipes/tenant-isolation/src/feature.ts +36 -0
  384. package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
  385. package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
@@ -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
  }
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.389.0",
4
+ "type": "fix",
5
+ "title": "Feature-prefixed undeclared secret keys are not deletable",
6
+ "detail": "The delete handler still accepts legacy undeclared keys, but a key starting with \"<feature>:\" of a mounted feature now fails as unknown key. Feature-owned credentials keep their writeRoles protection."
7
+ },
2
8
  {
3
9
  "version": "0.370.0",
4
10
  "type": "improvement",
@@ -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,
@@ -18,9 +18,10 @@ export function createDeleteHandler(access: AccessRule = DEFAULT_SECRETS_ACCESS)
18
18
  access,
19
19
  escapeHatch: { grants: ["unsafeRaw"], reason: JOIN_REQUEST_TRANSACTION_REASON },
20
20
  handler: async (event, ctx) => {
21
- const secrets = requireSecretsContext(ctx, SecretsHandlers.delete).withRunner(
22
- ctx.db.unsafeRaw(),
23
- );
21
+ const secrets = requireSecretsContext(
22
+ { secrets: ctx.services.secrets, user: ctx.user },
23
+ SecretsHandlers.delete,
24
+ ).withRunner(ctx.db.unsafeRaw());
24
25
  const gate = checkSecretKeyWrite(ctx.registry, event.user.roles, event.payload.key, {
25
26
  allowUndeclared: true,
26
27
  });
@@ -61,13 +61,13 @@ export async function rotateJob(
61
61
  db: DbConnection,
62
62
  ): Promise<void> {
63
63
  const payload = rawPayload as RotateJobPayload; // @cast-boundary engine-payload
64
- if (!ctx.masterKeyProvider) {
64
+ if (!ctx.services.masterKeyProvider) {
65
65
  throw new InternalError({
66
66
  message:
67
- "[secrets:rotate] ctx.masterKeyProvider missing — wire it via extraContext.masterKeyProvider at boot.",
67
+ "[secrets:rotate] ctx.services.masterKeyProvider missing — wire it via extraContext.masterKeyProvider at boot.",
68
68
  });
69
69
  }
70
- const provider = ctx.masterKeyProvider;
70
+ const provider = ctx.services.masterKeyProvider;
71
71
  const batchSize = payload.batchSize ?? DEFAULT_BATCH_SIZE;
72
72
  const maxFailures = payload.maxFailures ?? DEFAULT_MAX_FAILURES;
73
73
  const deadline = payload.maxDurationMs
@@ -81,7 +81,12 @@ export async function rotateJob(
81
81
  function tdbFor(tenantId: TenantId): TenantDb {
82
82
  let existing = tdbCache.get(tenantId);
83
83
  if (!existing) {
84
- existing = createTenantDb({ db, tenantId, mode: "system" });
84
+ existing = createTenantDb({
85
+ db,
86
+ tenantId,
87
+ mode: "system",
88
+ reason: "secret key rotation job re-encrypts secrets of every tenant",
89
+ });
85
90
  tdbCache.set(tenantId, existing);
86
91
  }
87
92
  return existing;
@@ -137,7 +142,7 @@ export async function rotateJob(
137
142
  // potentially even past target.
138
143
  if (!result.isSuccess) {
139
144
  if (result.error.code === "version_conflict") return "skipped";
140
- ctx.log?.warn?.(`[secrets:rotate] executor rejected row ${row.id}`, {
145
+ ctx.obs.log?.warn?.(`[secrets:rotate] executor rejected row ${row.id}`, {
141
146
  code: result.error.code,
142
147
  });
143
148
  return "failed";
@@ -150,9 +155,9 @@ export async function rotateJob(
150
155
  migrateRow,
151
156
  maxFailures,
152
157
  deadlineAt: deadline,
153
- signal: ctx.signal,
158
+ signal: ctx.lifecycle.signal,
154
159
  onRowError: (row, err) => {
155
- ctx.log?.warn?.(`[secrets:rotate] failed to rotate row ${row.id}`, { err });
160
+ ctx.obs.log?.warn?.(`[secrets:rotate] failed to rotate row ${row.id}`, { err });
156
161
  },
157
162
  });
158
163
 
@@ -162,5 +167,5 @@ export async function rotateJob(
162
167
  batchesProcessed: outcome.batchesProcessed,
163
168
  stoppedReason: outcome.stoppedReason,
164
169
  };
165
- ctx.log?.info?.(`[secrets:rotate] complete: ${JSON.stringify(result)}`);
170
+ ctx.obs.log?.info?.(`[secrets:rotate] complete: ${JSON.stringify(result)}`);
166
171
  }
@@ -25,9 +25,10 @@ export function createSetHandler(access: AccessRule = DEFAULT_SECRETS_ACCESS) {
25
25
  access,
26
26
  escapeHatch: { grants: ["unsafeRaw"], reason: JOIN_REQUEST_TRANSACTION_REASON },
27
27
  handler: async (event, ctx) => {
28
- const secrets = requireSecretsContext(ctx, SecretsHandlers.set).withRunner(
29
- ctx.db.unsafeRaw(),
30
- );
28
+ const secrets = requireSecretsContext(
29
+ { secrets: ctx.services.secrets, user: ctx.user },
30
+ SecretsHandlers.set,
31
+ ).withRunner(ctx.db.unsafeRaw());
31
32
  const { key, value, redactedPreview, hint } = event.payload;
32
33
 
33
34
  // Preview-priority: explicit payload param > feature-declared redact
@@ -1,6 +1,6 @@
1
- // Feature-level accessor injected as `ctx.secrets` at boot. Not a HTTP API —
1
+ // Feature-level accessor injected as `ctx.services.secrets` at boot. Not a HTTP API —
2
2
  // feature code that needs a plaintext secret (SMTP-connect, Stripe-call, …)
3
- // pulls it via ctx.secrets.get. Cleartext never crosses the wire.
3
+ // pulls it via ctx.services.secrets.get. Cleartext never crosses the wire.
4
4
  //
5
5
  // Post-ES pivot: all three ops (get/set/delete) flow through the events-
6
6
  // table.
@@ -154,7 +154,7 @@ function buildSecretsContext(
154
154
  const readId = generateId();
155
155
  const metadata: EventMetadata = { userId: auditCtx.userId };
156
156
  // Parse against the registered schema so the low-level append
157
- // here gets the same validation guarantee as ctx.appendEvent.
157
+ // here gets the same validation guarantee as ctx.events.append.
158
158
  // A payload-shape drift between schema + call-site fails at the
159
159
  // source instead of landing on the events-stream.
160
160
  const payload = secretReadSchema.parse({
@@ -206,7 +206,12 @@ function buildSecretsContext(
206
206
  tenantId,
207
207
  roles: SYSTEM_ROLES,
208
208
  };
209
- const tdb = createTenantDb({ db, tenantId, mode: "system" });
209
+ const tdb = createTenantDb({
210
+ db,
211
+ tenantId,
212
+ mode: "system",
213
+ reason: "secrets API writes for a caller-named tenant, outside a request scope",
214
+ });
210
215
 
211
216
  const existing = await lookup(tenantId, key);
212
217
  const commonFields = {
@@ -271,7 +276,12 @@ function buildSecretsContext(
271
276
  tenantId,
272
277
  roles: SYSTEM_ROLES,
273
278
  };
274
- const tdb = createTenantDb({ db, tenantId, mode: "system" });
279
+ const tdb = createTenantDb({
280
+ db,
281
+ tenantId,
282
+ mode: "system",
283
+ reason: "secrets API writes for a caller-named tenant, outside a request scope",
284
+ });
275
285
  const result = await executor.delete({ id: existing.id }, actor, tdb);
276
286
  return result.isSuccess;
277
287
  },
@@ -279,7 +289,7 @@ function buildSecretsContext(
279
289
  }
280
290
 
281
291
  // Wrap an executor-level write failure into a KumikoError so callers of
282
- // ctx.secrets.set / .delete can still branch on .code / details / i18nKey
292
+ // ctx.services.secrets.set / .delete can still branch on .code / details / i18nKey
283
293
  // after it propagates up. Plain `new Error(...)` would have stripped the
284
294
  // structured payload the error-contract promises.
285
295
  function wrapSetFailure(err: WriteErrorInfo): InternalError {
@@ -28,6 +28,11 @@ function unknownKeyFailure(key: string): SecretKeyWriteCheck {
28
28
  };
29
29
  }
30
30
 
31
+ function isUnderMountedFeaturePrefix(registry: Registry, key: string): boolean {
32
+ const separator = key.indexOf(":");
33
+ return separator > 0 && registry.getFeature(key.slice(0, separator)) !== undefined;
34
+ }
35
+
31
36
  function namespaceAcceptsKey(namespace: SecretNamespaceDefinition, key: string): boolean {
32
37
  const suffix = key.slice(namespace.qualifiedPrefix.length);
33
38
  if (suffix.length === 0) return false;
@@ -38,7 +43,9 @@ function namespaceAcceptsKey(namespace: SecretNamespaceDefinition, key: string):
38
43
  // are writable through the API, so a tenant admin cannot plant arbitrary rows
39
44
  // next to the feature-owned ones. writeRoles narrows the handler access
40
45
  // further for a key or a whole namespace. Delete passes allowUndeclared: it
41
- // creates nothing, and legacy rows under since-undeclared keys must stay removable.
46
+ // creates nothing, and legacy rows under since-undeclared keys must stay removable,
47
+ // unless the key sits under a mounted feature's prefix: those rows belong to the
48
+ // feature and must not be removable by a tenant admin outside its writeRoles.
42
49
  export function checkSecretKeyWrite(
43
50
  registry: Registry,
44
51
  userRoles: readonly string[],
@@ -50,7 +57,7 @@ export function checkSecretKeyWrite(
50
57
  if (
51
58
  !keyDef &&
52
59
  !(namespace && namespaceAcceptsKey(namespace, key)) &&
53
- options.allowUndeclared !== true
60
+ (options.allowUndeclared !== true || isUnderMountedFeaturePrefix(registry, key))
54
61
  ) {
55
62
  return unknownKeyFailure(key);
56
63
  }
@@ -70,7 +77,7 @@ export function checkSecretKeyWrite(
70
77
  return { ok: true, keyDef };
71
78
  }
72
79
 
73
- // The registry surface the value check needs; lets ctx.secrets validate without a full Registry.
80
+ // The registry surface the value check needs; lets ctx.services.secrets validate without a full Registry.
74
81
  export type SecretValueSchemaSource = Pick<Registry, "getSecretKey" | "findSecretNamespace">;
75
82
 
76
83
  // Carries neither the value nor the schema's issues.
@@ -285,7 +285,7 @@ export function createSeoFeature(opts: SeoOptions): FeatureDefinition {
285
285
  sitemapEntries: opts.sitemapEntries,
286
286
  includeLegalPages: opts.includeLegalPages ?? false,
287
287
  hasManagedPages: opts.managedPages !== undefined,
288
- log: ctx.log,
288
+ log: ctx.obs.log,
289
289
  }),
290
290
  );
291
291
 
@@ -1,4 +1,23 @@
1
1
  [
2
+ {
3
+ "version": "0.390.0",
4
+ "type": "fix",
5
+ "title": "Session screens show the device without sessionsClient()",
6
+ "detail": "The list, my-sessions and detail screens showed [object Object] in the device column for apps that do not register sessionsClient(). The session queries (user-session:list, user-session:mine, user-session:detail) now return a deviceLabel string such as \"Chrome 141 · macOS\" next to the unchanged device object, null when the user agent is unknown, and the screens render that field. sessionsClient() only adds the translated \"Unknown device\" for rows without a label."
7
+ },
8
+ {
9
+ "version": "0.390.0",
10
+ "type": "improvement",
11
+ "title": "Sessions tests no longer write projection tables directly",
12
+ "detail": "The sessions integration tests change tenant memberships and user status through the update-member-roles handler or the event-store executor instead of writing into the projection tables. Nothing changes for apps; the package is only republished."
13
+ },
14
+ {
15
+ "version": "0.389.0",
16
+ "type": "breaking",
17
+ "title": "isPrincipalBlocked is no longer exported from sessions",
18
+ "detail": "The sessions export dropped its re-export of isPrincipalBlocked, which ignored isDeleted. isPrincipalRowBlocked from the user export covers status and isDeleted and is what the session callbacks use.",
19
+ "migration": "import isPrincipalRowBlocked (for a user row) or isPrincipalBlocked (for a bare status) from \"@cosmicdrift/kumiko-bundled-features/user\" instead of the sessions export"
20
+ },
2
21
  {
3
22
  "version": "0.384.0",
4
23
  "type": "improvement",
@@ -102,6 +102,7 @@ export function createSessionsFeature(options?: SessionsFeatureOptions): Feature
102
102
  "sessions",
103
103
  (r) => {
104
104
  const adminRoles = access[options?.adminAccess ?? "admin"];
105
+ r.adoptsBundledLists();
105
106
  r.describe(
106
107
  "Tracks signed-in clients in the `store_user_sessions` table (one row per JWT, keyed by the `sid`/`jti` claim) and exposes handlers for `mine` (list your sessions), `revoke`, and `revokeAllOthers`. Session creation and revocation on the hot auth path are handled by `createSessionCallbacks()`, wired into `buildServer({ auth: { ... } })` outside the dispatcher; the same callbacks are also registered as an auth-foundation sessionStore provider, resolvable generically via `resolveSessionStore()`. The feature also ships a manual-trigger cleanup job for pruning expired rows and an optional `autoRevokeOnPasswordChange` hook that mass-revokes all sessions for a user whenever their `passwordHash` changes.",
107
108
  );
@@ -156,7 +157,7 @@ export function createSessionsFeature(options?: SessionsFeatureOptions): Feature
156
157
  } satisfies SessionStoreProvider);
157
158
 
158
159
  // Custom domain-event for cross-instance access-invalidation.
159
- // r.defineEvent registers the schema so ctx.unsafeAppendEvent (revoke,
160
+ // r.defineEvent registers the schema so ctx.events.unsafeAppend (revoke,
160
161
  // revoke-all-others) enforces it at append time. revoke-all-for-user
161
162
  // appends via the low-level append() instead (needs to anchor on
162
163
  // SYSTEM_TENANT_ID — see that handler for why) — append() does NOT
@@ -60,7 +60,7 @@ export async function cleanupJob(
60
60
  let stoppedReason: SessionCleanupResult["stoppedReason"] = "empty";
61
61
 
62
62
  while (true) {
63
- if (ctx.signal?.aborted) {
63
+ if (ctx.lifecycle.signal?.aborted) {
64
64
  stoppedReason = "signal";
65
65
  break;
66
66
  }
@@ -79,5 +79,5 @@ export async function cleanupJob(
79
79
  }
80
80
 
81
81
  const result: SessionCleanupResult = { deleted, batchesProcessed, stoppedReason };
82
- ctx.log?.info?.(`[sessions:cleanup] complete: ${JSON.stringify(result)}`);
82
+ ctx.obs.log?.info?.(`[sessions:cleanup] complete: ${JSON.stringify(result)}`);
83
83
  }
@@ -3,7 +3,7 @@ import { access, defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine
3
3
  import * as z from "zod";
4
4
  import { decryptStoredPii } from "../../shared/index.ts";
5
5
  import { userSessionTable } from "../schema/user-session.ts";
6
- import { parsedDeviceSchema, parseUserAgent } from "../web/user-agent.ts";
6
+ import { formatDeviceLabel, parsedDeviceSchema, parseUserAgent } from "../web/user-agent.ts";
7
7
 
8
8
  // Admin single-session inspector — mirrors list.query's decrypt handling for
9
9
  // the one-row case. ctx.db (TenantDb) applies tenant-scoping automatically.
@@ -16,6 +16,7 @@ const sessionRowSchema = z.object({
16
16
  ip: z.string().nullable(),
17
17
  userAgent: z.string().nullable(),
18
18
  device: parsedDeviceSchema,
19
+ deviceLabel: z.string().nullable(),
19
20
  });
20
21
 
21
22
  export const createDetailQuery = (adminRoles: readonly string[] = access.admin) =>
@@ -42,6 +43,7 @@ export const createDetailQuery = (adminRoles: readonly string[] = access.admin)
42
43
  const userAgent = row.userAgent
43
44
  ? await decryptStoredPii(row.userAgent, "userAgent", "sessions:detail")
44
45
  : row.userAgent;
46
+ const device = parseUserAgent(userAgent);
45
47
  return {
46
48
  id: row.id,
47
49
  userId: row.userId,
@@ -50,7 +52,8 @@ export const createDetailQuery = (adminRoles: readonly string[] = access.admin)
50
52
  revokedAt: row.revokedAt,
51
53
  ip: row.ip ? await decryptStoredPii(row.ip, "ip", "sessions:detail") : row.ip,
52
54
  userAgent,
53
- device: parseUserAgent(userAgent),
55
+ device,
56
+ deviceLabel: formatDeviceLabel(device),
54
57
  };
55
58
  },
56
59
  });
@@ -8,7 +8,7 @@ import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
8
8
  import * as z from "zod";
9
9
  import { decryptStoredPii } from "../../shared/index.ts";
10
10
  import { userSessionTable } from "../schema/user-session.ts";
11
- import { parsedDeviceSchema, parseUserAgent } from "../web/user-agent.ts";
11
+ import { formatDeviceLabel, parsedDeviceSchema, parseUserAgent } from "../web/user-agent.ts";
12
12
 
13
13
  // `sort` arrives raw from the client's query string. selectMany's orderBy
14
14
  // has no column-existence check — an unrecognised field just gets
@@ -49,6 +49,7 @@ export const createListQuery = (adminRoles: readonly string[] = access.admin) =>
49
49
  ip: z.string().nullable(),
50
50
  userAgent: z.string().nullable(),
51
51
  device: parsedDeviceSchema,
52
+ deviceLabel: z.string().nullable(),
52
53
  }),
53
54
  ),
54
55
  nextCursor: z.string().nullable(),
@@ -85,6 +86,7 @@ export const createListQuery = (adminRoles: readonly string[] = access.admin) =>
85
86
  const userAgent = r.userAgent
86
87
  ? await decryptStoredPii(r.userAgent, "userAgent", "sessions:list")
87
88
  : r.userAgent;
89
+ const device = parseUserAgent(userAgent);
88
90
  return {
89
91
  id: r.id,
90
92
  userId: r.userId,
@@ -93,7 +95,8 @@ export const createListQuery = (adminRoles: readonly string[] = access.admin) =>
93
95
  revokedAt: r.revokedAt,
94
96
  ip: r.ip ? await decryptStoredPii(r.ip, "ip", "sessions:list") : r.ip,
95
97
  userAgent,
96
- device: parseUserAgent(userAgent),
98
+ device,
99
+ deviceLabel: formatDeviceLabel(device),
97
100
  };
98
101
  }),
99
102
  );
@@ -3,7 +3,7 @@ import { definePagedQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
3
3
  import * as z from "zod";
4
4
  import { decryptStoredPii } from "../../shared/index.ts";
5
5
  import { userSessionTable } from "../schema/user-session.ts";
6
- import { parsedDeviceSchema, parseUserAgent } from "../web/user-agent.ts";
6
+ import { formatDeviceLabel, parsedDeviceSchema, parseUserAgent } from "../web/user-agent.ts";
7
7
 
8
8
  // "My live sessions" — the backing data for a devices/sessions UI. Returns
9
9
  // ONLY the current user's own, currently-live sessions, ordered by most-
@@ -31,6 +31,7 @@ export const mineQuery = definePagedQueryHandler({
31
31
  ip: z.string().nullable(),
32
32
  userAgent: z.string().nullable(),
33
33
  device: parsedDeviceSchema,
34
+ deviceLabel: z.string().nullable(),
34
35
  current: z.boolean(),
35
36
  }),
36
37
  ),
@@ -57,13 +58,15 @@ export const mineQuery = definePagedQueryHandler({
57
58
  const userAgent = r.userAgent
58
59
  ? await decryptStoredPii(r.userAgent, "userAgent", "sessions:mine")
59
60
  : r.userAgent;
61
+ const device = parseUserAgent(userAgent);
60
62
  return {
61
63
  id: r.id,
62
64
  createdAt: r.createdAt,
63
65
  expiresAt: r.expiresAt,
64
66
  ip: r.ip ? await decryptStoredPii(r.ip, "ip", "sessions:mine") : r.ip,
65
67
  userAgent,
66
- device: parseUserAgent(userAgent),
68
+ device,
69
+ deviceLabel: formatDeviceLabel(device),
67
70
  current: currentSid === r.id,
68
71
  };
69
72
  }),
@@ -67,9 +67,9 @@ export const revokeAllForUserWrite = defineWriteHandler({
67
67
  // Lightweight append alongside the direct-write above; see revoke.write.ts
68
68
  // for why this isn't a lifecycle event on
69
69
  // store_user_sessions. Uses the low-level append() (not
70
- // ctx.unsafeAppendEvent) because this handler is deliberately
70
+ // ctx.events.unsafeAppend) because this handler is deliberately
71
71
  // cross-tenant: the privileged caller's own tenantId (what
72
- // ctx.unsafeAppendEvent would bind to) has no relationship to the
72
+ // ctx.events.unsafeAppend would bind to) has no relationship to the
73
73
  // target user's session tenant(s). Anchored on SYSTEM_TENANT_ID
74
74
  // instead, same as the jobs feature's cross-tenant run-lifecycle
75
75
  // events (job-run-logger.ts) — a fresh aggregate per call, so no
@@ -77,7 +77,7 @@ export const revokeAllForUserWrite = defineWriteHandler({
77
77
  //
78
78
  // append() itself does NOT validate against the r.defineEvent-
79
79
  // registered schema (that guarantee only comes from going through
80
- // ctx.unsafeAppendEvent's dispatcher path). Mirrors job-run-logger.ts:
80
+ // ctx.events.unsafeAppend's dispatcher path). Mirrors job-run-logger.ts:
81
81
  // parse explicitly before the raw append so a shape drift fails loudly
82
82
  // here instead of landing unvalidated on the events table.
83
83
  if (updated.length > 0) {
@@ -51,7 +51,7 @@ export const revokeAllOthersWrite = defineWriteHandler({
51
51
  // store_user_sessions. Skipped when nothing was revoked (no other
52
52
  // live sessions) — a spurious invalidation would be the failure mode.
53
53
  if (updated.length > 0) {
54
- await ctx.unsafeAppendEvent({
54
+ await ctx.events.unsafeAppend({
55
55
  aggregateId: generateId(),
56
56
  aggregateType: SESSION_REVOKED_AGGREGATE_TYPE,
57
57
  type: SESSION_REVOKED_EVENT_QN,
@@ -55,7 +55,7 @@ export const revokeWrite = defineWriteHandler({
55
55
  // unmanaged direct-write store, see feature.ts). Own aggregate id per
56
56
  // revoke, no predecessor to satisfy, no version_conflict against a
57
57
  // concurrent revoke on a different sid.
58
- await ctx.unsafeAppendEvent({
58
+ await ctx.events.unsafeAppend({
59
59
  aggregateId: generateId(),
60
60
  aggregateType: SESSION_REVOKED_AGGREGATE_TYPE,
61
61
  type: SESSION_REVOKED_EVENT_QN,
@@ -15,7 +15,7 @@ export type {
15
15
  SessionCallbacksOptions,
16
16
  SessionMassRevoker,
17
17
  } from "./session-callbacks.ts";
18
- export { createSessionCallbacks, isPrincipalBlocked } from "./session-callbacks.ts";
18
+ export { createSessionCallbacks } from "./session-callbacks.ts";
19
19
  export type { SessionRevokedPayload } from "./session-revoked-event.ts";
20
20
  export {
21
21
  SESSION_REVOKED_AGGREGATE_TYPE,
@@ -39,7 +39,7 @@ export const createSessionListScreen = (
39
39
  refLabelField: "displayName",
40
40
  },
41
41
  {
42
- field: "device",
42
+ field: "deviceLabel",
43
43
  label: i18nKey("sessions.list.col.device"),
44
44
  renderer: DEVICE_RENDERER,
45
45
  },
@@ -94,7 +94,7 @@ export const createSessionDetailScreen = (
94
94
  { field: "createdAt", renderer: { format: "timestamp" } },
95
95
  { field: "expiresAt", renderer: { format: "timestamp" } },
96
96
  { field: "revokedAt", renderer: { format: "timestamp" } },
97
- { field: "device", renderer: DEVICE_RENDERER },
97
+ { field: "deviceLabel", renderer: DEVICE_RENDERER },
98
98
  "ip",
99
99
  "userAgent",
100
100
  ],
@@ -107,7 +107,7 @@ export const createSessionDetailScreen = (
107
107
  createdAt: i18nKey("sessions.detail.field.createdAt"),
108
108
  expiresAt: i18nKey("sessions.detail.field.expiresAt"),
109
109
  revokedAt: i18nKey("sessions.detail.field.revokedAt"),
110
- device: i18nKey("sessions.list.col.device"),
110
+ deviceLabel: i18nKey("sessions.list.col.device"),
111
111
  ip: i18nKey("sessions.detail.field.ip"),
112
112
  userAgent: i18nKey("sessions.detail.field.userAgent"),
113
113
  },
@@ -120,7 +120,11 @@ export const sessionMineScreen: ProjectionListScreenDefinition = {
120
120
  type: "projectionList",
121
121
  query: SessionQueries.mine,
122
122
  columns: [
123
- { field: "device", label: i18nKey("sessions.mine.col.userAgent"), renderer: DEVICE_RENDERER },
123
+ {
124
+ field: "deviceLabel",
125
+ label: i18nKey("sessions.mine.col.userAgent"),
126
+ renderer: DEVICE_RENDERER,
127
+ },
124
128
  { field: "ip", label: i18nKey("sessions.mine.col.ip") },
125
129
  {
126
130
  field: "current",
@@ -18,12 +18,7 @@ import { generateId, parseRoles } from "@cosmicdrift/kumiko-framework/utils";
18
18
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
19
19
  import { encryptForDirectWrite } from "../shared/index.ts";
20
20
  import { tenantMembershipsTable } from "../tenant/index.ts";
21
- import {
22
- isPrincipalBlocked,
23
- isPrincipalRowBlocked,
24
- type UserStatus,
25
- userTable,
26
- } from "../user/index.ts";
21
+ import { isPrincipalRowBlocked, type UserStatus, userTable } from "../user/index.ts";
27
22
  import {
28
23
  DEFAULT_SESSION_EXPIRY_MS,
29
24
  LAST_SEEN_REFRESH_MS,
@@ -36,9 +31,6 @@ import {
36
31
  sessionRevokedSchema,
37
32
  } from "./session-revoked-event.ts";
38
33
 
39
- // Re-exported so existing `../sessions` importers keep working unchanged.
40
- export { isPrincipalBlocked };
41
-
42
34
  type SettledLookup<T> = { readonly ok: true; readonly value: T } | { readonly ok: false };
43
35
 
44
36
  function settleLookup<T>(lookup: Promise<T>): Promise<SettledLookup<T>> {