@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
@@ -107,7 +107,12 @@ export async function seedSystemTemplate(
107
107
  ): Promise<{ id: string }> {
108
108
  const tenantId = SYSTEM_TENANT_ID;
109
109
  const by = opts.by ?? createSystemUser(tenantId);
110
- const tdb = createTenantDb({ db, tenantId, mode: "system" });
110
+ const tdb = createTenantDb({
111
+ db,
112
+ tenantId,
113
+ mode: "system",
114
+ reason: "system templates live in the system tenant",
115
+ });
111
116
 
112
117
  const label = `seedSystemTemplate ${opts.slug}/${opts.kind}/${opts.locale}`;
113
118
  const slug = templateSlugSchema.safeParse(opts.slug);
@@ -408,7 +413,12 @@ export async function seedTextBlock(
408
413
  opts: SeedTextBlockOptions,
409
414
  ): Promise<{ id: string }> {
410
415
  const by = opts.by ?? createSystemUser(opts.tenantId);
411
- const tdb = createTenantDb({ db, tenantId: opts.tenantId, mode: "system" });
416
+ const tdb = createTenantDb({
417
+ db,
418
+ tenantId: opts.tenantId,
419
+ mode: "system",
420
+ reason: "seeding writes text blocks into an explicitly named tenant",
421
+ });
412
422
 
413
423
  const existingRow = await fetchOne<TemplateResourceRow>(db, templateResourcesTable, {
414
424
  tenantId: opts.tenantId,
@@ -442,12 +452,14 @@ export async function seedTextBlock(
442
452
  row: { id: string; version: number },
443
453
  current: TemplateResourceRow | null | undefined,
444
454
  ): Promise<{ id: string }> => {
455
+ // No folder option keeps the row's folder, so a re-seed cannot drop it from the content tree.
456
+ const folder = opts.folder === undefined ? (current?.folder ?? null) : fields.folder;
445
457
  // Skip no-op updates so a legal re-seed on every pod boot does not bump
446
458
  // version/modifiedAt (ETag, "last changed", event-store growth).
447
459
  if (
448
460
  current?.title === fields.title &&
449
461
  current?.content === fields.content &&
450
- current?.folder === fields.folder
462
+ current?.folder === folder
451
463
  ) {
452
464
  return { id: String(row.id) };
453
465
  }
@@ -458,7 +470,7 @@ export async function seedTextBlock(
458
470
  changes: {
459
471
  title: fields.title,
460
472
  content: fields.content,
461
- folder: fields.folder,
473
+ folder,
462
474
  // A re-seed must not revive an archived block; only a served one gets a new served version.
463
475
  ...(current?.status === "active" && servedStampChanges(row)),
464
476
  },
@@ -533,7 +545,11 @@ export type LegalContentBlock = {
533
545
  export async function seedLegalContentFromJson(
534
546
  db: DbConnection,
535
547
  blocks: readonly LegalContentBlock[],
536
- opts: { readonly tenantId?: TenantId; readonly by?: SessionUser } = {},
548
+ opts: {
549
+ readonly tenantId?: TenantId;
550
+ readonly by?: SessionUser;
551
+ readonly folder?: string;
552
+ } = {},
537
553
  ): Promise<void> {
538
554
  const tenantId = opts.tenantId ?? SYSTEM_TENANT_ID;
539
555
  const by = opts.by ?? createSystemUser(tenantId);
@@ -544,6 +560,7 @@ export async function seedLegalContentFromJson(
544
560
  locale: block.locale,
545
561
  title: block.title,
546
562
  content: block.content,
563
+ ...(opts.folder !== undefined && { folder: opts.folder }),
547
564
  ifExists: "update",
548
565
  by,
549
566
  });
@@ -1,5 +1,6 @@
1
1
  import { fetchOne, selectMany } from "@cosmicdrift/kumiko-framework/db";
2
2
  import type {
3
+ BeforeTransactionContext,
3
4
  BeforeTransactionResult,
4
5
  ConfigAccessor,
5
6
  HandlerContext,
@@ -19,7 +20,7 @@ import {
19
20
  type UploadedFileBytes,
20
21
  uploadFileBytesBeforeTransaction,
21
22
  } from "@cosmicdrift/kumiko-framework/files";
22
- import { fileContextForTenant } from "../file-foundation/index.ts";
23
+ import { fileContextForTenant, readFileRefContent } from "../file-foundation/index.ts";
23
24
  import { extensionForResourceContentType, requireForTenant } from "../shared/index.ts";
24
25
  import {
25
26
  DEFAULT_RESOURCE_MAX_BYTES,
@@ -29,13 +30,13 @@ import {
29
30
  import { executor } from "./handlers/shared.ts";
30
31
  import {
31
32
  attachmentEntriesOf,
33
+ isFileAttachmentEntry,
32
34
  type LinkedResourceEntry,
33
35
  parseLinkedResourceEntries,
34
36
  templatePdfAttachmentLinkSchema,
35
37
  } from "./linked-resources.ts";
36
38
  import { normalizeResourceImage } from "./resource-image.ts";
37
39
  import { type TemplateResourceRow, templateResourcesTable } from "./table.ts";
38
- import { readTemplateResource } from "./template-resources.ts";
39
40
  import { ACTIVE_TEMPLATE_STATUS, SYSTEM_TENANT_ID } from "./web/constants.ts";
40
41
  import { TemplateResolverErrors } from "./web/qualified-names.ts";
41
42
 
@@ -100,7 +101,7 @@ function isAttachmentKey(links: Readonly<Record<string, LinkedResourceEntry>>, k
100
101
  // so both receive the target tenant's db and identity.
101
102
  function uploadToTenant(
102
103
  args: {
103
- readonly ctx: HandlerContext;
104
+ readonly ctx: PrepareContext;
104
105
  readonly user: SessionUser;
105
106
  readonly tenantId: TenantId;
106
107
  },
@@ -110,7 +111,7 @@ function uploadToTenant(
110
111
  return uploadFileBytesBeforeTransaction(
111
112
  {
112
113
  db: requireForTenant(ctx, tenantId).db,
113
- files: fileContextForTenant(ctx, ctx.user.tenantId, tenantId),
114
+ services: { files: fileContextForTenant(ctx, ctx.user.tenantId, tenantId) },
114
115
  registry: ctx.registry,
115
116
  user: { ...user, tenantId },
116
117
  },
@@ -119,7 +120,7 @@ function uploadToTenant(
119
120
  }
120
121
 
121
122
  type TenantScope = {
122
- readonly ctx: HandlerContext;
123
+ readonly ctx: PrepareContext;
123
124
  readonly user: SessionUser;
124
125
  readonly tenantId: TenantId;
125
126
  };
@@ -133,7 +134,7 @@ function createFileRefFromPrepared(scope: TenantScope, upload: UploadedFileBytes
133
134
  }
134
135
 
135
136
  async function linkImage(args: {
136
- readonly ctx: HandlerContext;
137
+ readonly ctx: PrepareContext;
137
138
  readonly user: SessionUser;
138
139
  readonly tenantId: TenantId;
139
140
  readonly row: TemplateResourceRow;
@@ -197,7 +198,7 @@ type ImageTarget = {
197
198
 
198
199
  // Runs before the upload (reject early, store nothing) and again in the transaction on the fresh row.
199
200
  async function checkImageTarget(args: {
200
- readonly ctx: HandlerContext;
201
+ readonly ctx: PrepareContext;
201
202
  readonly tenantId: TenantId;
202
203
  readonly templateId?: string | undefined;
203
204
  readonly key?: string | undefined;
@@ -220,7 +221,7 @@ async function checkImageTarget(args: {
220
221
  // beforeTransaction half: normalizes the image and uploads it to the target tenant's storage.
221
222
  // The caller is responsible for authorization; the function trusts tenantId.
222
223
  export async function prepareTemplateResourceBeforeTransaction(args: {
223
- readonly ctx: HandlerContext;
224
+ readonly ctx: PrepareContext;
224
225
  readonly user: SessionUser;
225
226
  readonly tenantId: TenantId;
226
227
  readonly bytes: Uint8Array;
@@ -260,6 +261,10 @@ export async function prepareTemplateResourceBeforeTransaction(args: {
260
261
  };
261
262
  }
262
263
 
264
+ // The prepare phase runs before the transaction, so it must not rely on anything the in-transaction
265
+ // context adds (events, dispatch.write).
266
+ export type PrepareContext = HandlerContext | BeforeTransactionContext;
267
+
263
268
  export type Release = () => Promise<void>;
264
269
 
265
270
  // Tries every release, so one failing delete does not strand the other uploads; the first rejection is rethrown.
@@ -271,13 +276,13 @@ export async function releaseAll(releases: readonly Release[]): Promise<void> {
271
276
 
272
277
  // Failure paths: a failing release must not replace the upload error the caller needs to see.
273
278
  export async function releaseAfterFailure(
274
- ctx: HandlerContext,
279
+ ctx: PrepareContext,
275
280
  releases: readonly Release[],
276
281
  ): Promise<void> {
277
282
  try {
278
283
  await releaseAll(releases);
279
284
  } catch (releaseError) {
280
- ctx.log?.warn("template upload rollback: releasing stored files failed", {
285
+ ctx.obs.log?.warn("template upload rollback: releasing stored files failed", {
281
286
  error: releaseError instanceof Error ? releaseError.message : String(releaseError),
282
287
  });
283
288
  }
@@ -290,12 +295,50 @@ export type PreparedLinkedResourceCopy = {
290
295
  readonly upload?: UploadedFileBytes;
291
296
  };
292
297
 
298
+ // The source row's own fileName is personal data and may be ciphertext, so the copy is named from
299
+ // the link entry or the key.
300
+ function copiedFileName(key: string, entry: LinkedResourceEntry, contentType: string): string {
301
+ if (typeof entry !== "string" && isFileAttachmentEntry(entry)) return entry.fileName;
302
+ return `${key}.${extensionForResourceContentType(contentType) ?? "bin"}`;
303
+ }
304
+
305
+ function sourceUnreadable(): WriteResult<never> {
306
+ return writeFailure(
307
+ new UnprocessableError(TemplateResolverErrors.resourceSourceUnreadable, {
308
+ i18nKey: "templateResolver.errors.resource.sourceUnreadable",
309
+ }),
310
+ );
311
+ }
312
+
313
+ // Undefined: the fileRef row exists but its bytes cannot be read, which must not look like a
314
+ // deleted file. The provider is chosen by the tenant of the fileRef, not by the caller's.
315
+ async function readCopySource(
316
+ ctx: PrepareContext,
317
+ fileRefId: string,
318
+ ): Promise<{ contentType: string; bytes: Uint8Array } | null | undefined> {
319
+ try {
320
+ const found = await readFileRefContent({
321
+ db: ctx.db,
322
+ filesForTenant: (tid) => fileContextForTenant(ctx, ctx.user.tenantId, tid),
323
+ fileRefId,
324
+ allowedTenantIds: [SYSTEM_TENANT_ID, ctx.user.tenantId],
325
+ });
326
+ return found === null ? null : { contentType: found.contentType, bytes: found.content };
327
+ } catch (cause) {
328
+ ctx.obs.log?.warn("template copy: source file could not be read", {
329
+ fileRefId,
330
+ errorClass: cause instanceof Error ? cause.name : typeof cause,
331
+ });
332
+ return undefined;
333
+ }
334
+ }
335
+
293
336
  // beforeTransaction half: copies the file behind every entry into the target tenant's storage, so a
294
337
  // template copied from the system default keeps working once the tenant exports it or measures its
295
338
  // attachments. An entry whose source file is gone is left out: it could not be resolved at render
296
339
  // time either. A failed or throwing upload releases the ones before it.
297
340
  export async function prepareLinkedResourceCopiesBeforeTransaction(args: {
298
- readonly ctx: HandlerContext;
341
+ readonly ctx: PrepareContext;
299
342
  readonly user: SessionUser;
300
343
  readonly tenantId: TenantId;
301
344
  readonly resources: Readonly<Record<string, LinkedResourceEntry>>;
@@ -309,14 +352,20 @@ export async function prepareLinkedResourceCopiesBeforeTransaction(args: {
309
352
  copies.push({ key, entry });
310
353
  continue;
311
354
  }
312
- const file = await readTemplateResource(
313
- ctx,
314
- typeof entry === "string" ? entry : entry.fileRefId,
315
- );
316
- if (file === null) continue;
355
+ const source = await readCopySource(ctx, typeof entry === "string" ? entry : entry.fileRefId);
356
+ if (source === undefined) {
357
+ await releaseAfterFailure(ctx, releases);
358
+ return sourceUnreadable();
359
+ }
360
+ // skip: the source fileRef is deleted or unknown, so there is nothing to copy
361
+ if (source === null) continue;
317
362
  const uploaded = await uploadToTenant(
318
363
  { ctx, user, tenantId },
319
- { fileName: file.fileName, mimeType: file.contentType, data: file.bytes },
364
+ {
365
+ fileName: copiedFileName(key, entry, source.contentType),
366
+ mimeType: source.contentType,
367
+ data: source.bytes,
368
+ },
320
369
  );
321
370
  if (!uploaded.isSuccess) {
322
371
  await releaseAfterFailure(ctx, releases);
@@ -481,7 +530,7 @@ type AttachmentTarget = {
481
530
 
482
531
  // Runs before the upload (reject early, store nothing) and again in the transaction on the fresh row.
483
532
  async function checkAttachmentTarget(args: {
484
- readonly ctx: HandlerContext;
533
+ readonly ctx: PrepareContext;
485
534
  readonly tenantId: TenantId;
486
535
  readonly templateId: string;
487
536
  readonly key: string;
@@ -506,7 +555,7 @@ async function checkAttachmentTarget(args: {
506
555
  // beforeTransaction half: validates a PDF/PNG/JPEG and uploads it unchanged to the target tenant's
507
556
  // storage. With templateId the template is checked first; a bundle import has none yet.
508
557
  export async function prepareTemplateAttachmentBeforeTransaction(args: {
509
- readonly ctx: HandlerContext;
558
+ readonly ctx: PrepareContext;
510
559
  readonly user: SessionUser;
511
560
  readonly tenantId: TenantId;
512
561
  readonly bytes: Uint8Array;
@@ -27,16 +27,16 @@ type FileRefRow = {
27
27
  // Reads the file behind a template resource. Null when the fileRef is unknown
28
28
  // to the caller's tenant or has no bytes.
29
29
  export async function readTemplateResource(
30
- ctx: Pick<HandlerContext, "db" | "files" | "user">,
30
+ ctx: Pick<HandlerContext, "db" | "services" | "user">,
31
31
  fileRefId: string,
32
32
  ): Promise<TemplateResourceFile | null> {
33
- if (!ctx.files) return null;
33
+ if (!ctx.services.files) return null;
34
34
  const row = await fetchOne<FileRefRow>(ctx.db, fileRefsTable, {
35
35
  id: fileRefId,
36
36
  isDeleted: false,
37
37
  });
38
38
  if (!row) return null;
39
- const handle = ctx.files.refFromFileRef(row);
39
+ const handle = ctx.services.files.refFromFileRef(row);
40
40
  if (!(await handle.exists())) return null;
41
41
  return { fileName: row.fileName, contentType: row.mimeType, bytes: await handle.read() };
42
42
  }
@@ -44,7 +44,7 @@ export async function readTemplateResource(
44
44
  // Replaces the whole key map of one row with files that are already stored (bundle import,
45
45
  // copy-locale). Single keys go through storePreparedTemplateResource and resource-remove.
46
46
  export async function attachTemplateResources(
47
- ctx: Pick<HandlerContext, "forTenant">,
47
+ ctx: Pick<HandlerContext, "tenants">,
48
48
  args: {
49
49
  readonly user: SessionUser;
50
50
  readonly tenantId: TenantId;
@@ -3,7 +3,7 @@
3
3
  // No bun:test here — callers register cases with their own test runner.
4
4
 
5
5
  import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
6
- import { seedRow } from "@cosmicdrift/kumiko-testing/helpers";
6
+ import { seedRowViaEventStore } from "@cosmicdrift/kumiko-testing/helpers";
7
7
  import type { ResolveRequest, TemplateResource } from "./api.ts";
8
8
  import { TemplateNotFoundError } from "./api.ts";
9
9
  import { templateResourcesTable } from "./table.ts";
@@ -50,7 +50,7 @@ type SeedTemplateArgs = {
50
50
  };
51
51
 
52
52
  async function seedTemplate(db: DbConnection, args: SeedTemplateArgs): Promise<void> {
53
- await seedRow(db, templateResourcesTable, {
53
+ await seedRowViaEventStore(db, templateResourcesTable, {
54
54
  tenantId: args.tenantId,
55
55
  slug: args.slug,
56
56
  kind: args.kind,
@@ -0,0 +1,222 @@
1
+ import { usePrimitives, VariableChips } from "@cosmicdrift/kumiko-renderer";
2
+ import { AlignCenter, AlignJustify, AlignLeft, AlignRight } from "lucide-react";
3
+ import type { ReactNode } from "react";
4
+ import { insertAndCloseOf, type PanelModel } from "./document-insert-model.ts";
5
+ import { DOCUMENT_TEXT_ALIGNMENTS, type DocumentTextAlignment } from "./document-schema.ts";
6
+ import { type PanelKind, variableLabelOf } from "./document-tiptap-helpers.ts";
7
+
8
+ const ALIGNMENT_ICONS: Record<DocumentTextAlignment, ReactNode> = {
9
+ left: <AlignLeft size={16} />,
10
+ center: <AlignCenter size={16} />,
11
+ right: <AlignRight size={16} />,
12
+ justify: <AlignJustify size={16} />,
13
+ };
14
+
15
+ const ALIGNMENT_LABEL_KEYS: Record<DocumentTextAlignment, string> = {
16
+ left: "template-resolver.documentEditor.alignLeft",
17
+ center: "template-resolver.documentEditor.alignCenter",
18
+ right: "template-resolver.documentEditor.alignRight",
19
+ justify: "template-resolver.documentEditor.alignJustify",
20
+ };
21
+
22
+ type OptionSelectProps = {
23
+ readonly id: string;
24
+ readonly label: string;
25
+ readonly names: readonly string[];
26
+ readonly labels: Readonly<Record<string, string>>;
27
+ readonly value: string;
28
+ readonly onChange: (name: string) => void;
29
+ };
30
+
31
+ function NameSelect({ id, label, names, labels, value, onChange }: OptionSelectProps): ReactNode {
32
+ const { Field, Input } = usePrimitives();
33
+ return (
34
+ <Field id={id} label={label}>
35
+ <Input
36
+ kind="select"
37
+ id={id}
38
+ name={id}
39
+ value={value}
40
+ onChange={onChange}
41
+ display="dropdown"
42
+ options={names.map((name) => ({ value: name, label: variableLabelOf(name, labels) }))}
43
+ />
44
+ </Field>
45
+ );
46
+ }
47
+
48
+ function renderLinkFields(m: PanelModel): ReactNode {
49
+ const { t, Field, Input } = m;
50
+ return (
51
+ <Field id={`${m.idPrefix}-href`} label={t("template-resolver.documentEditor.linkHref")}>
52
+ <Input
53
+ kind="text"
54
+ id={`${m.idPrefix}-href`}
55
+ name="href"
56
+ value={m.href}
57
+ onChange={m.setHref}
58
+ placeholder={t("template-resolver.documentEditor.linkHrefPlaceholder")}
59
+ />
60
+ </Field>
61
+ );
62
+ }
63
+
64
+ function renderPlaceholderFields(m: PanelModel): ReactNode {
65
+ return (
66
+ !m.noVariables && (
67
+ <div className="flex flex-wrap gap-1">
68
+ <VariableChips
69
+ variables={m.selectable}
70
+ labels={m.variableLabels}
71
+ onInsert={(name) => insertAndCloseOf(m)({ type: "variable", attrs: { name } })}
72
+ />
73
+ </div>
74
+ )
75
+ );
76
+ }
77
+
78
+ function renderAlignFields(m: PanelModel): ReactNode {
79
+ const { t, Button, editor } = m;
80
+ return (
81
+ <div className="flex flex-wrap gap-1">
82
+ {DOCUMENT_TEXT_ALIGNMENTS.map((alignment) => (
83
+ <Button
84
+ key={alignment}
85
+ type="button"
86
+ variant="secondary"
87
+ size="sm"
88
+ onClick={() => {
89
+ editor
90
+ .chain()
91
+ .focus()
92
+ .updateAttributes("paragraph", { textAlign: alignment })
93
+ .updateAttributes("heading", { textAlign: alignment })
94
+ .run();
95
+ m.onClose();
96
+ }}
97
+ >
98
+ {ALIGNMENT_ICONS[alignment]}
99
+ {t(ALIGNMENT_LABEL_KEYS[alignment])}
100
+ </Button>
101
+ ))}
102
+ </div>
103
+ );
104
+ }
105
+
106
+ function renderImageFields(m: PanelModel): ReactNode {
107
+ const { t, Field, Input, idPrefix } = m;
108
+ return (
109
+ <>
110
+ <Field id={`${idPrefix}-resource`} label={t("template-resolver.documentEditor.resourceKey")}>
111
+ <Input
112
+ kind="text"
113
+ id={`${idPrefix}-resource`}
114
+ name="resource"
115
+ value={m.resource}
116
+ onChange={m.setResource}
117
+ />
118
+ </Field>
119
+ <Field id={`${idPrefix}-alt`} label={t("template-resolver.documentEditor.imageAlt")}>
120
+ <Input kind="text" id={`${idPrefix}-alt`} name="alt" value={m.alt} onChange={m.setAlt} />
121
+ </Field>
122
+ </>
123
+ );
124
+ }
125
+
126
+ function renderButtonFields(m: PanelModel): ReactNode {
127
+ const { t, Field, Input, idPrefix } = m;
128
+ return (
129
+ !m.noVariables && (
130
+ <>
131
+ <NameSelect
132
+ id={`${idPrefix}-target`}
133
+ label={t("template-resolver.documentEditor.buttonTarget")}
134
+ names={m.selectable}
135
+ labels={m.variableLabels}
136
+ value={m.selectedName}
137
+ onChange={m.setChosenName}
138
+ />
139
+ <Field id={`${idPrefix}-text`} label={t("template-resolver.documentEditor.buttonText")}>
140
+ <Input
141
+ kind="text"
142
+ id={`${idPrefix}-text`}
143
+ name="text"
144
+ value={m.buttonText}
145
+ onChange={m.setButtonText}
146
+ />
147
+ </Field>
148
+ </>
149
+ )
150
+ );
151
+ }
152
+
153
+ function renderRepeatFields(m: PanelModel): ReactNode {
154
+ return (
155
+ !m.noVariables && (
156
+ <NameSelect
157
+ id={`${m.idPrefix}-list`}
158
+ label={m.t("template-resolver.documentEditor.repeatList")}
159
+ names={m.selectable}
160
+ labels={m.variableLabels}
161
+ value={m.selectedName}
162
+ onChange={m.setChosenName}
163
+ />
164
+ )
165
+ );
166
+ }
167
+
168
+ function renderImagesFields(m: PanelModel): ReactNode {
169
+ return (
170
+ !m.noVariables && (
171
+ <NameSelect
172
+ id={`${m.idPrefix}-images-variable`}
173
+ label={m.t("template-resolver.documentEditor.imagesVariable")}
174
+ names={m.selectable}
175
+ labels={m.variableLabels}
176
+ value={m.selectedName}
177
+ onChange={m.setChosenName}
178
+ />
179
+ )
180
+ );
181
+ }
182
+
183
+ function renderStepsFields(m: PanelModel): ReactNode {
184
+ const { t, Field, Input, idPrefix } = m;
185
+ return (
186
+ !m.noVariables && (
187
+ <>
188
+ <NameSelect
189
+ id={`${idPrefix}-steps-variable`}
190
+ label={t("template-resolver.documentEditor.stepsVariable")}
191
+ names={m.selectable}
192
+ labels={m.variableLabels}
193
+ value={m.selectedName}
194
+ onChange={m.setChosenName}
195
+ />
196
+ <Field
197
+ id={`${idPrefix}-steps-list`}
198
+ label={t("template-resolver.documentEditor.stepsList")}
199
+ >
200
+ <Input
201
+ kind="text"
202
+ id={`${idPrefix}-steps-list`}
203
+ name="steps"
204
+ value={m.stepsText}
205
+ onChange={m.setStepsText}
206
+ />
207
+ </Field>
208
+ </>
209
+ )
210
+ );
211
+ }
212
+
213
+ export const PANEL_FIELDS: Readonly<Record<PanelKind, (m: PanelModel) => ReactNode>> = {
214
+ link: renderLinkFields,
215
+ placeholder: renderPlaceholderFields,
216
+ align: renderAlignFields,
217
+ image: renderImageFields,
218
+ button: renderButtonFields,
219
+ repeat: renderRepeatFields,
220
+ images: renderImagesFields,
221
+ steps: renderStepsFields,
222
+ };