@beechcms/api 0.6.0-preview.2 → 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 (375) hide show
  1. package/README.md +4 -4
  2. package/assets/dashboard/assets/chart-Cws4Yj6K.js +39 -0
  3. package/assets/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  4. package/assets/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  5. package/assets/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  6. package/assets/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  7. package/assets/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  8. package/assets/dashboard/assets/index-BjPpppHe.css +1 -0
  9. package/assets/dashboard/assets/index-CrW-zhkA.js +642 -0
  10. package/assets/dashboard/assets/lora-cyrillic-ext-wght-normal-YcVxMP-u.woff2 +0 -0
  11. package/assets/dashboard/assets/lora-cyrillic-wght-normal-UWPuZLjM.woff2 +0 -0
  12. package/assets/dashboard/assets/lora-latin-ext-wght-normal-C2Wlntb9.woff2 +0 -0
  13. package/assets/dashboard/assets/lora-latin-wght-normal-BiLcIKcI.woff2 +0 -0
  14. package/assets/dashboard/assets/lora-math-wght-normal-sOy7rEfW.woff2 +0 -0
  15. package/assets/dashboard/assets/lora-symbols-wght-normal-DQ5VrUkH.woff2 +0 -0
  16. package/assets/dashboard/assets/lora-vietnamese-wght-normal-CfJ7gtf3.woff2 +0 -0
  17. package/assets/dashboard/assets/pie-chart-recharts-CAxoDQp2.js +1 -0
  18. package/assets/dashboard/assets/timeseries-chart-recharts-CixDW-LM.js +1 -0
  19. package/assets/dashboard/index.html +2 -2
  20. package/dist/auth/__fixtures__/in-memory-hash-provider.d.ts +5 -0
  21. package/dist/auth/__fixtures__/static-token-service.d.ts +6 -0
  22. package/dist/auth/bcrypt-hash-provider.d.ts +8 -0
  23. package/dist/auth/constants.d.ts +10 -0
  24. package/dist/auth/jose-token-service.d.ts +14 -0
  25. package/dist/auth/login.d.ts +32 -0
  26. package/dist/auth/refresh.d.ts +1 -0
  27. package/dist/factory.d.ts +39 -0
  28. package/dist/features/automations/action-executors/create-entry.executor.d.ts +7 -0
  29. package/dist/features/automations/action-executors/edit-field.executor.d.ts +7 -0
  30. package/dist/features/automations/action-executors/index.d.ts +13 -0
  31. package/dist/features/automations/action-executors/send-mail.executor.d.ts +7 -0
  32. package/dist/features/automations/action-executors/set-variable.executor.d.ts +15 -0
  33. package/dist/features/automations/action-executors/webhook.executor.d.ts +7 -0
  34. package/dist/features/automations/automation-runner.d.ts +13 -0
  35. package/dist/features/automations/automation-runner.utils.d.ts +3 -0
  36. package/dist/features/automations/automations.handler.d.ts +7 -0
  37. package/dist/features/automations/automations.schema.d.ts +191 -0
  38. package/dist/features/automations/context-resolver.d.ts +8 -0
  39. package/dist/features/automations/cron-runner.d.ts +9 -0
  40. package/dist/features/automations/cron-runner.utils.d.ts +1 -0
  41. package/dist/features/automations/filter-translation.d.ts +9 -0
  42. package/dist/features/automations/index.d.ts +7 -0
  43. package/dist/features/automations/template-grammar.d.ts +73 -0
  44. package/dist/features/automations/var-access-resolver.d.ts +6 -0
  45. package/dist/features/automations/when-evaluator.d.ts +3 -0
  46. package/dist/features/automations/when-pushdown.d.ts +2 -0
  47. package/dist/features/backrefs/backrefs.handler.d.ts +3 -0
  48. package/dist/features/backrefs/d1-backref.repository.d.ts +17 -0
  49. package/dist/features/backrefs/index.d.ts +1 -0
  50. package/dist/features/content/constants.d.ts +12 -0
  51. package/dist/features/content/handlers/bulk.handler.d.ts +3 -0
  52. package/dist/features/content/handlers/create.d.ts +3 -0
  53. package/dist/features/content/handlers/delete.d.ts +3 -0
  54. package/dist/features/content/handlers/facets.d.ts +3 -0
  55. package/dist/features/content/handlers/get.d.ts +4 -0
  56. package/dist/features/content/handlers/helpers.d.ts +12 -0
  57. package/dist/features/content/handlers/list.d.ts +3 -0
  58. package/dist/features/content/handlers/update.d.ts +3 -0
  59. package/dist/features/content/index.d.ts +4 -0
  60. package/dist/features/dashboard-layout/dashboard-layout.handler.d.ts +7 -0
  61. package/dist/features/dashboard-layout/index.d.ts +1 -0
  62. package/dist/features/draft/draft.handler.d.ts +4 -0
  63. package/dist/features/draft/draft.middleware.d.ts +9 -0
  64. package/dist/features/draft/index.d.ts +1 -0
  65. package/{src/features/email/email.provider.ts → dist/features/email/email.provider.d.ts} +19 -24
  66. package/dist/features/email/email.service.d.ts +13 -0
  67. package/dist/features/email/email.types.d.ts +94 -0
  68. package/{src/features/email/index.ts → dist/features/email/index.d.ts} +3 -16
  69. package/dist/features/email/providers/resend.d.ts +36 -0
  70. package/dist/features/email/providers/smtp.d.ts +11 -0
  71. package/dist/features/email/templates/automation-mail.d.ts +8 -0
  72. package/dist/features/email/templates/password-changed.d.ts +15 -0
  73. package/dist/features/email/templates/password-reset.d.ts +18 -0
  74. package/dist/features/email/templates/shell.d.ts +58 -0
  75. package/dist/features/notifications/index.d.ts +1 -0
  76. package/dist/features/notifications/notifications.handler.d.ts +15 -0
  77. package/dist/features/password-reset/index.d.ts +6 -0
  78. package/dist/features/password-reset/request.d.ts +10 -0
  79. package/dist/features/password-reset/reset.d.ts +9 -0
  80. package/dist/features/rotate-field/index.d.ts +1 -0
  81. package/dist/features/rotate-field/rotate-field.handler.d.ts +7 -0
  82. package/dist/features/rotate-field/rotate-field.schema.d.ts +11 -0
  83. package/dist/features/schema/schema.handler.d.ts +7 -0
  84. package/dist/features/seeds/index.d.ts +1 -0
  85. package/dist/features/seeds/seeds.handler.d.ts +6 -0
  86. package/dist/features/settings/settings.handler.d.ts +7 -0
  87. package/dist/features/setup/index.d.ts +7 -0
  88. package/dist/features/stats/index.d.ts +1 -0
  89. package/dist/features/stats/stats.handler.d.ts +7 -0
  90. package/dist/features/webhooks/index.d.ts +3 -0
  91. package/dist/index.d.ts +6 -0
  92. package/dist/index.js +9666 -0
  93. package/dist/media-utils.d.ts +25 -0
  94. package/dist/middleware/auth-providers.middleware.d.ts +8 -0
  95. package/dist/middleware/observability.middleware.d.ts +20 -0
  96. package/dist/middleware/rate-limit.middleware.d.ts +9 -0
  97. package/dist/middleware/repository.middleware.d.ts +34 -0
  98. package/dist/middleware/seed-registry.middleware.d.ts +10 -0
  99. package/dist/middleware/storage.middleware.d.ts +7 -0
  100. package/dist/middleware.d.ts +11 -0
  101. package/dist/public/access-policy.d.ts +12 -0
  102. package/dist/public/api-key-middleware.d.ts +6 -0
  103. package/dist/public/cache-utils.d.ts +10 -0
  104. package/dist/public/entry-projection.d.ts +2 -0
  105. package/dist/public/idempotency.d.ts +9 -0
  106. package/dist/public/index.d.ts +11 -0
  107. package/dist/public/problem-details.d.ts +31 -0
  108. package/dist/public/public-add.d.ts +3 -0
  109. package/dist/public/public-edit.d.ts +5 -0
  110. package/dist/public/public-errors.d.ts +13 -0
  111. package/dist/public/public-read.d.ts +3 -0
  112. package/dist/public/public-routes.d.ts +3 -0
  113. package/dist/public/query-builder.d.ts +28 -0
  114. package/dist/public/rate-limit-middleware.d.ts +3 -0
  115. package/dist/public/read-list.d.ts +26 -0
  116. package/dist/public/read-single.d.ts +22 -0
  117. package/dist/public/response-builder.d.ts +22 -0
  118. package/dist/public/sanitize.d.ts +23 -0
  119. package/{src/public/slug-utils.ts → dist/public/slug-utils.d.ts} +3 -10
  120. package/dist/rate-limit/cloudflare-rate-limiter.d.ts +6 -0
  121. package/dist/rate-limit/in-memory-rate-limiter.d.ts +7 -0
  122. package/dist/rate-limit/no-op-rate-limiter.d.ts +4 -0
  123. package/dist/search-utils.d.ts +54 -0
  124. package/dist/search.d.ts +6 -0
  125. package/dist/shared/apply-policies.d.ts +10 -0
  126. package/dist/shared/automations.repository.d1.d.ts +12 -0
  127. package/dist/shared/background-notification-service.d.ts +18 -0
  128. package/dist/shared/base.repository.d1.d.ts +18 -0
  129. package/dist/shared/content-utils.d.ts +21 -0
  130. package/dist/shared/content.repository.d1.d.ts +68 -0
  131. package/dist/shared/d1-activity-log.repository.d.ts +13 -0
  132. package/dist/shared/d1-activity-logger.d.ts +20 -0
  133. package/dist/shared/d1-analytics.repository.d.ts +20 -0
  134. package/dist/shared/d1-content-scan.repository.d.ts +7 -0
  135. package/dist/shared/d1-notification.repository.d.ts +17 -0
  136. package/dist/shared/d1-password-reset-token.repository.d.ts +10 -0
  137. package/dist/shared/d1-search.repository.d.ts +14 -0
  138. package/dist/shared/d1-session.repository.d.ts +12 -0
  139. package/dist/shared/d1-setup-checklist.repository.d.ts +12 -0
  140. package/dist/shared/d1-user.repository.d.ts +18 -0
  141. package/dist/shared/d1-widget.repository.d.ts +31 -0
  142. package/dist/shared/dashboard-layout.repository.d1.d.ts +9 -0
  143. package/dist/shared/demo-data-sql.d.ts +1 -0
  144. package/dist/shared/demo-data.repository.d1.d.ts +6 -0
  145. package/dist/shared/execution-context-scheduler.d.ts +6 -0
  146. package/dist/shared/fixed-clock.d.ts +13 -0
  147. package/dist/shared/fts-sync.d.ts +1 -0
  148. package/dist/shared/idempotency.repository.d1.d.ts +16 -0
  149. package/dist/shared/in-memory-activity-logger.d.ts +11 -0
  150. package/dist/shared/in-memory-notification-service.d.ts +11 -0
  151. package/dist/shared/in-memory-seed.repository.d.ts +18 -0
  152. package/dist/shared/media.repository.d1.d.ts +35 -0
  153. package/dist/shared/qstash-notification-service.d.ts +18 -0
  154. package/dist/shared/query-utils.d.ts +36 -0
  155. package/dist/shared/request-utils.d.ts +13 -0
  156. package/dist/shared/schema-mutator.d1.d.ts +12 -0
  157. package/dist/shared/seed-layout.repository.d1.d.ts +9 -0
  158. package/dist/shared/seed-registry-cache.d.ts +12 -0
  159. package/dist/shared/seed.repository.d1.d.ts +13 -0
  160. package/dist/shared/sequential-id-generator.d.ts +13 -0
  161. package/dist/shared/site-settings.repository.d1.d.ts +7 -0
  162. package/dist/shared/storage/factory.d.ts +8 -0
  163. package/dist/shared/storage/s3-bucket.d.ts +43 -0
  164. package/dist/shared/storage-utils.d.ts +7 -0
  165. package/dist/shared/system-stats.repository.d1.d.ts +25 -0
  166. package/dist/types.d.ts +85 -0
  167. package/dist/upload.d.ts +16 -0
  168. package/dist/widget.d.ts +7 -0
  169. package/package.json +27 -18
  170. package/assets/dashboard/assets/chart-BrJFdsGZ.js +0 -39
  171. package/assets/dashboard/assets/index-CIaoMxuB.css +0 -1
  172. package/assets/dashboard/assets/index-wTKgygsg.js +0 -636
  173. package/assets/dashboard/assets/pie-chart-recharts-BEaE1FOE.js +0 -1
  174. package/assets/dashboard/assets/timeseries-chart-recharts-DmWM6imF.js +0 -1
  175. package/src/auth/__fixtures__/in-memory-hash-provider.ts +0 -17
  176. package/src/auth/__fixtures__/static-token-service.ts +0 -22
  177. package/src/auth/bcrypt-hash-provider.ts +0 -24
  178. package/src/auth/constants.ts +0 -14
  179. package/src/auth/generate-refresh-token.test.ts +0 -23
  180. package/src/auth/hash-provider.test.ts +0 -55
  181. package/src/auth/jose-token-service.ts +0 -62
  182. package/src/auth/jwt-claims-passthrough.test.ts +0 -87
  183. package/src/auth/login.test.ts +0 -100
  184. package/src/auth/login.ts +0 -78
  185. package/src/auth/refresh.ts +0 -11
  186. package/src/auth/token-service.test.ts +0 -90
  187. package/src/factory.ts +0 -370
  188. package/src/features/automations/__tests__/action-executors.test.ts +0 -255
  189. package/src/features/automations/__tests__/automation-runner.test.ts +0 -196
  190. package/src/features/automations/__tests__/automation-runner.utils.test.ts +0 -60
  191. package/src/features/automations/__tests__/automations.handler.test.ts +0 -264
  192. package/src/features/automations/__tests__/automations.repository.test.ts +0 -163
  193. package/src/features/automations/__tests__/automations.schema.test.ts +0 -224
  194. package/src/features/automations/__tests__/context-resolver.test.ts +0 -126
  195. package/src/features/automations/__tests__/cron-runner.test.ts +0 -267
  196. package/src/features/automations/__tests__/cron-runner.utils.test.ts +0 -144
  197. package/src/features/automations/__tests__/set-variable.executor.test.ts +0 -383
  198. package/src/features/automations/__tests__/template-grammar.test.ts +0 -308
  199. package/src/features/automations/__tests__/webhook.executor.test.ts +0 -142
  200. package/src/features/automations/__tests__/when-evaluator.test.ts +0 -274
  201. package/src/features/automations/__tests__/when-pushdown.test.ts +0 -281
  202. package/src/features/automations/action-executors/create-entry.executor.ts +0 -27
  203. package/src/features/automations/action-executors/edit-field.executor.ts +0 -26
  204. package/src/features/automations/action-executors/index.ts +0 -37
  205. package/src/features/automations/action-executors/send-mail.executor.ts +0 -53
  206. package/src/features/automations/action-executors/set-variable.executor.ts +0 -159
  207. package/src/features/automations/action-executors/webhook.executor.ts +0 -67
  208. package/src/features/automations/automation-runner.ts +0 -85
  209. package/src/features/automations/automation-runner.utils.ts +0 -47
  210. package/src/features/automations/automations.handler.ts +0 -197
  211. package/src/features/automations/automations.schema.ts +0 -177
  212. package/src/features/automations/context-resolver.ts +0 -152
  213. package/src/features/automations/cron-runner.ts +0 -140
  214. package/src/features/automations/cron-runner.utils.ts +0 -44
  215. package/src/features/automations/filter-translation.ts +0 -46
  216. package/src/features/automations/index.ts +0 -16
  217. package/src/features/automations/template-grammar.ts +0 -245
  218. package/src/features/automations/var-access-resolver.ts +0 -140
  219. package/src/features/automations/when-evaluator.ts +0 -87
  220. package/src/features/automations/when-pushdown.ts +0 -57
  221. package/src/features/backrefs/__tests__/backrefs.handler.test.ts +0 -359
  222. package/src/features/backrefs/backrefs.handler.ts +0 -168
  223. package/src/features/backrefs/d1-backref.repository.ts +0 -84
  224. package/src/features/backrefs/index.ts +0 -5
  225. package/src/features/content/constants.ts +0 -16
  226. package/src/features/content/handlers/bulk.handler.ts +0 -185
  227. package/src/features/content/handlers/create.ts +0 -129
  228. package/src/features/content/handlers/delete.ts +0 -62
  229. package/src/features/content/handlers/facets.ts +0 -49
  230. package/src/features/content/handlers/get.ts +0 -120
  231. package/src/features/content/handlers/helpers.test.ts +0 -180
  232. package/src/features/content/handlers/helpers.ts +0 -93
  233. package/src/features/content/handlers/list.ts +0 -174
  234. package/src/features/content/handlers/update.ts +0 -174
  235. package/src/features/content/index.ts +0 -26
  236. package/src/features/dashboard-layout/__tests__/dashboard-layout.handler.test.ts +0 -388
  237. package/src/features/dashboard-layout/dashboard-layout.handler.ts +0 -206
  238. package/src/features/dashboard-layout/index.ts +0 -5
  239. package/src/features/draft/draft.handler.ts +0 -195
  240. package/src/features/draft/draft.middleware.ts +0 -66
  241. package/src/features/draft/index.ts +0 -5
  242. package/src/features/email/email.service.ts +0 -98
  243. package/src/features/email/email.types.ts +0 -119
  244. package/src/features/email/providers/resend.ts +0 -67
  245. package/src/features/email/providers/smtp.ts +0 -55
  246. package/src/features/email/templates/automation-mail.ts +0 -19
  247. package/src/features/email/templates/password-changed.ts +0 -63
  248. package/src/features/email/templates/password-reset.ts +0 -68
  249. package/src/features/email/templates/shell.ts +0 -96
  250. package/src/features/notifications/index.ts +0 -5
  251. package/src/features/notifications/notifications.handler.ts +0 -105
  252. package/src/features/password-reset/index.ts +0 -19
  253. package/src/features/password-reset/request.ts +0 -92
  254. package/src/features/password-reset/reset.ts +0 -102
  255. package/src/features/rotate-field/index.ts +0 -5
  256. package/src/features/rotate-field/rotate-field.handler.ts +0 -132
  257. package/src/features/rotate-field/rotate-field.schema.ts +0 -17
  258. package/src/features/schema/schema.handler.ts +0 -131
  259. package/src/features/seeds/index.ts +0 -5
  260. package/src/features/seeds/seeds.handler.test.ts +0 -1199
  261. package/src/features/seeds/seeds.handler.ts +0 -558
  262. package/src/features/settings/__tests__/settings.handler.test.ts +0 -555
  263. package/src/features/settings/settings.handler.ts +0 -392
  264. package/src/features/setup/index.ts +0 -288
  265. package/src/features/stats/index.ts +0 -5
  266. package/src/features/stats/stats.handler.ts +0 -420
  267. package/src/features/webhooks/index.ts +0 -63
  268. package/src/index.ts +0 -65
  269. package/src/media-utils.ts +0 -82
  270. package/src/middleware/auth-providers.middleware.test.ts +0 -85
  271. package/src/middleware/auth-providers.middleware.ts +0 -45
  272. package/src/middleware/observability.middleware.test.ts +0 -93
  273. package/src/middleware/observability.middleware.ts +0 -70
  274. package/src/middleware/rate-limit.middleware.ts +0 -45
  275. package/src/middleware/repository.middleware.ts +0 -117
  276. package/src/middleware/seed-registry.middleware.test.ts +0 -97
  277. package/src/middleware/seed-registry.middleware.ts +0 -21
  278. package/src/middleware/storage.middleware.ts +0 -25
  279. package/src/middleware.ts +0 -45
  280. package/src/public/access-policy.ts +0 -27
  281. package/src/public/api-key-middleware.ts +0 -57
  282. package/src/public/cache-utils.ts +0 -38
  283. package/src/public/entry-projection.ts +0 -46
  284. package/src/public/idempotency.ts +0 -23
  285. package/src/public/index.ts +0 -16
  286. package/src/public/problem-details.ts +0 -124
  287. package/src/public/public-add.ts +0 -114
  288. package/src/public/public-edit.ts +0 -163
  289. package/src/public/public-errors.ts +0 -19
  290. package/src/public/public-read.ts +0 -63
  291. package/src/public/public-routes.ts +0 -88
  292. package/src/public/query-builder.test.ts +0 -224
  293. package/src/public/query-builder.ts +0 -156
  294. package/src/public/rate-limit-middleware.ts +0 -34
  295. package/src/public/read-list.ts +0 -54
  296. package/src/public/read-single.ts +0 -48
  297. package/src/public/response-builder.ts +0 -30
  298. package/src/public/sanitize.ts +0 -69
  299. package/src/rate-limit/cloudflare-rate-limiter.test.ts +0 -30
  300. package/src/rate-limit/cloudflare-rate-limiter.ts +0 -15
  301. package/src/rate-limit/in-memory-rate-limiter.test.ts +0 -37
  302. package/src/rate-limit/in-memory-rate-limiter.ts +0 -17
  303. package/src/rate-limit/no-op-rate-limiter.test.ts +0 -22
  304. package/src/rate-limit/no-op-rate-limiter.ts +0 -11
  305. package/src/search-utils.test.ts +0 -211
  306. package/src/search-utils.ts +0 -213
  307. package/src/search.ts +0 -65
  308. package/src/shared/apply-policies.test.ts +0 -81
  309. package/src/shared/apply-policies.ts +0 -67
  310. package/src/shared/automations.repository.d1.ts +0 -150
  311. package/src/shared/background-notification-service.test.ts +0 -62
  312. package/src/shared/background-notification-service.ts +0 -52
  313. package/src/shared/base.repository.d1.ts +0 -32
  314. package/src/shared/content-utils.test.ts +0 -165
  315. package/src/shared/content-utils.ts +0 -86
  316. package/src/shared/content.repository.d1.test.ts +0 -507
  317. package/src/shared/content.repository.d1.ts +0 -851
  318. package/src/shared/d1-activity-log.repository.test.ts +0 -140
  319. package/src/shared/d1-activity-log.repository.ts +0 -105
  320. package/src/shared/d1-activity-logger.test.ts +0 -86
  321. package/src/shared/d1-activity-logger.ts +0 -67
  322. package/src/shared/d1-analytics.repository.test.ts +0 -78
  323. package/src/shared/d1-analytics.repository.ts +0 -85
  324. package/src/shared/d1-content-scan.repository.test.ts +0 -80
  325. package/src/shared/d1-content-scan.repository.ts +0 -37
  326. package/src/shared/d1-notification.repository.test.ts +0 -128
  327. package/src/shared/d1-notification.repository.ts +0 -118
  328. package/src/shared/d1-password-reset-token.repository.test.ts +0 -81
  329. package/src/shared/d1-password-reset-token.repository.ts +0 -56
  330. package/src/shared/d1-search.repository.test.ts +0 -87
  331. package/src/shared/d1-search.repository.ts +0 -88
  332. package/src/shared/d1-session.repository.test.ts +0 -125
  333. package/src/shared/d1-session.repository.ts +0 -102
  334. package/src/shared/d1-setup-checklist.repository.ts +0 -36
  335. package/src/shared/d1-user.repository.test.ts +0 -151
  336. package/src/shared/d1-user.repository.ts +0 -119
  337. package/src/shared/d1-widget.repository.test.ts +0 -267
  338. package/src/shared/d1-widget.repository.ts +0 -385
  339. package/src/shared/dashboard-layout.repository.d1.test.ts +0 -135
  340. package/src/shared/dashboard-layout.repository.d1.ts +0 -58
  341. package/src/shared/demo-data-sql.ts +0 -216
  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 -58
  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.test.ts +0 -142
  358. package/src/shared/schema-mutator.d1.ts +0 -64
  359. package/src/shared/seed-layout.repository.d1.test.ts +0 -114
  360. package/src/shared/seed-layout.repository.d1.ts +0 -62
  361. package/src/shared/seed-registry-cache.test.ts +0 -68
  362. package/src/shared/seed-registry-cache.ts +0 -49
  363. package/src/shared/seed.repository.d1.test.ts +0 -143
  364. package/src/shared/seed.repository.d1.ts +0 -98
  365. package/src/shared/sequential-id-generator.ts +0 -33
  366. package/src/shared/site-settings.repository.d1.ts +0 -53
  367. package/src/shared/storage/factory.ts +0 -41
  368. package/src/shared/storage/s3-bucket.ts +0 -195
  369. package/src/shared/storage-utils.ts +0 -40
  370. package/src/shared/system-stats.repository.d1.test.ts +0 -58
  371. package/src/shared/system-stats.repository.d1.ts +0 -48
  372. package/src/types.ts +0 -90
  373. package/src/upload.ts +0 -198
  374. package/src/widget.test.ts +0 -85
  375. package/src/widget.ts +0 -251
@@ -1,383 +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 { executeSetVariable } from '../action-executors/set-variable.executor'
7
- import { resolveAutomationContext } from '../context-resolver'
8
- import { resolveVarAccess } from '../var-access-resolver'
9
- import { parseTemplateKey } from '../template-grammar'
10
- import type { ContentRepository, Seed } from '@beechcms/core'
11
-
12
- const MOCK_SEED: Seed = {
13
- slug: 'clienti',
14
- label: 'Clienti',
15
- branches: [
16
- { alias: 'name', label: 'Name', type: 'text', id: 'br_01' },
17
- { alias: 'email', label: 'Email', type: 'text', id: 'br_02' },
18
- { alias: 'total', label: 'Total', type: 'number', id: 'br_03' },
19
- ],
20
- } as unknown as Seed
21
-
22
- async function makeCtx(overrides: Partial<{
23
- entry: Record<string, unknown>
24
- variables: Record<string, unknown>
25
- repository: ContentRepository
26
- getSeed: (slug: string) => Seed | null
27
- seed: Seed
28
- }> = {}) {
29
- const entry = overrides.entry ?? { id: 'entry-1', customer_id: 'cust-42' }
30
- const variables = overrides.variables ?? {}
31
- const baseContext = await resolveAutomationContext({} as any, entry, [entry])
32
- const context = {
33
- triggerEntry: baseContext.triggerEntry,
34
- lookup(parsed: any, onMissing?: any) {
35
- if (parsed.kind === 'simple') {
36
- const varVal = (variables as any)[parsed.path]
37
- if (varVal !== undefined) return varVal
38
- if (parsed.path.includes('.')) {
39
- const [first, ...rest] = parsed.path.split('.')
40
- const varRoot = (variables as any)[first]
41
- if (varRoot !== undefined) {
42
- let cur: any = varRoot
43
- for (const k of rest) cur = cur?.[k]
44
- if (cur !== undefined) return cur
45
- }
46
- }
47
- } else if (parsed.kind === 'var_access') {
48
- return resolveVarAccess(parsed, variables, onMissing)
49
- }
50
- return baseContext.lookup(parsed, onMissing)
51
- },
52
- }
53
- return {
54
- entry,
55
- variables,
56
- repository: overrides.repository ?? {
57
- findMany: vi.fn().mockResolvedValue({ items: [] }),
58
- } as unknown as ContentRepository,
59
- getSeed: overrides.getSeed ?? vi.fn().mockReturnValue(MOCK_SEED),
60
- seed: overrides.seed ?? MOCK_SEED,
61
- context,
62
- }
63
- }
64
-
65
- describe('fixed_id mode', () => {
66
- it('1 — fixed_id set, record exists → variables[name] equals item', async () => {
67
- const item = { id: 'c_1', name: 'Mario', email: 'mario@x.com', total: 99 }
68
- const findMany = vi.fn().mockResolvedValue({ items: [item] })
69
- const ctx = await makeCtx({ repository: { findMany } as unknown as ContentRepository })
70
-
71
- await executeSetVariable(
72
- { type: 'set_variable', name: 'cliente', seed_slug: 'clienti', fixed_id: 'c_1' },
73
- ctx,
74
- )
75
-
76
- expect(ctx.variables.cliente).toEqual(item)
77
- const [, opts] = findMany.mock.calls[0]
78
- expect(opts.filters[0].conditions[0].value).toBe('c_1')
79
- expect(opts.pagination.limit).toBe(1)
80
- })
81
-
82
- it('2 — fixed_id set, record missing → null + console.warn', async () => {
83
- const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
84
- const ctx = await makeCtx({
85
- repository: { findMany: vi.fn().mockResolvedValue({ items: [] }) } as unknown as ContentRepository,
86
- })
87
-
88
- await executeSetVariable(
89
- { type: 'set_variable', name: 'cliente', seed_slug: 'clienti', fixed_id: 'ghost' },
90
- ctx,
91
- )
92
-
93
- expect(ctx.variables.cliente).toBeNull()
94
- expect(warnSpy).toHaveBeenCalled()
95
- })
96
-
97
- it('3 — fixed_id + column → { _value: item[column] }', async () => {
98
- const item = { id: 'c_1', name: 'Mario', email: 'mario@x.com', total: 99 }
99
- const ctx = await makeCtx({
100
- repository: { findMany: vi.fn().mockResolvedValue({ items: [item] }) } as unknown as ContentRepository,
101
- })
102
-
103
- await executeSetVariable(
104
- { type: 'set_variable', name: 'cliente', seed_slug: 'clienti', fixed_id: 'c_1', column: 'email' },
105
- ctx,
106
- )
107
-
108
- expect(ctx.variables.cliente).toEqual({ _value: 'mario@x.com' })
109
- })
110
-
111
- it('4 — fixed_id interpolation from this scope (dot notation)', async () => {
112
- const findMany = vi.fn().mockResolvedValue({ items: [] })
113
- const ctx = await makeCtx({
114
- entry: { id: 'e1', customer_id: 'cust-77' },
115
- repository: { findMany } as unknown as ContentRepository,
116
- })
117
-
118
- await executeSetVariable(
119
- { type: 'set_variable', name: 'cliente', seed_slug: 'clienti', fixed_id: '{{this.customer_id}}' },
120
- ctx,
121
- )
122
-
123
- const [, opts] = findMany.mock.calls[0]
124
- expect(opts.filters[0].conditions[0].value).toBe('cust-77')
125
- })
126
- })
127
-
128
- describe('collection mode', () => {
129
- it('5 — no filters: count, firstone, lastone, sum, pluck correct', async () => {
130
- const items = [
131
- { id: '1', name: 'A', email: 'a@x.com', total: 10, created_at: 1000 },
132
- { id: '2', name: 'B', email: 'b@x.com', total: 20, created_at: 2000 },
133
- { id: '3', name: 'C', email: 'c@x.com', total: 30, created_at: 500 },
134
- ]
135
- const ctx = await makeCtx({
136
- repository: { findMany: vi.fn().mockResolvedValue({ items }) } as unknown as ContentRepository,
137
- })
138
-
139
- await executeSetVariable(
140
- { type: 'set_variable', name: 'ordini', seed_slug: 'clienti' },
141
- ctx,
142
- )
143
-
144
- const result = ctx.variables.ordini as any
145
- expect(result.count).toBe(3)
146
- expect(result.sum.total).toBe(60)
147
- expect(result.firstone).toEqual(items[2])
148
- expect(result.lastone).toEqual(items[1])
149
- expect(result.pluck.name).toBe('A, B, C')
150
- })
151
-
152
- it('6 — collection with filters → findMany receives resolved filter groups', async () => {
153
- const findMany = vi.fn().mockResolvedValue({ items: [] })
154
- const ctx = await makeCtx({ repository: { findMany } as unknown as ContentRepository })
155
-
156
- await executeSetVariable(
157
- {
158
- type: 'set_variable',
159
- name: 'ordini',
160
- seed_slug: 'clienti',
161
- filters: [{ field: 'name', op: 'eq', value: 'Mario' }],
162
- },
163
- ctx,
164
- )
165
-
166
- const [, opts] = findMany.mock.calls[0]
167
- expect(opts.filters).toHaveLength(1)
168
- expect(opts.filters[0].column).toBe('name')
169
- })
170
-
171
- it('7 — collection with column pin: scalar aggregates + count non-null', async () => {
172
- const items = [
173
- { id: '1', total: 10, created_at: 1 },
174
- { id: '2', total: null, created_at: 2 },
175
- { id: '3', total: 20, created_at: 3 },
176
- ]
177
- const ctx = await makeCtx({
178
- repository: { findMany: vi.fn().mockResolvedValue({ items }) } as unknown as ContentRepository,
179
- })
180
-
181
- await executeSetVariable(
182
- { type: 'set_variable', name: 'r', seed_slug: 'clienti', column: 'total' },
183
- ctx,
184
- )
185
-
186
- const result = ctx.variables.r as any
187
- expect(result.count).toBe(2)
188
- expect(result.sum).toBe(30)
189
- expect(result.avg).toBe(15)
190
- expect(result.min).toBe(10)
191
- expect(result.max).toBe(20)
192
- })
193
-
194
- it('8 — seed_slug omitted → uses ctx.seed.slug', async () => {
195
- const findMany = vi.fn().mockResolvedValue({ items: [] })
196
- const triggerSeed: Seed = { ...MOCK_SEED, slug: 'ordini' }
197
- const ctx = await makeCtx({
198
- repository: { findMany } as unknown as ContentRepository,
199
- getSeed: vi.fn().mockReturnValue(triggerSeed),
200
- seed: triggerSeed,
201
- })
202
-
203
- await executeSetVariable(
204
- { type: 'set_variable', name: 'result' },
205
- ctx,
206
- )
207
-
208
- const [seed] = findMany.mock.calls[0]
209
- expect(seed.slug).toBe('ordini')
210
- })
211
-
212
- it('9 — seed not found → null + warn', async () => {
213
- const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
214
- const ctx = await makeCtx({ getSeed: vi.fn().mockReturnValue(null) })
215
-
216
- await executeSetVariable(
217
- { type: 'set_variable', name: 'x', seed_slug: 'ghost' },
218
- ctx,
219
- )
220
-
221
- expect(ctx.variables.x).toBeNull()
222
- expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('ghost'))
223
- })
224
-
225
- it('10 — filter value interpolated from a previously set variable', async () => {
226
- const findMany = vi.fn().mockResolvedValue({ items: [] })
227
- const ctx = await makeCtx({
228
- variables: { cliente: { id: 'cust-123' } } as Record<string, unknown>,
229
- repository: { findMany } as unknown as ContentRepository,
230
- })
231
-
232
- await executeSetVariable(
233
- {
234
- type: 'set_variable',
235
- name: 'ordini',
236
- seed_slug: 'clienti',
237
- filters: [{ field: 'customer_id', op: 'eq', value: '{{cliente.id}}' }],
238
- },
239
- ctx,
240
- )
241
-
242
- const [, opts] = findMany.mock.calls[0]
243
- expect(opts.filters[0].conditions[0].value).toBe('cust-123')
244
- })
245
-
246
- it('12 — date branch: sum/avg/min/max computed from ISO strings', async () => {
247
- const dateSeed: Seed = {
248
- ...MOCK_SEED,
249
- branches: [
250
- { alias: 'publishedAt', label: 'Published', type: 'date', id: 'br_d1' },
251
- ],
252
- } as unknown as Seed
253
- const items = [
254
- { id: '1', publishedAt: '2025-01-10T00:00:00.000Z', created_at: 1 },
255
- { id: '2', publishedAt: '2025-03-05T00:00:00.000Z', created_at: 2 },
256
- { id: '3', publishedAt: '2025-06-01T00:00:00.000Z', created_at: 3 },
257
- ]
258
- const ctx = await makeCtx({
259
- repository: { findMany: vi.fn().mockResolvedValue({ items }) } as unknown as ContentRepository,
260
- getSeed: vi.fn().mockReturnValue(dateSeed),
261
- seed: dateSeed,
262
- })
263
-
264
- await executeSetVariable({ type: 'set_variable', name: 'articoli', seed_slug: 'clienti' }, ctx)
265
-
266
- const result = ctx.variables.articoli as any
267
- expect(result.sum.publishedAt).toBeGreaterThan(0)
268
- expect(result.avg.publishedAt).toBeGreaterThan(0)
269
- expect(result.min.publishedAt).toBeLessThan(result.max.publishedAt)
270
- const minDate = new Date('2025-01-10T00:00:00.000Z').getTime() / 1000
271
- const maxDate = new Date('2025-06-01T00:00:00.000Z').getTime() / 1000
272
- expect(result.min.publishedAt).toBe(minDate)
273
- expect(result.max.publishedAt).toBe(maxDate)
274
- })
275
-
276
- it('13 — var_access inline condition on ISO date field', async () => {
277
- const items = [
278
- { id: '1', publishedAt: '2025-01-10T00:00:00.000Z', created_at: 1 },
279
- { id: '2', publishedAt: '2025-03-05T00:00:00.000Z', created_at: 2 },
280
- { id: '3', publishedAt: '2026-06-01T00:00:00.000Z', created_at: 3 },
281
- ]
282
- const ctx = await makeCtx({
283
- repository: { findMany: vi.fn().mockResolvedValue({ items }) } as unknown as ContentRepository,
284
- })
285
- await executeSetVariable({ type: 'set_variable', name: 'arts', seed_slug: 'clienti' }, ctx)
286
-
287
- const parsed = parseTemplateKey('arts.(publishedAt>1740000000).count')
288
- expect(parsed?.kind).toBe('var_access')
289
- const result = ctx.context.lookup(parsed!)
290
- // 1740000000 = ~2025-02-20; art-0002 (Mar 2025) and art-0003 (Jun 2026) qualify
291
- expect(result).toBe(2)
292
- })
293
-
294
- it('11 — _items is non-enumerable (not in JSON.stringify)', async () => {
295
- const items = [{ id: '1', name: 'A', email: 'a@x.com', total: 5, created_at: 1 }]
296
- const ctx = await makeCtx({
297
- repository: { findMany: vi.fn().mockResolvedValue({ items }) } as unknown as ContentRepository,
298
- })
299
-
300
- await executeSetVariable(
301
- { type: 'set_variable', name: 'ordini', seed_slug: 'clienti' },
302
- ctx,
303
- )
304
-
305
- const json = JSON.stringify(ctx.variables)
306
- expect(json).not.toContain('_items')
307
- const result = ctx.variables.ordini as any
308
- expect(result._items).toEqual(items)
309
- })
310
- })
311
-
312
- // ── Sprint 07: backward-compatibility with legacy load_type field ──────────────
313
-
314
- describe('legacy load_type backward compatibility', () => {
315
- it('L1 — load_type: fruit + single id-eq filter → upgraded to fixed_id path', async () => {
316
- const item = { id: 'c_1', name: 'Mario', email: 'mario@x.com', total: 99 }
317
- const findMany = vi.fn().mockResolvedValue({ items: [item] })
318
- const ctx = await makeCtx({ repository: { findMany } as unknown as ContentRepository })
319
-
320
- await executeSetVariable(
321
- {
322
- type: 'set_variable',
323
- name: 'cliente',
324
- seed_slug: 'clienti',
325
- load_type: 'fruit',
326
- filters: [{ field: 'id', op: 'eq', value: 'c_1' }],
327
- } as any,
328
- ctx,
329
- )
330
-
331
- // Should use fixed_id path: pagination.limit = 1, filter on id column
332
- const [, opts] = findMany.mock.calls[0]
333
- expect(opts.filters[0].column).toBe('id')
334
- expect(opts.filters[0].conditions[0].value).toBe('c_1')
335
- expect(opts.pagination.limit).toBe(1)
336
- expect(ctx.variables.cliente).toEqual(item)
337
- })
338
-
339
- it('L2 — load_type: fruit + non-id filter → collection path (degraded)', async () => {
340
- const items = [{ id: '1', name: 'Mario', email: 'mario@x.com', total: 10, created_at: 1 }]
341
- const findMany = vi.fn().mockResolvedValue({ items })
342
- const ctx = await makeCtx({ repository: { findMany } as unknown as ContentRepository })
343
-
344
- await executeSetVariable(
345
- {
346
- type: 'set_variable',
347
- name: 'clienti',
348
- seed_slug: 'clienti',
349
- load_type: 'fruit',
350
- filters: [{ field: 'name', op: 'eq', value: 'Mario' }],
351
- } as any,
352
- ctx,
353
- )
354
-
355
- // Non-id filter: falls through to collection path
356
- const result = ctx.variables.clienti as any
357
- expect(result.count).toBe(1)
358
- })
359
-
360
- it('L3 — load_type: branch → collection path', async () => {
361
- const items = [
362
- { id: '1', name: 'A', email: 'a@x.com', total: 10, created_at: 1 },
363
- { id: '2', name: 'B', email: 'b@x.com', total: 20, created_at: 2 },
364
- ]
365
- const findMany = vi.fn().mockResolvedValue({ items })
366
- const ctx = await makeCtx({ repository: { findMany } as unknown as ContentRepository })
367
-
368
- await executeSetVariable(
369
- {
370
- type: 'set_variable',
371
- name: 'ordini',
372
- seed_slug: 'clienti',
373
- load_type: 'branch',
374
- } as any,
375
- ctx,
376
- )
377
-
378
- const result = ctx.variables.ordini as any
379
- expect(result.count).toBe(2)
380
- expect(result.sum).toBeDefined()
381
- expect(result.pluck).toBeDefined()
382
- })
383
- })
@@ -1,308 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import { parseTemplateKey } from '../template-grammar'
7
- import type { ParsedKey, VarStep, InlineCondition } from '../template-grammar'
8
-
9
- describe('parseTemplateKey', () => {
10
- describe('simple keys', () => {
11
- it('returns simple for a plain field name', () => {
12
- expect(parseTemplateKey('title')).toEqual<ParsedKey>({ kind: 'simple', path: 'title' })
13
- })
14
-
15
- it('returns simple for a dot-path', () => {
16
- expect(parseTemplateKey('author.name')).toEqual<ParsedKey>({ kind: 'simple', path: 'author.name' })
17
- })
18
-
19
- it('returns simple for _count (legacy)', () => {
20
- expect(parseTemplateKey('_count')).toEqual<ParsedKey>({ kind: 'simple', path: '_count' })
21
- })
22
-
23
- it('returns null for empty string', () => {
24
- expect(parseTemplateKey('')).toBeNull()
25
- })
26
- })
27
-
28
- describe('two-token sugar: <scope>:<field>', () => {
29
- it('defaults to lastone selector for <seedSlug>:<field>', () => {
30
- expect(parseTemplateKey('customers:email')).toEqual<ParsedKey>({
31
- kind: 'scoped',
32
- scope: 'customers',
33
- selector: { kind: 'lastone' },
34
- op: 'field',
35
- field: 'email',
36
- })
37
- })
38
-
39
- it('defaults to lastone selector for this:<field>', () => {
40
- expect(parseTemplateKey('this:email')).toEqual<ParsedKey>({
41
- kind: 'scoped',
42
- scope: 'this',
43
- selector: { kind: 'lastone' },
44
- op: 'field',
45
- field: 'email',
46
- })
47
- })
48
-
49
- it('sugar: batch:count → batch:all:count', () => {
50
- expect(parseTemplateKey('batch:count')).toEqual<ParsedKey>({
51
- kind: 'scoped',
52
- scope: 'batch',
53
- selector: { kind: 'all' },
54
- op: 'count',
55
- field: null,
56
- })
57
- })
58
-
59
- it('sugar: customers:sum → customers:all:sum (aggregate sugar without subfield)', () => {
60
- const result = parseTemplateKey('customers:sum')
61
- expect(result).toMatchObject({ kind: 'scoped', scope: 'customers', selector: { kind: 'all' }, op: 'sum' })
62
- })
63
- })
64
-
65
- describe('three-token: <scope>:<selector>:<field>', () => {
66
- it('lastone selector', () => {
67
- expect(parseTemplateKey('customers:lastone:email')).toEqual<ParsedKey>({
68
- kind: 'scoped',
69
- scope: 'customers',
70
- selector: { kind: 'lastone' },
71
- op: 'field',
72
- field: 'email',
73
- })
74
- })
75
-
76
- it('firstone selector', () => {
77
- expect(parseTemplateKey('customers:firstone:name')).toEqual<ParsedKey>({
78
- kind: 'scoped',
79
- scope: 'customers',
80
- selector: { kind: 'firstone' },
81
- op: 'field',
82
- field: 'name',
83
- })
84
- })
85
-
86
- it('byid selector', () => {
87
- expect(parseTemplateKey('customers:byid(c_42):name')).toEqual<ParsedKey>({
88
- kind: 'scoped',
89
- scope: 'customers',
90
- selector: { kind: 'byid', id: 'c_42' },
91
- op: 'field',
92
- field: 'name',
93
- })
94
- })
95
-
96
- it('where selector', () => {
97
- expect(parseTemplateKey('orders:where(status=paid):total')).toEqual<ParsedKey>({
98
- kind: 'scoped',
99
- scope: 'orders',
100
- selector: { kind: 'where', alias: 'status', value: 'paid' },
101
- op: 'field',
102
- field: 'total',
103
- })
104
- })
105
-
106
- it('all:count aggregate', () => {
107
- expect(parseTemplateKey('orders:all:count')).toEqual<ParsedKey>({
108
- kind: 'scoped',
109
- scope: 'orders',
110
- selector: { kind: 'all' },
111
- op: 'count',
112
- field: null,
113
- })
114
- })
115
-
116
- it('all:sum:<subfield> aggregate', () => {
117
- expect(parseTemplateKey('orders:all:sum:total')).toEqual<ParsedKey>({
118
- kind: 'scoped',
119
- scope: 'orders',
120
- selector: { kind: 'all' },
121
- op: 'sum',
122
- field: 'total',
123
- })
124
- })
125
-
126
- it('all:avg aggregate', () => {
127
- expect(parseTemplateKey('orders:all:avg:amount')).toMatchObject({
128
- kind: 'scoped', scope: 'orders', op: 'avg', field: 'amount',
129
- })
130
- })
131
-
132
- it('all:min aggregate', () => {
133
- expect(parseTemplateKey('orders:all:min:price')).toMatchObject({
134
- kind: 'scoped', scope: 'orders', op: 'min', field: 'price',
135
- })
136
- })
137
-
138
- it('all:max aggregate', () => {
139
- expect(parseTemplateKey('orders:all:max:score')).toMatchObject({
140
- kind: 'scoped', scope: 'orders', op: 'max', field: 'score',
141
- })
142
- })
143
-
144
- it('all:pluck:<subfield> aggregate', () => {
145
- expect(parseTemplateKey('customers:all:pluck:email')).toEqual<ParsedKey>({
146
- kind: 'scoped',
147
- scope: 'customers',
148
- selector: { kind: 'all' },
149
- op: 'pluck',
150
- field: 'email',
151
- })
152
- })
153
-
154
- it('batch:all:count', () => {
155
- expect(parseTemplateKey('batch:all:count')).toEqual<ParsedKey>({
156
- kind: 'scoped',
157
- scope: 'batch',
158
- selector: { kind: 'all' },
159
- op: 'count',
160
- field: null,
161
- })
162
- })
163
-
164
- it('batch:all:pluck:email', () => {
165
- expect(parseTemplateKey('batch:all:pluck:email')).toMatchObject({
166
- kind: 'scoped', scope: 'batch', op: 'pluck', field: 'email',
167
- })
168
- })
169
- })
170
-
171
- describe('rejection rules', () => {
172
- it('returns null when aggregate used without all selector', () => {
173
- expect(parseTemplateKey('orders:lastone:sum')).toBeNull()
174
- })
175
-
176
- it('returns null when selector token is unrecognised', () => {
177
- expect(parseTemplateKey('orders:unknown:field')).toBeNull()
178
- })
179
-
180
- it('returns null when there are no field tokens after selector', () => {
181
- expect(parseTemplateKey('orders:lastone')).toEqual<ParsedKey>({
182
- kind: 'scoped',
183
- scope: 'orders',
184
- selector: { kind: 'lastone' },
185
- op: 'field',
186
- field: 'lastone',
187
- })
188
- })
189
-
190
- it('colons inside parens do not split tokens', () => {
191
- const result = parseTemplateKey('customers:byid(ns:id-123):name')
192
- expect(result).toMatchObject({
193
- kind: 'scoped',
194
- scope: 'customers',
195
- selector: { kind: 'byid', id: 'ns:id-123' },
196
- op: 'field',
197
- field: 'name',
198
- })
199
- })
200
- })
201
-
202
- describe('scoped with this scope', () => {
203
- it('this:lastone:field', () => {
204
- expect(parseTemplateKey('this:lastone:name')).toMatchObject({
205
- kind: 'scoped', scope: 'this', selector: { kind: 'lastone' }, op: 'field', field: 'name',
206
- })
207
- })
208
- })
209
- })
210
-
211
- // ── Sprint 07 var_access cases ─────────────────────────────────────────────────
212
-
213
- describe('parseTemplateKey — var_access (Sprint 07)', () => {
214
- it('1 — ordini.array[a,b].count → var_access with array + agg:count', () => {
215
- const result = parseTemplateKey('ordini.array[a,b].count')
216
- expect(result).toEqual<ParsedKey>({
217
- kind: 'var_access',
218
- name: 'ordini',
219
- steps: [
220
- { type: 'array', ids: ['a', 'b'] },
221
- { type: 'agg', op: 'count' },
222
- ],
223
- conditions: [],
224
- })
225
- })
226
-
227
- it('2 — ordini.count.(status=paid) → var_access with agg:count + condition', () => {
228
- const result = parseTemplateKey('ordini.count.(status=paid)')
229
- expect(result).toEqual<ParsedKey>({
230
- kind: 'var_access',
231
- name: 'ordini',
232
- steps: [{ type: 'agg', op: 'count' }],
233
- conditions: [{ column: 'status', op: '=', value: 'paid' }],
234
- })
235
- })
236
-
237
- it('3 — ordini.sum.total.(amount>100) → var_access with agg:sum:total + condition', () => {
238
- const result = parseTemplateKey('ordini.sum.total.(amount>100)')
239
- expect(result).toEqual<ParsedKey>({
240
- kind: 'var_access',
241
- name: 'ordini',
242
- steps: [{ type: 'agg', op: 'sum', field: 'total' }],
243
- conditions: [{ column: 'amount', op: '>', value: '100' }],
244
- })
245
- })
246
-
247
- it('4 — ordini.firstone.id.(status=paid) → nav + field + condition', () => {
248
- const result = parseTemplateKey('ordini.firstone.id.(status=paid)')
249
- expect(result).toEqual<ParsedKey>({
250
- kind: 'var_access',
251
- name: 'ordini',
252
- steps: [
253
- { type: 'nav', nav: 'firstone' },
254
- { type: 'field', name: 'id' },
255
- ],
256
- conditions: [{ column: 'status', op: '=', value: 'paid' }],
257
- })
258
- })
259
-
260
- it('5 — ordini.array[a,b].(status=paid).count → array + condition + agg', () => {
261
- const result = parseTemplateKey('ordini.array[a,b].(status=paid).count')
262
- expect(result).toMatchObject({
263
- kind: 'var_access',
264
- name: 'ordini',
265
- steps: [
266
- { type: 'array', ids: ['a', 'b'] },
267
- { type: 'agg', op: 'count' },
268
- ],
269
- conditions: [{ column: 'status', op: '=', value: 'paid' }],
270
- })
271
- })
272
-
273
- it('6 — cliente.email → falls through to simple (fast path)', () => {
274
- const result = parseTemplateKey('cliente.email')
275
- expect(result).toEqual<ParsedKey>({ kind: 'simple', path: 'cliente.email' })
276
- })
277
-
278
- it('handles multiple inline conditions (AND-chained)', () => {
279
- const result = parseTemplateKey('ordini.count.(status=paid).(amount>10)')
280
- expect(result).toMatchObject({
281
- kind: 'var_access',
282
- conditions: [
283
- { column: 'status', op: '=', value: 'paid' },
284
- { column: 'amount', op: '>', value: '10' },
285
- ],
286
- })
287
- })
288
-
289
- it('handles != operator', () => {
290
- const result = parseTemplateKey('ordini.count.(status!=draft)')
291
- expect(result).toMatchObject({
292
- kind: 'var_access',
293
- conditions: [{ column: 'status', op: '!=', value: 'draft' }],
294
- })
295
- })
296
-
297
- it('handles <= and >= operators', () => {
298
- const r1 = parseTemplateKey('ordini.count.(amount<=100)')
299
- const r2 = parseTemplateKey('ordini.count.(amount>=50)')
300
- expect(r1).toMatchObject({ conditions: [{ op: '<=' }] })
301
- expect(r2).toMatchObject({ conditions: [{ op: '>=' }] })
302
- })
303
-
304
- it('plain dot-path without special chars stays simple', () => {
305
- expect(parseTemplateKey('ordini.count')).toEqual({ kind: 'simple', path: 'ordini.count' })
306
- expect(parseTemplateKey('cliente.nome')).toEqual({ kind: 'simple', path: 'cliente.nome' })
307
- })
308
- })