@beechcms/api 0.6.0-preview.2 → 0.6.0-preview.4

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 (388) hide show
  1. package/README.md +4 -4
  2. package/assets/dashboard/BeechLogo.svg +18 -18
  3. package/assets/dashboard/BeechLogoLIght.svg +48 -48
  4. package/assets/dashboard/assets/chart-BwlCt-IA.js +39 -0
  5. package/assets/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  6. package/assets/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  7. package/assets/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  8. package/assets/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  9. package/assets/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  10. package/assets/dashboard/assets/index-BcVN-DNy.js +642 -0
  11. package/assets/dashboard/assets/index-Cf1yRCqG.css +1 -0
  12. package/assets/dashboard/assets/lora-cyrillic-ext-wght-normal-YcVxMP-u.woff2 +0 -0
  13. package/assets/dashboard/assets/lora-cyrillic-wght-normal-UWPuZLjM.woff2 +0 -0
  14. package/assets/dashboard/assets/lora-latin-ext-wght-normal-C2Wlntb9.woff2 +0 -0
  15. package/assets/dashboard/assets/lora-latin-wght-normal-BiLcIKcI.woff2 +0 -0
  16. package/assets/dashboard/assets/lora-math-wght-normal-sOy7rEfW.woff2 +0 -0
  17. package/assets/dashboard/assets/lora-symbols-wght-normal-DQ5VrUkH.woff2 +0 -0
  18. package/assets/dashboard/assets/lora-vietnamese-wght-normal-CfJ7gtf3.woff2 +0 -0
  19. package/assets/dashboard/assets/pie-chart-recharts-BMWpjFzf.js +1 -0
  20. package/assets/dashboard/assets/timeseries-chart-recharts-8_GwAFR0.js +1 -0
  21. package/assets/dashboard/beechLogoDark.svg +48 -48
  22. package/assets/dashboard/index.html +18 -18
  23. package/assets/dashboard/noResult.svg +42 -42
  24. package/assets/dashboard/sol.svg +3 -3
  25. package/assets/dashboard/undraw_enter_nwx3.svg +36 -36
  26. package/dist/auth/__fixtures__/in-memory-hash-provider.d.ts +5 -0
  27. package/dist/auth/__fixtures__/static-token-service.d.ts +6 -0
  28. package/dist/auth/bcrypt-hash-provider.d.ts +8 -0
  29. package/dist/auth/constants.d.ts +10 -0
  30. package/dist/auth/jose-token-service.d.ts +14 -0
  31. package/dist/auth/login.d.ts +32 -0
  32. package/dist/auth/refresh.d.ts +1 -0
  33. package/dist/factory.d.ts +45 -0
  34. package/dist/features/automations/action-executors/create-entry.executor.d.ts +7 -0
  35. package/dist/features/automations/action-executors/edit-field.executor.d.ts +7 -0
  36. package/dist/features/automations/action-executors/index.d.ts +13 -0
  37. package/dist/features/automations/action-executors/send-mail.executor.d.ts +7 -0
  38. package/dist/features/automations/action-executors/set-variable.executor.d.ts +15 -0
  39. package/dist/features/automations/action-executors/webhook.executor.d.ts +7 -0
  40. package/dist/features/automations/automation-runner.d.ts +13 -0
  41. package/dist/features/automations/automation-runner.utils.d.ts +3 -0
  42. package/dist/features/automations/automations.handler.d.ts +7 -0
  43. package/dist/features/automations/automations.schema.d.ts +191 -0
  44. package/dist/features/automations/context-resolver.d.ts +8 -0
  45. package/dist/features/automations/cron-runner.d.ts +9 -0
  46. package/dist/features/automations/cron-runner.utils.d.ts +1 -0
  47. package/dist/features/automations/filter-translation.d.ts +9 -0
  48. package/dist/features/automations/index.d.ts +7 -0
  49. package/dist/features/automations/template-grammar.d.ts +73 -0
  50. package/dist/features/automations/var-access-resolver.d.ts +6 -0
  51. package/dist/features/automations/when-evaluator.d.ts +3 -0
  52. package/dist/features/automations/when-pushdown.d.ts +2 -0
  53. package/dist/features/backrefs/backrefs.handler.d.ts +3 -0
  54. package/dist/features/backrefs/d1-backref.repository.d.ts +17 -0
  55. package/dist/features/backrefs/index.d.ts +1 -0
  56. package/dist/features/content/constants.d.ts +12 -0
  57. package/dist/features/content/handlers/bulk.handler.d.ts +3 -0
  58. package/dist/features/content/handlers/create.d.ts +3 -0
  59. package/dist/features/content/handlers/delete.d.ts +3 -0
  60. package/dist/features/content/handlers/facets.d.ts +3 -0
  61. package/dist/features/content/handlers/get.d.ts +4 -0
  62. package/dist/features/content/handlers/helpers.d.ts +12 -0
  63. package/dist/features/content/handlers/list.d.ts +3 -0
  64. package/dist/features/content/handlers/update.d.ts +3 -0
  65. package/dist/features/content/index.d.ts +4 -0
  66. package/dist/features/dashboard-layout/dashboard-layout.handler.d.ts +7 -0
  67. package/dist/features/dashboard-layout/index.d.ts +1 -0
  68. package/dist/features/draft/draft.handler.d.ts +4 -0
  69. package/dist/features/draft/draft.middleware.d.ts +9 -0
  70. package/dist/features/draft/index.d.ts +1 -0
  71. package/{src/features/email/email.provider.ts → dist/features/email/email.provider.d.ts} +19 -24
  72. package/dist/features/email/email.service.d.ts +13 -0
  73. package/dist/features/email/email.types.d.ts +94 -0
  74. package/{src/features/email/index.ts → dist/features/email/index.d.ts} +3 -16
  75. package/dist/features/email/providers/resend.d.ts +36 -0
  76. package/dist/features/email/providers/smtp.d.ts +11 -0
  77. package/dist/features/email/templates/automation-mail.d.ts +8 -0
  78. package/dist/features/email/templates/password-changed.d.ts +15 -0
  79. package/dist/features/email/templates/password-reset.d.ts +18 -0
  80. package/dist/features/email/templates/shell.d.ts +58 -0
  81. package/dist/features/notifications/index.d.ts +1 -0
  82. package/dist/features/notifications/notifications.handler.d.ts +15 -0
  83. package/dist/features/password-reset/index.d.ts +6 -0
  84. package/dist/features/password-reset/request.d.ts +10 -0
  85. package/dist/features/password-reset/reset.d.ts +9 -0
  86. package/dist/features/rotate-field/index.d.ts +1 -0
  87. package/dist/features/rotate-field/rotate-field.handler.d.ts +7 -0
  88. package/dist/features/rotate-field/rotate-field.schema.d.ts +11 -0
  89. package/dist/features/schema/schema.handler.d.ts +7 -0
  90. package/dist/features/seeds/index.d.ts +1 -0
  91. package/dist/features/seeds/seeds.handler.d.ts +6 -0
  92. package/dist/features/settings/settings.handler.d.ts +7 -0
  93. package/dist/features/setup/index.d.ts +7 -0
  94. package/dist/features/stats/index.d.ts +1 -0
  95. package/dist/features/stats/stats.handler.d.ts +7 -0
  96. package/dist/features/webhooks/index.d.ts +3 -0
  97. package/dist/index.d.ts +7 -0
  98. package/dist/index.js +9732 -0
  99. package/dist/media-utils.d.ts +25 -0
  100. package/dist/middleware/auth-providers.middleware.d.ts +8 -0
  101. package/dist/middleware/observability.middleware.d.ts +20 -0
  102. package/dist/middleware/queue.middleware.d.ts +11 -0
  103. package/dist/middleware/rate-limit.middleware.d.ts +9 -0
  104. package/dist/middleware/repository.middleware.d.ts +34 -0
  105. package/dist/middleware/seed-registry.middleware.d.ts +10 -0
  106. package/dist/middleware/storage.middleware.d.ts +7 -0
  107. package/dist/middleware.d.ts +11 -0
  108. package/dist/public/access-policy.d.ts +12 -0
  109. package/dist/public/api-key-middleware.d.ts +6 -0
  110. package/dist/public/cache-utils.d.ts +10 -0
  111. package/dist/public/entry-projection.d.ts +2 -0
  112. package/dist/public/idempotency.d.ts +9 -0
  113. package/dist/public/index.d.ts +11 -0
  114. package/dist/public/problem-details.d.ts +31 -0
  115. package/dist/public/public-add.d.ts +3 -0
  116. package/dist/public/public-edit.d.ts +5 -0
  117. package/dist/public/public-errors.d.ts +13 -0
  118. package/dist/public/public-read.d.ts +3 -0
  119. package/dist/public/public-routes.d.ts +3 -0
  120. package/dist/public/query-builder.d.ts +28 -0
  121. package/dist/public/rate-limit-middleware.d.ts +3 -0
  122. package/dist/public/read-list.d.ts +26 -0
  123. package/dist/public/read-single.d.ts +22 -0
  124. package/dist/public/response-builder.d.ts +22 -0
  125. package/dist/public/sanitize.d.ts +23 -0
  126. package/{src/public/slug-utils.ts → dist/public/slug-utils.d.ts} +3 -10
  127. package/dist/rate-limit/cloudflare-rate-limiter.d.ts +6 -0
  128. package/dist/rate-limit/in-memory-rate-limiter.d.ts +7 -0
  129. package/dist/rate-limit/no-op-rate-limiter.d.ts +4 -0
  130. package/dist/search-utils.d.ts +54 -0
  131. package/dist/search.d.ts +6 -0
  132. package/dist/shared/apply-policies.d.ts +10 -0
  133. package/dist/shared/automations.repository.d1.d.ts +12 -0
  134. package/dist/shared/background-notification-service.d.ts +18 -0
  135. package/dist/shared/base.repository.d1.d.ts +18 -0
  136. package/dist/shared/cloudflare-queue-service.d.ts +11 -0
  137. package/dist/shared/content-utils.d.ts +21 -0
  138. package/dist/shared/content.repository.d1.d.ts +68 -0
  139. package/dist/shared/d1-activity-log.repository.d.ts +13 -0
  140. package/dist/shared/d1-activity-logger.d.ts +20 -0
  141. package/dist/shared/d1-analytics.repository.d.ts +20 -0
  142. package/dist/shared/d1-content-scan.repository.d.ts +7 -0
  143. package/dist/shared/d1-notification.repository.d.ts +17 -0
  144. package/dist/shared/d1-password-reset-token.repository.d.ts +10 -0
  145. package/dist/shared/d1-search.repository.d.ts +14 -0
  146. package/dist/shared/d1-session.repository.d.ts +12 -0
  147. package/dist/shared/d1-setup-checklist.repository.d.ts +12 -0
  148. package/dist/shared/d1-user.repository.d.ts +18 -0
  149. package/dist/shared/d1-widget.repository.d.ts +31 -0
  150. package/dist/shared/dashboard-layout.repository.d1.d.ts +9 -0
  151. package/dist/shared/demo-data-sql.d.ts +1 -0
  152. package/dist/shared/demo-data.repository.d1.d.ts +6 -0
  153. package/dist/shared/execution-context-scheduler.d.ts +6 -0
  154. package/dist/shared/fixed-clock.d.ts +13 -0
  155. package/dist/shared/fts-sync.d.ts +1 -0
  156. package/dist/shared/idempotency.repository.d1.d.ts +16 -0
  157. package/dist/shared/in-memory-activity-logger.d.ts +11 -0
  158. package/dist/shared/in-memory-notification-service.d.ts +11 -0
  159. package/dist/shared/in-memory-queue-service.d.ts +16 -0
  160. package/dist/shared/in-memory-seed.repository.d.ts +18 -0
  161. package/dist/shared/media.repository.d1.d.ts +35 -0
  162. package/dist/shared/qstash-notification-service.d.ts +18 -0
  163. package/dist/shared/query-utils.d.ts +36 -0
  164. package/dist/shared/queue-consumer.d.ts +9 -0
  165. package/dist/shared/request-utils.d.ts +13 -0
  166. package/dist/shared/schema-mutator.d1.d.ts +12 -0
  167. package/dist/shared/seed-layout.repository.d1.d.ts +9 -0
  168. package/dist/shared/seed-registry-cache.d.ts +12 -0
  169. package/dist/shared/seed.repository.d1.d.ts +13 -0
  170. package/dist/shared/sequential-id-generator.d.ts +13 -0
  171. package/dist/shared/site-settings.repository.d1.d.ts +7 -0
  172. package/dist/shared/storage/factory.d.ts +8 -0
  173. package/dist/shared/storage/s3-bucket.d.ts +43 -0
  174. package/dist/shared/storage-utils.d.ts +7 -0
  175. package/dist/shared/system-stats.repository.d1.d.ts +25 -0
  176. package/dist/types.d.ts +87 -0
  177. package/dist/upload.d.ts +16 -0
  178. package/dist/widget.d.ts +7 -0
  179. package/migrations/0000_v040_base.sql +265 -265
  180. package/migrations/0029_automations.sql +14 -14
  181. package/migrations/0030_test_seeds.sql +125 -125
  182. package/package.json +28 -18
  183. package/assets/dashboard/assets/chart-BrJFdsGZ.js +0 -39
  184. package/assets/dashboard/assets/index-CIaoMxuB.css +0 -1
  185. package/assets/dashboard/assets/index-wTKgygsg.js +0 -636
  186. package/assets/dashboard/assets/pie-chart-recharts-BEaE1FOE.js +0 -1
  187. package/assets/dashboard/assets/timeseries-chart-recharts-DmWM6imF.js +0 -1
  188. package/src/auth/__fixtures__/in-memory-hash-provider.ts +0 -17
  189. package/src/auth/__fixtures__/static-token-service.ts +0 -22
  190. package/src/auth/bcrypt-hash-provider.ts +0 -24
  191. package/src/auth/constants.ts +0 -14
  192. package/src/auth/generate-refresh-token.test.ts +0 -23
  193. package/src/auth/hash-provider.test.ts +0 -55
  194. package/src/auth/jose-token-service.ts +0 -62
  195. package/src/auth/jwt-claims-passthrough.test.ts +0 -87
  196. package/src/auth/login.test.ts +0 -100
  197. package/src/auth/login.ts +0 -78
  198. package/src/auth/refresh.ts +0 -11
  199. package/src/auth/token-service.test.ts +0 -90
  200. package/src/factory.ts +0 -370
  201. package/src/features/automations/__tests__/action-executors.test.ts +0 -255
  202. package/src/features/automations/__tests__/automation-runner.test.ts +0 -196
  203. package/src/features/automations/__tests__/automation-runner.utils.test.ts +0 -60
  204. package/src/features/automations/__tests__/automations.handler.test.ts +0 -264
  205. package/src/features/automations/__tests__/automations.repository.test.ts +0 -163
  206. package/src/features/automations/__tests__/automations.schema.test.ts +0 -224
  207. package/src/features/automations/__tests__/context-resolver.test.ts +0 -126
  208. package/src/features/automations/__tests__/cron-runner.test.ts +0 -267
  209. package/src/features/automations/__tests__/cron-runner.utils.test.ts +0 -144
  210. package/src/features/automations/__tests__/set-variable.executor.test.ts +0 -383
  211. package/src/features/automations/__tests__/template-grammar.test.ts +0 -308
  212. package/src/features/automations/__tests__/webhook.executor.test.ts +0 -142
  213. package/src/features/automations/__tests__/when-evaluator.test.ts +0 -274
  214. package/src/features/automations/__tests__/when-pushdown.test.ts +0 -281
  215. package/src/features/automations/action-executors/create-entry.executor.ts +0 -27
  216. package/src/features/automations/action-executors/edit-field.executor.ts +0 -26
  217. package/src/features/automations/action-executors/index.ts +0 -37
  218. package/src/features/automations/action-executors/send-mail.executor.ts +0 -53
  219. package/src/features/automations/action-executors/set-variable.executor.ts +0 -159
  220. package/src/features/automations/action-executors/webhook.executor.ts +0 -67
  221. package/src/features/automations/automation-runner.ts +0 -85
  222. package/src/features/automations/automation-runner.utils.ts +0 -47
  223. package/src/features/automations/automations.handler.ts +0 -197
  224. package/src/features/automations/automations.schema.ts +0 -177
  225. package/src/features/automations/context-resolver.ts +0 -152
  226. package/src/features/automations/cron-runner.ts +0 -140
  227. package/src/features/automations/cron-runner.utils.ts +0 -44
  228. package/src/features/automations/filter-translation.ts +0 -46
  229. package/src/features/automations/index.ts +0 -16
  230. package/src/features/automations/template-grammar.ts +0 -245
  231. package/src/features/automations/var-access-resolver.ts +0 -140
  232. package/src/features/automations/when-evaluator.ts +0 -87
  233. package/src/features/automations/when-pushdown.ts +0 -57
  234. package/src/features/backrefs/__tests__/backrefs.handler.test.ts +0 -359
  235. package/src/features/backrefs/backrefs.handler.ts +0 -168
  236. package/src/features/backrefs/d1-backref.repository.ts +0 -84
  237. package/src/features/backrefs/index.ts +0 -5
  238. package/src/features/content/constants.ts +0 -16
  239. package/src/features/content/handlers/bulk.handler.ts +0 -185
  240. package/src/features/content/handlers/create.ts +0 -129
  241. package/src/features/content/handlers/delete.ts +0 -62
  242. package/src/features/content/handlers/facets.ts +0 -49
  243. package/src/features/content/handlers/get.ts +0 -120
  244. package/src/features/content/handlers/helpers.test.ts +0 -180
  245. package/src/features/content/handlers/helpers.ts +0 -93
  246. package/src/features/content/handlers/list.ts +0 -174
  247. package/src/features/content/handlers/update.ts +0 -174
  248. package/src/features/content/index.ts +0 -26
  249. package/src/features/dashboard-layout/__tests__/dashboard-layout.handler.test.ts +0 -388
  250. package/src/features/dashboard-layout/dashboard-layout.handler.ts +0 -206
  251. package/src/features/dashboard-layout/index.ts +0 -5
  252. package/src/features/draft/draft.handler.ts +0 -195
  253. package/src/features/draft/draft.middleware.ts +0 -66
  254. package/src/features/draft/index.ts +0 -5
  255. package/src/features/email/email.service.ts +0 -98
  256. package/src/features/email/email.types.ts +0 -119
  257. package/src/features/email/providers/resend.ts +0 -67
  258. package/src/features/email/providers/smtp.ts +0 -55
  259. package/src/features/email/templates/automation-mail.ts +0 -19
  260. package/src/features/email/templates/password-changed.ts +0 -63
  261. package/src/features/email/templates/password-reset.ts +0 -68
  262. package/src/features/email/templates/shell.ts +0 -96
  263. package/src/features/notifications/index.ts +0 -5
  264. package/src/features/notifications/notifications.handler.ts +0 -105
  265. package/src/features/password-reset/index.ts +0 -19
  266. package/src/features/password-reset/request.ts +0 -92
  267. package/src/features/password-reset/reset.ts +0 -102
  268. package/src/features/rotate-field/index.ts +0 -5
  269. package/src/features/rotate-field/rotate-field.handler.ts +0 -132
  270. package/src/features/rotate-field/rotate-field.schema.ts +0 -17
  271. package/src/features/schema/schema.handler.ts +0 -131
  272. package/src/features/seeds/index.ts +0 -5
  273. package/src/features/seeds/seeds.handler.test.ts +0 -1199
  274. package/src/features/seeds/seeds.handler.ts +0 -558
  275. package/src/features/settings/__tests__/settings.handler.test.ts +0 -555
  276. package/src/features/settings/settings.handler.ts +0 -392
  277. package/src/features/setup/index.ts +0 -288
  278. package/src/features/stats/index.ts +0 -5
  279. package/src/features/stats/stats.handler.ts +0 -420
  280. package/src/features/webhooks/index.ts +0 -63
  281. package/src/index.ts +0 -65
  282. package/src/media-utils.ts +0 -82
  283. package/src/middleware/auth-providers.middleware.test.ts +0 -85
  284. package/src/middleware/auth-providers.middleware.ts +0 -45
  285. package/src/middleware/observability.middleware.test.ts +0 -93
  286. package/src/middleware/observability.middleware.ts +0 -70
  287. package/src/middleware/rate-limit.middleware.ts +0 -45
  288. package/src/middleware/repository.middleware.ts +0 -117
  289. package/src/middleware/seed-registry.middleware.test.ts +0 -97
  290. package/src/middleware/seed-registry.middleware.ts +0 -21
  291. package/src/middleware/storage.middleware.ts +0 -25
  292. package/src/middleware.ts +0 -45
  293. package/src/public/access-policy.ts +0 -27
  294. package/src/public/api-key-middleware.ts +0 -57
  295. package/src/public/cache-utils.ts +0 -38
  296. package/src/public/entry-projection.ts +0 -46
  297. package/src/public/idempotency.ts +0 -23
  298. package/src/public/index.ts +0 -16
  299. package/src/public/problem-details.ts +0 -124
  300. package/src/public/public-add.ts +0 -114
  301. package/src/public/public-edit.ts +0 -163
  302. package/src/public/public-errors.ts +0 -19
  303. package/src/public/public-read.ts +0 -63
  304. package/src/public/public-routes.ts +0 -88
  305. package/src/public/query-builder.test.ts +0 -224
  306. package/src/public/query-builder.ts +0 -156
  307. package/src/public/rate-limit-middleware.ts +0 -34
  308. package/src/public/read-list.ts +0 -54
  309. package/src/public/read-single.ts +0 -48
  310. package/src/public/response-builder.ts +0 -30
  311. package/src/public/sanitize.ts +0 -69
  312. package/src/rate-limit/cloudflare-rate-limiter.test.ts +0 -30
  313. package/src/rate-limit/cloudflare-rate-limiter.ts +0 -15
  314. package/src/rate-limit/in-memory-rate-limiter.test.ts +0 -37
  315. package/src/rate-limit/in-memory-rate-limiter.ts +0 -17
  316. package/src/rate-limit/no-op-rate-limiter.test.ts +0 -22
  317. package/src/rate-limit/no-op-rate-limiter.ts +0 -11
  318. package/src/search-utils.test.ts +0 -211
  319. package/src/search-utils.ts +0 -213
  320. package/src/search.ts +0 -65
  321. package/src/shared/apply-policies.test.ts +0 -81
  322. package/src/shared/apply-policies.ts +0 -67
  323. package/src/shared/automations.repository.d1.ts +0 -150
  324. package/src/shared/background-notification-service.test.ts +0 -62
  325. package/src/shared/background-notification-service.ts +0 -52
  326. package/src/shared/base.repository.d1.ts +0 -32
  327. package/src/shared/content-utils.test.ts +0 -165
  328. package/src/shared/content-utils.ts +0 -86
  329. package/src/shared/content.repository.d1.test.ts +0 -507
  330. package/src/shared/content.repository.d1.ts +0 -851
  331. package/src/shared/d1-activity-log.repository.test.ts +0 -140
  332. package/src/shared/d1-activity-log.repository.ts +0 -105
  333. package/src/shared/d1-activity-logger.test.ts +0 -86
  334. package/src/shared/d1-activity-logger.ts +0 -67
  335. package/src/shared/d1-analytics.repository.test.ts +0 -78
  336. package/src/shared/d1-analytics.repository.ts +0 -85
  337. package/src/shared/d1-content-scan.repository.test.ts +0 -80
  338. package/src/shared/d1-content-scan.repository.ts +0 -37
  339. package/src/shared/d1-notification.repository.test.ts +0 -128
  340. package/src/shared/d1-notification.repository.ts +0 -118
  341. package/src/shared/d1-password-reset-token.repository.test.ts +0 -81
  342. package/src/shared/d1-password-reset-token.repository.ts +0 -56
  343. package/src/shared/d1-search.repository.test.ts +0 -87
  344. package/src/shared/d1-search.repository.ts +0 -88
  345. package/src/shared/d1-session.repository.test.ts +0 -125
  346. package/src/shared/d1-session.repository.ts +0 -102
  347. package/src/shared/d1-setup-checklist.repository.ts +0 -36
  348. package/src/shared/d1-user.repository.test.ts +0 -151
  349. package/src/shared/d1-user.repository.ts +0 -119
  350. package/src/shared/d1-widget.repository.test.ts +0 -267
  351. package/src/shared/d1-widget.repository.ts +0 -385
  352. package/src/shared/dashboard-layout.repository.d1.test.ts +0 -135
  353. package/src/shared/dashboard-layout.repository.d1.ts +0 -58
  354. package/src/shared/demo-data-sql.ts +0 -216
  355. package/src/shared/demo-data.repository.d1.ts +0 -20
  356. package/src/shared/execution-context-scheduler.ts +0 -13
  357. package/src/shared/fixed-clock.ts +0 -25
  358. package/src/shared/fts-sync.ts +0 -8
  359. package/src/shared/idempotency.repository.d1.test.ts +0 -83
  360. package/src/shared/idempotency.repository.d1.ts +0 -60
  361. package/src/shared/in-memory-activity-logger.ts +0 -19
  362. package/src/shared/in-memory-notification-service.ts +0 -19
  363. package/src/shared/in-memory-seed.repository.ts +0 -58
  364. package/src/shared/media.repository.d1.test.ts +0 -107
  365. package/src/shared/media.repository.d1.ts +0 -68
  366. package/src/shared/qstash-notification-service.test.ts +0 -67
  367. package/src/shared/qstash-notification-service.ts +0 -55
  368. package/src/shared/query-utils.ts +0 -141
  369. package/src/shared/request-utils.ts +0 -26
  370. package/src/shared/schema-mutator.d1.test.ts +0 -142
  371. package/src/shared/schema-mutator.d1.ts +0 -64
  372. package/src/shared/seed-layout.repository.d1.test.ts +0 -114
  373. package/src/shared/seed-layout.repository.d1.ts +0 -62
  374. package/src/shared/seed-registry-cache.test.ts +0 -68
  375. package/src/shared/seed-registry-cache.ts +0 -49
  376. package/src/shared/seed.repository.d1.test.ts +0 -143
  377. package/src/shared/seed.repository.d1.ts +0 -98
  378. package/src/shared/sequential-id-generator.ts +0 -33
  379. package/src/shared/site-settings.repository.d1.ts +0 -53
  380. package/src/shared/storage/factory.ts +0 -41
  381. package/src/shared/storage/s3-bucket.ts +0 -195
  382. package/src/shared/storage-utils.ts +0 -40
  383. package/src/shared/system-stats.repository.d1.test.ts +0 -58
  384. package/src/shared/system-stats.repository.d1.ts +0 -48
  385. package/src/types.ts +0 -90
  386. package/src/upload.ts +0 -198
  387. package/src/widget.test.ts +0 -85
  388. package/src/widget.ts +0 -251
@@ -1,264 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- import { describe, it, expect, vi, beforeEach } from 'vitest'
6
- import { Hono } from 'hono'
7
- import type { IAutomationRepository, Automation } from '@beechcms/core'
8
- import { automationsApp } from '../automations.handler'
9
-
10
- const baseAutomation: Automation = {
11
- id: 'test-id',
12
- seed_slug: 'posts',
13
- name: 'test',
14
- enabled: true,
15
- triggers: [{ event: 'create' }],
16
- trigger_conditions: null,
17
- actions: [{ type: 'webhook', url: 'https://example.com', body_template: '{}' }],
18
- created_at: 1000,
19
- updated_at: 1000,
20
- }
21
-
22
- function makeStub(overrides: Partial<IAutomationRepository> = {}): IAutomationRepository {
23
- return {
24
- list: vi.fn().mockResolvedValue([baseAutomation]),
25
- findById: vi.fn().mockResolvedValue(baseAutomation),
26
- findActive: vi.fn().mockResolvedValue([]),
27
- create: vi.fn().mockResolvedValue('new-id'),
28
- update: vi.fn().mockResolvedValue(undefined),
29
- toggle: vi.fn().mockResolvedValue(undefined),
30
- delete: vi.fn().mockResolvedValue(undefined),
31
- ...overrides,
32
- }
33
- }
34
-
35
- function buildApp(stub: IAutomationRepository) {
36
- const app = new Hono()
37
- app.use('*', async (c, next) => {
38
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- ;(c as any).set('automationRepository', stub)
40
- await next()
41
- })
42
- app.route('/', automationsApp)
43
- return app
44
- }
45
-
46
- describe('GET /', () => {
47
- it('returns 400 missing-seed without ?seed=', async () => {
48
- const app = buildApp(makeStub())
49
- const res = await app.request('/')
50
- expect(res.status).toBe(400)
51
- const body = await res.json() as { type: string }
52
- expect(body.type).toContain('missing-seed')
53
- })
54
-
55
- it('returns 200 with automations list', async () => {
56
- const app = buildApp(makeStub())
57
- const res = await app.request('/?seed=posts')
58
- expect(res.status).toBe(200)
59
- const body = await res.json() as Automation[]
60
- expect(body).toHaveLength(1)
61
- expect(body[0].id).toBe('test-id')
62
- })
63
- })
64
-
65
- describe('POST /', () => {
66
- it('returns 400 invalid-json for malformed JSON body', async () => {
67
- const app = buildApp(makeStub())
68
- const res = await app.request('/', {
69
- method: 'POST',
70
- headers: { 'Content-Type': 'application/json' },
71
- body: 'malformed-not-json',
72
- })
73
- expect(res.status).toBe(400)
74
- const body = await res.json() as { type: string }
75
- expect(body.type).toContain('invalid-json')
76
- })
77
-
78
- it('returns 400 automation-validation-failed for schema non-compliant body', async () => {
79
- const app = buildApp(makeStub())
80
- const res = await app.request('/', {
81
- method: 'POST',
82
- headers: { 'Content-Type': 'application/json' },
83
- body: JSON.stringify({ seed_slug: 'posts' }), // missing name, triggers, actions
84
- })
85
- expect(res.status).toBe(400)
86
- const body = await res.json() as { type: string }
87
- expect(body.type).toContain('automation-validation-failed')
88
- })
89
-
90
- it('returns 201 with id for valid body including optional trigger fields', async () => {
91
- const app = buildApp(makeStub())
92
- const res = await app.request('/', {
93
- method: 'POST',
94
- headers: { 'Content-Type': 'application/json' },
95
- body: JSON.stringify({
96
- seed_slug: 'posts',
97
- name: 'test-cron-automation',
98
- triggers: [{ event: 'cron', cron: '0 0 * * *' }],
99
- trigger_conditions: {
100
- kind: 'group',
101
- op: 'AND',
102
- children: [
103
- { kind: 'predicate', left: { kind: 'ref', key: 'this.status' }, op: 'eq', right: { kind: 'literal', value: 'draft' } },
104
- ],
105
- },
106
- actions: [{ type: 'webhook', url: 'https://example.com', body_template: '{}' }],
107
- }),
108
- })
109
- expect(res.status).toBe(201)
110
- const body = await res.json() as { id: string }
111
- expect(body.id).toBe('new-id')
112
- })
113
-
114
- it('returns 201 with id for valid body omitting optional trigger fields', async () => {
115
- const app = buildApp(makeStub())
116
- const res = await app.request('/', {
117
- method: 'POST',
118
- headers: { 'Content-Type': 'application/json' },
119
- body: JSON.stringify({
120
- seed_slug: 'posts',
121
- name: 'test-create-automation',
122
- triggers: [{ event: 'create' }],
123
- actions: [{ type: 'webhook', url: 'https://example.com', body_template: '{}' }],
124
- }),
125
- })
126
- expect(res.status).toBe(201)
127
- const body = await res.json() as { id: string }
128
- expect(body.id).toBe('new-id')
129
- })
130
- })
131
-
132
- describe('GET /:id', () => {
133
- it('returns 404 when not found', async () => {
134
- const app = buildApp(makeStub({ findById: vi.fn().mockResolvedValue(null) }))
135
- const res = await app.request('/unknown-id')
136
- expect(res.status).toBe(404)
137
- })
138
-
139
- it('returns 200 with automation', async () => {
140
- const app = buildApp(makeStub())
141
- const res = await app.request('/test-id')
142
- expect(res.status).toBe(200)
143
- })
144
- })
145
-
146
- describe('PUT /:id', () => {
147
- it('returns 404 when not found', async () => {
148
- const app = buildApp(makeStub({ findById: vi.fn().mockResolvedValue(null) }))
149
- const res = await app.request('/unknown-id', {
150
- method: 'PUT',
151
- headers: { 'Content-Type': 'application/json' },
152
- body: JSON.stringify({ name: 'renamed' }),
153
- })
154
- expect(res.status).toBe(404)
155
- })
156
-
157
- it('returns 400 invalid-json for malformed JSON body', async () => {
158
- const app = buildApp(makeStub())
159
- const res = await app.request('/test-id', {
160
- method: 'PUT',
161
- headers: { 'Content-Type': 'application/json' },
162
- body: 'malformed-not-json',
163
- })
164
- expect(res.status).toBe(400)
165
- const body = await res.json() as { type: string }
166
- expect(body.type).toContain('invalid-json')
167
- })
168
-
169
- it('returns 400 automation-validation-failed for invalid update values', async () => {
170
- const app = buildApp(makeStub())
171
- const res = await app.request('/test-id', {
172
- method: 'PUT',
173
- headers: { 'Content-Type': 'application/json' },
174
- body: JSON.stringify({ name: 12345 }), // name must be string
175
- })
176
- expect(res.status).toBe(400)
177
- const body = await res.json() as { type: string }
178
- expect(body.type).toContain('automation-validation-failed')
179
- })
180
-
181
- it('returns 400 when cron trigger has no cron expression', async () => {
182
- const app = buildApp(makeStub())
183
- const res = await app.request('/test-id', {
184
- method: 'PUT',
185
- headers: { 'Content-Type': 'application/json' },
186
- body: JSON.stringify({ triggers: [{ event: 'cron' }] }),
187
- })
188
- expect(res.status).toBe(400)
189
- const body = await res.json() as { type: string }
190
- expect(body.type).toContain('automation-validation-failed')
191
- })
192
-
193
- it('returns 204 for valid update', async () => {
194
- const app = buildApp(makeStub())
195
- const res = await app.request('/test-id', {
196
- method: 'PUT',
197
- headers: { 'Content-Type': 'application/json' },
198
- body: JSON.stringify({ name: 'renamed' }),
199
- })
200
- expect(res.status).toBe(204)
201
- })
202
- })
203
-
204
- describe('PATCH /:id/toggle', () => {
205
- it('returns 404 when automation not found', async () => {
206
- const app = buildApp(makeStub({ findById: vi.fn().mockResolvedValue(null) }))
207
- const res = await app.request('/unknown-id/toggle', {
208
- method: 'PATCH',
209
- headers: { 'Content-Type': 'application/json' },
210
- body: JSON.stringify({ enabled: false }),
211
- })
212
- expect(res.status).toBe(404)
213
- })
214
-
215
- it('returns 400 invalid-json for malformed JSON body', async () => {
216
- const app = buildApp(makeStub())
217
- const res = await app.request('/test-id/toggle', {
218
- method: 'PATCH',
219
- headers: { 'Content-Type': 'application/json' },
220
- body: 'malformed-not-json',
221
- })
222
- expect(res.status).toBe(400)
223
- const body = await res.json() as { type: string }
224
- expect(body.type).toContain('invalid-json')
225
- })
226
-
227
- it('returns 400 automation-validation-failed for invalid payload', async () => {
228
- const app = buildApp(makeStub())
229
- const res = await app.request('/test-id/toggle', {
230
- method: 'PATCH',
231
- headers: { 'Content-Type': 'application/json' },
232
- body: JSON.stringify({ enabled: 'not-a-boolean' }),
233
- })
234
- expect(res.status).toBe(400)
235
- const body = await res.json() as { type: string }
236
- expect(body.type).toContain('automation-validation-failed')
237
- })
238
-
239
- it('returns 204 and calls toggle(id, false)', async () => {
240
- const stub = makeStub()
241
- const app = buildApp(stub)
242
- const res = await app.request('/test-id/toggle', {
243
- method: 'PATCH',
244
- headers: { 'Content-Type': 'application/json' },
245
- body: JSON.stringify({ enabled: false }),
246
- })
247
- expect(res.status).toBe(204)
248
- expect(stub.toggle).toHaveBeenCalledWith('test-id', false)
249
- })
250
- })
251
-
252
- describe('DELETE /:id', () => {
253
- it('returns 404 when not found', async () => {
254
- const app = buildApp(makeStub({ findById: vi.fn().mockResolvedValue(null) }))
255
- const res = await app.request('/unknown-id', { method: 'DELETE' })
256
- expect(res.status).toBe(404)
257
- })
258
-
259
- it('returns 204 on success', async () => {
260
- const app = buildApp(makeStub())
261
- const res = await app.request('/test-id', { method: 'DELETE' })
262
- expect(res.status).toBe(204)
263
- })
264
- })
@@ -1,163 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- import { describe, it, expect, vi } from 'vitest'
6
- import { D1AutomationRepository } from '../../../shared/automations.repository.d1'
7
-
8
- function makeDb() {
9
- const calls: { sql: string; bindings: unknown[] }[] = []
10
- let nextAllResult: unknown[] = []
11
- let nextFirstResult: unknown = null
12
-
13
- const db = {
14
- _calls: calls,
15
- _setAllResult(rows: unknown[]) { nextAllResult = rows },
16
- _setFirstResult(row: unknown) { nextFirstResult = row },
17
- prepare(sql: string) {
18
- let boundValues: unknown[] = []
19
- const stmt = {
20
- bind(...args: unknown[]) { boundValues = args; return stmt },
21
- async run() {
22
- calls.push({ sql, bindings: boundValues })
23
- return { success: true }
24
- },
25
- async all() {
26
- calls.push({ sql, bindings: boundValues })
27
- return { results: nextAllResult }
28
- },
29
- async first() {
30
- calls.push({ sql, bindings: boundValues })
31
- return nextFirstResult
32
- },
33
- }
34
- return stmt
35
- },
36
- }
37
- return db as unknown as D1Database & typeof db
38
- }
39
-
40
- const fixtureRow = {
41
- id: 'abc',
42
- seed_slug: 'posts',
43
- name: 'test',
44
- enabled: 1,
45
- triggers: JSON.stringify([{ event: 'create' }]),
46
- trigger_conditions: null,
47
- actions: JSON.stringify([{ type: 'webhook', url: 'https://example.com' }]),
48
- created_at: 1000,
49
- updated_at: 1000,
50
- }
51
-
52
- describe('D1AutomationRepository.create', () => {
53
- it('serialises actions and trigger_conditions to JSON', async () => {
54
- const db = makeDb()
55
- const repo = new D1AutomationRepository(db)
56
- const whenNode = {
57
- kind: 'group' as const,
58
- op: 'AND' as const,
59
- children: [{ kind: 'predicate' as const, left: { kind: 'ref' as const, key: 'this.status' }, op: 'eq' as const, right: { kind: 'literal' as const, value: 'published' } }],
60
- }
61
- await repo.create({
62
- seed_slug: 'posts',
63
- name: 'test',
64
- triggers: [{ event: 'create' }],
65
- trigger_conditions: whenNode,
66
- actions: [{ type: 'webhook', url: 'https://example.com' }],
67
- })
68
- expect(db._calls).toHaveLength(1)
69
- const { bindings } = db._calls[0]
70
- // triggers is index 3, trigger_conditions is index 4, actions is index 5
71
- expect(typeof bindings[3]).toBe('string')
72
- expect(JSON.parse(bindings[3] as string)).toEqual([{ event: 'create' }])
73
- expect(typeof bindings[4]).toBe('string')
74
- expect(JSON.parse(bindings[4] as string)).toEqual(whenNode)
75
- expect(typeof bindings[5]).toBe('string')
76
- expect(JSON.parse(bindings[5] as string)).toEqual([{ type: 'webhook', url: 'https://example.com' }])
77
- })
78
-
79
- it('stores null for missing trigger_conditions', async () => {
80
- const db = makeDb()
81
- const repo = new D1AutomationRepository(db)
82
- await repo.create({
83
- seed_slug: 'posts',
84
- name: 'test',
85
- triggers: [{ event: 'create' }],
86
- trigger_conditions: null,
87
- actions: [{ type: 'webhook', url: 'https://example.com' }],
88
- })
89
- const { bindings } = db._calls[0]
90
- expect(bindings[4]).toBeNull()
91
- })
92
- })
93
-
94
- describe('D1AutomationRepository.update', () => {
95
- it('builds SET clause only from provided keys', async () => {
96
- const db = makeDb()
97
- const repo = new D1AutomationRepository(db)
98
- await repo.update('abc', { name: 'renamed' })
99
- expect(db._calls).toHaveLength(1)
100
- expect(db._calls[0].sql).toContain('name = ?')
101
- expect(db._calls[0].sql).not.toContain('seed_slug')
102
- })
103
-
104
- it('short-circuits when no fields provided', async () => {
105
- const db = makeDb()
106
- const repo = new D1AutomationRepository(db)
107
- await repo.update('abc', {})
108
- expect(db._calls).toHaveLength(0)
109
- })
110
- })
111
-
112
- describe('D1AutomationRepository.toggle', () => {
113
- it('only writes enabled and updated_at', async () => {
114
- const db = makeDb()
115
- const repo = new D1AutomationRepository(db)
116
- await repo.toggle('abc', false)
117
- expect(db._calls).toHaveLength(1)
118
- const { sql, bindings } = db._calls[0]
119
- expect(sql).toBe('UPDATE automations SET enabled = ?, updated_at = ? WHERE id = ?')
120
- expect(bindings[0]).toBe(0)
121
- expect(bindings[2]).toBe('abc')
122
- })
123
- })
124
-
125
- describe('rowToAutomation JSON round-trip', () => {
126
- it('deserialises trigger_conditions as null when column is null', async () => {
127
- const db = makeDb()
128
- db._setAllResult([fixtureRow])
129
- const repo = new D1AutomationRepository(db)
130
- const rows = await repo.list('posts')
131
- expect(rows[0].trigger_conditions).toBeNull()
132
- })
133
-
134
- it('deserialises trigger_conditions as WhenNode when column contains WhenNode JSON', async () => {
135
- const db = makeDb()
136
- const whenNode = {
137
- kind: 'group',
138
- op: 'AND',
139
- children: [{
140
- kind: 'predicate',
141
- left: { kind: 'ref', key: 'this.status' },
142
- op: 'eq',
143
- right: { kind: 'literal', value: 'published' },
144
- }],
145
- }
146
- const rowWithConditions = {
147
- ...fixtureRow,
148
- trigger_conditions: JSON.stringify(whenNode),
149
- }
150
- db._setAllResult([rowWithConditions])
151
- const repo = new D1AutomationRepository(db)
152
- const rows = await repo.list('posts')
153
- expect(rows[0].trigger_conditions).toEqual(whenNode)
154
- })
155
-
156
- it('deserialises actions as parsed array', async () => {
157
- const db = makeDb()
158
- db._setAllResult([fixtureRow])
159
- const repo = new D1AutomationRepository(db)
160
- const rows = await repo.list('posts')
161
- expect(rows[0].actions).toEqual([{ type: 'webhook', url: 'https://example.com' }])
162
- })
163
- })
@@ -1,224 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import {
7
- createAutomationSchema,
8
- updateAutomationSchema,
9
- toggleAutomationSchema,
10
- } from '../automations.schema'
11
-
12
- const validWebhookAction = { type: 'webhook' as const, url: 'https://example.com/hook', body_template: '{}' }
13
-
14
- const minimalValid = {
15
- seed_slug: 'posts',
16
- name: 'notify-on-create',
17
- triggers: [{ event: 'create' as const }],
18
- actions: [validWebhookAction],
19
- }
20
-
21
- describe('createAutomationSchema', () => {
22
- it('accepts minimal valid create payload', () => {
23
- expect(createAutomationSchema.safeParse(minimalValid).success).toBe(true)
24
- })
25
-
26
- it('rejects cron trigger without cron expression', () => {
27
- const result = createAutomationSchema.safeParse({
28
- ...minimalValid,
29
- triggers: [{ event: 'cron' }],
30
- })
31
- expect(result.success).toBe(false)
32
- })
33
-
34
- it('accepts cron trigger with cron expression', () => {
35
- const result = createAutomationSchema.safeParse({
36
- ...minimalValid,
37
- triggers: [{ event: 'cron', cron: '0 * * * *' }],
38
- })
39
- expect(result.success).toBe(true)
40
- })
41
-
42
- it('rejects duplicate trigger events', () => {
43
- const result = createAutomationSchema.safeParse({
44
- ...minimalValid,
45
- triggers: [{ event: 'create' }, { event: 'create' }],
46
- })
47
- expect(result.success).toBe(false)
48
- })
49
-
50
- it('accepts multiple distinct triggers', () => {
51
- const result = createAutomationSchema.safeParse({
52
- ...minimalValid,
53
- triggers: [{ event: 'create' }, { event: 'update' }],
54
- })
55
- expect(result.success).toBe(true)
56
- })
57
-
58
- it('rejects empty actions array', () => {
59
- const result = createAutomationSchema.safeParse({ ...minimalValid, actions: [] })
60
- expect(result.success).toBe(false)
61
- })
62
-
63
- it('rejects unknown action type', () => {
64
- const result = createAutomationSchema.safeParse({
65
- ...minimalValid,
66
- actions: [{ type: 'unknown_type', url: 'https://example.com' }],
67
- })
68
- expect(result.success).toBe(false)
69
- })
70
-
71
- it('rejects webhook with non-url', () => {
72
- const result = createAutomationSchema.safeParse({
73
- ...minimalValid,
74
- actions: [{ type: 'webhook', url: 'not-a-url', body_template: '{}' }],
75
- })
76
- expect(result.success).toBe(false)
77
- })
78
-
79
- it('rejects webhook with HTTP url (not HTTPS)', () => {
80
- const result = createAutomationSchema.safeParse({
81
- ...minimalValid,
82
- actions: [{ type: 'webhook', url: 'http://example.com', body_template: '{}' }],
83
- })
84
- expect(result.success).toBe(false)
85
- const issues = result.error?.issues ?? []
86
- expect(issues.some((i) => i.message === 'automations.editor.errors.webhookHttpsRequired')).toBe(true)
87
- })
88
-
89
- it('rejects webhook with localhost url', () => {
90
- const result = createAutomationSchema.safeParse({
91
- ...minimalValid,
92
- actions: [{ type: 'webhook', url: 'https://localhost/x', body_template: '{}' }],
93
- })
94
- expect(result.success).toBe(false)
95
- const issues = result.error?.issues ?? []
96
- expect(issues.some((i) => i.message === 'automations.editor.errors.webhookPrivateHostBlocked')).toBe(true)
97
- })
98
-
99
- it('rejects webhook with 127.0.0.1', () => {
100
- const result = createAutomationSchema.safeParse({
101
- ...minimalValid,
102
- actions: [{ type: 'webhook', url: 'https://127.0.0.1/x', body_template: '{}' }],
103
- })
104
- expect(result.success).toBe(false)
105
- })
106
-
107
- it('rejects webhook with AWS metadata IP (169.254.169.254)', () => {
108
- const result = createAutomationSchema.safeParse({
109
- ...minimalValid,
110
- actions: [{ type: 'webhook', url: 'https://169.254.169.254/x', body_template: '{}' }],
111
- })
112
- expect(result.success).toBe(false)
113
- })
114
-
115
- it('rejects webhook with 10.x.x.x private IP', () => {
116
- const result = createAutomationSchema.safeParse({
117
- ...minimalValid,
118
- actions: [{ type: 'webhook', url: 'https://10.0.0.5/x', body_template: '{}' }],
119
- })
120
- expect(result.success).toBe(false)
121
- })
122
-
123
- it('rejects webhook with 192.168.x.x private IP', () => {
124
- const result = createAutomationSchema.safeParse({
125
- ...minimalValid,
126
- actions: [{ type: 'webhook', url: 'https://192.168.1.1/x', body_template: '{}' }],
127
- })
128
- expect(result.success).toBe(false)
129
- })
130
-
131
- it('rejects webhook with 172.16.x.x private IP', () => {
132
- const result = createAutomationSchema.safeParse({
133
- ...minimalValid,
134
- actions: [{ type: 'webhook', url: 'https://172.16.0.1/x', body_template: '{}' }],
135
- })
136
- expect(result.success).toBe(false)
137
- })
138
-
139
- it('accepts webhook with 172.32.x.x (outside private range)', () => {
140
- const result = createAutomationSchema.safeParse({
141
- ...minimalValid,
142
- actions: [{ type: 'webhook', url: 'https://172.32.0.1/x', body_template: '{}' }],
143
- })
144
- expect(result.success).toBe(true)
145
- })
146
-
147
- it('rejects webhook without body_template', () => {
148
- const result = createAutomationSchema.safeParse({
149
- ...minimalValid,
150
- actions: [{ type: 'webhook', url: 'https://example.com/x' }],
151
- })
152
- expect(result.success).toBe(false)
153
- const issues = result.error?.issues ?? []
154
- expect(issues.some((i) => i.message === 'automations.editor.errors.bodyRequired')).toBe(true)
155
- })
156
-
157
- it('accepts webhook with valid HTTPS public url and body_template', () => {
158
- const result = createAutomationSchema.safeParse({
159
- ...minimalValid,
160
- actions: [{ type: 'webhook', url: 'https://example.com/x', body_template: '{}' }],
161
- })
162
- expect(result.success).toBe(true)
163
- })
164
-
165
- it('rejects send_mail with invalid email', () => {
166
- const result = createAutomationSchema.safeParse({
167
- ...minimalValid,
168
- actions: [{ type: 'send_mail', to: 'not-an-email', subject_template: 'hi', body_template: 'body' }],
169
- })
170
- expect(result.success).toBe(false)
171
- })
172
-
173
- it('accepts trigger_conditions as null', () => {
174
- const result = createAutomationSchema.safeParse({
175
- ...minimalValid,
176
- trigger_conditions: null,
177
- })
178
-
179
- expect(result.success).toBe(true)
180
- })
181
-
182
- it('accepts trigger_conditions as WhenNode', () => {
183
- const result = createAutomationSchema.safeParse({
184
- ...minimalValid,
185
- trigger_conditions: {
186
- kind: 'group',
187
- op: 'AND',
188
- children: [
189
- { kind: 'predicate', left: { kind: 'ref', key: 'this.status' }, op: 'eq', right: { kind: 'literal', value: 'published' } },
190
- ],
191
- },
192
- })
193
- expect(result.success).toBe(true)
194
- })
195
- })
196
-
197
- describe('updateAutomationSchema', () => {
198
- it('accepts empty object', () => {
199
- expect(updateAutomationSchema.safeParse({}).success).toBe(true)
200
- })
201
-
202
- it('accepts name-only without requiring triggers', () => {
203
- const result = updateAutomationSchema.safeParse({ name: 'renamed' })
204
- expect(result.success).toBe(true)
205
- })
206
- })
207
-
208
- describe('toggleAutomationSchema', () => {
209
- it('accepts { enabled: true }', () => {
210
- expect(toggleAutomationSchema.safeParse({ enabled: true }).success).toBe(true)
211
- })
212
-
213
- it('accepts { enabled: false }', () => {
214
- expect(toggleAutomationSchema.safeParse({ enabled: false }).success).toBe(true)
215
- })
216
-
217
- it('rejects missing enabled', () => {
218
- expect(toggleAutomationSchema.safeParse({}).success).toBe(false)
219
- })
220
-
221
- it('rejects non-boolean enabled', () => {
222
- expect(toggleAutomationSchema.safeParse({ enabled: 'true' }).success).toBe(false)
223
- })
224
- })