@antelopejs/dms-mailing 0.2.2

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 (550) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +162 -0
  3. package/dist/antelope-modules.d.ts +32 -0
  4. package/dist/antelope-modules.js +36 -0
  5. package/dist/antelope-modules.js.map +1 -0
  6. package/dist/automation/email-event-trigger.d.ts +16 -0
  7. package/dist/automation/email-event-trigger.js +70 -0
  8. package/dist/automation/email-event-trigger.js.map +1 -0
  9. package/dist/automation/index.d.ts +4 -0
  10. package/dist/automation/index.js +39 -0
  11. package/dist/automation/index.js.map +1 -0
  12. package/dist/automation/send-template-action.d.ts +13 -0
  13. package/dist/automation/send-template-action.js +71 -0
  14. package/dist/automation/send-template-action.js.map +1 -0
  15. package/dist/config.d.ts +5 -0
  16. package/dist/config.js +13 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/constants.d.ts +28 -0
  19. package/dist/constants.js +32 -0
  20. package/dist/constants.js.map +1 -0
  21. package/dist/crons/index.d.ts +1 -0
  22. package/dist/crons/index.js +18 -0
  23. package/dist/crons/index.js.map +1 -0
  24. package/dist/crons/retention.d.ts +18 -0
  25. package/dist/crons/retention.js +107 -0
  26. package/dist/crons/retention.js.map +1 -0
  27. package/dist/data/body.d.ts +1 -0
  28. package/dist/data/body.js +18 -0
  29. package/dist/data/body.js.map +1 -0
  30. package/dist/data/index.d.ts +3 -0
  31. package/dist/data/index.js +20 -0
  32. package/dist/data/index.js.map +1 -0
  33. package/dist/data/sends-table.d.ts +28 -0
  34. package/dist/data/sends-table.js +161 -0
  35. package/dist/data/sends-table.js.map +1 -0
  36. package/dist/data/templates-table.d.ts +31 -0
  37. package/dist/data/templates-table.js +173 -0
  38. package/dist/data/templates-table.js.map +1 -0
  39. package/dist/data-types/categories-type.d.ts +35 -0
  40. package/dist/data-types/categories-type.js +37 -0
  41. package/dist/data-types/categories-type.js.map +1 -0
  42. package/dist/data-types/category-type.d.ts +8 -0
  43. package/dist/data-types/category-type.js +43 -0
  44. package/dist/data-types/category-type.js.map +1 -0
  45. package/dist/data-types/index.d.ts +2 -0
  46. package/dist/data-types/index.js +19 -0
  47. package/dist/data-types/index.js.map +1 -0
  48. package/dist/db/index.d.ts +2 -0
  49. package/dist/db/index.js +19 -0
  50. package/dist/db/index.js.map +1 -0
  51. package/dist/db/models/index.d.ts +4 -0
  52. package/dist/db/models/index.js +21 -0
  53. package/dist/db/models/index.js.map +1 -0
  54. package/dist/db/models/send-events.model.d.ts +27 -0
  55. package/dist/db/models/send-events.model.js +35 -0
  56. package/dist/db/models/send-events.model.js.map +1 -0
  57. package/dist/db/models/sends.model.d.ts +32 -0
  58. package/dist/db/models/sends.model.js +115 -0
  59. package/dist/db/models/sends.model.js.map +1 -0
  60. package/dist/db/models/settings.model.d.ts +23 -0
  61. package/dist/db/models/settings.model.js +13 -0
  62. package/dist/db/models/settings.model.js.map +1 -0
  63. package/dist/db/models/templates.model.d.ts +23 -0
  64. package/dist/db/models/templates.model.js +13 -0
  65. package/dist/db/models/templates.model.js.map +1 -0
  66. package/dist/db/tables/index.d.ts +4 -0
  67. package/dist/db/tables/index.js +21 -0
  68. package/dist/db/tables/index.js.map +1 -0
  69. package/dist/db/tables/send-events.table.d.ts +10 -0
  70. package/dist/db/tables/send-events.table.js +44 -0
  71. package/dist/db/tables/send-events.table.js.map +1 -0
  72. package/dist/db/tables/sends.table.d.ts +26 -0
  73. package/dist/db/tables/sends.table.js +113 -0
  74. package/dist/db/tables/sends.table.js.map +1 -0
  75. package/dist/db/tables/settings.table.d.ts +15 -0
  76. package/dist/db/tables/settings.table.js +68 -0
  77. package/dist/db/tables/settings.table.js.map +1 -0
  78. package/dist/db/tables/templates.table.d.ts +18 -0
  79. package/dist/db/tables/templates.table.js +81 -0
  80. package/dist/db/tables/templates.table.js.map +1 -0
  81. package/dist/engine/conditions.d.ts +2 -0
  82. package/dist/engine/conditions.js +20 -0
  83. package/dist/engine/conditions.js.map +1 -0
  84. package/dist/engine/coverage.d.ts +4 -0
  85. package/dist/engine/coverage.js +45 -0
  86. package/dist/engine/coverage.js.map +1 -0
  87. package/dist/engine/fallback-html.d.ts +4 -0
  88. package/dist/engine/fallback-html.js +84 -0
  89. package/dist/engine/fallback-html.js.map +1 -0
  90. package/dist/engine/index.d.ts +7 -0
  91. package/dist/engine/index.js +24 -0
  92. package/dist/engine/index.js.map +1 -0
  93. package/dist/engine/paths.d.ts +3 -0
  94. package/dist/engine/paths.js +32 -0
  95. package/dist/engine/paths.js.map +1 -0
  96. package/dist/engine/resolve.d.ts +4 -0
  97. package/dist/engine/resolve.js +139 -0
  98. package/dist/engine/resolve.js.map +1 -0
  99. package/dist/engine/tokens.d.ts +7 -0
  100. package/dist/engine/tokens.js +29 -0
  101. package/dist/engine/tokens.js.map +1 -0
  102. package/dist/engine/walk.d.ts +3 -0
  103. package/dist/engine/walk.js +14 -0
  104. package/dist/engine/walk.js.map +1 -0
  105. package/dist/hooks/index.d.ts +1 -0
  106. package/dist/hooks/index.js +18 -0
  107. package/dist/hooks/index.js.map +1 -0
  108. package/dist/hooks/tenant-export.d.ts +14 -0
  109. package/dist/hooks/tenant-export.js +84 -0
  110. package/dist/hooks/tenant-export.js.map +1 -0
  111. package/dist/implementations/dms-mailing/index.d.ts +2 -0
  112. package/dist/implementations/dms-mailing/index.js +8 -0
  113. package/dist/implementations/dms-mailing/index.js.map +1 -0
  114. package/dist/index.d.ts +13 -0
  115. package/dist/index.js +95 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/pages/editor.d.ts +9 -0
  118. package/dist/pages/editor.js +30 -0
  119. package/dist/pages/editor.js.map +1 -0
  120. package/dist/pages/index.d.ts +7 -0
  121. package/dist/pages/index.js +24 -0
  122. package/dist/pages/index.js.map +1 -0
  123. package/dist/pages/module.d.ts +1 -0
  124. package/dist/pages/module.js +12 -0
  125. package/dist/pages/module.js.map +1 -0
  126. package/dist/pages/overview.d.ts +14 -0
  127. package/dist/pages/overview.js +92 -0
  128. package/dist/pages/overview.js.map +1 -0
  129. package/dist/pages/sends.d.ts +13 -0
  130. package/dist/pages/sends.js +125 -0
  131. package/dist/pages/sends.js.map +1 -0
  132. package/dist/pages/settings/form.d.ts +1 -0
  133. package/dist/pages/settings/form.js +68 -0
  134. package/dist/pages/settings/form.js.map +1 -0
  135. package/dist/pages/settings/index.d.ts +2 -0
  136. package/dist/pages/settings/index.js +19 -0
  137. package/dist/pages/settings/index.js.map +1 -0
  138. package/dist/pages/settings/page.d.ts +10 -0
  139. package/dist/pages/settings/page.js +35 -0
  140. package/dist/pages/settings/page.js.map +1 -0
  141. package/dist/pages/templates.d.ts +10 -0
  142. package/dist/pages/templates.js +140 -0
  143. package/dist/pages/templates.js.map +1 -0
  144. package/dist/permissions.d.ts +1 -0
  145. package/dist/permissions.js +37 -0
  146. package/dist/permissions.js.map +1 -0
  147. package/dist/quick-actions.d.ts +2 -0
  148. package/dist/quick-actions.js +18 -0
  149. package/dist/quick-actions.js.map +1 -0
  150. package/dist/realtime.d.ts +4 -0
  151. package/dist/realtime.js +44 -0
  152. package/dist/realtime.js.map +1 -0
  153. package/dist/routes/events.d.ts +9 -0
  154. package/dist/routes/events.js +63 -0
  155. package/dist/routes/events.js.map +1 -0
  156. package/dist/routes/index.d.ts +7 -0
  157. package/dist/routes/index.js +24 -0
  158. package/dist/routes/index.js.map +1 -0
  159. package/dist/routes/metrics.d.ts +54 -0
  160. package/dist/routes/metrics.js +235 -0
  161. package/dist/routes/metrics.js.map +1 -0
  162. package/dist/routes/period.d.ts +5 -0
  163. package/dist/routes/period.js +36 -0
  164. package/dist/routes/period.js.map +1 -0
  165. package/dist/routes/provider.d.ts +13 -0
  166. package/dist/routes/provider.js +41 -0
  167. package/dist/routes/provider.js.map +1 -0
  168. package/dist/routes/sends.d.ts +31 -0
  169. package/dist/routes/sends.js +178 -0
  170. package/dist/routes/sends.js.map +1 -0
  171. package/dist/routes/settings.d.ts +14 -0
  172. package/dist/routes/settings.js +72 -0
  173. package/dist/routes/settings.js.map +1 -0
  174. package/dist/routes/templates.d.ts +61 -0
  175. package/dist/routes/templates.js +230 -0
  176. package/dist/routes/templates.js.map +1 -0
  177. package/dist/services/attention.d.ts +23 -0
  178. package/dist/services/attention.js +104 -0
  179. package/dist/services/attention.js.map +1 -0
  180. package/dist/services/categories.d.ts +34 -0
  181. package/dist/services/categories.js +34 -0
  182. package/dist/services/categories.js.map +1 -0
  183. package/dist/services/events.d.ts +10 -0
  184. package/dist/services/events.js +89 -0
  185. package/dist/services/events.js.map +1 -0
  186. package/dist/services/metrics.d.ts +34 -0
  187. package/dist/services/metrics.js +103 -0
  188. package/dist/services/metrics.js.map +1 -0
  189. package/dist/services/notify.d.ts +20 -0
  190. package/dist/services/notify.js +63 -0
  191. package/dist/services/notify.js.map +1 -0
  192. package/dist/services/permissions.d.ts +2 -0
  193. package/dist/services/permissions.js +15 -0
  194. package/dist/services/permissions.js.map +1 -0
  195. package/dist/services/provider.d.ts +6 -0
  196. package/dist/services/provider.js +25 -0
  197. package/dist/services/provider.js.map +1 -0
  198. package/dist/services/render.d.ts +6 -0
  199. package/dist/services/render.js +21 -0
  200. package/dist/services/render.js.map +1 -0
  201. package/dist/services/send.d.ts +49 -0
  202. package/dist/services/send.js +259 -0
  203. package/dist/services/send.js.map +1 -0
  204. package/dist/services/settings.d.ts +4 -0
  205. package/dist/services/settings.js +61 -0
  206. package/dist/services/settings.js.map +1 -0
  207. package/dist/services/status.d.ts +3 -0
  208. package/dist/services/status.js +16 -0
  209. package/dist/services/status.js.map +1 -0
  210. package/dist/services/templates.d.ts +17 -0
  211. package/dist/services/templates.js +92 -0
  212. package/dist/services/templates.js.map +1 -0
  213. package/dist/test/antelope.test.d.ts +2 -0
  214. package/dist/test/antelope.test.js +179 -0
  215. package/dist/test/antelope.test.js.map +1 -0
  216. package/dist/test/helpers/http.d.ts +4 -0
  217. package/dist/test/helpers/http.js +28 -0
  218. package/dist/test/helpers/http.js.map +1 -0
  219. package/dist/test/helpers/owner.d.ts +5 -0
  220. package/dist/test/helpers/owner.js +30 -0
  221. package/dist/test/helpers/owner.js.map +1 -0
  222. package/dist/test/integration/events.test.d.ts +1 -0
  223. package/dist/test/integration/events.test.js +132 -0
  224. package/dist/test/integration/events.test.js.map +1 -0
  225. package/dist/test/integration/retention.test.d.ts +1 -0
  226. package/dist/test/integration/retention.test.js +291 -0
  227. package/dist/test/integration/retention.test.js.map +1 -0
  228. package/dist/test/integration/sends.test.d.ts +1 -0
  229. package/dist/test/integration/sends.test.js +157 -0
  230. package/dist/test/integration/sends.test.js.map +1 -0
  231. package/dist/test/integration/settings.test.d.ts +1 -0
  232. package/dist/test/integration/settings.test.js +45 -0
  233. package/dist/test/integration/settings.test.js.map +1 -0
  234. package/dist/test/integration/setup.test.d.ts +1 -0
  235. package/dist/test/integration/setup.test.js +32 -0
  236. package/dist/test/integration/setup.test.js.map +1 -0
  237. package/dist/test/integration/templates.test.d.ts +1 -0
  238. package/dist/test/integration/templates.test.js +167 -0
  239. package/dist/test/integration/templates.test.js.map +1 -0
  240. package/dist/test/integration/tenant-export.test.d.ts +1 -0
  241. package/dist/test/integration/tenant-export.test.js +85 -0
  242. package/dist/test/integration/tenant-export.test.js.map +1 -0
  243. package/dist/test/unit/attention.test.d.ts +1 -0
  244. package/dist/test/unit/attention.test.js +109 -0
  245. package/dist/test/unit/attention.test.js.map +1 -0
  246. package/dist/test/unit/automation.test.d.ts +1 -0
  247. package/dist/test/unit/automation.test.js +104 -0
  248. package/dist/test/unit/automation.test.js.map +1 -0
  249. package/dist/test/unit/categories.test.d.ts +1 -0
  250. package/dist/test/unit/categories.test.js +38 -0
  251. package/dist/test/unit/categories.test.js.map +1 -0
  252. package/dist/test/unit/engine/conditions.test.d.ts +1 -0
  253. package/dist/test/unit/engine/conditions.test.js +23 -0
  254. package/dist/test/unit/engine/conditions.test.js.map +1 -0
  255. package/dist/test/unit/engine/coverage.test.d.ts +1 -0
  256. package/dist/test/unit/engine/coverage.test.js +106 -0
  257. package/dist/test/unit/engine/coverage.test.js.map +1 -0
  258. package/dist/test/unit/engine/fallback-html.test.d.ts +1 -0
  259. package/dist/test/unit/engine/fallback-html.test.js +78 -0
  260. package/dist/test/unit/engine/fallback-html.test.js.map +1 -0
  261. package/dist/test/unit/engine/paths.test.d.ts +1 -0
  262. package/dist/test/unit/engine/paths.test.js +33 -0
  263. package/dist/test/unit/engine/paths.test.js.map +1 -0
  264. package/dist/test/unit/engine/resolve.test.d.ts +1 -0
  265. package/dist/test/unit/engine/resolve.test.js +157 -0
  266. package/dist/test/unit/engine/resolve.test.js.map +1 -0
  267. package/dist/test/unit/engine/tokens.test.d.ts +1 -0
  268. package/dist/test/unit/engine/tokens.test.js +26 -0
  269. package/dist/test/unit/engine/tokens.test.js.map +1 -0
  270. package/dist/test/unit/events.test.d.ts +1 -0
  271. package/dist/test/unit/events.test.js +13 -0
  272. package/dist/test/unit/events.test.js.map +1 -0
  273. package/dist/test/unit/metrics.test.d.ts +1 -0
  274. package/dist/test/unit/metrics.test.js +108 -0
  275. package/dist/test/unit/metrics.test.js.map +1 -0
  276. package/dist/test/unit/notify.test.d.ts +1 -0
  277. package/dist/test/unit/notify.test.js +49 -0
  278. package/dist/test/unit/notify.test.js.map +1 -0
  279. package/dist/test/unit/period.test.d.ts +1 -0
  280. package/dist/test/unit/period.test.js +44 -0
  281. package/dist/test/unit/period.test.js.map +1 -0
  282. package/dist/test/unit/provider.test.d.ts +1 -0
  283. package/dist/test/unit/provider.test.js +47 -0
  284. package/dist/test/unit/provider.test.js.map +1 -0
  285. package/dist/test/unit/realtime.test.d.ts +1 -0
  286. package/dist/test/unit/realtime.test.js +13 -0
  287. package/dist/test/unit/realtime.test.js.map +1 -0
  288. package/dist/test/unit/render.test.d.ts +1 -0
  289. package/dist/test/unit/render.test.js +23 -0
  290. package/dist/test/unit/render.test.js.map +1 -0
  291. package/dist/test/unit/retention.test.d.ts +1 -0
  292. package/dist/test/unit/retention.test.js +66 -0
  293. package/dist/test/unit/retention.test.js.map +1 -0
  294. package/dist/test/unit/send-batch.test.d.ts +1 -0
  295. package/dist/test/unit/send-batch.test.js +46 -0
  296. package/dist/test/unit/send-batch.test.js.map +1 -0
  297. package/dist/test/unit/send.test.d.ts +1 -0
  298. package/dist/test/unit/send.test.js +120 -0
  299. package/dist/test/unit/send.test.js.map +1 -0
  300. package/dist/test/unit/smoke.test.d.ts +1 -0
  301. package/dist/test/unit/smoke.test.js +10 -0
  302. package/dist/test/unit/smoke.test.js.map +1 -0
  303. package/dist/test/unit/status.test.d.ts +1 -0
  304. package/dist/test/unit/status.test.js +16 -0
  305. package/dist/test/unit/status.test.js.map +1 -0
  306. package/dist/test/unit/templates.test.d.ts +1 -0
  307. package/dist/test/unit/templates.test.js +48 -0
  308. package/dist/test/unit/templates.test.js.map +1 -0
  309. package/dist/test/unit/tenant-export.test.d.ts +1 -0
  310. package/dist/test/unit/tenant-export.test.js +59 -0
  311. package/dist/test/unit/tenant-export.test.js.map +1 -0
  312. package/dist/test/unit/webhook-secret.test.d.ts +1 -0
  313. package/dist/test/unit/webhook-secret.test.js +42 -0
  314. package/dist/test/unit/webhook-secret.test.js.map +1 -0
  315. package/dist/types/blocks.d.ts +2 -0
  316. package/dist/types/blocks.js +3 -0
  317. package/dist/types/blocks.js.map +1 -0
  318. package/dist/types/index.d.ts +4 -0
  319. package/dist/types/index.js +21 -0
  320. package/dist/types/index.js.map +1 -0
  321. package/dist/types/resolved.d.ts +68 -0
  322. package/dist/types/resolved.js +3 -0
  323. package/dist/types/resolved.js.map +1 -0
  324. package/dist/types/sends.d.ts +14 -0
  325. package/dist/types/sends.js +19 -0
  326. package/dist/types/sends.js.map +1 -0
  327. package/dist/types/settings.d.ts +16 -0
  328. package/dist/types/settings.js +11 -0
  329. package/dist/types/settings.js.map +1 -0
  330. package/dist/validation/settings.schema.d.ts +63 -0
  331. package/dist/validation/settings.schema.js +29 -0
  332. package/dist/validation/settings.schema.js.map +1 -0
  333. package/dist/validation/templates.schema.d.ts +248 -0
  334. package/dist/validation/templates.schema.js +126 -0
  335. package/dist/validation/templates.schema.js.map +1 -0
  336. package/frontend-vue/.editorconfig +12 -0
  337. package/frontend-vue/.prettierrc +11 -0
  338. package/frontend-vue/app/components/AttentionCard.vue +78 -0
  339. package/frontend-vue/app/components/CategoriesInput.vue +126 -0
  340. package/frontend-vue/app/components/CategoryInput.vue +67 -0
  341. package/frontend-vue/app/components/Editor.vue +63 -0
  342. package/frontend-vue/app/components/FunnelCard.vue +66 -0
  343. package/frontend-vue/app/components/HtmlPreviewModal.vue +20 -0
  344. package/frontend-vue/app/components/NewTemplateModal.vue +158 -0
  345. package/frontend-vue/app/components/ProviderChip.vue +39 -0
  346. package/frontend-vue/app/components/SendDrawer.vue +298 -0
  347. package/frontend-vue/app/components/TemplateCard.vue +127 -0
  348. package/frontend-vue/app/components/TemplateDrawer.vue +394 -0
  349. package/frontend-vue/app/components/TemplateGallery.vue +112 -0
  350. package/frontend-vue/app/components/TemplatePreviewFrame.vue +103 -0
  351. package/frontend-vue/app/components/TestSendModal.vue +162 -0
  352. package/frontend-vue/app/components/TokenText.vue +26 -0
  353. package/frontend-vue/app/components/blocks/Button.vue +19 -0
  354. package/frontend-vue/app/components/blocks/Code.vue +17 -0
  355. package/frontend-vue/app/components/blocks/Divider.vue +3 -0
  356. package/frontend-vue/app/components/blocks/Footer.vue +22 -0
  357. package/frontend-vue/app/components/blocks/Heading.vue +18 -0
  358. package/frontend-vue/app/components/blocks/Hero.vue +32 -0
  359. package/frontend-vue/app/components/blocks/List.vue +33 -0
  360. package/frontend-vue/app/components/blocks/Paragraph.vue +18 -0
  361. package/frontend-vue/app/components/blocks/Total.vue +18 -0
  362. package/frontend-vue/app/components/editor/EditorBlock.vue +121 -0
  363. package/frontend-vue/app/components/editor/EditorBlockList.vue +44 -0
  364. package/frontend-vue/app/components/editor/EditorBlockSettings.vue +186 -0
  365. package/frontend-vue/app/components/editor/EditorCanvas.vue +91 -0
  366. package/frontend-vue/app/components/editor/EditorConditionFields.vue +88 -0
  367. package/frontend-vue/app/components/editor/EditorDataPanel.vue +128 -0
  368. package/frontend-vue/app/components/editor/EditorDraftBar.vue +35 -0
  369. package/frontend-vue/app/components/editor/EditorIfBlock.vue +120 -0
  370. package/frontend-vue/app/components/editor/EditorMissingLocale.vue +62 -0
  371. package/frontend-vue/app/components/editor/EditorPalette.vue +62 -0
  372. package/frontend-vue/app/components/editor/EditorRail.vue +58 -0
  373. package/frontend-vue/app/components/editor/EditorSubjectSettings.vue +37 -0
  374. package/frontend-vue/app/components/editor/EditorTemplateSettings.vue +87 -0
  375. package/frontend-vue/app/components/editor/EditorTestSendModal.vue +66 -0
  376. package/frontend-vue/app/components/editor/EditorToolbar.vue +155 -0
  377. package/frontend-vue/app/components/editor/EditorVariables.vue +163 -0
  378. package/frontend-vue/app/components/editor/EditorWorkspace.vue +189 -0
  379. package/frontend-vue/app/composables/useEditorContext.ts +44 -0
  380. package/frontend-vue/app/composables/useMailingApi.ts +197 -0
  381. package/frontend-vue/app/composables/useMailingPeriodData.ts +39 -0
  382. package/frontend-vue/app/composables/useTemplateDrawer.ts +90 -0
  383. package/frontend-vue/app/composables/useTemplateEditor.ts +228 -0
  384. package/frontend-vue/app/emails/EmailMailingTemplate.vue +24 -0
  385. package/frontend-vue/app/emails/components/MailingBlocks.vue +117 -0
  386. package/frontend-vue/app/plugins/template-gallery-display.client.ts +14 -0
  387. package/frontend-vue/app/types/component.ts +29 -0
  388. package/frontend-vue/app/types/mailing.ts +365 -0
  389. package/frontend-vue/app/utils/blocks/components.ts +14 -0
  390. package/frontend-vue/app/utils/blocks/dnd.ts +15 -0
  391. package/frontend-vue/app/utils/blocks/factory.ts +100 -0
  392. package/frontend-vue/app/utils/blocks/fields.ts +58 -0
  393. package/frontend-vue/app/utils/blocks/index.ts +5 -0
  394. package/frontend-vue/app/utils/blocks/tree.ts +74 -0
  395. package/frontend-vue/app/utils/categories.ts +54 -0
  396. package/frontend-vue/app/utils/clone.ts +3 -0
  397. package/frontend-vue/app/utils/conditions.ts +50 -0
  398. package/frontend-vue/app/utils/editor-route.ts +18 -0
  399. package/frontend-vue/app/utils/gallery.ts +41 -0
  400. package/frontend-vue/app/utils/locales.ts +41 -0
  401. package/frontend-vue/app/utils/paths.ts +84 -0
  402. package/frontend-vue/app/utils/period.ts +23 -0
  403. package/frontend-vue/app/utils/testData.ts +10 -0
  404. package/frontend-vue/app/utils/timeline.ts +68 -0
  405. package/frontend-vue/app/utils/tokens.ts +24 -0
  406. package/frontend-vue/app/utils/variables.ts +48 -0
  407. package/frontend-vue/dms.email.ts +9 -0
  408. package/frontend-vue/dms.frontend.ts +29 -0
  409. package/frontend-vue/eslint.config.mjs +13 -0
  410. package/frontend-vue/i18n/locales/mailing-en-GB.json +397 -0
  411. package/frontend-vue/i18n/locales/mailing-fr-FR.json +397 -0
  412. package/frontend-vue/package.json +32 -0
  413. package/frontend-vue/pnpm-lock.yaml +6446 -0
  414. package/frontend-vue/pnpm-workspace.yaml +2 -0
  415. package/frontend-vue/tests/block-components.test.ts +14 -0
  416. package/frontend-vue/tests/block-fields.test.ts +23 -0
  417. package/frontend-vue/tests/blocks-factory.test.ts +28 -0
  418. package/frontend-vue/tests/blocks-tree.test.ts +49 -0
  419. package/frontend-vue/tests/categories.test.ts +64 -0
  420. package/frontend-vue/tests/conditions.test.ts +36 -0
  421. package/frontend-vue/tests/dnd.test.ts +11 -0
  422. package/frontend-vue/tests/editor-block-list.test.ts +54 -0
  423. package/frontend-vue/tests/editor-route.test.ts +23 -0
  424. package/frontend-vue/tests/email-render-cases.json +77 -0
  425. package/frontend-vue/tests/gallery.test.ts +39 -0
  426. package/frontend-vue/tests/i18n-editor.test.ts +21 -0
  427. package/frontend-vue/tests/i18n.test.ts +83 -0
  428. package/frontend-vue/tests/locales.test.ts +38 -0
  429. package/frontend-vue/tests/mailing-api.test.ts +10 -0
  430. package/frontend-vue/tests/paths.test.ts +128 -0
  431. package/frontend-vue/tests/period.test.ts +13 -0
  432. package/frontend-vue/tests/smoke.test.ts +8 -0
  433. package/frontend-vue/tests/template-editor.test.ts +65 -0
  434. package/frontend-vue/tests/test-data.test.ts +15 -0
  435. package/frontend-vue/tests/timeline.test.ts +29 -0
  436. package/frontend-vue/tests/tokens.test.ts +12 -0
  437. package/frontend-vue/tests/variables.test.ts +46 -0
  438. package/frontend-vue/vitest.config.ts +10 -0
  439. package/package.json +94 -0
  440. package/src/antelope-modules.ts +48 -0
  441. package/src/automation/email-event-trigger.ts +112 -0
  442. package/src/automation/index.ts +33 -0
  443. package/src/automation/send-template-action.ts +100 -0
  444. package/src/config.ts +15 -0
  445. package/src/constants.ts +42 -0
  446. package/src/crons/index.ts +1 -0
  447. package/src/crons/retention.ts +126 -0
  448. package/src/data/body.ts +14 -0
  449. package/src/data/index.ts +3 -0
  450. package/src/data/sends-table.ts +154 -0
  451. package/src/data/templates-table.ts +202 -0
  452. package/src/data-types/categories-type.ts +29 -0
  453. package/src/data-types/category-type.ts +44 -0
  454. package/src/data-types/index.ts +2 -0
  455. package/src/db/index.ts +2 -0
  456. package/src/db/models/index.ts +4 -0
  457. package/src/db/models/send-events.model.ts +46 -0
  458. package/src/db/models/sends.model.ts +147 -0
  459. package/src/db/models/settings.model.ts +12 -0
  460. package/src/db/models/templates.model.ts +12 -0
  461. package/src/db/tables/index.ts +4 -0
  462. package/src/db/tables/send-events.table.ts +31 -0
  463. package/src/db/tables/sends.table.ts +85 -0
  464. package/src/db/tables/settings.table.ts +49 -0
  465. package/src/db/tables/templates.table.ts +70 -0
  466. package/src/engine/conditions.ts +30 -0
  467. package/src/engine/coverage.ts +67 -0
  468. package/src/engine/fallback-html.ts +116 -0
  469. package/src/engine/index.ts +7 -0
  470. package/src/engine/paths.ts +33 -0
  471. package/src/engine/resolve.ts +178 -0
  472. package/src/engine/tokens.ts +35 -0
  473. package/src/engine/walk.ts +18 -0
  474. package/src/hooks/index.ts +1 -0
  475. package/src/hooks/tenant-export.ts +127 -0
  476. package/src/implementations/dms-mailing/index.ts +2 -0
  477. package/src/index.ts +62 -0
  478. package/src/pages/editor.ts +23 -0
  479. package/src/pages/index.ts +11 -0
  480. package/src/pages/module.ts +9 -0
  481. package/src/pages/overview.ts +175 -0
  482. package/src/pages/sends.ts +174 -0
  483. package/src/pages/settings/form.ts +67 -0
  484. package/src/pages/settings/index.ts +2 -0
  485. package/src/pages/settings/page.ts +34 -0
  486. package/src/pages/templates.ts +141 -0
  487. package/src/permissions.ts +44 -0
  488. package/src/quick-actions.ts +20 -0
  489. package/src/realtime.ts +53 -0
  490. package/src/routes/events.ts +57 -0
  491. package/src/routes/index.ts +7 -0
  492. package/src/routes/metrics.ts +243 -0
  493. package/src/routes/period.ts +46 -0
  494. package/src/routes/provider.ts +31 -0
  495. package/src/routes/sends.ts +215 -0
  496. package/src/routes/settings.ts +66 -0
  497. package/src/routes/templates.ts +256 -0
  498. package/src/services/attention.ts +151 -0
  499. package/src/services/categories.ts +43 -0
  500. package/src/services/events.ts +121 -0
  501. package/src/services/metrics.ts +175 -0
  502. package/src/services/notify.ts +90 -0
  503. package/src/services/permissions.ts +42 -0
  504. package/src/services/provider.ts +46 -0
  505. package/src/services/render.ts +33 -0
  506. package/src/services/send.ts +409 -0
  507. package/src/services/settings.ts +74 -0
  508. package/src/services/status.ts +16 -0
  509. package/src/services/templates.ts +157 -0
  510. package/src/test/antelope.test.ts +188 -0
  511. package/src/test/helpers/http.ts +23 -0
  512. package/src/test/helpers/owner.ts +37 -0
  513. package/src/test/integration/events.test.ts +136 -0
  514. package/src/test/integration/retention.test.ts +363 -0
  515. package/src/test/integration/sends.test.ts +200 -0
  516. package/src/test/integration/settings.test.ts +50 -0
  517. package/src/test/integration/setup.test.ts +35 -0
  518. package/src/test/integration/templates.test.ts +229 -0
  519. package/src/test/integration/tenant-export.test.ts +125 -0
  520. package/src/test/unit/attention.test.ts +129 -0
  521. package/src/test/unit/automation.test.ts +130 -0
  522. package/src/test/unit/categories.test.ts +51 -0
  523. package/src/test/unit/engine/conditions.test.ts +35 -0
  524. package/src/test/unit/engine/coverage.test.ts +118 -0
  525. package/src/test/unit/engine/fallback-html.test.ts +86 -0
  526. package/src/test/unit/engine/paths.test.ts +39 -0
  527. package/src/test/unit/engine/resolve.test.ts +176 -0
  528. package/src/test/unit/engine/tokens.test.ts +26 -0
  529. package/src/test/unit/events.test.ts +19 -0
  530. package/src/test/unit/metrics.test.ts +170 -0
  531. package/src/test/unit/notify.test.ts +71 -0
  532. package/src/test/unit/period.test.ts +64 -0
  533. package/src/test/unit/provider.test.ts +57 -0
  534. package/src/test/unit/realtime.test.ts +11 -0
  535. package/src/test/unit/render.test.ts +23 -0
  536. package/src/test/unit/retention.test.ts +91 -0
  537. package/src/test/unit/send-batch.test.ts +72 -0
  538. package/src/test/unit/send.test.ts +161 -0
  539. package/src/test/unit/smoke.test.ts +8 -0
  540. package/src/test/unit/status.test.ts +14 -0
  541. package/src/test/unit/templates.test.ts +52 -0
  542. package/src/test/unit/tenant-export.test.ts +88 -0
  543. package/src/test/unit/webhook-secret.test.ts +50 -0
  544. package/src/types/blocks.ts +31 -0
  545. package/src/types/index.ts +4 -0
  546. package/src/types/resolved.ts +89 -0
  547. package/src/types/sends.ts +44 -0
  548. package/src/types/settings.ts +24 -0
  549. package/src/validation/settings.schema.ts +33 -0
  550. package/src/validation/templates.schema.ts +145 -0
@@ -0,0 +1,50 @@
1
+ import type { Condition, ConditionOperator } from '../types/mailing'
2
+
3
+ export const OPERATORS: ConditionOperator[] = [
4
+ 'truthy',
5
+ 'falsy',
6
+ 'eq',
7
+ 'ne',
8
+ 'gt',
9
+ 'lt',
10
+ ]
11
+ export const OPERATORS_WITH_VALUE: ConditionOperator[] = [
12
+ 'eq',
13
+ 'ne',
14
+ 'gt',
15
+ 'lt',
16
+ ]
17
+
18
+ const OPERATOR_LABEL_PREFIX = 'dms_mailing.editor.operators'
19
+ const EMPTY_VALUE_PLACEHOLDER = '…'
20
+
21
+ const quote = (value: string): string =>
22
+ value !== '' && !Number.isNaN(Number(value)) ? value : `"${value}"`
23
+
24
+ type ConditionCompiler = (condition: Condition) => string
25
+
26
+ const COMPILERS: Record<ConditionOperator, ConditionCompiler> = {
27
+ truthy: (condition) => `{{#if ${condition.path}}}`,
28
+ falsy: (condition) => `{{#unless ${condition.path}}}`,
29
+ eq: (condition) => `{{#if (eq ${condition.path} ${quote(condition.value)})}}`,
30
+ ne: (condition) =>
31
+ `{{#unless (eq ${condition.path} ${quote(condition.value)})}}`,
32
+ gt: (condition) => `{{#if (gt ${condition.path} ${quote(condition.value)})}}`,
33
+ lt: (condition) => `{{#if (lt ${condition.path} ${quote(condition.value)})}}`,
34
+ }
35
+
36
+ export function compileCondition(condition: Condition): string {
37
+ return COMPILERS[condition.operator](condition)
38
+ }
39
+
40
+ export type OperatorTranslator = (key: string) => string
41
+
42
+ export function describeCondition(
43
+ condition: Condition,
44
+ t: OperatorTranslator,
45
+ ): string {
46
+ const label = t(`${OPERATOR_LABEL_PREFIX}.${condition.operator}`)
47
+ if (!OPERATORS_WITH_VALUE.includes(condition.operator))
48
+ return `${condition.path} ${label}`
49
+ return `${condition.path} ${label} ${condition.value || EMPTY_VALUE_PLACEHOLDER}`
50
+ }
@@ -0,0 +1,18 @@
1
+ export const EDITOR_PATH = '/modules/mailing/editor'
2
+
3
+ export interface EditorRouteQuery extends Record<string, string | undefined> {
4
+ id: string
5
+ locale?: string
6
+ }
7
+
8
+ export interface EditorRoute {
9
+ path: string
10
+ query: EditorRouteQuery
11
+ }
12
+
13
+ export function editorRoute(templateId: string, locale?: string): EditorRoute {
14
+ return {
15
+ path: EDITOR_PATH,
16
+ query: locale ? { id: templateId, locale } : { id: templateId },
17
+ }
18
+ }
@@ -0,0 +1,41 @@
1
+ import type { TemplateCategory } from '../types/mailing'
2
+
3
+ export interface CategoryGroup<T> {
4
+ category: TemplateCategory
5
+ items: T[]
6
+ }
7
+
8
+ export interface CategorizedRow {
9
+ category?: string
10
+ }
11
+
12
+ export const UNCATEGORISED_GROUP_ID = 'uncategorised'
13
+
14
+ const UNCATEGORISED_ICON = 'i-ph-folder-dashed'
15
+
16
+ /**
17
+ * Groups template rows by the categories declared in the settings, keeping the
18
+ * declared order. Rows with no category, or one the settings no longer declare,
19
+ * land in a trailing group the caller labels — it is the only heading here that
20
+ * needs translating. Empty groups are dropped.
21
+ */
22
+ export function groupByCategory<T extends CategorizedRow>(
23
+ rows: T[],
24
+ categories: TemplateCategory[],
25
+ uncategorisedLabel: string,
26
+ ): CategoryGroup<T>[] {
27
+ const known = categories.map((category) => ({
28
+ category,
29
+ items: rows.filter((row) => row.category === category.id),
30
+ }))
31
+ const knownIds = new Set(categories.map((category) => category.id))
32
+ const uncategorised = {
33
+ category: {
34
+ id: UNCATEGORISED_GROUP_ID,
35
+ label: uncategorisedLabel,
36
+ icon: UNCATEGORISED_ICON,
37
+ },
38
+ items: rows.filter((row) => !row.category || !knownIds.has(row.category)),
39
+ }
40
+ return [...known, uncategorised].filter((group) => group.items.length > 0)
41
+ }
@@ -0,0 +1,41 @@
1
+ export interface LocalePresence {
2
+ code: string
3
+ present: boolean
4
+ }
5
+
6
+ const LOCALE_LIST_SEPARATOR = ','
7
+
8
+ /** Reads the `locales` column, whose format is a comma-separated code list. */
9
+ export function parseLocaleList(value: string | undefined): string[] {
10
+ return (value ?? '')
11
+ .split(LOCALE_LIST_SEPARATOR)
12
+ .map((code) => code.trim())
13
+ .filter(Boolean)
14
+ }
15
+
16
+ /**
17
+ * Coverage of `codes` (the locales the tenant offers, in its own order) by
18
+ * `present` (the locales something actually has).
19
+ */
20
+ export function localeCoverage(
21
+ present: string[],
22
+ codes: string[],
23
+ ): LocalePresence[] {
24
+ const owned = new Set(present)
25
+ return codes.map((code) => ({ code, present: owned.has(code) }))
26
+ }
27
+
28
+ /**
29
+ * Badge list for a template row. A row written before the `locales` column
30
+ * existed carries no information, which is not the same as "no locale": it
31
+ * answers an empty list so the card shows nothing rather than claiming every
32
+ * locale is missing.
33
+ */
34
+ export function localeBadges(
35
+ stored: string | undefined,
36
+ codes: string[],
37
+ ): LocalePresence[] {
38
+ const present = parseLocaleList(stored)
39
+ if (!present.length) return []
40
+ return localeCoverage(present, codes)
41
+ }
@@ -0,0 +1,84 @@
1
+ import type { Block, LocaleContent, TemplateContent } from '../types/mailing'
2
+ import { splitTokens } from './tokens'
3
+
4
+ const TOKEN_FIELDS = ['text', 'value', 'href', 'label', 'alt', 'imageUrl']
5
+
6
+ function tokenPaths(block: Block): string[] {
7
+ const record = block as unknown as Record<string, unknown>
8
+ return TOKEN_FIELDS.flatMap((key) => {
9
+ const value = record[key]
10
+ if (typeof value !== 'string') return []
11
+ return splitTokens(value)
12
+ .filter((part) => part.kind === 'token')
13
+ .map((part) => part.value)
14
+ })
15
+ }
16
+
17
+ function branchPaths(block: Block): string[] {
18
+ if (block.type === 'if') {
19
+ return [
20
+ block.condition.path,
21
+ ...pathsInBlocks(block.children),
22
+ ...pathsInBlocks(block.elseChildren ?? []),
23
+ ]
24
+ }
25
+ return block.type === 'list' ? [block.source] : []
26
+ }
27
+
28
+ export function pathsInBlocks(blocks: Block[]): string[] {
29
+ return blocks.flatMap((block) => [
30
+ ...tokenPaths(block),
31
+ ...(block.visibleIf ? [block.visibleIf.path] : []),
32
+ ...branchPaths(block),
33
+ ])
34
+ }
35
+
36
+ export function collectPaths(
37
+ declared: string[],
38
+ blocks: Block[],
39
+ current?: string,
40
+ ): string[] {
41
+ const all = [
42
+ ...declared,
43
+ ...pathsInBlocks(blocks),
44
+ ...(current ? [current] : []),
45
+ ]
46
+ return [...new Set(all.filter(Boolean))].sort()
47
+ }
48
+
49
+ /**
50
+ * Paths the runtime fills in itself, so an author never declares them. The
51
+ * backend drops these before answering; this list mirrors it.
52
+ */
53
+ const SYSTEM_PATHS = ['unsubscribeUrl', 'preferencesUrl']
54
+
55
+ function textPaths(text: string): string[] {
56
+ return splitTokens(text)
57
+ .filter((part) => part.kind === 'token')
58
+ .map((part) => part.value)
59
+ }
60
+
61
+ function pathsInLocale(locale: LocaleContent): string[] {
62
+ return [
63
+ ...textPaths(locale.subject),
64
+ ...textPaths(locale.preheader),
65
+ ...pathsInBlocks(locale.blocks),
66
+ ]
67
+ }
68
+
69
+ /**
70
+ * Every variable path the content references, across all of its locales — the
71
+ * live equivalent of what the backend returns as `detectedVariables`.
72
+ *
73
+ * The panel needs this because the server's list only refreshes on save: until
74
+ * then a path wired into a display condition reads as unused, which is exactly
75
+ * the moment an author looks at it.
76
+ */
77
+ export function collectContentPaths(
78
+ content: Pick<TemplateContent, 'locales'>,
79
+ ): string[] {
80
+ const paths = Object.values(content.locales ?? {}).flatMap(pathsInLocale)
81
+ return [
82
+ ...new Set(paths.filter((path) => path && !SYSTEM_PATHS.includes(path))),
83
+ ].sort()
84
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The DMS period selector owns the window: `PeriodState` and its `PeriodRange`
3
+ * come from the host layer and reach us through the workspace auto-imports.
4
+ * Declaring a second `PeriodRange` here put two entries of the same name in
5
+ * that registry, and the host's won -- ours was silently ignored.
6
+ */
7
+ export type PeriodLike = Pick<PeriodState, 'range' | 'compareRange'>
8
+
9
+ /**
10
+ * Serialises a period-selector state into the from/to (+ comparison) query the
11
+ * mailing metrics routes read.
12
+ */
13
+ export function periodQuery(period: PeriodLike): Record<string, string> {
14
+ const query: Record<string, string> = {
15
+ from: period.range.from.toISOString(),
16
+ to: period.range.to.toISOString(),
17
+ }
18
+ if (period.compareRange) {
19
+ query.compareFrom = period.compareRange.from.toISOString()
20
+ query.compareTo = period.compareRange.to.toISOString()
21
+ }
22
+ return query
23
+ }
@@ -0,0 +1,10 @@
1
+ export function parseTestData(source: string): Record<string, unknown> | null {
2
+ try {
3
+ const parsed: unknown = JSON.parse(source)
4
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
5
+ return null
6
+ return parsed as Record<string, unknown>
7
+ } catch {
8
+ return null
9
+ }
10
+ }
@@ -0,0 +1,68 @@
1
+ import type { SendEventRow, SendStatus } from '../types/mailing'
2
+
3
+ export type TimelineTone =
4
+ 'neutral' | 'success' | 'primary' | 'error' | 'warning'
5
+
6
+ export interface TimelineMeta {
7
+ icon: string
8
+ tone: TimelineTone
9
+ }
10
+
11
+ export interface TimelineItem {
12
+ id: string
13
+ type: SendStatus
14
+ icon: string
15
+ tone: TimelineTone
16
+ description: string
17
+ at: string
18
+ }
19
+
20
+ export const TIMELINE_META: Record<SendStatus, TimelineMeta> = {
21
+ queued: { icon: 'i-ph-clock', tone: 'neutral' },
22
+ sent: { icon: 'i-ph-paper-plane-tilt', tone: 'primary' },
23
+ delivered: { icon: 'i-ph-check', tone: 'success' },
24
+ opened: { icon: 'i-ph-eye', tone: 'primary' },
25
+ clicked: { icon: 'i-ph-cursor-click', tone: 'primary' },
26
+ bounced: { icon: 'i-ph-warning', tone: 'error' },
27
+ spam: { icon: 'i-ph-prohibit', tone: 'warning' },
28
+ failed: { icon: 'i-ph-warning', tone: 'error' },
29
+ unsubscribed: { icon: 'i-ph-user-minus', tone: 'warning' },
30
+ }
31
+
32
+ const DESCRIPTION_KEYS = ['reason', 'url', 'userAgent']
33
+
34
+ interface EventDetails {
35
+ [key: string]: unknown
36
+ }
37
+
38
+ function parseDetails(raw: string): EventDetails {
39
+ try {
40
+ const parsed: unknown = raw ? JSON.parse(raw) : {}
41
+ return typeof parsed === 'object' && parsed !== null
42
+ ? (parsed as EventDetails)
43
+ : {}
44
+ } catch {
45
+ return {}
46
+ }
47
+ }
48
+
49
+ function describe(details: EventDetails): string {
50
+ const key = DESCRIPTION_KEYS.find((candidate) => details[candidate])
51
+ return key ? String(details[key]) : ''
52
+ }
53
+
54
+ /**
55
+ * Orders a send's events oldest-first and resolves the icon, tone and one-line
56
+ * description each row shows.
57
+ */
58
+ export function buildTimeline(events: SendEventRow[]): TimelineItem[] {
59
+ return [...events]
60
+ .sort((a, b) => new Date(a.at).getTime() - new Date(b.at).getTime())
61
+ .map((event) => ({
62
+ id: event._id,
63
+ type: event.type,
64
+ at: event.at,
65
+ description: describe(parseDetails(event.json_details)),
66
+ ...TIMELINE_META[event.type],
67
+ }))
68
+ }
@@ -0,0 +1,24 @@
1
+ export type TextPartKind = 'text' | 'token'
2
+
3
+ export interface TextPart {
4
+ kind: TextPartKind
5
+ value: string
6
+ }
7
+
8
+ const TOKEN_SPLIT = /(\{\{[^}]+\}\})/g
9
+ const TOKEN_OPEN = '{{'
10
+ const TOKEN_MARKER_LENGTH = 2
11
+
12
+ export function splitTokens(text: string): TextPart[] {
13
+ return text
14
+ .split(TOKEN_SPLIT)
15
+ .filter(Boolean)
16
+ .map((part) =>
17
+ part.startsWith(TOKEN_OPEN)
18
+ ? {
19
+ kind: 'token' as const,
20
+ value: part.slice(TOKEN_MARKER_LENGTH, -TOKEN_MARKER_LENGTH).trim(),
21
+ }
22
+ : { kind: 'text' as const, value: part },
23
+ )
24
+ }
@@ -0,0 +1,48 @@
1
+ import type { VariableDefinition, VariableType } from '../types/mailing'
2
+
3
+ /**
4
+ * `declared` — the content uses it and someone typed it;
5
+ * `detected` — the content uses it, nobody declared it;
6
+ * `unused` — declared but the content no longer references it.
7
+ */
8
+ export type VariableUsage = 'declared' | 'detected' | 'unused'
9
+
10
+ export interface MergedVariable {
11
+ path: string
12
+ type: VariableType
13
+ required: boolean
14
+ usage: VariableUsage
15
+ }
16
+
17
+ const DEFAULT_TYPE: VariableType = 'string'
18
+
19
+ /**
20
+ * One list for the variables panel: every path the content references, in the
21
+ * order it was detected, enriched by its declaration when there is one, then
22
+ * the declarations the content has stopped using.
23
+ */
24
+ export function mergeVariables(
25
+ detected: string[],
26
+ declared: VariableDefinition[],
27
+ ): MergedVariable[] {
28
+ const byPath = new Map(declared.map((entry) => [entry.path, entry]))
29
+ const used = detected.map((path) => {
30
+ const declaration = byPath.get(path)
31
+ return {
32
+ path,
33
+ type: declaration?.type ?? DEFAULT_TYPE,
34
+ required: declaration?.required ?? false,
35
+ usage: declaration ? ('declared' as const) : ('detected' as const),
36
+ }
37
+ })
38
+ const seen = new Set(detected)
39
+ const unused = declared
40
+ .filter((entry) => !seen.has(entry.path))
41
+ .map((entry) => ({
42
+ path: entry.path,
43
+ type: entry.type,
44
+ required: entry.required,
45
+ usage: 'unused' as const,
46
+ }))
47
+ return [...used, ...unused]
48
+ }
@@ -0,0 +1,9 @@
1
+ import type { Component } from 'vue'
2
+
3
+ interface EmailModule {
4
+ default: Component
5
+ }
6
+
7
+ export const serverEmailTemplates = import.meta.glob<EmailModule>(
8
+ './app/emails/*.vue',
9
+ )
@@ -0,0 +1,29 @@
1
+ import { defineAsyncComponent, type Component } from 'vue'
2
+ import type { DmsFrontendModule } from '#dms/frontend-module'
3
+ import templateGallery from './app/plugins/template-gallery-display.client'
4
+
5
+ interface VueModule {
6
+ default: Component
7
+ }
8
+
9
+ const components = import.meta.glob<VueModule>('./app/components/**/*.vue')
10
+
11
+ const frontendModule: DmsFrontendModule = {
12
+ setup(sdk) {
13
+ Object.entries(components)
14
+ .sort()
15
+ .forEach(([path, loader]) => {
16
+ const name = path
17
+ .split('/')
18
+ .at(-1)!
19
+ .replace(/\.vue$/, '')
20
+ const prefix = path.includes('/components/blocks/')
21
+ ? 'DmsMailingBlock'
22
+ : 'DmsMailing'
23
+ sdk.registerComponent(`${prefix}${name}`, defineAsyncComponent(loader))
24
+ })
25
+ sdk.registerPlugin(templateGallery, { clientOnly: true })
26
+ },
27
+ }
28
+
29
+ export default frontendModule
@@ -0,0 +1,13 @@
1
+ import vue from 'eslint-plugin-vue'
2
+ import ts from 'typescript-eslint'
3
+
4
+ export default [
5
+ { ignores: ['node_modules/**'] },
6
+ ...vue.configs['flat/essential'],
7
+ {
8
+ files: ['**/*.ts', '**/*.vue'],
9
+ languageOptions: { parserOptions: { parser: ts.parser } },
10
+ rules: { 'vue/multi-word-component-names': 'off' },
11
+ },
12
+ { files: ['**/*.ts'], languageOptions: { parser: ts.parser } },
13
+ ]