@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
@@ -9,10 +9,10 @@
9
9
  // - emitEvent with headers — Marten free key/value metadata
10
10
  // - r.projection — single-stream read model (inline in the write TX)
11
11
  // - r.multiStreamProjection — cross-aggregate async read model
12
- // - ctx.loadAggregate with { asOf } — point-in-time aggregate state
13
- // - ctx.archiveStream / ctx.restoreStream — Marten ArchiveStream
14
- // - ctx.queryProjection — tenant-scoped read of a projection table
15
- // - ctx.snapshotAggregate + ctx.loadAggregateWithSnapshot — perf path
12
+ // - ctx.events.load with { asOf } — point-in-time aggregate state
13
+ // - ctx.events.archive / ctx.events.restore — Marten ArchiveStream
14
+ // - ctx.dispatch.queryProjection — tenant-scoped read of a projection table
15
+ // - ctx.events.snapshot + ctx.events.loadWithSnapshot — perf path
16
16
  // for aggregates with long event tails (O(1) vs O(N) reduce), incl.
17
17
  // the auto-snapshot policy ({ snapshotEvery, snapshotVersion })
18
18
  // - streamAllEventsByType — memory-bounded iteration for ops/export jobs
@@ -339,7 +339,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
339
339
  name: "invoice:archive",
340
340
  schema: z.object({ id: z.uuid() }),
341
341
  handler: async (event, ctx) => {
342
- await ctx.archiveStream(event.payload.id, { aggregateType: "showcase-invoice" });
342
+ await ctx.events.archive(event.payload.id, { aggregateType: "showcase-invoice" });
343
343
  return { isSuccess: true as const, data: { id: event.payload.id } };
344
344
  },
345
345
  access: { roles: ["Admin"] },
@@ -354,14 +354,14 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
354
354
  handler: async (event, ctx) => {
355
355
  // Rebuild the state from the current stream so the snapshot reflects
356
356
  // the committed event log, not an in-memory guess. Use the upcaster-
357
- // aware ctx.loadAggregate to stay consistent with read-time semantics.
358
- const events = await ctx.loadAggregate(event.payload.id);
357
+ // aware ctx.events.load to stay consistent with read-time semantics.
358
+ const events = await ctx.events.load(event.payload.id);
359
359
  const state: InvoiceState = { ...initialInvoiceState };
360
360
  for (const evt of events) {
361
361
  reduceInvoice(state, evt);
362
362
  }
363
363
  const version = events.length > 0 ? (events[events.length - 1]?.version ?? 0) : 0;
364
- await ctx.snapshotAggregate({
364
+ await ctx.events.snapshot({
365
365
  aggregateId: event.payload.id,
366
366
  aggregateType: "showcase-invoice",
367
367
  version,
@@ -374,13 +374,13 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
374
374
 
375
375
  // --- Query handlers ---
376
376
 
377
- // Live aggregation via ctx.loadAggregate — reduces events into a state
377
+ // Live aggregation via ctx.events.load — reduces events into a state
378
378
  // snapshot. Supports asOf for point-in-time reads.
379
379
  r.queryHandler({
380
380
  name: "invoice:state",
381
381
  schema: z.object({ id: z.uuid(), asOf: z.iso.datetime().optional() }),
382
382
  handler: async (query, ctx) => {
383
- const events = await ctx.loadAggregate(query.payload.id, {
383
+ const events = await ctx.events.load(query.payload.id, {
384
384
  ...(query.payload.asOf ? { asOf: Temporal.Instant.from(query.payload.asOf) } : {}),
385
385
  });
386
386
  const state: InvoiceState = { ...initialInvoiceState };
@@ -405,7 +405,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
405
405
  name: "invoice:fast-state",
406
406
  schema: z.object({ id: z.uuid() }),
407
407
  handler: async (query, ctx) => {
408
- const result = await ctx.loadAggregateWithSnapshot<InvoiceStateRecord>(
408
+ const result = await ctx.events.loadWithSnapshot<InvoiceStateRecord>(
409
409
  query.payload.id,
410
410
  (state, evt) => {
411
411
  const next: InvoiceStateRecord = { ...state };
@@ -434,7 +434,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
434
434
  },
435
435
  });
436
436
 
437
- // Read-model query via ctx.queryProjection — auto-tenant-scoped. The
437
+ // Read-model query via ctx.dispatch.queryProjection — auto-tenant-scoped. The
438
438
  // helper collapses the zero-argument "return this projection" pattern
439
439
  // into a single call; returns { rows } so the response shape stays
440
440
  // consistent with other list handlers.
@@ -450,11 +450,11 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
450
450
  }),
451
451
  );
452
452
 
453
- // Slow synthetic export — demonstrates ctx.signal honouring inside a
453
+ // Slow synthetic export — demonstrates ctx.lifecycle.signal honouring inside a
454
454
  // long-running handler loop. Real-world equivalents: PDF batch export,
455
455
  // bulk CSV download, multi-stream audit walk. Mobile clients that
456
456
  // navigate away mid-export should NOT keep the server busy producing
457
- // bytes nobody will read. The handler checks ctx.signal at every
457
+ // bytes nobody will read. The handler checks ctx.lifecycle.signal at every
458
458
  // iteration; on abort, throws and the dispatcher returns an error to
459
459
  // the (already-disconnected) client.
460
460
  r.queryHandler({
@@ -463,7 +463,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
463
463
  handler: async (query, ctx) => {
464
464
  const accumulated: number[] = [];
465
465
  for (let i = 0; i < query.payload.chunks; i++) {
466
- ctx.signal?.throwIfAborted();
466
+ ctx.lifecycle.signal?.throwIfAborted();
467
467
  // Fake "work per chunk". Realistic handler would assemble a row,
468
468
  // hit a remote service, etc.
469
469
  await new Promise((resolve) => setTimeout(resolve, 10));
@@ -5,7 +5,7 @@ File upload plus post-processing hooks.
5
5
  ## What it shows
6
6
 
7
7
  - Declarative `variants` on `createImageField` (file-derivatives)
8
- - Imperative `ctx.derivatives.variant()` path and the public variant route (`EXT_DERIVATIVE_PUBLIC_PREDICATE`)
8
+ - Imperative `ctx.services.derivatives.variant()` path and the public variant route (`EXT_DERIVATIVE_PUBLIC_PREDICATE`)
9
9
 
10
10
  ## Source
11
11
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-sample-files-post-processing",
3
- "description": "The three ways to generate an image variant: declarative field variants, imperative ctx.derivatives.variant() with region-blur, and the public route behind a default-deny predicate.",
3
+ "description": "The three ways to generate an image variant: declarative field variants, imperative ctx.services.derivatives.variant() with region-blur, and the public route behind a default-deny predicate.",
4
4
  "private": true,
5
5
  "dependencies": {
6
6
  "@cosmicdrift/kumiko-bundled-features": "workspace:*",
@@ -9,7 +9,7 @@
9
9
  // resolves + caches the variant behind the auth-gated
10
10
  // `GET /api/files/:id/variant/:name` route, no handler code needed.
11
11
  // 2. Imperative — the `set-cover` write-handler calls
12
- // `ctx.derivatives.variant()` directly. This is for apps that track
12
+ // `ctx.services.derivatives.variant()` directly. This is for apps that track
13
13
  // images as their own records (ordering, a chosen cover image) and for
14
14
  // region-blur, whose coordinates are runtime data that can't live in a
15
15
  // static field declaration.
@@ -88,13 +88,15 @@ export const filesPostProcessingFeature = defineFeature("files-post-processing",
88
88
  .optional(),
89
89
  }),
90
90
  handler: async (event, ctx) => {
91
- if (!ctx.derivatives) {
91
+ if (!ctx.services.derivatives) {
92
92
  return writeFailure(
93
- new InternalError({ message: "ctx.derivatives missing — is file-derivatives mounted?" }),
93
+ new InternalError({
94
+ message: "ctx.services.derivatives missing — is file-derivatives mounted?",
95
+ }),
94
96
  );
95
97
  }
96
98
  const { fileRefId, blurRegion } = event.payload;
97
- const result = await ctx.derivatives.variant(
99
+ const result = await ctx.services.derivatives.variant(
98
100
  fileRefId,
99
101
  {
100
102
  maxEdge: 800,
@@ -7,7 +7,7 @@ import {
7
7
  emitEvent,
8
8
  setFields,
9
9
  } from "@cosmicdrift/kumiko-framework/engine";
10
- import { failNotFound } from "@cosmicdrift/kumiko-framework/errors";
10
+ import { failNotFound, failUnprocessable } from "@cosmicdrift/kumiko-framework/errors";
11
11
  import { z } from "zod";
12
12
 
13
13
  export const ticketEntity = createEntity({
@@ -43,6 +43,7 @@ export const helpdeskFeature = defineFeature("helpdesk", (r) => {
43
43
  if (!ticket) {
44
44
  return failNotFound("ticket", event.payload.id);
45
45
  }
46
+ if (ticket["isClosed"]) return failUnprocessable("ticket_already_closed");
46
47
  await emitEvent(ctx, ticketClosed, {
47
48
  aggregateId: event.payload.id,
48
49
  aggregateType: "ticket",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-sample-lane-routing",
3
- "description": "runIn lane-routing: r.job pinned to api/worker deploy-lanes, event-chain via ctx.appendEvent, verified end-to-end through createAllInOneEntrypoint.",
3
+ "description": "runIn lane-routing: r.job pinned to api/worker deploy-lanes, event-chain via ctx.events.append, verified end-to-end through createAllInOneEntrypoint.",
4
4
  "private": true,
5
5
  "dependencies": {
6
6
  "@cosmicdrift/kumiko-framework": "workspace:*"
@@ -100,9 +100,9 @@ A one-shot setup routine that runs on first boot or via the CLI:
100
100
  // bin/seed-legal.ts
101
101
  import { seedTextBlock } from "@cosmicdrift/kumiko-bundled-features/template-resolver/seeding";
102
102
  import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
103
- import { createDb } from "@cosmicdrift/kumiko-framework/db";
103
+ import { createDbConnection } from "@cosmicdrift/kumiko-framework/db";
104
104
 
105
- const db = createDb(process.env.DATABASE_URL!);
105
+ const { db } = createDbConnection({ url: process.env.DATABASE_URL! });
106
106
 
107
107
  await seedTextBlock(db, {
108
108
  tenantId: SYSTEM_TENANT_ID,
@@ -27,6 +27,10 @@ back.
27
27
  - **`r.hook({ type: "postDelete", target: { allOf: entityRef }, handler })`** — fires after commit.
28
28
  External side effects (logs, notifications) belong here because
29
29
  failures cannot roll the delete back.
30
+ - **`ctx.lifecycle.afterRollback(fn)`** — the counterpart of `ctx.lifecycle.afterCommit`: runs `fn` when
31
+ the handler transaction (or the enclosing savepoint of a nested `ctx.dispatch.write`) does not commit.
32
+ Use it to give back something reserved outside the transaction, like the slot in
33
+ `article:reserve-slot`. A failed COMMIT triggers it too, so write the callback idempotent.
30
34
 
31
35
  ## When to reach for it
32
36
 
@@ -13,7 +13,12 @@ import {
13
13
  defineFeature,
14
14
  type SaveContext,
15
15
  } from "@cosmicdrift/kumiko-framework/engine";
16
- import { ConflictError } from "@cosmicdrift/kumiko-framework/errors";
16
+ import {
17
+ ConflictError,
18
+ UnprocessableError,
19
+ writeFailure,
20
+ } from "@cosmicdrift/kumiko-framework/errors";
21
+ import * as z from "zod";
17
22
 
18
23
  export const articleEntity = createEntity({
19
24
  table: "read_sample_articles",
@@ -35,6 +40,9 @@ export const articleEntity = createEntity({
35
40
  // Collect hook events for testing
36
41
  export const hookLog: { type: string; data: Record<string, unknown> }[] = [];
37
42
 
43
+ // Stands in for a quota held in an external system, outside the handler transaction.
44
+ export const reservedSlots = new Set<string>();
45
+
38
46
  const editorWrite = { access: { roles: ["Admin", "Editor"] } } as const;
39
47
 
40
48
  export const articlesFeature = defineFeature("blog", (r) => {
@@ -48,6 +56,23 @@ export const articlesFeature = defineFeature("blog", (r) => {
48
56
  );
49
57
  r.writeHandler(defineEntityDeleteHandler("article", articleEntity, editorWrite));
50
58
 
59
+ // --- scheduleAfterRollback: give back a reservation made outside the transaction when it does
60
+ // not commit. The callback must be idempotent: a failed COMMIT also triggers it.
61
+ r.writeHandler({
62
+ name: "article:reserve-slot",
63
+ schema: z.object({ slot: z.string(), reject: z.boolean() }),
64
+ access: editorWrite.access,
65
+ handler: async (event, ctx) => {
66
+ const { slot, reject } = event.payload;
67
+ reservedSlots.add(slot);
68
+ ctx.lifecycle.afterRollback?.(async () => {
69
+ reservedSlots.delete(slot);
70
+ });
71
+ if (reject) return writeFailure(new UnprocessableError("slot_rejected"));
72
+ return { isSuccess: true as const, data: {} };
73
+ },
74
+ });
75
+
51
76
  // --- Validation hook on create: reject banned words + length ---
52
77
  r.hook({
53
78
  type: "validation",
@@ -0,0 +1,50 @@
1
+ # List table options
2
+
3
+ Opt-in table behaviour for entity, projection and related lists. A list without
4
+ the `table` block renders and behaves as before.
5
+
6
+ ## What it shows
7
+
8
+ - `table.rememberState`: search, sort, page size, facets and date range survive
9
+ leaving and re-entering the list within the browser session.
10
+ - `table.resetAll`: one button resets everything that differs from the defaults.
11
+ - `table.columnPicker`: the view menu lets the user hide and reorder columns;
12
+ `hidden: true` on a column starts it hidden.
13
+ - `table.density`: row density default and an optional user choice.
14
+ - `table.pinning`: the user pins columns left or right in the view menu;
15
+ `pinned: "left"` on a column starts it pinned.
16
+ - `table.resizing`: columns can be resized (table-layout fixed); `width` and
17
+ `minWidth` on a column are start values in px.
18
+ - `table.keyboardNavigation`: rows are one tab stop, arrow keys move between rows,
19
+ Space selects the focused row.
20
+ - `table.export`: an Export button downloads the whole filtered list as an XLSX
21
+ file; `maxRows` lowers the default limit of 10000 rows.
22
+ Reference columns export their label and timestamps use the browser time zone.
23
+ - `bulkActions`: row checkboxes and a selection bar. "Delete" runs the delete
24
+ row action once per selected row; done tasks are skipped.
25
+ - `table.grouping`: the server groups the list by status; each header shows the
26
+ group's count and estimate sum, "done" starts collapsed and the view menu
27
+ offers grouping by priority or none.
28
+ - `table.totals`: a footer row with the estimate sum over the filtered list.
29
+ - `facets.estimateHours: { display: "range" }`: from/to inputs for a number field.
30
+ - The same block on a `projectionList`, and `task:workload`, a hand-written list
31
+ query that keeps grouping and totals by accepting `aggregate`
32
+ (`entityListSchema`) and answering it with `answerListAggregate`.
33
+
34
+ ## Source
35
+
36
+ Feature entry point: `src/feature.ts`.
37
+
38
+ ## Tests
39
+
40
+ ```bash
41
+ cd samples/recipes/list-table-options
42
+ bun test
43
+ ```
44
+
45
+ The test boots the feature through the boot validator and checks that a
46
+ misspelled key, a hidden column without the picker, an unknown density, a pinned column
47
+ without `pinning`, a width without `resizing` and a bulk action on a navigate
48
+ row action are rejected.
49
+ `workload.integration.test.ts` runs the hand-written query over HTTP and checks
50
+ the grouped sums and the filtered total.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@cosmicdrift/kumiko-sample-list-table-options",
3
+ "description": "Opt-in table behaviour of a list: remembered search/sort/filters, one reset button, column picker with hidden columns, row density, server-side groups with sums, a totals row and a number range facet.",
4
+ "private": true,
5
+ "dependencies": {
6
+ "@cosmicdrift/kumiko-framework": "workspace:*"
7
+ },
8
+ "devDependencies": {
9
+ "@cosmicdrift/kumiko-testing": "workspace:*"
10
+ }
11
+ }
@@ -0,0 +1,221 @@
1
+ import {
2
+ answerListAggregate,
3
+ buildEntityTable,
4
+ createEventStoreExecutor,
5
+ } from "@cosmicdrift/kumiko-framework/db";
6
+ import {
7
+ createDateField,
8
+ createEntity,
9
+ createNumberField,
10
+ createSelectField,
11
+ createTextField,
12
+ defineFeature,
13
+ entityListSchema,
14
+ type FeatureDefinition,
15
+ } from "@cosmicdrift/kumiko-framework/engine";
16
+ import type { ListTableOptions } from "@cosmicdrift/kumiko-framework/ui-types";
17
+ import { z } from "zod";
18
+
19
+ export const taskEntity = createEntity({
20
+ table: "read_list_table_tasks",
21
+ fields: {
22
+ title: createTextField({
23
+ personal: false,
24
+ reason: "is_business_data",
25
+ required: true,
26
+ searchable: true,
27
+ sortable: true,
28
+ }),
29
+ status: createSelectField({
30
+ options: ["open", "inProgress", "blocked", "done"] as const,
31
+ default: "open",
32
+ sortable: true,
33
+ filterable: true,
34
+ }),
35
+ priority: createSelectField({
36
+ options: ["low", "medium", "high"] as const,
37
+ default: "medium",
38
+ sortable: true,
39
+ filterable: true,
40
+ }),
41
+ dueDate: createDateField({ sortable: true }),
42
+ assignee: createTextField({ personal: false, reason: "is_business_data", sortable: true }),
43
+ estimateHours: createNumberField({ sortable: true, filterable: true }),
44
+ },
45
+ });
46
+
47
+ export const openAccess = {
48
+ access: {
49
+ openToAll: { reason: "recipe: any signed-in user manages the tasks; no per-user ownership" },
50
+ },
51
+ } as const;
52
+
53
+ // Overridable so a test can feed the boot validator a block that must be rejected.
54
+ export function createTaskBoardFeature(
55
+ taskTable: ListTableOptions = {
56
+ rememberState: true,
57
+ resetAll: true,
58
+ columnPicker: true,
59
+ density: { default: "comfortable", userChoice: true },
60
+ pinning: true,
61
+ resizing: true,
62
+ keyboardNavigation: true,
63
+ export: { xlsx: true, maxRows: 5000 },
64
+ grouping: { field: "status", collapsedWhen: "done", userChoice: true, fields: ["priority"] },
65
+ totals: [{ field: "estimateHours", kind: "sum" }],
66
+ },
67
+ ): FeatureDefinition {
68
+ return defineFeature("task-board", (r) => {
69
+ r.crud("task", taskEntity, { write: openAccess, read: openAccess });
70
+
71
+ r.screen({
72
+ id: "task-list",
73
+ type: "entityList",
74
+ entity: "task",
75
+ columns: [
76
+ // Starts pinned left; width and minWidth are start values for resizing.
77
+ { field: "title", pinned: "left", width: 280, minWidth: 160 },
78
+ "status",
79
+ "priority",
80
+ { field: "dueDate", hideOnNarrow: true },
81
+ { field: "assignee", hideOnNarrow: true },
82
+ // Starts hidden; the user can add it back in the view menu.
83
+ { field: "estimateHours", hidden: true, hideOnNarrow: true },
84
+ ],
85
+ facets: { status: { display: "chips" }, priority: {}, estimateHours: { display: "range" } },
86
+ defaultSort: { field: "dueDate", dir: "asc" },
87
+ searchable: true,
88
+ table: taskTable,
89
+ rowActions: [
90
+ { kind: "navigate", id: "open", label: "Open", screen: "task-edit", rowClick: true },
91
+ {
92
+ kind: "writeHandler",
93
+ id: "delete",
94
+ label: "Delete",
95
+ handler: "task-board:write:task:delete",
96
+ style: "danger",
97
+ // Done tasks are skipped by the bulk run and counted as skipped.
98
+ visible: { field: "status", ne: "done" },
99
+ },
100
+ ],
101
+ // Row checkboxes and a selection bar; each selected row gets its own delete write.
102
+ bulkActions: [{ id: "delete", rowAction: "delete" }],
103
+ access: openAccess.access,
104
+ });
105
+
106
+ r.screen({
107
+ id: "task-edit",
108
+ type: "entityEdit",
109
+ entity: "task",
110
+ dormant: true,
111
+ listScreenId: "task-list",
112
+ layout: {
113
+ sections: [
114
+ {
115
+ columns: 2,
116
+ fields: ["title", "assignee", "status", "priority", "dueDate", "estimateHours"],
117
+ },
118
+ ],
119
+ },
120
+ });
121
+
122
+ // The same block works on a list over any query.
123
+ r.queryHandler({
124
+ name: "task:overdue",
125
+ schema: z.object({}),
126
+ handler: async () => ({
127
+ rows: [{ id: "t1", title: "Quarterly VAT filing", assignee: "Ines", daysLate: 3 }],
128
+ nextCursor: null,
129
+ }),
130
+ access: openAccess.access,
131
+ outputSchema: z.object({
132
+ rows: z.array(
133
+ z.object({
134
+ id: z.string(),
135
+ title: z.string(),
136
+ assignee: z.string(),
137
+ daysLate: z.number(),
138
+ }),
139
+ ),
140
+ nextCursor: z.string().nullable(),
141
+ }),
142
+ });
143
+
144
+ r.screen({
145
+ id: "task-overdue",
146
+ type: "projectionList",
147
+ query: "task-board:query:task:overdue",
148
+ columns: ["title", "assignee", "daysLate"],
149
+ table: { rememberState: true, density: { userChoice: true } },
150
+ });
151
+
152
+ // A hand-written list query keeps grouping and totals when it accepts
153
+ // `aggregate` (entityListSchema carries it) and answers it on the server.
154
+ const taskExecutor = createEventStoreExecutor(
155
+ buildEntityTable("task", taskEntity),
156
+ taskEntity,
157
+ {
158
+ entityName: "task",
159
+ },
160
+ );
161
+ r.queryHandler({
162
+ name: "task:workload",
163
+ schema: entityListSchema,
164
+ handler: async (query, ctx) => {
165
+ const parentVisibility = { entities: ctx.registry.getAllEntities() };
166
+ const listPayload = query.payload;
167
+ if (listPayload.aggregate !== undefined) {
168
+ return answerListAggregate(
169
+ taskExecutor,
170
+ { ...listPayload, aggregate: listPayload.aggregate },
171
+ query.user,
172
+ ctx.db,
173
+ { parentVisibility },
174
+ {
175
+ entity: taskEntity,
176
+ resolveEntity: (name) => ctx.registry.getEntity(name),
177
+ viewer: { user: query.user, parentVisibility },
178
+ },
179
+ );
180
+ }
181
+ return taskExecutor.list(listPayload, query.user, ctx.db, { parentVisibility });
182
+ },
183
+ access: openAccess.access,
184
+ });
185
+
186
+ r.screen({
187
+ id: "task-workload",
188
+ type: "projectionList",
189
+ query: "task-board:query:task:workload",
190
+ columns: ["title", "assignee", "status", "estimateHours"],
191
+ defaultSort: { field: "title", dir: "asc" },
192
+ table: {
193
+ grouping: { field: "assignee" },
194
+ totals: [{ field: "estimateHours", kind: "sum" }],
195
+ },
196
+ });
197
+
198
+ r.nav({ id: "tasks", label: "task-board:nav.tasks", icon: "list", order: 10 });
199
+ r.nav({
200
+ id: "task-list",
201
+ label: "task-board:nav.list",
202
+ parent: "task-board:nav:tasks",
203
+ screen: "task-board:screen:task-list",
204
+ order: 10,
205
+ });
206
+ r.nav({
207
+ id: "task-overdue",
208
+ label: "task-board:nav.overdue",
209
+ parent: "task-board:nav:tasks",
210
+ screen: "task-board:screen:task-overdue",
211
+ order: 20,
212
+ });
213
+ r.nav({
214
+ id: "task-workload",
215
+ label: "task-board:nav.workload",
216
+ parent: "task-board:nav:tasks",
217
+ screen: "task-board:screen:task-workload",
218
+ order: 30,
219
+ });
220
+ });
221
+ }
@@ -43,7 +43,7 @@ r.config({
43
43
 
44
44
  - **`backing: "secrets"`** — the value lives in the secrets store (envelope
45
45
  encryption, KEK rotation, audit-on-read) instead of `config_values`. Set/read/
46
- reset dispatch through `ctx.secrets`; the query handlers mask the value while
46
+ reset dispatch through `ctx.services.secrets`; the query handlers mask the value while
47
47
  the owning feature still reads the revealed plaintext via `ctx.config`. Only
48
48
  valid for `scope: "system"` — secrets are flat per `(tenant, key)` with no
49
49
  cascade, so a tenant override would be incoherent and is rejected at boot.
@@ -72,7 +72,7 @@ await runProdApp({
72
72
  envSchema, // composeEnvSchema({ core: frameworkCoreEnvSchema, features })
73
73
  extraContext: ({ db, registry }) => ({
74
74
  configResolver: resolver,
75
- _configAccessorFactory: createConfigAccessorFactory(registry, resolver),
75
+ configAccessorFactory: createConfigAccessorFactory(registry, resolver),
76
76
  // Required whenever a key declares backing:"secrets" — without it the set
77
77
  // and read paths fail loud, never silently miss.
78
78
  secrets: createSecretsContext({ db, masterKeyProvider, registry }),
@@ -82,7 +82,7 @@ await runProdApp({
82
82
  ```
83
83
 
84
84
  Register the `secrets` feature too so its `tenant_secrets` table is migrated;
85
- the config backing only needs the `ctx.secrets` context shown above.
85
+ the config backing only needs the `ctx.services.secrets` context shown above.
86
86
 
87
87
  ## Vs. the neighbouring recipes
88
88
 
@@ -15,6 +15,7 @@ import {
15
15
  createTextField,
16
16
  defineFeature,
17
17
  defineWriteHandler,
18
+ MAX_LIST_LIMIT,
18
19
  } from "@cosmicdrift/kumiko-framework/engine";
19
20
  import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
20
21
  import {
@@ -51,7 +52,7 @@ function toFileNameSegment(text: string): string {
51
52
  }
52
53
 
53
54
  // One row above the limit tells "exactly full" from "cut off"; entity lists cap at 200 rows.
54
- const AUDIT_ROW_LIMIT = 100;
55
+ const AUDIT_ROW_LIMIT = MAX_LIST_LIMIT - 1;
55
56
 
56
57
  const access = { access: { roles: ["Admin", "User"] } } as const;
57
58
 
@@ -5,7 +5,7 @@ Tenant-scoped full-text search (Meilisearch / in-memory adapter).
5
5
  ## What it shows
6
6
 
7
7
  - `searchable` / `searchWeight` on fields
8
- - Handler reads via `ctx.searchAdapter`
8
+ - Handler reads via `ctx.services.searchAdapter`
9
9
 
10
10
  ## Source
11
11
 
@@ -1,6 +1,6 @@
1
1
  // Search Sample
2
2
  // Shows: searchable fields, searchWeight, search via InMemory SearchAdapter.
3
- // Each handler builds its executor inline so `ctx.searchAdapter` is read fresh
3
+ // Each handler builds its executor inline so `ctx.services.searchAdapter` is read fresh
4
4
  // per call — tests swap the adapter between runs.
5
5
 
6
6
  import { buildEntityTable, createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
@@ -38,7 +38,7 @@ export const productFeature = defineFeature("shop", (r) => {
38
38
  }),
39
39
  handler: async (event, ctx) => {
40
40
  const crud = createEventStoreExecutor(productTable, productEntity, {
41
- searchAdapter: ctx.searchAdapter,
41
+ searchAdapter: ctx.services.searchAdapter,
42
42
  entityName: "product",
43
43
  });
44
44
  return crud.create(event.payload, event.user, ctx.db);
@@ -54,7 +54,7 @@ export const productFeature = defineFeature("shop", (r) => {
54
54
  }),
55
55
  handler: async (query, ctx) => {
56
56
  const crud = createEventStoreExecutor(productTable, productEntity, {
57
- searchAdapter: ctx.searchAdapter,
57
+ searchAdapter: ctx.services.searchAdapter,
58
58
  entityName: "product",
59
59
  });
60
60
  return crud.list(query.payload, query.user, ctx.db);
@@ -4,7 +4,7 @@ Tenant-owned secrets with envelope encryption, DEK cache, and KEK rotation.
4
4
 
5
5
  ## What it shows
6
6
 
7
- - `r.secret` + `ctx.secrets.get` (audited reads)
7
+ - `r.secret` + `ctx.services.secrets.get` (audited reads)
8
8
  - Charge-style handler that never returns the plaintext key
9
9
 
10
10
  ## Source
@@ -1,4 +1,4 @@
1
- // A miniature "billing" feature that reads a Stripe API key from ctx.secrets
1
+ // A miniature "billing" feature that reads a Stripe API key from ctx.services.secrets
2
2
  // and simulates a charge. The point is to show how feature code uses the
3
3
  // plaintext secret without ever exposing it to clients — the charge endpoint
4
4
  // returns { chargeId } but never the apiKey.
@@ -27,7 +27,10 @@ const chargeWrite = defineWriteHandler({
27
27
  }),
28
28
  access: { roles: ["TenantAdmin", "User"] },
29
29
  handler: async (event, ctx) => {
30
- const secrets = requireSecretsContext(ctx, "billing:write:charge");
30
+ const secrets = requireSecretsContext(
31
+ { secrets: ctx.services.secrets, user: ctx.user },
32
+ "billing:write:charge",
33
+ );
31
34
  // requireSecretsContext returned a wrapper that auto-fills the audit
32
35
  // context (userId + "billing:write:charge") on every .get — the read
33
36
  // lands in tenant_secret_reads without the handler having to remember.