emdash 0.28.1 → 0.29.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 (680) hide show
  1. package/dist/{adapters-hUy8vOB-.d.mts → adapters-DPAkL6QF.d.mts} +1 -1
  2. package/dist/{adapters-hUy8vOB-.d.mts.map → adapters-DPAkL6QF.d.mts.map} +1 -1
  3. package/dist/{allowed-origins-CRsJ6BPM.mjs → allowed-origins-FXB-FplB.mjs} +2 -2
  4. package/dist/{allowed-origins-CRsJ6BPM.mjs.map → allowed-origins-FXB-FplB.mjs.map} +1 -1
  5. package/dist/api/route-utils.d.mts +3 -3
  6. package/dist/api/route-utils.mjs +22 -21
  7. package/dist/api/route-utils.mjs.map +1 -1
  8. package/dist/api/schemas/index.d.mts +2 -2
  9. package/dist/api/schemas/index.mjs +4 -4
  10. package/dist/{api-DxFSorgk.mjs → api-DVbHNApo.mjs} +75 -23
  11. package/dist/api-DVbHNApo.mjs.map +1 -0
  12. package/dist/{api-tokens-DvqeZmVq.mjs → api-tokens-UYBkl17d.mjs} +2 -2
  13. package/dist/{api-tokens-DvqeZmVq.mjs.map → api-tokens-UYBkl17d.mjs.map} +1 -1
  14. package/dist/{apply-CDJqIjPU.mjs → apply-D23iUV4q.mjs} +20 -20
  15. package/dist/{apply-CDJqIjPU.mjs.map → apply-D23iUV4q.mjs.map} +1 -1
  16. package/dist/astro/index.d.mts +13 -12
  17. package/dist/astro/index.d.mts.map +1 -1
  18. package/dist/astro/index.mjs +10 -5
  19. package/dist/astro/index.mjs.map +1 -1
  20. package/dist/astro/middleware/auth.d.mts +11 -10
  21. package/dist/astro/middleware/auth.d.mts.map +1 -1
  22. package/dist/astro/middleware/auth.mjs +8 -8
  23. package/dist/astro/middleware/redirect.mjs +7 -7
  24. package/dist/astro/middleware/request-context.d.mts +3 -0
  25. package/dist/astro/middleware/request-context.d.mts.map +1 -1
  26. package/dist/astro/middleware/request-context.mjs +257 -30
  27. package/dist/astro/middleware/request-context.mjs.map +1 -1
  28. package/dist/astro/middleware/setup.mjs +1 -1
  29. package/dist/astro/middleware.d.mts +2 -2
  30. package/dist/astro/middleware.d.mts.map +1 -1
  31. package/dist/astro/middleware.mjs +125 -79
  32. package/dist/astro/middleware.mjs.map +1 -1
  33. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -7
  34. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -7
  35. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
  36. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  37. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -6
  38. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -10
  39. package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -10
  40. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -10
  41. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -17
  42. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -17
  43. package/dist/astro/routes/api/admin/bylines/index.mjs +17 -17
  44. package/dist/astro/routes/api/admin/comments/_id_/status.d.mts.map +1 -1
  45. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -13
  46. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
  47. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
  48. package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
  49. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
  50. package/dist/astro/routes/api/admin/comments/index.d.mts.map +1 -1
  51. package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
  52. package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
  53. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
  54. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
  55. package/dist/astro/routes/api/admin/media-usage/repair.d.mts +8 -0
  56. package/dist/astro/routes/api/admin/media-usage/repair.d.mts.map +1 -0
  57. package/dist/astro/routes/api/admin/media-usage/repair.mjs +28 -0
  58. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -0
  59. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
  60. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
  61. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +43 -41
  62. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  63. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +43 -41
  64. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  65. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +42 -40
  66. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  67. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +42 -40
  68. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  69. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +42 -40
  70. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  71. package/dist/astro/routes/api/admin/plugins/index.mjs +42 -40
  72. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  73. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
  74. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +42 -40
  75. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +42 -40
  77. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +42 -40
  79. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +42 -40
  81. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  82. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +43 -41
  83. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  84. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +42 -40
  85. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  86. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +43 -41
  87. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  88. package/dist/astro/routes/api/admin/plugins/updates.mjs +42 -40
  89. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +42 -40
  91. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
  93. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +42 -40
  94. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  95. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
  96. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -3
  97. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -8
  98. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
  99. package/dist/astro/routes/api/admin/users/index.mjs +7 -7
  100. package/dist/astro/routes/api/auth/dev-bypass.mjs +7 -7
  101. package/dist/astro/routes/api/auth/invite/accept.mjs +3 -3
  102. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -12
  103. package/dist/astro/routes/api/auth/invite/index.mjs +9 -9
  104. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -11
  105. package/dist/astro/routes/api/auth/logout.mjs +4 -4
  106. package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -11
  107. package/dist/astro/routes/api/auth/magic-link/verify.mjs +4 -4
  108. package/dist/astro/routes/api/auth/me.mjs +8 -8
  109. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  110. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  111. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs.map +1 -1
  112. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  113. package/dist/astro/routes/api/auth/oauth/_provider_.mjs.map +1 -1
  114. package/dist/astro/routes/api/auth/passkey/_id_.mjs +7 -7
  115. package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
  116. package/dist/astro/routes/api/auth/passkey/options.mjs +13 -13
  117. package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -11
  118. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -12
  119. package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -12
  120. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -12
  121. package/dist/astro/routes/api/auth/signup/request.mjs +11 -11
  122. package/dist/astro/routes/api/auth/signup/verify.mjs +3 -3
  123. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.d.mts.map +1 -1
  124. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +69 -14
  125. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
  126. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
  127. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -4
  128. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +4 -4
  129. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -4
  130. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -4
  131. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +12 -12
  132. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +8 -8
  133. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts +9 -0
  134. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts.map +1 -0
  135. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +71 -0
  136. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -0
  137. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.d.mts +8 -0
  138. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.d.mts.map +1 -0
  139. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +50 -0
  140. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -0
  141. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -4
  142. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -4
  143. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
  144. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +15 -15
  145. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -4
  146. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +4 -4
  147. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
  148. package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -4
  149. package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
  150. package/dist/astro/routes/api/content/_collection_/trash.mjs +8 -8
  151. package/dist/astro/routes/api/dashboard.mjs +8 -8
  152. package/dist/astro/routes/api/dev/emails.mjs +4 -4
  153. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  154. package/dist/astro/routes/api/import/probe.mjs +18 -18
  155. package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -5
  156. package/dist/astro/routes/api/import/wordpress/execute.d.mts +12 -11
  157. package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
  158. package/dist/astro/routes/api/import/wordpress/execute.mjs +16 -16
  159. package/dist/astro/routes/api/import/wordpress/media.d.mts +11 -10
  160. package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
  161. package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
  162. package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -11
  163. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +12 -16
  164. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
  165. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +11 -11
  166. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  167. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +18 -18
  168. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
  169. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +12 -11
  170. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -1
  171. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +28 -28
  172. package/dist/astro/routes/api/manifest.mjs +6 -6
  173. package/dist/astro/routes/api/mcp.mjs +56 -47
  174. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  175. package/dist/astro/routes/api/media/_id_/confirm.mjs +9 -9
  176. package/dist/astro/routes/api/media/_id_.mjs +8 -8
  177. package/dist/astro/routes/api/media/file/_...key_.mjs +3 -3
  178. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
  179. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
  180. package/dist/astro/routes/api/media/providers/index.mjs +4 -4
  181. package/dist/astro/routes/api/media/upload-url.mjs +10 -10
  182. package/dist/astro/routes/api/media.mjs +13 -13
  183. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +9 -9
  184. package/dist/astro/routes/api/menus/_name_/items.mjs +9 -9
  185. package/dist/astro/routes/api/menus/_name_/reorder.mjs +9 -9
  186. package/dist/astro/routes/api/menus/_name_/translations.mjs +9 -9
  187. package/dist/astro/routes/api/menus/_name_.mjs +9 -9
  188. package/dist/astro/routes/api/menus/index.mjs +9 -9
  189. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  190. package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
  191. package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
  192. package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
  193. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  194. package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
  195. package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
  196. package/dist/astro/routes/api/oauth/token.mjs +7 -7
  197. package/dist/astro/routes/api/openapi.json.mjs +24 -5
  198. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  199. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +5 -5
  200. package/dist/astro/routes/api/redirects/404s/index.mjs +12 -12
  201. package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -12
  202. package/dist/astro/routes/api/redirects/_id_.mjs +13 -13
  203. package/dist/astro/routes/api/redirects/index.mjs +13 -13
  204. package/dist/astro/routes/api/relations/_id_/index.d.mts +10 -0
  205. package/dist/astro/routes/api/relations/_id_/index.d.mts.map +1 -0
  206. package/dist/astro/routes/api/relations/_id_/index.mjs +69 -0
  207. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -0
  208. package/dist/astro/routes/api/relations/_id_/translations.d.mts +8 -0
  209. package/dist/astro/routes/api/relations/_id_/translations.d.mts.map +1 -0
  210. package/dist/astro/routes/api/relations/_id_/translations.mjs +34 -0
  211. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -0
  212. package/dist/astro/routes/api/relations/index.d.mts +9 -0
  213. package/dist/astro/routes/api/relations/index.d.mts.map +1 -0
  214. package/dist/astro/routes/api/relations/index.mjs +53 -0
  215. package/dist/astro/routes/api/relations/index.mjs.map +1 -0
  216. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
  217. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -4
  218. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.d.mts.map +1 -1
  219. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +42 -40
  220. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  221. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.d.mts.map +1 -1
  222. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +42 -40
  223. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  224. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +42 -40
  225. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  226. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +42 -40
  227. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  228. package/dist/astro/routes/api/schema/collections/index.mjs +42 -40
  229. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  230. package/dist/astro/routes/api/schema/index.mjs +9 -9
  231. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +42 -40
  232. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  233. package/dist/astro/routes/api/schema/orphans/index.mjs +42 -40
  234. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  235. package/dist/astro/routes/api/search/enable.mjs +12 -12
  236. package/dist/astro/routes/api/search/index.mjs +13 -12
  237. package/dist/astro/routes/api/search/index.mjs.map +1 -1
  238. package/dist/astro/routes/api/search/rebuild.mjs +12 -12
  239. package/dist/astro/routes/api/search/stats.mjs +8 -8
  240. package/dist/astro/routes/api/search/suggest.mjs +11 -11
  241. package/dist/astro/routes/api/sections/_slug_.mjs +11 -11
  242. package/dist/astro/routes/api/sections/index.mjs +11 -11
  243. package/dist/astro/routes/api/settings/email.mjs +6 -6
  244. package/dist/astro/routes/api/settings.mjs +15 -15
  245. package/dist/astro/routes/api/setup/admin-verify.mjs +13 -13
  246. package/dist/astro/routes/api/setup/admin.mjs +12 -12
  247. package/dist/astro/routes/api/setup/dev-bypass.mjs +29 -29
  248. package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
  249. package/dist/astro/routes/api/setup/index.mjs +30 -30
  250. package/dist/astro/routes/api/setup/status.mjs +5 -5
  251. package/dist/astro/routes/api/snapshot.mjs +8 -8
  252. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +15 -15
  253. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +15 -15
  254. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +15 -15
  255. package/dist/astro/routes/api/taxonomies/index.mjs +15 -15
  256. package/dist/astro/routes/api/themes/preview.mjs +7 -7
  257. package/dist/astro/routes/api/typegen.mjs +6 -6
  258. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  259. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  260. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  261. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +8 -8
  262. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +12 -12
  263. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +12 -12
  264. package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -8
  265. package/dist/astro/routes/api/widget-areas/index.mjs +12 -12
  266. package/dist/astro/routes/api/widget-components.mjs +4 -4
  267. package/dist/astro/routes/robots.txt.mjs +9 -9
  268. package/dist/astro/routes/sitemap-_collection_.xml.mjs +12 -12
  269. package/dist/astro/routes/sitemap.xml.mjs +10 -10
  270. package/dist/astro/types.d.mts +16 -13
  271. package/dist/astro/types.d.mts.map +1 -1
  272. package/dist/auth/providers/github.d.mts +1 -1
  273. package/dist/auth/providers/google.d.mts +1 -1
  274. package/dist/{authorize-Dz88F_Fw.mjs → authorize-BmCwQCib.mjs} +2 -2
  275. package/dist/{authorize-Dz88F_Fw.mjs.map → authorize-BmCwQCib.mjs.map} +1 -1
  276. package/dist/{base64-CmWvODNW.mjs → base64-C74Msx8h.mjs} +1 -1
  277. package/dist/{base64-CmWvODNW.mjs.map → base64-C74Msx8h.mjs.map} +1 -1
  278. package/dist/{byline-Bm8ZgUP9.mjs → byline-BWb5H-65.mjs} +8 -8
  279. package/dist/{byline-Bm8ZgUP9.mjs.map → byline-BWb5H-65.mjs.map} +1 -1
  280. package/dist/{byline-fields-CIx_GuN4.mjs → byline-fields-DO6bAYK8.mjs} +2 -2
  281. package/dist/{byline-fields-CIx_GuN4.mjs.map → byline-fields-DO6bAYK8.mjs.map} +1 -1
  282. package/dist/{byline-registry-BOjqDOim.mjs → byline-registry-BCuOp4UF.mjs} +3 -3
  283. package/dist/{byline-registry-BOjqDOim.mjs.map → byline-registry-BCuOp4UF.mjs.map} +1 -1
  284. package/dist/{bylines-6tm9mgQb.mjs → bylines-BGuKFaBy.mjs} +4 -4
  285. package/dist/{bylines-6tm9mgQb.mjs.map → bylines-BGuKFaBy.mjs.map} +1 -1
  286. package/dist/{bylines-CAZxGSrm.mjs → bylines-CHojDMpj.mjs} +8 -8
  287. package/dist/{bylines-CAZxGSrm.mjs.map → bylines-CHojDMpj.mjs.map} +1 -1
  288. package/dist/{cache-CEBAZjir.mjs → cache-BeR7RfN8.mjs} +3 -3
  289. package/dist/{cache-CEBAZjir.mjs.map → cache-BeR7RfN8.mjs.map} +1 -1
  290. package/dist/{challenge-store-anhwVER0.mjs → challenge-store-B426wQ6O.mjs} +1 -1
  291. package/dist/{challenge-store-anhwVER0.mjs.map → challenge-store-B426wQ6O.mjs.map} +1 -1
  292. package/dist/{chunks-B2isWQhr.mjs → chunks-BU5_nrAC.mjs} +2 -2
  293. package/dist/{chunks-B2isWQhr.mjs.map → chunks-BU5_nrAC.mjs.map} +1 -1
  294. package/dist/cli/index.mjs +28 -28
  295. package/dist/cli/index.mjs.map +1 -1
  296. package/dist/client/cf-access.d.mts +1 -1
  297. package/dist/client/index.d.mts +34 -2
  298. package/dist/client/index.d.mts.map +1 -1
  299. package/dist/client/index.mjs +6 -2
  300. package/dist/client/index.mjs.map +1 -1
  301. package/dist/{comment-CzeatwBV.mjs → comment-SWh31kS0.mjs} +2 -2
  302. package/dist/{comment-CzeatwBV.mjs.map → comment-SWh31kS0.mjs.map} +1 -1
  303. package/dist/{comment-reaction-Cj9QJY7I.mjs → comment-reaction-DgcpziXI.mjs} +2 -2
  304. package/dist/{comment-reaction-Cj9QJY7I.mjs.map → comment-reaction-DgcpziXI.mjs.map} +1 -1
  305. package/dist/{comments-BLE-jv5k.mjs → comments-kZYGppMS.mjs} +3 -3
  306. package/dist/{comments-BLE-jv5k.mjs.map → comments-kZYGppMS.mjs.map} +1 -1
  307. package/dist/{components-B_sATNUc.mjs → components-CliOkZAs.mjs} +1 -1
  308. package/dist/{components-B_sATNUc.mjs.map → components-CliOkZAs.mjs.map} +1 -1
  309. package/dist/{content-DhgEth4k.mjs → content-BO61Sx4j.mjs} +179 -20
  310. package/dist/content-BO61Sx4j.mjs.map +1 -0
  311. package/dist/{content-refresh-Br4gMzh8.mjs → content-refresh-BkkApzeS.mjs} +8 -8
  312. package/dist/{content-refresh-Br4gMzh8.mjs.map → content-refresh-BkkApzeS.mjs.map} +1 -1
  313. package/dist/{context-BRIpHARC.mjs → context-DtSFYFuS.mjs} +71 -14
  314. package/dist/context-DtSFYFuS.mjs.map +1 -0
  315. package/dist/{cron-v4FTEFR_.mjs → cron-YFJ7uNVA.mjs} +1 -1
  316. package/dist/{cron-v4FTEFR_.mjs.map → cron-YFJ7uNVA.mjs.map} +1 -1
  317. package/dist/{dashboard-EvEKF5en.mjs → dashboard-xo_gPs_D.mjs} +6 -5
  318. package/dist/dashboard-xo_gPs_D.mjs.map +1 -0
  319. package/dist/database/pg-migration-lock.d.mts +21 -0
  320. package/dist/database/pg-migration-lock.d.mts.map +1 -0
  321. package/dist/database/pg-migration-lock.mjs +45 -0
  322. package/dist/database/pg-migration-lock.mjs.map +1 -0
  323. package/dist/db/index.d.mts +3 -3
  324. package/dist/db/index.mjs +2 -2
  325. package/dist/db/libsql.d.mts +1 -1
  326. package/dist/db/postgres.d.mts +1 -1
  327. package/dist/db/postgres.d.mts.map +1 -1
  328. package/dist/db/postgres.mjs +2 -8
  329. package/dist/db/postgres.mjs.map +1 -1
  330. package/dist/db/sqlite.d.mts +1 -1
  331. package/dist/{db-errors-CK46D-ly.mjs → db-errors-BKuXh_fi.mjs} +1 -1
  332. package/dist/{db-errors-CK46D-ly.mjs.map → db-errors-BKuXh_fi.mjs.map} +1 -1
  333. package/dist/{default-CM_D83Mg.mjs → default-DY5ovGUG.mjs} +1 -1
  334. package/dist/{default-CM_D83Mg.mjs.map → default-DY5ovGUG.mjs.map} +1 -1
  335. package/dist/{device-flow-CfGP18Uw.mjs → device-flow-CPXQ_QfW.mjs} +5 -5
  336. package/dist/{device-flow-CfGP18Uw.mjs.map → device-flow-CPXQ_QfW.mjs.map} +1 -1
  337. package/dist/{dialect-helpers-DRI5pyY3.mjs → dialect-helpers-Cue7QQOR.mjs} +21 -2
  338. package/dist/dialect-helpers-Cue7QQOR.mjs.map +1 -0
  339. package/dist/{email-console-CIf4Lfvv.mjs → email-console-CjkQc7oY.mjs} +1 -1
  340. package/dist/{email-console-CIf4Lfvv.mjs.map → email-console-CjkQc7oY.mjs.map} +1 -1
  341. package/dist/{enrich-BQ0mxJRs.mjs → enrich-CFJJgxs_.mjs} +2 -2
  342. package/dist/{enrich-BQ0mxJRs.mjs.map → enrich-CFJJgxs_.mjs.map} +1 -1
  343. package/dist/{error-CkAseSqZ.mjs → error-CfJMLSkd.mjs} +12 -3
  344. package/dist/error-CfJMLSkd.mjs.map +1 -0
  345. package/dist/{escape-DqD0aCKu.mjs → escape-r8_GbEU2.mjs} +1 -1
  346. package/dist/{escape-DqD0aCKu.mjs.map → escape-r8_GbEU2.mjs.map} +1 -1
  347. package/dist/{field-defs-cache-WUI88LX5.mjs → field-defs-cache-BVrNCgSj.mjs} +5 -5
  348. package/dist/{field-defs-cache-WUI88LX5.mjs.map → field-defs-cache-BVrNCgSj.mjs.map} +1 -1
  349. package/dist/{fts-manager-BbFaF_V8.mjs → fts-manager-BD9s77qh.mjs} +27 -3
  350. package/dist/{fts-manager-BbFaF_V8.mjs.map → fts-manager-BD9s77qh.mjs.map} +1 -1
  351. package/dist/{hash-DrvzIXcz.mjs → hash-DFFrkivP.mjs} +1 -1
  352. package/dist/{hash-DrvzIXcz.mjs.map → hash-DFFrkivP.mjs.map} +1 -1
  353. package/dist/hreflang-CgIxhH4k.d.mts +48 -0
  354. package/dist/hreflang-CgIxhH4k.d.mts.map +1 -0
  355. package/dist/hreflang-pAWUiPd_.mjs +102 -0
  356. package/dist/hreflang-pAWUiPd_.mjs.map +1 -0
  357. package/dist/{import-fYLf14Co.mjs → import-2jHtLzsO.mjs} +6 -6
  358. package/dist/{import-fYLf14Co.mjs.map → import-2jHtLzsO.mjs.map} +1 -1
  359. package/dist/{index-B9sIxwe4.d.mts → index-BXOCyLj0.d.mts} +115 -21
  360. package/dist/index-BXOCyLj0.d.mts.map +1 -0
  361. package/dist/{index-Bm-Gy8Mh.d.mts → index-DxjWLKGT.d.mts} +3 -3
  362. package/dist/{index-Bm-Gy8Mh.d.mts.map → index-DxjWLKGT.d.mts.map} +1 -1
  363. package/dist/index.d.mts +20 -19
  364. package/dist/index.mjs +68 -64
  365. package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
  366. package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
  367. package/dist/{load-Djn3HWQS.mjs → load-DgoWMdRX.mjs} +2 -2
  368. package/dist/{load-Djn3HWQS.mjs.map → load-DgoWMdRX.mjs.map} +1 -1
  369. package/dist/{loader-CB6YHeul.mjs → loader-Rb8QlGZp.mjs} +5 -20
  370. package/dist/loader-Rb8QlGZp.mjs.map +1 -0
  371. package/dist/{manifest-schema-BHlgZM1W.mjs → manifest-schema-DeGNma2O.mjs} +3 -1
  372. package/dist/manifest-schema-DeGNma2O.mjs.map +1 -0
  373. package/dist/media/image-endpoint.mjs +1 -1
  374. package/dist/media/index.d.mts +1 -1
  375. package/dist/media/index.mjs +2 -2
  376. package/dist/media/local-runtime.d.mts +13 -12
  377. package/dist/media/local-runtime.d.mts.map +1 -1
  378. package/dist/media/local-runtime.mjs +8 -8
  379. package/dist/{media-BrXIaMFN.mjs → media-D2ZFowpn.mjs} +2 -2
  380. package/dist/media-D2ZFowpn.mjs.map +1 -0
  381. package/dist/{media-allowlist-CRHEeJ_i.mjs → media-allowlist-Dc05vKtt.mjs} +2 -2
  382. package/dist/{media-allowlist-CRHEeJ_i.mjs.map → media-allowlist-Dc05vKtt.mjs.map} +1 -1
  383. package/dist/{media-url-CKi5XSw4.mjs → media-url-DgIEYZr4.mjs} +1 -1
  384. package/dist/{media-url-CKi5XSw4.mjs.map → media-url-DgIEYZr4.mjs.map} +1 -1
  385. package/dist/media-usage-B__v9NMm.mjs +421 -0
  386. package/dist/media-usage-B__v9NMm.mjs.map +1 -0
  387. package/dist/{menus-Cfivc2Ad.mjs → menus-BpfevlJD.mjs} +3 -3
  388. package/dist/{menus-Cfivc2Ad.mjs.map → menus-BpfevlJD.mjs.map} +1 -1
  389. package/dist/{menus-b2T4clbO.mjs → menus-BtvH7Z7t.mjs} +31 -12
  390. package/dist/menus-BtvH7Z7t.mjs.map +1 -0
  391. package/dist/{mime-CCEzze7W.mjs → mime-DfIVjNkr.mjs} +1 -1
  392. package/dist/{mime-CCEzze7W.mjs.map → mime-DfIVjNkr.mjs.map} +1 -1
  393. package/dist/{mode-BM9QgEmt.mjs → mode-BVnhGZ2o.mjs} +1 -1
  394. package/dist/{mode-BM9QgEmt.mjs.map → mode-BVnhGZ2o.mjs.map} +1 -1
  395. package/dist/{normalize-CeglrBT9.mjs → normalize-C-SHXmra.mjs} +1 -1
  396. package/dist/{normalize-CeglrBT9.mjs.map → normalize-C-SHXmra.mjs.map} +1 -1
  397. package/dist/{oauth-authorization-CXWkIGT4.mjs → oauth-authorization-BWqWTHBK.mjs} +5 -5
  398. package/dist/{oauth-authorization-CXWkIGT4.mjs.map → oauth-authorization-BWqWTHBK.mjs.map} +1 -1
  399. package/dist/{oauth-clients-BmTI5rsl.mjs → oauth-clients-43_XADoa.mjs} +1 -1
  400. package/dist/{oauth-clients-BmTI5rsl.mjs.map → oauth-clients-43_XADoa.mjs.map} +1 -1
  401. package/dist/{oauth-state-store-YLFLcH5F.mjs → oauth-state-store-BRSUEkmx.mjs} +1 -1
  402. package/dist/{oauth-state-store-YLFLcH5F.mjs.map → oauth-state-store-BRSUEkmx.mjs.map} +1 -1
  403. package/dist/{oauth-user-lookup-Bsc_GsX-.mjs → oauth-user-lookup-Ctgemb0k.mjs} +1 -1
  404. package/dist/{oauth-user-lookup-Bsc_GsX-.mjs.map → oauth-user-lookup-Ctgemb0k.mjs.map} +1 -1
  405. package/dist/object-cache/memory.d.mts +1 -1
  406. package/dist/{options-DTTML-Tx.mjs → options-BlmBHTvX.mjs} +1 -1
  407. package/dist/{options-DTTML-Tx.mjs.map → options-BlmBHTvX.mjs.map} +1 -1
  408. package/dist/{options-iMpRRxIe.d.mts → options-COXGTtqd.d.mts} +3 -3
  409. package/dist/{options-iMpRRxIe.d.mts.map → options-COXGTtqd.d.mts.map} +1 -1
  410. package/dist/page/index.d.mts +2 -2
  411. package/dist/{parse-GmjecGct.mjs → parse-GLksnn-B.mjs} +2 -2
  412. package/dist/{parse-GmjecGct.mjs.map → parse-GLksnn-B.mjs.map} +1 -1
  413. package/dist/{passkey-config-De0ahnRg.mjs → passkey-config-CjA1UbDj.mjs} +1 -1
  414. package/dist/{passkey-config-De0ahnRg.mjs.map → passkey-config-CjA1UbDj.mjs.map} +1 -1
  415. package/dist/{patterns-BKmjvM7K.mjs → patterns-CiyXeDgr.mjs} +1 -1
  416. package/dist/{patterns-BKmjvM7K.mjs.map → patterns-CiyXeDgr.mjs.map} +1 -1
  417. package/dist/{placeholder-BSKwzRe4.d.mts → placeholder-DR-eeZr-.d.mts} +1 -1
  418. package/dist/{placeholder-BSKwzRe4.d.mts.map → placeholder-DR-eeZr-.d.mts.map} +1 -1
  419. package/dist/plugin-types.d.mts +1 -1
  420. package/dist/plugin-utils.d.mts +11 -10
  421. package/dist/plugin-utils.d.mts.map +1 -1
  422. package/dist/plugins/adapt-sandbox-entry.d.mts +11 -10
  423. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
  424. package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
  425. package/dist/{portable-text-cCa0cGV6.mjs → portable-text-BAkgEKcv.mjs} +1 -1
  426. package/dist/{portable-text-cCa0cGV6.mjs.map → portable-text-BAkgEKcv.mjs.map} +1 -1
  427. package/dist/{preview-SSXNVPAC.mjs → preview-Rloa09Wp.mjs} +2 -2
  428. package/dist/{preview-SSXNVPAC.mjs.map → preview-Rloa09Wp.mjs.map} +1 -1
  429. package/dist/{public-url-Danv47cS.mjs → public-url-C0cEvrRu.mjs} +1 -1
  430. package/dist/{public-url-Danv47cS.mjs.map → public-url-C0cEvrRu.mjs.map} +1 -1
  431. package/dist/{schema-CX9cfNkZ.mjs → query-BxT95opY.mjs} +5 -10
  432. package/dist/query-BxT95opY.mjs.map +1 -0
  433. package/dist/{query-1Cp6U9oo.mjs → query-ZzvXyxWq.mjs} +15 -15
  434. package/dist/query-ZzvXyxWq.mjs.map +1 -0
  435. package/dist/{rate-limit-C_Lf63Dd.mjs → rate-limit-Cg4Rvc_-.mjs} +2 -2
  436. package/dist/{rate-limit-C_Lf63Dd.mjs.map → rate-limit-Cg4Rvc_-.mjs.map} +1 -1
  437. package/dist/{redirect-CoW3G-0U.mjs → redirect-Ce_6Yb6z.mjs} +1 -1
  438. package/dist/{redirect-CoW3G-0U.mjs.map → redirect-Ce_6Yb6z.mjs.map} +1 -1
  439. package/dist/{redirect-IyfNirgS.mjs → redirect-Ch3E98qZ.mjs} +4 -4
  440. package/dist/{redirect-IyfNirgS.mjs.map → redirect-Ch3E98qZ.mjs.map} +1 -1
  441. package/dist/{redirects-Bs1g42Ri.mjs → redirects-CXmVGRNZ.mjs} +6 -6
  442. package/dist/{redirects-Bs1g42Ri.mjs.map → redirects-CXmVGRNZ.mjs.map} +1 -1
  443. package/dist/{redirects-DXEXBAII.mjs → redirects-CeD7K7EX.mjs} +43 -5
  444. package/dist/redirects-CeD7K7EX.mjs.map +1 -0
  445. package/dist/{registry-B57vp_X9.mjs → registry-CyIXPi4E.mjs} +8 -8
  446. package/dist/{registry-B57vp_X9.mjs.map → registry-CyIXPi4E.mjs.map} +1 -1
  447. package/dist/relation-Ba68Tm4Z.mjs +334 -0
  448. package/dist/relation-Ba68Tm4Z.mjs.map +1 -0
  449. package/dist/{byline-fields-2Lp5Hbgf.mjs → relations-BmBmX4Eb.mjs} +60 -4
  450. package/dist/relations-BmBmX4Eb.mjs.map +1 -0
  451. package/dist/relations-C8hVyEy0.mjs +397 -0
  452. package/dist/relations-C8hVyEy0.mjs.map +1 -0
  453. package/dist/{byline-fields-CHE8UStY.d.mts → relations-CbBpoOys.d.mts} +182 -29
  454. package/dist/relations-CbBpoOys.d.mts.map +1 -0
  455. package/dist/{request-cache-KCNHp_RJ.mjs → request-cache-BSUptuJR.mjs} +1 -1
  456. package/dist/{request-cache-KCNHp_RJ.mjs.map → request-cache-BSUptuJR.mjs.map} +1 -1
  457. package/dist/{request-meta-DbvXaAvU.mjs → request-meta-DCwktC5P.mjs} +2 -2
  458. package/dist/{request-meta-DbvXaAvU.mjs.map → request-meta-DCwktC5P.mjs.map} +1 -1
  459. package/dist/{resolve-DR3wqMTy.mjs → resolve-Cd9dzclN.mjs} +1 -1
  460. package/dist/{resolve-DR3wqMTy.mjs.map → resolve-Cd9dzclN.mjs.map} +1 -1
  461. package/dist/{runner-DvFWoz8-.d.mts → runner-CiNdIk3y.d.mts} +8 -2
  462. package/dist/runner-CiNdIk3y.d.mts.map +1 -0
  463. package/dist/{runner-K8XK101B.mjs → runner-jt65bF7F.mjs} +24 -7
  464. package/dist/{runner-K8XK101B.mjs.map → runner-jt65bF7F.mjs.map} +1 -1
  465. package/dist/runtime.d.mts +12 -11
  466. package/dist/runtime.d.mts.map +1 -1
  467. package/dist/runtime.mjs +4 -4
  468. package/dist/{scheduled-publish-Drffrf2a.d.mts → scheduled-publish-DlJHGcuL.d.mts} +1 -1
  469. package/dist/scheduled-publish-DlJHGcuL.d.mts.map +1 -0
  470. package/dist/schema-DwjufZ1S.mjs +9 -0
  471. package/dist/schema-DwjufZ1S.mjs.map +1 -0
  472. package/dist/{search-DZN2HZ-V.mjs → search-Cl875CVE.mjs} +62 -12
  473. package/dist/search-Cl875CVE.mjs.map +1 -0
  474. package/dist/{secrets-kp760LlD.mjs → secrets-CO_bEmII.mjs} +4 -4
  475. package/dist/{secrets-kp760LlD.mjs.map → secrets-CO_bEmII.mjs.map} +1 -1
  476. package/dist/{sections-CpF5SaGr.mjs → sections-DTKzgoiS.mjs} +3 -3
  477. package/dist/{sections-CpF5SaGr.mjs.map → sections-DTKzgoiS.mjs.map} +1 -1
  478. package/dist/seed/index.d.mts +2 -2
  479. package/dist/seed/index.mjs +23 -23
  480. package/dist/seo/index.d.mts +4 -2
  481. package/dist/seo/index.d.mts.map +1 -1
  482. package/dist/seo/index.mjs +19 -2
  483. package/dist/seo/index.mjs.map +1 -1
  484. package/dist/{seo-DXu2UQnC.mjs → seo-BLm6SXzS.mjs} +2 -2
  485. package/dist/{seo-DXu2UQnC.mjs.map → seo-BLm6SXzS.mjs.map} +1 -1
  486. package/dist/{seo-89-53DDs.mjs → seo-BjrbXAd_.mjs} +1 -1
  487. package/dist/{seo-89-53DDs.mjs.map → seo-BjrbXAd_.mjs.map} +1 -1
  488. package/dist/{service-DQmiBMzt.mjs → service-D6zDmKXy.mjs} +3 -3
  489. package/dist/{service-DQmiBMzt.mjs.map → service-D6zDmKXy.mjs.map} +1 -1
  490. package/dist/{session-user-Cascbax7.mjs → session-user-DoSs6YM8.mjs} +1 -1
  491. package/dist/{session-user-Cascbax7.mjs.map → session-user-DoSs6YM8.mjs.map} +1 -1
  492. package/dist/{settings-zhmdKyJQ.mjs → settings-BuQYvLfy.mjs} +6 -6
  493. package/dist/{settings-zhmdKyJQ.mjs.map → settings-BuQYvLfy.mjs.map} +1 -1
  494. package/dist/{settings-rZyHEcNT.mjs → settings-CmS3TwXf.mjs} +3 -3
  495. package/dist/{settings-rZyHEcNT.mjs.map → settings-CmS3TwXf.mjs.map} +1 -1
  496. package/dist/{setup-complete-Cb3LRyzy.mjs → setup-complete-BPNgLHlU.mjs} +2 -2
  497. package/dist/{setup-complete-Cb3LRyzy.mjs.map → setup-complete-BPNgLHlU.mjs.map} +1 -1
  498. package/dist/{setup-nonce-Z8x0tHf8.mjs → setup-nonce-DEPZX96T.mjs} +1 -1
  499. package/dist/{setup-nonce-Z8x0tHf8.mjs.map → setup-nonce-DEPZX96T.mjs.map} +1 -1
  500. package/dist/{single-flight-cache-BG3B_GhN.mjs → single-flight-cache-DEtW-OEG.mjs} +2 -2
  501. package/dist/{single-flight-cache-BG3B_GhN.mjs.map → single-flight-cache-DEtW-OEG.mjs.map} +1 -1
  502. package/dist/{site-url-BBfDTgZB.mjs → site-url-CcLy5D5s.mjs} +2 -2
  503. package/dist/{site-url-BBfDTgZB.mjs.map → site-url-CcLy5D5s.mjs.map} +1 -1
  504. package/dist/{slugify-Cce3dTdg.mjs → slugify-C_tqlU4G.mjs} +1 -1
  505. package/dist/{slugify-Cce3dTdg.mjs.map → slugify-C_tqlU4G.mjs.map} +1 -1
  506. package/dist/{ssrf-1g7usGBg.mjs → ssrf-TrKYJAy0.mjs} +1 -1
  507. package/dist/{ssrf-1g7usGBg.mjs.map → ssrf-TrKYJAy0.mjs.map} +1 -1
  508. package/dist/{status-CqJQX1QN.mjs → status-D3Gy1Zc6.mjs} +1 -1
  509. package/dist/{status-CqJQX1QN.mjs.map → status-D3Gy1Zc6.mjs.map} +1 -1
  510. package/dist/storage/local.d.mts +1 -1
  511. package/dist/storage/local.mjs +1 -1
  512. package/dist/storage/s3.d.mts +1 -1
  513. package/dist/storage/s3.mjs +1 -1
  514. package/dist/{taxonomies-Bs13SN-F.mjs → taxonomies-ClS4RKaZ.mjs} +13 -9
  515. package/dist/taxonomies-ClS4RKaZ.mjs.map +1 -0
  516. package/dist/{taxonomies-kH9ABDUF.mjs → taxonomies-pUkuD6nj.mjs} +115 -36
  517. package/dist/taxonomies-pUkuD6nj.mjs.map +1 -0
  518. package/dist/{taxonomy-BxGxZZdu.mjs → taxonomy-BkxbUMSw.mjs} +12 -3
  519. package/dist/{taxonomy-BxGxZZdu.mjs.map → taxonomy-BkxbUMSw.mjs.map} +1 -1
  520. package/dist/{tokens-BGT0TUUo.mjs → tokens-BV6fgl5A.mjs} +2 -2
  521. package/dist/{tokens-BGT0TUUo.mjs.map → tokens-BV6fgl5A.mjs.map} +1 -1
  522. package/dist/{transaction-qfqpPVpu.mjs → transaction-D0FOsb3X.mjs} +1 -1
  523. package/dist/{transaction-qfqpPVpu.mjs.map → transaction-D0FOsb3X.mjs.map} +1 -1
  524. package/dist/{transport-DPefPMcz.mjs → transport-DTFhhPOg.mjs} +1 -1
  525. package/dist/{transport-DPefPMcz.mjs.map → transport-DTFhhPOg.mjs.map} +1 -1
  526. package/dist/{transport-BMDWHSgZ.d.mts → transport-DmlmzD2U.d.mts} +1 -1
  527. package/dist/{transport-BMDWHSgZ.d.mts.map → transport-DmlmzD2U.d.mts.map} +1 -1
  528. package/dist/{trusted-proxy-VTPRiX2s.mjs → trusted-proxy-CSgZVESw.mjs} +1 -1
  529. package/dist/{trusted-proxy-VTPRiX2s.mjs.map → trusted-proxy-CSgZVESw.mjs.map} +1 -1
  530. package/dist/{types-CgvhU-hF.d.mts → types-B6-N9cBB.d.mts} +1 -1
  531. package/dist/{types-CgvhU-hF.d.mts.map → types-B6-N9cBB.d.mts.map} +1 -1
  532. package/dist/{types-g_aFIdW0.d.mts → types-BCVuUPrL.d.mts} +1 -1
  533. package/dist/{types-g_aFIdW0.d.mts.map → types-BCVuUPrL.d.mts.map} +1 -1
  534. package/dist/{types-Jo3UYd6b.mjs → types-BNZsPL9z.mjs} +1 -1
  535. package/dist/{types-Jo3UYd6b.mjs.map → types-BNZsPL9z.mjs.map} +1 -1
  536. package/dist/{types-vgEWF18A.d.mts → types-B_pmP7Yq.d.mts} +1 -1
  537. package/dist/{types-vgEWF18A.d.mts.map → types-B_pmP7Yq.d.mts.map} +1 -1
  538. package/dist/{types-CuW0zWDA.mjs → types-Ch8pFMEm.mjs} +3 -3
  539. package/dist/{types-CuW0zWDA.mjs.map → types-Ch8pFMEm.mjs.map} +1 -1
  540. package/dist/{types-ea9sAPck.d.mts → types-Cm4Cz-8M.d.mts} +1 -1
  541. package/dist/{types-ea9sAPck.d.mts.map → types-Cm4Cz-8M.d.mts.map} +1 -1
  542. package/dist/{types-CGbYowGQ.d.mts → types-CtDVagM4.d.mts} +55 -3
  543. package/dist/types-CtDVagM4.d.mts.map +1 -0
  544. package/dist/{types-BcLUUbbR.d.mts → types-DLIrNtv5.d.mts} +11 -3
  545. package/dist/{types-BcLUUbbR.d.mts.map → types-DLIrNtv5.d.mts.map} +1 -1
  546. package/dist/{types-DmHk7vAt.d.mts → types-QnlQIyPE.d.mts} +1 -1
  547. package/dist/{types-DmHk7vAt.d.mts.map → types-QnlQIyPE.d.mts.map} +1 -1
  548. package/dist/{types-_HFLRzTn.d.mts → types-momGysZk.d.mts} +1 -1
  549. package/dist/{types-_HFLRzTn.d.mts.map → types-momGysZk.d.mts.map} +1 -1
  550. package/dist/{types-tM44hEcf.mjs → types-o7xo7VgH.mjs} +1 -1
  551. package/dist/{types-tM44hEcf.mjs.map → types-o7xo7VgH.mjs.map} +1 -1
  552. package/dist/{types-ClUkdbWb.d.mts → types-qljd1hsL.d.mts} +1 -1
  553. package/dist/{types-ClUkdbWb.d.mts.map → types-qljd1hsL.d.mts.map} +1 -1
  554. package/dist/{user-BjAOvU3Z.mjs → user-DTxX_Fha.mjs} +3 -3
  555. package/dist/{user-BjAOvU3Z.mjs.map → user-DTxX_Fha.mjs.map} +1 -1
  556. package/dist/{utils-BJXElxdw.mjs → utils-Cz8PVrl4.mjs} +3 -3
  557. package/dist/{utils-BJXElxdw.mjs.map → utils-Cz8PVrl4.mjs.map} +1 -1
  558. package/dist/{validate-Z1b86_3B.d.mts → validate-9jLuE301.d.mts} +5 -5
  559. package/dist/{validate-Z1b86_3B.d.mts.map → validate-9jLuE301.d.mts.map} +1 -1
  560. package/dist/{validate-CWSnKVIJ.mjs → validate-C3CpWlNt.mjs} +3 -3
  561. package/dist/{validate-CWSnKVIJ.mjs.map → validate-C3CpWlNt.mjs.map} +1 -1
  562. package/dist/{validation-0l8-RYj4.mjs → validation-DM2K_NJC.mjs} +10 -10
  563. package/dist/{validation-0l8-RYj4.mjs.map → validation-DM2K_NJC.mjs.map} +1 -1
  564. package/dist/version-BlIJvNAZ.mjs +7 -0
  565. package/dist/{version-qmeO1XZY.mjs.map → version-BlIJvNAZ.mjs.map} +1 -1
  566. package/dist/{widgets-hWcIicX8.mjs → widgets-B_X6_UXJ.mjs} +4 -4
  567. package/dist/{widgets-hWcIicX8.mjs.map → widgets-B_X6_UXJ.mjs.map} +1 -1
  568. package/dist/{wxr-taxonomies-CNFcsNCr.mjs → wxr-taxonomies-oNJB2S81.mjs} +4 -4
  569. package/dist/{wxr-taxonomies-CNFcsNCr.mjs.map → wxr-taxonomies-oNJB2S81.mjs.map} +1 -1
  570. package/dist/{wxr-taxonomies-0_uxSrmf.d.mts → wxr-taxonomies-tADW4KTa.d.mts} +1 -1
  571. package/dist/{wxr-taxonomies-0_uxSrmf.d.mts.map → wxr-taxonomies-tADW4KTa.d.mts.map} +1 -1
  572. package/dist/{zod-generator-CacrR-i8.mjs → zod-generator-CrfV79De.mjs} +3 -3
  573. package/dist/{zod-generator-CacrR-i8.mjs.map → zod-generator-CrfV79De.mjs.map} +1 -1
  574. package/package.json +11 -7
  575. package/src/api/errors.ts +11 -0
  576. package/src/api/handlers/content.ts +111 -1
  577. package/src/api/handlers/dashboard.ts +2 -0
  578. package/src/api/handlers/index.ts +6 -0
  579. package/src/api/handlers/media-usage.ts +80 -0
  580. package/src/api/handlers/relations.ts +474 -0
  581. package/src/api/handlers/taxonomies.ts +26 -4
  582. package/src/api/handlers/validate-media-fields.ts +1 -1
  583. package/src/api/openapi/document.ts +25 -0
  584. package/src/api/parse.ts +1 -1
  585. package/src/api/schemas/comments.ts +6 -0
  586. package/src/api/schemas/content.ts +8 -0
  587. package/src/api/schemas/index.ts +2 -0
  588. package/src/api/schemas/media-usage.ts +48 -0
  589. package/src/api/schemas/relations.ts +109 -0
  590. package/src/api/schemas/search.ts +1 -0
  591. package/src/astro/integration/index.ts +1 -0
  592. package/src/astro/integration/routes.ts +5 -0
  593. package/src/astro/integration/runtime.ts +24 -0
  594. package/src/astro/middleware/request-context.ts +162 -20
  595. package/src/astro/middleware.ts +22 -9
  596. package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -2
  597. package/src/astro/routes/api/admin/comments/index.ts +1 -2
  598. package/src/astro/routes/api/admin/media-usage/repair.ts +24 -0
  599. package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +1 -2
  600. package/src/astro/routes/api/auth/oauth/[provider].ts +1 -2
  601. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +13 -0
  602. package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/children.ts +97 -0
  603. package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/parents.ts +48 -0
  604. package/src/astro/routes/api/relations/[id]/index.ts +84 -0
  605. package/src/astro/routes/api/relations/[id]/translations.ts +33 -0
  606. package/src/astro/routes/api/relations/index.ts +56 -0
  607. package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +1 -7
  608. package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +1 -2
  609. package/src/astro/routes/api/search/index.ts +2 -0
  610. package/src/astro/types.ts +2 -0
  611. package/src/cli/commands/export-seed.ts +1 -10
  612. package/src/client/index.ts +39 -0
  613. package/src/comments/service.ts +1 -1
  614. package/src/comments/turnstile.ts +64 -0
  615. package/src/components/BlockquoteGroup.astro +34 -0
  616. package/src/components/CommentForm.astro +12 -5
  617. package/src/components/EmDashHead.astro +27 -1
  618. package/src/components/InlinePortableTextEditor.tsx +34 -0
  619. package/src/components/PortableText.astro +7 -1
  620. package/src/components/index.ts +2 -0
  621. package/src/components/portable-text-blockquote-group.ts +68 -0
  622. package/src/content/converters/portable-text-to-prosemirror.ts +32 -1
  623. package/src/database/dialect-helpers.ts +34 -0
  624. package/src/database/migrations/039_fix_fts5_triggers.ts +1 -1
  625. package/src/database/migrations/runner.ts +46 -6
  626. package/src/database/pg-migration-lock.ts +72 -0
  627. package/src/database/repositories/comment.ts +1 -1
  628. package/src/database/repositories/content.ts +172 -21
  629. package/src/database/repositories/media.ts +1 -1
  630. package/src/database/repositories/relation.ts +130 -5
  631. package/src/database/repositories/taxonomy.ts +9 -0
  632. package/src/database/repositories/types.ts +8 -0
  633. package/src/db/postgres.ts +5 -2
  634. package/src/emdash-runtime.ts +72 -4
  635. package/src/index.ts +6 -2
  636. package/src/loader.ts +1 -31
  637. package/src/mcp/server.ts +29 -3
  638. package/src/plugins/context.ts +85 -0
  639. package/src/plugins/define-plugin.ts +1 -0
  640. package/src/plugins/index.ts +4 -0
  641. package/src/plugins/manifest-schema.ts +2 -0
  642. package/src/plugins/types.ts +59 -0
  643. package/src/schema/byline-registry.ts +1 -1
  644. package/src/search/fts-manager.ts +34 -0
  645. package/src/search/match.ts +50 -0
  646. package/src/search/query.ts +94 -9
  647. package/src/seo/hreflang.ts +192 -0
  648. package/src/seo/index.ts +3 -0
  649. package/src/taxonomies/index.ts +62 -45
  650. package/src/taxonomies/term-counts.ts +117 -0
  651. package/src/virtual-modules.d.ts +1 -0
  652. package/src/visual-editing/editable.ts +41 -35
  653. package/src/visual-editing/toolbar-bootstrap.ts +120 -0
  654. package/src/visual-editing/toolbar.ts +46 -0
  655. package/dist/api-DxFSorgk.mjs.map +0 -1
  656. package/dist/byline-fields-2Lp5Hbgf.mjs.map +0 -1
  657. package/dist/byline-fields-CHE8UStY.d.mts.map +0 -1
  658. package/dist/content-DhgEth4k.mjs.map +0 -1
  659. package/dist/context-BRIpHARC.mjs.map +0 -1
  660. package/dist/dashboard-EvEKF5en.mjs.map +0 -1
  661. package/dist/dialect-helpers-DRI5pyY3.mjs.map +0 -1
  662. package/dist/error-CkAseSqZ.mjs.map +0 -1
  663. package/dist/index-B9sIxwe4.d.mts.map +0 -1
  664. package/dist/loader-CB6YHeul.mjs.map +0 -1
  665. package/dist/manifest-schema-BHlgZM1W.mjs.map +0 -1
  666. package/dist/media-BrXIaMFN.mjs.map +0 -1
  667. package/dist/menus-b2T4clbO.mjs.map +0 -1
  668. package/dist/query-1Cp6U9oo.mjs.map +0 -1
  669. package/dist/redirects-DXEXBAII.mjs.map +0 -1
  670. package/dist/runner-DvFWoz8-.d.mts.map +0 -1
  671. package/dist/scheduled-publish-Drffrf2a.d.mts.map +0 -1
  672. package/dist/schema-CX9cfNkZ.mjs.map +0 -1
  673. package/dist/search-DZN2HZ-V.mjs.map +0 -1
  674. package/dist/taxonomies-Bs13SN-F.mjs.map +0 -1
  675. package/dist/taxonomies-kH9ABDUF.mjs.map +0 -1
  676. package/dist/types-CGbYowGQ.d.mts.map +0 -1
  677. package/dist/version-qmeO1XZY.mjs +0 -7
  678. /package/dist/{api-tokens-DsvvCtdh.mjs → api-tokens-Dk2GrJMY.mjs} +0 -0
  679. /package/dist/{ssrf-CmuGgCq3.mjs → ssrf-B5DOv5E5.mjs} +0 -0
  680. /package/dist/{types-DROWRxYV.mjs → types-DeCZBZxn.mjs} +0 -0
@@ -1,5 +1,5 @@
1
- import { n as getPublicOrigin } from "../../../../../public-url-Danv47cS.mjs";
2
- import { t as createOAuthStateStore } from "../../../../../oauth-state-store-YLFLcH5F.mjs";
1
+ import { n as getPublicOrigin } from "../../../../../public-url-C0cEvrRu.mjs";
2
+ import { t as createOAuthStateStore } from "../../../../../oauth-state-store-BRSUEkmx.mjs";
3
3
  import { createAuthorizationUrl } from "@emdash-cms/auth";
4
4
 
5
5
  //#region src/astro/routes/api/auth/oauth/[provider].ts
@@ -1 +1 @@
1
- {"version":3,"file":"_provider_.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/auth/oauth/[provider].ts"],"sourcesContent":["/**\n * GET /_emdash/api/auth/oauth/[provider]\n *\n * Start OAuth flow - redirects to provider authorization URL\n */\n\nimport type { APIRoute } from \"astro\";\n\nexport const prerender = false;\n\nimport { createAuthorizationUrl, type OAuthConsumerConfig } from \"@emdash-cms/auth\";\n\nimport { getPublicOrigin } from \"#api/public-url.js\";\nimport { createOAuthStateStore } from \"#auth/oauth-state-store.js\";\n\ntype ProviderName = \"github\" | \"google\";\n\nconst VALID_PROVIDERS = new Set<string>([\"github\", \"google\"]);\n\nfunction isValidProvider(provider: string): provider is ProviderName {\n\treturn VALID_PROVIDERS.has(provider);\n}\n\n/** Safely extract a string value from an env-like record */\nfunction envString(env: Record<string, unknown>, ...keys: string[]): string | undefined {\n\tfor (const key of keys) {\n\t\tconst val = env[key];\n\t\tif (typeof val === \"string\" && val) return val;\n\t}\n\treturn undefined;\n}\n\n/**\n * Get OAuth config from environment variables\n */\nfunction getOAuthConfig(env: Record<string, unknown>): OAuthConsumerConfig[\"providers\"] {\n\tconst providers: OAuthConsumerConfig[\"providers\"] = {};\n\n\t// GitHub\n\tconst githubClientId = envString(env, \"EMDASH_OAUTH_GITHUB_CLIENT_ID\", \"GITHUB_CLIENT_ID\");\n\tconst githubClientSecret = envString(\n\t\tenv,\n\t\t\"EMDASH_OAUTH_GITHUB_CLIENT_SECRET\",\n\t\t\"GITHUB_CLIENT_SECRET\",\n\t);\n\tif (githubClientId && githubClientSecret) {\n\t\tproviders.github = {\n\t\t\tclientId: githubClientId,\n\t\t\tclientSecret: githubClientSecret,\n\t\t};\n\t}\n\n\t// Google\n\tconst googleClientId = envString(env, \"EMDASH_OAUTH_GOOGLE_CLIENT_ID\", \"GOOGLE_CLIENT_ID\");\n\tconst googleClientSecret = envString(\n\t\tenv,\n\t\t\"EMDASH_OAUTH_GOOGLE_CLIENT_SECRET\",\n\t\t\"GOOGLE_CLIENT_SECRET\",\n\t);\n\tif (googleClientId && googleClientSecret) {\n\t\tproviders.google = {\n\t\t\tclientId: googleClientId,\n\t\t\tclientSecret: googleClientSecret,\n\t\t};\n\t}\n\n\treturn providers;\n}\n\nexport const GET: APIRoute = async ({ params, request, locals, redirect }) => {\n\tconst { emdash } = locals;\n\tconst provider = params.provider;\n\n\t// Determine where to redirect errors (setup wizard or login page)\n\tconst referer = request.headers.get(\"referer\") ?? \"\";\n\tconst errorRedirectBase = referer.includes(\"/setup\")\n\t\t? \"/_emdash/admin/setup\"\n\t\t: \"/_emdash/admin/login\";\n\n\t// Validate provider\n\tif (!provider || !isValidProvider(provider)) {\n\t\treturn redirect(\n\t\t\t`${errorRedirectBase}?error=invalid_provider&message=${encodeURIComponent(\"Invalid OAuth provider\")}`,\n\t\t);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn redirect(\n\t\t\t`${errorRedirectBase}?error=server_error&message=${encodeURIComponent(\"Database not configured\")}`,\n\t\t);\n\t}\n\n\ttry {\n\t\tconst url = new URL(request.url);\n\n\t\t// Get OAuth providers from environment\n\t\t// Access via locals.runtime for Cloudflare, or import.meta.env for Node\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- locals.runtime is injected by the Cloudflare adapter at runtime; not declared on App.Locals since the adapter is optional\n\t\tconst runtimeLocals = locals as unknown as { runtime?: { env?: Record<string, unknown> } };\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- import.meta.env is typed as ImportMetaEnv but we need Record<string, unknown> for getOAuthConfig\n\t\tconst env = runtimeLocals.runtime?.env ?? (import.meta.env as Record<string, unknown>);\n\t\tconst providers = getOAuthConfig(env);\n\n\t\tif (!providers[provider]) {\n\t\t\treturn redirect(\n\t\t\t\t`${errorRedirectBase}?error=provider_not_configured&message=${encodeURIComponent(`OAuth provider ${provider} is not configured. Set either EMDASH_OAUTH_${provider.toUpperCase()}_CLIENT_ID and EMDASH_OAUTH_${provider.toUpperCase()}_CLIENT_SECRET, or ${provider.toUpperCase()}_CLIENT_ID and ${provider.toUpperCase()}_CLIENT_SECRET.`)}`,\n\t\t\t);\n\t\t}\n\n\t\tconst config: OAuthConsumerConfig = {\n\t\t\tbaseUrl: `${getPublicOrigin(url, emdash?.config)}/_emdash`,\n\t\t\tproviders,\n\t\t};\n\n\t\tconst stateStore = createOAuthStateStore(emdash.db);\n\n\t\tconst { url: authUrl } = await createAuthorizationUrl(config, provider, stateStore);\n\n\t\treturn redirect(authUrl);\n\t} catch (error) {\n\t\tconsole.error(\"OAuth initiation error:\", error);\n\t\treturn redirect(\n\t\t\t`${errorRedirectBase}?error=oauth_error&message=${encodeURIComponent(\"Failed to start OAuth flow. Please try again.\")}`,\n\t\t);\n\t}\n};\n"],"mappings":";;;;;AAQA,MAAa,YAAY;AASzB,MAAM,kBAAkB,IAAI,IAAY,CAAC,UAAU,SAAS,CAAC;AAE7D,SAAS,gBAAgB,UAA4C;AACpE,QAAO,gBAAgB,IAAI,SAAS;;;AAIrC,SAAS,UAAU,KAA8B,GAAG,MAAoC;AACvF,MAAK,MAAM,OAAO,MAAM;EACvB,MAAM,MAAM,IAAI;AAChB,MAAI,OAAO,QAAQ,YAAY,IAAK,QAAO;;;;;;AAQ7C,SAAS,eAAe,KAAgE;CACvF,MAAM,YAA8C,EAAE;CAGtD,MAAM,iBAAiB,UAAU,KAAK,iCAAiC,mBAAmB;CAC1F,MAAM,qBAAqB,UAC1B,KACA,qCACA,uBACA;AACD,KAAI,kBAAkB,mBACrB,WAAU,SAAS;EAClB,UAAU;EACV,cAAc;EACd;CAIF,MAAM,iBAAiB,UAAU,KAAK,iCAAiC,mBAAmB;CAC1F,MAAM,qBAAqB,UAC1B,KACA,qCACA,uBACA;AACD,KAAI,kBAAkB,mBACrB,WAAU,SAAS;EAClB,UAAU;EACV,cAAc;EACd;AAGF,QAAO;;AAGR,MAAa,MAAgB,OAAO,EAAE,QAAQ,SAAS,QAAQ,eAAe;CAC7E,MAAM,EAAE,WAAW;CACnB,MAAM,WAAW,OAAO;CAIxB,MAAM,qBADU,QAAQ,QAAQ,IAAI,UAAU,IAAI,IAChB,SAAS,SAAS,GACjD,yBACA;AAGH,KAAI,CAAC,YAAY,CAAC,gBAAgB,SAAS,CAC1C,QAAO,SACN,GAAG,kBAAkB,kCAAkC,mBAAmB,yBAAyB,GACnG;AAGF,KAAI,CAAC,QAAQ,GACZ,QAAO,SACN,GAAG,kBAAkB,8BAA8B,mBAAmB,0BAA0B,GAChG;AAGF,KAAI;EACH,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;EAQhC,MAAM,YAAY,eAHI,OAEI,SAAS,OAAQ,OAAO,KAAK,IAClB;AAErC,MAAI,CAAC,UAAU,UACd,QAAO,SACN,GAAG,kBAAkB,yCAAyC,mBAAmB,kBAAkB,SAAS,8CAA8C,SAAS,aAAa,CAAC,8BAA8B,SAAS,aAAa,CAAC,qBAAqB,SAAS,aAAa,CAAC,iBAAiB,SAAS,aAAa,CAAC,iBAAiB,GAC3U;EAUF,MAAM,EAAE,KAAK,YAAY,MAAM,uBAPK;GACnC,SAAS,GAAG,gBAAgB,KAAK,QAAQ,OAAO,CAAC;GACjD;GACA,EAI6D,UAF3C,sBAAsB,OAAO,GAAG,CAEgC;AAEnF,SAAO,SAAS,QAAQ;UAChB,OAAO;AACf,UAAQ,MAAM,2BAA2B,MAAM;AAC/C,SAAO,SACN,GAAG,kBAAkB,6BAA6B,mBAAmB,gDAAgD,GACrH"}
1
+ {"version":3,"file":"_provider_.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/auth/oauth/[provider].ts"],"sourcesContent":["/**\n * GET /_emdash/api/auth/oauth/[provider]\n *\n * Start OAuth flow - redirects to provider authorization URL\n */\n\nimport type { APIRoute } from \"astro\";\n\nexport const prerender = false;\n\nimport { createAuthorizationUrl, type OAuthConsumerConfig } from \"@emdash-cms/auth\";\n\nimport { getPublicOrigin } from \"#api/public-url.js\";\nimport { createOAuthStateStore } from \"#auth/oauth-state-store.js\";\n\ntype ProviderName = \"github\" | \"google\";\n\nconst VALID_PROVIDERS = new Set<string>([\"github\", \"google\"]);\n\nfunction isValidProvider(provider: string): provider is ProviderName {\n\treturn VALID_PROVIDERS.has(provider);\n}\n\n/** Safely extract a string value from an env-like record */\nfunction envString(env: Record<string, unknown>, ...keys: string[]): string | undefined {\n\tfor (const key of keys) {\n\t\tconst val = env[key];\n\t\tif (typeof val === \"string\" && val) return val;\n\t}\n\treturn undefined;\n}\n\n/**\n * Get OAuth config from environment variables\n */\nfunction getOAuthConfig(env: Record<string, unknown>): OAuthConsumerConfig[\"providers\"] {\n\tconst providers: OAuthConsumerConfig[\"providers\"] = {};\n\n\t// GitHub\n\tconst githubClientId = envString(env, \"EMDASH_OAUTH_GITHUB_CLIENT_ID\", \"GITHUB_CLIENT_ID\");\n\tconst githubClientSecret = envString(\n\t\tenv,\n\t\t\"EMDASH_OAUTH_GITHUB_CLIENT_SECRET\",\n\t\t\"GITHUB_CLIENT_SECRET\",\n\t);\n\tif (githubClientId && githubClientSecret) {\n\t\tproviders.github = {\n\t\t\tclientId: githubClientId,\n\t\t\tclientSecret: githubClientSecret,\n\t\t};\n\t}\n\n\t// Google\n\tconst googleClientId = envString(env, \"EMDASH_OAUTH_GOOGLE_CLIENT_ID\", \"GOOGLE_CLIENT_ID\");\n\tconst googleClientSecret = envString(\n\t\tenv,\n\t\t\"EMDASH_OAUTH_GOOGLE_CLIENT_SECRET\",\n\t\t\"GOOGLE_CLIENT_SECRET\",\n\t);\n\tif (googleClientId && googleClientSecret) {\n\t\tproviders.google = {\n\t\t\tclientId: googleClientId,\n\t\t\tclientSecret: googleClientSecret,\n\t\t};\n\t}\n\n\treturn providers;\n}\n\nexport const GET: APIRoute = async ({ params, request, locals, redirect }) => {\n\tconst { emdash } = locals;\n\tconst provider = params.provider;\n\n\t// Determine where to redirect errors (setup wizard or login page)\n\tconst referer = request.headers.get(\"referer\") ?? \"\";\n\tconst errorRedirectBase = referer.includes(\"/setup\")\n\t\t? \"/_emdash/admin/setup\"\n\t\t: \"/_emdash/admin/login\";\n\n\t// Validate provider\n\tif (!provider || !isValidProvider(provider)) {\n\t\treturn redirect(\n\t\t\t`${errorRedirectBase}?error=invalid_provider&message=${encodeURIComponent(\"Invalid OAuth provider\")}`,\n\t\t);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn redirect(\n\t\t\t`${errorRedirectBase}?error=server_error&message=${encodeURIComponent(\"Database not configured\")}`,\n\t\t);\n\t}\n\n\ttry {\n\t\tconst url = new URL(request.url);\n\n\t\t// Get OAuth providers from environment\n\t\t// Access via locals.runtime for Cloudflare, or import.meta.env for Node\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- locals.runtime is injected by the Cloudflare adapter at runtime; not declared on App.Locals since the adapter is optional\n\t\tconst runtimeLocals = locals as unknown as { runtime?: { env?: Record<string, unknown> } };\n\t\tconst env = runtimeLocals.runtime?.env ?? import.meta.env;\n\t\tconst providers = getOAuthConfig(env);\n\n\t\tif (!providers[provider]) {\n\t\t\treturn redirect(\n\t\t\t\t`${errorRedirectBase}?error=provider_not_configured&message=${encodeURIComponent(`OAuth provider ${provider} is not configured. Set either EMDASH_OAUTH_${provider.toUpperCase()}_CLIENT_ID and EMDASH_OAUTH_${provider.toUpperCase()}_CLIENT_SECRET, or ${provider.toUpperCase()}_CLIENT_ID and ${provider.toUpperCase()}_CLIENT_SECRET.`)}`,\n\t\t\t);\n\t\t}\n\n\t\tconst config: OAuthConsumerConfig = {\n\t\t\tbaseUrl: `${getPublicOrigin(url, emdash?.config)}/_emdash`,\n\t\t\tproviders,\n\t\t};\n\n\t\tconst stateStore = createOAuthStateStore(emdash.db);\n\n\t\tconst { url: authUrl } = await createAuthorizationUrl(config, provider, stateStore);\n\n\t\treturn redirect(authUrl);\n\t} catch (error) {\n\t\tconsole.error(\"OAuth initiation error:\", error);\n\t\treturn redirect(\n\t\t\t`${errorRedirectBase}?error=oauth_error&message=${encodeURIComponent(\"Failed to start OAuth flow. Please try again.\")}`,\n\t\t);\n\t}\n};\n"],"mappings":";;;;;AAQA,MAAa,YAAY;AASzB,MAAM,kBAAkB,IAAI,IAAY,CAAC,UAAU,SAAS,CAAC;AAE7D,SAAS,gBAAgB,UAA4C;AACpE,QAAO,gBAAgB,IAAI,SAAS;;;AAIrC,SAAS,UAAU,KAA8B,GAAG,MAAoC;AACvF,MAAK,MAAM,OAAO,MAAM;EACvB,MAAM,MAAM,IAAI;AAChB,MAAI,OAAO,QAAQ,YAAY,IAAK,QAAO;;;;;;AAQ7C,SAAS,eAAe,KAAgE;CACvF,MAAM,YAA8C,EAAE;CAGtD,MAAM,iBAAiB,UAAU,KAAK,iCAAiC,mBAAmB;CAC1F,MAAM,qBAAqB,UAC1B,KACA,qCACA,uBACA;AACD,KAAI,kBAAkB,mBACrB,WAAU,SAAS;EAClB,UAAU;EACV,cAAc;EACd;CAIF,MAAM,iBAAiB,UAAU,KAAK,iCAAiC,mBAAmB;CAC1F,MAAM,qBAAqB,UAC1B,KACA,qCACA,uBACA;AACD,KAAI,kBAAkB,mBACrB,WAAU,SAAS;EAClB,UAAU;EACV,cAAc;EACd;AAGF,QAAO;;AAGR,MAAa,MAAgB,OAAO,EAAE,QAAQ,SAAS,QAAQ,eAAe;CAC7E,MAAM,EAAE,WAAW;CACnB,MAAM,WAAW,OAAO;CAIxB,MAAM,qBADU,QAAQ,QAAQ,IAAI,UAAU,IAAI,IAChB,SAAS,SAAS,GACjD,yBACA;AAGH,KAAI,CAAC,YAAY,CAAC,gBAAgB,SAAS,CAC1C,QAAO,SACN,GAAG,kBAAkB,kCAAkC,mBAAmB,yBAAyB,GACnG;AAGF,KAAI,CAAC,QAAQ,GACZ,QAAO,SACN,GAAG,kBAAkB,8BAA8B,mBAAmB,0BAA0B,GAChG;AAGF,KAAI;EACH,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;EAOhC,MAAM,YAAY,eAFI,OACI,SAAS,OAAO,OAAO,KAAK,IACjB;AAErC,MAAI,CAAC,UAAU,UACd,QAAO,SACN,GAAG,kBAAkB,yCAAyC,mBAAmB,kBAAkB,SAAS,8CAA8C,SAAS,aAAa,CAAC,8BAA8B,SAAS,aAAa,CAAC,qBAAqB,SAAS,aAAa,CAAC,iBAAiB,SAAS,aAAa,CAAC,iBAAiB,GAC3U;EAUF,MAAM,EAAE,KAAK,YAAY,MAAM,uBAPK;GACnC,SAAS,GAAG,gBAAgB,KAAK,QAAQ,OAAO,CAAC;GACjD;GACA,EAI6D,UAF3C,sBAAsB,OAAO,GAAG,CAEgC;AAEnF,SAAO,SAAS,QAAQ;UAChB,OAAO;AACf,UAAQ,MAAM,2BAA2B,MAAM;AAC/C,SAAO,SACN,GAAG,kBAAkB,6BAA6B,mBAAmB,gDAAgD,GACrH"}
@@ -1,10 +1,10 @@
1
- import "../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../types-CuW0zWDA.mjs";
3
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CkAseSqZ.mjs";
4
- import { n as parseBody, t as isParseError } from "../../../../../parse-GmjecGct.mjs";
5
- import "../../../../../redirects-DXEXBAII.mjs";
6
- import { E as passkeyRenameBody } from "../../../../../byline-fields-2Lp5Hbgf.mjs";
7
- import "../../../../../status-CqJQX1QN.mjs";
1
+ import "../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../types-Ch8pFMEm.mjs";
3
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CfJMLSkd.mjs";
4
+ import { n as parseBody, t as isParseError } from "../../../../../parse-GLksnn-B.mjs";
5
+ import "../../../../../redirects-CeD7K7EX.mjs";
6
+ import { I as passkeyRenameBody } from "../../../../../relations-BmBmX4Eb.mjs";
7
+ import "../../../../../status-D3Gy1Zc6.mjs";
8
8
  import "../../../../../api/schemas/index.mjs";
9
9
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
10
10
 
@@ -1,6 +1,6 @@
1
- import "../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../types-CuW0zWDA.mjs";
3
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CkAseSqZ.mjs";
1
+ import "../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../types-Ch8pFMEm.mjs";
3
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CfJMLSkd.mjs";
4
4
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
5
5
 
6
6
  //#region src/astro/routes/api/auth/passkey/index.ts
@@ -1,17 +1,17 @@
1
- import "../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../types-CuW0zWDA.mjs";
3
- import { t as OptionsRepository } from "../../../../../options-DTTML-Tx.mjs";
4
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CkAseSqZ.mjs";
5
- import { r as parseOptionalBody, t as isParseError } from "../../../../../parse-GmjecGct.mjs";
6
- import "../../../../../redirects-DXEXBAII.mjs";
7
- import { C as passkeyOptionsBody } from "../../../../../byline-fields-2Lp5Hbgf.mjs";
8
- import "../../../../../status-CqJQX1QN.mjs";
1
+ import "../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../types-Ch8pFMEm.mjs";
3
+ import { t as OptionsRepository } from "../../../../../options-BlmBHTvX.mjs";
4
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CfJMLSkd.mjs";
5
+ import { r as parseOptionalBody, t as isParseError } from "../../../../../parse-GLksnn-B.mjs";
6
+ import "../../../../../redirects-CeD7K7EX.mjs";
7
+ import { N as passkeyOptionsBody } from "../../../../../relations-BmBmX4Eb.mjs";
8
+ import "../../../../../status-D3Gy1Zc6.mjs";
9
9
  import "../../../../../api/schemas/index.mjs";
10
- import { t as getTrustedProxyHeaders } from "../../../../../trusted-proxy-VTPRiX2s.mjs";
11
- import { n as getPublicOrigin } from "../../../../../public-url-Danv47cS.mjs";
12
- import { n as createChallengeStore, t as cleanupExpiredChallenges } from "../../../../../challenge-store-anhwVER0.mjs";
13
- import { t as getPasskeyConfig } from "../../../../../passkey-config-De0ahnRg.mjs";
14
- import { n as getClientIp, r as rateLimitResponse, t as checkRateLimit } from "../../../../../rate-limit-C_Lf63Dd.mjs";
10
+ import { t as getTrustedProxyHeaders } from "../../../../../trusted-proxy-CSgZVESw.mjs";
11
+ import { n as getPublicOrigin } from "../../../../../public-url-C0cEvrRu.mjs";
12
+ import { n as createChallengeStore, t as cleanupExpiredChallenges } from "../../../../../challenge-store-B426wQ6O.mjs";
13
+ import { t as getPasskeyConfig } from "../../../../../passkey-config-CjA1UbDj.mjs";
14
+ import { n as getClientIp, r as rateLimitResponse, t as checkRateLimit } from "../../../../../rate-limit-Cg4Rvc_-.mjs";
15
15
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
16
16
  import { generateAuthenticationOptions } from "@emdash-cms/auth/passkey";
17
17
 
@@ -1,15 +1,15 @@
1
- import "../../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../../types-CuW0zWDA.mjs";
3
- import { t as OptionsRepository } from "../../../../../../options-DTTML-Tx.mjs";
4
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
5
- import { r as parseOptionalBody, t as isParseError } from "../../../../../../parse-GmjecGct.mjs";
6
- import "../../../../../../redirects-DXEXBAII.mjs";
7
- import { w as passkeyRegisterOptionsBody } from "../../../../../../byline-fields-2Lp5Hbgf.mjs";
8
- import "../../../../../../status-CqJQX1QN.mjs";
1
+ import "../../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../../types-Ch8pFMEm.mjs";
3
+ import { t as OptionsRepository } from "../../../../../../options-BlmBHTvX.mjs";
4
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
5
+ import { r as parseOptionalBody, t as isParseError } from "../../../../../../parse-GLksnn-B.mjs";
6
+ import "../../../../../../redirects-CeD7K7EX.mjs";
7
+ import { P as passkeyRegisterOptionsBody } from "../../../../../../relations-BmBmX4Eb.mjs";
8
+ import "../../../../../../status-D3Gy1Zc6.mjs";
9
9
  import "../../../../../../api/schemas/index.mjs";
10
- import { n as getPublicOrigin } from "../../../../../../public-url-Danv47cS.mjs";
11
- import { n as createChallengeStore } from "../../../../../../challenge-store-anhwVER0.mjs";
12
- import { t as getPasskeyConfig } from "../../../../../../passkey-config-De0ahnRg.mjs";
10
+ import { n as getPublicOrigin } from "../../../../../../public-url-C0cEvrRu.mjs";
11
+ import { n as createChallengeStore } from "../../../../../../challenge-store-B426wQ6O.mjs";
12
+ import { t as getPasskeyConfig } from "../../../../../../passkey-config-CjA1UbDj.mjs";
13
13
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
14
14
  import { generateRegistrationOptions } from "@emdash-cms/auth/passkey";
15
15
 
@@ -1,16 +1,16 @@
1
- import "../../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../../types-CuW0zWDA.mjs";
3
- import { t as OptionsRepository } from "../../../../../../options-DTTML-Tx.mjs";
4
- import { n as apiSuccess, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
5
- import { n as parseBody, t as isParseError } from "../../../../../../parse-GmjecGct.mjs";
6
- import "../../../../../../redirects-DXEXBAII.mjs";
7
- import { T as passkeyRegisterVerifyBody } from "../../../../../../byline-fields-2Lp5Hbgf.mjs";
8
- import "../../../../../../status-CqJQX1QN.mjs";
1
+ import "../../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../../types-Ch8pFMEm.mjs";
3
+ import { t as OptionsRepository } from "../../../../../../options-BlmBHTvX.mjs";
4
+ import { n as apiSuccess, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
5
+ import { n as parseBody, t as isParseError } from "../../../../../../parse-GLksnn-B.mjs";
6
+ import "../../../../../../redirects-CeD7K7EX.mjs";
7
+ import { F as passkeyRegisterVerifyBody } from "../../../../../../relations-BmBmX4Eb.mjs";
8
+ import "../../../../../../status-D3Gy1Zc6.mjs";
9
9
  import "../../../../../../api/schemas/index.mjs";
10
- import { n as getPublicOrigin } from "../../../../../../public-url-Danv47cS.mjs";
11
- import { n as validateAllowedOrigins, t as getConfiguredAllowedOrigins } from "../../../../../../allowed-origins-CRsJ6BPM.mjs";
12
- import { n as createChallengeStore } from "../../../../../../challenge-store-anhwVER0.mjs";
13
- import { t as getPasskeyConfig } from "../../../../../../passkey-config-De0ahnRg.mjs";
10
+ import { n as getPublicOrigin } from "../../../../../../public-url-C0cEvrRu.mjs";
11
+ import { n as validateAllowedOrigins, t as getConfiguredAllowedOrigins } from "../../../../../../allowed-origins-FXB-FplB.mjs";
12
+ import { n as createChallengeStore } from "../../../../../../challenge-store-B426wQ6O.mjs";
13
+ import { t as getPasskeyConfig } from "../../../../../../passkey-config-CjA1UbDj.mjs";
14
14
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
15
15
  import { registerPasskey, verifyRegistrationResponse } from "@emdash-cms/auth/passkey";
16
16
 
@@ -1,16 +1,16 @@
1
- import "../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../types-CuW0zWDA.mjs";
3
- import { t as OptionsRepository } from "../../../../../options-DTTML-Tx.mjs";
4
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CkAseSqZ.mjs";
5
- import { n as parseBody, t as isParseError } from "../../../../../parse-GmjecGct.mjs";
6
- import "../../../../../redirects-DXEXBAII.mjs";
7
- import { D as passkeyVerifyBody } from "../../../../../byline-fields-2Lp5Hbgf.mjs";
8
- import "../../../../../status-CqJQX1QN.mjs";
1
+ import "../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../types-Ch8pFMEm.mjs";
3
+ import { t as OptionsRepository } from "../../../../../options-BlmBHTvX.mjs";
4
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CfJMLSkd.mjs";
5
+ import { n as parseBody, t as isParseError } from "../../../../../parse-GLksnn-B.mjs";
6
+ import "../../../../../redirects-CeD7K7EX.mjs";
7
+ import { L as passkeyVerifyBody } from "../../../../../relations-BmBmX4Eb.mjs";
8
+ import "../../../../../status-D3Gy1Zc6.mjs";
9
9
  import "../../../../../api/schemas/index.mjs";
10
- import { n as getPublicOrigin } from "../../../../../public-url-Danv47cS.mjs";
11
- import { n as validateAllowedOrigins, t as getConfiguredAllowedOrigins } from "../../../../../allowed-origins-CRsJ6BPM.mjs";
12
- import { n as createChallengeStore } from "../../../../../challenge-store-anhwVER0.mjs";
13
- import { t as getPasskeyConfig } from "../../../../../passkey-config-De0ahnRg.mjs";
10
+ import { n as getPublicOrigin } from "../../../../../public-url-C0cEvrRu.mjs";
11
+ import { n as validateAllowedOrigins, t as getConfiguredAllowedOrigins } from "../../../../../allowed-origins-FXB-FplB.mjs";
12
+ import { n as createChallengeStore } from "../../../../../challenge-store-B426wQ6O.mjs";
13
+ import { t as getPasskeyConfig } from "../../../../../passkey-config-CjA1UbDj.mjs";
14
14
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
15
15
  import { PasskeyAuthenticationError, authenticateWithPasskey } from "@emdash-cms/auth/passkey";
16
16
 
@@ -1,16 +1,16 @@
1
- import "../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../types-CuW0zWDA.mjs";
3
- import { t as OptionsRepository } from "../../../../../options-DTTML-Tx.mjs";
4
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CkAseSqZ.mjs";
5
- import { n as parseBody, t as isParseError } from "../../../../../parse-GmjecGct.mjs";
6
- import "../../../../../redirects-DXEXBAII.mjs";
7
- import { O as signupCompleteBody } from "../../../../../byline-fields-2Lp5Hbgf.mjs";
8
- import "../../../../../status-CqJQX1QN.mjs";
1
+ import "../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../types-Ch8pFMEm.mjs";
3
+ import { t as OptionsRepository } from "../../../../../options-BlmBHTvX.mjs";
4
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CfJMLSkd.mjs";
5
+ import { n as parseBody, t as isParseError } from "../../../../../parse-GLksnn-B.mjs";
6
+ import "../../../../../redirects-CeD7K7EX.mjs";
7
+ import { R as signupCompleteBody } from "../../../../../relations-BmBmX4Eb.mjs";
8
+ import "../../../../../status-D3Gy1Zc6.mjs";
9
9
  import "../../../../../api/schemas/index.mjs";
10
- import { n as getPublicOrigin } from "../../../../../public-url-Danv47cS.mjs";
11
- import { n as validateAllowedOrigins, t as getConfiguredAllowedOrigins } from "../../../../../allowed-origins-CRsJ6BPM.mjs";
12
- import { n as createChallengeStore } from "../../../../../challenge-store-anhwVER0.mjs";
13
- import { t as getPasskeyConfig } from "../../../../../passkey-config-De0ahnRg.mjs";
10
+ import { n as getPublicOrigin } from "../../../../../public-url-C0cEvrRu.mjs";
11
+ import { n as validateAllowedOrigins, t as getConfiguredAllowedOrigins } from "../../../../../allowed-origins-FXB-FplB.mjs";
12
+ import { n as createChallengeStore } from "../../../../../challenge-store-B426wQ6O.mjs";
13
+ import { t as getPasskeyConfig } from "../../../../../passkey-config-CjA1UbDj.mjs";
14
14
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
15
15
  import { SignupError, completeSignup } from "@emdash-cms/auth";
16
16
  import { registerPasskey, verifyRegistrationResponse } from "@emdash-cms/auth/passkey";
@@ -1,15 +1,15 @@
1
- import "../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../types-CuW0zWDA.mjs";
3
- import { t as OptionsRepository } from "../../../../../options-DTTML-Tx.mjs";
4
- import { n as apiSuccess, t as apiError } from "../../../../../error-CkAseSqZ.mjs";
5
- import { n as parseBody, t as isParseError } from "../../../../../parse-GmjecGct.mjs";
6
- import "../../../../../redirects-DXEXBAII.mjs";
7
- import { k as signupRequestBody } from "../../../../../byline-fields-2Lp5Hbgf.mjs";
8
- import "../../../../../status-CqJQX1QN.mjs";
1
+ import "../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../types-Ch8pFMEm.mjs";
3
+ import { t as OptionsRepository } from "../../../../../options-BlmBHTvX.mjs";
4
+ import { n as apiSuccess, t as apiError } from "../../../../../error-CfJMLSkd.mjs";
5
+ import { n as parseBody, t as isParseError } from "../../../../../parse-GLksnn-B.mjs";
6
+ import "../../../../../redirects-CeD7K7EX.mjs";
7
+ import { z as signupRequestBody } from "../../../../../relations-BmBmX4Eb.mjs";
8
+ import "../../../../../status-D3Gy1Zc6.mjs";
9
9
  import "../../../../../api/schemas/index.mjs";
10
- import { t as getTrustedProxyHeaders } from "../../../../../trusted-proxy-VTPRiX2s.mjs";
11
- import { t as getSiteBaseUrl } from "../../../../../site-url-BBfDTgZB.mjs";
12
- import { n as getClientIp, t as checkRateLimit } from "../../../../../rate-limit-C_Lf63Dd.mjs";
10
+ import { t as getTrustedProxyHeaders } from "../../../../../trusted-proxy-CSgZVESw.mjs";
11
+ import { t as getSiteBaseUrl } from "../../../../../site-url-CcLy5D5s.mjs";
12
+ import { n as getClientIp, t as checkRateLimit } from "../../../../../rate-limit-Cg4Rvc_-.mjs";
13
13
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
14
14
  import { requestSignup } from "@emdash-cms/auth";
15
15
 
@@ -1,6 +1,6 @@
1
- import "../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../types-CuW0zWDA.mjs";
3
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CkAseSqZ.mjs";
1
+ import "../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../types-Ch8pFMEm.mjs";
3
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-CfJMLSkd.mjs";
4
4
  import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
5
5
  import { SignupError, roleFromLevel, validateSignupToken } from "@emdash-cms/auth";
6
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../src/astro/routes/api/comments/[collection]/[contentId]/index.ts"],"mappings":";;;cAsBa,SAAA;AAKb;;;AAAA,cAAa,GAAA,EAAK,QAAA;;AA8ClB;;cAAa,IAAA,EAAM,QAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../src/astro/routes/api/comments/[collection]/[contentId]/index.ts"],"mappings":";;;cAuBa,SAAA;AAKb;;;AAAA,cAAa,GAAA,EAAK,QAAA;;AA8ClB;;cAAa,IAAA,EAAM,QAAA"}
@@ -1,22 +1,73 @@
1
1
  import { t as validateIdentifier } from "../../../../../../validate-VPnKoIzW.mjs";
2
2
  import "../../../../../../after-B1IIdH3Y.mjs";
3
3
  import "../../../../../../object-cache-Bok5j2ae.mjs";
4
- import "../../../../../../base64-CmWvODNW.mjs";
5
- import "../../../../../../types-CuW0zWDA.mjs";
6
- import { t as CommentRepository } from "../../../../../../comment-CzeatwBV.mjs";
7
- import "../../../../../../options-DTTML-Tx.mjs";
8
- import { a as unwrapResult, i as requireDb, n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
9
- import { n as parseBody, t as isParseError } from "../../../../../../parse-GmjecGct.mjs";
10
- import { yt as createCommentBody } from "../../../../../../redirects-DXEXBAII.mjs";
11
- import "../../../../../../byline-fields-2Lp5Hbgf.mjs";
12
- import "../../../../../../status-CqJQX1QN.mjs";
4
+ import "../../../../../../base64-C74Msx8h.mjs";
5
+ import "../../../../../../types-Ch8pFMEm.mjs";
6
+ import { t as CommentRepository } from "../../../../../../comment-SWh31kS0.mjs";
7
+ import "../../../../../../options-BlmBHTvX.mjs";
8
+ import { a as unwrapResult, i as requireDb, n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
9
+ import { n as parseBody, t as isParseError } from "../../../../../../parse-GLksnn-B.mjs";
10
+ import { yt as createCommentBody } from "../../../../../../redirects-CeD7K7EX.mjs";
11
+ import "../../../../../../relations-BmBmX4Eb.mjs";
12
+ import "../../../../../../status-D3Gy1Zc6.mjs";
13
13
  import "../../../../../../api/schemas/index.mjs";
14
- import { t as extractRequestMeta } from "../../../../../../request-meta-DbvXaAvU.mjs";
15
- import { i as resolveSecretsCached } from "../../../../../../secrets-kp760LlD.mjs";
16
- import { c as hashIp, s as handleCommentList, t as checkRateLimit } from "../../../../../../comments-BLE-jv5k.mjs";
17
- import { t as getSiteBaseUrl } from "../../../../../../site-url-BBfDTgZB.mjs";
18
- import { i as sendCommentNotification, t as createComment } from "../../../../../../service-DQmiBMzt.mjs";
14
+ import { t as extractRequestMeta } from "../../../../../../request-meta-DCwktC5P.mjs";
15
+ import { i as resolveSecretsCached } from "../../../../../../secrets-CO_bEmII.mjs";
16
+ import { c as hashIp, s as handleCommentList, t as checkRateLimit } from "../../../../../../comments-kZYGppMS.mjs";
17
+ import { t as getSiteBaseUrl } from "../../../../../../site-url-CcLy5D5s.mjs";
18
+ import { i as sendCommentNotification, t as createComment } from "../../../../../../service-D6zDmKXy.mjs";
19
19
 
20
+ //#region src/comments/turnstile.ts
21
+ /**
22
+ * Server-side Turnstile verification for public comment submissions.
23
+ *
24
+ * The comment form widget (`CommentForm.astro`) submits a `turnstileToken`;
25
+ * this verifies it against Cloudflare's siteverify API. Enforcement is
26
+ * opt-in: it only runs when the operator configures the Turnstile secret
27
+ * key (`EMDASH_TURNSTILE_SECRET_KEY` or `TURNSTILE_SECRET_KEY`), so
28
+ * existing non-Turnstile sites are unaffected.
29
+ *
30
+ * Mirrors `verifyTurnstile` in `@emdash-cms/plugin-forms` (not imported —
31
+ * core doesn't depend on plugin packages).
32
+ */
33
+ const SITEVERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify";
34
+ /**
35
+ * Resolve the configured Turnstile secret key, or `""` when Turnstile
36
+ * enforcement is not configured.
37
+ */
38
+ function getTurnstileSecretKey() {
39
+ return import.meta.env.EMDASH_TURNSTILE_SECRET_KEY || import.meta.env.TURNSTILE_SECRET_KEY || "";
40
+ }
41
+ /**
42
+ * Verify a Turnstile response token via siteverify.
43
+ *
44
+ * Fails closed: a missing token, a failed verification, and a siteverify
45
+ * transport error all return `false` — when the operator has configured a
46
+ * secret, an unverifiable submission must not be persisted.
47
+ */
48
+ async function verifyTurnstileToken(token, secretKey, remoteIp) {
49
+ if (!token) return false;
50
+ const body = {
51
+ secret: secretKey,
52
+ response: token
53
+ };
54
+ if (remoteIp) body.remoteip = remoteIp;
55
+ try {
56
+ const data = await (await fetch(SITEVERIFY_URL, {
57
+ method: "POST",
58
+ headers: { "Content-Type": "application/json" },
59
+ body: JSON.stringify(body),
60
+ signal: AbortSignal.timeout(1e4)
61
+ })).json();
62
+ if (!data.success) console.warn("[comments] Turnstile verification failed:", data["error-codes"] ?? []);
63
+ return data.success === true;
64
+ } catch (error) {
65
+ console.error("[comments] Turnstile siteverify request failed:", error instanceof Error ? error.message : error);
66
+ return false;
67
+ }
68
+ }
69
+
70
+ //#endregion
20
71
  //#region src/astro/routes/api/comments/[collection]/[contentId]/index.ts
21
72
  const prerender = false;
22
73
  /**
@@ -91,6 +142,10 @@ const POST = async ({ params, request, locals }) => {
91
142
  else ipHash = "unknown";
92
143
  const unknownBucketLimit = ipHash === "unknown" ? 20 : void 0;
93
144
  if (await checkRateLimit(emdash.db, ipHash, unknownBucketLimit)) return apiError("RATE_LIMITED", "Too many comments. Please try again later.", 429);
145
+ const turnstileSecretKey = getTurnstileSecretKey();
146
+ if (turnstileSecretKey) {
147
+ if (!await verifyTurnstileToken(body.turnstileToken, turnstileSecretKey, meta.ip)) return apiError("TURNSTILE_FAILED", "CAPTCHA verification failed", 403);
148
+ }
94
149
  const collectionSettings = {
95
150
  commentsEnabled: collectionRow.comments_enabled === 1,
96
151
  commentsModeration: collectionRow.comments_moderation,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../src/astro/routes/api/comments/[collection]/[contentId]/index.ts"],"sourcesContent":["/**\n * Public comment endpoints\n *\n * GET /_emdash/api/comments/:collection/:contentId - List approved comments\n * POST /_emdash/api/comments/:collection/:contentId - Submit a comment\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { apiError, apiSuccess, handleError, requireDb, unwrapResult } from \"#api/error.js\";\nimport { handleCommentList, checkRateLimit, hashIp } from \"#api/handlers/comments.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { createCommentBody } from \"#api/schemas.js\";\nimport { getSiteBaseUrl } from \"#api/site-url.js\";\nimport { sendCommentNotification } from \"#comments/notifications.js\";\nimport { createComment, type CommentHookRunner } from \"#comments/service.js\";\nimport { resolveSecretsCached } from \"#config/secrets.js\";\nimport { CommentRepository } from \"#db/repositories/comment.js\";\nimport { validateIdentifier } from \"#db/validate.js\";\nimport { extractRequestMeta } from \"#plugins/request-meta.js\";\nimport type { CollectionCommentSettings, ModerationDecision } from \"#plugins/types.js\";\n\nexport const prerender = false;\n\n/**\n * List approved comments for a content item (public, no auth required)\n */\nexport const GET: APIRoute = async ({ params, url, locals }) => {\n\tconst { emdash } = locals;\n\tconst { collection, contentId } = params;\n\n\tif (!collection || !contentId) {\n\t\treturn apiError(\"VALIDATION_ERROR\", \"Collection and content ID required\", 400);\n\t}\n\n\tconst dbErr = requireDb(emdash?.db);\n\tif (dbErr) return dbErr;\n\n\ttry {\n\t\tconst limit = Math.min(Number(url.searchParams.get(\"limit\") || 50), 100);\n\t\tconst cursor = url.searchParams.get(\"cursor\") ?? undefined;\n\t\tconst threaded = url.searchParams.get(\"threaded\") === \"true\";\n\n\t\t// Check collection exists and has comments enabled\n\t\tconst collectionRow = await emdash.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.select([\"comments_enabled\"])\n\t\t\t.where(\"slug\", \"=\", collection)\n\t\t\t.executeTakeFirst();\n\n\t\tif (!collectionRow) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Collection '${collection}' not found`, 404);\n\t\t}\n\n\t\tif (!collectionRow.comments_enabled) {\n\t\t\treturn apiError(\"COMMENTS_DISABLED\", \"Comments are not enabled for this collection\", 403);\n\t\t}\n\n\t\tconst result = await handleCommentList(emdash.db, collection, contentId, {\n\t\t\tlimit,\n\t\t\tcursor,\n\t\t\tthreaded,\n\t\t});\n\n\t\treturn unwrapResult(result);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to list comments\", \"COMMENT_LIST_ERROR\");\n\t}\n};\n\n/**\n * Submit a comment (public, gated by anti-spam checks)\n */\nexport const POST: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { collection, contentId } = params;\n\n\tif (!collection || !contentId) {\n\t\treturn apiError(\"VALIDATION_ERROR\", \"Collection and content ID required\", 400);\n\t}\n\n\tconst dbErr = requireDb(emdash?.db);\n\tif (dbErr) return dbErr;\n\n\ttry {\n\t\t// Parse and validate input\n\t\tconst body = await parseBody(request, createCommentBody);\n\t\tif (isParseError(body)) return body;\n\n\t\t// Check collection exists and has comments enabled\n\t\tconst collectionRow = await emdash.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.select([\n\t\t\t\t\"comments_enabled\",\n\t\t\t\t\"comments_moderation\",\n\t\t\t\t\"comments_closed_after_days\",\n\t\t\t\t\"comments_auto_approve_users\",\n\t\t\t])\n\t\t\t.where(\"slug\", \"=\", collection)\n\t\t\t.executeTakeFirst();\n\n\t\tif (!collectionRow) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Collection '${collection}' not found`, 404);\n\t\t}\n\n\t\tif (!collectionRow.comments_enabled) {\n\t\t\treturn apiError(\"COMMENTS_DISABLED\", \"Comments are not enabled for this collection\", 403);\n\t\t}\n\n\t\t// Verify the content item exists, is published, and not soft-deleted\n\t\tvalidateIdentifier(collection, \"collection\");\n\t\tconst contentRow = await emdash.db\n\t\t\t.selectFrom(`ec_${collection}` as never)\n\t\t\t.select([\"id\" as never, \"slug\" as never, \"author_id\" as never, \"published_at\" as never])\n\t\t\t.where(\"id\" as never, \"=\", contentId as never)\n\t\t\t.where(\"status\" as never, \"=\", \"published\" as never)\n\t\t\t.where(\"deleted_at\" as never, \"is\", null as never)\n\t\t\t.executeTakeFirst();\n\n\t\tif (!contentRow) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Content not found\", 404);\n\t\t}\n\n\t\t// Check if comments are closed (published_at + closed_after_days)\n\t\tif (collectionRow.comments_closed_after_days > 0) {\n\t\t\tconst publishedAt = (contentRow as { published_at: string | null }).published_at;\n\t\t\tif (publishedAt) {\n\t\t\t\tconst closedDate = new Date(publishedAt);\n\t\t\t\tclosedDate.setDate(closedDate.getDate() + collectionRow.comments_closed_after_days);\n\t\t\t\tif (new Date() > closedDate) {\n\t\t\t\t\treturn apiError(\"COMMENTS_CLOSED\", \"Comments are closed for this content\", 403);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Anti-spam: Honeypot — hidden field filled only by bots\n\t\tif (body.website_url) {\n\t\t\t// Silently accept — don't reveal the honeypot to bots\n\t\t\treturn apiSuccess({ status: \"pending\", message: \"Comment submitted for review\" });\n\t\t}\n\n\t\t// Anti-spam: Rate limiting\n\t\tconst meta = extractRequestMeta(request, emdash.config);\n\t\tconst { ipSalt } = await resolveSecretsCached(emdash.db);\n\t\tlet ipHash: string;\n\t\tif (meta.ip) {\n\t\t\tipHash = await hashIp(meta.ip, ipSalt);\n\t\t} else {\n\t\t\t// No trusted IP — fail closed by bucketing all unidentifiable\n\t\t\t// requests together. A larger limit reflects the shared bucket.\n\t\t\t//\n\t\t\t// Self-hosted operators behind a reverse proxy should set\n\t\t\t// `trustedProxyHeaders` in the EmDash config (or the\n\t\t\t// EMDASH_TRUSTED_PROXY_HEADERS env var) so this path isn't hit\n\t\t\t// for legitimate traffic. UA-hashing was previously used here\n\t\t\t// but was trivially rotatable — the shared bucket is stricter\n\t\t\t// and forces operators toward a real fix.\n\t\t\tipHash = \"unknown\";\n\t\t}\n\t\tconst unknownBucketLimit = ipHash === \"unknown\" ? 20 : undefined;\n\t\tconst rateLimited = await checkRateLimit(emdash.db, ipHash, unknownBucketLimit);\n\t\tif (rateLimited) {\n\t\t\treturn apiError(\"RATE_LIMITED\", \"Too many comments. Please try again later.\", 429);\n\t\t}\n\n\t\t// Build collection settings\n\t\tconst collectionSettings: CollectionCommentSettings = {\n\t\t\tcommentsEnabled: collectionRow.comments_enabled === 1,\n\t\t\tcommentsModeration:\n\t\t\t\tcollectionRow.comments_moderation as CollectionCommentSettings[\"commentsModeration\"],\n\t\t\tcommentsClosedAfterDays: collectionRow.comments_closed_after_days,\n\t\t\tcommentsAutoApproveUsers: collectionRow.comments_auto_approve_users === 1,\n\t\t};\n\n\t\t// Determine author fields — authenticated user overrides form input\n\t\tlet authorName = body.authorName;\n\t\tlet authorEmail = body.authorEmail;\n\t\tlet authorUserId: string | null = null;\n\n\t\tif (user) {\n\t\t\tauthorName = user.name || authorName;\n\t\t\tauthorEmail = user.email;\n\t\t\tauthorUserId = user.id;\n\t\t}\n\n\t\t// Validate parent exists and belongs to the same content.\n\t\t// Enforce 1-level nesting: if the parent is itself a reply, attach to its root.\n\t\tlet resolvedParentId = body.parentId ?? null;\n\t\tif (body.parentId) {\n\t\t\tconst repo = new CommentRepository(emdash.db);\n\t\t\tconst parent = await repo.findById(body.parentId);\n\t\t\tif (!parent) {\n\t\t\t\treturn apiError(\"VALIDATION_ERROR\", \"Parent comment not found\", 400);\n\t\t\t}\n\t\t\tif (parent.collection !== collection || parent.contentId !== contentId) {\n\t\t\t\treturn apiError(\"VALIDATION_ERROR\", \"Parent comment belongs to different content\", 400);\n\t\t\t}\n\t\t\t// Flatten: if parent is a reply, use its parent (the root) instead\n\t\t\tresolvedParentId = parent.parentId ?? parent.id;\n\t\t}\n\n\t\t// Wire the comment service to the real hook pipeline\n\t\tconst hookRunner: CommentHookRunner = {\n\t\t\tasync runBeforeCreate(event) {\n\t\t\t\treturn emdash.hooks.runCommentBeforeCreate(event);\n\t\t\t},\n\t\t\tasync runModerate(event) {\n\t\t\t\tconst result = await emdash.hooks.invokeExclusiveHook(\"comment:moderate\", event);\n\t\t\t\tif (!result) return { status: \"pending\" as const, reason: \"No moderator configured\" };\n\t\t\t\tif (result.error) {\n\t\t\t\t\tconsole.error(`[comments] Moderation error (${result.pluginId}):`, result.error.message);\n\t\t\t\t\treturn { status: \"pending\" as const, reason: \"Moderation error\" };\n\t\t\t\t}\n\t\t\t\treturn result.result as ModerationDecision;\n\t\t\t},\n\t\t\tfireAfterCreate(event) {\n\t\t\t\temdash.hooks\n\t\t\t\t\t.runCommentAfterCreate(event)\n\t\t\t\t\t.catch((err) =>\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\"[comments] afterCreate error:\",\n\t\t\t\t\t\t\terr instanceof Error ? err.message : err,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t},\n\t\t\tfireAfterModerate(event) {\n\t\t\t\temdash.hooks\n\t\t\t\t\t.runCommentAfterModerate(event)\n\t\t\t\t\t.catch((err) =>\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\"[comments] afterModerate error:\",\n\t\t\t\t\t\t\terr instanceof Error ? err.message : err,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\t// Build content info for afterCreate hooks (e.g. email notifications)\n\t\tconst typedContent = contentRow as {\n\t\t\tid: string;\n\t\t\tslug: string;\n\t\t\tauthor_id: string | null;\n\t\t};\n\t\tlet contentAuthor: { id: string; name: string | null; email: string } | undefined;\n\t\tif (typedContent.author_id) {\n\t\t\tconst authorRow = await emdash.db\n\t\t\t\t.selectFrom(\"users\")\n\t\t\t\t.select([\"id\", \"name\", \"email\", \"email_verified\"])\n\t\t\t\t.where(\"id\", \"=\", typedContent.author_id)\n\t\t\t\t.executeTakeFirst();\n\t\t\tif (authorRow && authorRow.email_verified) {\n\t\t\t\tcontentAuthor = {\n\t\t\t\t\tid: authorRow.id,\n\t\t\t\t\tname: authorRow.name,\n\t\t\t\t\temail: authorRow.email,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst result = await createComment(\n\t\t\temdash.db,\n\t\t\t{\n\t\t\t\tcollection,\n\t\t\t\tcontentId,\n\t\t\t\tparentId: resolvedParentId,\n\t\t\t\tauthorName,\n\t\t\t\tauthorEmail,\n\t\t\t\tauthorUserId,\n\t\t\t\tbody: body.body,\n\t\t\t\tipHash,\n\t\t\t\tuserAgent: meta.userAgent,\n\t\t\t},\n\t\t\tcollectionSettings,\n\t\t\thookRunner,\n\t\t\t{\n\t\t\t\tid: typedContent.id,\n\t\t\t\tcollection,\n\t\t\t\tslug: typedContent.slug,\n\t\t\t\tauthor: contentAuthor,\n\t\t\t},\n\t\t);\n\n\t\tif (!result) {\n\t\t\treturn apiError(\"COMMENT_REJECTED\", \"Comment was rejected\", 403);\n\t\t}\n\n\t\t// Send notification to content author (awaited so it completes before\n\t\t// the response is sent — required for Cloudflare Workers where the\n\t\t// isolate terminates after the response).\n\t\tif (result.comment.status === \"approved\" && emdash.email && contentAuthor) {\n\t\t\ttry {\n\t\t\t\tconst adminBaseUrl = await getSiteBaseUrl(emdash.db, request);\n\t\t\t\tawait sendCommentNotification({\n\t\t\t\t\temail: emdash.email,\n\t\t\t\t\tcomment: result.comment,\n\t\t\t\t\tcontentAuthor,\n\t\t\t\t\tadminBaseUrl,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\"[comments] notification error:\", err instanceof Error ? err.message : err);\n\t\t\t}\n\t\t}\n\n\t\treturn apiSuccess(\n\t\t\t{\n\t\t\t\tid: result.comment.id,\n\t\t\t\tstatus: result.comment.status,\n\t\t\t\tmessage:\n\t\t\t\t\tresult.comment.status === \"approved\"\n\t\t\t\t\t\t? \"Comment published\"\n\t\t\t\t\t\t: \"Comment submitted for review\",\n\t\t\t},\n\t\t\t201,\n\t\t);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to submit comment\", \"COMMENT_CREATE_ERROR\");\n\t}\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../src/comments/turnstile.ts","../../../../../../../src/astro/routes/api/comments/[collection]/[contentId]/index.ts"],"sourcesContent":["/**\n * Server-side Turnstile verification for public comment submissions.\n *\n * The comment form widget (`CommentForm.astro`) submits a `turnstileToken`;\n * this verifies it against Cloudflare's siteverify API. Enforcement is\n * opt-in: it only runs when the operator configures the Turnstile secret\n * key (`EMDASH_TURNSTILE_SECRET_KEY` or `TURNSTILE_SECRET_KEY`), so\n * existing non-Turnstile sites are unaffected.\n *\n * Mirrors `verifyTurnstile` in `@emdash-cms/plugin-forms` (not imported —\n * core doesn't depend on plugin packages).\n */\n\nconst SITEVERIFY_URL = \"https://challenges.cloudflare.com/turnstile/v0/siteverify\";\n\n/**\n * Resolve the configured Turnstile secret key, or `\"\"` when Turnstile\n * enforcement is not configured.\n */\nexport function getTurnstileSecretKey(): string {\n\treturn import.meta.env.EMDASH_TURNSTILE_SECRET_KEY || import.meta.env.TURNSTILE_SECRET_KEY || \"\";\n}\n\n/**\n * Verify a Turnstile response token via siteverify.\n *\n * Fails closed: a missing token, a failed verification, and a siteverify\n * transport error all return `false` — when the operator has configured a\n * secret, an unverifiable submission must not be persisted.\n */\nexport async function verifyTurnstileToken(\n\ttoken: string | undefined,\n\tsecretKey: string,\n\tremoteIp?: string | null,\n): Promise<boolean> {\n\tif (!token) return false;\n\n\tconst body: Record<string, string> = { secret: secretKey, response: token };\n\tif (remoteIp) {\n\t\tbody.remoteip = remoteIp;\n\t}\n\n\ttry {\n\t\tconst res = await fetch(SITEVERIFY_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify(body),\n\t\t\t// Fail closed *quickly* if siteverify is slow — without a timeout\n\t\t\t// the comment POST would hang until the runtime kills it\n\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t});\n\t\tconst data: { success?: boolean; \"error-codes\"?: string[] } = await res.json();\n\t\tif (!data.success) {\n\t\t\tconsole.warn(\"[comments] Turnstile verification failed:\", data[\"error-codes\"] ?? []);\n\t\t}\n\t\treturn data.success === true;\n\t} catch (error) {\n\t\tconsole.error(\n\t\t\t\"[comments] Turnstile siteverify request failed:\",\n\t\t\terror instanceof Error ? error.message : error,\n\t\t);\n\t\treturn false;\n\t}\n}\n","/**\n * Public comment endpoints\n *\n * GET /_emdash/api/comments/:collection/:contentId - List approved comments\n * POST /_emdash/api/comments/:collection/:contentId - Submit a comment\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { apiError, apiSuccess, handleError, requireDb, unwrapResult } from \"#api/error.js\";\nimport { handleCommentList, checkRateLimit, hashIp } from \"#api/handlers/comments.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { createCommentBody } from \"#api/schemas.js\";\nimport { getSiteBaseUrl } from \"#api/site-url.js\";\nimport { sendCommentNotification } from \"#comments/notifications.js\";\nimport { createComment, type CommentHookRunner } from \"#comments/service.js\";\nimport { getTurnstileSecretKey, verifyTurnstileToken } from \"#comments/turnstile.js\";\nimport { resolveSecretsCached } from \"#config/secrets.js\";\nimport { CommentRepository } from \"#db/repositories/comment.js\";\nimport { validateIdentifier } from \"#db/validate.js\";\nimport { extractRequestMeta } from \"#plugins/request-meta.js\";\nimport type { CollectionCommentSettings, ModerationDecision } from \"#plugins/types.js\";\n\nexport const prerender = false;\n\n/**\n * List approved comments for a content item (public, no auth required)\n */\nexport const GET: APIRoute = async ({ params, url, locals }) => {\n\tconst { emdash } = locals;\n\tconst { collection, contentId } = params;\n\n\tif (!collection || !contentId) {\n\t\treturn apiError(\"VALIDATION_ERROR\", \"Collection and content ID required\", 400);\n\t}\n\n\tconst dbErr = requireDb(emdash?.db);\n\tif (dbErr) return dbErr;\n\n\ttry {\n\t\tconst limit = Math.min(Number(url.searchParams.get(\"limit\") || 50), 100);\n\t\tconst cursor = url.searchParams.get(\"cursor\") ?? undefined;\n\t\tconst threaded = url.searchParams.get(\"threaded\") === \"true\";\n\n\t\t// Check collection exists and has comments enabled\n\t\tconst collectionRow = await emdash.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.select([\"comments_enabled\"])\n\t\t\t.where(\"slug\", \"=\", collection)\n\t\t\t.executeTakeFirst();\n\n\t\tif (!collectionRow) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Collection '${collection}' not found`, 404);\n\t\t}\n\n\t\tif (!collectionRow.comments_enabled) {\n\t\t\treturn apiError(\"COMMENTS_DISABLED\", \"Comments are not enabled for this collection\", 403);\n\t\t}\n\n\t\tconst result = await handleCommentList(emdash.db, collection, contentId, {\n\t\t\tlimit,\n\t\t\tcursor,\n\t\t\tthreaded,\n\t\t});\n\n\t\treturn unwrapResult(result);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to list comments\", \"COMMENT_LIST_ERROR\");\n\t}\n};\n\n/**\n * Submit a comment (public, gated by anti-spam checks)\n */\nexport const POST: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { collection, contentId } = params;\n\n\tif (!collection || !contentId) {\n\t\treturn apiError(\"VALIDATION_ERROR\", \"Collection and content ID required\", 400);\n\t}\n\n\tconst dbErr = requireDb(emdash?.db);\n\tif (dbErr) return dbErr;\n\n\ttry {\n\t\t// Parse and validate input\n\t\tconst body = await parseBody(request, createCommentBody);\n\t\tif (isParseError(body)) return body;\n\n\t\t// Check collection exists and has comments enabled\n\t\tconst collectionRow = await emdash.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.select([\n\t\t\t\t\"comments_enabled\",\n\t\t\t\t\"comments_moderation\",\n\t\t\t\t\"comments_closed_after_days\",\n\t\t\t\t\"comments_auto_approve_users\",\n\t\t\t])\n\t\t\t.where(\"slug\", \"=\", collection)\n\t\t\t.executeTakeFirst();\n\n\t\tif (!collectionRow) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Collection '${collection}' not found`, 404);\n\t\t}\n\n\t\tif (!collectionRow.comments_enabled) {\n\t\t\treturn apiError(\"COMMENTS_DISABLED\", \"Comments are not enabled for this collection\", 403);\n\t\t}\n\n\t\t// Verify the content item exists, is published, and not soft-deleted\n\t\tvalidateIdentifier(collection, \"collection\");\n\t\tconst contentRow = await emdash.db\n\t\t\t.selectFrom(`ec_${collection}` as never)\n\t\t\t.select([\"id\" as never, \"slug\" as never, \"author_id\" as never, \"published_at\" as never])\n\t\t\t.where(\"id\" as never, \"=\", contentId as never)\n\t\t\t.where(\"status\" as never, \"=\", \"published\" as never)\n\t\t\t.where(\"deleted_at\" as never, \"is\", null as never)\n\t\t\t.executeTakeFirst();\n\n\t\tif (!contentRow) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Content not found\", 404);\n\t\t}\n\n\t\t// Check if comments are closed (published_at + closed_after_days)\n\t\tif (collectionRow.comments_closed_after_days > 0) {\n\t\t\tconst publishedAt = (contentRow as { published_at: string | null }).published_at;\n\t\t\tif (publishedAt) {\n\t\t\t\tconst closedDate = new Date(publishedAt);\n\t\t\t\tclosedDate.setDate(closedDate.getDate() + collectionRow.comments_closed_after_days);\n\t\t\t\tif (new Date() > closedDate) {\n\t\t\t\t\treturn apiError(\"COMMENTS_CLOSED\", \"Comments are closed for this content\", 403);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Anti-spam: Honeypot — hidden field filled only by bots\n\t\tif (body.website_url) {\n\t\t\t// Silently accept — don't reveal the honeypot to bots\n\t\t\treturn apiSuccess({ status: \"pending\", message: \"Comment submitted for review\" });\n\t\t}\n\n\t\t// Anti-spam: Rate limiting\n\t\tconst meta = extractRequestMeta(request, emdash.config);\n\t\tconst { ipSalt } = await resolveSecretsCached(emdash.db);\n\t\tlet ipHash: string;\n\t\tif (meta.ip) {\n\t\t\tipHash = await hashIp(meta.ip, ipSalt);\n\t\t} else {\n\t\t\t// No trusted IP — fail closed by bucketing all unidentifiable\n\t\t\t// requests together. A larger limit reflects the shared bucket.\n\t\t\t//\n\t\t\t// Self-hosted operators behind a reverse proxy should set\n\t\t\t// `trustedProxyHeaders` in the EmDash config (or the\n\t\t\t// EMDASH_TRUSTED_PROXY_HEADERS env var) so this path isn't hit\n\t\t\t// for legitimate traffic. UA-hashing was previously used here\n\t\t\t// but was trivially rotatable — the shared bucket is stricter\n\t\t\t// and forces operators toward a real fix.\n\t\t\tipHash = \"unknown\";\n\t\t}\n\t\tconst unknownBucketLimit = ipHash === \"unknown\" ? 20 : undefined;\n\t\tconst rateLimited = await checkRateLimit(emdash.db, ipHash, unknownBucketLimit);\n\t\tif (rateLimited) {\n\t\t\treturn apiError(\"RATE_LIMITED\", \"Too many comments. Please try again later.\", 429);\n\t\t}\n\n\t\t// Anti-spam: Turnstile — enforced only when the operator configured a\n\t\t// secret key. Runs after the free checks (honeypot, rate limit) so\n\t\t// obvious spam never triggers a siteverify subrequest. Fails closed:\n\t\t// missing token, failed verification, and siteverify errors all reject.\n\t\tconst turnstileSecretKey = getTurnstileSecretKey();\n\t\tif (turnstileSecretKey) {\n\t\t\tconst verified = await verifyTurnstileToken(body.turnstileToken, turnstileSecretKey, meta.ip);\n\t\t\tif (!verified) {\n\t\t\t\treturn apiError(\"TURNSTILE_FAILED\", \"CAPTCHA verification failed\", 403);\n\t\t\t}\n\t\t}\n\n\t\t// Build collection settings\n\t\tconst collectionSettings: CollectionCommentSettings = {\n\t\t\tcommentsEnabled: collectionRow.comments_enabled === 1,\n\t\t\tcommentsModeration:\n\t\t\t\tcollectionRow.comments_moderation as CollectionCommentSettings[\"commentsModeration\"],\n\t\t\tcommentsClosedAfterDays: collectionRow.comments_closed_after_days,\n\t\t\tcommentsAutoApproveUsers: collectionRow.comments_auto_approve_users === 1,\n\t\t};\n\n\t\t// Determine author fields — authenticated user overrides form input\n\t\tlet authorName = body.authorName;\n\t\tlet authorEmail = body.authorEmail;\n\t\tlet authorUserId: string | null = null;\n\n\t\tif (user) {\n\t\t\tauthorName = user.name || authorName;\n\t\t\tauthorEmail = user.email;\n\t\t\tauthorUserId = user.id;\n\t\t}\n\n\t\t// Validate parent exists and belongs to the same content.\n\t\t// Enforce 1-level nesting: if the parent is itself a reply, attach to its root.\n\t\tlet resolvedParentId = body.parentId ?? null;\n\t\tif (body.parentId) {\n\t\t\tconst repo = new CommentRepository(emdash.db);\n\t\t\tconst parent = await repo.findById(body.parentId);\n\t\t\tif (!parent) {\n\t\t\t\treturn apiError(\"VALIDATION_ERROR\", \"Parent comment not found\", 400);\n\t\t\t}\n\t\t\tif (parent.collection !== collection || parent.contentId !== contentId) {\n\t\t\t\treturn apiError(\"VALIDATION_ERROR\", \"Parent comment belongs to different content\", 400);\n\t\t\t}\n\t\t\t// Flatten: if parent is a reply, use its parent (the root) instead\n\t\t\tresolvedParentId = parent.parentId ?? parent.id;\n\t\t}\n\n\t\t// Wire the comment service to the real hook pipeline\n\t\tconst hookRunner: CommentHookRunner = {\n\t\t\tasync runBeforeCreate(event) {\n\t\t\t\treturn emdash.hooks.runCommentBeforeCreate(event);\n\t\t\t},\n\t\t\tasync runModerate(event) {\n\t\t\t\tconst result = await emdash.hooks.invokeExclusiveHook(\"comment:moderate\", event);\n\t\t\t\tif (!result) return { status: \"pending\" as const, reason: \"No moderator configured\" };\n\t\t\t\tif (result.error) {\n\t\t\t\t\tconsole.error(`[comments] Moderation error (${result.pluginId}):`, result.error.message);\n\t\t\t\t\treturn { status: \"pending\" as const, reason: \"Moderation error\" };\n\t\t\t\t}\n\t\t\t\treturn result.result as ModerationDecision;\n\t\t\t},\n\t\t\tfireAfterCreate(event) {\n\t\t\t\temdash.hooks\n\t\t\t\t\t.runCommentAfterCreate(event)\n\t\t\t\t\t.catch((err) =>\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\"[comments] afterCreate error:\",\n\t\t\t\t\t\t\terr instanceof Error ? err.message : err,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t},\n\t\t\tfireAfterModerate(event) {\n\t\t\t\temdash.hooks\n\t\t\t\t\t.runCommentAfterModerate(event)\n\t\t\t\t\t.catch((err) =>\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\"[comments] afterModerate error:\",\n\t\t\t\t\t\t\terr instanceof Error ? err.message : err,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\t// Build content info for afterCreate hooks (e.g. email notifications)\n\t\tconst typedContent = contentRow as {\n\t\t\tid: string;\n\t\t\tslug: string;\n\t\t\tauthor_id: string | null;\n\t\t};\n\t\tlet contentAuthor: { id: string; name: string | null; email: string } | undefined;\n\t\tif (typedContent.author_id) {\n\t\t\tconst authorRow = await emdash.db\n\t\t\t\t.selectFrom(\"users\")\n\t\t\t\t.select([\"id\", \"name\", \"email\", \"email_verified\"])\n\t\t\t\t.where(\"id\", \"=\", typedContent.author_id)\n\t\t\t\t.executeTakeFirst();\n\t\t\tif (authorRow && authorRow.email_verified) {\n\t\t\t\tcontentAuthor = {\n\t\t\t\t\tid: authorRow.id,\n\t\t\t\t\tname: authorRow.name,\n\t\t\t\t\temail: authorRow.email,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst result = await createComment(\n\t\t\temdash.db,\n\t\t\t{\n\t\t\t\tcollection,\n\t\t\t\tcontentId,\n\t\t\t\tparentId: resolvedParentId,\n\t\t\t\tauthorName,\n\t\t\t\tauthorEmail,\n\t\t\t\tauthorUserId,\n\t\t\t\tbody: body.body,\n\t\t\t\tipHash,\n\t\t\t\tuserAgent: meta.userAgent,\n\t\t\t},\n\t\t\tcollectionSettings,\n\t\t\thookRunner,\n\t\t\t{\n\t\t\t\tid: typedContent.id,\n\t\t\t\tcollection,\n\t\t\t\tslug: typedContent.slug,\n\t\t\t\tauthor: contentAuthor,\n\t\t\t},\n\t\t);\n\n\t\tif (!result) {\n\t\t\treturn apiError(\"COMMENT_REJECTED\", \"Comment was rejected\", 403);\n\t\t}\n\n\t\t// Send notification to content author (awaited so it completes before\n\t\t// the response is sent — required for Cloudflare Workers where the\n\t\t// isolate terminates after the response).\n\t\tif (result.comment.status === \"approved\" && emdash.email && contentAuthor) {\n\t\t\ttry {\n\t\t\t\tconst adminBaseUrl = await getSiteBaseUrl(emdash.db, request);\n\t\t\t\tawait sendCommentNotification({\n\t\t\t\t\temail: emdash.email,\n\t\t\t\t\tcomment: result.comment,\n\t\t\t\t\tcontentAuthor,\n\t\t\t\t\tadminBaseUrl,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\"[comments] notification error:\", err instanceof Error ? err.message : err);\n\t\t\t}\n\t\t}\n\n\t\treturn apiSuccess(\n\t\t\t{\n\t\t\t\tid: result.comment.id,\n\t\t\t\tstatus: result.comment.status,\n\t\t\t\tmessage:\n\t\t\t\t\tresult.comment.status === \"approved\"\n\t\t\t\t\t\t? \"Comment published\"\n\t\t\t\t\t\t: \"Comment submitted for review\",\n\t\t\t},\n\t\t\t201,\n\t\t);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to submit comment\", \"COMMENT_CREATE_ERROR\");\n\t}\n};\n"],"mappings":"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"}
@@ -1,19 +1,19 @@
1
1
  import "../../../../../../after-B1IIdH3Y.mjs";
2
2
  import { s as invalidateCommentObjectCache } from "../../../../../../object-cache-Bok5j2ae.mjs";
3
- import "../../../../../../base64-CmWvODNW.mjs";
4
- import "../../../../../../types-CuW0zWDA.mjs";
5
- import "../../../../../../comment-CzeatwBV.mjs";
6
- import "../../../../../../options-DTTML-Tx.mjs";
7
- import { a as unwrapResult, i as requireDb, n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
8
- import { n as parseBody, t as isParseError } from "../../../../../../parse-GmjecGct.mjs";
9
- import { bt as createReactionBody } from "../../../../../../redirects-DXEXBAII.mjs";
10
- import "../../../../../../byline-fields-2Lp5Hbgf.mjs";
11
- import "../../../../../../status-CqJQX1QN.mjs";
3
+ import "../../../../../../base64-C74Msx8h.mjs";
4
+ import "../../../../../../types-Ch8pFMEm.mjs";
5
+ import "../../../../../../comment-SWh31kS0.mjs";
6
+ import "../../../../../../options-BlmBHTvX.mjs";
7
+ import { a as unwrapResult, i as requireDb, n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
8
+ import { n as parseBody, t as isParseError } from "../../../../../../parse-GLksnn-B.mjs";
9
+ import { bt as createReactionBody } from "../../../../../../redirects-CeD7K7EX.mjs";
10
+ import "../../../../../../relations-BmBmX4Eb.mjs";
11
+ import "../../../../../../status-D3Gy1Zc6.mjs";
12
12
  import "../../../../../../api/schemas/index.mjs";
13
- import { t as extractRequestMeta } from "../../../../../../request-meta-DbvXaAvU.mjs";
14
- import { t as CommentReactionRepository } from "../../../../../../comment-reaction-Cj9QJY7I.mjs";
15
- import { i as resolveSecretsCached } from "../../../../../../secrets-kp760LlD.mjs";
16
- import { c as hashIp } from "../../../../../../comments-BLE-jv5k.mjs";
13
+ import { t as extractRequestMeta } from "../../../../../../request-meta-DCwktC5P.mjs";
14
+ import { t as CommentReactionRepository } from "../../../../../../comment-reaction-DgcpziXI.mjs";
15
+ import { i as resolveSecretsCached } from "../../../../../../secrets-CO_bEmII.mjs";
16
+ import { c as hashIp } from "../../../../../../comments-kZYGppMS.mjs";
17
17
 
18
18
  //#region src/api/handlers/comment-reactions.ts
19
19
  /** Max reactions a single voter may register per window before throttling. */
@@ -1,7 +1,7 @@
1
- import "../../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../../types-CuW0zWDA.mjs";
3
- import { a as unwrapResult, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
4
- import { n as requirePerm } from "../../../../../../authorize-Dz88F_Fw.mjs";
1
+ import "../../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../../types-Ch8pFMEm.mjs";
3
+ import { a as unwrapResult, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
4
+ import { n as requirePerm } from "../../../../../../authorize-BmCwQCib.mjs";
5
5
 
6
6
  //#region src/astro/routes/api/content/[collection]/[id]/compare.ts
7
7
  const prerender = false;
@@ -1,7 +1,7 @@
1
- import "../../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../../types-CuW0zWDA.mjs";
3
- import { a as unwrapResult, o as mapErrorStatus, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
4
- import { t as requireOwnerPerm } from "../../../../../../authorize-Dz88F_Fw.mjs";
1
+ import "../../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../../types-Ch8pFMEm.mjs";
3
+ import { a as unwrapResult, s as mapErrorStatus, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
4
+ import { t as requireOwnerPerm } from "../../../../../../authorize-BmCwQCib.mjs";
5
5
 
6
6
  //#region src/astro/routes/api/content/[collection]/[id]/discard-draft.ts
7
7
  const prerender = false;
@@ -1,7 +1,7 @@
1
- import "../../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../../types-CuW0zWDA.mjs";
3
- import { a as unwrapResult, o as mapErrorStatus, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
4
- import { n as requirePerm, t as requireOwnerPerm } from "../../../../../../authorize-Dz88F_Fw.mjs";
1
+ import "../../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../../types-Ch8pFMEm.mjs";
3
+ import { a as unwrapResult, s as mapErrorStatus, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
4
+ import { n as requirePerm, t as requireOwnerPerm } from "../../../../../../authorize-BmCwQCib.mjs";
5
5
 
6
6
  //#region src/astro/routes/api/content/[collection]/[id]/duplicate.ts
7
7
  const prerender = false;
@@ -1,7 +1,7 @@
1
- import "../../../../../../base64-CmWvODNW.mjs";
2
- import "../../../../../../types-CuW0zWDA.mjs";
3
- import { a as unwrapResult, t as apiError } from "../../../../../../error-CkAseSqZ.mjs";
4
- import { n as requirePerm } from "../../../../../../authorize-Dz88F_Fw.mjs";
1
+ import "../../../../../../base64-C74Msx8h.mjs";
2
+ import "../../../../../../types-Ch8pFMEm.mjs";
3
+ import { a as unwrapResult, t as apiError } from "../../../../../../error-CfJMLSkd.mjs";
4
+ import { n as requirePerm } from "../../../../../../authorize-BmCwQCib.mjs";
5
5
 
6
6
  //#region src/astro/routes/api/content/[collection]/[id]/permanent.ts
7
7
  const prerender = false;