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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/README.md +4 -4
  2. package/assets/dashboard/BeechLogo.svg +18 -18
  3. package/assets/dashboard/BeechLogoLIght.svg +48 -48
  4. package/assets/dashboard/assets/chart-BwlCt-IA.js +39 -0
  5. package/assets/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  6. package/assets/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  7. package/assets/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  8. package/assets/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  9. package/assets/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  10. package/assets/dashboard/assets/index-BcVN-DNy.js +642 -0
  11. package/assets/dashboard/assets/index-Cf1yRCqG.css +1 -0
  12. package/assets/dashboard/assets/lora-cyrillic-ext-wght-normal-YcVxMP-u.woff2 +0 -0
  13. package/assets/dashboard/assets/lora-cyrillic-wght-normal-UWPuZLjM.woff2 +0 -0
  14. package/assets/dashboard/assets/lora-latin-ext-wght-normal-C2Wlntb9.woff2 +0 -0
  15. package/assets/dashboard/assets/lora-latin-wght-normal-BiLcIKcI.woff2 +0 -0
  16. package/assets/dashboard/assets/lora-math-wght-normal-sOy7rEfW.woff2 +0 -0
  17. package/assets/dashboard/assets/lora-symbols-wght-normal-DQ5VrUkH.woff2 +0 -0
  18. package/assets/dashboard/assets/lora-vietnamese-wght-normal-CfJ7gtf3.woff2 +0 -0
  19. package/assets/dashboard/assets/pie-chart-recharts-BMWpjFzf.js +1 -0
  20. package/assets/dashboard/assets/timeseries-chart-recharts-8_GwAFR0.js +1 -0
  21. package/assets/dashboard/beechLogoDark.svg +48 -48
  22. package/assets/dashboard/index.html +18 -18
  23. package/assets/dashboard/noResult.svg +42 -42
  24. package/assets/dashboard/sol.svg +3 -3
  25. package/assets/dashboard/undraw_enter_nwx3.svg +36 -36
  26. package/dist/auth/__fixtures__/in-memory-hash-provider.d.ts +5 -0
  27. package/dist/auth/__fixtures__/static-token-service.d.ts +6 -0
  28. package/dist/auth/bcrypt-hash-provider.d.ts +8 -0
  29. package/dist/auth/constants.d.ts +10 -0
  30. package/dist/auth/jose-token-service.d.ts +14 -0
  31. package/dist/auth/login.d.ts +32 -0
  32. package/dist/auth/refresh.d.ts +1 -0
  33. package/dist/factory.d.ts +45 -0
  34. package/dist/features/automations/action-executors/create-entry.executor.d.ts +7 -0
  35. package/dist/features/automations/action-executors/edit-field.executor.d.ts +7 -0
  36. package/dist/features/automations/action-executors/index.d.ts +13 -0
  37. package/dist/features/automations/action-executors/send-mail.executor.d.ts +7 -0
  38. package/dist/features/automations/action-executors/set-variable.executor.d.ts +15 -0
  39. package/dist/features/automations/action-executors/webhook.executor.d.ts +7 -0
  40. package/dist/features/automations/automation-runner.d.ts +13 -0
  41. package/dist/features/automations/automation-runner.utils.d.ts +3 -0
  42. package/dist/features/automations/automations.handler.d.ts +7 -0
  43. package/dist/features/automations/automations.schema.d.ts +191 -0
  44. package/dist/features/automations/context-resolver.d.ts +8 -0
  45. package/dist/features/automations/cron-runner.d.ts +9 -0
  46. package/dist/features/automations/cron-runner.utils.d.ts +1 -0
  47. package/dist/features/automations/filter-translation.d.ts +9 -0
  48. package/dist/features/automations/index.d.ts +7 -0
  49. package/dist/features/automations/template-grammar.d.ts +73 -0
  50. package/dist/features/automations/var-access-resolver.d.ts +6 -0
  51. package/dist/features/automations/when-evaluator.d.ts +3 -0
  52. package/dist/features/automations/when-pushdown.d.ts +2 -0
  53. package/dist/features/backrefs/backrefs.handler.d.ts +3 -0
  54. package/dist/features/backrefs/d1-backref.repository.d.ts +17 -0
  55. package/dist/features/backrefs/index.d.ts +1 -0
  56. package/dist/features/content/constants.d.ts +12 -0
  57. package/dist/features/content/handlers/bulk.handler.d.ts +3 -0
  58. package/dist/features/content/handlers/create.d.ts +3 -0
  59. package/dist/features/content/handlers/delete.d.ts +3 -0
  60. package/dist/features/content/handlers/facets.d.ts +3 -0
  61. package/dist/features/content/handlers/get.d.ts +4 -0
  62. package/dist/features/content/handlers/helpers.d.ts +12 -0
  63. package/dist/features/content/handlers/list.d.ts +3 -0
  64. package/dist/features/content/handlers/update.d.ts +3 -0
  65. package/dist/features/content/index.d.ts +4 -0
  66. package/dist/features/dashboard-layout/dashboard-layout.handler.d.ts +7 -0
  67. package/dist/features/dashboard-layout/index.d.ts +1 -0
  68. package/dist/features/draft/draft.handler.d.ts +4 -0
  69. package/dist/features/draft/draft.middleware.d.ts +9 -0
  70. package/dist/features/draft/index.d.ts +1 -0
  71. package/{src/features/email/email.provider.ts → dist/features/email/email.provider.d.ts} +19 -24
  72. package/dist/features/email/email.service.d.ts +13 -0
  73. package/dist/features/email/email.types.d.ts +94 -0
  74. package/{src/features/email/index.ts → dist/features/email/index.d.ts} +3 -16
  75. package/dist/features/email/providers/resend.d.ts +36 -0
  76. package/dist/features/email/providers/smtp.d.ts +11 -0
  77. package/dist/features/email/templates/automation-mail.d.ts +8 -0
  78. package/dist/features/email/templates/password-changed.d.ts +15 -0
  79. package/dist/features/email/templates/password-reset.d.ts +18 -0
  80. package/dist/features/email/templates/shell.d.ts +58 -0
  81. package/dist/features/notifications/index.d.ts +1 -0
  82. package/dist/features/notifications/notifications.handler.d.ts +15 -0
  83. package/dist/features/password-reset/index.d.ts +6 -0
  84. package/dist/features/password-reset/request.d.ts +10 -0
  85. package/dist/features/password-reset/reset.d.ts +9 -0
  86. package/dist/features/rotate-field/index.d.ts +1 -0
  87. package/dist/features/rotate-field/rotate-field.handler.d.ts +7 -0
  88. package/dist/features/rotate-field/rotate-field.schema.d.ts +11 -0
  89. package/dist/features/schema/schema.handler.d.ts +7 -0
  90. package/dist/features/seeds/index.d.ts +1 -0
  91. package/dist/features/seeds/seeds.handler.d.ts +6 -0
  92. package/dist/features/settings/settings.handler.d.ts +7 -0
  93. package/dist/features/setup/index.d.ts +7 -0
  94. package/dist/features/stats/index.d.ts +1 -0
  95. package/dist/features/stats/stats.handler.d.ts +7 -0
  96. package/dist/features/webhooks/index.d.ts +3 -0
  97. package/dist/index.d.ts +7 -0
  98. package/dist/index.js +9732 -0
  99. package/dist/media-utils.d.ts +25 -0
  100. package/dist/middleware/auth-providers.middleware.d.ts +8 -0
  101. package/dist/middleware/observability.middleware.d.ts +20 -0
  102. package/dist/middleware/queue.middleware.d.ts +11 -0
  103. package/dist/middleware/rate-limit.middleware.d.ts +9 -0
  104. package/dist/middleware/repository.middleware.d.ts +34 -0
  105. package/dist/middleware/seed-registry.middleware.d.ts +10 -0
  106. package/dist/middleware/storage.middleware.d.ts +7 -0
  107. package/dist/middleware.d.ts +11 -0
  108. package/dist/public/access-policy.d.ts +12 -0
  109. package/dist/public/api-key-middleware.d.ts +6 -0
  110. package/dist/public/cache-utils.d.ts +10 -0
  111. package/dist/public/entry-projection.d.ts +2 -0
  112. package/dist/public/idempotency.d.ts +9 -0
  113. package/dist/public/index.d.ts +11 -0
  114. package/dist/public/problem-details.d.ts +31 -0
  115. package/dist/public/public-add.d.ts +3 -0
  116. package/dist/public/public-edit.d.ts +5 -0
  117. package/dist/public/public-errors.d.ts +13 -0
  118. package/dist/public/public-read.d.ts +3 -0
  119. package/dist/public/public-routes.d.ts +3 -0
  120. package/dist/public/query-builder.d.ts +28 -0
  121. package/dist/public/rate-limit-middleware.d.ts +3 -0
  122. package/dist/public/read-list.d.ts +26 -0
  123. package/dist/public/read-single.d.ts +22 -0
  124. package/dist/public/response-builder.d.ts +22 -0
  125. package/dist/public/sanitize.d.ts +23 -0
  126. package/{src/public/slug-utils.ts → dist/public/slug-utils.d.ts} +3 -10
  127. package/dist/rate-limit/cloudflare-rate-limiter.d.ts +6 -0
  128. package/dist/rate-limit/in-memory-rate-limiter.d.ts +7 -0
  129. package/dist/rate-limit/no-op-rate-limiter.d.ts +4 -0
  130. package/dist/search-utils.d.ts +54 -0
  131. package/dist/search.d.ts +6 -0
  132. package/dist/shared/apply-policies.d.ts +10 -0
  133. package/dist/shared/automations.repository.d1.d.ts +12 -0
  134. package/dist/shared/background-notification-service.d.ts +18 -0
  135. package/dist/shared/base.repository.d1.d.ts +18 -0
  136. package/dist/shared/cloudflare-queue-service.d.ts +11 -0
  137. package/dist/shared/content-utils.d.ts +21 -0
  138. package/dist/shared/content.repository.d1.d.ts +68 -0
  139. package/dist/shared/d1-activity-log.repository.d.ts +13 -0
  140. package/dist/shared/d1-activity-logger.d.ts +20 -0
  141. package/dist/shared/d1-analytics.repository.d.ts +20 -0
  142. package/dist/shared/d1-content-scan.repository.d.ts +7 -0
  143. package/dist/shared/d1-notification.repository.d.ts +17 -0
  144. package/dist/shared/d1-password-reset-token.repository.d.ts +10 -0
  145. package/dist/shared/d1-search.repository.d.ts +14 -0
  146. package/dist/shared/d1-session.repository.d.ts +12 -0
  147. package/dist/shared/d1-setup-checklist.repository.d.ts +12 -0
  148. package/dist/shared/d1-user.repository.d.ts +18 -0
  149. package/dist/shared/d1-widget.repository.d.ts +31 -0
  150. package/dist/shared/dashboard-layout.repository.d1.d.ts +9 -0
  151. package/dist/shared/demo-data-sql.d.ts +1 -0
  152. package/dist/shared/demo-data.repository.d1.d.ts +6 -0
  153. package/dist/shared/execution-context-scheduler.d.ts +6 -0
  154. package/dist/shared/fixed-clock.d.ts +13 -0
  155. package/dist/shared/fts-sync.d.ts +1 -0
  156. package/dist/shared/idempotency.repository.d1.d.ts +16 -0
  157. package/dist/shared/in-memory-activity-logger.d.ts +11 -0
  158. package/dist/shared/in-memory-notification-service.d.ts +11 -0
  159. package/dist/shared/in-memory-queue-service.d.ts +16 -0
  160. package/dist/shared/in-memory-seed.repository.d.ts +18 -0
  161. package/dist/shared/media.repository.d1.d.ts +35 -0
  162. package/dist/shared/qstash-notification-service.d.ts +18 -0
  163. package/dist/shared/query-utils.d.ts +36 -0
  164. package/dist/shared/queue-consumer.d.ts +9 -0
  165. package/dist/shared/request-utils.d.ts +13 -0
  166. package/dist/shared/schema-mutator.d1.d.ts +12 -0
  167. package/dist/shared/seed-layout.repository.d1.d.ts +9 -0
  168. package/dist/shared/seed-registry-cache.d.ts +12 -0
  169. package/dist/shared/seed.repository.d1.d.ts +13 -0
  170. package/dist/shared/sequential-id-generator.d.ts +13 -0
  171. package/dist/shared/site-settings.repository.d1.d.ts +7 -0
  172. package/dist/shared/storage/factory.d.ts +8 -0
  173. package/dist/shared/storage/s3-bucket.d.ts +43 -0
  174. package/dist/shared/storage-utils.d.ts +7 -0
  175. package/dist/shared/system-stats.repository.d1.d.ts +25 -0
  176. package/dist/types.d.ts +87 -0
  177. package/dist/upload.d.ts +16 -0
  178. package/dist/widget.d.ts +7 -0
  179. package/migrations/0000_v040_base.sql +265 -265
  180. package/migrations/0029_automations.sql +14 -14
  181. package/migrations/0030_test_seeds.sql +125 -125
  182. package/package.json +28 -18
  183. package/assets/dashboard/assets/chart-BrJFdsGZ.js +0 -39
  184. package/assets/dashboard/assets/index-CIaoMxuB.css +0 -1
  185. package/assets/dashboard/assets/index-wTKgygsg.js +0 -636
  186. package/assets/dashboard/assets/pie-chart-recharts-BEaE1FOE.js +0 -1
  187. package/assets/dashboard/assets/timeseries-chart-recharts-DmWM6imF.js +0 -1
  188. package/src/auth/__fixtures__/in-memory-hash-provider.ts +0 -17
  189. package/src/auth/__fixtures__/static-token-service.ts +0 -22
  190. package/src/auth/bcrypt-hash-provider.ts +0 -24
  191. package/src/auth/constants.ts +0 -14
  192. package/src/auth/generate-refresh-token.test.ts +0 -23
  193. package/src/auth/hash-provider.test.ts +0 -55
  194. package/src/auth/jose-token-service.ts +0 -62
  195. package/src/auth/jwt-claims-passthrough.test.ts +0 -87
  196. package/src/auth/login.test.ts +0 -100
  197. package/src/auth/login.ts +0 -78
  198. package/src/auth/refresh.ts +0 -11
  199. package/src/auth/token-service.test.ts +0 -90
  200. package/src/factory.ts +0 -370
  201. package/src/features/automations/__tests__/action-executors.test.ts +0 -255
  202. package/src/features/automations/__tests__/automation-runner.test.ts +0 -196
  203. package/src/features/automations/__tests__/automation-runner.utils.test.ts +0 -60
  204. package/src/features/automations/__tests__/automations.handler.test.ts +0 -264
  205. package/src/features/automations/__tests__/automations.repository.test.ts +0 -163
  206. package/src/features/automations/__tests__/automations.schema.test.ts +0 -224
  207. package/src/features/automations/__tests__/context-resolver.test.ts +0 -126
  208. package/src/features/automations/__tests__/cron-runner.test.ts +0 -267
  209. package/src/features/automations/__tests__/cron-runner.utils.test.ts +0 -144
  210. package/src/features/automations/__tests__/set-variable.executor.test.ts +0 -383
  211. package/src/features/automations/__tests__/template-grammar.test.ts +0 -308
  212. package/src/features/automations/__tests__/webhook.executor.test.ts +0 -142
  213. package/src/features/automations/__tests__/when-evaluator.test.ts +0 -274
  214. package/src/features/automations/__tests__/when-pushdown.test.ts +0 -281
  215. package/src/features/automations/action-executors/create-entry.executor.ts +0 -27
  216. package/src/features/automations/action-executors/edit-field.executor.ts +0 -26
  217. package/src/features/automations/action-executors/index.ts +0 -37
  218. package/src/features/automations/action-executors/send-mail.executor.ts +0 -53
  219. package/src/features/automations/action-executors/set-variable.executor.ts +0 -159
  220. package/src/features/automations/action-executors/webhook.executor.ts +0 -67
  221. package/src/features/automations/automation-runner.ts +0 -85
  222. package/src/features/automations/automation-runner.utils.ts +0 -47
  223. package/src/features/automations/automations.handler.ts +0 -197
  224. package/src/features/automations/automations.schema.ts +0 -177
  225. package/src/features/automations/context-resolver.ts +0 -152
  226. package/src/features/automations/cron-runner.ts +0 -140
  227. package/src/features/automations/cron-runner.utils.ts +0 -44
  228. package/src/features/automations/filter-translation.ts +0 -46
  229. package/src/features/automations/index.ts +0 -16
  230. package/src/features/automations/template-grammar.ts +0 -245
  231. package/src/features/automations/var-access-resolver.ts +0 -140
  232. package/src/features/automations/when-evaluator.ts +0 -87
  233. package/src/features/automations/when-pushdown.ts +0 -57
  234. package/src/features/backrefs/__tests__/backrefs.handler.test.ts +0 -359
  235. package/src/features/backrefs/backrefs.handler.ts +0 -168
  236. package/src/features/backrefs/d1-backref.repository.ts +0 -84
  237. package/src/features/backrefs/index.ts +0 -5
  238. package/src/features/content/constants.ts +0 -16
  239. package/src/features/content/handlers/bulk.handler.ts +0 -185
  240. package/src/features/content/handlers/create.ts +0 -129
  241. package/src/features/content/handlers/delete.ts +0 -62
  242. package/src/features/content/handlers/facets.ts +0 -49
  243. package/src/features/content/handlers/get.ts +0 -120
  244. package/src/features/content/handlers/helpers.test.ts +0 -180
  245. package/src/features/content/handlers/helpers.ts +0 -93
  246. package/src/features/content/handlers/list.ts +0 -174
  247. package/src/features/content/handlers/update.ts +0 -174
  248. package/src/features/content/index.ts +0 -26
  249. package/src/features/dashboard-layout/__tests__/dashboard-layout.handler.test.ts +0 -388
  250. package/src/features/dashboard-layout/dashboard-layout.handler.ts +0 -206
  251. package/src/features/dashboard-layout/index.ts +0 -5
  252. package/src/features/draft/draft.handler.ts +0 -195
  253. package/src/features/draft/draft.middleware.ts +0 -66
  254. package/src/features/draft/index.ts +0 -5
  255. package/src/features/email/email.service.ts +0 -98
  256. package/src/features/email/email.types.ts +0 -119
  257. package/src/features/email/providers/resend.ts +0 -67
  258. package/src/features/email/providers/smtp.ts +0 -55
  259. package/src/features/email/templates/automation-mail.ts +0 -19
  260. package/src/features/email/templates/password-changed.ts +0 -63
  261. package/src/features/email/templates/password-reset.ts +0 -68
  262. package/src/features/email/templates/shell.ts +0 -96
  263. package/src/features/notifications/index.ts +0 -5
  264. package/src/features/notifications/notifications.handler.ts +0 -105
  265. package/src/features/password-reset/index.ts +0 -19
  266. package/src/features/password-reset/request.ts +0 -92
  267. package/src/features/password-reset/reset.ts +0 -102
  268. package/src/features/rotate-field/index.ts +0 -5
  269. package/src/features/rotate-field/rotate-field.handler.ts +0 -132
  270. package/src/features/rotate-field/rotate-field.schema.ts +0 -17
  271. package/src/features/schema/schema.handler.ts +0 -131
  272. package/src/features/seeds/index.ts +0 -5
  273. package/src/features/seeds/seeds.handler.test.ts +0 -1199
  274. package/src/features/seeds/seeds.handler.ts +0 -558
  275. package/src/features/settings/__tests__/settings.handler.test.ts +0 -555
  276. package/src/features/settings/settings.handler.ts +0 -392
  277. package/src/features/setup/index.ts +0 -288
  278. package/src/features/stats/index.ts +0 -5
  279. package/src/features/stats/stats.handler.ts +0 -420
  280. package/src/features/webhooks/index.ts +0 -63
  281. package/src/index.ts +0 -65
  282. package/src/media-utils.ts +0 -82
  283. package/src/middleware/auth-providers.middleware.test.ts +0 -85
  284. package/src/middleware/auth-providers.middleware.ts +0 -45
  285. package/src/middleware/observability.middleware.test.ts +0 -93
  286. package/src/middleware/observability.middleware.ts +0 -70
  287. package/src/middleware/rate-limit.middleware.ts +0 -45
  288. package/src/middleware/repository.middleware.ts +0 -117
  289. package/src/middleware/seed-registry.middleware.test.ts +0 -97
  290. package/src/middleware/seed-registry.middleware.ts +0 -21
  291. package/src/middleware/storage.middleware.ts +0 -25
  292. package/src/middleware.ts +0 -45
  293. package/src/public/access-policy.ts +0 -27
  294. package/src/public/api-key-middleware.ts +0 -57
  295. package/src/public/cache-utils.ts +0 -38
  296. package/src/public/entry-projection.ts +0 -46
  297. package/src/public/idempotency.ts +0 -23
  298. package/src/public/index.ts +0 -16
  299. package/src/public/problem-details.ts +0 -124
  300. package/src/public/public-add.ts +0 -114
  301. package/src/public/public-edit.ts +0 -163
  302. package/src/public/public-errors.ts +0 -19
  303. package/src/public/public-read.ts +0 -63
  304. package/src/public/public-routes.ts +0 -88
  305. package/src/public/query-builder.test.ts +0 -224
  306. package/src/public/query-builder.ts +0 -156
  307. package/src/public/rate-limit-middleware.ts +0 -34
  308. package/src/public/read-list.ts +0 -54
  309. package/src/public/read-single.ts +0 -48
  310. package/src/public/response-builder.ts +0 -30
  311. package/src/public/sanitize.ts +0 -69
  312. package/src/rate-limit/cloudflare-rate-limiter.test.ts +0 -30
  313. package/src/rate-limit/cloudflare-rate-limiter.ts +0 -15
  314. package/src/rate-limit/in-memory-rate-limiter.test.ts +0 -37
  315. package/src/rate-limit/in-memory-rate-limiter.ts +0 -17
  316. package/src/rate-limit/no-op-rate-limiter.test.ts +0 -22
  317. package/src/rate-limit/no-op-rate-limiter.ts +0 -11
  318. package/src/search-utils.test.ts +0 -211
  319. package/src/search-utils.ts +0 -213
  320. package/src/search.ts +0 -65
  321. package/src/shared/apply-policies.test.ts +0 -81
  322. package/src/shared/apply-policies.ts +0 -67
  323. package/src/shared/automations.repository.d1.ts +0 -150
  324. package/src/shared/background-notification-service.test.ts +0 -62
  325. package/src/shared/background-notification-service.ts +0 -52
  326. package/src/shared/base.repository.d1.ts +0 -32
  327. package/src/shared/content-utils.test.ts +0 -165
  328. package/src/shared/content-utils.ts +0 -86
  329. package/src/shared/content.repository.d1.test.ts +0 -507
  330. package/src/shared/content.repository.d1.ts +0 -851
  331. package/src/shared/d1-activity-log.repository.test.ts +0 -140
  332. package/src/shared/d1-activity-log.repository.ts +0 -105
  333. package/src/shared/d1-activity-logger.test.ts +0 -86
  334. package/src/shared/d1-activity-logger.ts +0 -67
  335. package/src/shared/d1-analytics.repository.test.ts +0 -78
  336. package/src/shared/d1-analytics.repository.ts +0 -85
  337. package/src/shared/d1-content-scan.repository.test.ts +0 -80
  338. package/src/shared/d1-content-scan.repository.ts +0 -37
  339. package/src/shared/d1-notification.repository.test.ts +0 -128
  340. package/src/shared/d1-notification.repository.ts +0 -118
  341. package/src/shared/d1-password-reset-token.repository.test.ts +0 -81
  342. package/src/shared/d1-password-reset-token.repository.ts +0 -56
  343. package/src/shared/d1-search.repository.test.ts +0 -87
  344. package/src/shared/d1-search.repository.ts +0 -88
  345. package/src/shared/d1-session.repository.test.ts +0 -125
  346. package/src/shared/d1-session.repository.ts +0 -102
  347. package/src/shared/d1-setup-checklist.repository.ts +0 -36
  348. package/src/shared/d1-user.repository.test.ts +0 -151
  349. package/src/shared/d1-user.repository.ts +0 -119
  350. package/src/shared/d1-widget.repository.test.ts +0 -267
  351. package/src/shared/d1-widget.repository.ts +0 -385
  352. package/src/shared/dashboard-layout.repository.d1.test.ts +0 -135
  353. package/src/shared/dashboard-layout.repository.d1.ts +0 -58
  354. package/src/shared/demo-data-sql.ts +0 -216
  355. package/src/shared/demo-data.repository.d1.ts +0 -20
  356. package/src/shared/execution-context-scheduler.ts +0 -13
  357. package/src/shared/fixed-clock.ts +0 -25
  358. package/src/shared/fts-sync.ts +0 -8
  359. package/src/shared/idempotency.repository.d1.test.ts +0 -83
  360. package/src/shared/idempotency.repository.d1.ts +0 -60
  361. package/src/shared/in-memory-activity-logger.ts +0 -19
  362. package/src/shared/in-memory-notification-service.ts +0 -19
  363. package/src/shared/in-memory-seed.repository.ts +0 -58
  364. package/src/shared/media.repository.d1.test.ts +0 -107
  365. package/src/shared/media.repository.d1.ts +0 -68
  366. package/src/shared/qstash-notification-service.test.ts +0 -67
  367. package/src/shared/qstash-notification-service.ts +0 -55
  368. package/src/shared/query-utils.ts +0 -141
  369. package/src/shared/request-utils.ts +0 -26
  370. package/src/shared/schema-mutator.d1.test.ts +0 -142
  371. package/src/shared/schema-mutator.d1.ts +0 -64
  372. package/src/shared/seed-layout.repository.d1.test.ts +0 -114
  373. package/src/shared/seed-layout.repository.d1.ts +0 -62
  374. package/src/shared/seed-registry-cache.test.ts +0 -68
  375. package/src/shared/seed-registry-cache.ts +0 -49
  376. package/src/shared/seed.repository.d1.test.ts +0 -143
  377. package/src/shared/seed.repository.d1.ts +0 -98
  378. package/src/shared/sequential-id-generator.ts +0 -33
  379. package/src/shared/site-settings.repository.d1.ts +0 -53
  380. package/src/shared/storage/factory.ts +0 -41
  381. package/src/shared/storage/s3-bucket.ts +0 -195
  382. package/src/shared/storage-utils.ts +0 -40
  383. package/src/shared/system-stats.repository.d1.test.ts +0 -58
  384. package/src/shared/system-stats.repository.d1.ts +0 -48
  385. package/src/types.ts +0 -90
  386. package/src/upload.ts +0 -198
  387. package/src/widget.test.ts +0 -85
  388. package/src/widget.ts +0 -251
@@ -1,388 +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 { Hono } from 'hono'
7
- import { dashboardLayoutApp } from '../dashboard-layout.handler'
8
- import type { DashboardLayout, DashboardLayoutRecord, IDashboardLayoutRepository } from '@beechcms/core'
9
- import type { Env, Variables } from '../../../types'
10
-
11
- // ---------------------------------------------------------------------------
12
- // Helpers and Mocks
13
- // ---------------------------------------------------------------------------
14
-
15
- /** Stateful in-memory stub so PUT/DELETE effects are observable by a later GET. */
16
- function makeRepoStub(initial: DashboardLayoutRecord | null = null): IDashboardLayoutRepository {
17
- let state = initial
18
- return {
19
- get: vi.fn(async (scope: string) => (state && state.scope === scope ? state : null)),
20
- listScopes: vi.fn(async () => (state ? [state.scope] : [])),
21
- upsert: vi.fn(async (scope: string, layout: DashboardLayout, updatedBy: string) => {
22
- state = { scope, layout, updatedAt: 0, updatedBy }
23
- }),
24
- remove: vi.fn(async (scope: string) => {
25
- if (state?.scope === scope) state = null
26
- }),
27
- }
28
- }
29
-
30
- /** Stateful in-memory stub supporting multiple scopes at once. */
31
- function makeMultiRepoStub(initial: Record<string, DashboardLayout> = {}): IDashboardLayoutRepository {
32
- const state = new Map<string, DashboardLayoutRecord>(
33
- Object.entries(initial).map(([scope, layout]) => [scope, { scope, layout, updatedAt: 0, updatedBy: 'seed' }]),
34
- )
35
- return {
36
- get: vi.fn(async (scope: string) => state.get(scope) ?? null),
37
- listScopes: vi.fn(async () => [...state.keys()]),
38
- upsert: vi.fn(async (scope: string, layout: DashboardLayout, updatedBy: string) => {
39
- state.set(scope, { scope, layout, updatedAt: 0, updatedBy })
40
- }),
41
- remove: vi.fn(async (scope: string) => {
42
- state.delete(scope)
43
- }),
44
- }
45
- }
46
-
47
- function buildApp(opts: {
48
- repo?: IDashboardLayoutRepository
49
- role?: string
50
- seedSlugs?: string[]
51
- } = {}) {
52
- const repo = opts.repo ?? makeRepoStub()
53
- const app = new Hono<{ Bindings: Env; Variables: Variables }>()
54
- app.use('*', async (c, next) => {
55
- c.set('dashboardLayoutRepository', repo)
56
- c.set('seedRegistry', {
57
- all: () => (opts.seedSlugs ?? ['articoli']).map((slug) => ({ slug })),
58
- } as any)
59
- c.set('jwtPayload', { sub: 'user-1', role: opts.role ?? 'admin', email: 'user@beechcms.com' } as any)
60
- await next()
61
- })
62
- app.route('/', dashboardLayoutApp)
63
- return { app, repo }
64
- }
65
-
66
- const validLayout: DashboardLayout = {
67
- version: 1,
68
- pages: [
69
- {
70
- id: 'page-1',
71
- slug: 'overview',
72
- label: 'Overview',
73
- sections: [
74
- {
75
- id: 'section-1',
76
- columns: [{ id: 'col-1', widgets: [{ id: 'w1', type: 'core/stat', config: {} }] }],
77
- },
78
- ],
79
- },
80
- ],
81
- }
82
-
83
- function putRequest(body: unknown) {
84
- return {
85
- method: 'PUT',
86
- headers: { 'Content-Type': 'application/json' },
87
- body: JSON.stringify(body),
88
- }
89
- }
90
-
91
- // ---------------------------------------------------------------------------
92
- // Tests
93
- // ---------------------------------------------------------------------------
94
-
95
- describe('Dashboard Layout Handler', () => {
96
- describe('GET /', () => {
97
- it('returns layout: null when no row exists', async () => {
98
- const { app } = buildApp()
99
- const res = await app.request('/')
100
- expect(res.status).toBe(200)
101
- expect(await res.json()).toEqual({ scope: 'default', layout: null })
102
- })
103
- })
104
-
105
- describe('PUT /', () => {
106
- it('returns 403 problem+json for an editor', async () => {
107
- const { app } = buildApp({ role: 'editor' })
108
- const res = await app.request('/', putRequest(validLayout))
109
- expect(res.status).toBe(403)
110
- expect(res.headers.get('Content-Type')).toContain('application/problem+json')
111
- const body = await res.json() as { type: string }
112
- expect(body.type).toContain('forbidden')
113
- })
114
-
115
- it('returns 400 invalid-json for malformed JSON body', async () => {
116
- const { app } = buildApp()
117
- const res = await app.request('/', {
118
- method: 'PUT',
119
- headers: { 'Content-Type': 'application/json' },
120
- body: 'not-json',
121
- })
122
- expect(res.status).toBe(400)
123
- const body = await res.json() as { type: string }
124
- expect(body.type).toContain('invalid-json')
125
- })
126
-
127
- it('returns 422 for a shape that fails Zod validation', async () => {
128
- const { app } = buildApp()
129
- const res = await app.request('/', putRequest({ version: 2, pages: [] }))
130
- expect(res.status).toBe(422)
131
- const body = await res.json() as { type: string }
132
- expect(body.type).toContain('invalid-layout')
133
- })
134
-
135
- it('returns 422 when the layout has duplicate widget ids', async () => {
136
- const duplicateLayout: DashboardLayout = {
137
- version: 1,
138
- pages: [
139
- {
140
- id: 'page-1',
141
- slug: 'overview',
142
- label: 'Overview',
143
- sections: [
144
- {
145
- id: 'section-1',
146
- columns: [
147
- { id: 'col-1', widgets: [{ id: 'dup', type: 'core/stat', config: {} }] },
148
- { id: 'col-2', widgets: [{ id: 'dup', type: 'core/stat', config: {} }] },
149
- ],
150
- columnSpans: [6, 6],
151
- },
152
- ],
153
- },
154
- ],
155
- }
156
- const { app } = buildApp()
157
- const res = await app.request('/', putRequest(duplicateLayout))
158
- expect(res.status).toBe(422)
159
- const body = await res.json() as { type: string; detail: string }
160
- expect(body.type).toContain('invalid-layout')
161
- expect(body.detail).toContain('dup')
162
- })
163
-
164
- it('strips widgets bound to a non-existent seed and reports a warning', async () => {
165
- const layoutWithGhostSeed: DashboardLayout = {
166
- version: 1,
167
- pages: [
168
- {
169
- id: 'page-1',
170
- slug: 'overview',
171
- label: 'Overview',
172
- sections: [
173
- {
174
- id: 'section-1',
175
- columns: [
176
- {
177
- id: 'col-1',
178
- widgets: [
179
- { id: 'w1', type: 'core/recent-content', config: { seedSlug: 'ghost-seed' } },
180
- { id: 'w2', type: 'core/stat', config: {} },
181
- ],
182
- },
183
- ],
184
- },
185
- ],
186
- },
187
- ],
188
- }
189
- const { app, repo } = buildApp({ seedSlugs: ['articoli'] })
190
- const res = await app.request('/', putRequest(layoutWithGhostSeed))
191
- expect(res.status).toBe(200)
192
- const body = await res.json() as { ok: boolean; layout: DashboardLayout; warnings: string[] }
193
- expect(body.ok).toBe(true)
194
- expect(body.warnings.length).toBeGreaterThan(0)
195
-
196
- const widgetIds = body.layout.pages[0].sections[0].columns[0].widgets.map((w) => w.id)
197
- expect(widgetIds).toEqual(['w2'])
198
- expect(repo.upsert).toHaveBeenCalledWith('default', body.layout, 'user-1')
199
- })
200
-
201
- it('passes through an unknown widget type unchanged with no warning', async () => {
202
- const layoutWithUnknownWidget: DashboardLayout = {
203
- version: 1,
204
- pages: [
205
- {
206
- id: 'page-1',
207
- slug: 'overview',
208
- label: 'Overview',
209
- sections: [
210
- {
211
- id: 'section-1',
212
- columns: [{ id: 'col-1', widgets: [{ id: 'w1', type: '@acme/ghost', config: {} }] }],
213
- },
214
- ],
215
- },
216
- ],
217
- }
218
- const { app } = buildApp()
219
- const res = await app.request('/', putRequest(layoutWithUnknownWidget))
220
- expect(res.status).toBe(200)
221
- const body = await res.json() as { ok: boolean; layout: DashboardLayout; warnings: string[] }
222
- expect(body.warnings).toEqual([])
223
- expect(body.layout.pages[0].sections[0].columns[0].widgets[0]).toEqual({
224
- id: 'w1',
225
- type: '@acme/ghost',
226
- config: {},
227
- })
228
- })
229
-
230
- it('persists a valid layout as admin and returns it on a subsequent GET', async () => {
231
- const { app } = buildApp()
232
-
233
- const putRes = await app.request('/', putRequest(validLayout))
234
- expect(putRes.status).toBe(200)
235
- const putBody = await putRes.json() as { ok: boolean; layout: DashboardLayout }
236
- expect(putBody.ok).toBe(true)
237
-
238
- const getRes = await app.request('/')
239
- expect(getRes.status).toBe(200)
240
- const getBody = await getRes.json() as { scope: string; layout: DashboardLayout }
241
- expect(getBody.scope).toBe('default')
242
- expect(getBody.layout).toEqual(putBody.layout)
243
- })
244
- })
245
-
246
- describe('DELETE /', () => {
247
- it('returns 403 problem+json for an editor', async () => {
248
- const { app } = buildApp({ role: 'editor' })
249
- const res = await app.request('/', { method: 'DELETE' })
250
- expect(res.status).toBe(403)
251
- expect(res.headers.get('Content-Type')).toContain('application/problem+json')
252
- })
253
-
254
- it('removes the stored layout as admin; subsequent GET returns layout: null', async () => {
255
- const repo = makeRepoStub({ scope: 'default', layout: validLayout, updatedAt: 0, updatedBy: 'admin-1' })
256
- const { app } = buildApp({ repo })
257
-
258
- const delRes = await app.request('/', { method: 'DELETE' })
259
- expect(delRes.status).toBe(200)
260
- expect(await delRes.json()).toEqual({ ok: true })
261
-
262
- const getRes = await app.request('/')
263
- expect(await getRes.json()).toEqual({ scope: 'default', layout: null })
264
- })
265
- })
266
-
267
- // ---------------------------------------------------------------------------
268
- // Sprint 06: role-based dashboards
269
- // ---------------------------------------------------------------------------
270
-
271
- describe('GET / — scope resolution matrix', () => {
272
- it('returns default + layout: null when nothing is stored (editor)', async () => {
273
- const { app } = buildApp({ role: 'editor', repo: makeMultiRepoStub() })
274
- const res = await app.request('/')
275
- expect(await res.json()).toEqual({ scope: 'default', layout: null })
276
- })
277
-
278
- it('returns default when only default is stored (editor)', async () => {
279
- const repo = makeMultiRepoStub({ default: validLayout })
280
- const { app } = buildApp({ role: 'editor', repo })
281
- const res = await app.request('/')
282
- const body = await res.json() as { scope: string; layout: DashboardLayout }
283
- expect(body.scope).toBe('default')
284
- expect(body.layout).toEqual(validLayout)
285
- })
286
-
287
- it('returns role:editor when only role:editor is stored (editor)', async () => {
288
- const repo = makeMultiRepoStub({ 'role:editor': validLayout })
289
- const { app } = buildApp({ role: 'editor', repo })
290
- const res = await app.request('/')
291
- const body = await res.json() as { scope: string; layout: DashboardLayout }
292
- expect(body.scope).toBe('role:editor')
293
- expect(body.layout).toEqual(validLayout)
294
- })
295
-
296
- it('returns role:editor when both are stored (editor)', async () => {
297
- const otherLayout: DashboardLayout = { version: 1, pages: [{ ...validLayout.pages[0], slug: 'other' }] }
298
- const repo = makeMultiRepoStub({ default: otherLayout, 'role:editor': validLayout })
299
- const { app } = buildApp({ role: 'editor', repo })
300
- const res = await app.request('/')
301
- const body = await res.json() as { scope: string; layout: DashboardLayout }
302
- expect(body.scope).toBe('role:editor')
303
- expect(body.layout).toEqual(validLayout)
304
- })
305
-
306
- it('ignores role:editor for an admin caller, falling back to default + null', async () => {
307
- const repo = makeMultiRepoStub({ 'role:editor': validLayout })
308
- const { app } = buildApp({ role: 'admin', repo })
309
- const res = await app.request('/')
310
- expect(await res.json()).toEqual({ scope: 'default', layout: null })
311
- })
312
- })
313
-
314
- describe('PUT/DELETE /:scope', () => {
315
- it('admin can write role:editor (200)', async () => {
316
- const { app } = buildApp({ role: 'admin', repo: makeMultiRepoStub() })
317
- const res = await app.request('/role:editor', putRequest(validLayout))
318
- expect(res.status).toBe(200)
319
- const body = await res.json() as { ok: boolean }
320
- expect(body.ok).toBe(true)
321
- })
322
-
323
- it('editor cannot write role:editor (403)', async () => {
324
- const { app } = buildApp({ role: 'editor', repo: makeMultiRepoStub() })
325
- const res = await app.request('/role:editor', putRequest(validLayout))
326
- expect(res.status).toBe(403)
327
- })
328
-
329
- it('rejects an unknown scope with a 400 invalid-scope problem', async () => {
330
- const { app } = buildApp({ role: 'admin', repo: makeMultiRepoStub() })
331
- const res = await app.request('/role:ghost', putRequest(validLayout))
332
- expect(res.status).toBe(400)
333
- const body = await res.json() as { type: string }
334
- expect(body.type).toContain('invalid-scope')
335
- })
336
-
337
- it('removes the stored row for a scope', async () => {
338
- const repo = makeMultiRepoStub({ 'role:editor': validLayout })
339
- const { app } = buildApp({ role: 'admin', repo })
340
- const res = await app.request('/role:editor', { method: 'DELETE' })
341
- expect(res.status).toBe(200)
342
- expect(await repo.get('role:editor')).toBeNull()
343
- })
344
- })
345
-
346
- describe('GET /:scope', () => {
347
- it('returns the raw stored layout for a scope', async () => {
348
- const repo = makeMultiRepoStub({ 'role:editor': validLayout })
349
- const { app } = buildApp({ role: 'admin', repo })
350
- const res = await app.request('/role:editor')
351
- const body = await res.json() as { scope: string; layout: DashboardLayout | null }
352
- expect(body.scope).toBe('role:editor')
353
- expect(body.layout).toEqual(validLayout)
354
- })
355
-
356
- it('returns layout: null for an unstored scope', async () => {
357
- const { app } = buildApp({ role: 'admin', repo: makeMultiRepoStub() })
358
- const res = await app.request('/role:editor')
359
- expect(await res.json()).toEqual({ scope: 'role:editor', layout: null })
360
- })
361
-
362
- it('returns 403 for an editor', async () => {
363
- const { app } = buildApp({ role: 'editor', repo: makeMultiRepoStub() })
364
- const res = await app.request('/role:editor')
365
- expect(res.status).toBe(403)
366
- })
367
- })
368
-
369
- describe('GET /scopes', () => {
370
- it('lists the closed set of scopes annotated with stored state', async () => {
371
- const repo = makeMultiRepoStub({ default: validLayout, 'role:editor': validLayout })
372
- const { app } = buildApp({ role: 'admin', repo })
373
- const res = await app.request('/scopes')
374
- expect(res.status).toBe(200)
375
- expect(await res.json()).toEqual([
376
- { scope: 'default', stored: true },
377
- { scope: 'role:admin', stored: false },
378
- { scope: 'role:editor', stored: true },
379
- ])
380
- })
381
-
382
- it('returns 403 for an editor', async () => {
383
- const { app } = buildApp({ role: 'editor', repo: makeMultiRepoStub() })
384
- const res = await app.request('/scopes')
385
- expect(res.status).toBe(403)
386
- })
387
- })
388
- })
@@ -1,206 +0,0 @@
1
- // SPDX-License-Identifier: BUSL-1.1
2
- // Copyright (c) 2024–2026 Flavio De Musso. All rights reserved.
3
- // See LICENSE in the repository root for license terms.
4
-
5
- /// <reference types="@cloudflare/workers-types" />
6
- import { Hono } from 'hono'
7
- import {
8
- canEditDashboard,
9
- dashboardLayoutSchema,
10
- validateDashboardLayout,
11
- isValidDashboardScope,
12
- resolveDashboardScopeChain,
13
- roleScope,
14
- KNOWN_DASHBOARD_ROLES,
15
- DEFAULT_DASHBOARD_SCOPE,
16
- } from '@beechcms/core'
17
- import { publicProblem } from '../../public/problem-details'
18
- import type { Context } from 'hono'
19
- import type { DashboardLayout } from '@beechcms/core'
20
- import type { Env, Variables } from '../../types'
21
-
22
- type AppContext = Context<{ Bindings: Env; Variables: Variables }>
23
-
24
- /** Returns a 403 response if the caller lacks dashboard-edit permission, otherwise undefined. */
25
- function requireDashboardEditPermission(context: AppContext) {
26
- const role = context.get('jwtPayload')?.role
27
- if (!canEditDashboard(role)) {
28
- return publicProblem(context, {
29
- type: 'forbidden',
30
- title: 'Forbidden',
31
- status: 403,
32
- detail: 'Dashboard layout edit requires admin role.',
33
- })
34
- }
35
- return undefined
36
- }
37
-
38
- /** Shared validate-and-upsert logic for the bare and `:scope` PUT routes. */
39
- async function putLayout(context: AppContext, scope: string) {
40
- const forbidden = requireDashboardEditPermission(context)
41
- if (forbidden) return forbidden
42
-
43
- let body: unknown
44
- try {
45
- body = await context.req.json()
46
- } catch {
47
- return publicProblem(context, {
48
- type: 'invalid-json',
49
- title: 'Invalid JSON',
50
- status: 400,
51
- detail: 'Body must be valid JSON.',
52
- })
53
- }
54
-
55
- const parsed = dashboardLayoutSchema.safeParse(body)
56
- if (!parsed.success) {
57
- return publicProblem(context, {
58
- type: 'invalid-layout',
59
- title: 'Invalid layout',
60
- status: 422,
61
- detail: parsed.error.message,
62
- })
63
- }
64
-
65
- const seedSlugs = new Set(context.get('seedRegistry').all().map((seed) => seed.slug))
66
- const result = validateDashboardLayout(parsed.data, { seedSlugs })
67
- if (!result.ok) {
68
- return publicProblem(context, {
69
- type: 'invalid-layout',
70
- title: 'Invalid layout',
71
- status: 422,
72
- detail: result.errors.join('; '),
73
- })
74
- }
75
-
76
- const userId = context.get('jwtPayload')?.sub ?? 'unknown'
77
- await context.get('dashboardLayoutRepository').upsert(scope, result.cleaned, userId)
78
-
79
- return context.json({ ok: true, layout: result.cleaned, warnings: result.warnings })
80
- }
81
-
82
- const dashboardLayoutApp = new Hono<{ Bindings: Env; Variables: Variables }>()
83
-
84
- /**
85
- * Returns the dashboard layout for the caller, walking the scope chain
86
- * derived from their role (`role:<role>` → `default`). Returns the winning
87
- * scope alongside the (auto-cleaned) layout, or `{ scope: 'default', layout: null }`
88
- * if nothing is stored anywhere. Any authenticated user may read it.
89
- */
90
- dashboardLayoutApp.get('/', async (context) => {
91
- const role = context.get('jwtPayload')?.role
92
- const chain = resolveDashboardScopeChain(role)
93
- const repo = context.get('dashboardLayoutRepository')
94
-
95
- for (const scope of chain) {
96
- const record = await repo.get(scope)
97
- if (!record) continue
98
-
99
- const seedSlugs = new Set(context.get('seedRegistry').all().map((seed) => seed.slug))
100
- const { cleaned } = validateDashboardLayout(record.layout, { seedSlugs })
101
- return context.json({ scope, layout: cleaned })
102
- }
103
-
104
- return context.json({ scope: DEFAULT_DASHBOARD_SCOPE, layout: null as DashboardLayout | null })
105
- })
106
-
107
- /**
108
- * Lists the closed set of dashboard scopes (`default` + one per known role),
109
- * annotated with whether each currently has a stored layout. Admin-only —
110
- * builder furniture for the scope switcher.
111
- */
112
- dashboardLayoutApp.get('/scopes', async (context) => {
113
- const forbidden = requireDashboardEditPermission(context)
114
- if (forbidden) return forbidden
115
-
116
- const stored = new Set(await context.get('dashboardLayoutRepository').listScopes())
117
- const scopes = [DEFAULT_DASHBOARD_SCOPE, ...KNOWN_DASHBOARD_ROLES.map(roleScope)]
118
- return context.json(scopes.map((scope) => ({ scope, stored: stored.has(scope) })))
119
- })
120
-
121
- /**
122
- * Returns the raw stored layout for an arbitrary scope (`'default'` or
123
- * `'role:<role>'`), or `{ scope, layout: null }` if nothing is stored.
124
- * Admin-only — builder furniture for loading a scope's draft.
125
- */
126
- dashboardLayoutApp.get('/:scope', async (context) => {
127
- const forbidden = requireDashboardEditPermission(context)
128
- if (forbidden) return forbidden
129
-
130
- const scope = context.req.param('scope')
131
- if (!isValidDashboardScope(scope)) {
132
- return publicProblem(context, {
133
- type: 'invalid-scope',
134
- title: 'Invalid scope',
135
- status: 400,
136
- detail: `Unknown dashboard scope '${scope}'.`,
137
- })
138
- }
139
-
140
- const record = await context.get('dashboardLayoutRepository').get(scope)
141
- if (!record) return context.json({ scope, layout: null as DashboardLayout | null })
142
-
143
- const seedSlugs = new Set(context.get('seedRegistry').all().map((seed) => seed.slug))
144
- const { cleaned } = validateDashboardLayout(record.layout, { seedSlugs })
145
- return context.json({ scope, layout: cleaned })
146
- })
147
-
148
- /**
149
- * Upserts the dashboard layout for the default scope.
150
- * Admin-only. Validates Zod shape, then semantic constraints (seed
151
- * auto-cleanup, span sums, config size cap) before persisting.
152
- */
153
- dashboardLayoutApp.put('/', (context) => putLayout(context, DEFAULT_DASHBOARD_SCOPE))
154
-
155
- /**
156
- * Removes the stored dashboard layout for the default scope ("Reset").
157
- * Admin-only. The next GET regenerates the default layout client-side.
158
- */
159
- dashboardLayoutApp.delete('/', async (context) => {
160
- const forbidden = requireDashboardEditPermission(context)
161
- if (forbidden) return forbidden
162
-
163
- await context.get('dashboardLayoutRepository').remove(DEFAULT_DASHBOARD_SCOPE)
164
- return context.json({ ok: true })
165
- })
166
-
167
- /**
168
- * Upserts the dashboard layout for an arbitrary scope (`'default'` or
169
- * `'role:<role>'`). Admin-only, `400` problem on an unknown scope.
170
- */
171
- dashboardLayoutApp.put('/:scope', async (context) => {
172
- const scope = context.req.param('scope')
173
- if (!isValidDashboardScope(scope)) {
174
- return publicProblem(context, {
175
- type: 'invalid-scope',
176
- title: 'Invalid scope',
177
- status: 400,
178
- detail: `Unknown dashboard scope '${scope}'.`,
179
- })
180
- }
181
- return putLayout(context, scope)
182
- })
183
-
184
- /**
185
- * Removes the stored dashboard layout for an arbitrary scope ("Reset").
186
- * Admin-only, `400` problem on an unknown scope.
187
- */
188
- dashboardLayoutApp.delete('/:scope', async (context) => {
189
- const forbidden = requireDashboardEditPermission(context)
190
- if (forbidden) return forbidden
191
-
192
- const scope = context.req.param('scope')
193
- if (!isValidDashboardScope(scope)) {
194
- return publicProblem(context, {
195
- type: 'invalid-scope',
196
- title: 'Invalid scope',
197
- status: 400,
198
- detail: `Unknown dashboard scope '${scope}'.`,
199
- })
200
- }
201
-
202
- await context.get('dashboardLayoutRepository').remove(scope)
203
- return context.json({ ok: true })
204
- })
205
-
206
- export { dashboardLayoutApp }
@@ -1,5 +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
- export { dashboardLayoutApp } from './dashboard-layout.handler'