@beechcms/api 0.6.0-preview.1 → 0.6.0-preview.3

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 (373) 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-Cws4Yj6K.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-BjPpppHe.css +1 -0
  11. package/assets/dashboard/assets/index-CrW-zhkA.js +642 -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-CAxoDQp2.js +1 -0
  20. package/assets/dashboard/assets/timeseries-chart-recharts-CixDW-LM.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 +39 -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} +37 -42
  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} +20 -33
  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 +6 -0
  98. package/dist/index.js +9666 -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/rate-limit.middleware.d.ts +9 -0
  103. package/dist/middleware/repository.middleware.d.ts +34 -0
  104. package/dist/middleware/seed-registry.middleware.d.ts +10 -0
  105. package/dist/middleware/storage.middleware.d.ts +7 -0
  106. package/dist/middleware.d.ts +11 -0
  107. package/dist/public/access-policy.d.ts +12 -0
  108. package/dist/public/api-key-middleware.d.ts +6 -0
  109. package/dist/public/cache-utils.d.ts +10 -0
  110. package/dist/public/entry-projection.d.ts +2 -0
  111. package/dist/public/idempotency.d.ts +9 -0
  112. package/dist/public/index.d.ts +11 -0
  113. package/dist/public/problem-details.d.ts +31 -0
  114. package/dist/public/public-add.d.ts +3 -0
  115. package/dist/public/public-edit.d.ts +5 -0
  116. package/dist/public/public-errors.d.ts +13 -0
  117. package/dist/public/public-read.d.ts +3 -0
  118. package/dist/public/public-routes.d.ts +3 -0
  119. package/dist/public/query-builder.d.ts +28 -0
  120. package/dist/public/rate-limit-middleware.d.ts +3 -0
  121. package/dist/public/read-list.d.ts +26 -0
  122. package/dist/public/read-single.d.ts +22 -0
  123. package/dist/public/response-builder.d.ts +22 -0
  124. package/dist/public/sanitize.d.ts +23 -0
  125. package/dist/public/slug-utils.d.ts +11 -0
  126. package/dist/rate-limit/cloudflare-rate-limiter.d.ts +6 -0
  127. package/dist/rate-limit/in-memory-rate-limiter.d.ts +7 -0
  128. package/dist/rate-limit/no-op-rate-limiter.d.ts +4 -0
  129. package/dist/search-utils.d.ts +54 -0
  130. package/dist/search.d.ts +6 -0
  131. package/dist/shared/apply-policies.d.ts +10 -0
  132. package/dist/shared/automations.repository.d1.d.ts +12 -0
  133. package/dist/shared/background-notification-service.d.ts +18 -0
  134. package/dist/shared/base.repository.d1.d.ts +18 -0
  135. package/dist/shared/content-utils.d.ts +21 -0
  136. package/dist/shared/content.repository.d1.d.ts +68 -0
  137. package/dist/shared/d1-activity-log.repository.d.ts +13 -0
  138. package/dist/shared/d1-activity-logger.d.ts +20 -0
  139. package/dist/shared/d1-analytics.repository.d.ts +20 -0
  140. package/dist/shared/d1-content-scan.repository.d.ts +7 -0
  141. package/dist/shared/d1-notification.repository.d.ts +17 -0
  142. package/dist/shared/d1-password-reset-token.repository.d.ts +10 -0
  143. package/dist/shared/d1-search.repository.d.ts +14 -0
  144. package/dist/shared/d1-session.repository.d.ts +12 -0
  145. package/dist/shared/d1-setup-checklist.repository.d.ts +12 -0
  146. package/dist/shared/d1-user.repository.d.ts +18 -0
  147. package/dist/shared/d1-widget.repository.d.ts +31 -0
  148. package/dist/shared/dashboard-layout.repository.d1.d.ts +9 -0
  149. package/dist/shared/demo-data-sql.d.ts +1 -0
  150. package/dist/shared/demo-data.repository.d1.d.ts +6 -0
  151. package/dist/shared/execution-context-scheduler.d.ts +6 -0
  152. package/dist/shared/fixed-clock.d.ts +13 -0
  153. package/dist/shared/fts-sync.d.ts +1 -0
  154. package/dist/shared/idempotency.repository.d1.d.ts +16 -0
  155. package/dist/shared/in-memory-activity-logger.d.ts +11 -0
  156. package/dist/shared/in-memory-notification-service.d.ts +11 -0
  157. package/dist/shared/in-memory-seed.repository.d.ts +18 -0
  158. package/dist/shared/media.repository.d1.d.ts +35 -0
  159. package/dist/shared/qstash-notification-service.d.ts +18 -0
  160. package/dist/shared/query-utils.d.ts +36 -0
  161. package/dist/shared/request-utils.d.ts +13 -0
  162. package/dist/shared/schema-mutator.d1.d.ts +12 -0
  163. package/dist/shared/seed-layout.repository.d1.d.ts +9 -0
  164. package/dist/shared/seed-registry-cache.d.ts +12 -0
  165. package/dist/shared/seed.repository.d1.d.ts +13 -0
  166. package/dist/shared/sequential-id-generator.d.ts +13 -0
  167. package/dist/shared/site-settings.repository.d1.d.ts +7 -0
  168. package/dist/shared/storage/factory.d.ts +8 -0
  169. package/dist/shared/storage/s3-bucket.d.ts +43 -0
  170. package/dist/shared/storage-utils.d.ts +7 -0
  171. package/dist/shared/system-stats.repository.d1.d.ts +25 -0
  172. package/dist/types.d.ts +85 -0
  173. package/dist/upload.d.ts +16 -0
  174. package/dist/widget.d.ts +7 -0
  175. package/migrations/0000_v040_base.sql +265 -265
  176. package/migrations/0029_automations.sql +14 -14
  177. package/migrations/0030_test_seeds.sql +125 -125
  178. package/package.json +27 -18
  179. package/assets/dashboard/assets/index-BSjk0Sx9.js +0 -634
  180. package/assets/dashboard/assets/index-kRCYVN2B.css +0 -1
  181. package/src/auth/__fixtures__/in-memory-hash-provider.ts +0 -17
  182. package/src/auth/__fixtures__/static-token-service.ts +0 -22
  183. package/src/auth/bcrypt-hash-provider.ts +0 -24
  184. package/src/auth/constants.ts +0 -14
  185. package/src/auth/generate-refresh-token.test.ts +0 -23
  186. package/src/auth/hash-provider.test.ts +0 -55
  187. package/src/auth/jose-token-service.ts +0 -62
  188. package/src/auth/jwt-claims-passthrough.test.ts +0 -87
  189. package/src/auth/login.test.ts +0 -100
  190. package/src/auth/login.ts +0 -78
  191. package/src/auth/refresh.ts +0 -11
  192. package/src/auth/token-service.test.ts +0 -90
  193. package/src/factory.ts +0 -368
  194. package/src/features/automations/__tests__/action-executors.test.ts +0 -255
  195. package/src/features/automations/__tests__/automation-runner.test.ts +0 -196
  196. package/src/features/automations/__tests__/automation-runner.utils.test.ts +0 -60
  197. package/src/features/automations/__tests__/automations.handler.test.ts +0 -264
  198. package/src/features/automations/__tests__/automations.repository.test.ts +0 -163
  199. package/src/features/automations/__tests__/automations.schema.test.ts +0 -224
  200. package/src/features/automations/__tests__/context-resolver.test.ts +0 -126
  201. package/src/features/automations/__tests__/cron-runner.test.ts +0 -267
  202. package/src/features/automations/__tests__/cron-runner.utils.test.ts +0 -144
  203. package/src/features/automations/__tests__/set-variable.executor.test.ts +0 -383
  204. package/src/features/automations/__tests__/template-grammar.test.ts +0 -308
  205. package/src/features/automations/__tests__/webhook.executor.test.ts +0 -142
  206. package/src/features/automations/__tests__/when-evaluator.test.ts +0 -274
  207. package/src/features/automations/__tests__/when-pushdown.test.ts +0 -281
  208. package/src/features/automations/action-executors/create-entry.executor.ts +0 -27
  209. package/src/features/automations/action-executors/edit-field.executor.ts +0 -26
  210. package/src/features/automations/action-executors/index.ts +0 -37
  211. package/src/features/automations/action-executors/send-mail.executor.ts +0 -53
  212. package/src/features/automations/action-executors/set-variable.executor.ts +0 -159
  213. package/src/features/automations/action-executors/webhook.executor.ts +0 -67
  214. package/src/features/automations/automation-runner.ts +0 -85
  215. package/src/features/automations/automation-runner.utils.ts +0 -47
  216. package/src/features/automations/automations.handler.ts +0 -197
  217. package/src/features/automations/automations.schema.ts +0 -177
  218. package/src/features/automations/context-resolver.ts +0 -152
  219. package/src/features/automations/cron-runner.ts +0 -140
  220. package/src/features/automations/cron-runner.utils.ts +0 -44
  221. package/src/features/automations/filter-translation.ts +0 -46
  222. package/src/features/automations/index.ts +0 -16
  223. package/src/features/automations/template-grammar.ts +0 -245
  224. package/src/features/automations/var-access-resolver.ts +0 -140
  225. package/src/features/automations/when-evaluator.ts +0 -87
  226. package/src/features/automations/when-pushdown.ts +0 -57
  227. package/src/features/backrefs/__tests__/backrefs.handler.test.ts +0 -359
  228. package/src/features/backrefs/backrefs.handler.ts +0 -168
  229. package/src/features/backrefs/d1-backref.repository.ts +0 -84
  230. package/src/features/backrefs/index.ts +0 -5
  231. package/src/features/content/constants.ts +0 -16
  232. package/src/features/content/handlers/bulk.handler.ts +0 -185
  233. package/src/features/content/handlers/create.ts +0 -129
  234. package/src/features/content/handlers/delete.ts +0 -62
  235. package/src/features/content/handlers/facets.ts +0 -49
  236. package/src/features/content/handlers/get.ts +0 -120
  237. package/src/features/content/handlers/helpers.test.ts +0 -180
  238. package/src/features/content/handlers/helpers.ts +0 -93
  239. package/src/features/content/handlers/list.ts +0 -174
  240. package/src/features/content/handlers/update.ts +0 -174
  241. package/src/features/content/index.ts +0 -26
  242. package/src/features/draft/draft.handler.ts +0 -195
  243. package/src/features/draft/draft.middleware.ts +0 -66
  244. package/src/features/draft/index.ts +0 -5
  245. package/src/features/email/email.service.ts +0 -98
  246. package/src/features/email/email.types.ts +0 -119
  247. package/src/features/email/providers/resend.ts +0 -67
  248. package/src/features/email/providers/smtp.ts +0 -55
  249. package/src/features/email/templates/automation-mail.ts +0 -19
  250. package/src/features/email/templates/password-changed.ts +0 -63
  251. package/src/features/email/templates/password-reset.ts +0 -68
  252. package/src/features/email/templates/shell.ts +0 -96
  253. package/src/features/notifications/index.ts +0 -5
  254. package/src/features/notifications/notifications.handler.ts +0 -105
  255. package/src/features/password-reset/index.ts +0 -19
  256. package/src/features/password-reset/request.ts +0 -92
  257. package/src/features/password-reset/reset.ts +0 -102
  258. package/src/features/rotate-field/index.ts +0 -5
  259. package/src/features/rotate-field/rotate-field.handler.ts +0 -132
  260. package/src/features/rotate-field/rotate-field.schema.ts +0 -17
  261. package/src/features/schema/schema.handler.ts +0 -131
  262. package/src/features/seeds/index.ts +0 -5
  263. package/src/features/seeds/seeds.handler.test.ts +0 -632
  264. package/src/features/seeds/seeds.handler.ts +0 -693
  265. package/src/features/settings/__tests__/settings.handler.test.ts +0 -555
  266. package/src/features/settings/settings.handler.ts +0 -392
  267. package/src/features/setup/index.ts +0 -204
  268. package/src/features/stats/index.ts +0 -5
  269. package/src/features/stats/stats.handler.ts +0 -420
  270. package/src/features/webhooks/index.ts +0 -63
  271. package/src/index.ts +0 -65
  272. package/src/media-utils.ts +0 -82
  273. package/src/middleware/auth-providers.middleware.ts +0 -45
  274. package/src/middleware/observability.middleware.ts +0 -70
  275. package/src/middleware/rate-limit.middleware.ts +0 -45
  276. package/src/middleware/repository.middleware.ts +0 -114
  277. package/src/middleware/seed-registry.middleware.test.ts +0 -97
  278. package/src/middleware/seed-registry.middleware.ts +0 -21
  279. package/src/middleware/storage.middleware.ts +0 -25
  280. package/src/middleware.ts +0 -45
  281. package/src/public/access-policy.ts +0 -27
  282. package/src/public/api-key-middleware.ts +0 -57
  283. package/src/public/cache-utils.ts +0 -38
  284. package/src/public/entry-projection.ts +0 -46
  285. package/src/public/idempotency.ts +0 -23
  286. package/src/public/index.ts +0 -16
  287. package/src/public/problem-details.ts +0 -124
  288. package/src/public/public-add.ts +0 -114
  289. package/src/public/public-edit.ts +0 -163
  290. package/src/public/public-errors.ts +0 -19
  291. package/src/public/public-read.ts +0 -63
  292. package/src/public/public-routes.ts +0 -88
  293. package/src/public/query-builder.test.ts +0 -224
  294. package/src/public/query-builder.ts +0 -156
  295. package/src/public/rate-limit-middleware.ts +0 -34
  296. package/src/public/read-list.ts +0 -54
  297. package/src/public/read-single.ts +0 -48
  298. package/src/public/response-builder.ts +0 -30
  299. package/src/public/sanitize.ts +0 -69
  300. package/src/public/slug-utils.ts +0 -18
  301. package/src/rate-limit/cloudflare-rate-limiter.test.ts +0 -30
  302. package/src/rate-limit/cloudflare-rate-limiter.ts +0 -15
  303. package/src/rate-limit/in-memory-rate-limiter.test.ts +0 -37
  304. package/src/rate-limit/in-memory-rate-limiter.ts +0 -17
  305. package/src/rate-limit/no-op-rate-limiter.test.ts +0 -22
  306. package/src/rate-limit/no-op-rate-limiter.ts +0 -11
  307. package/src/search-utils.test.ts +0 -211
  308. package/src/search-utils.ts +0 -213
  309. package/src/search.ts +0 -65
  310. package/src/shared/apply-policies.test.ts +0 -81
  311. package/src/shared/apply-policies.ts +0 -67
  312. package/src/shared/automations.repository.d1.ts +0 -150
  313. package/src/shared/background-notification-service.test.ts +0 -62
  314. package/src/shared/background-notification-service.ts +0 -52
  315. package/src/shared/base.repository.d1.ts +0 -32
  316. package/src/shared/content-utils.test.ts +0 -165
  317. package/src/shared/content-utils.ts +0 -86
  318. package/src/shared/content.repository.d1.test.ts +0 -458
  319. package/src/shared/content.repository.d1.ts +0 -851
  320. package/src/shared/d1-activity-log.repository.test.ts +0 -140
  321. package/src/shared/d1-activity-log.repository.ts +0 -105
  322. package/src/shared/d1-activity-logger.test.ts +0 -86
  323. package/src/shared/d1-activity-logger.ts +0 -67
  324. package/src/shared/d1-analytics.repository.test.ts +0 -78
  325. package/src/shared/d1-analytics.repository.ts +0 -85
  326. package/src/shared/d1-content-scan.repository.test.ts +0 -80
  327. package/src/shared/d1-content-scan.repository.ts +0 -37
  328. package/src/shared/d1-notification.repository.test.ts +0 -128
  329. package/src/shared/d1-notification.repository.ts +0 -118
  330. package/src/shared/d1-password-reset-token.repository.test.ts +0 -81
  331. package/src/shared/d1-password-reset-token.repository.ts +0 -56
  332. package/src/shared/d1-search.repository.test.ts +0 -87
  333. package/src/shared/d1-search.repository.ts +0 -88
  334. package/src/shared/d1-session.repository.test.ts +0 -125
  335. package/src/shared/d1-session.repository.ts +0 -102
  336. package/src/shared/d1-setup-checklist.repository.ts +0 -36
  337. package/src/shared/d1-user.repository.test.ts +0 -151
  338. package/src/shared/d1-user.repository.ts +0 -119
  339. package/src/shared/d1-widget.repository.test.ts +0 -221
  340. package/src/shared/d1-widget.repository.ts +0 -341
  341. package/src/shared/demo-data-sql.ts +0 -54
  342. package/src/shared/demo-data.repository.d1.ts +0 -20
  343. package/src/shared/execution-context-scheduler.ts +0 -13
  344. package/src/shared/fixed-clock.ts +0 -25
  345. package/src/shared/fts-sync.ts +0 -8
  346. package/src/shared/idempotency.repository.d1.test.ts +0 -83
  347. package/src/shared/idempotency.repository.d1.ts +0 -60
  348. package/src/shared/in-memory-activity-logger.ts +0 -19
  349. package/src/shared/in-memory-notification-service.ts +0 -19
  350. package/src/shared/in-memory-seed.repository.ts +0 -51
  351. package/src/shared/media.repository.d1.test.ts +0 -107
  352. package/src/shared/media.repository.d1.ts +0 -68
  353. package/src/shared/qstash-notification-service.test.ts +0 -67
  354. package/src/shared/qstash-notification-service.ts +0 -55
  355. package/src/shared/query-utils.ts +0 -141
  356. package/src/shared/request-utils.ts +0 -26
  357. package/src/shared/schema-mutator.d1.ts +0 -64
  358. package/src/shared/seed-layout.repository.d1.test.ts +0 -114
  359. package/src/shared/seed-layout.repository.d1.ts +0 -62
  360. package/src/shared/seed-registry-cache.test.ts +0 -68
  361. package/src/shared/seed-registry-cache.ts +0 -49
  362. package/src/shared/seed.repository.d1.test.ts +0 -143
  363. package/src/shared/seed.repository.d1.ts +0 -98
  364. package/src/shared/sequential-id-generator.ts +0 -33
  365. package/src/shared/site-settings.repository.d1.ts +0 -53
  366. package/src/shared/storage/factory.ts +0 -41
  367. package/src/shared/storage/s3-bucket.ts +0 -195
  368. package/src/shared/storage-utils.ts +0 -40
  369. package/src/shared/system-stats.repository.d1.test.ts +0 -58
  370. package/src/shared/system-stats.repository.d1.ts +0 -48
  371. package/src/types.ts +0 -87
  372. package/src/upload.ts +0 -198
  373. package/src/widget.ts +0 -212
@@ -1,341 +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 type {
7
- Seed,
8
- IWidgetRepository,
9
- AggregateFormula,
10
- TimeWindow,
11
- LeaderboardEntry,
12
- LeaderboardOptions,
13
- TimeseriesPoint,
14
- WidgetListOptions,
15
- WidgetListResult,
16
- GrowthResult,
17
- } from '@beechcms/core'
18
-
19
- const SYSTEM_COLUMNS: ReadonlySet<string> = new Set([
20
- 'id',
21
- 'slug',
22
- 'status',
23
- 'created_at',
24
- 'updated_at',
25
- ])
26
-
27
- const ALLOWED_FILTER_OPERATORS: ReadonlySet<string> = new Set([
28
- 'eq',
29
- '=',
30
- 'neq',
31
- '!=',
32
- 'like',
33
- 'gt',
34
- '>',
35
- 'lt',
36
- '<',
37
- ])
38
-
39
- const UNSAFE_COLUMN_ERROR = 'UNSAFE_COLUMN'
40
-
41
- /**
42
- * D1-backed implementation of {@link IWidgetRepository}.
43
- *
44
- * All user-supplied values are bound via parameterised statements. Column
45
- * aliases originating from query strings are validated against the seed
46
- * before being composed into the SQL string, preventing column-name
47
- * injection. SQL keywords (aggregate function names, ORDER direction) are
48
- * selected via hardcoded branches.
49
- */
50
- export class D1WidgetRepository implements IWidgetRepository {
51
- constructor(private readonly database: D1Database) {}
52
-
53
- async aggregate(seed: Seed, formula: AggregateFormula, window: TimeWindow): Promise<number> {
54
- const aggregateExpression = this.buildAggregateExpression(seed, formula)
55
- const timeWindowFilter = this.buildTimeWindowFilter(window)
56
- const tableName = `content_${seed.slug}`
57
-
58
- const sql =
59
- `SELECT ${aggregateExpression} as computed_value
60
- FROM ${tableName}
61
- WHERE ${timeWindowFilter}`
62
-
63
- const row = await this.database.prepare(sql).first<{ computed_value: number | null }>()
64
- return row?.computed_value ?? 0
65
- }
66
-
67
- async growth(
68
- seed: Seed,
69
- formula: AggregateFormula,
70
- window: TimeWindow,
71
- ): Promise<GrowthResult> {
72
- const aggregateExpression = this.buildAggregateExpression(seed, formula)
73
- const { currentFilter, previousFilter } = this.buildPreviousWindowFilter(window)
74
- const tableName = `content_${seed.slug}`
75
-
76
- const [currentRow, previousRow] = await Promise.all([
77
- this.database
78
- .prepare(`SELECT ${aggregateExpression} as computed_value FROM ${tableName} WHERE ${currentFilter}`)
79
- .first<{ computed_value: number | null }>(),
80
- this.database
81
- .prepare(`SELECT ${aggregateExpression} as computed_value FROM ${tableName} WHERE ${previousFilter}`)
82
- .first<{ computed_value: number | null }>(),
83
- ])
84
-
85
- return {
86
- currentValue: currentRow?.computed_value ?? 0,
87
- previousValue: previousRow?.computed_value ?? 0,
88
- }
89
- }
90
-
91
- async leaderboard(seed: Seed, options: LeaderboardOptions): Promise<LeaderboardEntry[]> {
92
- const scoreColumn = this.resolveColumnExpression(seed, options.scoreColumn)
93
- const labelColumn = this.resolveColumnExpression(seed, seed.displayNameAlias)
94
- const tableName = `content_${seed.slug}`
95
- const orderClause = options.orderDirection === 'ASC'
96
- ? `ORDER BY CAST(${scoreColumn} AS REAL) ASC`
97
- : `ORDER BY CAST(${scoreColumn} AS REAL) DESC`
98
-
99
- const sql =
100
- `SELECT id, ${labelColumn} as label, ${scoreColumn} as score
101
- FROM ${tableName}
102
- WHERE ${scoreColumn} IS NOT NULL
103
- ${orderClause}
104
- LIMIT ?`
105
-
106
- const rows = await this.database
107
- .prepare(sql)
108
- .bind(options.limit)
109
- .all<{ id: string; label: string | null; score: number | string | null }>()
110
-
111
- return (rows.results ?? []).map(row => ({
112
- id: row.id,
113
- label: row.label ?? row.id,
114
- score: row.score ?? 0,
115
- }))
116
- }
117
-
118
- async list(seed: Seed, options: WidgetListOptions): Promise<WidgetListResult> {
119
- const tableName = `content_${seed.slug}`
120
- const conditions: string[] = []
121
- const bindings: unknown[] = []
122
-
123
- const trimmedSearch = options.search?.trim() ?? ''
124
- if (trimmedSearch.length > 0) {
125
- const displayColumn = this.resolveColumnExpression(seed, seed.displayNameAlias)
126
- conditions.push(`${displayColumn} LIKE ?`)
127
- bindings.push(`%${trimmedSearch}%`)
128
- }
129
-
130
- for (const filter of options.filters ?? []) {
131
- this.appendFilterCondition(seed, filter, conditions, bindings)
132
- }
133
-
134
- const whereSql = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
135
- const orderClause = this.buildListOrderClause(seed, options)
136
- const branchColumns = seed.branches.map(branch => branch.alias).join(', ')
137
- const selectColumns = branchColumns.length > 0
138
- ? `id, slug, status, created_at, updated_at, ${branchColumns}`
139
- : `id, slug, status, created_at, updated_at`
140
-
141
- const dataSql =
142
- `SELECT ${selectColumns}
143
- FROM ${tableName}
144
- ${whereSql}
145
- ${orderClause}
146
- LIMIT ? OFFSET ?`
147
-
148
- const countSql = `SELECT COUNT(*) as total FROM ${tableName} ${whereSql}`
149
-
150
- const [countRow, listRows] = await Promise.all([
151
- this.database.prepare(countSql).bind(...bindings).first<{ total: number }>(),
152
- this.database
153
- .prepare(dataSql)
154
- .bind(...bindings, options.limit, options.offset)
155
- .all<Record<string, unknown>>(),
156
- ])
157
-
158
- return {
159
- entries: listRows.results ?? [],
160
- totalCount: countRow?.total ?? 0,
161
- }
162
- }
163
-
164
- async timeseries(
165
- seed: Seed,
166
- formula: AggregateFormula,
167
- window: TimeWindow,
168
- groupColumn: string,
169
- ): Promise<TimeseriesPoint[]> {
170
- const groupColumnExpression = this.resolveColumnExpression(seed, groupColumn)
171
- const aggregateExpression = this.buildAggregateExpression(seed, formula)
172
- const timeWindowFilter = this.buildTimeWindowFilter(window)
173
- const tableName = `content_${seed.slug}`
174
-
175
- const dateBucketExpression = groupColumnExpression === 'created_at'
176
- ? `strftime('%Y-%m-%d', created_at, 'unixepoch')`
177
- : `strftime('%Y-%m-%d', CAST(${groupColumnExpression} AS INTEGER), 'unixepoch')`
178
-
179
- const sql =
180
- `SELECT ${dateBucketExpression} as bucket_label,
181
- ${aggregateExpression} as bucket_value
182
- FROM ${tableName}
183
- WHERE ${timeWindowFilter}
184
- GROUP BY bucket_label
185
- ORDER BY bucket_label ASC`
186
-
187
- const rows = await this.database
188
- .prepare(sql)
189
- .all<{ bucket_label: string | null; bucket_value: number | null }>()
190
-
191
- return (rows.results ?? []).map(row => ({
192
- label: row.bucket_label ?? '',
193
- value: row.bucket_value ?? 0,
194
- }))
195
- }
196
-
197
- /**
198
- * Throws UNSAFE_COLUMN if the alias is neither a system column nor a
199
- * declared seed branch, preventing SQL injection via column names.
200
- */
201
- private resolveColumnExpression(seed: Seed, alias: string): string {
202
- if (SYSTEM_COLUMNS.has(alias)) return alias
203
- const branch = seed.branches.find(candidate => candidate.alias === alias)
204
- if (!branch) throw new Error(UNSAFE_COLUMN_ERROR)
205
- return branch.alias
206
- }
207
-
208
- private buildAggregateExpression(seed: Seed, formula: AggregateFormula): string {
209
- switch (formula.op) {
210
- case 'count':
211
- return 'COUNT(*)'
212
- case 'sum': {
213
- const column = this.resolveColumnExpression(seed, formula.column)
214
- return `SUM(CAST(${column} AS REAL))`
215
- }
216
- case 'avg': {
217
- const column = this.resolveColumnExpression(seed, formula.column)
218
- return `AVG(CAST(${column} AS REAL))`
219
- }
220
- case 'min': {
221
- const column = this.resolveColumnExpression(seed, formula.column)
222
- return `MIN(CAST(${column} AS REAL))`
223
- }
224
- case 'max': {
225
- const column = this.resolveColumnExpression(seed, formula.column)
226
- return `MAX(CAST(${column} AS REAL))`
227
- }
228
- case 'countWhere':
229
- return this.buildCountWhereExpression(seed, formula.column, formula.value)
230
- case 'percentageOf': {
231
- const numerator = this.resolveColumnExpression(seed, formula.numeratorColumn)
232
- const denominator = this.resolveColumnExpression(seed, formula.denominatorColumn)
233
- return `CASE WHEN SUM(CAST(${denominator} AS REAL)) = 0 THEN 0 ELSE (SUM(CAST(${numerator} AS REAL)) * 100.0 / SUM(CAST(${denominator} AS REAL))) END`
234
- }
235
- }
236
- }
237
-
238
- private buildCountWhereExpression(seed: Seed, alias: string, value: unknown): string {
239
- const column = this.resolveColumnExpression(seed, alias)
240
- if (value === null) return `COUNT(CASE WHEN ${column} IS NULL THEN 1 END)`
241
- if (typeof value === 'boolean') {
242
- const numericValue = value ? 1 : 0
243
- return `COUNT(CASE WHEN ${column} = ${numericValue} THEN 1 END)`
244
- }
245
- if (typeof value === 'number' && Number.isFinite(value)) {
246
- return `COUNT(CASE WHEN CAST(${column} AS REAL) = ${value} THEN 1 END)`
247
- }
248
- const escaped = String(value).replace(/'/g, "''")
249
- return `COUNT(CASE WHEN ${column} = '${escaped}' THEN 1 END)`
250
- }
251
-
252
- private buildTimeWindowFilter(window: TimeWindow): string {
253
- switch (window) {
254
- case 'week': return "created_at > unixepoch('now', '-7 days')"
255
- case 'month': return "created_at > unixepoch('now', '-1 month')"
256
- case 'year': return "created_at > unixepoch('now', '-1 year')"
257
- case 'all': return '1=1'
258
- }
259
- }
260
-
261
- private buildPreviousWindowFilter(
262
- window: TimeWindow,
263
- ): { currentFilter: string; previousFilter: string } {
264
- switch (window) {
265
- case 'week':
266
- return {
267
- currentFilter: "created_at > unixepoch('now', '-7 days')",
268
- previousFilter: "created_at > unixepoch('now', '-14 days') AND created_at <= unixepoch('now', '-7 days')",
269
- }
270
- case 'month':
271
- return {
272
- currentFilter: "created_at > unixepoch('now', '-1 month')",
273
- previousFilter: "created_at > unixepoch('now', '-2 months') AND created_at <= unixepoch('now', '-1 month')",
274
- }
275
- case 'year':
276
- return {
277
- currentFilter: "created_at > unixepoch('now', '-1 year')",
278
- previousFilter: "created_at > unixepoch('now', '-2 years') AND created_at <= unixepoch('now', '-1 year')",
279
- }
280
- case 'all':
281
- return { currentFilter: '1=1', previousFilter: '1=0' }
282
- }
283
- }
284
-
285
- private appendFilterCondition(
286
- seed: Seed,
287
- filter: { column: string; op: string; value: unknown },
288
- conditions: string[],
289
- bindings: unknown[],
290
- ): void {
291
- if (!ALLOWED_FILTER_OPERATORS.has(filter.op)) return
292
-
293
- let column: string
294
- try {
295
- column = this.resolveColumnExpression(seed, filter.column)
296
- } catch {
297
- return
298
- }
299
-
300
- switch (filter.op) {
301
- case 'eq':
302
- case '=':
303
- conditions.push(`${column} = ?`)
304
- bindings.push(filter.value)
305
- return
306
- case 'neq':
307
- case '!=':
308
- conditions.push(`${column} != ?`)
309
- bindings.push(filter.value)
310
- return
311
- case 'like':
312
- conditions.push(`${column} LIKE ?`)
313
- bindings.push(filter.value)
314
- return
315
- case 'gt':
316
- case '>':
317
- conditions.push(`CAST(${column} AS REAL) > ?`)
318
- bindings.push(filter.value)
319
- return
320
- case 'lt':
321
- case '<':
322
- conditions.push(`CAST(${column} AS REAL) < ?`)
323
- bindings.push(filter.value)
324
- return
325
- }
326
- }
327
-
328
- private buildListOrderClause(seed: Seed, options: WidgetListOptions): string {
329
- let orderColumn = 'created_at'
330
- if (options.orderByColumn) {
331
- try {
332
- orderColumn = this.resolveColumnExpression(seed, options.orderByColumn)
333
- } catch {
334
- orderColumn = 'created_at'
335
- }
336
- }
337
- return options.orderDirection === 'DESC'
338
- ? `ORDER BY ${orderColumn} DESC`
339
- : `ORDER BY ${orderColumn} ASC`
340
- }
341
- }
@@ -1,54 +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
- // Compiled-in demo dataset. Workers have no runtime FS so the SQL from
6
- // migrations/0028_v040_seed_data.sql is bundled here as a string constant.
7
- export const DEMO_DATA_SQL = `
8
- INSERT OR IGNORE INTO content_team (id, slug, status, name, role, bio, photo, linkedIn, active, metaTitle, metaDescription, created_at, updated_at) VALUES
9
- ('tm-0001', 'flavio-de-musso', 'published', 'Flavio De Musso', 'Founder & Principal Engineer', 'Progetta architetture moderne per CMS headless e prodotti digitali edge-native.', 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&q=80', 'https://www.linkedin.com/in/flaviodemusso', 1, 'Flavio De Musso', 'Founder e creatore di Beech CMS.', unixepoch(), unixepoch()),
10
- ('tm-0002', 'laura-rossi', 'published', 'Laura Rossi', 'Product Designer', 'Cura la UX della dashboard e dei workflow editoriali. Specializzata in design system a scala.', 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&q=80', 'https://www.linkedin.com/in/', 1, 'Laura Rossi – Designer', 'UX designer focalizzata su strumenti per content team.', unixepoch(), unixepoch()),
11
- ('tm-0003', 'marco-bianchi', 'published', 'Marco Bianchi', 'Frontend Engineer', 'Sviluppa i field renderer React e il registry della UI. Appassionato di performance web.', 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=400&q=80', 'https://www.linkedin.com/in/', 1, 'Marco Bianchi – Engineer', 'Sviluppatore frontend specializzato in React e UX.', unixepoch(), unixepoch()),
12
- ('tm-0004', 'giulia-verdi', 'published', 'Giulia Verdi', 'Developer Relations', 'Aiuta i team a integrare Beech CMS nelle loro pipeline di sviluppo. Developer Advocate.', 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&q=80', 'https://www.linkedin.com/in/', 1, 'Giulia Verdi – DevRel', 'Developer advocate per la community Beech.', unixepoch(), unixepoch()),
13
- ('tm-0005', 'andrea-neri', 'published', 'Andrea Neri', 'Support Specialist', 'Supporto di primo livello per clienti enterprise e gestione della knowledge base.', 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400&q=80', 'https://www.linkedin.com/in/', 1, 'Andrea Neri – Support', 'Supporto tecnico e customer success.', unixepoch(), unixepoch());
14
-
15
- INSERT OR IGNORE INTO content_prodotti (id, slug, status, name, price, stock, active, coverImage, images, description, metaTitle, metaDescription, created_at, updated_at) VALUES
16
- ('prd-0001', 'beech-starter', 'published', 'Beech Starter', 490, 50, 1, 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=800&q=80', '[]', '<p>Piano entry-level per piccoli siti e landing page. Include il Botanical Engine, la dashboard, e 1GB di storage R2.</p>', 'Beech Starter', 'Piano starter per progetti piccoli e medi.', unixepoch(), unixepoch()),
17
- ('prd-0002', 'beech-pro', 'published', 'Beech Pro', 1290, 30, 1, 'https://images.unsplash.com/photo-1499951360447-b19be8fe80f5?w=800&q=80', '[]', '<p>Tutte le feature avanzate per team strutturati: draft workflow, analytics per-seed, FTS5 full-text search, 10GB storage R2.</p>', 'Beech Pro', 'Piano professionale per agenzie e team marketing.', unixepoch(), unixepoch()),
18
- ('prd-0003', 'beech-enterprise', 'published', 'Beech Enterprise', 3990, 10, 1, 'https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800&q=80', '[]', '<p>Funzionalità enterprise, audit log, SLA garantito, multi-ambiente staging/production, e storage R2 illimitato.</p>', 'Beech Enterprise', 'Soluzione enterprise per grandi organizzazioni.', unixepoch(), unixepoch()),
19
- ('prd-0004', 'implementazione-custom', 'published', 'Implementazione custom', 15000, 3, 1, 'https://images.unsplash.com/photo-1593642532744-d377ab507dc8?w=800&q=80', '[]', '<p>Servizio di implementazione su misura di Beech CMS: analisi dei requisiti, definizione dei Seed, deploy su Cloudflare, formazione team.</p>', 'Implementazione custom Beech', 'Servizio di onboarding e integrazione personalizzata.', unixepoch(), unixepoch()),
20
- ('prd-0005', 'workshop-botanical-engine', 'published', 'Workshop Botanical Engine', 990, 20, 1, 'https://images.unsplash.com/photo-1561070791-2526d30994b5?w=800&q=80', '[]', '<p>Workshop avanzato sul Botanical Engine e sul registry dei seed. Impara a definire Seed ottimali e a usare beech seed:load per deployare schemi.</p>', 'Workshop Botanical Engine', 'Formazione per team tecnici su Beech CMS.', unixepoch(), unixepoch());
21
-
22
- INSERT OR IGNORE INTO content_articoli (id, slug, status, title, publishedAt, coverImage, tags, body, metaTitle, metaDescription, created_at, updated_at) VALUES
23
- ('art-0001', 'benvenuto-nel-blog', 'published', 'Benvenuto nel blog Beech', 1736467200, 'https://images.unsplash.com/photo-1633356122544-f134324a6cee?w=800&q=80', '["news","annuncio"]', '<p>Scopri come Beech CMS gestisce contenuti schema-driven con tabelle SQL dedicate per ogni tipo di contenuto.</p><h3>Architettura moderna</h3><p>Beech v0.4.0 elimina il JSON blob e usa colonne reali per ogni Branch del Seed. Il risultato è performance migliorate e query native SQLite.</p>', 'Benvenuto nel blog Beech', 'Introduzione al CMS headless schema-driven.', unixepoch(), unixepoch()),
24
- ('art-0002', 'roadmap-2026', 'published', 'Roadmap prodotto 2026', 1738713600, 'https://images.unsplash.com/photo-1557804506-669a67965ba0?w=800&q=80', '["release","aggiornamento"]', '<p>Tutte le novità in arrivo per Beech CMS nel 2026: Doctype Architecture, CLI seed:load, FTS5 per-seed, e molto altro.</p><h3>Fase 1 — Core</h3><p>Il Botanical Engine diventa un compilatore di schema che genera DDL SQL dalla definizione TypeScript dei Seed.</p>', 'Roadmap Beech CMS 2026', 'Piano di sviluppo delle funzionalità per il 2026.', unixepoch(), unixepoch()),
25
- ('art-0003', 'migrare-da-wordpress', 'published', 'Migrare da WordPress a Beech', 1740182400, 'https://images.unsplash.com/photo-1542831371-29b0f74f9713?w=800&q=80', '["guida","cms"]', '<p>Guida pratica per migrare contenuti da WordPress a Beech CMS senza downtime. Esporta, trasforma e importa usando la Public API.</p><h3>Step 1: Esporta da WordPress</h3><p>Usa il plugin WP All Export per ottenere CSV o JSON dai post. Poi mappali alle colonne del tuo Seed.</p>', 'Migrazione da WordPress a Beech', 'Strategie per migrare contenuti in sicurezza.', unixepoch(), unixepoch()),
26
- ('art-0004', 'pattern-registry-ui', 'published', 'Il Pattern Registry della UI', 1740873600, 'https://images.unsplash.com/photo-1561070791-2526d30994b5?w=800&q=80', '["tutorial","guida"]', '<p>Come il registry dei field renderer semplifica la dashboard: ogni Branch type ha un renderer React dedicato, registrato centralmente e richiamato automaticamente.</p>', 'Pattern Registry UI Beech', 'Architettura dei componenti field-driven.', unixepoch(), unixepoch()),
27
- ('art-0005', 'edge-first-architecture', 'published', 'Architettura edge-first con Cloudflare', 1741132800, 'https://images.unsplash.com/photo-1558494949-ef010cbdcc31?w=800&q=80', '["news","aggiornamento"]', '<p>Perché Beech nasce per l''edge: latenza minima, zero cold start, D1 SQLite replicato globalmente su Cloudflare. Il CMS arriva dove si trovano gli utenti.</p>', 'Architettura edge-first', 'Vantaggi di un CMS progettato per l''edge.', unixepoch(), unixepoch());
28
-
29
- INSERT OR IGNORE INTO content_testimonianze (id, slug, status, author, company, quote, rating, date, photo, active, metaTitle, metaDescription, created_at, updated_at) VALUES
30
- ('tes-0001', 'acme-corp', 'published', 'Chiara Conti', 'Acme Corp', 'Con Beech CMS abbiamo ridotto del 60% il tempo di pubblicazione dei contenuti.', 5, 1737331200, 'https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?w=400&q=80', 1, 'Caso studio Acme', 'Come Acme ha accelerato i workflow editoriali.', unixepoch(), unixepoch()),
31
- ('tes-0002', 'studio-x', 'published', 'Luca Ferri', 'Studio X', 'La flessibilità degli schema ci ha permesso di iterare velocemente sul design del sito.', 4, 1738195200, 'https://images.unsplash.com/photo-1560250097-0b93528c311a?w=400&q=80', 1, 'Testimonianza Studio X', 'Feedback di un''agenzia digital sul CMS.', unixepoch(), unixepoch()),
32
- ('tes-0003', 'media-hub', 'published', 'Sara Galli', 'MediaHub', 'Finalmente un CMS pensato per il contenuto, non per il database. Team felicissimo.', 5, 1739404800, 'https://images.unsplash.com/photo-1580489944761-15a19d654956?w=400&q=80', 1, 'Testimonianza MediaHub', 'Perché i content team amano Beech.', unixepoch(), unixepoch()),
33
- ('tes-0004', 'shop-co', 'published', 'Davide Rizzi', 'ShopCo', 'Gestire landing e pagine prodotto è diventato molto più semplice. Alta raccomandazione.', 4, 1740268800, 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&q=80', 1, 'Testimonianza ShopCo', 'Beech CMS per e-commerce moderni.', unixepoch(), unixepoch()),
34
- ('tes-0005', 'fit-tech', 'published', 'Elena Santi', 'FitTech Inc', 'La dashboard è così intuitiva che il team marketing la usa senza training.', 5, 1741046400, 'https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?w=400&q=80', 1, 'Testimonianza FitTech', 'Esperienza di un team marketing su Beech CMS.', unixepoch(), unixepoch());
35
-
36
- INSERT OR IGNORE INTO content_pagine (id, slug, status, title, coverImage, body, metaTitle, metaDescription, created_at, updated_at) VALUES
37
- ('pag-0001', 'home', 'published', 'Homepage', 'https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=1200&q=80', '<h1>Beech CMS</h1><p>Il CMS schema-driven per l''era edge. Definisci i tuoi Seed in TypeScript, deploya su Cloudflare, e inizia a pubblicare.</p>', 'Beech CMS - Homepage', 'CMS moderno schema-driven per deployment edge.', unixepoch(), unixepoch()),
38
- ('pag-0002', 'il-prodotto', 'published', 'Il prodotto', 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=1200&q=80', '<h1>Perché Beech</h1><p>Architettura ibrida con tabelle SQL dedicate per Seed, Botanical Engine come compilatore di schema, e CLI per il deploy.</p>', 'Perché scegliere Beech', 'Scopri le funzionalità chiave di Beech CMS.', unixepoch(), unixepoch()),
39
- ('pag-0003', 'casi-studio', 'published', 'Casi studio', 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&q=80', '<h1>Storie di successo</h1><p>Come i team usano Beech ogni giorno per pubblicare contenuti velocemente con la dashboard schema-driven.</p>', 'Casi studio Beech', 'Raccolta di case study su Beech CMS.', unixepoch(), unixepoch()),
40
- ('pag-0004', 'pricing', 'published', 'Pricing', 'https://images.unsplash.com/photo-1554224155-6726b3ff858f?w=1200&q=80', '<h1>Piani e prezzi</h1><p>Scegli il piano adatto al tuo team: Starter, Pro o Enterprise. Tutti includono il Botanical Engine e la dashboard.</p>', 'Pricing Beech CMS', 'Confronta i piani Starter, Pro ed Enterprise.', unixepoch(), unixepoch()),
41
- ('pag-0005', 'contatti', 'published', 'Contatti', 'https://images.unsplash.com/photo-1423666639041-f56000c27a9a?w=1200&q=80', '<h1>Parliamo del tuo progetto</h1><p>Compila il form per una demo gratuita. Il team risponde entro 24 ore lavorative.</p>', 'Contatta Beech', 'Richiedi una demo o una consulenza su Beech CMS.', unixepoch(), unixepoch());
42
-
43
- INSERT OR IGNORE INTO content_clienti (id, slug, status, name, email, passwordHash, tier, phone, internalNote, registeredAt, active, created_at, updated_at) VALUES
44
- ('clt-0001', 'marco-rossi', 'published', 'Marco Rossi', 'marco.rossi@acmecorp.it', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', 'enterprise', '+39 02 1234 5678', 'Cliente storico. Sconto applicato del 20% su rinnovo annuale.', 1710460800, 1, unixepoch(), unixepoch()),
45
- ('clt-0002', 'laura-bianchi', 'published', 'Laura Bianchi', 'laura.bianchi@studiox.io', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92', 'pro', '+39 06 9876 5432', 'Contattare prima del rinnovo per upsell a enterprise.', 1736294400, 1, unixepoch(), unixepoch()),
46
- ('clt-0003', 'davide-ferrari', 'published', 'Davide Ferrari', 'davide@mediahub.eu', '1c8bfe8f801d79745c4631d09fff36c82aa37fc4cce4fc946683d7b336b63032', 'starter', '+39 011 4567 890', 'Ha richiesto supporto per migrazione da WordPress.', 1750550400, 1, unixepoch(), unixepoch()),
47
- ('clt-0004', 'giulia-verdi', 'published', 'Giulia Verdi', 'giulia.verdi@personal.me', '6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090', 'free', '+39 081 3210 987', 'Account free scaduto. Inviare offerta upgrade.', 1756771200, 0, unixepoch(), unixepoch()),
48
- ('clt-0005', 'sofia-costa', 'published', 'Sofia Costa', 'sofia.costa@fittech.com', '65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5', 'pro', NULL, NULL, 1769731200, 1, unixepoch(), unixepoch());
49
-
50
- INSERT OR IGNORE INTO content_messaggi (id, slug, status, name, email, subject, message, read, created_at, updated_at) VALUES
51
- ('msg-0001', 'msg-demo-1', 'published', 'Riccardo Esposito', 'riccardo@example.com', 'Richiesta demo Beech Pro', '<p>Buongiorno, siamo un''agenzia di 8 persone e vorremmo valutare Beech Pro per i nostri clienti. Possiamo fissare una call la prossima settimana?</p>', 0, unixepoch(), unixepoch()),
52
- ('msg-0002', 'msg-demo-2', 'published', 'Anna Marchetti', 'anna@startup.io', 'Domanda su FTS5 e ricerca full-text', '<p>Ciao team! Sto valutando Beech per un progetto di knowledge base. La ricerca full-text FTS5 funziona anche sulla versione Starter? Grazie.</p>', 1, unixepoch(), unixepoch()),
53
- ('msg-0003', 'msg-demo-3', 'published', 'Francesco Lombardi', 'f.lombardi@enterprise.it', 'Beech Enterprise — SLA e uptime', '<p>Siamo interessati al piano Enterprise. Quali SLA garantite? Abbiamo bisogno di 99.9% uptime per un''applicazione mission-critical.</p>', 0, unixepoch(), unixepoch());
54
- `
@@ -1,20 +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 type { IDemoDataRepository } from '@beechcms/core'
7
- import { DEMO_DATA_SQL } from './demo-data-sql'
8
-
9
- export class D1DemoDataRepository implements IDemoDataRepository {
10
- constructor(private readonly db: D1Database) {}
11
-
12
- async loadDemoData(): Promise<void> {
13
- const statements = DEMO_DATA_SQL
14
- .split(';')
15
- .map(s => s.trim())
16
- .filter(s => s.length > 0)
17
- .map(s => this.db.prepare(s))
18
- await this.db.batch(statements)
19
- }
20
- }
@@ -1,13 +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 { IScheduler } from '@beechcms/core'
6
-
7
- export class ExecutionContextScheduler implements IScheduler {
8
- constructor(private readonly ctx: ExecutionContext) {}
9
-
10
- waitUntil(promise: Promise<unknown>): void {
11
- this.ctx.waitUntil(promise)
12
- }
13
- }
@@ -1,25 +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 { IClock } from '@beechcms/core'
6
-
7
- const MILLISECONDS_PER_SECOND = 1000
8
-
9
- /**
10
- * Test-only IClock implementation that returns a frozen timestamp on every
11
- * call. Lets specs assert exact values for `createdAt`, `iat`, and
12
- * day-bucket computations without resorting to vi.useFakeTimers or
13
- * patching the global Date constructor.
14
- */
15
- export class FixedClock implements IClock {
16
- constructor(private readonly fixedNowMs: number) {}
17
-
18
- now(): number {
19
- return this.fixedNowMs
20
- }
21
-
22
- nowSeconds(): number {
23
- return Math.floor(this.fixedNowMs / MILLISECONDS_PER_SECOND)
24
- }
25
- }
@@ -1,8 +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
- // v0.4.0: FTS sync handled automatically by SQL triggers (generateFtsTriggers).
6
- // syncFts/deleteFts eliminated. This file kept as empty module to avoid import errors
7
- // during the Phase 4 → Phase 6 transition.
8
- export {}
@@ -1,83 +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 { D1IdempotencyRepository } from './idempotency.repository.d1'
7
-
8
- function makeMockDb(opts: { firstResult?: unknown } = {}) {
9
- const { firstResult = null } = opts
10
- const runMock = vi.fn().mockResolvedValue({ success: true })
11
- const firstMock = vi.fn().mockResolvedValue(firstResult)
12
- const bindMock = vi.fn(() => ({ run: runMock, first: firstMock }))
13
- const prepareMock = vi.fn(() => ({ bind: bindMock }))
14
- return { db: { prepare: prepareMock } as any, prepareMock, bindMock, runMock }
15
- }
16
-
17
- describe('D1IdempotencyRepository', () => {
18
- describe('lookup', () => {
19
- it('returns a mapped IdempotencyRecord when the key is found', async () => {
20
- const row = {
21
- idempotency_key: 'idem-1',
22
- request_fingerprint: 'fp-abc',
23
- response_status: 200,
24
- response_body: '{"ok":true}',
25
- expires_at: 9999,
26
- }
27
- const { db } = makeMockDb({ firstResult: row })
28
- const result = await new D1IdempotencyRepository(db).lookup('idem-1')
29
- expect(result).toEqual({
30
- key: 'idem-1',
31
- fingerprint: 'fp-abc',
32
- responseStatus: 200,
33
- responseBody: '{"ok":true}',
34
- expiresAt: 9999,
35
- })
36
- })
37
-
38
- it('returns null when the key is not found', async () => {
39
- const { db } = makeMockDb({ firstResult: null })
40
- expect(await new D1IdempotencyRepository(db).lookup('missing')).toBeNull()
41
- })
42
-
43
- it('queries the correct table', async () => {
44
- const { db, prepareMock } = makeMockDb()
45
- await new D1IdempotencyRepository(db).lookup('k')
46
- expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('public_idempotency_keys'))
47
- })
48
- })
49
-
50
- describe('store', () => {
51
- it('calls INSERT INTO public_idempotency_keys with upsert on conflict', async () => {
52
- const { db, prepareMock } = makeMockDb()
53
- await new D1IdempotencyRepository(db).store({
54
- key: 'idem-1', fingerprint: 'fp', responseStatus: 201, responseBody: '{}', expiresAt: 9999,
55
- })
56
- const sql = prepareMock.mock.calls[0][0] as string
57
- expect(sql).toContain('INSERT INTO public_idempotency_keys')
58
- expect(sql).toContain('ON CONFLICT')
59
- })
60
-
61
- it('binds key, fingerprint, status, body, and expiresAt', async () => {
62
- const { db, bindMock } = makeMockDb()
63
- await new D1IdempotencyRepository(db).store({
64
- key: 'idem-1', fingerprint: 'fp-x', responseStatus: 200, responseBody: '{"ok":1}', expiresAt: 8888,
65
- })
66
- const args = bindMock.mock.calls[0] as unknown[]
67
- expect(args).toContain('idem-1')
68
- expect(args).toContain('fp-x')
69
- expect(args).toContain(200)
70
- expect(args).toContain('{"ok":1}')
71
- expect(args).toContain(8888)
72
- })
73
- })
74
-
75
- describe('cleanup', () => {
76
- it('calls DELETE with the expiration timestamp', async () => {
77
- const { db, prepareMock, bindMock } = makeMockDb()
78
- await new D1IdempotencyRepository(db).cleanup(12345)
79
- expect(prepareMock).toHaveBeenCalledWith(expect.stringContaining('DELETE FROM public_idempotency_keys'))
80
- expect(bindMock).toHaveBeenCalledWith(12345)
81
- })
82
- })
83
- })
@@ -1,60 +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 { IdempotencyRepository, IdempotencyRecord } from '@beechcms/core'
6
- import { BaseD1Repository } from './base.repository.d1.js'
7
-
8
- export class D1IdempotencyRepository extends BaseD1Repository implements IdempotencyRepository {
9
- /**
10
- * Looks up an idempotency record by its key.
11
- */
12
- async lookup(idempotencyKey: string): Promise<IdempotencyRecord | null> {
13
- const idempotencyRecordResult = await this.database.prepare(
14
- `SELECT idempotency_key, request_fingerprint, response_status, response_body, expires_at
15
- FROM public_idempotency_keys WHERE idempotency_key = ? LIMIT 1`
16
- ).bind(idempotencyKey).first<any>()
17
-
18
- if (!idempotencyRecordResult) {
19
- return null
20
- }
21
-
22
- return {
23
- key: idempotencyRecordResult.idempotency_key,
24
- fingerprint: idempotencyRecordResult.request_fingerprint,
25
- responseStatus: idempotencyRecordResult.response_status,
26
- responseBody: idempotencyRecordResult.response_body,
27
- expiresAt: idempotencyRecordResult.expires_at
28
- }
29
- }
30
-
31
- /**
32
- * Stores or updates an idempotency record.
33
- */
34
- async store(idempotencyData: IdempotencyRecord): Promise<void> {
35
- await this.database.prepare(
36
- `INSERT INTO public_idempotency_keys (idempotency_key, request_fingerprint, response_status, response_body, created_at, expires_at)
37
- VALUES (?, ?, ?, ?, ?, ?)
38
- ON CONFLICT(idempotency_key) DO UPDATE SET
39
- request_fingerprint = excluded.request_fingerprint,
40
- response_status = excluded.response_status,
41
- response_body = excluded.response_body,
42
- created_at = excluded.created_at,
43
- expires_at = excluded.expires_at`
44
- ).bind(
45
- idempotencyData.key,
46
- idempotencyData.fingerprint,
47
- idempotencyData.responseStatus,
48
- idempotencyData.responseBody,
49
- Math.floor(Date.now() / 1000),
50
- idempotencyData.expiresAt
51
- ).run()
52
- }
53
-
54
- /**
55
- * Cleans up expired idempotency records.
56
- */
57
- async cleanup(expirationTimestamp: number): Promise<void> {
58
- await this.database.prepare(`DELETE FROM public_idempotency_keys WHERE expires_at < ?`).bind(expirationTimestamp).run()
59
- }
60
- }
@@ -1,19 +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 { IActivityLogger, ActivityLogEntry } from '@beechcms/core'
6
-
7
- /**
8
- * Test double for {@link IActivityLogger}.
9
- *
10
- * Captures every call into a public array so test assertions can verify
11
- * audit-trail side effects without touching D1. Preserves insertion order.
12
- */
13
- export class InMemoryActivityLogger implements IActivityLogger {
14
- public readonly entries: ActivityLogEntry[] = []
15
-
16
- log(entry: ActivityLogEntry): void {
17
- this.entries.push(entry)
18
- }
19
- }