@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,197 +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
- /// <reference types="@cloudflare/workers-types" />
6
- import { Hono } from 'hono'
7
- import type { Env, Variables } from '../../types'
8
- import { publicProblem } from '../../public/problem-details'
9
- import {
10
- createAutomationSchema,
11
- updateAutomationSchema,
12
- toggleAutomationSchema,
13
- } from './automations.schema'
14
-
15
- const automationsApp = new Hono<{ Bindings: Env; Variables: Variables }>()
16
-
17
- /**
18
- * GET /automations?seed=<slug>
19
- * Returns every automation declared for the given seed, newest first.
20
- */
21
- automationsApp.get('/', async (context) => {
22
- const seedSlug = context.req.query('seed')
23
- if (!seedSlug) {
24
- return publicProblem(context, {
25
- type: 'missing-seed',
26
- status: 400,
27
- title: 'Bad Request',
28
- detail: 'Query param `seed` is required',
29
- })
30
- }
31
- const repository = context.get('automationRepository')
32
- const automations = await repository.list(seedSlug)
33
- return context.json(automations)
34
- })
35
-
36
- /**
37
- * POST /automations
38
- * Creates a new automation. Body validated by `createAutomationSchema`.
39
- */
40
- automationsApp.post('/', async (context) => {
41
- let body: unknown
42
- try {
43
- body = await context.req.json()
44
- } catch {
45
- return publicProblem(context, {
46
- type: 'invalid-json',
47
- status: 400,
48
- title: 'Bad Request',
49
- detail: 'Request body is not valid JSON',
50
- })
51
- }
52
-
53
- const parsed = createAutomationSchema.safeParse(body)
54
- if (!parsed.success) {
55
- return publicProblem(context, {
56
- type: 'automation-validation-failed',
57
- status: 400,
58
- title: 'Bad Request',
59
- detail: parsed.error.message,
60
- })
61
- }
62
-
63
- const repository = context.get('automationRepository')
64
- const id = await repository.create({
65
- seed_slug: parsed.data.seed_slug,
66
- name: parsed.data.name,
67
- triggers: parsed.data.triggers,
68
- trigger_conditions: parsed.data.trigger_conditions ?? null,
69
- actions: parsed.data.actions,
70
- })
71
- return context.json({ id }, 201)
72
- })
73
-
74
- /**
75
- * GET /automations/:id
76
- */
77
- automationsApp.get('/:id', async (context) => {
78
- const id = context.req.param('id')
79
- const automation = await context.get('automationRepository').findById(id)
80
- if (!automation) {
81
- return publicProblem(context, {
82
- type: 'automation-not-found',
83
- status: 404,
84
- title: 'Not Found',
85
- detail: `Automation ${id} does not exist`,
86
- })
87
- }
88
- return context.json(automation)
89
- })
90
-
91
- /**
92
- * PUT /automations/:id
93
- * Full update — partial bodies allowed; cron expression validation
94
- * is enforced by the triggers array schema.
95
- */
96
- automationsApp.put('/:id', async (context) => {
97
- const id = context.req.param('id')
98
- const repository = context.get('automationRepository')
99
- const existing = await repository.findById(id)
100
- if (!existing) {
101
- return publicProblem(context, {
102
- type: 'automation-not-found',
103
- status: 404,
104
- title: 'Not Found',
105
- detail: `Automation ${id} does not exist`,
106
- })
107
- }
108
-
109
- let body: unknown
110
- try {
111
- body = await context.req.json()
112
- } catch {
113
- return publicProblem(context, {
114
- type: 'invalid-json',
115
- status: 400,
116
- title: 'Bad Request',
117
- detail: 'Request body is not valid JSON',
118
- })
119
- }
120
-
121
- const parsed = updateAutomationSchema.safeParse(body)
122
- if (!parsed.success) {
123
- return publicProblem(context, {
124
- type: 'automation-validation-failed',
125
- status: 400,
126
- title: 'Bad Request',
127
- detail: parsed.error.message,
128
- })
129
- }
130
-
131
- await repository.update(id, parsed.data)
132
- return context.body(null, 204)
133
- })
134
-
135
- /**
136
- * PATCH /automations/:id/toggle
137
- * Atomic single-field flip — does not require sending the full body.
138
- */
139
- automationsApp.patch('/:id/toggle', async (context) => {
140
- const id = context.req.param('id')
141
- const repository = context.get('automationRepository')
142
- const existing = await repository.findById(id)
143
- if (!existing) {
144
- return publicProblem(context, {
145
- type: 'automation-not-found',
146
- status: 404,
147
- title: 'Not Found',
148
- detail: `Automation ${id} does not exist`,
149
- })
150
- }
151
-
152
- let body: unknown
153
- try {
154
- body = await context.req.json()
155
- } catch {
156
- return publicProblem(context, {
157
- type: 'invalid-json',
158
- status: 400,
159
- title: 'Bad Request',
160
- detail: 'Request body is not valid JSON',
161
- })
162
- }
163
-
164
- const parsed = toggleAutomationSchema.safeParse(body)
165
- if (!parsed.success) {
166
- return publicProblem(context, {
167
- type: 'automation-validation-failed',
168
- status: 400,
169
- title: 'Bad Request',
170
- detail: parsed.error.message,
171
- })
172
- }
173
-
174
- await repository.toggle(id, parsed.data.enabled)
175
- return context.body(null, 204)
176
- })
177
-
178
- /**
179
- * DELETE /automations/:id
180
- */
181
- automationsApp.delete('/:id', async (context) => {
182
- const id = context.req.param('id')
183
- const repository = context.get('automationRepository')
184
- const existing = await repository.findById(id)
185
- if (!existing) {
186
- return publicProblem(context, {
187
- type: 'automation-not-found',
188
- status: 404,
189
- title: 'Not Found',
190
- detail: `Automation ${id} does not exist`,
191
- })
192
- }
193
- await repository.delete(id)
194
- return context.body(null, 204)
195
- })
196
-
197
- export { automationsApp }
@@ -1,177 +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
- // Mirror: apps/dashboard/src/features/automations/schema/automation.schema.ts — keep structurally identical.
6
- import { z } from 'zod'
7
- import type { WhenNode } from '@beechcms/core'
8
- import { isPrivateHost } from '@beechcms/core'
9
- import { AUTOMATION_RESERVED_WORDS } from './template-grammar'
10
-
11
- // ---------------------------------------------------------------------------
12
- // Trigger conditions — recursive WhenNode schema (Task 14)
13
- // ---------------------------------------------------------------------------
14
-
15
- const whenOperandSchema = z.union([
16
- z.object({ kind: z.literal('literal'), value: z.unknown() }),
17
- z.object({ kind: z.literal('ref'), key: z.string().min(1) }),
18
- ])
19
-
20
- const whenPredicateSchema = z.object({
21
- kind: z.literal('predicate'),
22
- left: whenOperandSchema,
23
- op: z.enum(['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'contains', 'startswith', 'endswith', 'in', 'notin', 'isempty', 'isnotempty', 'matches']),
24
- right: whenOperandSchema.optional(),
25
- })
26
-
27
- const whenNodeSchema: z.ZodType<WhenNode> = z.lazy(() =>
28
- z.union([
29
- whenPredicateSchema,
30
- z.object({
31
- kind: z.literal('group'),
32
- op: z.enum(['AND', 'OR']),
33
- children: z.array(whenNodeSchema).min(1),
34
- negate: z.boolean().optional(),
35
- }).refine(
36
- (g) => depthOf(g) <= 10,
37
- { message: 'WhenNode nesting exceeds maximum depth of 10' },
38
- ),
39
- ]),
40
- )
41
-
42
- function depthOf(node: unknown, d = 0): number {
43
- if (d > 10) return d
44
- if (typeof node !== 'object' || !node) return d
45
- const n = node as Record<string, unknown>
46
- if (n['kind'] === 'group' && Array.isArray(n['children'])) {
47
- return Math.max(...(n['children'] as unknown[]).map((c) => depthOf(c, d + 1)))
48
- }
49
- return d
50
- }
51
-
52
- const triggerConditionsSchema = whenNodeSchema.nullable().optional()
53
-
54
- const setVariableFilterSchema = z.object({
55
- field: z.string().min(1),
56
- op: z.enum(['eq', 'neq', 'contains', 'gt', 'lt', 'isempty', 'isnotempty']),
57
- value: z.unknown(),
58
- })
59
-
60
- // ---------------------------------------------------------------------------
61
- // Action schemas
62
- // ---------------------------------------------------------------------------
63
-
64
- const setVariableActionSchema = z.object({
65
- type: z.literal('set_variable'),
66
- name: z
67
- .string()
68
- .min(1)
69
- .regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/)
70
- .refine((v) => !AUTOMATION_RESERVED_WORDS.has(v), {
71
- message: 'automations.editor.errors.variableNameReserved',
72
- }),
73
- seed_slug: z.string().min(1).optional(),
74
- fixed_id: z.string().min(1).optional(),
75
- column: z.string().min(1).optional(),
76
- filters: z.array(setVariableFilterSchema).default([]),
77
- order_by: z.string().optional(),
78
- order: z.enum(['asc', 'desc']).optional(),
79
- load_type: z.enum(['fruit', 'branch']).optional(), // accepted but ignored at runtime
80
- })
81
-
82
- const webhookActionSchema = z.object({
83
- type: z.literal('webhook'),
84
- url: z
85
- .string()
86
- .url()
87
- .refine((u) => {
88
- try { return new URL(u).protocol === 'https:' } catch { return false }
89
- }, { message: 'automations.editor.errors.webhookHttpsRequired' })
90
- .refine((u) => {
91
- try { return !isPrivateHost(new URL(u).hostname) } catch { return false }
92
- }, { message: 'automations.editor.errors.webhookPrivateHostBlocked' }),
93
- method: z.enum(['POST', 'GET', 'PUT']).optional(),
94
- headers: z.record(z.string(), z.string()).optional(),
95
- body_template: z.preprocess(
96
- (v) => (v == null ? '' : v),
97
- z.string().min(1, 'automations.editor.errors.bodyRequired'),
98
- ),
99
- })
100
-
101
- const sendMailActionSchema = z.object({
102
- type: z.literal('send_mail'),
103
- to: z.string().email(),
104
- subject_template: z.string().min(1),
105
- body_template: z.string().min(1),
106
- })
107
-
108
- const editFieldActionSchema = z.object({
109
- type: z.literal('edit_field'),
110
- field: z.string().min(1),
111
- value: z.unknown(),
112
- })
113
-
114
- const createEntryActionSchema = z.object({
115
- type: z.literal('create_entry'),
116
- seed_slug: z.string().min(1),
117
- field_map: z.record(z.string(), z.string()),
118
- })
119
-
120
- export const automationActionSchema = z.discriminatedUnion('type', [
121
- setVariableActionSchema,
122
- webhookActionSchema,
123
- sendMailActionSchema,
124
- editFieldActionSchema,
125
- createEntryActionSchema,
126
- ])
127
-
128
- // ---------------------------------------------------------------------------
129
- // Triggers schema
130
- // ---------------------------------------------------------------------------
131
-
132
- const automationTriggerSchema = z.object({
133
- event: z.enum(['create', 'update', 'delete', 'cron']),
134
- cron: z.string().nullable().optional(),
135
- })
136
-
137
- const triggersSchema = z
138
- .array(automationTriggerSchema)
139
- .min(1, 'At least one trigger is required')
140
- .refine(
141
- (triggers) => triggers.filter((t) => t.event === 'cron').length <= 1,
142
- { message: 'Only one cron trigger is allowed per automation' },
143
- )
144
- .refine(
145
- (triggers) => {
146
- const events = triggers.map((t) => t.event)
147
- return new Set(events).size === events.length
148
- },
149
- { message: 'Duplicate trigger events are not allowed' },
150
- )
151
- .refine(
152
- (triggers) => triggers.every((t) => t.event !== 'cron' || !!t.cron),
153
- { message: 'cron expression is required for cron triggers', path: ['cron'] },
154
- )
155
-
156
- // ---------------------------------------------------------------------------
157
- // Automation create/update schemas
158
- // ---------------------------------------------------------------------------
159
-
160
- const createAutomationBaseSchema = z.object({
161
- seed_slug: z.string().min(1),
162
- name: z.string().min(1).max(100),
163
- triggers: triggersSchema,
164
- trigger_conditions: triggerConditionsSchema,
165
- actions: z.array(automationActionSchema).min(1, 'At least one action is required'),
166
- })
167
-
168
- export const createAutomationSchema = createAutomationBaseSchema
169
-
170
- export const updateAutomationSchema = createAutomationBaseSchema.partial()
171
-
172
- export const toggleAutomationSchema = z.object({
173
- enabled: z.boolean(),
174
- })
175
-
176
- export type CreateAutomationBody = z.infer<typeof createAutomationSchema>
177
- export type UpdateAutomationBody = z.infer<typeof updateAutomationSchema>
@@ -1,152 +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 type { Automation } from '@beechcms/core'
6
- import type { AutomationContextSelector, ParsedKey } from './template-grammar'
7
- import { resolvePath } from './automation-runner.utils'
8
-
9
- export interface ResolvedContext {
10
- lookup(key: ParsedKey, onMissing?: (field: string) => void): unknown
11
- triggerEntry: Record<string, unknown> | null
12
- }
13
-
14
- const MAX_PLUCK = 100
15
- const AGGREGATE_OPS = new Set(['count', 'sum', 'avg', 'min', 'max', 'pluck'])
16
-
17
- function applyAggregate(
18
- op: string,
19
- field: string | null,
20
- rows: Array<Record<string, unknown>>,
21
- onMissing?: (f: string) => void,
22
- ): unknown {
23
- switch (op) {
24
- case 'count':
25
- return rows.length
26
-
27
- case 'sum': {
28
- if (!field) { if (onMissing) onMissing('sum:field'); return 0 }
29
- return rows.reduce((acc, r) => {
30
- const v = Number(r[field])
31
- if (Number.isNaN(v)) { if (onMissing) onMissing(`sum:${field}:NaN`); return acc }
32
- return acc + v
33
- }, 0)
34
- }
35
-
36
- case 'avg': {
37
- if (!field || rows.length === 0) { if (onMissing && !field) onMissing('avg:field'); return 0 }
38
- const total = rows.reduce((acc, r) => {
39
- const v = Number(r[field])
40
- if (Number.isNaN(v)) { if (onMissing) onMissing(`avg:${field}:NaN`); return acc }
41
- return acc + v
42
- }, 0)
43
- return rows.length > 0 ? total / rows.length : 0
44
- }
45
-
46
- case 'min': {
47
- if (!field || rows.length === 0) { if (onMissing && !field) onMissing('min:field'); return null }
48
- return rows.reduce<number | null>((acc, r) => {
49
- const v = Number(r[field])
50
- if (Number.isNaN(v)) return acc
51
- return acc === null || v < acc ? v : acc
52
- }, null)
53
- }
54
-
55
- case 'max': {
56
- if (!field || rows.length === 0) { if (onMissing && !field) onMissing('max:field'); return null }
57
- return rows.reduce<number | null>((acc, r) => {
58
- const v = Number(r[field])
59
- if (Number.isNaN(v)) return acc
60
- return acc === null || v > acc ? v : acc
61
- }, null)
62
- }
63
-
64
- case 'pluck': {
65
- if (!field) { if (onMissing) onMissing('pluck:field'); return '' }
66
- const values = rows.slice(0, MAX_PLUCK).map((r) => String(r[field] ?? ''))
67
- const truncated = rows.length > MAX_PLUCK
68
- return values.join(', ') + (truncated ? ' …' : '')
69
- }
70
-
71
- default:
72
- return undefined
73
- }
74
- }
75
-
76
- export async function resolveAutomationContext(
77
- _automation: Automation,
78
- triggerEntry: Record<string, unknown> | null,
79
- batchEntries: Array<Record<string, unknown>>,
80
- ): Promise<ResolvedContext> {
81
- function lookup(parsed: ParsedKey, onMissing?: (field: string) => void): unknown {
82
- if (parsed.kind === 'simple') {
83
- if (parsed.path.startsWith('this.')) {
84
- const field = parsed.path.slice(5)
85
- const val = resolvePath(triggerEntry ?? {}, field)
86
- if (val === undefined && onMissing) onMissing(parsed.path)
87
- return val
88
- }
89
- const val = resolvePath(triggerEntry ?? {}, parsed.path)
90
- if (val === undefined && onMissing) onMissing(parsed.path)
91
- return val
92
- }
93
-
94
- if (parsed.kind !== 'scoped') {
95
- if (onMissing) onMissing(parsed.kind === 'var_access' ? parsed.name : 'unknown')
96
- return undefined
97
- }
98
-
99
- const { scope, selector: _selector, op, field } = parsed
100
-
101
- if (scope === 'this') {
102
- if (op !== 'field' || !field) { if (onMissing) onMissing(`this.${field}`); return undefined }
103
- const val = resolvePath(triggerEntry ?? {}, field)
104
- if (val === undefined && onMissing) onMissing(`this.${field}`)
105
- return val
106
- }
107
-
108
- if (scope === 'batch') {
109
- if (op === 'field') {
110
- if (!field) { if (onMissing) onMissing('batch.field'); return undefined }
111
- const val = resolvePath(batchEntries[0] ?? {}, field)
112
- if (val === undefined && onMissing) onMissing(`batch.${field}`)
113
- return val
114
- }
115
- return applyAggregate(op, field, batchEntries, onMissing)
116
- }
117
-
118
- if (onMissing) onMissing(scope)
119
- return undefined
120
- }
121
-
122
- return { lookup, triggerEntry }
123
- }
124
-
125
- export function deriveEntryContext(
126
- base: ResolvedContext,
127
- entry: Record<string, unknown>,
128
- ): ResolvedContext {
129
- return {
130
- lookup(parsed: ParsedKey, onMissing?: (field: string) => void): unknown {
131
- if (parsed.kind === 'simple') {
132
- if (parsed.path.startsWith('this.')) {
133
- const field = parsed.path.slice(5)
134
- const val = resolvePath(entry, field)
135
- if (val === undefined && onMissing) onMissing(parsed.path)
136
- return val
137
- }
138
- const val = resolvePath(entry, parsed.path)
139
- if (val === undefined && onMissing) onMissing(parsed.path)
140
- return val
141
- }
142
- if (parsed.kind === 'scoped' && parsed.scope === 'this') {
143
- if (parsed.op !== 'field' || !parsed.field) { if (onMissing) onMissing('this.field'); return undefined }
144
- const val = resolvePath(entry, parsed.field)
145
- if (val === undefined && onMissing) onMissing(`this.${parsed.field}`)
146
- return val
147
- }
148
- return base.lookup(parsed, onMissing)
149
- },
150
- triggerEntry: entry,
151
- }
152
- }
@@ -1,140 +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 type {
6
- Automation,
7
- ContentRepository,
8
- FilterGroup,
9
- IAutomationRepository,
10
- IIdGenerator,
11
- Seed,
12
- } from '@beechcms/core'
13
- import { cronMatches } from './cron-runner.utils'
14
- import { executeAction } from './action-executors'
15
- import { resolveAutomationContext, deriveEntryContext } from './context-resolver'
16
- import { extractPushdownFilters } from './when-pushdown'
17
- import { evaluateWhen } from './when-evaluator'
18
-
19
- export interface CronRunnerDeps {
20
- automationRepository: IAutomationRepository
21
- contentRepository: ContentRepository
22
- getSeed: (slug: string) => Seed | null
23
- env: Record<string, string | undefined>
24
- idGenerator: IIdGenerator
25
- }
26
-
27
- // These actions mutate individual entries and must run once per entry.
28
- // All other actions (send_mail, webhook) run once per automation with a batch context.
29
- const PER_ENTRY_ACTIONS = new Set(['edit_field', 'create_entry'])
30
-
31
- export async function runCronAutomations(
32
- deps: CronRunnerDeps,
33
- scheduledTime: number,
34
- ): Promise<void> {
35
- console.log(`[cron] Starting runCronAutomations at scheduledTime: ${new Date(scheduledTime).toISOString()}`)
36
-
37
- let automations: Automation[]
38
- try {
39
- automations = await deps.automationRepository.findActive('*', 'cron')
40
- } catch (err) {
41
- console.error('[cron] Failed to fetch automations — is migration 0029_automations.sql applied?', err)
42
- return
43
- }
44
-
45
- console.log(`[cron] Found ${automations.length} active cron automation(s)`)
46
-
47
- for (const automation of automations) {
48
- const cronTrigger = automation.triggers.find((t) => t.event === 'cron')
49
- if (!cronMatches(cronTrigger?.cron ?? null, scheduledTime)) {
50
- console.log(`[cron] Skipping "${automation.name}": cron expression "${cronTrigger?.cron}" does not match.`)
51
- continue
52
- }
53
-
54
- const seed = deps.getSeed(automation.seed_slug)
55
- if (!seed) {
56
- console.warn('[cron] unknown seed', { automationId: automation.id, seedSlug: automation.seed_slug })
57
- continue
58
- }
59
-
60
- let entries: Array<Record<string, unknown>> = []
61
- try {
62
- entries = await fetchMatchingEntries(deps.contentRepository, seed, automation)
63
- console.log(`[cron] Fetched ${entries.length} matching entries for automation "${automation.name}"`)
64
- } catch (err) {
65
- console.error('[cron] fetch entries failed', { automationId: automation.id, err })
66
- continue
67
- }
68
-
69
- if (entries.length === 0) continue
70
-
71
- // Build the base ResolvedContext once per automation (shared seed-query cache).
72
- // triggerEntry = first entry; batchEntries = full SQL-filtered list.
73
- const batchResolved = await resolveAutomationContext(
74
- automation,
75
- entries[0] ?? null,
76
- entries,
77
- )
78
-
79
- const variables: Record<string, unknown> = {}
80
- const baseCtx = {
81
- env: deps.env,
82
- repository: deps.contentRepository,
83
- getSeed: deps.getSeed,
84
- seed,
85
- idGenerator: deps.idGenerator,
86
- variables,
87
- }
88
-
89
- for (const action of automation.actions) {
90
- if (PER_ENTRY_ACTIONS.has(action.type)) {
91
- for (const entry of entries) {
92
- // Per-entry: derive a lightweight context whose `this` is the current entry.
93
- // Seed-query cache is reused from batchResolved.
94
- const entryResolved = deriveEntryContext(batchResolved, entry)
95
- // In-memory per-entry condition check (Task 13)
96
- if (!evaluateWhen(automation.trigger_conditions, entryResolved)) continue
97
- try {
98
- await executeAction(action, { ...baseCtx, entry, context: entryResolved })
99
- } catch (err) {
100
- console.error('[cron] entry action failed', {
101
- automationId: automation.id,
102
- entryId: entry['id'],
103
- actionType: action.type,
104
- err,
105
- })
106
- }
107
- }
108
- } else {
109
- // Batch actions run once per automation with the shared batch context.
110
- try {
111
- await executeAction(action, { ...baseCtx, entry: entries[0] ?? {}, context: batchResolved })
112
- } catch (err) {
113
- console.error('[cron] batch action failed', {
114
- automationId: automation.id,
115
- actionType: action.type,
116
- err,
117
- })
118
- }
119
- }
120
- }
121
- }
122
- }
123
-
124
- async function fetchMatchingEntries(
125
- repository: ContentRepository,
126
- seed: Seed,
127
- automation: Automation,
128
- ): Promise<Array<Record<string, unknown>>> {
129
- // Task 12: push down only safe predicates to SQL (this.<field> = literal, AND group only).
130
- // OR branches, cross-seed refs, and gte/lte/etc. ops are evaluated in-memory by evaluateWhen().
131
- const filters: FilterGroup[] = automation.trigger_conditions
132
- ? extractPushdownFilters(automation.trigger_conditions, seed)
133
- : []
134
- const result = await repository.findMany(seed, {
135
- filters,
136
- status: null,
137
- pagination: { limit: 1000, offset: 0 },
138
- })
139
- return result.items
140
- }