@cosmicdrift/kumiko-samples 0.362.0 → 0.364.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 (321) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +23 -6
  3. package/packages/bundled-features/src/admin-shell/changes.json +6 -0
  4. package/packages/bundled-features/src/admin-shell/feature.ts +8 -1
  5. package/packages/bundled-features/src/agent-tools/agent-manifest.ts +3 -4
  6. package/packages/bundled-features/src/agent-tools/filter-ops.ts +1 -3
  7. package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -3
  8. package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +2 -1
  9. package/packages/bundled-features/src/audit/changes.json +6 -0
  10. package/packages/bundled-features/src/audit/handlers/list.query.ts +10 -5
  11. package/packages/bundled-features/src/auth-email-password/bootstrap.ts +29 -8
  12. package/packages/bundled-features/src/auth-email-password/changes.json +51 -0
  13. package/packages/bundled-features/src/auth-email-password/email-rate-limits.ts +16 -0
  14. package/packages/bundled-features/src/auth-email-password/email-templates.ts +12 -138
  15. package/packages/bundled-features/src/auth-email-password/feature.ts +30 -9
  16. package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +6 -2
  17. package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -5
  18. package/packages/bundled-features/src/auth-email-password/handlers/logout.write.ts +7 -2
  19. package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +2 -5
  20. package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +2 -5
  21. package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +2 -5
  22. package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +3 -4
  23. package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +2 -1
  24. package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +5 -13
  25. package/packages/bundled-features/src/auth-email-password/index.ts +2 -17
  26. package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +30 -22
  27. package/packages/bundled-features/src/auth-email-password/mail-notifications.ts +158 -0
  28. package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -3
  29. package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +24 -5
  30. package/packages/bundled-features/src/auth-foundation/types.ts +8 -18
  31. package/packages/bundled-features/src/auth-mfa/feature.ts +8 -10
  32. package/packages/bundled-features/src/auth-mfa/hmac-token-codec.ts +4 -4
  33. package/packages/bundled-features/src/auth-mfa/mfa-challenge-token.ts +3 -3
  34. package/packages/bundled-features/src/auth-mfa/mfa-preauth-setup-token.ts +3 -3
  35. package/packages/bundled-features/src/auth-mfa/mfa-setup-token.ts +8 -8
  36. package/packages/bundled-features/src/billing-foundation/changes.json +43 -0
  37. package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
  38. package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +90 -44
  39. package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +22 -1
  40. package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +3 -3
  41. package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +118 -0
  42. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
  43. package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +11 -1
  44. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +39 -33
  45. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +2 -2
  46. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +1 -4
  47. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +41 -0
  48. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +10 -1
  49. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +36 -20
  50. package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +9 -0
  51. package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +35 -0
  52. package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
  53. package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +11 -3
  54. package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +4 -9
  55. package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
  56. package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +12 -6
  57. package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
  58. package/packages/bundled-features/src/billing-foundation/i18n.ts +3 -0
  59. package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +20 -11
  60. package/packages/bundled-features/src/billing-foundation/types.ts +7 -2
  61. package/packages/bundled-features/src/cap-counter/calendar-cap-usage-reservation.ts +2 -0
  62. package/packages/bundled-features/src/cap-counter/changes.json +6 -0
  63. package/packages/bundled-features/src/cap-counter/enforce-cap.ts +75 -5
  64. package/packages/bundled-features/src/cap-counter/index.ts +1 -0
  65. package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -0
  66. package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
  67. package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +6 -4
  68. package/packages/bundled-features/src/channel-email/changes.json +19 -0
  69. package/packages/bundled-features/src/channel-email/email-channel.ts +135 -29
  70. package/packages/bundled-features/src/channel-email/feature.ts +6 -3
  71. package/packages/bundled-features/src/channel-email/index.ts +7 -1
  72. package/packages/bundled-features/src/channel-email/pii-guard.ts +13 -0
  73. package/packages/bundled-features/src/channel-email/smtp-transport.ts +15 -1
  74. package/packages/bundled-features/src/channel-email/types.ts +23 -0
  75. package/packages/bundled-features/src/channel-in-app/handlers/mark-read.write.ts +2 -2
  76. package/packages/bundled-features/src/config/changes.json +6 -0
  77. package/packages/bundled-features/src/config/handlers/set.write.ts +3 -7
  78. package/packages/bundled-features/src/config/resolver.ts +15 -12
  79. package/packages/bundled-features/src/config/write-helpers.ts +23 -0
  80. package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
  81. package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +48 -27
  82. package/packages/bundled-features/src/custom-fields/changes.json +6 -0
  83. package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
  84. package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +0 -1
  85. package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -11
  86. package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +7 -6
  87. package/packages/bundled-features/src/data-retention/changes.json +6 -0
  88. package/packages/bundled-features/src/data-retention/index.ts +1 -0
  89. package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +8 -6
  90. package/packages/bundled-features/src/delivery/attachment-ref.ts +105 -0
  91. package/packages/bundled-features/src/delivery/changes.json +66 -0
  92. package/packages/bundled-features/src/delivery/channel-context.ts +152 -4
  93. package/packages/bundled-features/src/delivery/constants.ts +2 -0
  94. package/packages/bundled-features/src/delivery/delivery-service.ts +104 -26
  95. package/packages/bundled-features/src/delivery/feature.ts +1 -1
  96. package/packages/bundled-features/src/delivery/index.ts +19 -2
  97. package/packages/bundled-features/src/delivery/jobs.ts +97 -17
  98. package/packages/bundled-features/src/delivery/notify-factory.ts +14 -9
  99. package/packages/bundled-features/src/delivery/testing.ts +9 -1
  100. package/packages/bundled-features/src/delivery/types.ts +34 -18
  101. package/packages/bundled-features/src/delivery/unsubscribe.ts +5 -2
  102. package/packages/bundled-features/src/derivatives-sharp/render.ts +1 -22
  103. package/packages/bundled-features/src/document-ingest-foundation/pages.ts +6 -3
  104. package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +4 -4
  105. package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
  106. package/packages/bundled-features/src/file-foundation/changes.json +6 -0
  107. package/packages/bundled-features/src/file-foundation/index.ts +7 -0
  108. package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +89 -0
  109. package/packages/bundled-features/src/files-tenant-data/changes.json +6 -0
  110. package/packages/bundled-features/src/files-tenant-data/hooks.ts +4 -0
  111. package/packages/bundled-features/src/folders/web/folder-manager.tsx +5 -2
  112. package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +11 -11
  113. package/packages/bundled-features/src/form-draft/db/queries/draft-count.ts +2 -2
  114. package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +2 -2
  115. package/packages/bundled-features/src/form-draft/release-file-refs.ts +3 -2
  116. package/packages/bundled-features/src/inbound-mail-foundation/changes.json +6 -0
  117. package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
  118. package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +6 -6
  119. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
  120. package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
  121. package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +2 -2
  122. package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +13 -8
  123. package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +7 -7
  124. package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +2 -6
  125. package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +5 -13
  126. package/packages/bundled-features/src/inbound-mail-foundation/types.ts +4 -6
  127. package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +42 -25
  128. package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
  129. package/packages/bundled-features/src/jobs/changes.json +19 -0
  130. package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +12 -4
  131. package/packages/bundled-features/src/jobs/feature.ts +7 -4
  132. package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -1
  133. package/packages/bundled-features/src/jobs/handlers/list.query.ts +3 -1
  134. package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +5 -2
  135. package/packages/bundled-features/src/jobs/job-run-logger.ts +52 -17
  136. package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +7 -2
  137. package/packages/bundled-features/src/ledger/changes.json +7 -0
  138. package/packages/bundled-features/src/ledger/entity.ts +3 -1
  139. package/packages/bundled-features/src/ledger/recurring.ts +1 -3
  140. package/packages/bundled-features/src/managed-pages/branding.ts +33 -9
  141. package/packages/bundled-features/src/managed-pages/changes.json +6 -0
  142. package/packages/bundled-features/src/managed-pages/feature.ts +1 -0
  143. package/packages/bundled-features/src/managed-pages/i18n.ts +1 -0
  144. package/packages/bundled-features/src/managed-pages/screens/branding-screen.ts +8 -0
  145. package/packages/bundled-features/src/page-render/branding.ts +3 -0
  146. package/packages/bundled-features/src/page-render/markdown.ts +1 -1
  147. package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -4
  148. package/packages/bundled-features/src/personal-access-tokens/handlers/list.query.ts +2 -1
  149. package/packages/bundled-features/src/personal-access-tokens/index.ts +1 -1
  150. package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +2 -1
  151. package/packages/bundled-features/src/renderer-foundation/README.md +11 -10
  152. package/packages/bundled-features/src/renderer-foundation/api.ts +14 -14
  153. package/packages/bundled-features/src/renderer-foundation/changes.json +39 -0
  154. package/packages/bundled-features/src/renderer-foundation/constants.ts +6 -8
  155. package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
  156. package/packages/bundled-features/src/renderer-foundation/index.ts +13 -2
  157. package/packages/bundled-features/src/renderer-foundation/types.ts +134 -35
  158. package/packages/bundled-features/src/renderer-pdf/changes.json +12 -0
  159. package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
  160. package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +162 -82
  161. package/packages/bundled-features/src/secrets/changes.json +6 -0
  162. package/packages/bundled-features/src/secrets/feature.ts +4 -3
  163. package/packages/bundled-features/src/secrets/secrets-context.ts +23 -3
  164. package/packages/bundled-features/src/sessions/db/queries/cleanup.ts +2 -2
  165. package/packages/bundled-features/src/sessions/feature.ts +2 -4
  166. package/packages/bundled-features/src/sessions/handlers/list.query.ts +2 -1
  167. package/packages/bundled-features/src/shared/attachment-file-name.ts +39 -0
  168. package/packages/bundled-features/src/shared/changes.json +6 -0
  169. package/packages/bundled-features/src/shared/chunked-entity-migration.ts +4 -1
  170. package/packages/bundled-features/src/shared/index.ts +6 -0
  171. package/packages/bundled-features/src/shared/is-tenant-db.ts +1 -1
  172. package/packages/bundled-features/src/shared/mail-template-body.ts +42 -0
  173. package/packages/bundled-features/src/shared/sharp-input-limits.ts +26 -0
  174. package/packages/bundled-features/src/shared/signup-handover.ts +4 -4
  175. package/packages/bundled-features/src/subscription-stripe/changes.json +6 -0
  176. package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +19 -11
  177. package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +3 -1
  178. package/packages/bundled-features/src/tags/handlers/assign-tag.write.ts +5 -1
  179. package/packages/bundled-features/src/tags/web/tag-chip.tsx +2 -1
  180. package/packages/bundled-features/src/template-resolver/README.md +66 -8
  181. package/packages/bundled-features/src/template-resolver/api.ts +85 -9
  182. package/packages/bundled-features/src/template-resolver/changes.json +186 -0
  183. package/packages/bundled-features/src/template-resolver/compile-document-body.ts +278 -0
  184. package/packages/bundled-features/src/template-resolver/constants.ts +49 -11
  185. package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
  186. package/packages/bundled-features/src/template-resolver/document-schema.ts +345 -0
  187. package/packages/bundled-features/src/template-resolver/feature.ts +59 -6
  188. package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +188 -0
  189. package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +30 -2
  190. package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +160 -0
  191. package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +33 -0
  192. package/packages/bundled-features/src/template-resolver/handlers/discard-draft.write.ts +52 -0
  193. package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +138 -0
  194. package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +9 -6
  195. package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +42 -0
  196. package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +22 -0
  197. package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +151 -0
  198. package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -18
  199. package/packages/bundled-features/src/template-resolver/handlers/override-diff.ts +49 -0
  200. package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +323 -0
  201. package/packages/bundled-features/src/template-resolver/handlers/preview.query.ts +25 -0
  202. package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +58 -0
  203. package/packages/bundled-features/src/template-resolver/handlers/resource-link-pdf.write.ts +34 -0
  204. package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +50 -0
  205. package/packages/bundled-features/src/template-resolver/handlers/resource-shared.ts +24 -0
  206. package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +135 -0
  207. package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +135 -0
  208. package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +32 -3
  209. package/packages/bundled-features/src/template-resolver/handlers/shared.ts +7 -2
  210. package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +196 -0
  211. package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +233 -0
  212. package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +137 -0
  213. package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +91 -17
  214. package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +19 -71
  215. package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +16 -77
  216. package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +31 -0
  217. package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
  218. package/packages/bundled-features/src/template-resolver/index.ts +111 -0
  219. package/packages/bundled-features/src/template-resolver/layout-appearance.ts +40 -0
  220. package/packages/bundled-features/src/template-resolver/layout-regions.ts +206 -0
  221. package/packages/bundled-features/src/template-resolver/linked-resources.ts +102 -0
  222. package/packages/bundled-features/src/template-resolver/pdf-constants.ts +12 -0
  223. package/packages/bundled-features/src/template-resolver/preview-display-html.ts +36 -0
  224. package/packages/bundled-features/src/template-resolver/print-document.ts +58 -0
  225. package/packages/bundled-features/src/template-resolver/qualified-names.ts +34 -0
  226. package/packages/bundled-features/src/template-resolver/record-mapping.ts +26 -0
  227. package/packages/bundled-features/src/template-resolver/resource-image.ts +47 -0
  228. package/packages/bundled-features/src/template-resolver/seeding.ts +171 -20
  229. package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
  230. package/packages/bundled-features/src/template-resolver/table.ts +27 -1
  231. package/packages/bundled-features/src/template-resolver/template-bundle.ts +79 -0
  232. package/packages/bundled-features/src/template-resolver/template-diff.ts +78 -0
  233. package/packages/bundled-features/src/template-resolver/template-draft.ts +106 -0
  234. package/packages/bundled-features/src/template-resolver/template-meta.ts +192 -0
  235. package/packages/bundled-features/src/template-resolver/template-preview.ts +676 -0
  236. package/packages/bundled-features/src/template-resolver/template-resources.ts +114 -0
  237. package/packages/bundled-features/src/template-resolver/template-usage.ts +68 -0
  238. package/packages/bundled-features/src/template-resolver/template-validation.ts +230 -0
  239. package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
  240. package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +20 -13
  241. package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +215 -0
  242. package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +37 -0
  243. package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +1065 -0
  244. package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +72 -0
  245. package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
  246. package/packages/bundled-features/src/template-resolver/web/i18n.ts +121 -0
  247. package/packages/bundled-features/src/template-resolver/web/index.ts +26 -0
  248. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor-lazy.tsx +36 -0
  249. package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +176 -0
  250. package/packages/bundled-features/src/template-resolver/web/placeholder-text.ts +37 -0
  251. package/packages/bundled-features/src/template-resolver/web/use-stable-by-key.ts +14 -0
  252. package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -2
  253. package/packages/bundled-features/src/tenant/seeding.ts +41 -7
  254. package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
  255. package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -1
  256. package/packages/bundled-features/src/tier-engine/changes.json +6 -0
  257. package/packages/bundled-features/src/tier-engine/feature.ts +11 -3
  258. package/packages/bundled-features/src/user/changes.json +6 -0
  259. package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +4 -4
  260. package/packages/bundled-features/src/user/handlers/detail.query.ts +3 -5
  261. package/packages/bundled-features/src/user/resolve-display-names.ts +8 -15
  262. package/packages/bundled-features/src/user/roles-input-schema.ts +2 -2
  263. package/packages/bundled-features/src/user/seeding.ts +19 -5
  264. package/packages/bundled-features/src/user-data-rights/changes.json +13 -0
  265. package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +2 -2
  266. package/packages/bundled-features/src/user-data-rights/email-templates.ts +5 -149
  267. package/packages/bundled-features/src/user-data-rights/feature.ts +72 -35
  268. package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +2 -1
  269. package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +5 -11
  270. package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +134 -53
  271. package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +2 -2
  272. package/packages/bundled-features/src/user-data-rights/mail-notifications.ts +157 -0
  273. package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +1 -1
  274. package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +2 -1
  275. package/packages/bundled-features/src/user-profile/changes.json +1 -1
  276. package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +2 -1
  277. package/packages/bundled-features/src/waitlist/changes.json +7 -0
  278. package/packages/bundled-features/src/waitlist/feature.ts +17 -0
  279. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +4 -5
  280. package/packages/bundled-features/src/waitlist/mail.ts +69 -18
  281. package/packages/bundled-features/src/waitlist/options.ts +1 -1
  282. package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
  283. package/packages/bundled-features/src/workflow-runner/event-trigger.ts +3 -3
  284. package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +7 -1
  285. package/samples/apps/ui-walkthrough/package.json +11 -0
  286. package/samples/apps/ui-walkthrough/playwright.config.ts +40 -7
  287. package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +9 -0
  288. package/samples/apps/ui-walkthrough/src/app/client.tsx +6 -0
  289. package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +239 -0
  290. package/samples/apps/ui-walkthrough/src/app/server.ts +5 -1
  291. package/samples/apps/ui-walkthrough/src/app/shell.tsx +12 -1
  292. package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +570 -0
  293. package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +72 -0
  294. package/samples/apps/ui-walkthrough/src/features/billing/i18n.ts +18 -0
  295. package/samples/apps/ui-walkthrough/src/features/billing/index.ts +2 -0
  296. package/samples/apps/ui-walkthrough/src/features/billing/web/index.ts +7 -0
  297. package/samples/apps/ui-walkthrough/src/run-config.ts +41 -1
  298. package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
  299. package/samples/apps/use-all-bundled/feature-manifest.json +118 -21
  300. package/samples/apps/use-all-bundled/kumiko/migrations/0045_template-resource-meta.rebuild.json +4 -0
  301. package/samples/apps/use-all-bundled/kumiko/migrations/0046_pdf-rendered.rebuild.json +4 -0
  302. package/samples/apps/use-all-bundled/package.json +2 -0
  303. package/samples/apps/use-all-bundled/playwright.config.ts +38 -3
  304. package/samples/apps/use-all-bundled/schema/generate.ts +13 -3
  305. package/samples/apps/use-all-bundled/schema/schema.generated.ts +3 -0
  306. package/samples/apps/use-all-bundled/scripts/check-coverage.ts +3 -0
  307. package/samples/apps/use-all-bundled/src/app/auth-constants.ts +3 -0
  308. package/samples/apps/use-all-bundled/src/app/client.tsx +4 -0
  309. package/samples/apps/use-all-bundled/src/app/seed-documents.ts +128 -0
  310. package/samples/apps/use-all-bundled/src/app/seed.ts +4 -0
  311. package/samples/apps/use-all-bundled/src/run-config.ts +11 -11
  312. package/samples/recipes/delivery-notifications/README.md +26 -25
  313. package/samples/recipes/delivery-notifications/package.json +2 -1
  314. package/samples/recipes/delivery-notifications/src/feature.ts +64 -58
  315. package/samples/recipes/seed-migration/README.md +1 -1
  316. package/packages/bundled-features/src/renderer-simple/changes.json +0 -27
  317. package/packages/bundled-features/src/renderer-simple/feature.ts +0 -66
  318. package/packages/bundled-features/src/renderer-simple/index.ts +0 -8
  319. package/packages/bundled-features/src/renderer-simple/mail-blocks.ts +0 -138
  320. package/packages/bundled-features/src/renderer-simple/resolve-variables.ts +0 -57
  321. package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +0 -304
@@ -0,0 +1,72 @@
1
+ // @runtime client
2
+ import type { ContentEditorProps } from "@cosmicdrift/kumiko-renderer";
3
+ import type { ReactNode } from "react";
4
+
5
+ export type DocumentToolbarBlock =
6
+ | "image"
7
+ | "table"
8
+ | "button"
9
+ | "divider"
10
+ | "repeat"
11
+ | "pageBreak"
12
+ | "align";
13
+
14
+ /** Imperative access for an outside panel (placeholder list, resource list). */
15
+ export type DocumentEditorHandle = {
16
+ readonly insertPlaceholder: (name: string) => void;
17
+ readonly insertResource: (key: string, alt?: string) => void;
18
+ readonly focus: () => void;
19
+ };
20
+
21
+ export type DocumentResourcePreview = {
22
+ /** Shown as the image source in the editor only; never written to the document. */
23
+ readonly src: string;
24
+ readonly caption?: string;
25
+ };
26
+
27
+ /** "default" keeps the compact bordered editor; "canvas" renders a flat text
28
+ * toolbar and a paper-like page with block labels. */
29
+ export type DocumentEditorChrome = "default" | "canvas";
30
+
31
+ export type DocumentEditorProps = ContentEditorProps & {
32
+ readonly blocks?: readonly DocumentToolbarBlock[];
33
+ /** Display label per variable name, as inserted (list item fields use the
34
+ * dotted form, e.g. "lines.description"). A chip shows label and name. */
35
+ readonly variableLabels?: Readonly<Record<string, string>>;
36
+ /** Variable names rendered as system chips (a different tint). */
37
+ readonly systemVariables?: readonly string[];
38
+ /** Image previews by resource key; resources without one show a key placeholder. */
39
+ readonly resourcePreviews?: Readonly<Record<string, DocumentResourcePreview>>;
40
+ readonly chrome?: DocumentEditorChrome;
41
+ /** "compact" renders the placeholder and block buttons icon-only with a tooltip, for narrow columns. Default "full". */
42
+ readonly toolbar?: "full" | "compact";
43
+ /** Canvas only: wraps the editable area in a caller-owned page (letterhead, margins) instead of the default mail paper. */
44
+ readonly renderPaper?: (editable: ReactNode) => ReactNode;
45
+ /** Canvas only: renders the toolbar (and its insert panel) into this element instead of above the editor;
46
+ * null renders none. Lets several region editors share one toolbar that follows the focused one. */
47
+ readonly toolbarHost?: HTMLElement | null;
48
+ /** Called with the handle once the editor exists and with null when it is destroyed. */
49
+ readonly onEditorReady?: (handle: DocumentEditorHandle) => void;
50
+ };
51
+
52
+ export const MAIL_DOCUMENT_BLOCKS = [
53
+ "image",
54
+ "table",
55
+ "button",
56
+ "divider",
57
+ "repeat",
58
+ ] as const satisfies readonly DocumentToolbarBlock[];
59
+
60
+ export const PDF_DOCUMENT_BLOCKS = [
61
+ "align",
62
+ "image",
63
+ "table",
64
+ "divider",
65
+ "repeat",
66
+ "pageBreak",
67
+ ] as const satisfies readonly DocumentToolbarBlock[];
68
+
69
+ /** Block set for a template-resource kind; every kind except "document-pdf" gets the mail set. */
70
+ export function documentBlocksForKind(kind: string | undefined): readonly DocumentToolbarBlock[] {
71
+ return kind === "document-pdf" ? PDF_DOCUMENT_BLOCKS : MAIL_DOCUMENT_BLOCKS;
72
+ }
@@ -0,0 +1,26 @@
1
+ // @runtime client
2
+ import type { Editor } from "@tiptap/core";
3
+ import { isDocumentReferenceName, isDocumentResourceKey } from "../document-schema.js";
4
+ import type { DocumentEditorHandle } from "./document-toolbar-blocks.js";
5
+
6
+ export function createEditorHandle(editor: Editor): DocumentEditorHandle {
7
+ return {
8
+ insertPlaceholder: (name) => {
9
+ // skip: an invalid variable name is not insertable
10
+ if (!isDocumentReferenceName(name)) return;
11
+ editor.chain().focus().insertContent({ type: "variable", attrs: { name } }).run();
12
+ },
13
+ insertResource: (key, alt) => {
14
+ // skip: an invalid resource key is not insertable
15
+ if (!isDocumentResourceKey(key)) return;
16
+ editor
17
+ .chain()
18
+ .focus()
19
+ .insertContent({ type: "resource", attrs: { resource: key, alt: alt ?? null } })
20
+ .run();
21
+ },
22
+ focus: () => {
23
+ editor.commands.focus();
24
+ },
25
+ };
26
+ }
@@ -9,12 +9,133 @@ import type { TranslationsByLocale } from "@cosmicdrift/kumiko-renderer";
9
9
 
10
10
  export const defaultTranslations: TranslationsByLocale = {
11
11
  en: {
12
+ "template-resolver.documentEditor.button": "Button",
13
+ "template-resolver.documentEditor.buttonDefaultText": "Confirm",
14
+ "template-resolver.documentEditor.buttonTarget": "Button target",
15
+ "template-resolver.documentEditor.buttonText": "Button text",
16
+ "template-resolver.documentEditor.cancel": "Cancel",
17
+ "template-resolver.documentEditor.divider": "Divider",
18
+ "template-resolver.documentEditor.heading3": "Heading 3",
19
+ "template-resolver.documentEditor.image": "Image",
20
+ "template-resolver.documentEditor.imageBlock": "Image · inline",
21
+ "template-resolver.documentEditor.imageAlt": "Alt text (optional)",
22
+ "template-resolver.documentEditor.insert": "Insert",
23
+ "template-resolver.documentEditor.insertPlaceholder": "Placeholder",
24
+ "template-resolver.documentEditor.invalidImageAlt": "The alt text must not contain < or >.",
25
+ "template-resolver.documentEditor.invalidLink":
26
+ "This link target is not allowed. Use https, http, mailto, tel or a placeholder.",
27
+ "template-resolver.documentEditor.invalidResourceKey": "This resource key is not valid.",
28
+ "template-resolver.documentEditor.link": "Link",
29
+ "template-resolver.documentEditor.linkHref": "Link target",
30
+ "template-resolver.documentEditor.linkHrefPlaceholder": "https://",
31
+ "template-resolver.documentEditor.noVariables": "No matching variables are available here.",
32
+ "template-resolver.documentEditor.pageBreak": "Page break",
33
+ "template-resolver.documentEditor.align": "Align",
34
+ "template-resolver.documentEditor.alignLeft": "Left",
35
+ "template-resolver.documentEditor.alignCenter": "Centered",
36
+ "template-resolver.documentEditor.alignRight": "Right",
37
+ "template-resolver.documentEditor.alignJustify": "Justified",
38
+ "template-resolver.documentEditor.repeatBarList": "Repeat · {list}",
39
+ "template-resolver.documentEditor.repeatHeader": "Repeat header row",
40
+ "template-resolver.documentEditor.paragraph": "Paragraph",
41
+ "template-resolver.documentEditor.paragraphStyle": "Paragraph style",
42
+ "template-resolver.documentEditor.placeholder": "Write your text…",
43
+ "template-resolver.documentEditor.removeLink": "Remove link",
44
+ "template-resolver.documentEditor.repeat": "Repeat",
45
+ "template-resolver.documentEditor.repeatList": "List to repeat",
46
+ "template-resolver.documentEditor.resourceKey": "Resource key",
47
+ "template-resolver.documentEditor.table": "Table",
12
48
  "template-resolver.editor.titleLabel": "Title",
13
49
  "template-resolver.editor.contentLabel": "Content",
14
50
  "template-resolver.editor.save": "Save",
15
51
  "template-resolver.editor.saving": "Saving…",
16
52
  "template-resolver.editor.created": "Created.",
17
53
  "template-resolver.editor.saved": "Saved.",
54
+ "templateResolver.errors.document.buttonHrefNotPlaceholder":
55
+ "The button target “{detail}” must be a placeholder.",
56
+ "templateResolver.errors.document.disallowedLinkHref":
57
+ "Link target “{detail}” is not allowed; use https, http, mailto, tel or a placeholder.",
58
+ "templateResolver.errors.document.documentRequiresHtmlFormat":
59
+ "This template kind requires the html format, got “{detail}”.",
60
+ "templateResolver.errors.document.layoutRequiresMjmlFormat":
61
+ "Mail layouts require the mjml format, got “{detail}”.",
62
+ "templateResolver.errors.document.layoutInvalidStructure":
63
+ "The layout may only contain the header, content, signature and footer regions: {detail}.",
64
+ "templateResolver.errors.document.layoutMissingContentSlot":
65
+ "The layout needs its content slot: {detail}.",
66
+ "templateResolver.errors.document.layoutSourceSystemOnly":
67
+ "Only system layouts may use {detail} source; use the structured html layout.",
68
+ "templateResolver.errors.template.targetExists":
69
+ "A template for locale “{detail}” already exists; overwrite it explicitly to replace it.",
70
+ "templateResolver.warnings.resourceNotUploaded": "Image not uploaded: {detail}",
71
+ "templateResolver.checks.resourcesResolved": "All images are available",
72
+ "templateResolver.checks.textPresent": "A plain-text version is present",
73
+ "templateResolver.checks.sizeUnderCap": "The mail is within the 10 MiB size limit",
74
+ "templateResolver.checks.requiredPlaceholders": "All required placeholders are used",
75
+ "templateResolver.errors.preview.renderFailed": "The preview could not be rendered.",
76
+ "templateResolver.errors.testMail.noChannel":
77
+ "No email channel is set up, so no test mail can be sent.",
78
+ "templateResolver.errors.testMail.noAddress":
79
+ "Your account has no email address to send the test mail to.",
80
+ "templateResolver.errors.testMail.failed": "The test mail could not be sent.",
81
+ "templateResolver.errors.resource.tooLarge": "The image is too large.",
82
+ "templateResolver.errors.resource.unsupportedImage":
83
+ "This file is not a supported image. Use PNG, JPEG, WebP or GIF.",
84
+ "templateResolver.errors.template.systemResourcesNeedSystemTenant":
85
+ "Resource files of system templates can only be imported or exported by a user of the system tenant.",
86
+ "templateResolver.errors.template.bundleLocalesDiffer":
87
+ "The locales of this template differ in content format or variables, so they cannot be exported as one bundle.",
88
+ "templateResolver.errors.collection.kindNotAllowed":
89
+ "“{detail}” templates are written through their own editor, not through a collection.",
90
+ "templateResolver.errors.noSystemTemplate": "There is no active system template to reset to.",
91
+ "templateResolver.errors.noDraft": "There is no draft to discard.",
92
+ "templateResolver.errors.template.invalidMeta": "The template settings are invalid.",
93
+ "templateResolver.errors.template.meta.subject.required": "The subject is missing.",
94
+ "templateResolver.errors.template.meta.subject.invalid":
95
+ "The subject is invalid (at most 200 characters, no line breaks).",
96
+ "templateResolver.errors.template.meta.previewText.required": "The preview text is missing.",
97
+ "templateResolver.errors.template.meta.previewText.invalid":
98
+ "The preview text is invalid (at most 200 characters, no line breaks).",
99
+ "templateResolver.errors.template.meta.senderName.required": "The sender name is missing.",
100
+ "templateResolver.errors.template.meta.senderName.invalid":
101
+ "The sender name is invalid (at most 80 characters, no line breaks).",
102
+ "templateResolver.errors.template.meta.replyTo.required": "The reply-to address is missing.",
103
+ "templateResolver.errors.template.meta.replyTo.invalid":
104
+ "The reply-to address is not a valid email address.",
105
+ "templateResolver.errors.template.meta.layoutSlug.required": "The layout is missing.",
106
+ "templateResolver.errors.template.meta.layoutSlug.invalid": "The chosen layout is invalid.",
107
+ "templateResolver.errors.template.meta.fileName.required": "The file name is missing.",
108
+ "templateResolver.errors.template.meta.fileName.invalid":
109
+ "The file name is invalid: no slashes, quotes, semicolons or two dots in a row.",
110
+ "templateResolver.errors.template.unknownPlaceholders": "Unknown placeholders: {detail}",
111
+ "templateResolver.errors.template.missingRequiredPlaceholders":
112
+ "Required placeholders are not used: {detail}",
113
+ "templateResolver.errors.document.embeddedContent":
114
+ "<{detail}> elements are not allowed in template bodies.",
115
+ "templateResolver.errors.document.unsafeAttributeValue":
116
+ "The attribute value “{detail}” must not contain < or >.",
117
+ "templateResolver.errors.document.eventHandlerAttribute":
118
+ "Inline event handler “{detail}” is not allowed.",
119
+ "templateResolver.errors.document.externalImageSource":
120
+ "Images must reference an uploaded resource; image source “{detail}” is not allowed.",
121
+ "templateResolver.errors.document.imageWithoutResource":
122
+ "Images must reference an uploaded resource.",
123
+ "templateResolver.errors.document.invalidReferenceName": "“{detail}” is not a valid name.",
124
+ "templateResolver.errors.document.scriptElement":
125
+ "<script> elements are not allowed in template bodies.",
126
+ "templateResolver.errors.attachment.unsupportedType":
127
+ "Attachments must be a PDF, PNG or JPEG file.",
128
+ "templateResolver.errors.attachment.limit": "A template can have at most {max} attachments.",
129
+ "templateResolver.errors.attachment.invalid":
130
+ "The attachment needs a template, a key and a valid file name.",
131
+ "templateResolver.errors.attachment.pdfTemplateMissing":
132
+ "There is no PDF template “{slug}” for this tenant or the system.",
133
+ "template-resolver.documentEditor.htmlSource": "HTML source",
134
+ "template-resolver.documentEditor.removeBlock": "Remove block",
135
+ "template-resolver.documentEditor.list": "List",
136
+ "template-resolver.documentEditor.more": "More",
137
+ "template-resolver.documentEditor.showHtml": "HTML",
138
+ "template-resolver.documentEditor.underline": "Underline",
18
139
  "template-resolver.editor.saveFailed": "Save failed.",
19
140
  "template-resolver.editor.networkError": "Network error while saving.",
20
141
  "template-resolver.editor.loading": "Loading current version…",
@@ -5,4 +5,30 @@
5
5
  // `@cosmicdrift/kumiko-bundled-features/template-resolver` and has no React or
6
6
  // DOM deps.
7
7
 
8
+ export {
9
+ PDF_DEFAULT_MARGIN_MM,
10
+ PDF_FOOTER_MARGIN_MM,
11
+ PDF_HEADER_MARGIN_MM,
12
+ PDF_ORIENTATIONS,
13
+ PDF_PAGE_FORMATS,
14
+ } from "../pdf-constants.js";
15
+ export {
16
+ TemplateResolverErrors,
17
+ TemplateResolverHandlers,
18
+ TemplateResolverQueries,
19
+ } from "../qualified-names.js";
8
20
  export { textBlocksClient } from "./client-plugin.js";
21
+ export { DocumentContentEditor } from "./document-editor.js";
22
+ export {
23
+ type DocumentEditorChrome,
24
+ type DocumentEditorHandle,
25
+ type DocumentEditorProps,
26
+ type DocumentResourcePreview,
27
+ type DocumentToolbarBlock,
28
+ MAIL_DOCUMENT_BLOCKS,
29
+ PDF_DOCUMENT_BLOCKS,
30
+ } from "./document-toolbar-blocks.js";
31
+ export {
32
+ PlaceholderLineEditor,
33
+ type PlaceholderLineEditorProps,
34
+ } from "./placeholder-line-editor-lazy.js";
@@ -0,0 +1,36 @@
1
+ // @runtime client
2
+ //
3
+ // Lazy boundary for the placeholder line editor: tiptap loads with the chunk,
4
+ // the fallback is a plain text input holding the same `{{name}}` text.
5
+ // Never import placeholder-line-editor statically here.
6
+
7
+ import { usePrimitives } from "@cosmicdrift/kumiko-renderer";
8
+ import { lazy, type ReactNode, Suspense } from "react";
9
+ import type { PlaceholderLineEditorProps } from "./placeholder-line-editor.js";
10
+
11
+ export type { PlaceholderLineEditorProps } from "./placeholder-line-editor.js";
12
+
13
+ const LazyPlaceholderLineEditor = lazy(() => import("./placeholder-line-editor.js"));
14
+
15
+ function PlainLineFallback({
16
+ id,
17
+ value,
18
+ onChange,
19
+ readOnly = false,
20
+ "aria-label": ariaLabel,
21
+ }: PlaceholderLineEditorProps): ReactNode {
22
+ const { Field, Input } = usePrimitives();
23
+ return (
24
+ <Field id={id} label={ariaLabel} hideLabel>
25
+ <Input kind="text" id={id} name={id} value={value} onChange={onChange} disabled={readOnly} />
26
+ </Field>
27
+ );
28
+ }
29
+
30
+ export function PlaceholderLineEditor(props: PlaceholderLineEditorProps): ReactNode {
31
+ return (
32
+ <Suspense fallback={<PlainLineFallback {...props} />}>
33
+ <LazyPlaceholderLineEditor {...props} />
34
+ </Suspense>
35
+ );
36
+ }
@@ -0,0 +1,176 @@
1
+ // @runtime client
2
+ //
3
+ // Single-line editor for plain-text fields that carry {{placeholders}}
4
+ // (subject, preheader). The stored value stays plain text; placeholders show
5
+ // as chips while editing. Lives in the same lazy chunk as the document editor.
6
+
7
+ import { cn } from "@cosmicdrift/kumiko-renderer-web";
8
+ import { Extension, Node } from "@tiptap/core";
9
+ import { UndoRedo } from "@tiptap/extensions";
10
+ import { EditorContent, useEditor } from "@tiptap/react";
11
+ import StarterKit from "@tiptap/starter-kit";
12
+ import { type ReactNode, useMemo, useState } from "react";
13
+ import { DocumentVariable } from "../document-schema.js";
14
+ import { createVariableView } from "./document-editor-extensions.js";
15
+ import type { DocumentEditorHandle } from "./document-toolbar-blocks.js";
16
+ import { createEditorHandle } from "./editor-handle.js";
17
+ import { contentToLine, lineToContent, singleLine } from "./placeholder-text.js";
18
+ import { useStableByKey } from "./use-stable-by-key.js";
19
+
20
+ export type PlaceholderLineEditorProps = {
21
+ readonly id: string;
22
+ /** Plain text with `{{name}}` placeholders. */
23
+ readonly value: string;
24
+ readonly onChange: (value: string) => void;
25
+ readonly variableLabels?: Readonly<Record<string, string>>;
26
+ /** Placeholders shown as system chips (a different tint). */
27
+ readonly systemVariables?: readonly string[];
28
+ readonly readOnly?: boolean;
29
+ /** Drops the input frame so the line sits flat inside a label/value row. */
30
+ readonly flat?: boolean;
31
+ readonly onEditorReady?: (handle: DocumentEditorHandle) => void;
32
+ readonly "aria-label": string;
33
+ };
34
+
35
+ const LineDocument = Node.create({ name: "doc", topNode: true, content: "paragraph" });
36
+
37
+ // One line only: Enter must not split the paragraph.
38
+ const BlockLineBreaks = Extension.create({
39
+ name: "blockLineBreaks",
40
+ addKeyboardShortcuts() {
41
+ return { Enter: () => true, "Shift-Enter": () => true, "Mod-Enter": () => true };
42
+ },
43
+ });
44
+
45
+ const NO_LABELS: Readonly<Record<string, string>> = {};
46
+ const NO_NAMES: readonly string[] = [];
47
+
48
+ function LineEditorBody({
49
+ id,
50
+ initialValue,
51
+ onChange,
52
+ variableLabels,
53
+ systemVariables,
54
+ readOnly,
55
+ flat,
56
+ onEditorReady,
57
+ ariaLabel,
58
+ }: {
59
+ readonly id: string;
60
+ readonly initialValue: string;
61
+ readonly onChange: (value: string) => void;
62
+ readonly variableLabels: Readonly<Record<string, string>>;
63
+ readonly systemVariables: readonly string[];
64
+ readonly readOnly: boolean;
65
+ readonly flat: boolean;
66
+ readonly onEditorReady: ((handle: DocumentEditorHandle) => void) | undefined;
67
+ readonly ariaLabel: string;
68
+ }): ReactNode {
69
+ const extensions = useMemo(
70
+ () => [
71
+ LineDocument,
72
+ StarterKit.configure({
73
+ document: false,
74
+ blockquote: false,
75
+ bold: false,
76
+ bulletList: false,
77
+ code: false,
78
+ codeBlock: false,
79
+ dropcursor: false,
80
+ gapcursor: false,
81
+ hardBreak: false,
82
+ heading: false,
83
+ horizontalRule: false,
84
+ italic: false,
85
+ link: false,
86
+ listItem: false,
87
+ listKeymap: false,
88
+ orderedList: false,
89
+ strike: false,
90
+ trailingNode: false,
91
+ underline: false,
92
+ undoRedo: false,
93
+ }),
94
+ DocumentVariable.extend({
95
+ addNodeView: () => createVariableView(variableLabels, systemVariables, flat),
96
+ }),
97
+ UndoRedo,
98
+ BlockLineBreaks,
99
+ ],
100
+ [variableLabels, systemVariables, flat],
101
+ );
102
+
103
+ const editor = useEditor({
104
+ extensions,
105
+ content: {
106
+ type: "doc",
107
+ content: [{ type: "paragraph", content: lineToContent(initialValue) }],
108
+ },
109
+ editable: !readOnly,
110
+ immediatelyRender: false,
111
+ editorProps: {
112
+ attributes: { id, role: "textbox", "aria-label": ariaLabel, "aria-multiline": "false" },
113
+ transformPastedText: singleLine,
114
+ // Rich pastes would bring several paragraphs; only their text is kept.
115
+ transformPastedHTML: (html) => singleLine(html.replace(/<[^>]*>/g, " ")),
116
+ },
117
+ onCreate: ({ editor: created }) => onEditorReady?.(createEditorHandle(created)),
118
+ onUpdate: ({ editor: e }) => onChange(contentToLine(e.getJSON().content?.[0]?.content)),
119
+ });
120
+
121
+ return (
122
+ <EditorContent
123
+ editor={editor}
124
+ className={cn(
125
+ "flex items-center bg-transparent text-base md:text-sm",
126
+ flat ? "min-h-10" : "min-h-9 rounded-md border border-input px-3",
127
+ "[&_.ProseMirror]:w-full [&_.ProseMirror]:whitespace-nowrap [&_.ProseMirror]:outline-none",
128
+ "[&_p]:m-0",
129
+ readOnly && "opacity-60",
130
+ )}
131
+ />
132
+ );
133
+ }
134
+
135
+ export default function PlaceholderLineEditor({
136
+ id,
137
+ value,
138
+ onChange,
139
+ variableLabels = NO_LABELS,
140
+ systemVariables = NO_NAMES,
141
+ readOnly = false,
142
+ flat = false,
143
+ onEditorReady,
144
+ "aria-label": ariaLabel,
145
+ }: PlaceholderLineEditorProps): ReactNode {
146
+ const stableLabels = useStableByKey(variableLabels, JSON.stringify(variableLabels));
147
+ const stableSystem = useStableByKey(systemVariables, systemVariables.join("\n"));
148
+
149
+ // The text is the source of truth. Typing reports it through onChange and
150
+ // keeps `emitted` equal to `value`; any other value (async load, reset)
151
+ // remounts the editor body on the new text.
152
+ const [emitted, setEmitted] = useState(value);
153
+ const [epoch, setEpoch] = useState(0);
154
+ if (value !== emitted) {
155
+ setEmitted(value);
156
+ setEpoch((current) => current + 1);
157
+ }
158
+
159
+ return (
160
+ <LineEditorBody
161
+ key={`${epoch}-${readOnly}-${JSON.stringify(stableLabels)}`}
162
+ id={id}
163
+ initialValue={value}
164
+ onChange={(next) => {
165
+ setEmitted(next);
166
+ onChange(next);
167
+ }}
168
+ variableLabels={stableLabels}
169
+ systemVariables={stableSystem}
170
+ readOnly={readOnly}
171
+ flat={flat}
172
+ onEditorReady={onEditorReady}
173
+ ariaLabel={ariaLabel}
174
+ />
175
+ );
176
+ }
@@ -0,0 +1,37 @@
1
+ // @runtime client
2
+ import type { JSONContent } from "@tiptap/core";
3
+ import { isDocumentReferenceName } from "../document-schema.js";
4
+
5
+ const PLACEHOLDER_TOKEN = /\{\{([^{}]*)\}\}/g;
6
+
7
+ // Names the schema refuses stay plain text, so a typo never becomes a chip
8
+ // that the server would reject.
9
+ export function lineToContent(text: string): JSONContent[] {
10
+ const content: JSONContent[] = [];
11
+ let cursor = 0;
12
+ const pushText = (value: string): void => {
13
+ if (value !== "") content.push({ type: "text", text: value });
14
+ };
15
+ for (const match of text.matchAll(PLACEHOLDER_TOKEN)) {
16
+ const name = match[1] ?? "";
17
+ if (!isDocumentReferenceName(name)) continue;
18
+ pushText(text.slice(cursor, match.index));
19
+ content.push({ type: "variable", attrs: { name } });
20
+ cursor = match.index + match[0].length;
21
+ }
22
+ pushText(text.slice(cursor));
23
+ return content;
24
+ }
25
+
26
+ export function contentToLine(nodes: readonly JSONContent[] | undefined): string {
27
+ return (nodes ?? [])
28
+ .map((node) => {
29
+ if (node.type === "variable") return `{{${String(node.attrs?.["name"])}}}`;
30
+ return node.type === "text" ? (node.text ?? "") : "";
31
+ })
32
+ .join("");
33
+ }
34
+
35
+ export function singleLine(text: string): string {
36
+ return text.replace(/\s*[\r\n]+\s*/g, " ");
37
+ }
@@ -0,0 +1,14 @@
1
+ // @runtime client
2
+ import { useState } from "react";
3
+
4
+ // Callers often pass fresh object literals every render; this keeps the first
5
+ // value for as long as the content key is unchanged, so memoized extension
6
+ // lists (and the editor built from them) are not rebuilt on every render.
7
+ export function useStableByKey<T>(value: T, key: string): T {
8
+ const [stored, setStored] = useState({ key, value });
9
+ if (stored.key !== key) {
10
+ setStored({ key, value });
11
+ return value;
12
+ }
13
+ return stored.value;
14
+ }
@@ -7,6 +7,7 @@ import {
7
7
  import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
8
8
  import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
9
9
  import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
10
+ import { errorCodeOf, isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
10
11
  import * as z from "zod";
11
12
  import { userSessionTable } from "../../sessions/index.js";
12
13
  import { decryptStoredPii, mapWithConcurrency } from "../../shared/index.js";
@@ -37,7 +38,7 @@ const SORT_FIELDS = ["email", "status", "createdAt", "lastSeenAt"] as const;
37
38
  type SortField = (typeof SORT_FIELDS)[number];
38
39
 
39
40
  function isSortField(value: string): value is SortField {
40
- return (SORT_FIELDS as readonly string[]).includes(value);
41
+ return isOneOf(SORT_FIELDS, value);
41
42
  }
42
43
 
43
44
  // Nulls (no session yet, invitation rows) sort last regardless of
@@ -141,7 +142,7 @@ export const teamListQuery = definePagedQueryHandler({
141
142
  // ponytail: lean sample apps (admin-console) mount auth without
142
143
  // sessions — store_user_sessions never gets pushed. Integration tests
143
144
  // may seed the table without mounting the feature; prod mounts both.
144
- if ((err as { code?: string }).code !== "42P01") throw err;
145
+ if (errorCodeOf(err) !== "42P01") throw err;
145
146
  }
146
147
  }
147
148
 
@@ -44,7 +44,10 @@ import {
44
44
  type TenantId,
45
45
  } from "@cosmicdrift/kumiko-framework/engine";
46
46
  import { getUnscopedAggregateStreamMaxVersion } from "@cosmicdrift/kumiko-framework/event-store";
47
- import { createLifecycleHooks } from "@cosmicdrift/kumiko-framework/pipeline";
47
+ import {
48
+ createDeferringNotify,
49
+ createLifecycleHooks,
50
+ } from "@cosmicdrift/kumiko-framework/pipeline";
48
51
  import { TestUsers } from "@cosmicdrift/kumiko-framework/stack";
49
52
  import { assertAssignableMembershipRoles } from "./membership-roles.js";
50
53
  import { tenantMembershipEntity, tenantMembershipsTable } from "./membership-table.js";
@@ -118,12 +121,40 @@ export type SeedTenantHooks = {
118
121
  // dispatcher's sink and flush post-commit. Without a sink (fixture / plain
119
122
  // Connection) they still fire immediately — there is no outer TX to wait for.
120
123
  //
124
+ function buildSeedHookNotify(
125
+ context: AppContext,
126
+ scopedUser: SessionUser,
127
+ targetTenantId: TenantId,
128
+ notifyTiming: "deferUntilCommit" | "inline",
129
+ ): AppContext["notify"] {
130
+ const notifyFactory = context._notifyFactory;
131
+ if (!notifyFactory) return context.notify;
132
+ if (notifyTiming === "inline") return notifyFactory(scopedUser, targetTenantId);
133
+ // Still inside the caller's write tx: delivery DB work must not wait for an app-pool slot.
134
+ const notifyInsideTransaction = (context._notifyInTransactionFactory ?? notifyFactory)(
135
+ scopedUser,
136
+ targetTenantId,
137
+ );
138
+ const scheduleAfterCommit = context.scheduleAfterCommit;
139
+ if (!scheduleAfterCommit) return notifyInsideTransaction;
140
+ return createDeferringNotify({
141
+ registry: context.registry,
142
+ tenantId: targetTenantId,
143
+ notifyAfterCommit: notifyFactory(scopedUser, targetTenantId),
144
+ notifyNow: notifyInsideTransaction,
145
+ scheduleAfterCommit,
146
+ });
147
+ }
148
+
121
149
  // `runner` comes from the caller, which already holds a declared DbRunner —
122
150
  // a TenantDb exposes no runner back out to feature code.
123
151
  function scopeSeedHookContext(
124
152
  context: AppContext,
125
153
  targetTenantId: TenantId,
126
154
  runner: DbRunner,
155
+ // The afterCommit sink is snapshotted when it flushes, so a hook scheduled from inside an
156
+ // afterCommit-phase hook would be dropped: that phase must notify inline.
157
+ notifyTiming: "deferUntilCommit" | "inline",
127
158
  ): AppContext {
128
159
  const db = createTenantDb(
129
160
  runner,
@@ -142,10 +173,9 @@ function scopeSeedHookContext(
142
173
  ? { ...baseUser, tenantId: targetTenantId }
143
174
  : undefined;
144
175
 
145
- const notify =
146
- context._notifyFactory && scopedUser
147
- ? context._notifyFactory(scopedUser, targetTenantId)
148
- : context.notify;
176
+ const notify = scopedUser
177
+ ? buildSeedHookNotify(context, scopedUser, targetTenantId, notifyTiming)
178
+ : context.notify;
149
179
 
150
180
  const features = context.effectiveFeatures as
151
181
  | (((tenantId: TenantId) => ReadonlySet<string>) & {
@@ -188,10 +218,14 @@ export async function fireEntityPostSave(
188
218
  const scopedContext =
189
219
  target === undefined
190
220
  ? hooks.context
191
- : scopeSeedHookContext(hooks.context, target.tenantId, target.db);
221
+ : scopeSeedHookContext(hooks.context, target.tenantId, target.db, "deferUntilCommit");
222
+ const afterCommitContext =
223
+ target === undefined
224
+ ? hooks.context
225
+ : scopeSeedHookContext(hooks.context, target.tenantId, target.db, "inline");
192
226
  await lifecycle.runPostSave(pseudoType, entityData, scopedContext, HookPhases.inTransaction);
193
227
  const runAfterCommit = () =>
194
- lifecycle.runPostSave(pseudoType, entityData, scopedContext, HookPhases.afterCommit);
228
+ lifecycle.runPostSave(pseudoType, entityData, afterCommitContext, HookPhases.afterCommit);
195
229
  if (scopedContext.scheduleAfterCommit) {
196
230
  scopedContext.scheduleAfterCommit(runAfterCommit);
197
231
  } else {
@@ -27,7 +27,7 @@ function hasTenantOwned(field: unknown): field is ResolvedPiiFlags {
27
27
  typeof field === "object" &&
28
28
  field !== null &&
29
29
  "tenantOwned" in field &&
30
- (field as { tenantOwned?: unknown }).tenantOwned === true
30
+ field.tenantOwned === true
31
31
  );
32
32
  }
33
33
 
@@ -72,12 +72,13 @@ export const cancelDestructionWrite = defineWriteHandler({
72
72
  payload: { cancelledBy: event.user.id },
73
73
  });
74
74
 
75
+ const gracePeriodEnd: string | null = null;
75
76
  return {
76
77
  isSuccess: true as const,
77
78
  data: {
78
79
  tenantId,
79
80
  status: "active" as const,
80
- gracePeriodEnd: null as string | null,
81
+ gracePeriodEnd,
81
82
  },
82
83
  };
83
84
  },