emdash 0.20.0 → 0.22.0

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 (685) hide show
  1. package/dist/{adapters-BzIHV3sw.d.mts → adapters-hUy8vOB-.d.mts} +1 -1
  2. package/dist/{adapters-BzIHV3sw.d.mts.map → adapters-hUy8vOB-.d.mts.map} +1 -1
  3. package/dist/after-B1IIdH3Y.mjs +29 -0
  4. package/dist/after-B1IIdH3Y.mjs.map +1 -0
  5. package/dist/{allowed-origins-B1u7Qnvg.mjs → allowed-origins-DjlhyfNN.mjs} +2 -2
  6. package/dist/{allowed-origins-B1u7Qnvg.mjs.map → allowed-origins-DjlhyfNN.mjs.map} +1 -1
  7. package/dist/api/route-utils.d.mts +3 -3
  8. package/dist/api/route-utils.mjs +20 -18
  9. package/dist/api/route-utils.mjs.map +1 -1
  10. package/dist/api/schemas/index.d.mts +2 -2
  11. package/dist/api/schemas/index.mjs +4 -3
  12. package/dist/{api-DStv36ik.mjs → api-BDuM4bEy.mjs} +31 -25
  13. package/dist/api-BDuM4bEy.mjs.map +1 -0
  14. package/dist/{api-tokens-DPfhPu5V.mjs → api-tokens-ViYKeIp9.mjs} +13 -3
  15. package/dist/api-tokens-ViYKeIp9.mjs.map +1 -0
  16. package/dist/{apply-Dr7snAMT.mjs → apply-wDqQoxX4.mjs} +18 -18
  17. package/dist/{apply-Dr7snAMT.mjs.map → apply-wDqQoxX4.mjs.map} +1 -1
  18. package/dist/astro/image-endpoint.d.mts +8 -0
  19. package/dist/astro/image-endpoint.d.mts.map +1 -0
  20. package/dist/astro/image-endpoint.mjs +55 -0
  21. package/dist/astro/image-endpoint.mjs.map +1 -0
  22. package/dist/astro/index.d.mts +36 -11
  23. package/dist/astro/index.d.mts.map +1 -1
  24. package/dist/astro/index.mjs +160 -35
  25. package/dist/astro/index.mjs.map +1 -1
  26. package/dist/astro/middleware/auth.d.mts +9 -9
  27. package/dist/astro/middleware/auth.d.mts.map +1 -1
  28. package/dist/astro/middleware/auth.mjs +12 -10
  29. package/dist/astro/middleware/auth.mjs.map +1 -1
  30. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  31. package/dist/astro/middleware/redirect.mjs +14 -5
  32. package/dist/astro/middleware/redirect.mjs.map +1 -1
  33. package/dist/astro/middleware/request-context.mjs +5 -4
  34. package/dist/astro/middleware/request-context.mjs.map +1 -1
  35. package/dist/astro/middleware/setup.mjs +1 -1
  36. package/dist/astro/middleware.d.mts +1 -1
  37. package/dist/astro/middleware.d.mts.map +1 -1
  38. package/dist/astro/middleware.mjs +157 -80
  39. package/dist/astro/middleware.mjs.map +1 -1
  40. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -6
  41. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs.map +1 -1
  42. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -6
  43. package/dist/astro/routes/api/admin/allowed-domains/index.mjs.map +1 -1
  44. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
  45. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  46. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -6
  47. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -9
  48. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -1
  49. package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -9
  50. package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -1
  51. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -9
  52. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -1
  53. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -14
  54. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
  55. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -14
  56. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
  57. package/dist/astro/routes/api/admin/bylines/index.mjs +17 -14
  58. package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
  59. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +15 -12
  60. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
  61. package/dist/astro/routes/api/admin/comments/_id_.mjs +8 -6
  62. package/dist/astro/routes/api/admin/comments/_id_.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/comments/bulk.mjs +12 -9
  64. package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/comments/counts.mjs +8 -6
  66. package/dist/astro/routes/api/admin/comments/counts.mjs.map +1 -1
  67. package/dist/astro/routes/api/admin/comments/index.mjs +12 -9
  68. package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
  69. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
  70. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
  71. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
  72. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
  73. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +40 -37
  74. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  75. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +40 -37
  76. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  77. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +39 -36
  78. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  79. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +39 -36
  80. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  81. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +39 -36
  82. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  83. package/dist/astro/routes/api/admin/plugins/index.mjs +39 -36
  84. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  85. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
  86. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +39 -36
  87. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +39 -36
  89. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +39 -36
  91. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +39 -36
  93. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  94. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +40 -37
  95. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  96. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +39 -36
  97. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +40 -37
  99. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  100. package/dist/astro/routes/api/admin/plugins/updates.mjs +39 -36
  101. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  102. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +39 -36
  103. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  104. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
  105. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +39 -36
  106. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  107. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
  108. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -3
  109. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -7
  110. package/dist/astro/routes/api/admin/users/_id_/index.mjs.map +1 -1
  111. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
  112. package/dist/astro/routes/api/admin/users/index.mjs +7 -6
  113. package/dist/astro/routes/api/admin/users/index.mjs.map +1 -1
  114. package/dist/astro/routes/api/auth/dev-bypass.mjs +6 -6
  115. package/dist/astro/routes/api/auth/invite/accept.mjs +3 -3
  116. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -11
  117. package/dist/astro/routes/api/auth/invite/complete.mjs.map +1 -1
  118. package/dist/astro/routes/api/auth/invite/index.mjs +9 -8
  119. package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
  120. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -10
  121. package/dist/astro/routes/api/auth/invite/register-options.mjs.map +1 -1
  122. package/dist/astro/routes/api/auth/logout.mjs +4 -4
  123. package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -10
  124. package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
  125. package/dist/astro/routes/api/auth/magic-link/verify.mjs +4 -4
  126. package/dist/astro/routes/api/auth/me.mjs +8 -7
  127. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  128. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  129. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  130. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  131. package/dist/astro/routes/api/auth/passkey/_id_.mjs +7 -6
  132. package/dist/astro/routes/api/auth/passkey/_id_.mjs.map +1 -1
  133. package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
  134. package/dist/astro/routes/api/auth/passkey/options.mjs +13 -12
  135. package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -1
  136. package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -10
  137. package/dist/astro/routes/api/auth/passkey/register/options.mjs.map +1 -1
  138. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -11
  139. package/dist/astro/routes/api/auth/passkey/register/verify.mjs.map +1 -1
  140. package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -11
  141. package/dist/astro/routes/api/auth/passkey/verify.mjs.map +1 -1
  142. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -11
  143. package/dist/astro/routes/api/auth/signup/complete.mjs.map +1 -1
  144. package/dist/astro/routes/api/auth/signup/request.mjs +11 -10
  145. package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
  146. package/dist/astro/routes/api/auth/signup/verify.mjs +3 -3
  147. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +16 -13
  148. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
  149. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +9 -0
  150. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts.map +1 -0
  151. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +179 -0
  152. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs.map +1 -0
  153. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -4
  154. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +4 -4
  155. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -4
  156. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -4
  157. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +13 -11
  158. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs.map +1 -1
  159. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +8 -7
  160. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
  161. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -4
  162. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -4
  163. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -7
  164. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
  165. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +17 -14
  166. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
  167. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -4
  168. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +4 -4
  169. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -7
  170. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
  171. package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -4
  172. package/dist/astro/routes/api/content/_collection_/index.mjs +8 -7
  173. package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -1
  174. package/dist/astro/routes/api/content/_collection_/trash.mjs +8 -7
  175. package/dist/astro/routes/api/content/_collection_/trash.mjs.map +1 -1
  176. package/dist/astro/routes/api/dashboard.mjs +10 -8
  177. package/dist/astro/routes/api/dashboard.mjs.map +1 -1
  178. package/dist/astro/routes/api/dev/emails.mjs +4 -4
  179. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  180. package/dist/astro/routes/api/import/probe.mjs +12 -11
  181. package/dist/astro/routes/api/import/probe.mjs.map +1 -1
  182. package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -5
  183. package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
  184. package/dist/astro/routes/api/import/wordpress/execute.mjs +16 -14
  185. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  186. package/dist/astro/routes/api/import/wordpress/media.mjs +10 -9
  187. package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
  188. package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -10
  189. package/dist/astro/routes/api/import/wordpress/prepare.mjs.map +1 -1
  190. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +10 -9
  191. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  192. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  193. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +12 -11
  194. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
  195. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
  196. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
  197. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +18 -15
  198. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  199. package/dist/astro/routes/api/manifest.mjs +5 -5
  200. package/dist/astro/routes/api/mcp.mjs +213 -45
  201. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  202. package/dist/astro/routes/api/media/_id_/confirm.mjs +8 -7
  203. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
  204. package/dist/astro/routes/api/media/_id_.mjs +8 -7
  205. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  206. package/dist/astro/routes/api/media/file/_...key_.mjs +3 -3
  207. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
  208. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
  209. package/dist/astro/routes/api/media/providers/index.mjs +4 -4
  210. package/dist/astro/routes/api/media/upload-url.mjs +10 -9
  211. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  212. package/dist/astro/routes/api/media.mjs +13 -12
  213. package/dist/astro/routes/api/media.mjs.map +1 -1
  214. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +11 -8
  215. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
  216. package/dist/astro/routes/api/menus/_name_/items.mjs +11 -8
  217. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
  218. package/dist/astro/routes/api/menus/_name_/reorder.mjs +11 -8
  219. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
  220. package/dist/astro/routes/api/menus/_name_/translations.mjs +11 -8
  221. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
  222. package/dist/astro/routes/api/menus/_name_.mjs +11 -8
  223. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
  224. package/dist/astro/routes/api/menus/index.mjs +11 -8
  225. package/dist/astro/routes/api/menus/index.mjs.map +1 -1
  226. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  227. package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
  228. package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
  229. package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
  230. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  231. package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
  232. package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
  233. package/dist/astro/routes/api/oauth/token.mjs +7 -7
  234. package/dist/astro/routes/api/openapi.json.mjs +5 -4
  235. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  236. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +5 -5
  237. package/dist/astro/routes/api/redirects/404s/index.mjs +11 -10
  238. package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
  239. package/dist/astro/routes/api/redirects/404s/summary.mjs +11 -10
  240. package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
  241. package/dist/astro/routes/api/redirects/_id_.mjs +12 -11
  242. package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
  243. package/dist/astro/routes/api/redirects/index.mjs +12 -11
  244. package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
  245. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
  246. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -4
  247. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +39 -36
  248. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  249. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +39 -36
  250. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  251. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +39 -36
  252. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  253. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +39 -36
  254. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  255. package/dist/astro/routes/api/schema/collections/index.mjs +39 -36
  256. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  257. package/dist/astro/routes/api/schema/index.mjs +7 -7
  258. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +39 -36
  259. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  260. package/dist/astro/routes/api/schema/orphans/index.mjs +39 -36
  261. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  262. package/dist/astro/routes/api/search/enable.mjs +11 -10
  263. package/dist/astro/routes/api/search/enable.mjs.map +1 -1
  264. package/dist/astro/routes/api/search/index.mjs +10 -9
  265. package/dist/astro/routes/api/search/index.mjs.map +1 -1
  266. package/dist/astro/routes/api/search/rebuild.mjs +11 -10
  267. package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
  268. package/dist/astro/routes/api/search/stats.mjs +7 -7
  269. package/dist/astro/routes/api/search/suggest.mjs +10 -9
  270. package/dist/astro/routes/api/search/suggest.mjs.map +1 -1
  271. package/dist/astro/routes/api/sections/_slug_.mjs +10 -9
  272. package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -1
  273. package/dist/astro/routes/api/sections/index.mjs +10 -9
  274. package/dist/astro/routes/api/sections/index.mjs.map +1 -1
  275. package/dist/astro/routes/api/settings/email.mjs +6 -6
  276. package/dist/astro/routes/api/settings.mjs +16 -13
  277. package/dist/astro/routes/api/settings.mjs.map +1 -1
  278. package/dist/astro/routes/api/setup/admin-verify.mjs +13 -12
  279. package/dist/astro/routes/api/setup/admin-verify.mjs.map +1 -1
  280. package/dist/astro/routes/api/setup/admin.mjs +12 -11
  281. package/dist/astro/routes/api/setup/admin.mjs.map +1 -1
  282. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
  283. package/dist/astro/routes/api/setup/dev-bypass.mjs +29 -26
  284. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  285. package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
  286. package/dist/astro/routes/api/setup/index.mjs +29 -26
  287. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  288. package/dist/astro/routes/api/setup/status.mjs +5 -5
  289. package/dist/astro/routes/api/snapshot.d.mts.map +1 -1
  290. package/dist/astro/routes/api/snapshot.mjs +10 -8
  291. package/dist/astro/routes/api/snapshot.mjs.map +1 -1
  292. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +16 -13
  293. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  294. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +16 -13
  295. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  296. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +16 -13
  297. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  298. package/dist/astro/routes/api/taxonomies/index.mjs +16 -13
  299. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  300. package/dist/astro/routes/api/themes/preview.mjs +8 -7
  301. package/dist/astro/routes/api/themes/preview.mjs.map +1 -1
  302. package/dist/astro/routes/api/typegen.mjs +6 -6
  303. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  304. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  305. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  306. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +8 -7
  307. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
  308. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +11 -10
  309. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  310. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +11 -10
  311. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  312. package/dist/astro/routes/api/widget-areas/_name_.mjs +7 -7
  313. package/dist/astro/routes/api/widget-areas/index.mjs +11 -10
  314. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  315. package/dist/astro/routes/api/widget-components.mjs +4 -4
  316. package/dist/astro/routes/robots.txt.mjs +10 -8
  317. package/dist/astro/routes/robots.txt.mjs.map +1 -1
  318. package/dist/astro/routes/sitemap-_collection_.xml.mjs +13 -11
  319. package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
  320. package/dist/astro/routes/sitemap.xml.mjs +11 -9
  321. package/dist/astro/routes/sitemap.xml.mjs.map +1 -1
  322. package/dist/astro/types.d.mts +16 -12
  323. package/dist/astro/types.d.mts.map +1 -1
  324. package/dist/auth/providers/github.d.mts +1 -1
  325. package/dist/auth/providers/google.d.mts +1 -1
  326. package/dist/{authorize-DsMSVSaY.mjs → authorize-C0EYoUeV.mjs} +2 -2
  327. package/dist/{authorize-DsMSVSaY.mjs.map → authorize-C0EYoUeV.mjs.map} +1 -1
  328. package/dist/{base64-CqR-7kqF.mjs → base64-CmWvODNW.mjs} +1 -1
  329. package/dist/{base64-CqR-7kqF.mjs.map → base64-CmWvODNW.mjs.map} +1 -1
  330. package/dist/{byline-DUx48sJp.mjs → byline-CC0WS4Gu.mjs} +40 -18
  331. package/dist/byline-CC0WS4Gu.mjs.map +1 -0
  332. package/dist/{byline-fields-8TMtkBnH.mjs → byline-fields-53i9Et0x.mjs} +3 -3
  333. package/dist/{byline-fields-8TMtkBnH.mjs.map → byline-fields-53i9Et0x.mjs.map} +1 -1
  334. package/dist/{byline-fields--WxSNS79.mjs → byline-fields-DNp0HNKc.mjs} +2 -2
  335. package/dist/{byline-fields--WxSNS79.mjs.map → byline-fields-DNp0HNKc.mjs.map} +1 -1
  336. package/dist/{byline-fields-DbibsvTl.d.mts → byline-fields-jy07PZJM.d.mts} +9 -4
  337. package/dist/byline-fields-jy07PZJM.d.mts.map +1 -0
  338. package/dist/{byline-registry-CWP7I71B.mjs → byline-registry-BOjqDOim.mjs} +3 -3
  339. package/dist/{byline-registry-CWP7I71B.mjs.map → byline-registry-BOjqDOim.mjs.map} +1 -1
  340. package/dist/{bylines-s8c2DXbH.mjs → bylines-CCO1CYnH.mjs} +8 -8
  341. package/dist/{bylines-s8c2DXbH.mjs.map → bylines-CCO1CYnH.mjs.map} +1 -1
  342. package/dist/{bylines-BdxWCnPL.mjs → bylines-DGekMGGm.mjs} +10 -4
  343. package/dist/{bylines-BdxWCnPL.mjs.map → bylines-DGekMGGm.mjs.map} +1 -1
  344. package/dist/{cache-B_HzASVT.mjs → cache-pXTpun6s.mjs} +3 -3
  345. package/dist/{cache-B_HzASVT.mjs.map → cache-pXTpun6s.mjs.map} +1 -1
  346. package/dist/{challenge-store-DXX3rfdI.mjs → challenge-store-C6E_kWTs.mjs} +1 -1
  347. package/dist/{challenge-store-DXX3rfdI.mjs.map → challenge-store-C6E_kWTs.mjs.map} +1 -1
  348. package/dist/{chunks-BerYVuve.mjs → chunks-SyjZaYwV.mjs} +2 -2
  349. package/dist/{chunks-BerYVuve.mjs.map → chunks-SyjZaYwV.mjs.map} +1 -1
  350. package/dist/cli/index.mjs +29 -27
  351. package/dist/cli/index.mjs.map +1 -1
  352. package/dist/client/cf-access.d.mts +1 -1
  353. package/dist/client/index.d.mts +1 -1
  354. package/dist/client/index.mjs +2 -1
  355. package/dist/client/index.mjs.map +1 -1
  356. package/dist/{comment-sqQxNpN3.mjs → comment-80UyJJUR.mjs} +11 -3
  357. package/dist/comment-80UyJJUR.mjs.map +1 -0
  358. package/dist/comment-reaction-B6AdcpXw.mjs +85 -0
  359. package/dist/comment-reaction-B6AdcpXw.mjs.map +1 -0
  360. package/dist/{comments-Vkivawyl.mjs → comments-CtPkT2tp.mjs} +3 -3
  361. package/dist/{comments-Vkivawyl.mjs.map → comments-CtPkT2tp.mjs.map} +1 -1
  362. package/dist/{components-CK0cuUoH.mjs → components-D6ZnrzbB.mjs} +1 -1
  363. package/dist/{components-CK0cuUoH.mjs.map → components-D6ZnrzbB.mjs.map} +1 -1
  364. package/dist/{content-BIlVx-RX.mjs → content-2PTDNnoh.mjs} +22 -7
  365. package/dist/content-2PTDNnoh.mjs.map +1 -0
  366. package/dist/{context-Y7BRkWes.mjs → context-f1__jpzF.mjs} +11 -11
  367. package/dist/{context-Y7BRkWes.mjs.map → context-f1__jpzF.mjs.map} +1 -1
  368. package/dist/{cron-BJ2ClIlj.mjs → cron-CQPxBjYs.mjs} +1 -1
  369. package/dist/{cron-BJ2ClIlj.mjs.map → cron-CQPxBjYs.mjs.map} +1 -1
  370. package/dist/{dashboard-2JgAMWxK.mjs → dashboard-BOESNcwE.mjs} +4 -4
  371. package/dist/{dashboard-2JgAMWxK.mjs.map → dashboard-BOESNcwE.mjs.map} +1 -1
  372. package/dist/database/instrumentation.d.mts +19 -0
  373. package/dist/database/instrumentation.d.mts.map +1 -1
  374. package/dist/database/instrumentation.mjs +8 -1
  375. package/dist/database/instrumentation.mjs.map +1 -1
  376. package/dist/db/index.d.mts +3 -3
  377. package/dist/db/index.mjs +1 -1
  378. package/dist/db/libsql.d.mts +1 -1
  379. package/dist/db/postgres.d.mts +1 -1
  380. package/dist/db/sqlite.d.mts +1 -1
  381. package/dist/{db-errors-CtzxKBxe.mjs → db-errors-CrC5w3Jz.mjs} +1 -1
  382. package/dist/{db-errors-CtzxKBxe.mjs.map → db-errors-CrC5w3Jz.mjs.map} +1 -1
  383. package/dist/{default-IlBaTFxM.mjs → default-CpBEPn6a.mjs} +1 -1
  384. package/dist/{default-IlBaTFxM.mjs.map → default-CpBEPn6a.mjs.map} +1 -1
  385. package/dist/{device-flow-R23SIbQ2.mjs → device-flow-Bn-qiDrw.mjs} +5 -5
  386. package/dist/{device-flow-R23SIbQ2.mjs.map → device-flow-Bn-qiDrw.mjs.map} +1 -1
  387. package/dist/{email-console-DHT2Fbpj.mjs → email-console-B9VS6eN4.mjs} +1 -1
  388. package/dist/{email-console-DHT2Fbpj.mjs.map → email-console-B9VS6eN4.mjs.map} +1 -1
  389. package/dist/{error-RwM4dD35.mjs → error-BQ1pxs0L.mjs} +2 -2
  390. package/dist/{error-RwM4dD35.mjs.map → error-BQ1pxs0L.mjs.map} +1 -1
  391. package/dist/{escape-Ds07EEyu.mjs → escape-BtbJWyaW.mjs} +1 -1
  392. package/dist/{escape-Ds07EEyu.mjs.map → escape-BtbJWyaW.mjs.map} +1 -1
  393. package/dist/{fts-manager-1RgHmopc.mjs → fts-manager-DtiWqwNJ.mjs} +2 -2
  394. package/dist/{fts-manager-1RgHmopc.mjs.map → fts-manager-DtiWqwNJ.mjs.map} +1 -1
  395. package/dist/{hash-9w3pd3-m.mjs → hash-BowcuPwv.mjs} +1 -1
  396. package/dist/{hash-9w3pd3-m.mjs.map → hash-BowcuPwv.mjs.map} +1 -1
  397. package/dist/{import-Dh8bWmyq.mjs → import-DqSGOlmR.mjs} +4 -4
  398. package/dist/{import-Dh8bWmyq.mjs.map → import-DqSGOlmR.mjs.map} +1 -1
  399. package/dist/{index-B1keaX5Y.d.mts → index-C2dLNT6S.d.mts} +169 -14
  400. package/dist/index-C2dLNT6S.d.mts.map +1 -0
  401. package/dist/{index-DR56od45.d.mts → index-ZDEwR7qW.d.mts} +3 -3
  402. package/dist/{index-DR56od45.d.mts.map → index-ZDEwR7qW.d.mts.map} +1 -1
  403. package/dist/index.d.mts +18 -17
  404. package/dist/index.mjs +63 -59
  405. package/dist/init-lock-6b309ZrF.mjs +56 -0
  406. package/dist/init-lock-6b309ZrF.mjs.map +1 -0
  407. package/dist/{load-BBetCvLC.mjs → load-Dtoo7KcJ.mjs} +2 -2
  408. package/dist/{load-BBetCvLC.mjs.map → load-Dtoo7KcJ.mjs.map} +1 -1
  409. package/dist/{loader-ZN1ll-d-.mjs → loader-C8Z48Uof.mjs} +4 -4
  410. package/dist/{loader-ZN1ll-d-.mjs.map → loader-C8Z48Uof.mjs.map} +1 -1
  411. package/dist/{manifest-schema-BtwbL_vj.mjs → manifest-schema-DDSbwkAL.mjs} +1 -1
  412. package/dist/{manifest-schema-BtwbL_vj.mjs.map → manifest-schema-DDSbwkAL.mjs.map} +1 -1
  413. package/dist/media/image-endpoint.d.mts +74 -0
  414. package/dist/media/image-endpoint.d.mts.map +1 -0
  415. package/dist/media/image-endpoint.mjs +162 -0
  416. package/dist/media/image-endpoint.mjs.map +1 -0
  417. package/dist/media/index.d.mts +1 -1
  418. package/dist/media/index.mjs +2 -2
  419. package/dist/media/local-runtime.d.mts +11 -11
  420. package/dist/media/local-runtime.mjs +9 -7
  421. package/dist/media/local-runtime.mjs.map +1 -1
  422. package/dist/{media-JOf3pNkw.mjs → media-Bw0sA6rb.mjs} +2 -2
  423. package/dist/{media-JOf3pNkw.mjs.map → media-Bw0sA6rb.mjs.map} +1 -1
  424. package/dist/{media-allowlist-Dknq-OFY.mjs → media-allowlist-DunzrKFM.mjs} +2 -2
  425. package/dist/{media-allowlist-Dknq-OFY.mjs.map → media-allowlist-DunzrKFM.mjs.map} +1 -1
  426. package/dist/{media-url-VClf8glU.mjs → media-url-S22B6aPr.mjs} +1 -1
  427. package/dist/{media-url-VClf8glU.mjs.map → media-url-S22B6aPr.mjs.map} +1 -1
  428. package/dist/{menus-DX4_E01q.mjs → menus-CYs0WZoL.mjs} +20 -6
  429. package/dist/menus-CYs0WZoL.mjs.map +1 -0
  430. package/dist/{menus-DrQLusqj.mjs → menus-DOs1MQJg.mjs} +120 -20
  431. package/dist/menus-DOs1MQJg.mjs.map +1 -0
  432. package/dist/{mime-CCEzze7W.mjs → mime-DYpsOAny.mjs} +1 -1
  433. package/dist/{mime-CCEzze7W.mjs.map → mime-DYpsOAny.mjs.map} +1 -1
  434. package/dist/{mode-CO2vQHfq.mjs → mode-CFh8Dw8G.mjs} +1 -1
  435. package/dist/{mode-CO2vQHfq.mjs.map → mode-CFh8Dw8G.mjs.map} +1 -1
  436. package/dist/{normalize-CK5o04zr.mjs → normalize-Uam4_Vkr.mjs} +1 -1
  437. package/dist/{normalize-CK5o04zr.mjs.map → normalize-Uam4_Vkr.mjs.map} +1 -1
  438. package/dist/{oauth-authorization-Bw4NdF_S.mjs → oauth-authorization-BN-t83Uy.mjs} +5 -5
  439. package/dist/{oauth-authorization-Bw4NdF_S.mjs.map → oauth-authorization-BN-t83Uy.mjs.map} +1 -1
  440. package/dist/{oauth-clients-BGGFp57s.mjs → oauth-clients-R6I_V0qz.mjs} +1 -1
  441. package/dist/{oauth-clients-BGGFp57s.mjs.map → oauth-clients-R6I_V0qz.mjs.map} +1 -1
  442. package/dist/{oauth-state-store-97x0xtN2.mjs → oauth-state-store-Csoc6kkR.mjs} +1 -1
  443. package/dist/{oauth-state-store-97x0xtN2.mjs.map → oauth-state-store-Csoc6kkR.mjs.map} +1 -1
  444. package/dist/{oauth-user-lookup-B_vnZHKO.mjs → oauth-user-lookup-d7VYjTpx.mjs} +1 -1
  445. package/dist/{oauth-user-lookup-B_vnZHKO.mjs.map → oauth-user-lookup-d7VYjTpx.mjs.map} +1 -1
  446. package/dist/object-cache/memory.d.mts +15 -0
  447. package/dist/object-cache/memory.d.mts.map +1 -0
  448. package/dist/object-cache/memory.mjs +56 -0
  449. package/dist/object-cache/memory.mjs.map +1 -0
  450. package/dist/object-cache-SEb2IM4Z.mjs +373 -0
  451. package/dist/object-cache-SEb2IM4Z.mjs.map +1 -0
  452. package/dist/{options-DyYIYpPd.d.mts → options-DFFpvNJU.d.mts} +3 -3
  453. package/dist/{options-DyYIYpPd.d.mts.map → options-DFFpvNJU.d.mts.map} +1 -1
  454. package/dist/{options-BPCVnesz.mjs → options-DTTML-Tx.mjs} +1 -1
  455. package/dist/{options-BPCVnesz.mjs.map → options-DTTML-Tx.mjs.map} +1 -1
  456. package/dist/page/index.d.mts +2 -2
  457. package/dist/{parse-CrGndy1A.mjs → parse-qrHlnzTy.mjs} +2 -2
  458. package/dist/{parse-CrGndy1A.mjs.map → parse-qrHlnzTy.mjs.map} +1 -1
  459. package/dist/{passkey-config-C3QgnQnU.mjs → passkey-config-BJTbcnI5.mjs} +1 -1
  460. package/dist/{passkey-config-C3QgnQnU.mjs.map → passkey-config-BJTbcnI5.mjs.map} +1 -1
  461. package/dist/{patterns-p-RBdTbM.mjs → patterns-BKmjvM7K.mjs} +1 -1
  462. package/dist/{patterns-p-RBdTbM.mjs.map → patterns-BKmjvM7K.mjs.map} +1 -1
  463. package/dist/{placeholder-BZxr8W1j.mjs → placeholder-CIDCpyEY.mjs} +1 -1
  464. package/dist/{placeholder-BZxr8W1j.mjs.map → placeholder-CIDCpyEY.mjs.map} +1 -1
  465. package/dist/{placeholder-CVBv5z8k.d.mts → placeholder-CirPauNG.d.mts} +1 -1
  466. package/dist/{placeholder-CVBv5z8k.d.mts.map → placeholder-CirPauNG.d.mts.map} +1 -1
  467. package/dist/plugin-types.d.mts +1 -1
  468. package/dist/plugin-utils.d.mts +9 -9
  469. package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
  470. package/dist/plugins/adapt-sandbox-entry.mjs +4 -2
  471. package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
  472. package/dist/{transport-CmpLD7W3.mjs → portable-text-BIFhrU6T.mjs} +2 -135
  473. package/dist/portable-text-BIFhrU6T.mjs.map +1 -0
  474. package/dist/{preview-BfuRkVKW.mjs → preview-IDye9SPQ.mjs} +2 -2
  475. package/dist/{preview-BfuRkVKW.mjs.map → preview-IDye9SPQ.mjs.map} +1 -1
  476. package/dist/{public-url-BFVC2OTJ.mjs → public-url-8lFyQ8ZF.mjs} +1 -1
  477. package/dist/{public-url-BFVC2OTJ.mjs.map → public-url-8lFyQ8ZF.mjs.map} +1 -1
  478. package/dist/{query-CbUcI4Xk.mjs → query-B1RVFUNM.mjs} +159 -31
  479. package/dist/query-B1RVFUNM.mjs.map +1 -0
  480. package/dist/{rate-limit-C7hjdkS5.mjs → rate-limit-DfNAqIWW.mjs} +2 -2
  481. package/dist/{rate-limit-C7hjdkS5.mjs.map → rate-limit-DfNAqIWW.mjs.map} +1 -1
  482. package/dist/{redirect-B_q19j4v.mjs → redirect-B4wuX2A5.mjs} +1 -1
  483. package/dist/{redirect-B_q19j4v.mjs.map → redirect-B4wuX2A5.mjs.map} +1 -1
  484. package/dist/{redirect-CRWIt8Zj.mjs → redirect-Bifv_yHv.mjs} +3 -3
  485. package/dist/{redirect-CRWIt8Zj.mjs.map → redirect-Bifv_yHv.mjs.map} +1 -1
  486. package/dist/{redirects-DxVoR7PI.mjs → redirects-CT_vb3NV.mjs} +24 -18
  487. package/dist/redirects-CT_vb3NV.mjs.map +1 -0
  488. package/dist/{redirects-CCbCqCCd.mjs → redirects-DUvXgzCX.mjs} +14 -10
  489. package/dist/redirects-DUvXgzCX.mjs.map +1 -0
  490. package/dist/{registry-brYh-rAT.mjs → registry-CjK96fHD.mjs} +6 -6
  491. package/dist/{registry-brYh-rAT.mjs.map → registry-CjK96fHD.mjs.map} +1 -1
  492. package/dist/{request-cache-D32LpnmI.mjs → request-cache-KCNHp_RJ.mjs} +1 -1
  493. package/dist/{request-cache-D32LpnmI.mjs.map → request-cache-KCNHp_RJ.mjs.map} +1 -1
  494. package/dist/{request-meta-7ByVLxB-.mjs → request-meta-ei3ATilC.mjs} +2 -2
  495. package/dist/{request-meta-7ByVLxB-.mjs.map → request-meta-ei3ATilC.mjs.map} +1 -1
  496. package/dist/{resolve-BqYMVG0D.mjs → resolve-CpbBglV0.mjs} +1 -1
  497. package/dist/{resolve-BqYMVG0D.mjs.map → resolve-CpbBglV0.mjs.map} +1 -1
  498. package/dist/{runner--4wMWwKM.mjs → runner-DYYnW530.mjs} +201 -170
  499. package/dist/runner-DYYnW530.mjs.map +1 -0
  500. package/dist/{runner-DTdhuI9i.d.mts → runner-jdXtFEd3.d.mts} +2 -2
  501. package/dist/{runner-DTdhuI9i.d.mts.map → runner-jdXtFEd3.d.mts.map} +1 -1
  502. package/dist/runtime.d.mts +10 -10
  503. package/dist/runtime.mjs +3 -3
  504. package/dist/{schema-C1E70ug_.mjs → schema-5z2Jfnbm.mjs} +13 -8
  505. package/dist/schema-5z2Jfnbm.mjs.map +1 -0
  506. package/dist/{search-B3SGZw91.mjs → search-D9FkoM-k.mjs} +4 -4
  507. package/dist/{search-B3SGZw91.mjs.map → search-D9FkoM-k.mjs.map} +1 -1
  508. package/dist/{secrets-ChPTmy9x.mjs → secrets-BUE5UQys.mjs} +23 -13
  509. package/dist/secrets-BUE5UQys.mjs.map +1 -0
  510. package/dist/{sections-D_lVzwRZ.mjs → sections-DWYZo6HK.mjs} +3 -3
  511. package/dist/{sections-D_lVzwRZ.mjs.map → sections-DWYZo6HK.mjs.map} +1 -1
  512. package/dist/seed/index.d.mts +2 -2
  513. package/dist/seed/index.mjs +22 -20
  514. package/dist/seo/index.d.mts +1 -1
  515. package/dist/seo/index.mjs +1 -1
  516. package/dist/{seo-B5e6y9Wk.mjs → seo-Cu47j3mJ.mjs} +5 -2
  517. package/dist/seo-Cu47j3mJ.mjs.map +1 -0
  518. package/dist/{seo-D_LPkOtu.mjs → seo-DRnqnstF.mjs} +1 -1
  519. package/dist/{seo-D_LPkOtu.mjs.map → seo-DRnqnstF.mjs.map} +1 -1
  520. package/dist/{service-ChDcsTBs.mjs → service-DTmtqCnw.mjs} +3 -3
  521. package/dist/{service-ChDcsTBs.mjs.map → service-DTmtqCnw.mjs.map} +1 -1
  522. package/dist/session-user-DmtPMNa1.mjs +51 -0
  523. package/dist/session-user-DmtPMNa1.mjs.map +1 -0
  524. package/dist/{settings-Cv47v9u8.mjs → settings-DUrVDN0c.mjs} +3 -3
  525. package/dist/{settings-Cv47v9u8.mjs.map → settings-DUrVDN0c.mjs.map} +1 -1
  526. package/dist/settings-yaxu9mU-.mjs +245 -0
  527. package/dist/settings-yaxu9mU-.mjs.map +1 -0
  528. package/dist/{setup-complete-yvPE4OsP.mjs → setup-complete-BWFHzgvZ.mjs} +2 -2
  529. package/dist/{setup-complete-yvPE4OsP.mjs.map → setup-complete-BWFHzgvZ.mjs.map} +1 -1
  530. package/dist/{setup-nonce-C9aFzb94.mjs → setup-nonce-DFWG-J5c.mjs} +1 -1
  531. package/dist/{setup-nonce-C9aFzb94.mjs.map → setup-nonce-DFWG-J5c.mjs.map} +1 -1
  532. package/dist/single-flight-cache-B5p5cNOL.mjs +104 -0
  533. package/dist/single-flight-cache-B5p5cNOL.mjs.map +1 -0
  534. package/dist/{site-url-CnHlmAs9.mjs → site-url-BOTPgmeM.mjs} +2 -2
  535. package/dist/{site-url-CnHlmAs9.mjs.map → site-url-BOTPgmeM.mjs.map} +1 -1
  536. package/dist/{slugify-Cjh1ssOZ.mjs → slugify-Cce3dTdg.mjs} +1 -1
  537. package/dist/{slugify-Cjh1ssOZ.mjs.map → slugify-Cce3dTdg.mjs.map} +1 -1
  538. package/dist/{ssrf-BsVGIE0Z.mjs → ssrf-B0VeRLrp.mjs} +1 -1
  539. package/dist/{ssrf-BsVGIE0Z.mjs.map → ssrf-B0VeRLrp.mjs.map} +1 -1
  540. package/dist/status-CB2basWJ.mjs +30 -0
  541. package/dist/status-CB2basWJ.mjs.map +1 -0
  542. package/dist/storage/local.d.mts +1 -1
  543. package/dist/storage/local.mjs +2 -2
  544. package/dist/storage/s3.d.mts +1 -1
  545. package/dist/storage/s3.mjs +1 -1
  546. package/dist/{taxonomies-BdAmbOwx.mjs → taxonomies-Be9B1jEj.mjs} +156 -122
  547. package/dist/taxonomies-Be9B1jEj.mjs.map +1 -0
  548. package/dist/{taxonomies-BILwiyGk.mjs → taxonomies-CJmkEpdS.mjs} +7 -7
  549. package/dist/{taxonomies-BILwiyGk.mjs.map → taxonomies-CJmkEpdS.mjs.map} +1 -1
  550. package/dist/{taxonomy-CdllE4oq.mjs → taxonomy-B2HQuVf5.mjs} +19 -6
  551. package/dist/taxonomy-B2HQuVf5.mjs.map +1 -0
  552. package/dist/{tokens-Bx2afeT-.mjs → tokens-BvrJvIB6.mjs} +2 -2
  553. package/dist/{tokens-Bx2afeT-.mjs.map → tokens-BvrJvIB6.mjs.map} +1 -1
  554. package/dist/{transaction-x2tJQ-A1.mjs → transaction-qfqpPVpu.mjs} +1 -1
  555. package/dist/{transaction-x2tJQ-A1.mjs.map → transaction-qfqpPVpu.mjs.map} +1 -1
  556. package/dist/transport-B4rXnSTf.mjs +135 -0
  557. package/dist/transport-B4rXnSTf.mjs.map +1 -0
  558. package/dist/{transport-B7PPP2CC.d.mts → transport-k7YjfmEn.d.mts} +1 -1
  559. package/dist/{transport-B7PPP2CC.d.mts.map → transport-k7YjfmEn.d.mts.map} +1 -1
  560. package/dist/{trusted-proxy-B4AfnoAp.mjs → trusted-proxy-DY1_UqHr.mjs} +1 -1
  561. package/dist/{trusted-proxy-B4AfnoAp.mjs.map → trusted-proxy-DY1_UqHr.mjs.map} +1 -1
  562. package/dist/{types-CPAPl93j.d.mts → types-CIKX2HO5.d.mts} +2 -2
  563. package/dist/{types-CPAPl93j.d.mts.map → types-CIKX2HO5.d.mts.map} +1 -1
  564. package/dist/{types-D4kUqbHh.d.mts → types-CKNXt63n.d.mts} +1 -1
  565. package/dist/{types-D4kUqbHh.d.mts.map → types-CKNXt63n.d.mts.map} +1 -1
  566. package/dist/{types-DTniiNto.d.mts → types-CWGYDYqF.d.mts} +2 -2
  567. package/dist/{types-DTniiNto.d.mts.map → types-CWGYDYqF.d.mts.map} +1 -1
  568. package/dist/{types-BH8-30hc.d.mts → types-CgvhU-hF.d.mts} +1 -1
  569. package/dist/{types-BH8-30hc.d.mts.map → types-CgvhU-hF.d.mts.map} +1 -1
  570. package/dist/{types-BFgrqwSk.d.mts → types-CmivgZbp.d.mts} +1 -1
  571. package/dist/{types-BFgrqwSk.d.mts.map → types-CmivgZbp.d.mts.map} +1 -1
  572. package/dist/{types-BPzXTV9x.d.mts → types-Czg1S3gB.d.mts} +9 -1
  573. package/dist/{types-BPzXTV9x.d.mts.map → types-Czg1S3gB.d.mts.map} +1 -1
  574. package/dist/types-DCNNfeCv.d.mts +124 -0
  575. package/dist/types-DCNNfeCv.d.mts.map +1 -0
  576. package/dist/{types-BUUVn1zr.d.mts → types-DPCpkzvS.d.mts} +1 -1
  577. package/dist/{types-BUUVn1zr.d.mts.map → types-DPCpkzvS.d.mts.map} +1 -1
  578. package/dist/{types-S15DXXNi.d.mts → types-DXLVzV2w.d.mts} +1 -1
  579. package/dist/{types-S15DXXNi.d.mts.map → types-DXLVzV2w.d.mts.map} +1 -1
  580. package/dist/{types-BXSUSAjt.mjs → types-DXOIbece.mjs} +3 -3
  581. package/dist/{types-BXSUSAjt.mjs.map → types-DXOIbece.mjs.map} +1 -1
  582. package/dist/{types-DpFmlNyB.mjs → types-DYF2_Vf4.mjs} +1 -1
  583. package/dist/{types-DpFmlNyB.mjs.map → types-DYF2_Vf4.mjs.map} +1 -1
  584. package/dist/{types-DZk_y-MU.mjs → types-tM44hEcf.mjs} +1 -1
  585. package/dist/{types-DZk_y-MU.mjs.map → types-tM44hEcf.mjs.map} +1 -1
  586. package/dist/{user-C0um7wrg.mjs → user-Db-Rti_z.mjs} +3 -3
  587. package/dist/{user-C0um7wrg.mjs.map → user-Db-Rti_z.mjs.map} +1 -1
  588. package/dist/{utils-C4Ih4DML.mjs → utils-LbHcSRTj.mjs} +2 -2
  589. package/dist/{utils-C4Ih4DML.mjs.map → utils-LbHcSRTj.mjs.map} +1 -1
  590. package/dist/{validate-Bz4vqcX1.mjs → validate-Bwl64sxv.mjs} +3 -3
  591. package/dist/{validate-Bz4vqcX1.mjs.map → validate-Bwl64sxv.mjs.map} +1 -1
  592. package/dist/{validate-CNwkPWzz.d.mts → validate-CE2M0nvx.d.mts} +5 -5
  593. package/dist/{validate-CNwkPWzz.d.mts.map → validate-CE2M0nvx.d.mts.map} +1 -1
  594. package/dist/{validation-DgGTJm3u.mjs → validation-C_yOvFaO.mjs} +6 -6
  595. package/dist/{validation-DgGTJm3u.mjs.map → validation-C_yOvFaO.mjs.map} +1 -1
  596. package/dist/version-RnYXYjzD.mjs +7 -0
  597. package/dist/{version-D-5txk2m.mjs.map → version-RnYXYjzD.mjs.map} +1 -1
  598. package/dist/{widgets-DZfmAbE4.mjs → widgets-8GONuRHo.mjs} +4 -4
  599. package/dist/{widgets-DZfmAbE4.mjs.map → widgets-8GONuRHo.mjs.map} +1 -1
  600. package/dist/{zod-generator-Djo_VHCt.mjs → zod-generator-D481JjzD.mjs} +3 -3
  601. package/dist/{zod-generator-Djo_VHCt.mjs.map → zod-generator-D481JjzD.mjs.map} +1 -1
  602. package/package.json +18 -6
  603. package/src/api/handlers/api-tokens.ts +19 -0
  604. package/src/api/handlers/comment-reactions.ts +165 -0
  605. package/src/api/handlers/redirects.ts +24 -13
  606. package/src/api/handlers/schema.ts +8 -0
  607. package/src/api/schemas/comments.ts +10 -0
  608. package/src/api/schemas/redirects.ts +11 -4
  609. package/src/astro/image-endpoint.ts +84 -0
  610. package/src/astro/index.ts +6 -0
  611. package/src/astro/integration/index.ts +127 -30
  612. package/src/astro/integration/routes.ts +51 -9
  613. package/src/astro/integration/runtime.ts +65 -1
  614. package/src/astro/integration/virtual-modules.ts +32 -0
  615. package/src/astro/integration/vite-config.ts +16 -0
  616. package/src/astro/middleware/auth.ts +4 -3
  617. package/src/astro/middleware/redirect.ts +12 -0
  618. package/src/astro/middleware/stream-end-metrics.ts +11 -1
  619. package/src/astro/middleware.ts +41 -3
  620. package/src/astro/object-cache/adapters.ts +50 -0
  621. package/src/astro/prefetch.ts +77 -0
  622. package/src/astro/routes/api/comments/[collection]/[contentId]/reactions.ts +97 -0
  623. package/src/astro/routes/api/setup/dev-bypass.ts +5 -1
  624. package/src/astro/routes/api/snapshot.ts +3 -1
  625. package/src/astro/session-user.ts +57 -0
  626. package/src/astro/types.ts +1 -0
  627. package/src/bylines/field-defs-cache.ts +70 -20
  628. package/src/cli/commands/doctor.ts +1 -1
  629. package/src/comments/query.ts +78 -7
  630. package/src/comments/ranking.ts +58 -0
  631. package/src/components/Comments.astro +136 -1
  632. package/src/config/secrets.ts +28 -14
  633. package/src/database/instrumentation.ts +21 -1
  634. package/src/database/migrations/044_comment_reactions.ts +56 -0
  635. package/src/database/migrations/runner.ts +2 -0
  636. package/src/database/repositories/byline.ts +13 -0
  637. package/src/database/repositories/comment-reaction.ts +132 -0
  638. package/src/database/repositories/comment.ts +10 -0
  639. package/src/database/repositories/content.ts +25 -3
  640. package/src/database/repositories/menu.ts +13 -1
  641. package/src/database/repositories/seo.ts +3 -0
  642. package/src/database/repositories/taxonomy.ts +13 -1
  643. package/src/database/types.ts +9 -0
  644. package/src/emdash-runtime.ts +23 -9
  645. package/src/index.ts +22 -0
  646. package/src/mcp/server.ts +307 -20
  647. package/src/media/image-endpoint.ts +167 -0
  648. package/src/menus/index.ts +11 -4
  649. package/src/object-cache/codec.ts +73 -0
  650. package/src/object-cache/index.ts +495 -0
  651. package/src/object-cache/memory.ts +91 -0
  652. package/src/object-cache/types.ts +124 -0
  653. package/src/plugins/adapt-sandbox-entry.ts +11 -1
  654. package/src/query.ts +196 -17
  655. package/src/redirects/status.ts +27 -0
  656. package/src/schema/query.ts +11 -4
  657. package/src/settings/index.ts +30 -17
  658. package/src/taxonomies/index.ts +237 -165
  659. package/src/utils/{isolate-cache.ts → single-flight-cache.ts} +26 -21
  660. package/src/virtual-modules.d.ts +11 -0
  661. package/dist/api-DStv36ik.mjs.map +0 -1
  662. package/dist/api-tokens-DPfhPu5V.mjs.map +0 -1
  663. package/dist/byline-DUx48sJp.mjs.map +0 -1
  664. package/dist/byline-fields-DbibsvTl.d.mts.map +0 -1
  665. package/dist/comment-sqQxNpN3.mjs.map +0 -1
  666. package/dist/content-BIlVx-RX.mjs.map +0 -1
  667. package/dist/index-B1keaX5Y.d.mts.map +0 -1
  668. package/dist/menus-DX4_E01q.mjs.map +0 -1
  669. package/dist/menus-DrQLusqj.mjs.map +0 -1
  670. package/dist/query-CbUcI4Xk.mjs.map +0 -1
  671. package/dist/redirects-CCbCqCCd.mjs.map +0 -1
  672. package/dist/redirects-DxVoR7PI.mjs.map +0 -1
  673. package/dist/runner--4wMWwKM.mjs.map +0 -1
  674. package/dist/schema-C1E70ug_.mjs.map +0 -1
  675. package/dist/secrets-ChPTmy9x.mjs.map +0 -1
  676. package/dist/seo-B5e6y9Wk.mjs.map +0 -1
  677. package/dist/settings-DfxiWY_s.mjs +0 -411
  678. package/dist/settings-DfxiWY_s.mjs.map +0 -1
  679. package/dist/taxonomies-BdAmbOwx.mjs.map +0 -1
  680. package/dist/taxonomy-CdllE4oq.mjs.map +0 -1
  681. package/dist/transport-CmpLD7W3.mjs.map +0 -1
  682. package/dist/version-D-5txk2m.mjs +0 -7
  683. /package/dist/{api-tokens-Oq39ba-Z.mjs → api-tokens-BVH-H8Z9.mjs} +0 -0
  684. /package/dist/{ssrf-BvgVcfNQ.mjs → ssrf-Cz1e2QPn.mjs} +0 -0
  685. /package/dist/{types-CZI4E3qG.mjs → types-Bfr2Pj6z.mjs} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"zod-generator-Djo_VHCt.mjs","names":[],"sources":["../src/schema/zod-generator.ts"],"sourcesContent":["import { z, type ZodTypeAny } from \"zod\";\n\nimport { hashString } from \"../utils/hash.js\";\nimport type { Field, FieldType, CollectionWithFields } from \"./types.js\";\n\n/** Pattern to split on underscores, hyphens, and spaces for PascalCase conversion */\nconst PASCAL_CASE_SPLIT_PATTERN = /[_\\-\\s]+/;\n\n/**\n * Generate a Zod schema from a collection's field definitions\n *\n * This allows runtime validation of content based on dynamically\n * defined schemas stored in D1.\n */\nexport function generateZodSchema(\n\tcollection: CollectionWithFields,\n): z.ZodObject<Record<string, ZodTypeAny>> {\n\tconst shape: Record<string, ZodTypeAny> = {};\n\n\tfor (const field of collection.fields) {\n\t\tshape[field.slug] = generateFieldSchema(field);\n\t}\n\n\treturn z.object(shape);\n}\n\n/**\n * Generate Zod schema for a single field\n */\nexport function generateFieldSchema(field: Field): ZodTypeAny {\n\tlet schema = getBaseSchema(field.type, field);\n\n\t// Apply validation rules\n\tif (field.validation) {\n\t\tschema = applyValidation(schema, field);\n\t}\n\n\t// Apply required/optional. Non-required fields use `.nullish()` rather\n\t// than `.optional()` because the underlying SQLite columns are nullable\n\t// (see `SchemaRegistry.addFieldColumn` -- non-required fields are added\n\t// without `NOT NULL`). The admin re-sends what it loaded from the\n\t// server on autosave, so any field that's actually `null` in the DB\n\t// must round-trip cleanly through the validator. `.optional()` only\n\t// accepts `undefined`; `.nullish()` accepts both `undefined` and\n\t// `null`. (#867 — autosave failures on seeded entries.)\n\tif (!field.required) {\n\t\tschema = schema.nullish();\n\t}\n\n\t// Apply default value\n\tif (field.defaultValue !== undefined) {\n\t\tschema = schema.default(field.defaultValue);\n\t}\n\n\treturn schema;\n}\n\n/**\n * Get base Zod schema for a field type\n */\nfunction getBaseSchema(type: FieldType, field: Field): ZodTypeAny {\n\tswitch (type) {\n\t\tcase \"url\":\n\t\t\treturn z.string().url();\n\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\t\treturn z.string();\n\n\t\tcase \"number\":\n\t\t\treturn z.number();\n\n\t\tcase \"integer\":\n\t\t\treturn z.number().int();\n\n\t\tcase \"boolean\":\n\t\t\t// Boolean fields map to `INTEGER` columns (`FIELD_TYPE_TO_COLUMN`\n\t\t\t// in `schema/types.ts`) and `serializeValue` in\n\t\t\t// `database/repositories/content.ts` writes booleans as 0/1.\n\t\t\t// `deserializeValue` never converts them back, so reads return\n\t\t\t// numbers. Coerce the stored 0/1 shape here so a GET → POST\n\t\t\t// round-trip on a boolean field passes validation. Other inputs\n\t\t\t// (strings, other numbers) fall through to `z.boolean()` and\n\t\t\t// produce its standard rejection.\n\t\t\treturn z.preprocess((v) => (v === 0 || v === 1 ? Boolean(v) : v), z.boolean());\n\n\t\tcase \"datetime\":\n\t\t\treturn z.string().datetime().or(z.string().date());\n\n\t\tcase \"select\": {\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\tconst [first, ...rest] = options;\n\t\t\t\treturn z.enum([first, ...rest]);\n\t\t\t}\n\t\t\treturn z.string();\n\t\t}\n\n\t\tcase \"multiSelect\": {\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\tconst [first, ...rest] = multiOptions;\n\t\t\t\treturn z.array(z.enum([first, ...rest]));\n\t\t\t}\n\t\t\treturn z.array(z.string());\n\t\t}\n\n\t\tcase \"portableText\":\n\t\t\t// Portable Text is an array of blocks. We require `_type` because\n\t\t\t// renderers dispatch on it, but `_key` is intentionally optional:\n\t\t\t// it's a UI-layer concern that the editor regenerates on every\n\t\t\t// change (see `PortableTextEditor`), and the rest of this schema\n\t\t\t// uses `.passthrough()` for everything below the top level. Making\n\t\t\t// `_key` strictly required here was an accidentally tight invariant\n\t\t\t// that rejected any seed/import data not authored against the\n\t\t\t// editor (#867 — autosave failures on seeded template content).\n\t\t\treturn z.array(\n\t\t\t\tz\n\t\t\t\t\t.object({\n\t\t\t\t\t\t_type: z.string(),\n\t\t\t\t\t\t_key: z.string().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.passthrough(),\n\t\t\t);\n\n\t\tcase \"image\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\talt: z.string().optional(),\n\t\t\t\twidth: z.number().optional(),\n\t\t\t\theight: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"cloudflare-images\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Admin-side preview URL for external providers (not persisted by plugins) */\n\t\t\t\tpreviewUrl: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"file\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\tfilename: z.string().optional(),\n\t\t\t\tmimeType: z.string().optional(),\n\t\t\t\tsize: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"s3\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"reference\":\n\t\t\treturn z.string(); // Reference ID\n\n\t\tcase \"json\":\n\t\t\treturn z.unknown();\n\n\t\tdefault:\n\t\t\treturn z.unknown();\n\t}\n}\n\n/**\n * Apply validation rules to a schema\n */\nfunction applyValidation(schema: ZodTypeAny, field: Field): ZodTypeAny {\n\tconst validation = field.validation;\n\tif (!validation) return schema;\n\n\t// String validations\n\tif (schema instanceof z.ZodString) {\n\t\tlet strSchema = schema;\n\t\tif (validation.minLength !== undefined) {\n\t\t\tstrSchema = strSchema.min(validation.minLength);\n\t\t}\n\t\tif (validation.maxLength !== undefined) {\n\t\t\tstrSchema = strSchema.max(validation.maxLength);\n\t\t}\n\t\tif (validation.pattern) {\n\t\t\tstrSchema = strSchema.regex(new RegExp(validation.pattern));\n\t\t}\n\t\treturn strSchema;\n\t}\n\n\t// Number validations\n\tif (schema instanceof z.ZodNumber) {\n\t\tlet numSchema = schema;\n\t\tif (validation.min !== undefined) {\n\t\t\tnumSchema = numSchema.min(validation.min);\n\t\t}\n\t\tif (validation.max !== undefined) {\n\t\t\tnumSchema = numSchema.max(validation.max);\n\t\t}\n\t\treturn numSchema;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Schema cache to avoid regenerating schemas on every request\n */\nconst schemaCache = new Map<string, { schema: z.ZodObject<any>; version: string }>();\n\n/**\n * Get or generate a cached schema for a collection\n */\nexport function getCachedSchema(\n\tcollection: CollectionWithFields,\n\tversion?: string,\n): z.ZodObject<any> {\n\tconst cacheKey = collection.slug;\n\tconst cached = schemaCache.get(cacheKey);\n\n\t// If version matches, return cached schema\n\tif (cached && (!version || cached.version === version)) {\n\t\treturn cached.schema;\n\t}\n\n\t// Generate new schema\n\tconst schema = generateZodSchema(collection);\n\n\t// Cache it\n\tschemaCache.set(cacheKey, {\n\t\tschema,\n\t\tversion: version || collection.updatedAt,\n\t});\n\n\treturn schema;\n}\n\n/**\n * Invalidate cached schema for a collection\n */\nexport function invalidateSchemaCache(slug: string): void {\n\tschemaCache.delete(slug);\n}\n\n/**\n * Clear all cached schemas\n */\nexport function clearSchemaCache(): void {\n\tschemaCache.clear();\n}\n\n/**\n * Validate data against a collection's schema\n */\nexport function validateContent(\n\tcollection: CollectionWithFields,\n\tdata: unknown,\n): { success: true; data: unknown } | { success: false; errors: z.ZodError } {\n\tconst schema = getCachedSchema(collection);\n\n\tconst result = schema.safeParse(data);\n\n\tif (result.success) {\n\t\treturn { success: true, data: result.data };\n\t}\n\n\treturn { success: false, errors: result.error };\n}\n\n/**\n * Generate TypeScript interface from field definitions\n * Used by CLI `emdash types` to generate types\n */\nexport function generateTypeScript(collection: CollectionWithFields): string {\n\tconst interfaceName = getInterfaceName(collection);\n\tconst lines: string[] = [];\n\n\tlines.push(`export interface ${interfaceName} {`);\n\tlines.push(` id: string;`);\n\tlines.push(` slug: string | null;`);\n\tlines.push(` status: string;`);\n\n\tfor (const field of collection.fields) {\n\t\tconst tsType = fieldTypeToTypeScript(field);\n\t\tconst optional = field.required ? \"\" : \"?\";\n\t\tlines.push(` ${field.slug}${optional}: ${tsType};`);\n\t}\n\n\tlines.push(` createdAt: Date;`);\n\tlines.push(` updatedAt: Date;`);\n\tlines.push(` publishedAt: Date | null;`);\n\t// Bylines are eagerly loaded by getEmDashCollection/getEmDashEntry\n\tlines.push(` bylines?: ContentBylineCredit[];`);\n\t// Taxonomy terms are eagerly loaded by getEmDashCollection/getEmDashEntry,\n\t// keyed by taxonomy name (e.g. data.terms?.tag)\n\tlines.push(` terms?: Record<string, TaxonomyTerm[]>;`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate a complete types file with module augmentation\n * This produces emdash-env.d.ts content that provides typed query functions\n */\nexport function generateTypesFile(collections: CollectionWithFields[]): string {\n\tconst lines: string[] = [];\n\n\t// Header\n\tlines.push(`// Generated by EmDash on dev server start`);\n\tlines.push(`// Do not edit manually`);\n\tlines.push(``);\n\tlines.push(`/// <reference types=\"emdash/locals\" />`);\n\tlines.push(``);\n\n\t// Check if we need PortableTextBlock import\n\tconst needsPortableText = collections.some((c) =>\n\t\tc.fields.some((f) => f.type === \"portableText\"),\n\t);\n\n\t// Build imports - ContentBylineCredit and TaxonomyTerm are always needed\n\t// for the hydrated bylines/terms fields\n\tconst imports = [\"ContentBylineCredit\", \"TaxonomyTerm\"];\n\tif (needsPortableText) {\n\t\timports.push(\"PortableTextBlock\");\n\t}\n\tlines.push(`import type { ${imports.join(\", \")} } from \"emdash\";`);\n\tlines.push(``);\n\n\t// Generate individual interfaces\n\tfor (const collection of collections) {\n\t\tlines.push(generateTypeScript(collection));\n\t\tlines.push(``);\n\t}\n\n\t// Generate the Collections interface for module augmentation\n\tlines.push(`declare module \"emdash\" {`);\n\tlines.push(` interface EmDashCollections {`);\n\tfor (const collection of collections) {\n\t\tconst interfaceName = getInterfaceName(collection);\n\t\tlines.push(` ${collection.slug}: ${interfaceName};`);\n\t}\n\tlines.push(` }`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate schema hash for cache invalidation\n */\nexport async function generateSchemaHash(collections: CollectionWithFields[]): Promise<string> {\n\tconst str = JSON.stringify(\n\t\tcollections.map((c) => ({\n\t\t\tslug: c.slug,\n\t\t\tfields: c.fields.map((f) => ({\n\t\t\t\tslug: f.slug,\n\t\t\t\ttype: f.type,\n\t\t\t\trequired: f.required,\n\t\t\t\tvalidation: f.validation,\n\t\t\t})),\n\t\t})),\n\t);\n\treturn hashString(str);\n}\n\n/**\n * Map field type to TypeScript type\n */\nfunction fieldTypeToTypeScript(field: Field): string {\n\tswitch (field.type) {\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\tcase \"url\":\n\t\tcase \"datetime\":\n\t\t\treturn \"string\";\n\n\t\tcase \"number\":\n\t\tcase \"integer\":\n\t\t\treturn \"number\";\n\n\t\tcase \"boolean\":\n\t\t\treturn \"boolean\";\n\n\t\tcase \"select\":\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\treturn options.map((o) => `\"${o}\"`).join(\" | \");\n\t\t\t}\n\t\t\treturn \"string\";\n\n\t\tcase \"multiSelect\":\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\treturn `(${multiOptions.map((o) => `\"${o}\"`).join(\" | \")})[]`;\n\t\t\t}\n\t\t\treturn \"string[]\";\n\n\t\tcase \"portableText\":\n\t\t\treturn \"PortableTextBlock[]\";\n\n\t\tcase \"image\":\n\t\t\treturn \"{ id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"file\":\n\t\t\treturn \"{ id: string; src?: string; filename?: string; mimeType?: string; size?: number; provider?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"reference\":\n\t\t\t// Could be enhanced to include the referenced collection type\n\t\t\treturn \"string\";\n\n\t\tcase \"json\":\n\t\t\treturn \"unknown\";\n\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n}\n\n/**\n * Convert string to PascalCase (handles slugs, spaces, etc.)\n */\nfunction pascalCase(str: string): string {\n\treturn str\n\t\t.split(PASCAL_CASE_SPLIT_PATTERN)\n\t\t.filter(Boolean)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t.join(\"\");\n}\n\n/**\n * Simple singularization - handles common cases\n */\nfunction singularize(str: string): string {\n\tif (str.endsWith(\"ies\")) {\n\t\treturn str.slice(0, -3) + \"y\";\n\t}\n\tif (\n\t\tstr.endsWith(\"es\") &&\n\t\t(str.endsWith(\"sses\") || str.endsWith(\"xes\") || str.endsWith(\"ches\") || str.endsWith(\"shes\"))\n\t) {\n\t\treturn str.slice(0, -2);\n\t}\n\tif (str.endsWith(\"s\") && !str.endsWith(\"ss\")) {\n\t\treturn str.slice(0, -1);\n\t}\n\treturn str;\n}\n\n/**\n * Get the interface name for a collection\n */\nfunction getInterfaceName(collection: CollectionWithFields): string {\n\treturn pascalCase(collection.labelSingular || singularize(collection.slug));\n}\n"],"mappings":";;;;;;;;;;;;;AAMA,MAAM,4BAA4B;;;;;;;AAQlC,SAAgB,kBACf,YAC0C;CAC1C,MAAM,QAAoC,EAAE;AAE5C,MAAK,MAAM,SAAS,WAAW,OAC9B,OAAM,MAAM,QAAQ,oBAAoB,MAAM;AAG/C,QAAO,EAAE,OAAO,MAAM;;;;;AAMvB,SAAgB,oBAAoB,OAA0B;CAC7D,IAAI,SAAS,cAAc,MAAM,MAAM,MAAM;AAG7C,KAAI,MAAM,WACT,UAAS,gBAAgB,QAAQ,MAAM;AAWxC,KAAI,CAAC,MAAM,SACV,UAAS,OAAO,SAAS;AAI1B,KAAI,MAAM,iBAAiB,OAC1B,UAAS,OAAO,QAAQ,MAAM,aAAa;AAG5C,QAAO;;;;;AAMR,SAAS,cAAc,MAAiB,OAA0B;AACjE,SAAQ,MAAR;EACC,KAAK,MACJ,QAAO,EAAE,QAAQ,CAAC,KAAK;EAExB,KAAK;EACL,KAAK;EACL,KAAK,OACJ,QAAO,EAAE,QAAQ;EAElB,KAAK,SACJ,QAAO,EAAE,QAAQ;EAElB,KAAK,UACJ,QAAO,EAAE,QAAQ,CAAC,KAAK;EAExB,KAAK,UASJ,QAAO,EAAE,YAAY,MAAO,MAAM,KAAK,MAAM,IAAI,QAAQ,EAAE,GAAG,GAAI,EAAE,SAAS,CAAC;EAE/E,KAAK,WACJ,QAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC;EAEnD,KAAK,UAAU;GACd,MAAM,UAAU,MAAM,YAAY;AAClC,OAAI,WAAW,QAAQ,SAAS,GAAG;IAClC,MAAM,CAAC,OAAO,GAAG,QAAQ;AACzB,WAAO,EAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;;AAEhC,UAAO,EAAE,QAAQ;;EAGlB,KAAK,eAAe;GACnB,MAAM,eAAe,MAAM,YAAY;AACvC,OAAI,gBAAgB,aAAa,SAAS,GAAG;IAC5C,MAAM,CAAC,OAAO,GAAG,QAAQ;AACzB,WAAO,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC;;AAEzC,UAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;;EAG3B,KAAK,eASJ,QAAO,EAAE,MACR,EACE,OAAO;GACP,OAAO,EAAE,QAAQ;GACjB,MAAM,EAAE,QAAQ,CAAC,UAAU;GAC3B,CAAC,CACD,aAAa,CACf;EAEF,KAAK,QACJ,QAAO,EAAE,OAAO;GACf,IAAI,EAAE,QAAQ;GACd,KAAK,EAAE,QAAQ,CAAC,UAAU;GAC1B,KAAK,EAAE,QAAQ,CAAC,UAAU;GAC1B,OAAO,EAAE,QAAQ,CAAC,UAAU;GAC5B,QAAQ,EAAE,QAAQ,CAAC,UAAU;GAE7B,UAAU,EAAE,QAAQ,CAAC,UAAU;GAE/B,YAAY,EAAE,QAAQ,CAAC,UAAU;GAEjC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;GAClD,CAAC;EAEH,KAAK,OACJ,QAAO,EAAE,OAAO;GACf,IAAI,EAAE,QAAQ;GACd,KAAK,EAAE,QAAQ,CAAC,UAAU;GAC1B,UAAU,EAAE,QAAQ,CAAC,UAAU;GAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;GAC/B,MAAM,EAAE,QAAQ,CAAC,UAAU;GAE3B,UAAU,EAAE,QAAQ,CAAC,UAAU;GAE/B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU;GAClD,CAAC;EAEH,KAAK,YACJ,QAAO,EAAE,QAAQ;EAElB,KAAK,OACJ,QAAO,EAAE,SAAS;EAEnB,QACC,QAAO,EAAE,SAAS;;;;;;AAOrB,SAAS,gBAAgB,QAAoB,OAA0B;CACtE,MAAM,aAAa,MAAM;AACzB,KAAI,CAAC,WAAY,QAAO;AAGxB,KAAI,kBAAkB,EAAE,WAAW;EAClC,IAAI,YAAY;AAChB,MAAI,WAAW,cAAc,OAC5B,aAAY,UAAU,IAAI,WAAW,UAAU;AAEhD,MAAI,WAAW,cAAc,OAC5B,aAAY,UAAU,IAAI,WAAW,UAAU;AAEhD,MAAI,WAAW,QACd,aAAY,UAAU,MAAM,IAAI,OAAO,WAAW,QAAQ,CAAC;AAE5D,SAAO;;AAIR,KAAI,kBAAkB,EAAE,WAAW;EAClC,IAAI,YAAY;AAChB,MAAI,WAAW,QAAQ,OACtB,aAAY,UAAU,IAAI,WAAW,IAAI;AAE1C,MAAI,WAAW,QAAQ,OACtB,aAAY,UAAU,IAAI,WAAW,IAAI;AAE1C,SAAO;;AAGR,QAAO;;;;;;AAuER,SAAgB,mBAAmB,YAA0C;CAC5E,MAAM,gBAAgB,iBAAiB,WAAW;CAClD,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAK,oBAAoB,cAAc,IAAI;AACjD,OAAM,KAAK,gBAAgB;AAC3B,OAAM,KAAK,yBAAyB;AACpC,OAAM,KAAK,oBAAoB;AAE/B,MAAK,MAAM,SAAS,WAAW,QAAQ;EACtC,MAAM,SAAS,sBAAsB,MAAM;EAC3C,MAAM,WAAW,MAAM,WAAW,KAAK;AACvC,QAAM,KAAK,KAAK,MAAM,OAAO,SAAS,IAAI,OAAO,GAAG;;AAGrD,OAAM,KAAK,qBAAqB;AAChC,OAAM,KAAK,qBAAqB;AAChC,OAAM,KAAK,8BAA8B;AAEzC,OAAM,KAAK,qCAAqC;AAGhD,OAAM,KAAK,4CAA4C;AACvD,OAAM,KAAK,IAAI;AAEf,QAAO,MAAM,KAAK,KAAK;;;;;;AAOxB,SAAgB,kBAAkB,aAA6C;CAC9E,MAAM,QAAkB,EAAE;AAG1B,OAAM,KAAK,6CAA6C;AACxD,OAAM,KAAK,0BAA0B;AACrC,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,0CAA0C;AACrD,OAAM,KAAK,GAAG;CAGd,MAAM,oBAAoB,YAAY,MAAM,MAC3C,EAAE,OAAO,MAAM,MAAM,EAAE,SAAS,eAAe,CAC/C;CAID,MAAM,UAAU,CAAC,uBAAuB,eAAe;AACvD,KAAI,kBACH,SAAQ,KAAK,oBAAoB;AAElC,OAAM,KAAK,iBAAiB,QAAQ,KAAK,KAAK,CAAC,mBAAmB;AAClE,OAAM,KAAK,GAAG;AAGd,MAAK,MAAM,cAAc,aAAa;AACrC,QAAM,KAAK,mBAAmB,WAAW,CAAC;AAC1C,QAAM,KAAK,GAAG;;AAIf,OAAM,KAAK,4BAA4B;AACvC,OAAM,KAAK,kCAAkC;AAC7C,MAAK,MAAM,cAAc,aAAa;EACrC,MAAM,gBAAgB,iBAAiB,WAAW;AAClD,QAAM,KAAK,OAAO,WAAW,KAAK,IAAI,cAAc,GAAG;;AAExD,OAAM,KAAK,MAAM;AACjB,OAAM,KAAK,IAAI;AAEf,QAAO,MAAM,KAAK,KAAK;;;;;AAMxB,eAAsB,mBAAmB,aAAsD;AAY9F,QAAO,WAXK,KAAK,UAChB,YAAY,KAAK,OAAO;EACvB,MAAM,EAAE;EACR,QAAQ,EAAE,OAAO,KAAK,OAAO;GAC5B,MAAM,EAAE;GACR,MAAM,EAAE;GACR,UAAU,EAAE;GACZ,YAAY,EAAE;GACd,EAAE;EACH,EAAE,CACH,CACqB;;;;;AAMvB,SAAS,sBAAsB,OAAsB;AACpD,SAAQ,MAAM,MAAd;EACC,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,WACJ,QAAO;EAER,KAAK;EACL,KAAK,UACJ,QAAO;EAER,KAAK,UACJ,QAAO;EAER,KAAK;GACJ,MAAM,UAAU,MAAM,YAAY;AAClC,OAAI,WAAW,QAAQ,SAAS,EAC/B,QAAO,QAAQ,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,MAAM;AAEhD,UAAO;EAER,KAAK;GACJ,MAAM,eAAe,MAAM,YAAY;AACvC,OAAI,gBAAgB,aAAa,SAAS,EACzC,QAAO,IAAI,aAAa,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,MAAM,CAAC;AAE1D,UAAO;EAER,KAAK,eACJ,QAAO;EAER,KAAK,QACJ,QAAO;EAER,KAAK,OACJ,QAAO;EAER,KAAK,YAEJ,QAAO;EAER,KAAK,OACJ,QAAO;EAER,QACC,QAAO;;;;;;AAOV,SAAS,WAAW,KAAqB;AACxC,QAAO,IACL,MAAM,0BAA0B,CAChC,OAAO,QAAQ,CACf,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,aAAa,CAAC,CACzE,KAAK,GAAG;;;;;AAMX,SAAS,YAAY,KAAqB;AACzC,KAAI,IAAI,SAAS,MAAM,CACtB,QAAO,IAAI,MAAM,GAAG,GAAG,GAAG;AAE3B,KACC,IAAI,SAAS,KAAK,KACjB,IAAI,SAAS,OAAO,IAAI,IAAI,SAAS,MAAM,IAAI,IAAI,SAAS,OAAO,IAAI,IAAI,SAAS,OAAO,EAE5F,QAAO,IAAI,MAAM,GAAG,GAAG;AAExB,KAAI,IAAI,SAAS,IAAI,IAAI,CAAC,IAAI,SAAS,KAAK,CAC3C,QAAO,IAAI,MAAM,GAAG,GAAG;AAExB,QAAO;;;;;AAMR,SAAS,iBAAiB,YAA0C;AACnE,QAAO,WAAW,WAAW,iBAAiB,YAAY,WAAW,KAAK,CAAC"}
1
+ {"version":3,"file":"zod-generator-D481JjzD.mjs","names":[],"sources":["../src/schema/zod-generator.ts"],"sourcesContent":["import { z, type ZodTypeAny } from \"zod\";\n\nimport { hashString } from \"../utils/hash.js\";\nimport type { Field, FieldType, CollectionWithFields } from \"./types.js\";\n\n/** Pattern to split on underscores, hyphens, and spaces for PascalCase conversion */\nconst PASCAL_CASE_SPLIT_PATTERN = /[_\\-\\s]+/;\n\n/**\n * Generate a Zod schema from a collection's field definitions\n *\n * This allows runtime validation of content based on dynamically\n * defined schemas stored in D1.\n */\nexport function generateZodSchema(\n\tcollection: CollectionWithFields,\n): z.ZodObject<Record<string, ZodTypeAny>> {\n\tconst shape: Record<string, ZodTypeAny> = {};\n\n\tfor (const field of collection.fields) {\n\t\tshape[field.slug] = generateFieldSchema(field);\n\t}\n\n\treturn z.object(shape);\n}\n\n/**\n * Generate Zod schema for a single field\n */\nexport function generateFieldSchema(field: Field): ZodTypeAny {\n\tlet schema = getBaseSchema(field.type, field);\n\n\t// Apply validation rules\n\tif (field.validation) {\n\t\tschema = applyValidation(schema, field);\n\t}\n\n\t// Apply required/optional. Non-required fields use `.nullish()` rather\n\t// than `.optional()` because the underlying SQLite columns are nullable\n\t// (see `SchemaRegistry.addFieldColumn` -- non-required fields are added\n\t// without `NOT NULL`). The admin re-sends what it loaded from the\n\t// server on autosave, so any field that's actually `null` in the DB\n\t// must round-trip cleanly through the validator. `.optional()` only\n\t// accepts `undefined`; `.nullish()` accepts both `undefined` and\n\t// `null`. (#867 — autosave failures on seeded entries.)\n\tif (!field.required) {\n\t\tschema = schema.nullish();\n\t}\n\n\t// Apply default value\n\tif (field.defaultValue !== undefined) {\n\t\tschema = schema.default(field.defaultValue);\n\t}\n\n\treturn schema;\n}\n\n/**\n * Get base Zod schema for a field type\n */\nfunction getBaseSchema(type: FieldType, field: Field): ZodTypeAny {\n\tswitch (type) {\n\t\tcase \"url\":\n\t\t\treturn z.string().url();\n\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\t\treturn z.string();\n\n\t\tcase \"number\":\n\t\t\treturn z.number();\n\n\t\tcase \"integer\":\n\t\t\treturn z.number().int();\n\n\t\tcase \"boolean\":\n\t\t\t// Boolean fields map to `INTEGER` columns (`FIELD_TYPE_TO_COLUMN`\n\t\t\t// in `schema/types.ts`) and `serializeValue` in\n\t\t\t// `database/repositories/content.ts` writes booleans as 0/1.\n\t\t\t// `deserializeValue` never converts them back, so reads return\n\t\t\t// numbers. Coerce the stored 0/1 shape here so a GET → POST\n\t\t\t// round-trip on a boolean field passes validation. Other inputs\n\t\t\t// (strings, other numbers) fall through to `z.boolean()` and\n\t\t\t// produce its standard rejection.\n\t\t\treturn z.preprocess((v) => (v === 0 || v === 1 ? Boolean(v) : v), z.boolean());\n\n\t\tcase \"datetime\":\n\t\t\treturn z.string().datetime().or(z.string().date());\n\n\t\tcase \"select\": {\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\tconst [first, ...rest] = options;\n\t\t\t\treturn z.enum([first, ...rest]);\n\t\t\t}\n\t\t\treturn z.string();\n\t\t}\n\n\t\tcase \"multiSelect\": {\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\tconst [first, ...rest] = multiOptions;\n\t\t\t\treturn z.array(z.enum([first, ...rest]));\n\t\t\t}\n\t\t\treturn z.array(z.string());\n\t\t}\n\n\t\tcase \"portableText\":\n\t\t\t// Portable Text is an array of blocks. We require `_type` because\n\t\t\t// renderers dispatch on it, but `_key` is intentionally optional:\n\t\t\t// it's a UI-layer concern that the editor regenerates on every\n\t\t\t// change (see `PortableTextEditor`), and the rest of this schema\n\t\t\t// uses `.passthrough()` for everything below the top level. Making\n\t\t\t// `_key` strictly required here was an accidentally tight invariant\n\t\t\t// that rejected any seed/import data not authored against the\n\t\t\t// editor (#867 — autosave failures on seeded template content).\n\t\t\treturn z.array(\n\t\t\t\tz\n\t\t\t\t\t.object({\n\t\t\t\t\t\t_type: z.string(),\n\t\t\t\t\t\t_key: z.string().optional(),\n\t\t\t\t\t})\n\t\t\t\t\t.passthrough(),\n\t\t\t);\n\n\t\tcase \"image\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\talt: z.string().optional(),\n\t\t\t\twidth: z.number().optional(),\n\t\t\t\theight: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"cloudflare-images\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Admin-side preview URL for external providers (not persisted by plugins) */\n\t\t\t\tpreviewUrl: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"file\":\n\t\t\treturn z.object({\n\t\t\t\tid: z.string(),\n\t\t\t\tsrc: z.string().optional(),\n\t\t\t\tfilename: z.string().optional(),\n\t\t\t\tmimeType: z.string().optional(),\n\t\t\t\tsize: z.number().optional(),\n\t\t\t\t/** Provider ID (e.g. \"local\", \"s3\") */\n\t\t\t\tprovider: z.string().optional(),\n\t\t\t\t/** Provider-specific metadata; for local media this carries storageKey */\n\t\t\t\tmeta: z.record(z.string(), z.unknown()).optional(),\n\t\t\t});\n\n\t\tcase \"reference\":\n\t\t\treturn z.string(); // Reference ID\n\n\t\tcase \"json\":\n\t\t\treturn z.unknown();\n\n\t\tdefault:\n\t\t\treturn z.unknown();\n\t}\n}\n\n/**\n * Apply validation rules to a schema\n */\nfunction applyValidation(schema: ZodTypeAny, field: Field): ZodTypeAny {\n\tconst validation = field.validation;\n\tif (!validation) return schema;\n\n\t// String validations\n\tif (schema instanceof z.ZodString) {\n\t\tlet strSchema = schema;\n\t\tif (validation.minLength !== undefined) {\n\t\t\tstrSchema = strSchema.min(validation.minLength);\n\t\t}\n\t\tif (validation.maxLength !== undefined) {\n\t\t\tstrSchema = strSchema.max(validation.maxLength);\n\t\t}\n\t\tif (validation.pattern) {\n\t\t\tstrSchema = strSchema.regex(new RegExp(validation.pattern));\n\t\t}\n\t\treturn strSchema;\n\t}\n\n\t// Number validations\n\tif (schema instanceof z.ZodNumber) {\n\t\tlet numSchema = schema;\n\t\tif (validation.min !== undefined) {\n\t\t\tnumSchema = numSchema.min(validation.min);\n\t\t}\n\t\tif (validation.max !== undefined) {\n\t\t\tnumSchema = numSchema.max(validation.max);\n\t\t}\n\t\treturn numSchema;\n\t}\n\n\treturn schema;\n}\n\n/**\n * Schema cache to avoid regenerating schemas on every request\n */\nconst schemaCache = new Map<string, { schema: z.ZodObject<any>; version: string }>();\n\n/**\n * Get or generate a cached schema for a collection\n */\nexport function getCachedSchema(\n\tcollection: CollectionWithFields,\n\tversion?: string,\n): z.ZodObject<any> {\n\tconst cacheKey = collection.slug;\n\tconst cached = schemaCache.get(cacheKey);\n\n\t// If version matches, return cached schema\n\tif (cached && (!version || cached.version === version)) {\n\t\treturn cached.schema;\n\t}\n\n\t// Generate new schema\n\tconst schema = generateZodSchema(collection);\n\n\t// Cache it\n\tschemaCache.set(cacheKey, {\n\t\tschema,\n\t\tversion: version || collection.updatedAt,\n\t});\n\n\treturn schema;\n}\n\n/**\n * Invalidate cached schema for a collection\n */\nexport function invalidateSchemaCache(slug: string): void {\n\tschemaCache.delete(slug);\n}\n\n/**\n * Clear all cached schemas\n */\nexport function clearSchemaCache(): void {\n\tschemaCache.clear();\n}\n\n/**\n * Validate data against a collection's schema\n */\nexport function validateContent(\n\tcollection: CollectionWithFields,\n\tdata: unknown,\n): { success: true; data: unknown } | { success: false; errors: z.ZodError } {\n\tconst schema = getCachedSchema(collection);\n\n\tconst result = schema.safeParse(data);\n\n\tif (result.success) {\n\t\treturn { success: true, data: result.data };\n\t}\n\n\treturn { success: false, errors: result.error };\n}\n\n/**\n * Generate TypeScript interface from field definitions\n * Used by CLI `emdash types` to generate types\n */\nexport function generateTypeScript(collection: CollectionWithFields): string {\n\tconst interfaceName = getInterfaceName(collection);\n\tconst lines: string[] = [];\n\n\tlines.push(`export interface ${interfaceName} {`);\n\tlines.push(` id: string;`);\n\tlines.push(` slug: string | null;`);\n\tlines.push(` status: string;`);\n\n\tfor (const field of collection.fields) {\n\t\tconst tsType = fieldTypeToTypeScript(field);\n\t\tconst optional = field.required ? \"\" : \"?\";\n\t\tlines.push(` ${field.slug}${optional}: ${tsType};`);\n\t}\n\n\tlines.push(` createdAt: Date;`);\n\tlines.push(` updatedAt: Date;`);\n\tlines.push(` publishedAt: Date | null;`);\n\t// Bylines are eagerly loaded by getEmDashCollection/getEmDashEntry\n\tlines.push(` bylines?: ContentBylineCredit[];`);\n\t// Taxonomy terms are eagerly loaded by getEmDashCollection/getEmDashEntry,\n\t// keyed by taxonomy name (e.g. data.terms?.tag)\n\tlines.push(` terms?: Record<string, TaxonomyTerm[]>;`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate a complete types file with module augmentation\n * This produces emdash-env.d.ts content that provides typed query functions\n */\nexport function generateTypesFile(collections: CollectionWithFields[]): string {\n\tconst lines: string[] = [];\n\n\t// Header\n\tlines.push(`// Generated by EmDash on dev server start`);\n\tlines.push(`// Do not edit manually`);\n\tlines.push(``);\n\tlines.push(`/// <reference types=\"emdash/locals\" />`);\n\tlines.push(``);\n\n\t// Check if we need PortableTextBlock import\n\tconst needsPortableText = collections.some((c) =>\n\t\tc.fields.some((f) => f.type === \"portableText\"),\n\t);\n\n\t// Build imports - ContentBylineCredit and TaxonomyTerm are always needed\n\t// for the hydrated bylines/terms fields\n\tconst imports = [\"ContentBylineCredit\", \"TaxonomyTerm\"];\n\tif (needsPortableText) {\n\t\timports.push(\"PortableTextBlock\");\n\t}\n\tlines.push(`import type { ${imports.join(\", \")} } from \"emdash\";`);\n\tlines.push(``);\n\n\t// Generate individual interfaces\n\tfor (const collection of collections) {\n\t\tlines.push(generateTypeScript(collection));\n\t\tlines.push(``);\n\t}\n\n\t// Generate the Collections interface for module augmentation\n\tlines.push(`declare module \"emdash\" {`);\n\tlines.push(` interface EmDashCollections {`);\n\tfor (const collection of collections) {\n\t\tconst interfaceName = getInterfaceName(collection);\n\t\tlines.push(` ${collection.slug}: ${interfaceName};`);\n\t}\n\tlines.push(` }`);\n\tlines.push(`}`);\n\n\treturn lines.join(\"\\n\");\n}\n\n/**\n * Generate schema hash for cache invalidation\n */\nexport async function generateSchemaHash(collections: CollectionWithFields[]): Promise<string> {\n\tconst str = JSON.stringify(\n\t\tcollections.map((c) => ({\n\t\t\tslug: c.slug,\n\t\t\tfields: c.fields.map((f) => ({\n\t\t\t\tslug: f.slug,\n\t\t\t\ttype: f.type,\n\t\t\t\trequired: f.required,\n\t\t\t\tvalidation: f.validation,\n\t\t\t})),\n\t\t})),\n\t);\n\treturn hashString(str);\n}\n\n/**\n * Map field type to TypeScript type\n */\nfunction fieldTypeToTypeScript(field: Field): string {\n\tswitch (field.type) {\n\t\tcase \"string\":\n\t\tcase \"text\":\n\t\tcase \"slug\":\n\t\tcase \"url\":\n\t\tcase \"datetime\":\n\t\t\treturn \"string\";\n\n\t\tcase \"number\":\n\t\tcase \"integer\":\n\t\t\treturn \"number\";\n\n\t\tcase \"boolean\":\n\t\t\treturn \"boolean\";\n\n\t\tcase \"select\":\n\t\t\tconst options = field.validation?.options;\n\t\t\tif (options && options.length > 0) {\n\t\t\t\treturn options.map((o) => `\"${o}\"`).join(\" | \");\n\t\t\t}\n\t\t\treturn \"string\";\n\n\t\tcase \"multiSelect\":\n\t\t\tconst multiOptions = field.validation?.options;\n\t\t\tif (multiOptions && multiOptions.length > 0) {\n\t\t\t\treturn `(${multiOptions.map((o) => `\"${o}\"`).join(\" | \")})[]`;\n\t\t\t}\n\t\t\treturn \"string[]\";\n\n\t\tcase \"portableText\":\n\t\t\treturn \"PortableTextBlock[]\";\n\n\t\tcase \"image\":\n\t\t\treturn \"{ id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"file\":\n\t\t\treturn \"{ id: string; src?: string; filename?: string; mimeType?: string; size?: number; provider?: string; meta?: Record<string, unknown> }\";\n\n\t\tcase \"reference\":\n\t\t\t// Could be enhanced to include the referenced collection type\n\t\t\treturn \"string\";\n\n\t\tcase \"json\":\n\t\t\treturn \"unknown\";\n\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n}\n\n/**\n * Convert string to PascalCase (handles slugs, spaces, etc.)\n */\nfunction pascalCase(str: string): string {\n\treturn str\n\t\t.split(PASCAL_CASE_SPLIT_PATTERN)\n\t\t.filter(Boolean)\n\t\t.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n\t\t.join(\"\");\n}\n\n/**\n * Simple singularization - handles common cases\n */\nfunction singularize(str: string): string {\n\tif (str.endsWith(\"ies\")) {\n\t\treturn str.slice(0, -3) + \"y\";\n\t}\n\tif (\n\t\tstr.endsWith(\"es\") &&\n\t\t(str.endsWith(\"sses\") || str.endsWith(\"xes\") || str.endsWith(\"ches\") || str.endsWith(\"shes\"))\n\t) {\n\t\treturn str.slice(0, -2);\n\t}\n\tif (str.endsWith(\"s\") && !str.endsWith(\"ss\")) {\n\t\treturn str.slice(0, -1);\n\t}\n\treturn str;\n}\n\n/**\n * Get the interface name for a collection\n */\nfunction getInterfaceName(collection: CollectionWithFields): string {\n\treturn pascalCase(collection.labelSingular || singularize(collection.slug));\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emdash",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "Astro-native CMS with WordPress migration support",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -98,6 +98,10 @@
98
98
  "types": "./dist/storage/s3.d.mts",
99
99
  "default": "./dist/storage/s3.mjs"
100
100
  },
101
+ "./object-cache/memory": {
102
+ "types": "./dist/object-cache/memory.d.mts",
103
+ "default": "./dist/object-cache/memory.mjs"
104
+ },
101
105
  "./media": {
102
106
  "types": "./dist/media/index.d.mts",
103
107
  "default": "./dist/media/index.mjs"
@@ -106,6 +110,14 @@
106
110
  "types": "./dist/media/local-runtime.d.mts",
107
111
  "default": "./dist/media/local-runtime.mjs"
108
112
  },
113
+ "./media/image-endpoint": {
114
+ "types": "./dist/media/image-endpoint.d.mts",
115
+ "default": "./dist/media/image-endpoint.mjs"
116
+ },
117
+ "./image-endpoint": {
118
+ "types": "./dist/astro/image-endpoint.d.mts",
119
+ "default": "./dist/astro/image-endpoint.mjs"
120
+ },
109
121
  "./runtime": {
110
122
  "types": "./dist/runtime.d.mts",
111
123
  "default": "./dist/runtime.mjs"
@@ -218,11 +230,11 @@
218
230
  "upng-js": "^2.1.0",
219
231
  "zod": "^4.4.1",
220
232
  "@atcute/client": "^5.0.0",
221
- "@emdash-cms/admin": "0.20.0",
222
- "@emdash-cms/auth": "0.20.0",
223
- "@emdash-cms/gutenberg-to-portable-text": "0.20.0",
233
+ "@emdash-cms/auth": "0.22.0",
234
+ "@emdash-cms/gutenberg-to-portable-text": "0.22.0",
224
235
  "@emdash-cms/plugin-types": "0.1.0",
225
- "@emdash-cms/registry-client": "0.3.2"
236
+ "@emdash-cms/registry-client": "0.3.2",
237
+ "@emdash-cms/admin": "0.22.0"
226
238
  },
227
239
  "optionalDependencies": {
228
240
  "@libsql/kysely-libsql": "^0.4.0",
@@ -255,7 +267,7 @@
255
267
  "vite": "^6.0.0",
256
268
  "vitest": "^4.1.5",
257
269
  "zod-openapi": "^5.4.6",
258
- "@emdash-cms/blocks": "0.20.0"
270
+ "@emdash-cms/blocks": "0.22.0"
259
271
  },
260
272
  "repository": {
261
273
  "type": "git",
@@ -172,6 +172,25 @@ export async function handleApiTokenRevoke(
172
172
  }
173
173
  }
174
174
 
175
+ /**
176
+ * Delete every token with the given name for a user, returning how many were
177
+ * removed. Token names aren't unique, so this is a bulk delete — used by the
178
+ * dev-bypass flow to keep a single `dev-bypass-token` instead of minting a new
179
+ * one on every reset.
180
+ */
181
+ export async function deleteApiTokensByName(
182
+ db: Kysely<Database>,
183
+ userId: string,
184
+ name: string,
185
+ ): Promise<number> {
186
+ const result = await db
187
+ .deleteFrom("_emdash_api_tokens")
188
+ .where("user_id", "=", userId)
189
+ .where("name", "=", name)
190
+ .executeTakeFirst();
191
+ return Number(result.numDeletedRows ?? 0n);
192
+ }
193
+
175
194
  /**
176
195
  * Resolve a raw API token (ec_pat_...) to a user ID and scopes.
177
196
  * Updates last_used_at on successful lookup.
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Comment reaction handlers (Tier 1 of the best-in-class comments RFC).
3
+ *
4
+ * Business logic for toggling reactions and reading aggregate counts. Route
5
+ * files stay thin wrappers; these return `ApiResult<T>`.
6
+ */
7
+
8
+ import type { Kysely } from "kysely";
9
+
10
+ import {
11
+ CommentReactionRepository,
12
+ type ReactionCounts,
13
+ } from "#db/repositories/comment-reaction.js";
14
+ import type { Database } from "#db/types.js";
15
+
16
+ import { invalidateCommentObjectCache } from "../../object-cache/index.js";
17
+ import type { ApiResult } from "../types.js";
18
+
19
+ /** Max reactions a single voter may register per window before throttling. */
20
+ const REACTION_RATE_LIMIT = 30;
21
+ const REACTION_RATE_WINDOW_MINUTES = 10;
22
+
23
+ /**
24
+ * Reactions the system accepts. Positive-only for now (matches the shipped
25
+ * widget); kept as an allowlist so a voter can't spam arbitrary reaction
26
+ * strings and bloat a comment's count map. Extend (or make configurable) as
27
+ * the UI grows.
28
+ */
29
+ const ALLOWED_REACTIONS: ReadonlySet<string> = new Set(["like"]);
30
+
31
+ export interface ReactionToggleResult {
32
+ commentId: string;
33
+ reaction: string;
34
+ /** true if the reaction was added, false if an existing one was removed */
35
+ reacted: boolean;
36
+ /** updated counts for this comment after the toggle */
37
+ counts: ReactionCounts;
38
+ }
39
+
40
+ export interface ReactionCountsResult {
41
+ /** comment id -> reaction counts */
42
+ reactions: Record<string, ReactionCounts>;
43
+ /** comment id -> reactions the current voter has active (omitted if anonymous) */
44
+ viewer?: Record<string, string[]>;
45
+ }
46
+
47
+ /**
48
+ * Toggle a reaction for a voter on an approved comment belonging to the given
49
+ * content item. Rate-limited per voter.
50
+ */
51
+ export async function handleReactionToggle(
52
+ db: Kysely<Database>,
53
+ params: {
54
+ collection: string;
55
+ contentId: string;
56
+ commentId: string;
57
+ reaction: string;
58
+ voterHash: string;
59
+ },
60
+ ): Promise<ApiResult<ReactionToggleResult>> {
61
+ try {
62
+ const { collection, contentId, commentId, reaction, voterHash } = params;
63
+
64
+ if (!ALLOWED_REACTIONS.has(reaction)) {
65
+ return {
66
+ success: false,
67
+ error: { code: "VALIDATION_ERROR", message: "Unsupported reaction" },
68
+ };
69
+ }
70
+
71
+ // The comment must exist, be approved, and belong to this content item.
72
+ const comment = await db
73
+ .selectFrom("_emdash_comments")
74
+ .select(["id", "status"])
75
+ .where("id", "=", commentId)
76
+ .where("collection", "=", collection)
77
+ .where("content_id", "=", contentId)
78
+ .executeTakeFirst();
79
+
80
+ if (!comment) {
81
+ return { success: false, error: { code: "NOT_FOUND", message: "Comment not found" } };
82
+ }
83
+ if (comment.status !== "approved") {
84
+ return {
85
+ success: false,
86
+ error: { code: "COMMENT_NOT_APPROVED", message: "Cannot react to this comment" },
87
+ };
88
+ }
89
+
90
+ const repo = new CommentReactionRepository(db);
91
+
92
+ const recent = await repo.countRecentByVoter(voterHash, REACTION_RATE_WINDOW_MINUTES);
93
+ if (recent >= REACTION_RATE_LIMIT) {
94
+ return {
95
+ success: false,
96
+ error: { code: "RATE_LIMITED", message: "Too many reactions. Please try again later." },
97
+ };
98
+ }
99
+
100
+ const { reacted } = await repo.toggle({ commentId, reaction, voterHash });
101
+ const countsMap = await repo.countsForComments([commentId]);
102
+
103
+ // Reaction counts (and `best` ordering) are folded into cached getComments
104
+ // reads, so a toggle must orphan them.
105
+ invalidateCommentObjectCache();
106
+
107
+ return {
108
+ success: true,
109
+ data: { commentId, reaction, reacted, counts: countsMap.get(commentId) ?? {} },
110
+ };
111
+ } catch {
112
+ return {
113
+ success: false,
114
+ error: { code: "REACTION_TOGGLE_ERROR", message: "Failed to toggle reaction" },
115
+ };
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Read aggregate reaction counts for every approved comment on a content item,
121
+ * plus (optionally) which reactions the current voter has active.
122
+ */
123
+ export async function handleReactionCounts(
124
+ db: Kysely<Database>,
125
+ collection: string,
126
+ contentId: string,
127
+ voterHash?: string,
128
+ ): Promise<ApiResult<ReactionCountsResult>> {
129
+ try {
130
+ const comments = await db
131
+ .selectFrom("_emdash_comments")
132
+ .select("id")
133
+ .where("collection", "=", collection)
134
+ .where("content_id", "=", contentId)
135
+ .where("status", "=", "approved")
136
+ .execute();
137
+
138
+ const ids = comments.map((c) => c.id);
139
+ const repo = new CommentReactionRepository(db);
140
+
141
+ const countsMap = await repo.countsForComments(ids);
142
+ const reactions: Record<string, ReactionCounts> = {};
143
+ for (const [id, counts] of countsMap) {
144
+ reactions[id] = counts;
145
+ }
146
+
147
+ const data: ReactionCountsResult = { reactions };
148
+
149
+ if (voterHash) {
150
+ const viewerMap = await repo.viewerReactions(ids, voterHash);
151
+ const viewer: Record<string, string[]> = {};
152
+ for (const [id, list] of viewerMap) {
153
+ viewer[id] = list;
154
+ }
155
+ data.viewer = viewer;
156
+ }
157
+
158
+ return { success: true, data };
159
+ } catch {
160
+ return {
161
+ success: false,
162
+ error: { code: "REACTION_COUNTS_ERROR", message: "Failed to read reactions" },
163
+ };
164
+ }
165
+ }
@@ -16,6 +16,7 @@ import type { FindManyResult } from "../../database/repositories/types.js";
16
16
  import type { Database } from "../../database/types.js";
17
17
  import { wouldCreateLoop, detectLoops, type RedirectEdge } from "../../redirects/loops.js";
18
18
  import { validatePattern, validateDestinationParams, isPattern } from "../../redirects/patterns.js";
19
+ import { isTerminalStatus } from "../../redirects/status.js";
19
20
  import type { ApiResult } from "../types.js";
20
21
 
21
22
  // ---------------------------------------------------------------------------
@@ -70,7 +71,7 @@ export async function handleRedirectCreate(
70
71
  db: Kysely<Database>,
71
72
  input: {
72
73
  source: string;
73
- destination: string;
74
+ destination?: string;
74
75
  type?: number;
75
76
  enabled?: boolean;
76
77
  groupName?: string | null;
@@ -79,8 +80,14 @@ export async function handleRedirectCreate(
79
80
  try {
80
81
  const repo = new RedirectRepository(db);
81
82
 
83
+ const type = input.type ?? 301;
84
+ // Terminal statuses (410 Gone / 451) are served directly and have no
85
+ // destination — skip the destination/loop checks for them.
86
+ const terminal = isTerminalStatus(type);
87
+ const destination = terminal ? "" : (input.destination ?? "");
88
+
82
89
  // Source and destination must differ
83
- if (input.source === input.destination) {
90
+ if (!terminal && input.source === destination) {
84
91
  return {
85
92
  success: false,
86
93
  error: {
@@ -102,12 +109,15 @@ export async function handleRedirectCreate(
102
109
  }
103
110
 
104
111
  // Validate destination params reference valid source params
105
- const destError = validateDestinationParams(input.source, input.destination);
106
- if (destError) {
107
- return {
108
- success: false,
109
- error: { code: "VALIDATION_ERROR", message: destError },
110
- };
112
+ // (terminal rules have no destination to interpolate)
113
+ if (!terminal) {
114
+ const destError = validateDestinationParams(input.source, destination);
115
+ if (destError) {
116
+ return {
117
+ success: false,
118
+ error: { code: "VALIDATION_ERROR", message: destError },
119
+ };
120
+ }
111
121
  }
112
122
  }
113
123
 
@@ -123,17 +133,18 @@ export async function handleRedirectCreate(
123
133
  };
124
134
  }
125
135
 
126
- // Check for redirect loops (skip if creating as disabled)
127
- if (input.enabled !== false) {
136
+ // Check for redirect loops (skip if creating as disabled, or terminal —
137
+ // a Gone rule has no destination, so it can't form a loop)
138
+ if (!terminal && input.enabled !== false) {
128
139
  const edges = toEdges(await repo.findAllEnabled());
129
- const loopPath = wouldCreateLoop(input.source, input.destination, edges);
140
+ const loopPath = wouldCreateLoop(input.source, destination, edges);
130
141
  if (loopPath) return loopError(loopPath);
131
142
  }
132
143
 
133
144
  const redirect = await repo.create({
134
145
  source: input.source,
135
- destination: input.destination,
136
- type: input.type ?? 301,
146
+ destination,
147
+ type,
137
148
  isPattern: sourceIsPattern,
138
149
  enabled: input.enabled ?? true,
139
150
  groupName: input.groupName ?? null,
@@ -5,6 +5,7 @@
5
5
  import type { Kysely } from "kysely";
6
6
 
7
7
  import type { Database } from "../../database/types.js";
8
+ import { invalidateCollectionCache } from "../../object-cache/index.js";
8
9
  import {
9
10
  SchemaRegistry,
10
11
  SchemaError,
@@ -315,6 +316,9 @@ export async function handleSchemaFieldCreate(
315
316
  const registry = new SchemaRegistry(db);
316
317
  const item = await registry.createField(collectionSlug, input);
317
318
 
319
+ // Content snapshots embed field values; a column change invalidates them.
320
+ invalidateCollectionCache(collectionSlug);
321
+
318
322
  return {
319
323
  success: true,
320
324
  data: { item },
@@ -353,6 +357,8 @@ export async function handleSchemaFieldUpdate(
353
357
  const registry = new SchemaRegistry(db);
354
358
  const item = await registry.updateField(collectionSlug, fieldSlug, input);
355
359
 
360
+ invalidateCollectionCache(collectionSlug);
361
+
356
362
  return {
357
363
  success: true,
358
364
  data: { item },
@@ -390,6 +396,8 @@ export async function handleSchemaFieldDelete(
390
396
  const registry = new SchemaRegistry(db);
391
397
  await registry.deleteField(collectionSlug, fieldSlug);
392
398
 
399
+ invalidateCollectionCache(collectionSlug);
400
+
393
401
  return {
394
402
  success: true,
395
403
  data: { success: true },
@@ -15,6 +15,16 @@ export const createCommentBody = z
15
15
  })
16
16
  .meta({ id: "CreateCommentBody" });
17
17
 
18
+ export const createReactionBody = z
19
+ .object({
20
+ commentId: z.string().min(1),
21
+ /** Reaction name. Positive-only ("like") by default; extensible. */
22
+ reaction: z.string().min(1).max(20).default("like"),
23
+ /** Honeypot field — hidden in the form, filled only by bots */
24
+ website_url: z.string().optional(),
25
+ })
26
+ .meta({ id: "CreateReactionBody" });
27
+
18
28
  export const commentStatusBody = z
19
29
  .object({
20
30
  status: z.enum(["approved", "pending", "spam", "trash"]),
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
 
3
+ import { isTerminalStatus, REDIRECT_RULE_STATUSES } from "../../redirects/status.js";
3
4
  import { cursorPaginationQuery } from "./common.js";
4
5
 
5
6
  // ---------------------------------------------------------------------------
@@ -9,8 +10,8 @@ import { cursorPaginationQuery } from "./common.js";
9
10
  const redirectType = z.coerce
10
11
  .number()
11
12
  .int()
12
- .refine((n) => [301, 302, 307, 308].includes(n), {
13
- message: "Redirect type must be 301, 302, 307, or 308",
13
+ .refine((n) => REDIRECT_RULE_STATUSES.includes(n), {
14
+ message: "Redirect type must be 301, 302, 307, 308, 410, or 451",
14
15
  });
15
16
 
16
17
  /** Matches CR or LF characters */
@@ -40,17 +41,23 @@ const urlPath = z
40
41
  export const createRedirectBody = z
41
42
  .object({
42
43
  source: urlPath,
43
- destination: urlPath,
44
+ // Optional: terminal statuses (410/451) have no destination. Redirect
45
+ // statuses require one — enforced by the refine below.
46
+ destination: z.union([urlPath, z.literal("")]).optional(),
44
47
  type: redirectType.optional().default(301),
45
48
  enabled: z.boolean().optional().default(true),
46
49
  groupName: z.string().nullish(),
47
50
  })
51
+ .refine((o) => isTerminalStatus(o.type ?? 301) || !!o.destination, {
52
+ message: "destination is required for redirect types (301, 302, 307, 308)",
53
+ path: ["destination"],
54
+ })
48
55
  .meta({ id: "CreateRedirectBody" });
49
56
 
50
57
  export const updateRedirectBody = z
51
58
  .object({
52
59
  source: urlPath.optional(),
53
- destination: urlPath.optional(),
60
+ destination: z.union([urlPath, z.literal("")]).optional(),
54
61
  type: redirectType.optional(),
55
62
  enabled: z.boolean().optional(),
56
63
  groupName: z.string().nullish(),
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Node image endpoint -- the `image.endpoint` EmDash installs on non-Cloudflare
3
+ * platforms whose image service is local (sharp).
4
+ *
5
+ * It wraps Astro's generic endpoint: for an EmDash media URL it loads the source
6
+ * bytes straight from the storage adapter (no HTTP, so it works behind any auth
7
+ * gate) and runs the configured image service's `transform`; every other image
8
+ * is delegated to the stock endpoint unchanged.
9
+ */
10
+
11
+ import type { APIRoute } from "astro";
12
+ // @ts-ignore - astro/assets internal endpoint, resolved by the consumer's Astro build
13
+ import { GET as genericGET } from "astro/assets/endpoint/generic";
14
+ // @ts-ignore - astro:assets is resolved by the consumer's Astro build
15
+ import { getConfiguredImageService, imageConfig } from "astro:assets";
16
+
17
+ import {
18
+ IMMUTABLE_IMAGE_CACHE,
19
+ matchInternalMediaKey,
20
+ originalMediaHeaders,
21
+ } from "../media/image-endpoint.js";
22
+
23
+ export const prerender = false;
24
+
25
+ const FORMAT_MIME: Record<string, string> = {
26
+ webp: "image/webp",
27
+ avif: "image/avif",
28
+ png: "image/png",
29
+ jpeg: "image/jpeg",
30
+ jpg: "image/jpeg",
31
+ gif: "image/gif",
32
+ };
33
+
34
+ function isNotFound(error: unknown): boolean {
35
+ return (
36
+ error instanceof Error &&
37
+ (error.message.includes("not found") || error.message.includes("NOT_FOUND"))
38
+ );
39
+ }
40
+
41
+ function streamOriginal(body: ReadableStream<Uint8Array>, contentType: string): Response {
42
+ return new Response(body, { status: 200, headers: originalMediaHeaders(contentType) });
43
+ }
44
+
45
+ export const GET: APIRoute = async (ctx) => {
46
+ const url = new URL(ctx.request.url);
47
+ const key = matchInternalMediaKey(url.searchParams.get("href"));
48
+ const storage = ctx.locals.emdash?.storage;
49
+
50
+ // Not EmDash media, or storage unavailable: let the stock endpoint handle it
51
+ // (bundled assets, allowed remote, `publicUrl` media).
52
+ if (!key || !storage) return genericGET(ctx);
53
+
54
+ const service = await getConfiguredImageService();
55
+ if (!("transform" in service)) return genericGET(ctx);
56
+
57
+ try {
58
+ const source = await storage.download(key);
59
+
60
+ // Only raster images are transformable; serve anything else unchanged.
61
+ if (!source.contentType.startsWith("image/")) {
62
+ return streamOriginal(source.body, source.contentType);
63
+ }
64
+
65
+ const transform = await service.parseURL(url, imageConfig);
66
+ if (!transform) return streamOriginal(source.body, source.contentType);
67
+
68
+ const inputBuffer = new Uint8Array(await new Response(source.body).arrayBuffer());
69
+ const { data, format } = await service.transform(inputBuffer, transform, imageConfig);
70
+
71
+ return new Response(data, {
72
+ status: 200,
73
+ headers: {
74
+ "Content-Type": FORMAT_MIME[format] ?? source.contentType,
75
+ "Cache-Control": IMMUTABLE_IMAGE_CACHE,
76
+ "X-Content-Type-Options": "nosniff",
77
+ },
78
+ });
79
+ } catch (error) {
80
+ if (isNotFound(error)) return new Response("Not Found", { status: 404 });
81
+ console.error("[emdash] image transform failed:", error);
82
+ return new Response("Internal Server Error", { status: 500 });
83
+ }
84
+ };
@@ -21,6 +21,12 @@ export type {
21
21
  export { local, s3 } from "./storage/index.js";
22
22
  export type { StorageDescriptor, LocalStorageConfig, S3StorageConfig } from "./storage/index.js";
23
23
 
24
+ // Object cache adapters (for integration config)
25
+ // Note: For Cloudflare KV, use `kvCache()` from `@emdash-cms/cloudflare`
26
+ export { memoryCache } from "./object-cache/adapters.js";
27
+ export type { MemoryCacheOptions } from "./object-cache/adapters.js";
28
+ export type { ObjectCacheDescriptor, ObjectCacheRuntimeConfig } from "../object-cache/types.js";
29
+
24
30
  // Integration (build-time only - the emdash() function uses Node.js APIs)
25
31
  export { default } from "./integration/index.js";
26
32
  export { getStoredConfig } from "./integration/runtime.js";