@cosmicdrift/kumiko-bundled-features 0.362.0 → 0.363.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 (307) hide show
  1. package/dist/admin-shell/feature.d.ts +3 -0
  2. package/dist/admin-shell/feature.js +6 -1
  3. package/dist/agent-tools/agent-manifest.js +2 -1
  4. package/dist/agent-tools/filter-ops.d.ts +1 -1
  5. package/dist/agent-tools/filter-ops.js +1 -3
  6. package/dist/agent-tools/tool-dispatch.js +2 -1
  7. package/dist/auth-email-password/email-templates.d.ts +4 -26
  8. package/dist/auth-email-password/email-templates.js +9 -101
  9. package/dist/auth-email-password/feature.js +24 -6
  10. package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
  11. package/dist/auth-email-password/handlers/request-account-unlock.write.js +2 -4
  12. package/dist/auth-email-password/handlers/request-email-verification.write.js +2 -4
  13. package/dist/auth-email-password/handlers/request-password-reset.write.js +2 -4
  14. package/dist/auth-email-password/handlers/signup-confirm.write.js +3 -4
  15. package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -2
  16. package/dist/auth-email-password/handlers/token-request-handler.js +0 -1
  17. package/dist/auth-email-password/index.d.ts +2 -2
  18. package/dist/auth-email-password/index.js +1 -1
  19. package/dist/auth-email-password/magic-link-mail.d.ts +3 -2
  20. package/dist/auth-email-password/magic-link-mail.js +19 -14
  21. package/dist/auth-email-password/mail-notifications.d.ts +32 -0
  22. package/dist/auth-email-password/mail-notifications.js +116 -0
  23. package/dist/auth-email-password/signup-activation.d.ts +1 -1
  24. package/dist/auth-email-password/signup-activation.js +2 -3
  25. package/dist/auth-foundation/types.js +2 -8
  26. package/dist/auth-mfa/feature.js +4 -4
  27. package/dist/auth-mfa/hmac-token-codec.js +4 -4
  28. package/dist/auth-mfa/mfa-challenge-token.js +3 -3
  29. package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -3
  30. package/dist/auth-mfa/mfa-setup-token.js +8 -8
  31. package/dist/billing-foundation/constants.d.ts +3 -3
  32. package/dist/billing-foundation/constants.js +5 -4
  33. package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +32 -14
  34. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +27 -32
  35. package/dist/billing-foundation/consumer-protection/issue-confirmation.js +3 -3
  36. package/dist/billing-foundation/consumer-protection/mail-notifications.d.ts +8 -0
  37. package/dist/billing-foundation/consumer-protection/mail-notifications.js +95 -0
  38. package/dist/billing-foundation/consumer-protection/register-consent.js +2 -0
  39. package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +20 -10
  40. package/dist/billing-foundation/consumer-protection/termination-mail.js +10 -21
  41. package/dist/billing-foundation/consumer-protection/termination-notify.js +2 -2
  42. package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -3
  43. package/dist/billing-foundation/consumer-protection/termination-request.js +2 -2
  44. package/dist/billing-foundation/handlers/process-event.write.js +2 -2
  45. package/dist/billing-foundation/handlers/process-payment-event.write.js +1 -1
  46. package/dist/billing-foundation/subscription-tier-sync.js +3 -4
  47. package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -1
  48. package/dist/cap-overview/web/cap-usage-cell.js +5 -4
  49. package/dist/channel-email/email-channel.d.ts +2 -2
  50. package/dist/channel-email/email-channel.js +89 -26
  51. package/dist/channel-email/feature.js +3 -3
  52. package/dist/channel-email/index.d.ts +1 -1
  53. package/dist/channel-email/index.js +1 -1
  54. package/dist/channel-email/pii-guard.js +10 -0
  55. package/dist/channel-email/smtp-transport.js +13 -0
  56. package/dist/channel-email/types.d.ts +8 -0
  57. package/dist/config/handlers/set.write.js +1 -2
  58. package/dist/custom-fields/lib/value-schema.js +10 -11
  59. package/dist/custom-fields/wire-user-data-rights.js +7 -6
  60. package/dist/data-retention/index.d.ts +1 -0
  61. package/dist/data-retention/index.js +1 -0
  62. package/dist/data-retention/run-retention-cleanup.js +6 -6
  63. package/dist/delivery/attachment-ref.d.ts +39 -0
  64. package/dist/delivery/attachment-ref.js +64 -0
  65. package/dist/delivery/channel-context.d.ts +8 -4
  66. package/dist/delivery/channel-context.js +76 -2
  67. package/dist/delivery/constants.d.ts +1 -0
  68. package/dist/delivery/constants.js +1 -0
  69. package/dist/delivery/delivery-service.d.ts +4 -1
  70. package/dist/delivery/delivery-service.js +38 -32
  71. package/dist/delivery/feature.js +1 -1
  72. package/dist/delivery/index.d.ts +5 -2
  73. package/dist/delivery/index.js +4 -1
  74. package/dist/delivery/jobs.js +25 -5
  75. package/dist/delivery/notify-factory.js +4 -1
  76. package/dist/delivery/testing.d.ts +4 -1
  77. package/dist/delivery/types.d.ts +20 -14
  78. package/dist/delivery/unsubscribe.js +4 -1
  79. package/dist/derivatives-sharp/render.js +1 -18
  80. package/dist/document-ingest-foundation/pages.js +4 -4
  81. package/dist/feature-toggles/toggle-sync-signal.js +4 -4
  82. package/dist/file-foundation/index.d.ts +1 -0
  83. package/dist/file-foundation/index.js +1 -0
  84. package/dist/file-foundation/read-file-ref-content.d.ts +27 -0
  85. package/dist/file-foundation/read-file-ref-content.js +49 -0
  86. package/dist/folders/web/folder-manager.js +3 -2
  87. package/dist/form-draft/db/queries/cleanup.js +4 -4
  88. package/dist/form-draft/db/queries/draft-count.js +2 -2
  89. package/dist/form-draft/db/queries/owned-file-refs.js +2 -2
  90. package/dist/form-draft/release-file-refs.js +3 -2
  91. package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
  92. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +6 -6
  93. package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +1 -1
  94. package/dist/inbound-mail-foundation/oauth-state.js +7 -7
  95. package/dist/inbound-mail-foundation/types.js +4 -4
  96. package/dist/inbound-mail-foundation/watch-supervisor.js +22 -13
  97. package/dist/inbound-provider-imap/imap-client.js +4 -3
  98. package/dist/ledger/recurring.js +1 -3
  99. package/dist/managed-pages/branding.d.ts +2 -0
  100. package/dist/managed-pages/branding.js +24 -2
  101. package/dist/managed-pages/feature.js +1 -0
  102. package/dist/managed-pages/i18n.js +1 -0
  103. package/dist/managed-pages/screens/branding-screen.js +8 -1
  104. package/dist/page-render/branding.d.ts +1 -0
  105. package/dist/page-render/branding.js +1 -0
  106. package/dist/page-render/markdown.js +1 -1
  107. package/dist/personal-access-tokens/feature.js +1 -1
  108. package/dist/personal-access-tokens/handlers/list.query.js +2 -1
  109. package/dist/personal-access-tokens/revoke-for-user.js +2 -1
  110. package/dist/renderer-foundation/api.d.ts +13 -13
  111. package/dist/renderer-foundation/api.js +1 -1
  112. package/dist/renderer-foundation/constants.js +2 -4
  113. package/dist/renderer-foundation/feature.js +7 -7
  114. package/dist/renderer-foundation/index.d.ts +2 -1
  115. package/dist/renderer-foundation/index.js +1 -0
  116. package/dist/renderer-foundation/types.d.ts +92 -32
  117. package/dist/renderer-pdf/feature.js +7 -1
  118. package/dist/sessions/db/queries/cleanup.js +2 -2
  119. package/dist/sessions/feature.js +1 -1
  120. package/dist/sessions/handlers/list.query.js +2 -1
  121. package/dist/shared/attachment-file-name.d.ts +3 -0
  122. package/dist/shared/attachment-file-name.js +35 -0
  123. package/dist/shared/index.d.ts +3 -0
  124. package/dist/shared/index.js +3 -0
  125. package/dist/shared/is-tenant-db.js +1 -1
  126. package/dist/shared/mail-template-body.d.ts +4 -0
  127. package/dist/shared/mail-template-body.js +17 -0
  128. package/dist/shared/sharp-input-limits.d.ts +6 -0
  129. package/dist/shared/sharp-input-limits.js +22 -0
  130. package/dist/shared/signup-handover.js +2 -2
  131. package/dist/tags/handlers/assign-tag.write.js +3 -1
  132. package/dist/tags/web/tag-chip.js +2 -0
  133. package/dist/template-resolver/api.d.ts +30 -2
  134. package/dist/template-resolver/api.js +40 -5
  135. package/dist/template-resolver/compile-document-body.d.ts +8 -0
  136. package/dist/template-resolver/compile-document-body.js +163 -0
  137. package/dist/template-resolver/constants.d.ts +13 -4
  138. package/dist/template-resolver/constants.js +30 -10
  139. package/dist/template-resolver/document-sanitizer.d.ts +32 -0
  140. package/dist/template-resolver/document-sanitizer.js +201 -0
  141. package/dist/template-resolver/document-schema.d.ts +21 -0
  142. package/dist/template-resolver/document-schema.js +296 -0
  143. package/dist/template-resolver/feature.d.ts +18 -0
  144. package/dist/template-resolver/feature.js +50 -6
  145. package/dist/template-resolver/handlers/bundle-import.d.ts +20 -0
  146. package/dist/template-resolver/handlers/bundle-import.js +147 -0
  147. package/dist/template-resolver/handlers/collection-set.write.js +25 -2
  148. package/dist/template-resolver/handlers/copy-locale.write.d.ts +19 -0
  149. package/dist/template-resolver/handlers/copy-locale.write.js +116 -0
  150. package/dist/template-resolver/handlers/diff-to-system.query.d.ts +22 -0
  151. package/dist/template-resolver/handlers/diff-to-system.query.js +64 -0
  152. package/dist/template-resolver/handlers/export-bundle.query.d.ts +35 -0
  153. package/dist/template-resolver/handlers/export-bundle.query.js +68 -0
  154. package/dist/template-resolver/handlers/find-by-id.query.d.ts +1 -0
  155. package/dist/template-resolver/handlers/find-by-id.query.js +3 -3
  156. package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +39 -0
  157. package/dist/template-resolver/handlers/import-bundle-system.write.js +36 -0
  158. package/dist/template-resolver/handlers/import-bundle.write.d.ts +40 -0
  159. package/dist/template-resolver/handlers/import-bundle.write.js +19 -0
  160. package/dist/template-resolver/handlers/layout-source.query.d.ts +26 -0
  161. package/dist/template-resolver/handlers/layout-source.query.js +64 -0
  162. package/dist/template-resolver/handlers/list.query.d.ts +4 -1
  163. package/dist/template-resolver/handlers/list.query.js +5 -7
  164. package/dist/template-resolver/handlers/overview.query.d.ts +59 -0
  165. package/dist/template-resolver/handlers/overview.query.js +165 -0
  166. package/dist/template-resolver/handlers/preview.query.d.ts +12 -0
  167. package/dist/template-resolver/handlers/preview.query.js +24 -0
  168. package/dist/template-resolver/handlers/reset-to-system.write.d.ts +8 -0
  169. package/dist/template-resolver/handlers/reset-to-system.write.js +50 -0
  170. package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +8 -0
  171. package/dist/template-resolver/handlers/resource-link-pdf.write.js +32 -0
  172. package/dist/template-resolver/handlers/resource-remove.write.d.ts +9 -0
  173. package/dist/template-resolver/handlers/resource-remove.write.js +44 -0
  174. package/dist/template-resolver/handlers/resource-shared.d.ts +9 -0
  175. package/dist/template-resolver/handlers/resource-shared.js +14 -0
  176. package/dist/template-resolver/handlers/resource-upload.write.d.ts +13 -0
  177. package/dist/template-resolver/handlers/resource-upload.write.js +98 -0
  178. package/dist/template-resolver/handlers/send-test.write.d.ts +11 -0
  179. package/dist/template-resolver/handlers/send-test.write.js +81 -0
  180. package/dist/template-resolver/handlers/set.write.d.ts +4 -1
  181. package/dist/template-resolver/handlers/set.write.js +25 -3
  182. package/dist/template-resolver/handlers/shared.d.ts +9 -5
  183. package/dist/template-resolver/handlers/shared.js +6 -2
  184. package/dist/template-resolver/handlers/template-gates.d.ts +36 -0
  185. package/dist/template-resolver/handlers/template-gates.js +131 -0
  186. package/dist/template-resolver/handlers/template-write.d.ts +45 -0
  187. package/dist/template-resolver/handlers/template-write.js +109 -0
  188. package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +17 -0
  189. package/dist/template-resolver/handlers/tenant-overrides.query.js +48 -0
  190. package/dist/template-resolver/handlers/toggle-status.write.d.ts +12 -0
  191. package/dist/template-resolver/handlers/toggle-status.write.js +27 -11
  192. package/dist/template-resolver/handlers/upsert-system.write.d.ts +5 -8
  193. package/dist/template-resolver/handlers/upsert-system.write.js +14 -59
  194. package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +5 -8
  195. package/dist/template-resolver/handlers/upsert-tenant.write.js +10 -61
  196. package/dist/template-resolver/handlers/usage.query.d.ts +17 -0
  197. package/dist/template-resolver/handlers/usage.query.js +15 -0
  198. package/dist/template-resolver/handlers/validate.query.d.ts +74 -0
  199. package/dist/template-resolver/handlers/validate.query.js +80 -0
  200. package/dist/template-resolver/index.d.ts +18 -1
  201. package/dist/template-resolver/index.js +16 -1
  202. package/dist/template-resolver/layout-regions.d.ts +23 -0
  203. package/dist/template-resolver/layout-regions.js +125 -0
  204. package/dist/template-resolver/linked-resources.d.ts +17 -0
  205. package/dist/template-resolver/linked-resources.js +75 -0
  206. package/dist/template-resolver/preview-display-html.d.ts +7 -0
  207. package/dist/template-resolver/preview-display-html.js +27 -0
  208. package/dist/template-resolver/print-document.d.ts +1 -0
  209. package/dist/template-resolver/print-document.js +55 -0
  210. package/dist/template-resolver/qualified-names.d.ts +30 -0
  211. package/dist/template-resolver/qualified-names.js +30 -0
  212. package/dist/template-resolver/record-mapping.d.ts +2 -0
  213. package/dist/template-resolver/record-mapping.js +21 -0
  214. package/dist/template-resolver/resource-image.d.ts +15 -0
  215. package/dist/template-resolver/resource-image.js +34 -0
  216. package/dist/template-resolver/seeding.d.ts +14 -1
  217. package/dist/template-resolver/seeding.js +105 -15
  218. package/dist/template-resolver/store-template-resource.d.ts +64 -0
  219. package/dist/template-resolver/store-template-resource.js +264 -0
  220. package/dist/template-resolver/table.d.ts +47 -8
  221. package/dist/template-resolver/table.js +11 -0
  222. package/dist/template-resolver/template-bundle.d.ts +36 -0
  223. package/dist/template-resolver/template-bundle.js +60 -0
  224. package/dist/template-resolver/template-diff.d.ts +7 -0
  225. package/dist/template-resolver/template-diff.js +68 -0
  226. package/dist/template-resolver/template-meta.d.ts +60 -0
  227. package/dist/template-resolver/template-meta.js +101 -0
  228. package/dist/template-resolver/template-preview.d.ts +86 -0
  229. package/dist/template-resolver/template-preview.js +374 -0
  230. package/dist/template-resolver/template-resources.d.ts +14 -0
  231. package/dist/template-resolver/template-resources.js +42 -0
  232. package/dist/template-resolver/template-usage.d.ts +12 -0
  233. package/dist/template-resolver/template-usage.js +30 -0
  234. package/dist/template-resolver/template-validation.d.ts +22 -0
  235. package/dist/template-resolver/template-validation.js +172 -0
  236. package/dist/template-resolver/testing.js +5 -5
  237. package/dist/template-resolver/user-content-table.d.ts +14 -8
  238. package/dist/template-resolver/web/client-plugin.d.ts +1 -0
  239. package/dist/template-resolver/web/client-plugin.js +9 -6
  240. package/dist/template-resolver/web/document-editor-extensions.d.ts +15 -0
  241. package/dist/template-resolver/web/document-editor-extensions.js +107 -0
  242. package/dist/template-resolver/web/document-editor.d.ts +3 -0
  243. package/dist/template-resolver/web/document-editor.js +11 -0
  244. package/dist/template-resolver/web/document-tiptap-editor.d.ts +3 -0
  245. package/dist/template-resolver/web/document-tiptap-editor.js +376 -0
  246. package/dist/template-resolver/web/document-toolbar-blocks.d.ts +33 -0
  247. package/dist/template-resolver/web/document-toolbar-blocks.js +15 -0
  248. package/dist/template-resolver/web/editor-handle.d.ts +3 -0
  249. package/dist/template-resolver/web/editor-handle.js +24 -0
  250. package/dist/template-resolver/web/i18n.js +76 -0
  251. package/dist/template-resolver/web/index.d.ts +4 -0
  252. package/dist/template-resolver/web/index.js +4 -0
  253. package/dist/template-resolver/web/placeholder-line-editor-lazy.d.ts +4 -0
  254. package/dist/template-resolver/web/placeholder-line-editor-lazy.js +16 -0
  255. package/dist/template-resolver/web/placeholder-line-editor.d.ts +15 -0
  256. package/dist/template-resolver/web/placeholder-line-editor.js +94 -0
  257. package/dist/template-resolver/web/placeholder-text.d.ts +4 -0
  258. package/dist/template-resolver/web/placeholder-text.js +34 -0
  259. package/dist/template-resolver/web/use-stable-by-key.d.ts +1 -0
  260. package/dist/template-resolver/web/use-stable-by-key.js +13 -0
  261. package/dist/tenant/handlers/team-list.query.js +3 -2
  262. package/dist/tenant/seeding.js +31 -6
  263. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
  264. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +2 -1
  265. package/dist/user/db/queries/stream-tenant-backfill.js +4 -4
  266. package/dist/user/handlers/detail.query.js +2 -1
  267. package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
  268. package/dist/user-data-rights/email-templates.d.ts +1 -29
  269. package/dist/user-data-rights/email-templates.js +5 -106
  270. package/dist/user-data-rights/feature.js +53 -35
  271. package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -1
  272. package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
  273. package/dist/user-data-rights/handlers/request-deletion.write.js +5 -7
  274. package/dist/user-data-rights/lib/default-mailers.d.ts +15 -7
  275. package/dist/user-data-rights/lib/default-mailers.js +100 -48
  276. package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
  277. package/dist/user-data-rights/mail-notifications.d.ts +13 -0
  278. package/dist/user-data-rights/mail-notifications.js +118 -0
  279. package/dist/user-data-rights/web/privacy-center-screen.js +2 -1
  280. package/dist/user-profile/web/profile-screen.js +2 -1
  281. package/dist/waitlist/feature.js +10 -0
  282. package/dist/waitlist/handlers/submit.write.js +3 -4
  283. package/dist/waitlist/mail.d.ts +19 -6
  284. package/dist/waitlist/mail.js +50 -14
  285. package/package.json +28 -17
  286. package/src/admin-shell/changes.json +6 -0
  287. package/src/auth-email-password/changes.json +14 -0
  288. package/src/billing-foundation/changes.json +7 -0
  289. package/src/channel-email/changes.json +19 -0
  290. package/src/data-retention/changes.json +6 -0
  291. package/src/delivery/changes.json +48 -0
  292. package/src/managed-pages/changes.json +6 -0
  293. package/src/renderer-foundation/changes.json +39 -0
  294. package/src/template-resolver/changes.json +118 -0
  295. package/src/user-data-rights/changes.json +7 -0
  296. package/src/waitlist/changes.json +7 -0
  297. package/dist/renderer-simple/feature.d.ts +0 -10
  298. package/dist/renderer-simple/feature.js +0 -42
  299. package/dist/renderer-simple/index.d.ts +0 -3
  300. package/dist/renderer-simple/index.js +0 -2
  301. package/dist/renderer-simple/mail-blocks.d.ts +0 -21
  302. package/dist/renderer-simple/mail-blocks.js +0 -107
  303. package/dist/renderer-simple/resolve-variables.d.ts +0 -7
  304. package/dist/renderer-simple/resolve-variables.js +0 -47
  305. package/dist/renderer-simple/simple-renderer.d.ts +0 -24
  306. package/dist/renderer-simple/simple-renderer.js +0 -200
  307. package/src/renderer-simple/changes.json +0 -27
@@ -0,0 +1,20 @@
1
+ import type { HandlerContext, SessionUser, TenantId, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
2
+ import { type FieldIssue } from "@cosmicdrift/kumiko-framework/errors";
3
+ import type { TemplateWriteScope } from "../layout-regions.js";
4
+ import type { TemplateBundle } from "../template-bundle.js";
5
+ export declare function targetExistsIssue(locale: string): FieldIssue;
6
+ type BundleImport = {
7
+ readonly ctx: HandlerContext;
8
+ readonly user: SessionUser;
9
+ readonly tenantId: TenantId;
10
+ readonly scope: TemplateWriteScope;
11
+ readonly bundle: TemplateBundle;
12
+ readonly overwrite: boolean;
13
+ };
14
+ type BundleImportStatus = "draft" | "active";
15
+ export declare function importTemplateBundle(args: BundleImport & {
16
+ readonly status: BundleImportStatus;
17
+ }): Promise<WriteResult<{
18
+ ids: Record<string, string>;
19
+ }>>;
20
+ export {};
@@ -0,0 +1,147 @@
1
+ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
2
+ import { ValidationError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
3
+ import { requireForTenant } from "../../shared/index.js";
4
+ import { attachmentLimitFailure, attachmentNeedsMailTemplate, readTemplateResourceMaxBytes, storeBundleAttachment, storeTemplateResource, } from "../store-template-resource.js";
5
+ import { templateResourcesTable } from "../table.js";
6
+ import { attachTemplateResources } from "../template-resources.js";
7
+ import { resolveTemplateVariables } from "../template-validation.js";
8
+ import { MAX_FILE_ATTACHMENTS_PER_TEMPLATE } from "./resource-shared.js";
9
+ import { evaluateTemplateWrite } from "./template-gates.js";
10
+ import { refuseTenantScopeUnderSystemTenant, writeTemplateRow, } from "./template-write.js";
11
+ export function targetExistsIssue(locale) {
12
+ return {
13
+ path: "locale",
14
+ code: "target_exists",
15
+ i18nKey: "templateResolver.errors.template.targetExists",
16
+ params: { detail: locale },
17
+ };
18
+ }
19
+ async function collectBundleIssues(args, status) {
20
+ const { ctx, tenantId, scope, bundle, overwrite } = args;
21
+ const { db } = requireForTenant(ctx, tenantId);
22
+ const variables = resolveTemplateVariables(ctx.registry, bundle.slug, bundle.variableSchema ?? {});
23
+ const issues = [];
24
+ for (const [locale, entry] of Object.entries(bundle.locales)) {
25
+ const evaluated = evaluateTemplateWrite({
26
+ kind: bundle.kind,
27
+ scope,
28
+ contentFormat: bundle.contentFormat,
29
+ content: entry.body,
30
+ rawMeta: entry.meta,
31
+ variables,
32
+ });
33
+ const found = evaluated.ok
34
+ ? status === "active"
35
+ ? evaluated.publishIssues
36
+ : []
37
+ : evaluated.issues;
38
+ issues.push(...found.map((issue) => ({ ...issue, path: `locales.${locale}.${issue.path}` })));
39
+ if (!overwrite) {
40
+ const existing = await fetchOne(db, templateResourcesTable, {
41
+ tenantId,
42
+ slug: bundle.slug,
43
+ kind: bundle.kind,
44
+ locale,
45
+ });
46
+ if (existing)
47
+ issues.push(targetExistsIssue(locale));
48
+ }
49
+ }
50
+ return issues;
51
+ }
52
+ async function storeBundleResources(args) {
53
+ const linked = {};
54
+ const pdfLinks = Object.entries(args.bundle.pdfAttachments);
55
+ const attachmentCount = Object.values(args.bundle.resources).filter((resource) => resource.purpose === "attachment")
56
+ .length + pdfLinks.length;
57
+ if (attachmentCount > 0 && args.bundle.kind !== "mail")
58
+ return attachmentNeedsMailTemplate();
59
+ if (attachmentCount > MAX_FILE_ATTACHMENTS_PER_TEMPLATE)
60
+ return attachmentLimitFailure();
61
+ // The document template is not looked up: a bundle may carry it later than the mail.
62
+ for (const [key, link] of pdfLinks)
63
+ linked[key] = { kind: "rendered-pdf", ...link };
64
+ const maxBytes = await readTemplateResourceMaxBytes(args.ctx.config, args.ctx.registry);
65
+ // A bundle is untrusted input like an upload: the same re-encoding decides the stored
66
+ // content type, so a declared image/png can't smuggle SVG or HTML into a mail or PDF.
67
+ for (const [key, resource] of Object.entries(args.bundle.resources)) {
68
+ const bytes = Uint8Array.from(Buffer.from(resource.base64, "base64"));
69
+ if (resource.purpose === "attachment") {
70
+ const attachment = await storeBundleAttachment({
71
+ ctx: args.ctx,
72
+ user: args.user,
73
+ tenantId: args.tenantId,
74
+ bytes,
75
+ maxBytes,
76
+ key,
77
+ fileName: resource.fileName,
78
+ });
79
+ if (!attachment.isSuccess)
80
+ return attachment;
81
+ linked[key] = attachment.data;
82
+ continue;
83
+ }
84
+ const stored = await storeTemplateResource({
85
+ ctx: args.ctx,
86
+ user: args.user,
87
+ tenantId: args.tenantId,
88
+ bytes,
89
+ maxBytes,
90
+ key,
91
+ });
92
+ if (!stored.isSuccess)
93
+ return stored;
94
+ linked[key] = stored.data.fileRefId;
95
+ }
96
+ return { isSuccess: true, data: linked };
97
+ }
98
+ // Validates every locale first, stores the resource files, then writes one row
99
+ // per locale, so a bad locale never leaves files or half a bundle behind. An image
100
+ // that fails re-encoding stops the import before any row is written; files stored
101
+ // for the resources before it stay as unlinked FileRefs, like a replaced upload.
102
+ export async function importTemplateBundle(args) {
103
+ const refused = refuseTenantScopeUnderSystemTenant(args.scope, args.tenantId);
104
+ if (refused !== null)
105
+ return refused;
106
+ const issues = await collectBundleIssues(args, args.status);
107
+ if (issues.length > 0)
108
+ return writeFailure(new ValidationError({ fields: [...issues] }));
109
+ const linked = await storeBundleResources(args);
110
+ if (!linked.isSuccess)
111
+ return linked;
112
+ const variableSchema = args.bundle.variableSchema ?? {};
113
+ const variables = resolveTemplateVariables(args.ctx.registry, args.bundle.slug, variableSchema);
114
+ const ids = {};
115
+ for (const [locale, entry] of Object.entries(args.bundle.locales)) {
116
+ const payload = {
117
+ slug: args.bundle.slug,
118
+ kind: args.bundle.kind,
119
+ locale,
120
+ content: entry.body,
121
+ contentFormat: args.bundle.contentFormat,
122
+ variableSchema,
123
+ meta: entry.meta,
124
+ };
125
+ const written = await writeTemplateRow({
126
+ ctx: args.ctx,
127
+ user: args.user,
128
+ tenantId: args.tenantId,
129
+ scope: args.scope,
130
+ status: args.status,
131
+ variables,
132
+ payload,
133
+ });
134
+ if (!written.isSuccess)
135
+ return written;
136
+ const attached = await attachTemplateResources(args.ctx, {
137
+ user: args.user,
138
+ tenantId: args.tenantId,
139
+ templateId: written.data.id,
140
+ resources: linked.data,
141
+ });
142
+ if (!attached.isSuccess)
143
+ return attached;
144
+ ids[locale] = written.data.id;
145
+ }
146
+ return { isSuccess: true, data: { ids } };
147
+ }
@@ -1,7 +1,10 @@
1
1
  import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
2
2
  import { defineWriteHandler, } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { ValidationError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
3
4
  import * as z from "zod";
4
5
  import { requireForTenant } from "../../shared/index.js";
6
+ import { COLLECTION_REFUSED_KINDS } from "../constants.js";
7
+ import { documentViolationsToFieldIssues, sanitizeTemplateBody } from "../document-sanitizer.js";
5
8
  import { collectionStore, DEFAULT_COLLECTION_ACCESS, } from "./collection-shared.js";
6
9
  import { contentFormatSchema, folderSchema, localeSchema, slugSchema } from "./shared.js";
7
10
  // Upsert inside one collection. Mirrors set.write for text-blocks, but the
@@ -30,6 +33,18 @@ export function makeCollectionSetWrite(collection) {
30
33
  ? `Creates or overwrites the caller's own "${collection.id}" entry at a slug and locale, live on save with no draft stage; it always writes the acting user's entry, never another user's.`
31
34
  : `Creates or overwrites the tenant-wide "${collection.id}" entry at a slug and locale, live on save with no draft stage; the change is visible to everyone who may reach this shared collection.`,
32
35
  handler: async (event, ctx) => {
36
+ if (COLLECTION_REFUSED_KINDS.includes(collection.kind)) {
37
+ return writeFailure(new ValidationError({
38
+ fields: [
39
+ {
40
+ path: "kind",
41
+ code: "kind_not_allowed",
42
+ i18nKey: "templateResolver.errors.collection.kindNotAllowed",
43
+ params: { detail: collection.kind },
44
+ },
45
+ ],
46
+ }));
47
+ }
33
48
  const override = event.payload.tenantIdOverride;
34
49
  // @cast-boundary engine-payload — override is a zod-validated string, the
35
50
  // user's own tenantId is already TenantId-branded.
@@ -39,6 +54,14 @@ export function makeCollectionSetWrite(collection) {
39
54
  // writing into another tenant still writes their own entry, never
40
55
  // someone else's signature.
41
56
  const owner = store.scopeOf(event.user);
57
+ const body = sanitizeTemplateBody({
58
+ kind: collection.kind,
59
+ contentFormat: event.payload.contentFormat,
60
+ content: event.payload.content,
61
+ });
62
+ if (!body.ok) {
63
+ return writeFailure(new ValidationError({ fields: documentViolationsToFieldIssues(body.violations) }));
64
+ }
42
65
  const existing = await fetchOne(db, store.table, {
43
66
  tenantId,
44
67
  slug: event.payload.slug,
@@ -55,7 +78,7 @@ export function makeCollectionSetWrite(collection) {
55
78
  // the template upserts put on the row.
56
79
  changes: {
57
80
  title: event.payload.title,
58
- content: event.payload.content,
81
+ content: body.content,
59
82
  contentFormat: event.payload.contentFormat,
60
83
  folder: event.payload.folder ?? null,
61
84
  },
@@ -73,7 +96,7 @@ export function makeCollectionSetWrite(collection) {
73
96
  kind: collection.kind,
74
97
  locale: event.payload.locale,
75
98
  title: event.payload.title,
76
- content: event.payload.content,
99
+ content: body.content,
77
100
  contentFormat: event.payload.contentFormat,
78
101
  folder: event.payload.folder ?? null,
79
102
  ...store.createDefaults(tenantId),
@@ -0,0 +1,19 @@
1
+ import * as z from "zod";
2
+ export declare const copyLocaleWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"copy-locale", z.ZodObject<{
3
+ slug: z.ZodString;
4
+ kind: z.ZodEnum<{
5
+ "ai-prompt": "ai-prompt";
6
+ "document-pdf": "document-pdf";
7
+ "image-snapshot": "image-snapshot";
8
+ mail: "mail";
9
+ "mail-layout": "mail-layout";
10
+ "pdf-layout": "pdf-layout";
11
+ }>;
12
+ fromLocale: z.ZodString;
13
+ toLocale: z.ZodString;
14
+ overwrite: z.ZodDefault<z.ZodBoolean>;
15
+ scope: z.ZodDefault<z.ZodEnum<{
16
+ system: "system";
17
+ tenant: "tenant";
18
+ }>>;
19
+ }, z.core.$strip>, import("./template-write.js").WrittenTemplateRow, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -0,0 +1,116 @@
1
+ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
2
+ import { defineWriteHandler, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { AccessDeniedError, NotFoundError, ValidationError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
4
+ import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
5
+ import * as z from "zod";
6
+ import { requireForTenant } from "../../shared/index.js";
7
+ import { CONTENT_FORMATS } from "../constants.js";
8
+ import { parseLinkedResourceEntries } from "../linked-resources.js";
9
+ import { TemplateResolverErrors } from "../qualified-names.js";
10
+ import { templateResourcesTable } from "../table.js";
11
+ import { attachTemplateResources } from "../template-resources.js";
12
+ import { resolveTemplateVariables } from "../template-validation.js";
13
+ import { targetExistsIssue } from "./bundle-import.js";
14
+ import { kindSchema, localeSchema, templateSlugSchema } from "./shared.js";
15
+ import { parseStoredJson, storedRecord } from "./template-gates.js";
16
+ import { refuseTenantScopeUnderSystemTenant, writeTemplateRow } from "./template-write.js";
17
+ function readDbFor(ctx, scope) {
18
+ return scope === "system" ? requireForTenant(ctx, SYSTEM_TENANT_ID).db : ctx.db;
19
+ }
20
+ // A tenant copy may start from the system default; a system copy only from system rows.
21
+ async function findCopySource(ctx, where) {
22
+ const { scope, tenantId, slug, kind, locale } = where;
23
+ const readDb = readDbFor(ctx, scope);
24
+ const own = await fetchOne(readDb, templateResourcesTable, {
25
+ tenantId,
26
+ slug,
27
+ kind,
28
+ locale,
29
+ });
30
+ if (own || scope === "system")
31
+ return own;
32
+ return fetchOne(readDb, templateResourcesTable, {
33
+ tenantId: SYSTEM_TENANT_ID,
34
+ slug,
35
+ kind,
36
+ locale,
37
+ status: "active",
38
+ });
39
+ }
40
+ export const copyLocaleWrite = defineWriteHandler({
41
+ name: "copy-locale",
42
+ schema: z.object({
43
+ slug: templateSlugSchema,
44
+ kind: kindSchema,
45
+ fromLocale: localeSchema,
46
+ toLocale: localeSchema,
47
+ overwrite: z.boolean().default(false),
48
+ /** "system" copies between system-default rows; SystemAdmin only. */
49
+ scope: z.enum(["tenant", "system"]).default("tenant"),
50
+ }),
51
+ access: { roles: ["TenantAdmin", "SystemAdmin"] },
52
+ description: "Copies the text of one locale of a template into another locale as a draft override of the caller's tenant, taking the tenant's own row or else the system default as the source; use it as the starting point for a translation. It refuses an existing target unless overwrite is set. With scope system (SystemAdmin only) both source and target are system-default rows and the target lands as a system draft; any other caller gets a 403.",
53
+ handler: async (event, ctx) => {
54
+ const { slug, kind, fromLocale, toLocale, overwrite, scope } = event.payload;
55
+ if (scope === "system" && !event.user.roles.includes("SystemAdmin")) {
56
+ return writeFailure(new AccessDeniedError({
57
+ i18nKey: "errors.access.denied",
58
+ details: { reason: TemplateResolverErrors.systemAdminRequired },
59
+ }));
60
+ }
61
+ const tenantId = scope === "system" ? SYSTEM_TENANT_ID : event.user.tenantId;
62
+ const refused = refuseTenantScopeUnderSystemTenant(scope, tenantId);
63
+ if (refused !== null)
64
+ return refused;
65
+ const readDb = readDbFor(ctx, scope);
66
+ const source = await findCopySource(ctx, { scope, tenantId, slug, kind, locale: fromLocale });
67
+ if (!source || !isOneOf(CONTENT_FORMATS, source.contentFormat)) {
68
+ return writeFailure(new NotFoundError("template-resource", `${slug}/${fromLocale}`));
69
+ }
70
+ if (!overwrite) {
71
+ const target = await fetchOne(readDb, templateResourcesTable, {
72
+ tenantId,
73
+ slug,
74
+ kind,
75
+ locale: toLocale,
76
+ });
77
+ if (target) {
78
+ return writeFailure(new ValidationError({ fields: [targetExistsIssue(toLocale)] }));
79
+ }
80
+ }
81
+ const variableSchema = storedRecord(source.variableSchema);
82
+ const written = await writeTemplateRow({
83
+ ctx,
84
+ user: event.user,
85
+ tenantId,
86
+ scope,
87
+ status: "draft",
88
+ variables: resolveTemplateVariables(ctx.registry, slug, variableSchema),
89
+ payload: {
90
+ slug,
91
+ kind,
92
+ locale: toLocale,
93
+ content: source.content,
94
+ contentFormat: source.contentFormat,
95
+ variableSchema,
96
+ meta: parseStoredJson(source.meta),
97
+ title: source.title,
98
+ folder: source.folder,
99
+ },
100
+ });
101
+ if (!written.isSuccess)
102
+ return written;
103
+ // A system source carries no files of its own: its resource keys are known to the tenant row anyway.
104
+ if (source.tenantId === tenantId) {
105
+ const attached = await attachTemplateResources(ctx, {
106
+ user: event.user,
107
+ tenantId,
108
+ templateId: written.data.id,
109
+ resources: parseLinkedResourceEntries(source.linkedResources),
110
+ });
111
+ if (!attached.isSuccess)
112
+ return attached;
113
+ }
114
+ return written;
115
+ },
116
+ });
@@ -0,0 +1,22 @@
1
+ import * as z from "zod";
2
+ export declare const diffToSystemQuery: import("@cosmicdrift/kumiko-types/define-handler").QueryHandlerDefinition<"diff-to-system", z.ZodObject<{
3
+ id: z.ZodString;
4
+ }, z.core.$strip>, {
5
+ tenant: {
6
+ templateId: string;
7
+ templateVersion: number;
8
+ contentFormat: string;
9
+ };
10
+ system: {
11
+ templateId: string;
12
+ templateVersion: number;
13
+ contentFormat: string;
14
+ };
15
+ identical: boolean;
16
+ blocks: import("../template-diff.js").TemplateDiffBlock[];
17
+ meta: {
18
+ key: string;
19
+ tenant: unknown;
20
+ system: unknown;
21
+ }[];
22
+ }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -0,0 +1,64 @@
1
+ import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
2
+ import { defineQueryHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { NotFoundError } from "@cosmicdrift/kumiko-framework/errors";
4
+ import * as z from "zod";
5
+ import { templateResourcesTable } from "../table.js";
6
+ import { diffTemplateBlocks, splitTemplateBlocks } from "../template-diff.js";
7
+ function parseStoredMeta(raw) {
8
+ if (raw === null)
9
+ return {};
10
+ try {
11
+ const parsed = JSON.parse(raw);
12
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
13
+ ? Object.fromEntries(Object.entries(parsed))
14
+ : {};
15
+ }
16
+ catch {
17
+ return {};
18
+ }
19
+ }
20
+ function diffMeta(system, tenant) {
21
+ const keys = [...new Set([...Object.keys(system), ...Object.keys(tenant)])].sort();
22
+ return keys
23
+ .filter((key) => JSON.stringify(system[key]) !== JSON.stringify(tenant[key]))
24
+ .map((key) => ({ key, tenant: tenant[key], system: system[key] }));
25
+ }
26
+ export const diffToSystemQuery = defineQueryHandler({
27
+ name: "diff-to-system",
28
+ schema: z.object({ id: z.string().min(1) }),
29
+ access: { roles: ["TenantAdmin", "SystemAdmin"] },
30
+ description: "Compares one tenant override with the system template of the same slug, kind and locale block by block and lists the changed meta settings; use it to show what an override changes before resetting it to the system version.",
31
+ handler: async (query, ctx) => {
32
+ const tenantRow = await fetchOne(ctx.db, templateResourcesTable, {
33
+ id: query.payload.id,
34
+ });
35
+ if (!tenantRow || tenantRow.tenantId !== query.user.tenantId || tenantRow.scope !== "tenant") {
36
+ throw new NotFoundError("template-resource", query.payload.id);
37
+ }
38
+ const systemRow = await fetchOne(ctx.db, templateResourcesTable, {
39
+ tenantId: SYSTEM_TENANT_ID,
40
+ slug: tenantRow.slug,
41
+ kind: tenantRow.kind,
42
+ locale: tenantRow.locale,
43
+ });
44
+ if (!systemRow)
45
+ throw new NotFoundError("template-resource", tenantRow.slug);
46
+ const blocks = diffTemplateBlocks(splitTemplateBlocks(systemRow.content ?? "", systemRow.contentFormat), splitTemplateBlocks(tenantRow.content ?? "", tenantRow.contentFormat));
47
+ const meta = diffMeta(parseStoredMeta(systemRow.meta), parseStoredMeta(tenantRow.meta));
48
+ return {
49
+ tenant: {
50
+ templateId: String(tenantRow.id),
51
+ templateVersion: tenantRow.version,
52
+ contentFormat: tenantRow.contentFormat,
53
+ },
54
+ system: {
55
+ templateId: String(systemRow.id),
56
+ templateVersion: systemRow.version,
57
+ contentFormat: systemRow.contentFormat,
58
+ },
59
+ identical: meta.length === 0 && blocks.every((block) => block.change === "same"),
60
+ blocks,
61
+ meta,
62
+ };
63
+ },
64
+ });
@@ -0,0 +1,35 @@
1
+ import * as z from "zod";
2
+ export declare const exportBundleQuery: import("@cosmicdrift/kumiko-types/define-handler").QueryHandlerDefinition<"export-bundle", z.ZodObject<{
3
+ slug: z.ZodString;
4
+ kind: z.ZodEnum<{
5
+ "ai-prompt": "ai-prompt";
6
+ "document-pdf": "document-pdf";
7
+ "image-snapshot": "image-snapshot";
8
+ mail: "mail";
9
+ "mail-layout": "mail-layout";
10
+ "pdf-layout": "pdf-layout";
11
+ }>;
12
+ scope: z.ZodDefault<z.ZodEnum<{
13
+ system: "system";
14
+ tenant: "tenant";
15
+ }>>;
16
+ }, z.core.$strip>, {
17
+ slug: string;
18
+ kind: "ai-prompt" | "document-pdf" | "image-snapshot" | "mail" | "mail-layout" | "pdf-layout";
19
+ contentFormat: string;
20
+ variableSchema: Readonly<Record<string, unknown>>;
21
+ locales: Record<string, {
22
+ body: string;
23
+ meta?: unknown;
24
+ }>;
25
+ resources: Record<string, {
26
+ fileName: string;
27
+ contentType: string;
28
+ base64: string;
29
+ purpose?: "attachment" | undefined;
30
+ }>;
31
+ pdfAttachments: Record<string, {
32
+ templateSlug: string;
33
+ fileName: string;
34
+ }>;
35
+ }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
@@ -0,0 +1,68 @@
1
+ import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
2
+ import { defineQueryHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
3
+ import { NotFoundError } from "@cosmicdrift/kumiko-framework/errors";
4
+ import * as z from "zod";
5
+ import { isFileAttachmentEntry, isPdfAttachmentLink, parseLinkedResourceEntries, } from "../linked-resources.js";
6
+ import { templateResourcesTable } from "../table.js";
7
+ import { readTemplateResource } from "../template-resources.js";
8
+ import { kindSchema, templateSlugSchema } from "./shared.js";
9
+ import { parseStoredJson, storedRecord } from "./template-gates.js";
10
+ export const exportBundleQuery = defineQueryHandler({
11
+ name: "export-bundle",
12
+ schema: z.object({
13
+ slug: templateSlugSchema,
14
+ kind: kindSchema,
15
+ scope: z.enum(["tenant", "system"]).default("tenant"),
16
+ }),
17
+ access: { roles: ["TenantAdmin", "SystemAdmin"] },
18
+ description: "Exports every locale of one template (the caller's own rows, or the active system rows) together with its linked resource files as a self-contained bundle that import-bundle can read back, for example to move a template between tenants or environments.",
19
+ handler: async (query, ctx) => {
20
+ const { slug, kind, scope } = query.payload;
21
+ const found = await selectMany(ctx.db, templateResourcesTable, scope === "system"
22
+ ? { tenantId: SYSTEM_TENANT_ID, slug, kind, scope: "system", status: "active" }
23
+ : { tenantId: query.user.tenantId, slug, kind, scope: "tenant" });
24
+ const rows = found
25
+ .filter((row) => row.status !== "archived")
26
+ .sort((a, b) => a.locale.localeCompare(b.locale));
27
+ const first = rows[0];
28
+ if (!first)
29
+ throw new NotFoundError("template-resource", slug);
30
+ const locales = {};
31
+ const entryByKey = new Map();
32
+ for (const row of rows) {
33
+ const meta = parseStoredJson(row.meta);
34
+ locales[row.locale] = { body: row.content ?? "", ...(meta === undefined ? {} : { meta }) };
35
+ for (const [key, entry] of Object.entries(parseLinkedResourceEntries(row.linkedResources))) {
36
+ if (!entryByKey.has(key))
37
+ entryByKey.set(key, entry);
38
+ }
39
+ }
40
+ const resources = {};
41
+ const pdfAttachments = {};
42
+ for (const [key, entry] of entryByKey) {
43
+ if (isPdfAttachmentLink(entry)) {
44
+ pdfAttachments[key] = { templateSlug: entry.templateSlug, fileName: entry.fileName };
45
+ continue;
46
+ }
47
+ const attachment = isFileAttachmentEntry(entry);
48
+ const file = await readTemplateResource({ db: ctx.db, files: ctx.files, user: query.user }, attachment ? entry.fileRefId : entry);
49
+ if (!file)
50
+ throw new NotFoundError("template-resource-file", key);
51
+ resources[key] = {
52
+ fileName: attachment ? entry.fileName : file.fileName,
53
+ contentType: file.contentType,
54
+ base64: Buffer.from(file.bytes).toString("base64"),
55
+ ...(attachment && { purpose: "attachment" }),
56
+ };
57
+ }
58
+ return {
59
+ slug,
60
+ kind,
61
+ contentFormat: first.contentFormat,
62
+ variableSchema: storedRecord(first.variableSchema),
63
+ locales,
64
+ resources,
65
+ pdfAttachments,
66
+ };
67
+ },
68
+ });
@@ -12,6 +12,7 @@ export declare const findByIdQuery: import("@cosmicdrift/kumiko-types/define-han
12
12
  contentFormat: string;
13
13
  variableSchema: string | null;
14
14
  linkedResources: string | null;
15
+ meta: string | null;
15
16
  scope: string;
16
17
  parentTemplateId: string | null;
17
18
  status: string;
@@ -5,7 +5,7 @@ import { templateResourcesTable } from "../table.js";
5
5
  // Admin-Lookup für UI-Edit-Flow. Returnt das raw Template inkl. draft/
6
6
  // archived Status. Tenant-Isolation: User sieht nur Templates des
7
7
  // eigenen Tenants oder SYSTEM_TENANT (system-defaults sind public-
8
- // readable). SystemAdmin sieht alle. Anonymous bleibt aus — Public-
8
+ // readable); ctx.db is tenant-scoped for every role, so the check below is defense in depth. Anonymous bleibt aus — Public-
9
9
  // Read geht über resolveTemplate aus der API, nicht hier.
10
10
  export const findByIdQuery = defineQueryHandler({
11
11
  name: "find-by-id",
@@ -18,10 +18,9 @@ export const findByIdQuery = defineQueryHandler({
18
18
  });
19
19
  if (!row)
20
20
  return null;
21
- const isSystemAdmin = query.user.roles.includes("SystemAdmin");
22
21
  const isOwnTenant = row.tenantId === query.user.tenantId;
23
22
  const isSystemTemplate = row.scope === "system";
24
- if (!isSystemAdmin && !isOwnTenant && !isSystemTemplate)
23
+ if (!isOwnTenant && !isSystemTemplate)
25
24
  return null;
26
25
  return {
27
26
  id: String(row.id),
@@ -34,6 +33,7 @@ export const findByIdQuery = defineQueryHandler({
34
33
  contentFormat: row.contentFormat,
35
34
  variableSchema: row.variableSchema,
36
35
  linkedResources: row.linkedResources,
36
+ meta: row.meta,
37
37
  scope: row.scope,
38
38
  parentTemplateId: row.parentTemplateId,
39
39
  status: row.status,
@@ -0,0 +1,39 @@
1
+ import * as z from "zod";
2
+ export declare const importBundleSystemWrite: import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"import-bundle-system", z.ZodObject<{
3
+ bundle: z.ZodObject<{
4
+ slug: z.ZodString;
5
+ kind: z.ZodEnum<{
6
+ "ai-prompt": "ai-prompt";
7
+ "document-pdf": "document-pdf";
8
+ "image-snapshot": "image-snapshot";
9
+ mail: "mail";
10
+ "mail-layout": "mail-layout";
11
+ "pdf-layout": "pdf-layout";
12
+ }>;
13
+ contentFormat: z.ZodEnum<{
14
+ html: "html";
15
+ markdown: "markdown";
16
+ mjml: "mjml";
17
+ plain: "plain";
18
+ }>;
19
+ variableSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
20
+ locales: z.ZodRecord<z.ZodString, z.ZodObject<{
21
+ body: z.ZodString;
22
+ meta: z.ZodOptional<z.ZodUnknown>;
23
+ }, z.core.$strict>>;
24
+ resources: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
25
+ fileName: z.ZodString;
26
+ contentType: z.ZodString;
27
+ base64: z.ZodString;
28
+ purpose: z.ZodOptional<z.ZodEnum<{
29
+ attachment: "attachment";
30
+ }>>;
31
+ }, z.core.$strict>>>;
32
+ pdfAttachments: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
33
+ templateSlug: z.ZodString;
34
+ fileName: z.ZodString;
35
+ }, z.core.$strict>>>;
36
+ }, z.core.$strict>;
37
+ }, z.core.$strip>, {
38
+ ids: Record<string, string>;
39
+ }, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;