emdash 0.12.0 → 0.13.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 (1003) hide show
  1. package/dist/{adapters-BktHA7EO.d.mts → adapters-9DybjTO6.d.mts} +1 -1
  2. package/dist/{adapters-BktHA7EO.d.mts.map → adapters-9DybjTO6.d.mts.map} +1 -1
  3. package/dist/allowed-origins-CDdG-4Gd.mjs +116 -0
  4. package/dist/allowed-origins-CDdG-4Gd.mjs.map +1 -0
  5. package/dist/api/route-utils.d.mts +68 -0
  6. package/dist/api/route-utils.d.mts.map +1 -0
  7. package/dist/api/route-utils.mjs +44 -0
  8. package/dist/api/route-utils.mjs.map +1 -0
  9. package/dist/api/schemas/index.d.mts +2 -0
  10. package/dist/api/schemas/index.mjs +4 -0
  11. package/dist/api-ayIQ7rIe.mjs +3941 -0
  12. package/dist/api-ayIQ7rIe.mjs.map +1 -0
  13. package/dist/api-tokens-D3C9v02m.mjs +3 -0
  14. package/dist/api-tokens-eYymBhIT.mjs +153 -0
  15. package/dist/api-tokens-eYymBhIT.mjs.map +1 -0
  16. package/dist/{apply-C1ZORgcy.mjs → apply-v4DBgjPw.mjs} +19 -346
  17. package/dist/apply-v4DBgjPw.mjs.map +1 -0
  18. package/dist/astro/index.d.mts +10 -6
  19. package/dist/astro/index.d.mts.map +1 -1
  20. package/dist/astro/index.mjs +42 -83
  21. package/dist/astro/index.mjs.map +1 -1
  22. package/dist/astro/middleware/auth.d.mts +9 -5
  23. package/dist/astro/middleware/auth.d.mts.map +1 -1
  24. package/dist/astro/middleware/auth.mjs +25 -65
  25. package/dist/astro/middleware/auth.mjs.map +1 -1
  26. package/dist/astro/middleware/redirect.mjs +5 -5
  27. package/dist/astro/middleware/request-context.mjs +4 -4
  28. package/dist/astro/middleware/setup.mjs +1 -1
  29. package/dist/astro/middleware.d.mts.map +1 -1
  30. package/dist/astro/middleware.mjs +140 -69
  31. package/dist/astro/middleware.mjs.map +1 -1
  32. package/dist/astro/routes/PluginRegistry.d.mts +15 -0
  33. package/dist/astro/routes/PluginRegistry.d.mts.map +1 -0
  34. package/dist/astro/routes/PluginRegistry.mjs +25 -0
  35. package/dist/astro/routes/PluginRegistry.mjs.map +1 -0
  36. package/dist/astro/routes/api/admin/allowed-domains/_domain_.d.mts +15 -0
  37. package/dist/astro/routes/api/admin/allowed-domains/_domain_.d.mts.map +1 -0
  38. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +67 -0
  39. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs.map +1 -0
  40. package/dist/astro/routes/api/admin/allowed-domains/index.d.mts +15 -0
  41. package/dist/astro/routes/api/admin/allowed-domains/index.d.mts.map +1 -0
  42. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +67 -0
  43. package/dist/astro/routes/api/admin/allowed-domains/index.mjs.map +1 -0
  44. package/dist/astro/routes/api/admin/api-tokens/_id_.d.mts +11 -0
  45. package/dist/astro/routes/api/admin/api-tokens/_id_.d.mts.map +1 -0
  46. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +33 -0
  47. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs.map +1 -0
  48. package/dist/astro/routes/api/admin/api-tokens/index.d.mts +17 -0
  49. package/dist/astro/routes/api/admin/api-tokens/index.d.mts.map +1 -0
  50. package/dist/astro/routes/api/admin/api-tokens/index.mjs +52 -0
  51. package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -0
  52. package/dist/astro/routes/api/admin/bylines/_id_/index.d.mts +10 -0
  53. package/dist/astro/routes/api/admin/bylines/_id_/index.d.mts.map +1 -0
  54. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +74 -0
  55. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -0
  56. package/dist/astro/routes/api/admin/bylines/index.d.mts +9 -0
  57. package/dist/astro/routes/api/admin/bylines/index.d.mts.map +1 -0
  58. package/dist/astro/routes/api/admin/bylines/index.mjs +61 -0
  59. package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -0
  60. package/dist/astro/routes/api/admin/comments/_id_/status.d.mts +8 -0
  61. package/dist/astro/routes/api/admin/comments/_id_/status.d.mts.map +1 -0
  62. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +80 -0
  63. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -0
  64. package/dist/astro/routes/api/admin/comments/_id_.d.mts +15 -0
  65. package/dist/astro/routes/api/admin/comments/_id_.d.mts.map +1 -0
  66. package/dist/astro/routes/api/admin/comments/_id_.mjs +47 -0
  67. package/dist/astro/routes/api/admin/comments/_id_.mjs.map +1 -0
  68. package/dist/astro/routes/api/admin/comments/bulk.d.mts +8 -0
  69. package/dist/astro/routes/api/admin/comments/bulk.d.mts.map +1 -0
  70. package/dist/astro/routes/api/admin/comments/bulk.mjs +36 -0
  71. package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -0
  72. package/dist/astro/routes/api/admin/comments/counts.d.mts +8 -0
  73. package/dist/astro/routes/api/admin/comments/counts.d.mts.map +1 -0
  74. package/dist/astro/routes/api/admin/comments/counts.mjs +25 -0
  75. package/dist/astro/routes/api/admin/comments/counts.mjs.map +1 -0
  76. package/dist/astro/routes/api/admin/comments/index.d.mts +11 -0
  77. package/dist/astro/routes/api/admin/comments/index.d.mts.map +1 -0
  78. package/dist/astro/routes/api/admin/comments/index.mjs +40 -0
  79. package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -0
  80. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.d.mts +8 -0
  81. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.d.mts.map +1 -0
  82. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +48 -0
  83. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs.map +1 -0
  84. package/dist/astro/routes/api/admin/hooks/exclusive/index.d.mts +8 -0
  85. package/dist/astro/routes/api/admin/hooks/exclusive/index.d.mts.map +1 -0
  86. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +36 -0
  87. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs.map +1 -0
  88. package/dist/astro/routes/api/admin/oauth-clients/_id_.d.mts +19 -0
  89. package/dist/astro/routes/api/admin/oauth-clients/_id_.d.mts.map +1 -0
  90. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +69 -0
  91. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs.map +1 -0
  92. package/dist/astro/routes/api/admin/oauth-clients/index.d.mts +15 -0
  93. package/dist/astro/routes/api/admin/oauth-clients/index.d.mts.map +1 -0
  94. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +50 -0
  95. package/dist/astro/routes/api/admin/oauth-clients/index.mjs.map +1 -0
  96. package/dist/astro/routes/api/admin/plugins/_id_/disable.d.mts +8 -0
  97. package/dist/astro/routes/api/admin/plugins/_id_/disable.d.mts.map +1 -0
  98. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -0
  99. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -0
  100. package/dist/astro/routes/api/admin/plugins/_id_/enable.d.mts +8 -0
  101. package/dist/astro/routes/api/admin/plugins/_id_/enable.d.mts.map +1 -0
  102. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +59 -0
  103. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -0
  104. package/dist/astro/routes/api/admin/plugins/_id_/index.d.mts +8 -0
  105. package/dist/astro/routes/api/admin/plugins/_id_/index.d.mts.map +1 -0
  106. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +51 -0
  107. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -0
  108. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.d.mts +8 -0
  109. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.d.mts.map +1 -0
  110. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +58 -0
  111. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -0
  112. package/dist/astro/routes/api/admin/plugins/_id_/update.d.mts +8 -0
  113. package/dist/astro/routes/api/admin/plugins/_id_/update.d.mts.map +1 -0
  114. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +66 -0
  115. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -0
  116. package/dist/astro/routes/api/admin/plugins/index.d.mts +8 -0
  117. package/dist/astro/routes/api/admin/plugins/index.d.mts.map +1 -0
  118. package/dist/astro/routes/api/admin/plugins/index.mjs +49 -0
  119. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -0
  120. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.d.mts +8 -0
  121. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.d.mts.map +1 -0
  122. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +39 -0
  123. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs.map +1 -0
  124. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.d.mts +8 -0
  125. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.d.mts.map +1 -0
  126. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +51 -0
  127. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -0
  128. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.d.mts +8 -0
  129. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.d.mts.map +1 -0
  130. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +69 -0
  131. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -0
  132. package/dist/astro/routes/api/admin/plugins/marketplace/index.d.mts +8 -0
  133. package/dist/astro/routes/api/admin/plugins/marketplace/index.d.mts.map +1 -0
  134. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +58 -0
  135. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -0
  136. package/dist/astro/routes/api/admin/plugins/registry/install.d.mts +8 -0
  137. package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -0
  138. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +72 -0
  139. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -0
  140. package/dist/astro/routes/api/admin/plugins/updates.d.mts +8 -0
  141. package/dist/astro/routes/api/admin/plugins/updates.d.mts.map +1 -0
  142. package/dist/astro/routes/api/admin/plugins/updates.mjs +49 -0
  143. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -0
  144. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.d.mts +8 -0
  145. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.d.mts.map +1 -0
  146. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +51 -0
  147. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -0
  148. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.d.mts +8 -0
  149. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.d.mts.map +1 -0
  150. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +39 -0
  151. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs.map +1 -0
  152. package/dist/astro/routes/api/admin/themes/marketplace/index.d.mts +8 -0
  153. package/dist/astro/routes/api/admin/themes/marketplace/index.d.mts.map +1 -0
  154. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +67 -0
  155. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -0
  156. package/dist/astro/routes/api/admin/users/_id_/disable.d.mts +8 -0
  157. package/dist/astro/routes/api/admin/users/_id_/disable.d.mts.map +1 -0
  158. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +43 -0
  159. package/dist/astro/routes/api/admin/users/_id_/disable.mjs.map +1 -0
  160. package/dist/astro/routes/api/admin/users/_id_/enable.d.mts +8 -0
  161. package/dist/astro/routes/api/admin/users/_id_/enable.d.mts.map +1 -0
  162. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +32 -0
  163. package/dist/astro/routes/api/admin/users/_id_/enable.mjs.map +1 -0
  164. package/dist/astro/routes/api/admin/users/_id_/index.d.mts +9 -0
  165. package/dist/astro/routes/api/admin/users/_id_/index.d.mts.map +1 -0
  166. package/dist/astro/routes/api/admin/users/_id_/index.mjs +106 -0
  167. package/dist/astro/routes/api/admin/users/_id_/index.mjs.map +1 -0
  168. package/dist/astro/routes/api/admin/users/_id_/send-recovery.d.mts +8 -0
  169. package/dist/astro/routes/api/admin/users/_id_/send-recovery.d.mts.map +1 -0
  170. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +46 -0
  171. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs.map +1 -0
  172. package/dist/astro/routes/api/admin/users/index.d.mts +8 -0
  173. package/dist/astro/routes/api/admin/users/index.d.mts.map +1 -0
  174. package/dist/astro/routes/api/admin/users/index.mjs +56 -0
  175. package/dist/astro/routes/api/admin/users/index.mjs.map +1 -0
  176. package/dist/astro/routes/api/auth/dev-bypass.d.mts +9 -0
  177. package/dist/astro/routes/api/auth/dev-bypass.d.mts.map +1 -0
  178. package/dist/astro/routes/api/auth/dev-bypass.mjs +84 -0
  179. package/dist/astro/routes/api/auth/dev-bypass.mjs.map +1 -0
  180. package/dist/astro/routes/api/auth/invite/accept.d.mts +8 -0
  181. package/dist/astro/routes/api/auth/invite/accept.d.mts.map +1 -0
  182. package/dist/astro/routes/api/auth/invite/accept.mjs +34 -0
  183. package/dist/astro/routes/api/auth/invite/accept.mjs.map +1 -0
  184. package/dist/astro/routes/api/auth/invite/complete.d.mts +8 -0
  185. package/dist/astro/routes/api/auth/invite/complete.d.mts.map +1 -0
  186. package/dist/astro/routes/api/auth/invite/complete.mjs +56 -0
  187. package/dist/astro/routes/api/auth/invite/complete.mjs.map +1 -0
  188. package/dist/astro/routes/api/auth/invite/index.d.mts +8 -0
  189. package/dist/astro/routes/api/auth/invite/index.d.mts.map +1 -0
  190. package/dist/astro/routes/api/auth/invite/index.mjs +53 -0
  191. package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -0
  192. package/dist/astro/routes/api/auth/invite/register-options.d.mts +8 -0
  193. package/dist/astro/routes/api/auth/invite/register-options.d.mts.map +1 -0
  194. package/dist/astro/routes/api/auth/invite/register-options.mjs +46 -0
  195. package/dist/astro/routes/api/auth/invite/register-options.mjs.map +1 -0
  196. package/dist/astro/routes/api/auth/logout.d.mts +8 -0
  197. package/dist/astro/routes/api/auth/logout.d.mts.map +1 -0
  198. package/dist/astro/routes/api/auth/logout.mjs +27 -0
  199. package/dist/astro/routes/api/auth/logout.mjs.map +1 -0
  200. package/dist/astro/routes/api/auth/magic-link/send.d.mts +8 -0
  201. package/dist/astro/routes/api/auth/magic-link/send.d.mts.map +1 -0
  202. package/dist/astro/routes/api/auth/magic-link/send.mjs +50 -0
  203. package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -0
  204. package/dist/astro/routes/api/auth/magic-link/verify.d.mts +8 -0
  205. package/dist/astro/routes/api/auth/magic-link/verify.d.mts.map +1 -0
  206. package/dist/astro/routes/api/auth/magic-link/verify.mjs +35 -0
  207. package/dist/astro/routes/api/auth/magic-link/verify.mjs.map +1 -0
  208. package/dist/astro/routes/api/auth/me.d.mts +14 -0
  209. package/dist/astro/routes/api/auth/me.d.mts.map +1 -0
  210. package/dist/astro/routes/api/auth/me.mjs +43 -0
  211. package/dist/astro/routes/api/auth/me.mjs.map +1 -0
  212. package/dist/astro/routes/api/auth/mode.d.mts +8 -0
  213. package/dist/astro/routes/api/auth/mode.d.mts.map +1 -0
  214. package/dist/astro/routes/api/auth/mode.mjs +29 -0
  215. package/dist/astro/routes/api/auth/mode.mjs.map +1 -0
  216. package/dist/astro/routes/api/auth/oauth/_provider_/callback.d.mts +8 -0
  217. package/dist/astro/routes/api/auth/oauth/_provider_/callback.d.mts.map +1 -0
  218. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +130 -0
  219. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs.map +1 -0
  220. package/dist/astro/routes/api/auth/oauth/_provider_.d.mts +8 -0
  221. package/dist/astro/routes/api/auth/oauth/_provider_.d.mts.map +1 -0
  222. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +60 -0
  223. package/dist/astro/routes/api/auth/oauth/_provider_.mjs.map +1 -0
  224. package/dist/astro/routes/api/auth/passkey/_id_.d.mts +15 -0
  225. package/dist/astro/routes/api/auth/passkey/_id_.d.mts.map +1 -0
  226. package/dist/astro/routes/api/auth/passkey/_id_.mjs +64 -0
  227. package/dist/astro/routes/api/auth/passkey/_id_.mjs.map +1 -0
  228. package/dist/astro/routes/api/auth/passkey/index.d.mts +8 -0
  229. package/dist/astro/routes/api/auth/passkey/index.d.mts.map +1 -0
  230. package/dist/astro/routes/api/auth/passkey/index.mjs +28 -0
  231. package/dist/astro/routes/api/auth/passkey/index.mjs.map +1 -0
  232. package/dist/astro/routes/api/auth/passkey/options.d.mts +8 -0
  233. package/dist/astro/routes/api/auth/passkey/options.d.mts.map +1 -0
  234. package/dist/astro/routes/api/auth/passkey/options.mjs +48 -0
  235. package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -0
  236. package/dist/astro/routes/api/auth/passkey/register/options.d.mts +8 -0
  237. package/dist/astro/routes/api/auth/passkey/register/options.d.mts.map +1 -0
  238. package/dist/astro/routes/api/auth/passkey/register/options.mjs +46 -0
  239. package/dist/astro/routes/api/auth/passkey/register/options.mjs.map +1 -0
  240. package/dist/astro/routes/api/auth/passkey/register/verify.d.mts +8 -0
  241. package/dist/astro/routes/api/auth/passkey/register/verify.d.mts.map +1 -0
  242. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +61 -0
  243. package/dist/astro/routes/api/auth/passkey/register/verify.mjs.map +1 -0
  244. package/dist/astro/routes/api/auth/passkey/verify.d.mts +8 -0
  245. package/dist/astro/routes/api/auth/passkey/verify.d.mts.map +1 -0
  246. package/dist/astro/routes/api/auth/passkey/verify.mjs +49 -0
  247. package/dist/astro/routes/api/auth/passkey/verify.mjs.map +1 -0
  248. package/dist/astro/routes/api/auth/signup/complete.d.mts +8 -0
  249. package/dist/astro/routes/api/auth/signup/complete.d.mts.map +1 -0
  250. package/dist/astro/routes/api/auth/signup/complete.mjs +57 -0
  251. package/dist/astro/routes/api/auth/signup/complete.mjs.map +1 -0
  252. package/dist/astro/routes/api/auth/signup/request.d.mts +8 -0
  253. package/dist/astro/routes/api/auth/signup/request.d.mts.map +1 -0
  254. package/dist/astro/routes/api/auth/signup/request.mjs +46 -0
  255. package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -0
  256. package/dist/astro/routes/api/auth/signup/verify.d.mts +8 -0
  257. package/dist/astro/routes/api/auth/signup/verify.d.mts.map +1 -0
  258. package/dist/astro/routes/api/auth/signup/verify.mjs +35 -0
  259. package/dist/astro/routes/api/auth/signup/verify.mjs.map +1 -0
  260. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.d.mts +15 -0
  261. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.d.mts.map +1 -0
  262. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +193 -0
  263. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -0
  264. package/dist/astro/routes/api/content/_collection_/_id_/compare.d.mts +8 -0
  265. package/dist/astro/routes/api/content/_collection_/_id_/compare.d.mts.map +1 -0
  266. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +20 -0
  267. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs.map +1 -0
  268. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts +8 -0
  269. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts.map +1 -0
  270. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +28 -0
  271. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -0
  272. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.d.mts +8 -0
  273. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.d.mts.map +1 -0
  274. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +30 -0
  275. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs.map +1 -0
  276. package/dist/astro/routes/api/content/_collection_/_id_/permanent.d.mts +8 -0
  277. package/dist/astro/routes/api/content/_collection_/_id_/permanent.d.mts.map +1 -0
  278. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +23 -0
  279. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs.map +1 -0
  280. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.d.mts +8 -0
  281. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.d.mts.map +1 -0
  282. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +78 -0
  283. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs.map +1 -0
  284. package/dist/astro/routes/api/content/_collection_/_id_/publish.d.mts +8 -0
  285. package/dist/astro/routes/api/content/_collection_/_id_/publish.d.mts.map +1 -0
  286. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +48 -0
  287. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -0
  288. package/dist/astro/routes/api/content/_collection_/_id_/restore.d.mts +8 -0
  289. package/dist/astro/routes/api/content/_collection_/_id_/restore.d.mts.map +1 -0
  290. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +28 -0
  291. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs.map +1 -0
  292. package/dist/astro/routes/api/content/_collection_/_id_/revisions.d.mts +8 -0
  293. package/dist/astro/routes/api/content/_collection_/_id_/revisions.d.mts.map +1 -0
  294. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +22 -0
  295. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs.map +1 -0
  296. package/dist/astro/routes/api/content/_collection_/_id_/schedule.d.mts +9 -0
  297. package/dist/astro/routes/api/content/_collection_/_id_/schedule.d.mts.map +1 -0
  298. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +58 -0
  299. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -0
  300. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts +15 -0
  301. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts.map +1 -0
  302. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +85 -0
  303. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -0
  304. package/dist/astro/routes/api/content/_collection_/_id_/translations.d.mts +8 -0
  305. package/dist/astro/routes/api/content/_collection_/_id_/translations.d.mts.map +1 -0
  306. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +43 -0
  307. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs.map +1 -0
  308. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts +8 -0
  309. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts.map +1 -0
  310. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +28 -0
  311. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -0
  312. package/dist/astro/routes/api/content/_collection_/_id_.d.mts +10 -0
  313. package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -0
  314. package/dist/astro/routes/api/content/_collection_/_id_.mjs +88 -0
  315. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -0
  316. package/dist/astro/routes/api/content/_collection_/index.d.mts +9 -0
  317. package/dist/astro/routes/api/content/_collection_/index.d.mts.map +1 -0
  318. package/dist/astro/routes/api/content/_collection_/index.mjs +61 -0
  319. package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -0
  320. package/dist/astro/routes/api/content/_collection_/trash.d.mts +8 -0
  321. package/dist/astro/routes/api/content/_collection_/trash.d.mts.map +1 -0
  322. package/dist/astro/routes/api/content/_collection_/trash.mjs +25 -0
  323. package/dist/astro/routes/api/content/_collection_/trash.mjs.map +1 -0
  324. package/dist/astro/routes/api/dashboard.d.mts +8 -0
  325. package/dist/astro/routes/api/dashboard.d.mts.map +1 -0
  326. package/dist/astro/routes/api/dashboard.mjs +26 -0
  327. package/dist/astro/routes/api/dashboard.mjs.map +1 -0
  328. package/dist/astro/routes/api/dev/emails.d.mts +9 -0
  329. package/dist/astro/routes/api/dev/emails.d.mts.map +1 -0
  330. package/dist/astro/routes/api/dev/emails.mjs +20 -0
  331. package/dist/astro/routes/api/dev/emails.mjs.map +1 -0
  332. package/dist/astro/routes/api/import/probe.d.mts +18 -0
  333. package/dist/astro/routes/api/import/probe.d.mts.map +1 -0
  334. package/dist/astro/routes/api/import/probe.mjs +35 -0
  335. package/dist/astro/routes/api/import/probe.mjs.map +1 -0
  336. package/dist/astro/routes/api/import/wordpress/analyze.d.mts +88 -0
  337. package/dist/astro/routes/api/import/wordpress/analyze.d.mts.map +1 -0
  338. package/dist/astro/routes/api/import/wordpress/analyze.mjs +313 -0
  339. package/dist/astro/routes/api/import/wordpress/analyze.mjs.map +1 -0
  340. package/dist/astro/routes/api/import/wordpress/execute.d.mts +93 -0
  341. package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -0
  342. package/dist/astro/routes/api/import/wordpress/execute.mjs +593 -0
  343. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -0
  344. package/dist/astro/routes/api/import/wordpress/media.d.mts +36 -0
  345. package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -0
  346. package/dist/astro/routes/api/import/wordpress/media.mjs +225 -0
  347. package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -0
  348. package/dist/astro/routes/api/import/wordpress/prepare.d.mts +20 -0
  349. package/dist/astro/routes/api/import/wordpress/prepare.d.mts.map +1 -0
  350. package/dist/astro/routes/api/import/wordpress/prepare.mjs +120 -0
  351. package/dist/astro/routes/api/import/wordpress/prepare.mjs.map +1 -0
  352. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts +49 -0
  353. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts.map +1 -0
  354. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs +131 -0
  355. package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs.map +1 -0
  356. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +22 -0
  357. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -0
  358. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +139 -0
  359. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -0
  360. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +16 -0
  361. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts.map +1 -0
  362. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +71 -0
  363. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -0
  364. package/dist/astro/routes/api/import/wordpress-plugin/callback.d.mts +8 -0
  365. package/dist/astro/routes/api/import/wordpress-plugin/callback.d.mts.map +1 -0
  366. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +29 -0
  367. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs.map +1 -0
  368. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +20 -0
  369. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -0
  370. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +219 -0
  371. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -0
  372. package/dist/astro/routes/api/manifest.d.mts +8 -0
  373. package/dist/astro/routes/api/manifest.d.mts.map +1 -0
  374. package/dist/astro/routes/api/manifest.mjs +47 -0
  375. package/dist/astro/routes/api/manifest.mjs.map +1 -0
  376. package/dist/astro/routes/api/mcp.d.mts +16 -0
  377. package/dist/astro/routes/api/mcp.d.mts.map +1 -0
  378. package/dist/astro/routes/api/mcp.mjs +1414 -0
  379. package/dist/astro/routes/api/mcp.mjs.map +1 -0
  380. package/dist/astro/routes/api/media/_id_/confirm.d.mts +11 -0
  381. package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -0
  382. package/dist/astro/routes/api/media/_id_/confirm.mjs +61 -0
  383. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -0
  384. package/dist/astro/routes/api/media/_id_.d.mts +23 -0
  385. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -0
  386. package/dist/astro/routes/api/media/_id_.mjs +83 -0
  387. package/dist/astro/routes/api/media/_id_.mjs.map +1 -0
  388. package/dist/astro/routes/api/media/file/_...key_.d.mts +8 -0
  389. package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -0
  390. package/dist/astro/routes/api/media/file/_...key_.mjs +52 -0
  391. package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -0
  392. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.d.mts +15 -0
  393. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.d.mts.map +1 -0
  394. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +52 -0
  395. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs.map +1 -0
  396. package/dist/astro/routes/api/media/providers/_providerId_/index.d.mts +15 -0
  397. package/dist/astro/routes/api/media/providers/_providerId_/index.d.mts.map +1 -0
  398. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +75 -0
  399. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs.map +1 -0
  400. package/dist/astro/routes/api/media/providers/index.d.mts +11 -0
  401. package/dist/astro/routes/api/media/providers/index.d.mts.map +1 -0
  402. package/dist/astro/routes/api/media/providers/index.mjs +21 -0
  403. package/dist/astro/routes/api/media/providers/index.mjs.map +1 -0
  404. package/dist/astro/routes/api/media/upload-url.d.mts +11 -0
  405. package/dist/astro/routes/api/media/upload-url.d.mts.map +1 -0
  406. package/dist/astro/routes/api/media/upload-url.mjs +82 -0
  407. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -0
  408. package/dist/astro/routes/api/media.d.mts +17 -0
  409. package/dist/astro/routes/api/media.d.mts.map +1 -0
  410. package/dist/astro/routes/api/media.mjs +138 -0
  411. package/dist/astro/routes/api/media.mjs.map +1 -0
  412. package/dist/astro/routes/api/menus/_name_/items/_id_.d.mts +9 -0
  413. package/dist/astro/routes/api/menus/_name_/items/_id_.d.mts.map +1 -0
  414. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +48 -0
  415. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -0
  416. package/dist/astro/routes/api/menus/_name_/items.d.mts +8 -0
  417. package/dist/astro/routes/api/menus/_name_/items.d.mts.map +1 -0
  418. package/dist/astro/routes/api/menus/_name_/items.mjs +31 -0
  419. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -0
  420. package/dist/astro/routes/api/menus/_name_/reorder.d.mts +8 -0
  421. package/dist/astro/routes/api/menus/_name_/reorder.d.mts.map +1 -0
  422. package/dist/astro/routes/api/menus/_name_/reorder.mjs +31 -0
  423. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -0
  424. package/dist/astro/routes/api/menus/_name_/translations.d.mts +9 -0
  425. package/dist/astro/routes/api/menus/_name_/translations.d.mts.map +1 -0
  426. package/dist/astro/routes/api/menus/_name_/translations.mjs +62 -0
  427. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -0
  428. package/dist/astro/routes/api/menus/_name_.d.mts +10 -0
  429. package/dist/astro/routes/api/menus/_name_.d.mts.map +1 -0
  430. package/dist/astro/routes/api/menus/_name_.mjs +60 -0
  431. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -0
  432. package/dist/astro/routes/api/menus/index.d.mts +9 -0
  433. package/dist/astro/routes/api/menus/index.d.mts.map +1 -0
  434. package/dist/astro/routes/api/menus/index.mjs +40 -0
  435. package/dist/astro/routes/api/menus/index.mjs.map +1 -0
  436. package/dist/astro/routes/api/oauth/authorize.d.mts +9 -0
  437. package/dist/astro/routes/api/oauth/authorize.d.mts.map +1 -0
  438. package/dist/astro/routes/api/oauth/authorize.mjs +260 -0
  439. package/dist/astro/routes/api/oauth/authorize.mjs.map +1 -0
  440. package/dist/astro/routes/api/oauth/device/authorize.d.mts +8 -0
  441. package/dist/astro/routes/api/oauth/device/authorize.d.mts.map +1 -0
  442. package/dist/astro/routes/api/oauth/device/authorize.mjs +32 -0
  443. package/dist/astro/routes/api/oauth/device/authorize.mjs.map +1 -0
  444. package/dist/astro/routes/api/oauth/device/code.d.mts +8 -0
  445. package/dist/astro/routes/api/oauth/device/code.d.mts.map +1 -0
  446. package/dist/astro/routes/api/oauth/device/code.mjs +36 -0
  447. package/dist/astro/routes/api/oauth/device/code.mjs.map +1 -0
  448. package/dist/astro/routes/api/oauth/device/token.d.mts +8 -0
  449. package/dist/astro/routes/api/oauth/device/token.d.mts.map +1 -0
  450. package/dist/astro/routes/api/oauth/device/token.mjs +47 -0
  451. package/dist/astro/routes/api/oauth/device/token.mjs.map +1 -0
  452. package/dist/astro/routes/api/oauth/register.d.mts +9 -0
  453. package/dist/astro/routes/api/oauth/register.d.mts.map +1 -0
  454. package/dist/astro/routes/api/oauth/register.mjs +113 -0
  455. package/dist/astro/routes/api/oauth/register.mjs.map +1 -0
  456. package/dist/astro/routes/api/oauth/token/refresh.d.mts +8 -0
  457. package/dist/astro/routes/api/oauth/token/refresh.d.mts.map +1 -0
  458. package/dist/astro/routes/api/oauth/token/refresh.mjs +30 -0
  459. package/dist/astro/routes/api/oauth/token/refresh.mjs.map +1 -0
  460. package/dist/astro/routes/api/oauth/token/revoke.d.mts +8 -0
  461. package/dist/astro/routes/api/oauth/token/revoke.d.mts.map +1 -0
  462. package/dist/astro/routes/api/oauth/token/revoke.mjs +27 -0
  463. package/dist/astro/routes/api/oauth/token/revoke.mjs.map +1 -0
  464. package/dist/astro/routes/api/oauth/token.d.mts +9 -0
  465. package/dist/astro/routes/api/oauth/token.d.mts.map +1 -0
  466. package/dist/astro/routes/api/oauth/token.mjs +141 -0
  467. package/dist/astro/routes/api/oauth/token.mjs.map +1 -0
  468. package/dist/astro/routes/api/openapi.json.d.mts +8 -0
  469. package/dist/astro/routes/api/openapi.json.d.mts.map +1 -0
  470. package/dist/astro/routes/api/openapi.json.mjs +2642 -0
  471. package/dist/astro/routes/api/openapi.json.mjs.map +1 -0
  472. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts +12 -0
  473. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts.map +1 -0
  474. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +78 -0
  475. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs.map +1 -0
  476. package/dist/astro/routes/api/redirects/404s/index.d.mts +10 -0
  477. package/dist/astro/routes/api/redirects/404s/index.d.mts.map +1 -0
  478. package/dist/astro/routes/api/redirects/404s/index.mjs +62 -0
  479. package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -0
  480. package/dist/astro/routes/api/redirects/404s/summary.d.mts +8 -0
  481. package/dist/astro/routes/api/redirects/404s/summary.d.mts.map +1 -0
  482. package/dist/astro/routes/api/redirects/404s/summary.mjs +34 -0
  483. package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -0
  484. package/dist/astro/routes/api/redirects/_id_.d.mts +10 -0
  485. package/dist/astro/routes/api/redirects/_id_.d.mts.map +1 -0
  486. package/dist/astro/routes/api/redirects/_id_.mjs +71 -0
  487. package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -0
  488. package/dist/astro/routes/api/redirects/index.d.mts +9 -0
  489. package/dist/astro/routes/api/redirects/index.d.mts.map +1 -0
  490. package/dist/astro/routes/api/redirects/index.mjs +52 -0
  491. package/dist/astro/routes/api/redirects/index.mjs.map +1 -0
  492. package/dist/astro/routes/api/revisions/_revisionId_/index.d.mts +8 -0
  493. package/dist/astro/routes/api/revisions/_revisionId_/index.d.mts.map +1 -0
  494. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +19 -0
  495. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs.map +1 -0
  496. package/dist/astro/routes/api/revisions/_revisionId_/restore.d.mts +8 -0
  497. package/dist/astro/routes/api/revisions/_revisionId_/restore.d.mts.map +1 -0
  498. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +26 -0
  499. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs.map +1 -0
  500. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.d.mts +10 -0
  501. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.d.mts.map +1 -0
  502. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +75 -0
  503. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -0
  504. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.d.mts +9 -0
  505. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.d.mts.map +1 -0
  506. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +63 -0
  507. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -0
  508. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.d.mts +8 -0
  509. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.d.mts.map +1 -0
  510. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +54 -0
  511. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -0
  512. package/dist/astro/routes/api/schema/collections/_slug_/index.d.mts +10 -0
  513. package/dist/astro/routes/api/schema/collections/_slug_/index.d.mts.map +1 -0
  514. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +79 -0
  515. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -0
  516. package/dist/astro/routes/api/schema/collections/index.d.mts +9 -0
  517. package/dist/astro/routes/api/schema/collections/index.d.mts.map +1 -0
  518. package/dist/astro/routes/api/schema/collections/index.mjs +63 -0
  519. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -0
  520. package/dist/astro/routes/api/schema/index.d.mts +8 -0
  521. package/dist/astro/routes/api/schema/index.d.mts.map +1 -0
  522. package/dist/astro/routes/api/schema/index.mjs +82 -0
  523. package/dist/astro/routes/api/schema/index.mjs.map +1 -0
  524. package/dist/astro/routes/api/schema/orphans/_slug_.d.mts +8 -0
  525. package/dist/astro/routes/api/schema/orphans/_slug_.d.mts.map +1 -0
  526. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +55 -0
  527. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -0
  528. package/dist/astro/routes/api/schema/orphans/index.d.mts +8 -0
  529. package/dist/astro/routes/api/schema/orphans/index.d.mts.map +1 -0
  530. package/dist/astro/routes/api/schema/orphans/index.mjs +50 -0
  531. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -0
  532. package/dist/astro/routes/api/search/enable.d.mts +16 -0
  533. package/dist/astro/routes/api/search/enable.d.mts.map +1 -0
  534. package/dist/astro/routes/api/search/enable.mjs +55 -0
  535. package/dist/astro/routes/api/search/enable.mjs.map +1 -0
  536. package/dist/astro/routes/api/search/index.d.mts +17 -0
  537. package/dist/astro/routes/api/search/index.d.mts.map +1 -0
  538. package/dist/astro/routes/api/search/index.mjs +52 -0
  539. package/dist/astro/routes/api/search/index.mjs.map +1 -0
  540. package/dist/astro/routes/api/search/rebuild.d.mts +14 -0
  541. package/dist/astro/routes/api/search/rebuild.d.mts.map +1 -0
  542. package/dist/astro/routes/api/search/rebuild.mjs +48 -0
  543. package/dist/astro/routes/api/search/rebuild.mjs.map +1 -0
  544. package/dist/astro/routes/api/search/stats.d.mts +11 -0
  545. package/dist/astro/routes/api/search/stats.d.mts.map +1 -0
  546. package/dist/astro/routes/api/search/stats.mjs +29 -0
  547. package/dist/astro/routes/api/search/stats.mjs.map +1 -0
  548. package/dist/astro/routes/api/search/suggest.d.mts +16 -0
  549. package/dist/astro/routes/api/search/suggest.d.mts.map +1 -0
  550. package/dist/astro/routes/api/search/suggest.mjs +43 -0
  551. package/dist/astro/routes/api/search/suggest.mjs.map +1 -0
  552. package/dist/astro/routes/api/sections/_slug_.d.mts +10 -0
  553. package/dist/astro/routes/api/sections/_slug_.d.mts.map +1 -0
  554. package/dist/astro/routes/api/sections/_slug_.mjs +65 -0
  555. package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -0
  556. package/dist/astro/routes/api/sections/index.d.mts +9 -0
  557. package/dist/astro/routes/api/sections/index.d.mts.map +1 -0
  558. package/dist/astro/routes/api/sections/index.mjs +48 -0
  559. package/dist/astro/routes/api/sections/index.mjs.map +1 -0
  560. package/dist/astro/routes/api/settings/email.d.mts +18 -0
  561. package/dist/astro/routes/api/settings/email.d.mts.map +1 -0
  562. package/dist/astro/routes/api/settings/email.mjs +105 -0
  563. package/dist/astro/routes/api/settings/email.mjs.map +1 -0
  564. package/dist/astro/routes/api/settings.d.mts +21 -0
  565. package/dist/astro/routes/api/settings.d.mts.map +1 -0
  566. package/dist/astro/routes/api/settings.mjs +58 -0
  567. package/dist/astro/routes/api/settings.mjs.map +1 -0
  568. package/dist/astro/routes/api/setup/admin-verify.d.mts +8 -0
  569. package/dist/astro/routes/api/setup/admin-verify.d.mts.map +1 -0
  570. package/dist/astro/routes/api/setup/admin-verify.mjs +68 -0
  571. package/dist/astro/routes/api/setup/admin-verify.mjs.map +1 -0
  572. package/dist/astro/routes/api/setup/admin.d.mts +8 -0
  573. package/dist/astro/routes/api/setup/admin.d.mts.map +1 -0
  574. package/dist/astro/routes/api/setup/admin.mjs +69 -0
  575. package/dist/astro/routes/api/setup/admin.mjs.map +1 -0
  576. package/dist/astro/routes/api/setup/dev-bypass.d.mts +9 -0
  577. package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -0
  578. package/dist/astro/routes/api/setup/dev-bypass.mjs +139 -0
  579. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -0
  580. package/dist/astro/routes/api/setup/dev-reset.d.mts +8 -0
  581. package/dist/astro/routes/api/setup/dev-reset.d.mts.map +1 -0
  582. package/dist/astro/routes/api/setup/dev-reset.mjs +25 -0
  583. package/dist/astro/routes/api/setup/dev-reset.mjs.map +1 -0
  584. package/dist/astro/routes/api/setup/index.d.mts +8 -0
  585. package/dist/astro/routes/api/setup/index.d.mts.map +1 -0
  586. package/dist/astro/routes/api/setup/index.mjs +93 -0
  587. package/dist/astro/routes/api/setup/index.mjs.map +1 -0
  588. package/dist/astro/routes/api/setup/status.d.mts +8 -0
  589. package/dist/astro/routes/api/setup/status.d.mts.map +1 -0
  590. package/dist/astro/routes/api/setup/status.mjs +60 -0
  591. package/dist/astro/routes/api/setup/status.mjs.map +1 -0
  592. package/dist/astro/routes/api/snapshot.d.mts +8 -0
  593. package/dist/astro/routes/api/snapshot.d.mts.map +1 -0
  594. package/dist/astro/routes/api/snapshot.mjs +270 -0
  595. package/dist/astro/routes/api/snapshot.mjs.map +1 -0
  596. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.d.mts +9 -0
  597. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.d.mts.map +1 -0
  598. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +72 -0
  599. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -0
  600. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts +19 -0
  601. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts.map +1 -0
  602. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +80 -0
  603. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -0
  604. package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts +15 -0
  605. package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -0
  606. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +59 -0
  607. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -0
  608. package/dist/astro/routes/api/taxonomies/index.d.mts +15 -0
  609. package/dist/astro/routes/api/taxonomies/index.d.mts.map +1 -0
  610. package/dist/astro/routes/api/taxonomies/index.mjs +55 -0
  611. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -0
  612. package/dist/astro/routes/api/themes/preview.d.mts +8 -0
  613. package/dist/astro/routes/api/themes/preview.d.mts.map +1 -0
  614. package/dist/astro/routes/api/themes/preview.mjs +49 -0
  615. package/dist/astro/routes/api/themes/preview.mjs.map +1 -0
  616. package/dist/astro/routes/api/typegen.d.mts +18 -0
  617. package/dist/astro/routes/api/typegen.d.mts.map +1 -0
  618. package/dist/astro/routes/api/typegen.mjs +78 -0
  619. package/dist/astro/routes/api/typegen.mjs.map +1 -0
  620. package/dist/astro/routes/api/well-known/auth.d.mts +8 -0
  621. package/dist/astro/routes/api/well-known/auth.d.mts.map +1 -0
  622. package/dist/astro/routes/api/well-known/auth.mjs +42 -0
  623. package/dist/astro/routes/api/well-known/auth.mjs.map +1 -0
  624. package/dist/astro/routes/api/well-known/oauth-authorization-server.d.mts +8 -0
  625. package/dist/astro/routes/api/well-known/oauth-authorization-server.d.mts.map +1 -0
  626. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +32 -0
  627. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs.map +1 -0
  628. package/dist/astro/routes/api/well-known/oauth-protected-resource.d.mts +8 -0
  629. package/dist/astro/routes/api/well-known/oauth-protected-resource.d.mts.map +1 -0
  630. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +21 -0
  631. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs.map +1 -0
  632. package/dist/astro/routes/api/widget-areas/_name_/reorder.d.mts +8 -0
  633. package/dist/astro/routes/api/widget-areas/_name_/reorder.d.mts.map +1 -0
  634. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +36 -0
  635. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -0
  636. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts +9 -0
  637. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts.map +1 -0
  638. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +62 -0
  639. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -0
  640. package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts +8 -0
  641. package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts.map +1 -0
  642. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +49 -0
  643. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -0
  644. package/dist/astro/routes/api/widget-areas/_name_.d.mts +9 -0
  645. package/dist/astro/routes/api/widget-areas/_name_.d.mts.map +1 -0
  646. package/dist/astro/routes/api/widget-areas/_name_.mjs +49 -0
  647. package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -0
  648. package/dist/astro/routes/api/widget-areas/index.d.mts +9 -0
  649. package/dist/astro/routes/api/widget-areas/index.d.mts.map +1 -0
  650. package/dist/astro/routes/api/widget-areas/index.mjs +59 -0
  651. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -0
  652. package/dist/astro/routes/api/widget-components.d.mts +8 -0
  653. package/dist/astro/routes/api/widget-components.d.mts.map +1 -0
  654. package/dist/astro/routes/api/widget-components.mjs +18 -0
  655. package/dist/astro/routes/api/widget-components.mjs.map +1 -0
  656. package/dist/astro/routes/robots.txt.d.mts +8 -0
  657. package/dist/astro/routes/robots.txt.d.mts.map +1 -0
  658. package/dist/astro/routes/robots.txt.mjs +61 -0
  659. package/dist/astro/routes/robots.txt.mjs.map +1 -0
  660. package/dist/astro/routes/sitemap-_collection_.xml.d.mts +8 -0
  661. package/dist/astro/routes/sitemap-_collection_.xml.d.mts.map +1 -0
  662. package/dist/astro/routes/sitemap-_collection_.xml.mjs +71 -0
  663. package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -0
  664. package/dist/astro/routes/sitemap.xml.d.mts +8 -0
  665. package/dist/astro/routes/sitemap.xml.d.mts.map +1 -0
  666. package/dist/astro/routes/sitemap.xml.mjs +64 -0
  667. package/dist/astro/routes/sitemap.xml.mjs.map +1 -0
  668. package/dist/astro/types.d.mts +48 -8
  669. package/dist/astro/types.d.mts.map +1 -1
  670. package/dist/auth/providers/github.d.mts +13 -0
  671. package/dist/auth/providers/github.d.mts.map +1 -0
  672. package/dist/auth/providers/github.mjs +18 -0
  673. package/dist/auth/providers/github.mjs.map +1 -0
  674. package/dist/auth/providers/google.d.mts +13 -0
  675. package/dist/auth/providers/google.d.mts.map +1 -0
  676. package/dist/auth/providers/google.mjs +18 -0
  677. package/dist/auth/providers/google.mjs.map +1 -0
  678. package/dist/authorize-BlyCH-96.mjs +37 -0
  679. package/dist/authorize-BlyCH-96.mjs.map +1 -0
  680. package/dist/{base64-MBPo9ozB.mjs → base64-CqR-7kqF.mjs} +1 -1
  681. package/dist/{base64-MBPo9ozB.mjs.map → base64-CqR-7kqF.mjs.map} +1 -1
  682. package/dist/{byline-gFn1r0vA.mjs → byline-D09BaS4j.mjs} +4 -4
  683. package/dist/{byline-gFn1r0vA.mjs.map → byline-D09BaS4j.mjs.map} +1 -1
  684. package/dist/{bylines-DTFI8nDM.mjs → bylines-BTM2xtP8.mjs} +6 -6
  685. package/dist/{bylines-DTFI8nDM.mjs.map → bylines-BTM2xtP8.mjs.map} +1 -1
  686. package/dist/bylines-C6eYUWlZ.d.mts +1971 -0
  687. package/dist/bylines-C6eYUWlZ.d.mts.map +1 -0
  688. package/dist/{cache-BAJbeoZ8.mjs → cache-CXCpjWiL.mjs} +3 -3
  689. package/dist/{cache-BAJbeoZ8.mjs.map → cache-CXCpjWiL.mjs.map} +1 -1
  690. package/dist/challenge-store-CJ0OOHOr.mjs +49 -0
  691. package/dist/challenge-store-CJ0OOHOr.mjs.map +1 -0
  692. package/dist/{chunks-BK1oZS-l.mjs → chunks-DyGtu1Bv.mjs} +2 -2
  693. package/dist/{chunks-BK1oZS-l.mjs.map → chunks-DyGtu1Bv.mjs.map} +1 -1
  694. package/dist/cli/index.mjs +23 -18
  695. package/dist/cli/index.mjs.map +1 -1
  696. package/dist/client/cf-access.d.mts +1 -1
  697. package/dist/client/index.d.mts +1 -1
  698. package/dist/client/index.d.mts.map +1 -1
  699. package/dist/client/index.mjs +2 -2
  700. package/dist/client/index.mjs.map +1 -1
  701. package/dist/comment-Dd9MI82-.mjs +247 -0
  702. package/dist/comment-Dd9MI82-.mjs.map +1 -0
  703. package/dist/comments-koGI0FrK.mjs +204 -0
  704. package/dist/comments-koGI0FrK.mjs.map +1 -0
  705. package/dist/components-mZem7pbe.mjs +108 -0
  706. package/dist/components-mZem7pbe.mjs.map +1 -0
  707. package/dist/{content-CERxPUN0.mjs → content-D6YG26WG.mjs} +10 -34
  708. package/dist/content-D6YG26WG.mjs.map +1 -0
  709. package/dist/context-qF8d3IPR.mjs +879 -0
  710. package/dist/context-qF8d3IPR.mjs.map +1 -0
  711. package/dist/cron-H8eJ46dv.mjs +264 -0
  712. package/dist/cron-H8eJ46dv.mjs.map +1 -0
  713. package/dist/dashboard-BmWSIUwY.mjs +105 -0
  714. package/dist/dashboard-BmWSIUwY.mjs.map +1 -0
  715. package/dist/db/index.d.mts +3 -3
  716. package/dist/db/index.mjs +1 -1
  717. package/dist/db/libsql.d.mts +1 -1
  718. package/dist/db/postgres.d.mts +1 -1
  719. package/dist/db/sqlite.d.mts +1 -1
  720. package/dist/{db-errors-B7P2pSCn.mjs → db-errors-CGN9kJfo.mjs} +1 -1
  721. package/dist/{db-errors-B7P2pSCn.mjs.map → db-errors-CGN9kJfo.mjs.map} +1 -1
  722. package/dist/{default-pHuz9WF6.mjs → default-Dbs22Gg4.mjs} +1 -1
  723. package/dist/{default-pHuz9WF6.mjs.map → default-Dbs22Gg4.mjs.map} +1 -1
  724. package/dist/device-flow-BqJRxa0Q.mjs +467 -0
  725. package/dist/device-flow-BqJRxa0Q.mjs.map +1 -0
  726. package/dist/email-console-Dmp5Q-P2.mjs +50 -0
  727. package/dist/email-console-Dmp5Q-P2.mjs.map +1 -0
  728. package/dist/error-tSQWIl5U.mjs +437 -0
  729. package/dist/error-tSQWIl5U.mjs.map +1 -0
  730. package/dist/escape-B8bdIryO.mjs +9 -0
  731. package/dist/escape-B8bdIryO.mjs.map +1 -0
  732. package/dist/fts-manager-B633C-kQ.mjs +339 -0
  733. package/dist/fts-manager-B633C-kQ.mjs.map +1 -0
  734. package/dist/hash-DlUxGhQS.mjs +33 -0
  735. package/dist/hash-DlUxGhQS.mjs.map +1 -0
  736. package/dist/import-CNfLOgDE.mjs +1531 -0
  737. package/dist/import-CNfLOgDE.mjs.map +1 -0
  738. package/dist/index-D2gvztOP.d.mts +262 -0
  739. package/dist/index-D2gvztOP.d.mts.map +1 -0
  740. package/dist/{index-Dlkzhb4C.d.mts → index-UmOMt9T-.d.mts} +310 -911
  741. package/dist/index-UmOMt9T-.d.mts.map +1 -0
  742. package/dist/index.d.mts +17 -11
  743. package/dist/index.mjs +57 -28
  744. package/dist/{load-DR1VwFXR.mjs → load-QzYRpVN3.mjs} +2 -2
  745. package/dist/{load-DR1VwFXR.mjs.map → load-QzYRpVN3.mjs.map} +1 -1
  746. package/dist/{loader-ou_PXAjg.mjs → loader-Cs6-Bqe6.mjs} +4 -4
  747. package/dist/{loader-ou_PXAjg.mjs.map → loader-Cs6-Bqe6.mjs.map} +1 -1
  748. package/dist/{manifest-schema-Bp6d4d4n.mjs → manifest-schema-HCtSh4Jq.mjs} +1 -1
  749. package/dist/{manifest-schema-Bp6d4d4n.mjs.map → manifest-schema-HCtSh4Jq.mjs.map} +1 -1
  750. package/dist/media/index.d.mts +1 -1
  751. package/dist/media/index.mjs +2 -1
  752. package/dist/media/index.mjs.map +1 -1
  753. package/dist/media/local-runtime.d.mts +11 -7
  754. package/dist/media/local-runtime.d.mts.map +1 -1
  755. package/dist/media/local-runtime.mjs +7 -6
  756. package/dist/media/local-runtime.mjs.map +1 -1
  757. package/dist/media-Dg7he9uK.mjs +209 -0
  758. package/dist/media-Dg7he9uK.mjs.map +1 -0
  759. package/dist/media-allowlist-B8EX01DH.mjs +32 -0
  760. package/dist/media-allowlist-B8EX01DH.mjs.map +1 -0
  761. package/dist/menus-DOzIecHi.mjs +723 -0
  762. package/dist/menus-DOzIecHi.mjs.map +1 -0
  763. package/dist/menus-X4Z-eBA1.mjs +2788 -0
  764. package/dist/menus-X4Z-eBA1.mjs.map +1 -0
  765. package/dist/mime-KV5TqkMN.mjs +36 -0
  766. package/dist/mime-KV5TqkMN.mjs.map +1 -0
  767. package/dist/{mode-YhqNVef_.mjs → mode-DPRPvJYm.mjs} +1 -1
  768. package/dist/{mode-YhqNVef_.mjs.map → mode-DPRPvJYm.mjs.map} +1 -1
  769. package/dist/normalize-CN5kRSMC.mjs +151 -0
  770. package/dist/normalize-CN5kRSMC.mjs.map +1 -0
  771. package/dist/oauth-authorization-62GmpGIH.mjs +275 -0
  772. package/dist/oauth-authorization-62GmpGIH.mjs.map +1 -0
  773. package/dist/oauth-clients-D_B0_-Bz.mjs +266 -0
  774. package/dist/oauth-clients-D_B0_-Bz.mjs.map +1 -0
  775. package/dist/oauth-state-store-DpsZViTu.mjs +49 -0
  776. package/dist/oauth-state-store-DpsZViTu.mjs.map +1 -0
  777. package/dist/oauth-user-lookup-meyS2oB1.mjs +26 -0
  778. package/dist/oauth-user-lookup-meyS2oB1.mjs.map +1 -0
  779. package/dist/{options-nPxWnrya.mjs → options-BL4X94qY.mjs} +1 -1
  780. package/dist/{options-nPxWnrya.mjs.map → options-BL4X94qY.mjs.map} +1 -1
  781. package/dist/options-Cq64Wx0O.d.mts +207 -0
  782. package/dist/options-Cq64Wx0O.d.mts.map +1 -0
  783. package/dist/page/index.d.mts +2 -2
  784. package/dist/parse-BFTPon-J.mjs +89 -0
  785. package/dist/parse-BFTPon-J.mjs.map +1 -0
  786. package/dist/passkey-config-Cg86_ISa.mjs +46 -0
  787. package/dist/passkey-config-Cg86_ISa.mjs.map +1 -0
  788. package/dist/{patterns-DsUZ4uxI.mjs → patterns-CqG5Ya3i.mjs} +54 -2
  789. package/dist/{patterns-DsUZ4uxI.mjs.map → patterns-CqG5Ya3i.mjs.map} +1 -1
  790. package/dist/{placeholder-CDPtkelt.d.mts → placeholder-D3cFCU9y.d.mts} +2 -1
  791. package/dist/{placeholder-CDPtkelt.d.mts.map → placeholder-D3cFCU9y.d.mts.map} +1 -1
  792. package/dist/placeholder-LqmHqvBw.mjs +143 -0
  793. package/dist/placeholder-LqmHqvBw.mjs.map +1 -0
  794. package/dist/plugin-types.d.mts +122 -0
  795. package/dist/plugin-types.d.mts.map +1 -0
  796. package/dist/plugin-types.mjs +1 -0
  797. package/dist/plugins/adapt-sandbox-entry.d.mts +20 -12
  798. package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
  799. package/dist/plugins/adapt-sandbox-entry.mjs +46 -23
  800. package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
  801. package/dist/preview-C1LOEbWZ.mjs +107 -0
  802. package/dist/preview-C1LOEbWZ.mjs.map +1 -0
  803. package/dist/{public-url-B1AxbbbQ.mjs → public-url-CseXl9Fv.mjs} +39 -2
  804. package/dist/{public-url-B1AxbbbQ.mjs.map → public-url-CseXl9Fv.mjs.map} +1 -1
  805. package/dist/{query-yA3-rFji.mjs → query-axZmO6Tn.mjs} +12 -12
  806. package/dist/{query-yA3-rFji.mjs.map → query-axZmO6Tn.mjs.map} +1 -1
  807. package/dist/rate-limit-t5CVjCO6.mjs +120 -0
  808. package/dist/rate-limit-t5CVjCO6.mjs.map +1 -0
  809. package/dist/redirect-DGRsLO2I.mjs +17 -0
  810. package/dist/redirect-DGRsLO2I.mjs.map +1 -0
  811. package/dist/{redirect-C5H7VGIX.mjs → redirect-DkaDxq8e.mjs} +3 -3
  812. package/dist/{redirect-C5H7VGIX.mjs.map → redirect-DkaDxq8e.mjs.map} +1 -1
  813. package/dist/redirects-D1fdd68T.mjs +573 -0
  814. package/dist/redirects-D1fdd68T.mjs.map +1 -0
  815. package/dist/redirects-Dmj6KRU3.mjs +1141 -0
  816. package/dist/redirects-Dmj6KRU3.mjs.map +1 -0
  817. package/dist/{registry-Do34mz_P.mjs → registry-BnCeHYsf.mjs} +8 -300
  818. package/dist/registry-BnCeHYsf.mjs.map +1 -0
  819. package/dist/{request-cache-D4I69LeL.mjs → request-cache-dzCt8TZB.mjs} +1 -1
  820. package/dist/{request-cache-D4I69LeL.mjs.map → request-cache-dzCt8TZB.mjs.map} +1 -1
  821. package/dist/request-meta-CLCwSQOS.mjs +140 -0
  822. package/dist/request-meta-CLCwSQOS.mjs.map +1 -0
  823. package/dist/{runner-Iu3IZSDM.d.mts → runner-DcfZewkO.d.mts} +2 -2
  824. package/dist/{runner-Iu3IZSDM.d.mts.map → runner-DcfZewkO.d.mts.map} +1 -1
  825. package/dist/{runner-DIcU2UCC.mjs → runner-DdnQIwz_.mjs} +436 -187
  826. package/dist/runner-DdnQIwz_.mjs.map +1 -0
  827. package/dist/runtime.d.mts +10 -6
  828. package/dist/runtime.d.mts.map +1 -1
  829. package/dist/runtime.mjs +3 -3
  830. package/dist/schema-BmqagCwG.mjs +41 -0
  831. package/dist/schema-BmqagCwG.mjs.map +1 -0
  832. package/dist/search-CPrvO5u8.mjs +376 -0
  833. package/dist/search-CPrvO5u8.mjs.map +1 -0
  834. package/dist/{secrets-CZ8rxLX3.mjs → secrets-6pgZyq0K.mjs} +3 -3
  835. package/dist/{secrets-CZ8rxLX3.mjs.map → secrets-6pgZyq0K.mjs.map} +1 -1
  836. package/dist/sections-Cm-zb-gZ.mjs +346 -0
  837. package/dist/sections-Cm-zb-gZ.mjs.map +1 -0
  838. package/dist/seed/index.d.mts +2 -2
  839. package/dist/seed/index.mjs +19 -15
  840. package/dist/seo/index.d.mts +1 -1
  841. package/dist/seo-BoR4wCUh.mjs +86 -0
  842. package/dist/seo-BoR4wCUh.mjs.map +1 -0
  843. package/dist/seo-DRq9-EPP.mjs +130 -0
  844. package/dist/seo-DRq9-EPP.mjs.map +1 -0
  845. package/dist/service-vByySp-2.mjs +195 -0
  846. package/dist/service-vByySp-2.mjs.map +1 -0
  847. package/dist/settings-CBBj7HUd.mjs +51 -0
  848. package/dist/settings-CBBj7HUd.mjs.map +1 -0
  849. package/dist/settings-xQKsWnzQ.mjs +235 -0
  850. package/dist/settings-xQKsWnzQ.mjs.map +1 -0
  851. package/dist/setup-BGAJ2uXs.mjs +137 -0
  852. package/dist/setup-BGAJ2uXs.mjs.map +1 -0
  853. package/dist/setup-complete-C6ZCLhKo.mjs +26 -0
  854. package/dist/setup-complete-C6ZCLhKo.mjs.map +1 -0
  855. package/dist/setup-nonce-CY1gQiAU.mjs +25 -0
  856. package/dist/setup-nonce-CY1gQiAU.mjs.map +1 -0
  857. package/dist/site-url-D-M4Fd8O.mjs +13 -0
  858. package/dist/site-url-D-M4Fd8O.mjs.map +1 -0
  859. package/dist/slugify-Cjh1ssOZ.mjs +30 -0
  860. package/dist/slugify-Cjh1ssOZ.mjs.map +1 -0
  861. package/dist/ssrf-CTul4uQi.mjs +1 -0
  862. package/dist/ssrf-DzFN_qV-.mjs +332 -0
  863. package/dist/ssrf-DzFN_qV-.mjs.map +1 -0
  864. package/dist/storage/local.d.mts +1 -1
  865. package/dist/storage/local.mjs +1 -1
  866. package/dist/storage/s3.d.mts +1 -1
  867. package/dist/storage/s3.mjs +1 -1
  868. package/dist/{taxonomies-JmQQZiG1.mjs → taxonomies-Cn9UpaR2.mjs} +7 -7
  869. package/dist/{taxonomies-JmQQZiG1.mjs.map → taxonomies-Cn9UpaR2.mjs.map} +1 -1
  870. package/dist/taxonomies-Dc0mzlms.mjs +508 -0
  871. package/dist/taxonomies-Dc0mzlms.mjs.map +1 -0
  872. package/dist/{taxonomy-D6NvlKo8.mjs → taxonomy-wPfusMK9.mjs} +3 -3
  873. package/dist/{taxonomy-D6NvlKo8.mjs.map → taxonomy-wPfusMK9.mjs.map} +1 -1
  874. package/dist/{tokens-CyRDPVW2.mjs → tokens-DILYNZMi.mjs} +2 -2
  875. package/dist/{tokens-CyRDPVW2.mjs.map → tokens-DILYNZMi.mjs.map} +1 -1
  876. package/dist/{transaction-D44LBXvU.mjs → transaction-NQj4VJ7Z.mjs} +1 -1
  877. package/dist/{transaction-D44LBXvU.mjs.map → transaction-NQj4VJ7Z.mjs.map} +1 -1
  878. package/dist/{transport-DX_5rpsq.d.mts → transport-GeXlLscf.d.mts} +1 -1
  879. package/dist/{transport-DX_5rpsq.d.mts.map → transport-GeXlLscf.d.mts.map} +1 -1
  880. package/dist/{transport-xpzIjCIB.mjs → transport-fw-mKJzT.mjs} +1 -1
  881. package/dist/{transport-xpzIjCIB.mjs.map → transport-fw-mKJzT.mjs.map} +1 -1
  882. package/dist/trusted-proxy-CJhQIk65.mjs +51 -0
  883. package/dist/trusted-proxy-CJhQIk65.mjs.map +1 -0
  884. package/dist/{types-DgSc9Rpc.d.mts → types-B05e2naf.d.mts} +5 -59
  885. package/dist/types-B05e2naf.d.mts.map +1 -0
  886. package/dist/{types-B1gLSAH2.d.mts → types-BWhaSS7U.d.mts} +2 -75
  887. package/dist/types-BWhaSS7U.d.mts.map +1 -0
  888. package/dist/{types-BQx6ZXpR.d.mts → types-C1KKK4VP.d.mts} +3 -1
  889. package/dist/{types-BQx6ZXpR.d.mts.map → types-C1KKK4VP.d.mts.map} +1 -1
  890. package/dist/types-Cb2UCDJg.d.mts +345 -0
  891. package/dist/types-Cb2UCDJg.d.mts.map +1 -0
  892. package/dist/{types-BIgulNsW.mjs → types-CwXMEPRr.mjs} +10 -3
  893. package/dist/types-CwXMEPRr.mjs.map +1 -0
  894. package/dist/{types-B_CXXnzh.d.mts → types-CzvJd1ND.d.mts} +7 -1
  895. package/dist/{types-B_CXXnzh.d.mts.map → types-CzvJd1ND.d.mts.map} +1 -1
  896. package/dist/types-DFowNO60.d.mts +198 -0
  897. package/dist/types-DFowNO60.d.mts.map +1 -0
  898. package/dist/{types-56BKbld_.mjs → types-DSZl1Dsv.mjs} +1 -1
  899. package/dist/{types-56BKbld_.mjs.map → types-DSZl1Dsv.mjs.map} +1 -1
  900. package/dist/types-DW1l0gCv.d.mts +75 -0
  901. package/dist/types-DW1l0gCv.d.mts.map +1 -0
  902. package/dist/types-Db67HHlU.mjs +3 -0
  903. package/dist/{types-C-aFbqmA.d.mts → types-DmxPPXGf.d.mts} +1 -1
  904. package/dist/{types-C-aFbqmA.d.mts.map → types-DmxPPXGf.d.mts.map} +1 -1
  905. package/dist/{types-PafqtQuM.mjs → types-Dz9CGX_d.mjs} +1 -1
  906. package/dist/{types-PafqtQuM.mjs.map → types-Dz9CGX_d.mjs.map} +1 -1
  907. package/dist/user-Dr1bOCqS.mjs +155 -0
  908. package/dist/user-Dr1bOCqS.mjs.map +1 -0
  909. package/dist/utils-_F-rWBTN.mjs +286 -0
  910. package/dist/utils-_F-rWBTN.mjs.map +1 -0
  911. package/dist/{validate-BcC3m2O7.d.mts → validate-BpQGsmd7.d.mts} +5 -4
  912. package/dist/validate-BpQGsmd7.d.mts.map +1 -0
  913. package/dist/{validate-UK4Ja1uo.mjs → validate-DlFxcVVK.mjs} +3 -3
  914. package/dist/{validate-UK4Ja1uo.mjs.map → validate-DlFxcVVK.mjs.map} +1 -1
  915. package/dist/{validation-Vc5DQkJa.mjs → validation-BiFJqUp5.mjs} +6 -5
  916. package/dist/{validation-Vc5DQkJa.mjs.map → validation-BiFJqUp5.mjs.map} +1 -1
  917. package/dist/version-Dw7Z5PVU.mjs +7 -0
  918. package/dist/{version-BdP--J1g.mjs.map → version-Dw7Z5PVU.mjs.map} +1 -1
  919. package/dist/widgets-B9j_yzlk.mjs +106 -0
  920. package/dist/widgets-B9j_yzlk.mjs.map +1 -0
  921. package/dist/zod-generator-DSyz01KE.mjs +234 -0
  922. package/dist/zod-generator-DSyz01KE.mjs.map +1 -0
  923. package/locals.d.ts +1 -1
  924. package/package.json +37 -14
  925. package/src/api/handlers/content.ts +1 -0
  926. package/src/api/handlers/index.ts +7 -0
  927. package/src/api/handlers/marketplace.ts +27 -6
  928. package/src/api/handlers/menus.ts +157 -580
  929. package/src/api/handlers/plugins.ts +77 -31
  930. package/src/api/handlers/registry.ts +1086 -0
  931. package/src/api/openapi/document.ts +10 -4
  932. package/src/api/schemas/content.ts +1 -0
  933. package/src/api/schemas/menus.ts +27 -23
  934. package/src/api/types.ts +6 -0
  935. package/src/astro/integration/index.ts +1 -0
  936. package/src/astro/integration/route-naming.ts +19 -0
  937. package/src/astro/integration/routes.ts +25 -3
  938. package/src/astro/integration/runtime.ts +35 -8
  939. package/src/astro/middleware/auth.ts +8 -2
  940. package/src/astro/middleware/csp.ts +25 -3
  941. package/src/astro/middleware.ts +3 -0
  942. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +10 -0
  943. package/src/astro/routes/api/admin/plugins/registry/install.ts +107 -0
  944. package/src/astro/routes/api/auth/invite/register-options.ts +8 -1
  945. package/src/astro/routes/api/import/wordpress/execute.ts +185 -6
  946. package/src/astro/routes/api/menus/[name]/items/[id].ts +69 -0
  947. package/src/astro/routes/api/menus/[name]/items.ts +4 -65
  948. package/src/astro/types.ts +38 -0
  949. package/src/cli/wxr/parser.ts +263 -0
  950. package/src/client/index.ts +2 -1
  951. package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +166 -49
  952. package/src/database/migrations/038_registry_plugin_state.ts +130 -0
  953. package/src/database/migrations/039_fix_fts5_triggers.ts +264 -0
  954. package/src/database/migrations/runner.ts +4 -0
  955. package/src/database/repositories/content.ts +5 -1
  956. package/src/database/repositories/index.ts +14 -0
  957. package/src/database/repositories/menu.ts +644 -0
  958. package/src/database/repositories/types.ts +6 -0
  959. package/src/database/types.ts +5 -1
  960. package/src/emdash-runtime.ts +122 -34
  961. package/src/import/sources/wordpress-plugin.ts +9 -2
  962. package/src/import/sources/wxr.ts +16 -2
  963. package/src/import/ssrf.ts +20 -500
  964. package/src/import/wxr-taxonomies.ts +730 -0
  965. package/src/index.ts +3 -10
  966. package/src/media/normalize.ts +37 -4
  967. package/src/plugin-types.ts +240 -0
  968. package/src/plugins/adapt-sandbox-entry.ts +115 -39
  969. package/src/plugins/define-plugin.ts +34 -56
  970. package/src/plugins/index.ts +1 -9
  971. package/src/plugins/marketplace.ts +63 -4
  972. package/src/plugins/sandbox/index.ts +1 -1
  973. package/src/plugins/sandbox/noop.ts +2 -2
  974. package/src/plugins/sandbox/types.ts +7 -4
  975. package/src/plugins/state.ts +84 -38
  976. package/src/plugins/types.ts +2 -79
  977. package/src/registry/config.ts +311 -0
  978. package/src/registry/plugin-id.ts +116 -0
  979. package/src/registry/types.ts +206 -0
  980. package/src/search/fts-manager.ts +77 -15
  981. package/src/security/ssrf.ts +501 -0
  982. package/dist/apply-C1ZORgcy.mjs.map +0 -1
  983. package/dist/content-CERxPUN0.mjs.map +0 -1
  984. package/dist/error-D6LuHLw9.mjs +0 -27
  985. package/dist/error-D6LuHLw9.mjs.map +0 -1
  986. package/dist/index-Dlkzhb4C.d.mts.map +0 -1
  987. package/dist/placeholder-Ci0RLeCk.mjs +0 -268
  988. package/dist/placeholder-Ci0RLeCk.mjs.map +0 -1
  989. package/dist/registry-Do34mz_P.mjs.map +0 -1
  990. package/dist/runner-DIcU2UCC.mjs.map +0 -1
  991. package/dist/search-n-ZCMfr3.mjs +0 -9914
  992. package/dist/search-n-ZCMfr3.mjs.map +0 -1
  993. package/dist/settings-nTXPRi3D.mjs +0 -440
  994. package/dist/settings-nTXPRi3D.mjs.map +0 -1
  995. package/dist/types-B1gLSAH2.d.mts.map +0 -1
  996. package/dist/types-BIgulNsW.mjs.map +0 -1
  997. package/dist/types-Cug_RO3W.mjs +0 -16
  998. package/dist/types-Cug_RO3W.mjs.map +0 -1
  999. package/dist/types-DgSc9Rpc.d.mts.map +0 -1
  1000. package/dist/validate-BcC3m2O7.d.mts.map +0 -1
  1001. package/dist/version-BdP--J1g.mjs +0 -7
  1002. package/dist/zod-generator-CHnJUP2l.mjs +0 -137
  1003. package/dist/zod-generator-CHnJUP2l.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menus-X4Z-eBA1.mjs","names":["convertList","convertCodeBlock","convertImage","convertListItem","EXCLUSIVE_HOOK_KEY_PREFIX","resolveExclusiveHooksShared"],"sources":["../src/fields/image.ts","../src/fields/file.ts","../src/fields/reference.ts","../src/fields/portable-text.ts","../src/content/converters/prosemirror-to-portable-text.ts","../src/content/converters/portable-text-to-prosemirror.ts","../src/after.ts","../src/plugins/define-plugin.ts","../src/plugins/hooks.ts","../src/plugins/email.ts","../src/plugins/routes.ts","../src/plugins/manager.ts","../src/plugins/sandbox/noop.ts","../src/comments/query.ts","../src/menus/index.ts"],"sourcesContent":["import { z } from \"astro/zod\";\n\nimport type { FieldDefinition, ImageValue } from \"./types.js\";\n\nconst imageSchema = z.object({\n\tid: z.string(),\n\tsrc: z.string(),\n\talt: z.string().optional(),\n\twidth: z.number().optional(),\n\theight: z.number().optional(),\n});\n\nexport interface ImageOptions {\n\trequired?: boolean;\n\tmaxSize?: number; // in bytes\n\tallowedTypes?: string[]; // MIME types — exact or prefix\n}\n\nexport function image(options: ImageOptions = {}): FieldDefinition<ImageValue | undefined> {\n\tconst validation =\n\t\toptions.allowedTypes && options.allowedTypes.length > 0\n\t\t\t? { allowedMimeTypes: [...options.allowedTypes] }\n\t\t\t: undefined;\n\n\treturn {\n\t\ttype: \"image\",\n\t\tcolumnType: \"TEXT\",\n\t\tschema: options.required === false ? imageSchema.optional() : imageSchema,\n\t\toptions,\n\t\tui: {\n\t\t\twidget: \"image\",\n\t\t},\n\t\tvalidation,\n\t};\n}\n","import { z } from \"astro/zod\";\n\nimport type { FieldDefinition, FieldUIHints, FileValue } from \"./types.js\";\n\nexport interface FileOptions {\n\trequired?: boolean;\n\tmaxSize?: number; // In bytes\n\tallowedTypes?: string[]; // MIME types — exact (image/png) or prefix (image/)\n\thelpText?: string;\n}\n\nexport function file(options: FileOptions = {}): FieldDefinition<FileValue> {\n\tconst fileObjSchema = z.object({\n\t\tid: z.string(),\n\t\turl: z.string(),\n\t\tfilename: z.string(),\n\t\tmimeType: z.string(),\n\t\tsize: z.number(),\n\t});\n\n\tconst schema: z.ZodTypeAny = options.required ? fileObjSchema : fileObjSchema.optional();\n\n\tconst ui: FieldUIHints = {\n\t\twidget: \"file\",\n\t\thelpText: options.helpText,\n\t\tmaxSize: options.maxSize,\n\t};\n\n\tconst validation =\n\t\toptions.allowedTypes && options.allowedTypes.length > 0\n\t\t\t? { allowedMimeTypes: [...options.allowedTypes] }\n\t\t\t: undefined;\n\n\treturn {\n\t\ttype: \"file\",\n\t\tcolumnType: \"TEXT\",\n\t\tschema,\n\t\toptions,\n\t\tui,\n\t\tvalidation,\n\t};\n}\n","import { z } from \"astro/zod\";\n\nimport type { FieldDefinition } from \"./types.js\";\n\n/**\n * Reference field\n * References another content item by ID\n */\nexport function reference(\n\tcollection: string,\n\toptions?: {\n\t\trequired?: boolean;\n\t},\n): FieldDefinition<string | undefined> {\n\tconst schema = z.string();\n\n\treturn {\n\t\ttype: \"reference\",\n\t\tcolumnType: \"TEXT\",\n\t\tschema: options?.required === false ? schema.optional() : schema,\n\t\toptions: {\n\t\t\t...options,\n\t\t\tcollection,\n\t\t},\n\t\tui: {\n\t\t\twidget: \"reference\",\n\t\t},\n\t};\n}\n","import { z } from \"astro/zod\";\n\nimport type { FieldDefinition, PortableTextBlock } from \"./types.js\";\n\n/**\n * Portable Text block schema\n */\nconst portableTextBlockSchema: z.ZodType<PortableTextBlock> = z\n\t.object({\n\t\t_type: z.string(),\n\t\t_key: z.string(),\n\t})\n\t.passthrough();\n\n/**\n * Portable Text field\n * Stores structured content in Portable Text format\n */\nexport function portableText(options?: {\n\trequired?: boolean;\n}): FieldDefinition<PortableTextBlock[] | undefined> {\n\tconst schema = z.array(portableTextBlockSchema);\n\n\treturn {\n\t\ttype: \"portableText\",\n\t\tcolumnType: \"JSON\",\n\t\tschema: options?.required === false ? schema.optional() : schema,\n\t\toptions,\n\t\tui: {\n\t\t\twidget: \"portableText\",\n\t\t},\n\t};\n}\n","/**\n * ProseMirror to Portable Text Converter\n *\n * Converts TipTap's ProseMirror JSON format to Portable Text for storage.\n */\n\nimport type {\n\tProseMirrorDocument,\n\tProseMirrorNode,\n\tProseMirrorMark,\n\tPortableTextBlock,\n\tPortableTextTextBlock,\n\tPortableTextSpan,\n\tPortableTextMarkDef,\n\tPortableTextImageBlock,\n\tPortableTextCodeBlock,\n} from \"./types.js\";\n\n/**\n * Generate a unique key for Portable Text blocks\n */\nfunction generateKey(): string {\n\treturn Math.random().toString(36).substring(2, 11);\n}\n\n/**\n * Convert ProseMirror document to Portable Text\n */\nexport function prosemirrorToPortableText(doc: ProseMirrorDocument): PortableTextBlock[] {\n\tif (!doc || doc.type !== \"doc\" || !doc.content) {\n\t\treturn [];\n\t}\n\n\tconst blocks: PortableTextBlock[] = [];\n\n\tfor (const node of doc.content) {\n\t\tconst converted = convertNode(node);\n\t\tif (converted) {\n\t\t\tif (Array.isArray(converted)) {\n\t\t\t\tblocks.push(...converted);\n\t\t\t} else {\n\t\t\t\tblocks.push(converted);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Convert a single ProseMirror node to Portable Text block(s)\n */\nfunction convertNode(node: ProseMirrorNode): PortableTextBlock | PortableTextBlock[] | null {\n\tswitch (node.type) {\n\t\tcase \"paragraph\":\n\t\t\treturn convertParagraph(node);\n\n\t\tcase \"heading\":\n\t\t\treturn convertHeading(node);\n\n\t\tcase \"bulletList\":\n\t\t\treturn convertList(node, \"bullet\");\n\n\t\tcase \"orderedList\":\n\t\t\treturn convertList(node, \"number\");\n\n\t\tcase \"blockquote\":\n\t\t\treturn convertBlockquote(node);\n\n\t\tcase \"codeBlock\":\n\t\t\treturn convertCodeBlock(node);\n\n\t\tcase \"image\":\n\t\t\treturn convertImage(node);\n\n\t\tcase \"horizontalRule\":\n\t\t\treturn {\n\t\t\t\t_type: \"break\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tstyle: \"lineBreak\",\n\t\t\t};\n\n\t\tdefault:\n\t\t\t// Preserve unknown blocks\n\t\t\treturn {\n\t\t\t\t_type: node.type,\n\t\t\t\t_key: generateKey(),\n\t\t\t\t...node.attrs,\n\t\t\t\t_pmContent: node.content,\n\t\t\t};\n\t}\n}\n\n/**\n * Convert paragraph to Portable Text block\n */\nfunction convertParagraph(node: ProseMirrorNode): PortableTextTextBlock | null {\n\tconst { children, markDefs } = convertInlineContent(node.content || []);\n\n\t// Skip empty paragraphs\n\tif (children.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\t_type: \"block\",\n\t\t_key: generateKey(),\n\t\tstyle: \"normal\",\n\t\tchildren,\n\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t};\n}\n\n/** Map heading level number to Portable Text style */\nfunction headingLevelToStyle(level: number): PortableTextTextBlock[\"style\"] {\n\tswitch (level) {\n\t\tcase 1:\n\t\t\treturn \"h1\";\n\t\tcase 2:\n\t\t\treturn \"h2\";\n\t\tcase 3:\n\t\t\treturn \"h3\";\n\t\tcase 4:\n\t\t\treturn \"h4\";\n\t\tcase 5:\n\t\t\treturn \"h5\";\n\t\tcase 6:\n\t\t\treturn \"h6\";\n\t\tdefault:\n\t\t\treturn \"h1\";\n\t}\n}\n\n/**\n * Convert heading to Portable Text block\n */\nfunction convertHeading(node: ProseMirrorNode): PortableTextTextBlock | null {\n\tconst { children, markDefs } = convertInlineContent(node.content || []);\n\tconst rawLevel = typeof node.attrs?.level === \"number\" ? node.attrs.level : 1;\n\tconst style = headingLevelToStyle(rawLevel);\n\n\tif (children.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\t_type: \"block\",\n\t\t_key: generateKey(),\n\t\tstyle,\n\t\tchildren,\n\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t};\n}\n\n/**\n * Convert list to Portable Text blocks\n */\nfunction convertList(\n\tnode: ProseMirrorNode,\n\tlistItem: \"bullet\" | \"number\",\n): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\tfor (const item of node.content || []) {\n\t\tif (item.type === \"listItem\") {\n\t\t\tconst itemBlocks = convertListItem(item, listItem, 1);\n\t\t\tblocks.push(...itemBlocks);\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Convert list item to Portable Text blocks\n */\nfunction convertListItem(\n\titem: ProseMirrorNode,\n\tlistItem: \"bullet\" | \"number\",\n\tlevel: number,\n): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\tfor (const child of item.content || []) {\n\t\tif (child.type === \"paragraph\") {\n\t\t\tconst { children, markDefs } = convertInlineContent(child.content || []);\n\n\t\t\tif (children.length > 0) {\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\tlistItem,\n\t\t\t\t\tlevel,\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (child.type === \"bulletList\") {\n\t\t\tblocks.push(...convertListItemNested(child, \"bullet\", level + 1));\n\t\t} else if (child.type === \"orderedList\") {\n\t\t\tblocks.push(...convertListItemNested(child, \"number\", level + 1));\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Convert nested list\n */\nfunction convertListItemNested(\n\tnode: ProseMirrorNode,\n\tlistItem: \"bullet\" | \"number\",\n\tlevel: number,\n): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\tfor (const item of node.content || []) {\n\t\tif (item.type === \"listItem\") {\n\t\t\tblocks.push(...convertListItem(item, listItem, level));\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Convert blockquote to Portable Text blocks\n */\nfunction convertBlockquote(\n\tnode: ProseMirrorNode,\n): PortableTextTextBlock | PortableTextTextBlock[] | null {\n\t// Blockquotes in PT are just blocks with style: \"blockquote\"\n\tconst blocks: PortableTextTextBlock[] = [];\n\n\tfor (const child of node.content || []) {\n\t\tif (child.type === \"paragraph\") {\n\t\t\tconst { children, markDefs } = convertInlineContent(child.content || []);\n\n\t\t\tif (children.length > 0) {\n\t\t\t\tblocks.push({\n\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\tstyle: \"blockquote\",\n\t\t\t\t\tchildren,\n\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn blocks.length === 1 ? blocks[0] : blocks.length > 0 ? blocks : null;\n}\n\n/**\n * Convert code block to Portable Text\n */\nfunction convertCodeBlock(node: ProseMirrorNode): PortableTextCodeBlock {\n\tconst code = node.content?.map((n) => n.text || \"\").join(\"\") || \"\";\n\tconst language = typeof node.attrs?.language === \"string\" ? node.attrs.language : undefined;\n\n\treturn {\n\t\t_type: \"code\",\n\t\t_key: generateKey(),\n\t\tcode,\n\t\tlanguage: language || undefined,\n\t};\n}\n\n/**\n * Convert image to Portable Text\n */\nfunction convertImage(node: ProseMirrorNode): PortableTextImageBlock {\n\tconst attrs = node.attrs;\n\tconst provider = typeof attrs?.provider === \"string\" ? attrs.provider : undefined;\n\tconst mediaId = typeof attrs?.mediaId === \"string\" ? attrs.mediaId : undefined;\n\tconst src = typeof attrs?.src === \"string\" ? attrs.src : \"\";\n\tconst alt = typeof attrs?.alt === \"string\" ? attrs.alt : undefined;\n\tconst title = typeof attrs?.title === \"string\" ? attrs.title : undefined;\n\tconst width = typeof attrs?.width === \"number\" ? attrs.width : undefined;\n\tconst height = typeof attrs?.height === \"number\" ? attrs.height : undefined;\n\tconst displayWidth = typeof attrs?.displayWidth === \"number\" ? attrs.displayWidth : undefined;\n\tconst displayHeight = typeof attrs?.displayHeight === \"number\" ? attrs.displayHeight : undefined;\n\n\treturn {\n\t\t_type: \"image\",\n\t\t_key: generateKey(),\n\t\tasset: {\n\t\t\t// Use mediaId as _ref if available (for proper provider lookups)\n\t\t\t_ref: mediaId || src || \"\",\n\t\t\t// Store URL for admin preview and fallback rendering\n\t\t\turl: src || \"\",\n\t\t\t// Store provider for external media\n\t\t\tprovider: provider && provider !== \"local\" ? provider : undefined,\n\t\t},\n\t\talt: alt || undefined,\n\t\tcaption: title || undefined,\n\t\twidth: width || undefined,\n\t\theight: height || undefined,\n\t\tdisplayWidth: displayWidth || undefined,\n\t\tdisplayHeight: displayHeight || undefined,\n\t};\n}\n\n/**\n * Convert inline content (text nodes with marks) to Portable Text spans\n */\nfunction convertInlineContent(nodes: ProseMirrorNode[]): {\n\tchildren: PortableTextSpan[];\n\tmarkDefs: PortableTextMarkDef[];\n} {\n\tconst children: PortableTextSpan[] = [];\n\tconst markDefs: PortableTextMarkDef[] = [];\n\tconst markDefMap = new Map<string, string>(); // href -> key\n\n\tfor (const node of nodes) {\n\t\tif (node.type === \"text\" && node.text) {\n\t\t\tconst marks: string[] = [];\n\n\t\t\tfor (const mark of node.marks || []) {\n\t\t\t\tconst markType = convertMark(mark, markDefs, markDefMap);\n\t\t\t\tif (markType) {\n\t\t\t\t\tmarks.push(markType);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tchildren.push({\n\t\t\t\t_type: \"span\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\ttext: node.text,\n\t\t\t\tmarks: marks.length > 0 ? marks : undefined,\n\t\t\t});\n\t\t} else if (node.type === \"hardBreak\") {\n\t\t\t// Hard breaks become newlines in the text\n\t\t\tif (children.length > 0) {\n\t\t\t\tconst lastChild = children.at(-1)!;\n\t\t\t\tlastChild.text += \"\\n\";\n\t\t\t} else {\n\t\t\t\tchildren.push({\n\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\ttext: \"\\n\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Ensure at least one span exists\n\tif (children.length === 0) {\n\t\tchildren.push({\n\t\t\t_type: \"span\",\n\t\t\t_key: generateKey(),\n\t\t\ttext: \"\",\n\t\t});\n\t}\n\n\treturn { children, markDefs };\n}\n\n/**\n * Convert a ProseMirror mark to Portable Text mark\n */\nfunction convertMark(\n\tmark: ProseMirrorMark,\n\tmarkDefs: PortableTextMarkDef[],\n\tmarkDefMap: Map<string, string>,\n): string | null {\n\tswitch (mark.type) {\n\t\tcase \"bold\":\n\t\tcase \"strong\":\n\t\t\treturn \"strong\";\n\n\t\tcase \"italic\":\n\t\tcase \"em\":\n\t\t\treturn \"em\";\n\n\t\tcase \"underline\":\n\t\t\treturn \"underline\";\n\n\t\tcase \"strike\":\n\t\tcase \"strikethrough\":\n\t\t\treturn \"strike-through\";\n\n\t\tcase \"code\":\n\t\t\treturn \"code\";\n\n\t\tcase \"link\": {\n\t\t\tconst href = (typeof mark.attrs?.href === \"string\" ? mark.attrs.href : \"\") || \"\";\n\n\t\t\t// Check if we already have a mark def for this link\n\t\t\tif (markDefMap.has(href)) {\n\t\t\t\treturn markDefMap.get(href)!;\n\t\t\t}\n\n\t\t\t// Create new mark def\n\t\t\tconst key = generateKey();\n\t\t\tmarkDefs.push({\n\t\t\t\t_type: \"link\",\n\t\t\t\t_key: key,\n\t\t\t\thref,\n\t\t\t\tblank: mark.attrs?.target === \"_blank\",\n\t\t\t});\n\t\t\tmarkDefMap.set(href, key);\n\n\t\t\treturn key;\n\t\t}\n\n\t\tdefault:\n\t\t\t// Unknown mark - preserve as-is\n\t\t\treturn mark.type;\n\t}\n}\n","/**\n * Portable Text to ProseMirror Converter\n *\n * Converts Portable Text to TipTap's ProseMirror JSON format for editing.\n */\n\nimport type {\n\tProseMirrorDocument,\n\tProseMirrorNode,\n\tProseMirrorMark,\n\tPortableTextBlock,\n\tPortableTextTextBlock,\n\tPortableTextSpan,\n\tPortableTextMarkDef,\n\tPortableTextImageBlock,\n\tPortableTextCodeBlock,\n} from \"./types.js\";\n\n/**\n * Convert Portable Text to ProseMirror document\n */\nexport function portableTextToProsemirror(blocks: PortableTextBlock[]): ProseMirrorDocument {\n\tif (!blocks || blocks.length === 0) {\n\t\treturn {\n\t\t\ttype: \"doc\",\n\t\t\tcontent: [{ type: \"paragraph\" }],\n\t\t};\n\t}\n\n\tconst content: ProseMirrorNode[] = [];\n\tlet i = 0;\n\n\twhile (i < blocks.length) {\n\t\tconst block = blocks[i];\n\n\t\t// Check for list items\n\t\tif (isTextBlock(block) && block.listItem) {\n\t\t\t// Collect consecutive list items\n\t\t\tconst listBlocks: PortableTextTextBlock[] = [];\n\t\t\tconst listType = block.listItem;\n\n\t\t\twhile (i < blocks.length) {\n\t\t\t\tconst current = blocks[i];\n\t\t\t\tif (isTextBlock(current) && current.listItem === listType) {\n\t\t\t\t\tlistBlocks.push(current);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcontent.push(convertList(listBlocks, listType));\n\t\t} else {\n\t\t\tconst converted = convertBlock(block);\n\t\t\tif (converted) {\n\t\t\t\tcontent.push(converted);\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t}\n\n\treturn {\n\t\ttype: \"doc\",\n\t\tcontent: content.length > 0 ? content : [{ type: \"paragraph\" }],\n\t};\n}\n\n/**\n * Type guard for text blocks\n */\nfunction isTextBlock(block: PortableTextBlock): block is PortableTextTextBlock {\n\treturn block._type === \"block\";\n}\n\n/**\n * Type guard for image blocks.\n * Checks both `_type` and that `asset` is a valid object — image blocks\n * without an `asset` wrapper (e.g. `{ _type: \"image\", url: \"...\" }`) are\n * malformed and should not be cast to `PortableTextImageBlock`.\n */\nfunction isImageBlock(block: PortableTextBlock): block is PortableTextImageBlock {\n\treturn (\n\t\tblock._type === \"image\" &&\n\t\t\"asset\" in block &&\n\t\ttypeof block.asset === \"object\" &&\n\t\tblock.asset !== null\n\t);\n}\n\n/**\n * Type guard for code blocks\n */\nfunction isCodeBlock(block: PortableTextBlock): block is PortableTextCodeBlock {\n\treturn block._type === \"code\";\n}\n\n/**\n * Convert a single Portable Text block to ProseMirror node\n */\nfunction convertBlock(block: PortableTextBlock): ProseMirrorNode | null {\n\tif (isTextBlock(block)) {\n\t\treturn convertTextBlock(block);\n\t}\n\tif (isImageBlock(block)) {\n\t\treturn convertImage(block);\n\t}\n\tif (block._type === \"image\") {\n\t\t// Malformed image block (no asset wrapper) — extract url from top level\n\t\treturn convertMalformedImage(block);\n\t}\n\tif (isCodeBlock(block)) {\n\t\treturn convertCodeBlock(block);\n\t}\n\tif (block._type === \"break\") {\n\t\treturn { type: \"horizontalRule\" };\n\t}\n\t// Unknown block - wrap in a div or preserve as placeholder\n\treturn {\n\t\ttype: \"paragraph\",\n\t\tcontent: [\n\t\t\t{\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: `[Unknown block type: ${block._type}]`,\n\t\t\t\tmarks: [{ type: \"code\" }],\n\t\t\t},\n\t\t],\n\t};\n}\n\n/**\n * Convert text block to ProseMirror paragraph or heading\n */\nfunction convertTextBlock(block: PortableTextTextBlock): ProseMirrorNode | null {\n\tconst { style = \"normal\", children, markDefs = [] } = block;\n\n\t// Convert children to ProseMirror nodes\n\tconst content = convertSpans(children, markDefs);\n\n\t// Determine node type based on style\n\tswitch (style) {\n\t\tcase \"h1\":\n\t\tcase \"h2\":\n\t\tcase \"h3\":\n\t\tcase \"h4\":\n\t\tcase \"h5\":\n\t\tcase \"h6\": {\n\t\t\tconst level = parseInt(style.substring(1), 10);\n\t\t\treturn {\n\t\t\t\ttype: \"heading\",\n\t\t\t\tattrs: { level },\n\t\t\t\tcontent: content.length > 0 ? content : undefined,\n\t\t\t};\n\t\t}\n\n\t\tcase \"blockquote\":\n\t\t\treturn {\n\t\t\t\ttype: \"blockquote\",\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"paragraph\",\n\t\t\t\t\t\tcontent: content.length > 0 ? content : undefined,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\n\t\tcase \"normal\":\n\t\tdefault:\n\t\t\treturn {\n\t\t\t\ttype: \"paragraph\",\n\t\t\t\tcontent: content.length > 0 ? content : undefined,\n\t\t\t};\n\t}\n}\n\n/**\n * Convert list items to ProseMirror list\n */\nfunction convertList(\n\titems: PortableTextTextBlock[],\n\tlistType: \"bullet\" | \"number\",\n): ProseMirrorNode {\n\t// Group items by level\n\tconst rootItems: ProseMirrorNode[] = [];\n\tlet i = 0;\n\n\twhile (i < items.length) {\n\t\tconst item = items[i];\n\t\tconst level = item.level || 1;\n\n\t\tif (level === 1) {\n\t\t\t// Collect nested items for this root item\n\t\t\tconst nestedItems: PortableTextTextBlock[] = [];\n\t\t\ti++;\n\n\t\t\twhile (i < items.length && (items[i].level || 1) > 1) {\n\t\t\t\tnestedItems.push(items[i]);\n\t\t\t\ti++;\n\t\t\t}\n\n\t\t\trootItems.push(convertListItem(item, nestedItems, listType));\n\t\t} else {\n\t\t\t// Orphan nested item - treat as root\n\t\t\trootItems.push(convertListItem(item, [], listType));\n\t\t\ti++;\n\t\t}\n\t}\n\n\treturn {\n\t\ttype: listType === \"bullet\" ? \"bulletList\" : \"orderedList\",\n\t\tcontent: rootItems,\n\t};\n}\n\n/**\n * Convert a single list item to ProseMirror\n */\nfunction convertListItem(\n\titem: PortableTextTextBlock,\n\tnestedItems: PortableTextTextBlock[],\n\tparentListType: \"bullet\" | \"number\",\n): ProseMirrorNode {\n\tconst content: ProseMirrorNode[] = [];\n\n\t// Add paragraph content\n\tconst spans = convertSpans(item.children, item.markDefs || []);\n\tcontent.push({\n\t\ttype: \"paragraph\",\n\t\tcontent: spans.length > 0 ? spans : undefined,\n\t});\n\n\t// Handle nested items\n\tif (nestedItems.length > 0) {\n\t\t// Group nested items by their list type\n\t\tlet j = 0;\n\n\t\twhile (j < nestedItems.length) {\n\t\t\tconst nestedListType = nestedItems[j].listItem || parentListType;\n\t\t\tconst nestedGroup: PortableTextTextBlock[] = [];\n\n\t\t\twhile (\n\t\t\t\tj < nestedItems.length &&\n\t\t\t\t(nestedItems[j].listItem || parentListType) === nestedListType\n\t\t\t) {\n\t\t\t\tnestedGroup.push(nestedItems[j]);\n\t\t\t\tj++;\n\t\t\t}\n\n\t\t\tif (nestedGroup.length > 0) {\n\t\t\t\t// Decrease level for nested conversion\n\t\t\t\tconst adjustedGroup = nestedGroup.map((ni) => ({\n\t\t\t\t\t...ni,\n\t\t\t\t\tlevel: (ni.level || 2) - 1,\n\t\t\t\t}));\n\t\t\t\tcontent.push(convertList(adjustedGroup, nestedListType));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\ttype: \"listItem\",\n\t\tcontent,\n\t};\n}\n\n/**\n * Convert Portable Text spans to ProseMirror text nodes\n */\nfunction convertSpans(\n\tspans: PortableTextSpan[],\n\tmarkDefs: PortableTextMarkDef[],\n): ProseMirrorNode[] {\n\tconst nodes: ProseMirrorNode[] = [];\n\tconst markDefsMap = new Map(markDefs.map((md) => [md._key, md]));\n\n\tfor (const span of spans) {\n\t\tif (span._type !== \"span\") continue;\n\n\t\t// Handle newlines in text\n\t\tconst parts = span.text.split(\"\\n\");\n\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tconst text = parts[i];\n\n\t\t\t// Add text node\n\t\t\tif (text.length > 0) {\n\t\t\t\tconst marks = convertMarks(span.marks || [], markDefsMap);\n\t\t\t\tconst node: ProseMirrorNode = {\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext,\n\t\t\t\t};\n\t\t\t\tif (marks.length > 0) {\n\t\t\t\t\tnode.marks = marks;\n\t\t\t\t}\n\t\t\t\tnodes.push(node);\n\t\t\t}\n\n\t\t\t// Add hard break between parts (not after last)\n\t\t\tif (i < parts.length - 1) {\n\t\t\t\tnodes.push({ type: \"hardBreak\" });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nodes;\n}\n\n/**\n * Convert Portable Text marks to ProseMirror marks\n */\nfunction convertMarks(\n\tmarks: string[],\n\tmarkDefs: Map<string, PortableTextMarkDef>,\n): ProseMirrorMark[] {\n\tconst pmMarks: ProseMirrorMark[] = [];\n\n\tfor (const mark of marks) {\n\t\tswitch (mark) {\n\t\t\tcase \"strong\":\n\t\t\t\tpmMarks.push({ type: \"bold\" });\n\t\t\t\tbreak;\n\n\t\t\tcase \"em\":\n\t\t\t\tpmMarks.push({ type: \"italic\" });\n\t\t\t\tbreak;\n\n\t\t\tcase \"underline\":\n\t\t\t\tpmMarks.push({ type: \"underline\" });\n\t\t\t\tbreak;\n\n\t\t\tcase \"strike-through\":\n\t\t\t\tpmMarks.push({ type: \"strike\" });\n\t\t\t\tbreak;\n\n\t\t\tcase \"code\":\n\t\t\t\tpmMarks.push({ type: \"code\" });\n\t\t\t\tbreak;\n\n\t\t\tdefault: {\n\t\t\t\t// Check if it's a mark definition reference\n\t\t\t\tconst markDef = markDefs.get(mark);\n\t\t\t\tif (markDef) {\n\t\t\t\t\tif (markDef._type === \"link\") {\n\t\t\t\t\t\tpmMarks.push({\n\t\t\t\t\t\t\ttype: \"link\",\n\t\t\t\t\t\t\tattrs: {\n\t\t\t\t\t\t\t\thref: markDef.href,\n\t\t\t\t\t\t\t\ttarget: markDef.blank ? \"_blank\" : null,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Unknown mark def type - preserve attrs\n\t\t\t\t\t\tpmMarks.push({\n\t\t\t\t\t\t\ttype: markDef._type,\n\t\t\t\t\t\t\tattrs: markDef as Record<string, unknown>,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pmMarks;\n}\n\n/**\n * Convert image block to ProseMirror\n */\nfunction convertImage(block: PortableTextImageBlock): ProseMirrorNode {\n\treturn {\n\t\ttype: \"image\",\n\t\tattrs: {\n\t\t\tsrc: block.asset.url || block.asset._ref,\n\t\t\talt: block.alt || \"\",\n\t\t\ttitle: block.caption || \"\",\n\t\t\tmediaId: block.asset._ref,\n\t\t\tprovider: block.asset.provider,\n\t\t\twidth: block.width,\n\t\t\theight: block.height,\n\t\t\tdisplayWidth: block.displayWidth,\n\t\t\tdisplayHeight: block.displayHeight,\n\t\t},\n\t};\n}\n\n/**\n * Convert a malformed image block (missing `asset` wrapper) to ProseMirror.\n * Handles blocks like `{ _type: \"image\", url: \"...\", alt: \"...\" }` that may\n * originate from migrations or third-party imports.\n */\nfunction convertMalformedImage(block: PortableTextBlock): ProseMirrorNode {\n\t// PortableTextUnknownBlock allows indexed access via [key: string]: unknown\n\tconst url = \"url\" in block && typeof block.url === \"string\" ? block.url : \"\";\n\tconst alt = \"alt\" in block && typeof block.alt === \"string\" ? block.alt : \"\";\n\tconst caption = \"caption\" in block && typeof block.caption === \"string\" ? block.caption : \"\";\n\tconst width = \"width\" in block && typeof block.width === \"number\" ? block.width : undefined;\n\tconst height = \"height\" in block && typeof block.height === \"number\" ? block.height : undefined;\n\tconst displayWidth =\n\t\t\"displayWidth\" in block && typeof block.displayWidth === \"number\"\n\t\t\t? block.displayWidth\n\t\t\t: undefined;\n\tconst displayHeight =\n\t\t\"displayHeight\" in block && typeof block.displayHeight === \"number\"\n\t\t\t? block.displayHeight\n\t\t\t: undefined;\n\treturn {\n\t\ttype: \"image\",\n\t\tattrs: {\n\t\t\tsrc: url,\n\t\t\talt,\n\t\t\ttitle: caption,\n\t\t\tmediaId: undefined,\n\t\t\tprovider: undefined,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tdisplayWidth,\n\t\t\tdisplayHeight,\n\t\t},\n\t};\n}\n\n/**\n * Convert code block to ProseMirror\n */\nfunction convertCodeBlock(block: PortableTextCodeBlock): ProseMirrorNode {\n\treturn {\n\t\ttype: \"codeBlock\",\n\t\tattrs: {\n\t\t\tlanguage: block.language || null,\n\t\t},\n\t\tcontent: block.code ? [{ type: \"text\", text: block.code }] : undefined,\n\t};\n}\n","/**\n * Defer work past the HTTP response.\n *\n * Use for bookkeeping that doesn't need to complete before the client\n * gets bytes — writes that record state, maintenance queries, cache\n * refreshes. `after()` hands the promise to the host's lifetime\n * extender when one is available (Cloudflare's `waitUntil` under\n * workerd), or fires-and-forgets on Node (the process lives for the\n * next request anyway).\n *\n * Host binding is resolved lazily via a dynamic import of the\n * `virtual:emdash/wait-until` virtual module. Lazy — rather than a\n * static top-level import — so tools that walk the dist in a plain\n * Node loader (`astro check`, Vitest, etc.) don't trip over the\n * `virtual:` scheme: they'd only fail if they actually called\n * `after()`, which they don't during type-checking.\n */\n\nexport type WaitUntilFn = (promise: Promise<unknown>) => void;\n\n// Resolves to the host's waitUntil if the adapter provided one, or\n// null otherwise. Kicked off once at module load; subsequent `after()`\n// calls see the cached result without re-importing.\nconst waitUntilReady: Promise<WaitUntilFn | null> = (async () => {\n\ttry {\n\t\t// @ts-ignore - virtual module, generated by the Astro integration\n\t\tconst mod = (await import(\"virtual:emdash/wait-until\")) as {\n\t\t\twaitUntil?: WaitUntilFn;\n\t\t};\n\t\treturn mod.waitUntil ?? null;\n\t} catch {\n\t\t// No virtual module available (Node-side tooling, tests without the\n\t\t// integration in scope). Fire-and-forget is the safe fallback.\n\t\treturn null;\n\t}\n})();\n// Surface rejections without making the module-load fail.\nwaitUntilReady.catch(() => {});\n\n/**\n * Schedule `fn` to run without blocking the response.\n *\n * Errors are caught and logged — a deferred task should never surface\n * as an unhandled rejection because the response is long gone. Callers\n * that care about errors should handle them inside `fn`.\n */\nexport function after(fn: () => void | Promise<void>): void {\n\tconst promise = Promise.resolve()\n\t\t.then(fn)\n\t\t.catch((error) => {\n\t\t\tconsole.error(\"[emdash] deferred task failed:\", error);\n\t\t});\n\n\t// Defer the lifetime-extender handoff to the microtask that resolves\n\t// waitUntilReady. On workerd this is effectively instant (the virtual\n\t// module is already loaded in the bundle); on Node the promise\n\t// resolves to null, so this is just one extra microtask and no-op.\n\tvoid waitUntilReady.then((waitUntil) => {\n\t\tif (waitUntil) waitUntil(promise);\n\t\treturn null;\n\t});\n}\n","/**\n * definePlugin() Helper\n *\n * Native plugin authoring entry. Returns a fully-resolved\n * `ResolvedPlugin` ready for the host integration to mount.\n *\n * Sandboxed plugins do NOT use this function. They default-export\n * a bare `{ hooks?, routes? }` object with a `satisfies SandboxedPlugin`\n * annotation from `emdash/plugin`. See the `emdash` changeset for the\n * authoring shape.\n */\n\nimport { normalizeCapabilities } from \"./types.js\";\nimport type {\n\tPluginDefinition,\n\tResolvedPlugin,\n\tPluginHooks,\n\tResolvedPluginHooks,\n\tResolvedHook,\n\tHookConfig,\n\tPluginCapability,\n\tPluginStorageConfig,\n} from \"./types.js\";\n\n// Plugin ID validation patterns\nconst SIMPLE_ID = /^[a-z0-9-]+$/;\nconst SCOPED_ID = /^@[a-z0-9-]+\\/[a-z0-9-]+$/;\nconst SEMVER_PATTERN = /^\\d+\\.\\d+\\.\\d+/;\n\n/**\n * Define a native EmDash plugin.\n *\n * Native plugins ship as regular npm modules, get installed via\n * `pnpm add` + an `astro.config.mjs` edit, and run in the host\n * process. They have full access to the runtime — capabilities are\n * still enforced by `PluginContextFactory`, but there is no isolation\n * boundary.\n *\n * @example\n * ```typescript\n * import { definePlugin } from \"emdash\";\n *\n * export default definePlugin({\n * id: \"my-plugin\",\n * version: \"1.0.0\",\n * capabilities: [\"content:read\"],\n * hooks: {\n * \"content:beforeSave\": async (event, ctx) => {\n * ctx.log.info(\"Saving content\", { collection: event.collection });\n * return event.content;\n * }\n * },\n * routes: {\n * \"sync\": {\n * handler: async (ctx) => {\n * return { status: \"ok\" };\n * }\n * }\n * }\n * });\n * ```\n *\n * Sandboxed-format plugins do not use `definePlugin`. They\n * default-export a bare `{ hooks?, routes? }` object with a\n * `satisfies SandboxedPlugin` annotation from `emdash/plugin`. Calling\n * `definePlugin` with an object that has no `id` throws at runtime\n * (the type system already rejects it at compile time — this check is\n * for callers that bypass typechecking).\n */\nexport function definePlugin<TStorage extends PluginStorageConfig>(\n\tdefinition: PluginDefinition<TStorage>,\n): ResolvedPlugin<TStorage> {\n\t// Semantic check, not a structural one: `id` is what makes this a\n\t// native definition. Sandboxed plugins (the only other shape that\n\t// might land here at runtime) intentionally never have an `id` —\n\t// identity comes from the manifest's `slug` + `publisher`, computed\n\t// at install time. So \"no id\" is the unambiguous signal that the\n\t// caller meant the sandboxed authoring flow.\n\tif (typeof definition.id !== \"string\" || definition.id.length === 0) {\n\t\tthrow new Error(\n\t\t\t`definePlugin() requires \\`id\\` (got ${typeof definition.id}). ` +\n\t\t\t\t\"For native plugins, make sure your definition has both `id` and \" +\n\t\t\t\t\"`version`. For sandboxed plugins, drop `definePlugin()` entirely \" +\n\t\t\t\t\"and `export default { hooks, routes } satisfies SandboxedPlugin` \" +\n\t\t\t\t'from \"emdash/plugin\" — identity comes from `emdash-plugin.jsonc`.',\n\t\t);\n\t}\n\treturn defineNativePlugin(definition);\n}\n\n/**\n * Internal: define a native-format plugin with full validation and normalization.\n */\nfunction defineNativePlugin<TStorage extends PluginStorageConfig>(\n\tdefinition: PluginDefinition<TStorage>,\n): ResolvedPlugin<TStorage> {\n\tconst {\n\t\tid,\n\t\tversion,\n\t\tcapabilities = [],\n\t\tallowedHosts = [],\n\t\thooks = {},\n\t\troutes = {},\n\t\tadmin = {},\n\t} = definition;\n\n\t// Default to empty object if no storage declared.\n\t// The empty object satisfies PluginStorageConfig (Record<string, ...>).\n\t// The cast is structurally safe because an empty record has no keys to conflict.\n\tconst storage = (definition.storage ?? {}) as TStorage;\n\n\t// Validate id format: either simple (my-plugin) or scoped (@scope/my-plugin)\n\t// Simple: lowercase alphanumeric with dashes\n\t// Scoped: @scope/name where both parts are lowercase alphanumeric with dashes\n\tif (!SIMPLE_ID.test(id) && !SCOPED_ID.test(id)) {\n\t\tthrow new Error(\n\t\t\t`Invalid plugin id \"${id}\". Must be lowercase alphanumeric with dashes (e.g., \"my-plugin\" or \"@scope/my-plugin\").`,\n\t\t);\n\t}\n\n\t// Validate version format (basic semver)\n\tif (!SEMVER_PATTERN.test(version)) {\n\t\tthrow new Error(`Invalid plugin version \"${version}\". Must be semver format (e.g., \"1.0.0\").`);\n\t}\n\n\t// Validate capabilities. Both current names and deprecated aliases are\n\t// accepted; aliases are silently rewritten to current names below so the\n\t// runtime only ever sees the canonical form. Authors are warned at\n\t// bundle/validate and hard-failed at publish.\n\tconst validCapabilities = new Set<string>([\n\t\t// Current names\n\t\t\"network:request\",\n\t\t\"network:request:unrestricted\",\n\t\t\"content:read\",\n\t\t\"content:write\",\n\t\t\"media:read\",\n\t\t\"media:write\",\n\t\t\"users:read\",\n\t\t\"email:send\",\n\t\t\"hooks.email-transport:register\",\n\t\t\"hooks.email-events:register\",\n\t\t\"hooks.page-fragments:register\",\n\t\t// Deprecated aliases\n\t\t\"network:fetch\",\n\t\t\"network:fetch:any\",\n\t\t\"read:content\",\n\t\t\"write:content\",\n\t\t\"read:media\",\n\t\t\"write:media\",\n\t\t\"read:users\",\n\t\t\"email:provide\",\n\t\t\"email:intercept\",\n\t\t\"page:inject\",\n\t]);\n\tfor (const cap of capabilities) {\n\t\tif (!validCapabilities.has(cap)) {\n\t\t\tthrow new Error(`Invalid capability \"${cap}\" in plugin \"${id}\".`);\n\t\t}\n\t}\n\n\t// Silent normalization: rewrite deprecated names to current names. Done\n\t// before the implication pass so implications work on canonical names.\n\t// `as PluginCapability[]` is safe because `normalizeCapabilities` only\n\t// returns strings from the validated input plus current names from the\n\t// rename map, all of which are in the union.\n\tconst canonical = normalizeCapabilities(capabilities) as PluginCapability[];\n\n\t// Capability implications: broader capabilities imply narrower ones.\n\t// Operates on canonical names only.\n\tconst normalizedCapabilities: PluginCapability[] = [...canonical];\n\tif (canonical.includes(\"content:write\") && !canonical.includes(\"content:read\")) {\n\t\tnormalizedCapabilities.push(\"content:read\");\n\t}\n\tif (canonical.includes(\"media:write\") && !canonical.includes(\"media:read\")) {\n\t\tnormalizedCapabilities.push(\"media:read\");\n\t}\n\tif (\n\t\tcanonical.includes(\"network:request:unrestricted\") &&\n\t\t!canonical.includes(\"network:request\")\n\t) {\n\t\tnormalizedCapabilities.push(\"network:request\");\n\t}\n\n\t// Normalize hooks\n\tconst resolvedHooks = resolveHooks(hooks, id);\n\n\treturn {\n\t\tid,\n\t\tversion,\n\t\tcapabilities: normalizedCapabilities,\n\t\tallowedHosts,\n\t\tstorage,\n\t\thooks: resolvedHooks,\n\t\troutes,\n\t\tadmin,\n\t};\n}\n\n/**\n * Resolve hooks to normalized format with defaults.\n *\n * PluginHooks and ResolvedPluginHooks share the same keys — each input value is\n * `HookConfig<H> | H` and the output is `ResolvedHook<H>`. TS can't narrow\n * the handler type through a dynamic key, so we assert at the loop boundary.\n */\nfunction resolveHooks(hooks: PluginHooks, pluginId: string): ResolvedPluginHooks {\n\tconst resolved: ResolvedPluginHooks = {};\n\n\tfor (const key of Object.keys(hooks) as Array<keyof PluginHooks>) {\n\t\tconst hook = hooks[key];\n\t\tif (hook) {\n\t\t\t(resolved as Record<string, unknown>)[key] = resolveHook(hook, pluginId);\n\t\t}\n\t}\n\n\treturn resolved;\n}\n\n/**\n * Check if a hook value is a config object (has a `handler` property)\n */\nfunction isHookConfig<THandler>(\n\thook: HookConfig<THandler> | THandler,\n): hook is HookConfig<THandler> {\n\treturn typeof hook === \"object\" && hook !== null && \"handler\" in hook;\n}\n\n/**\n * Resolve a single hook to normalized format\n */\nfunction resolveHook<THandler>(\n\thook: HookConfig<THandler> | THandler,\n\tpluginId: string,\n): ResolvedHook<THandler> {\n\t// If it's a config object with handler property\n\tif (isHookConfig(hook)) {\n\t\tif (hook.exclusive !== undefined && typeof hook.exclusive !== \"boolean\") {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid \"exclusive\" value in hook config for plugin \"${pluginId}\". Must be boolean.`,\n\t\t\t);\n\t\t}\n\t\treturn {\n\t\t\tpriority: hook.priority ?? 100,\n\t\t\ttimeout: hook.timeout ?? 5000,\n\t\t\tdependencies: hook.dependencies ?? [],\n\t\t\terrorPolicy: hook.errorPolicy ?? \"abort\",\n\t\t\texclusive: hook.exclusive ?? false,\n\t\t\thandler: hook.handler,\n\t\t\tpluginId,\n\t\t};\n\t}\n\n\t// It's just a handler function\n\treturn {\n\t\tpriority: 100,\n\t\ttimeout: 5000,\n\t\tdependencies: [],\n\t\terrorPolicy: \"abort\",\n\t\texclusive: false,\n\t\thandler: hook,\n\t\tpluginId,\n\t};\n}\n\nexport default definePlugin;\n","/**\n * Plugin Hooks System v2\n *\n * Uses the unified PluginContext for all hooks.\n * Manages lifecycle hooks with:\n * - Deterministic ordering via priority + dependencies\n * - Timeout enforcement\n * - Error isolation\n * - Observability\n *\n */\n\nimport { PluginContextFactory, type PluginContextFactoryOptions } from \"./context.js\";\nimport type {\n\tResolvedPlugin,\n\tResolvedHook,\n\tPluginContext,\n\tContentHookEvent,\n\tContentDeleteEvent,\n\tContentPublishStateChangeEvent,\n\tMediaUploadEvent,\n\tMediaAfterUploadEvent,\n\tLifecycleEvent,\n\tUninstallEvent,\n\tCronEvent,\n\tEmailBeforeSendEvent,\n\tEmailBeforeSendHandler,\n\tEmailDeliverHandler,\n\tEmailAfterSendHandler,\n\tContentBeforeSaveHandler,\n\tContentAfterSaveHandler,\n\tContentBeforeDeleteHandler,\n\tContentAfterDeleteHandler,\n\tContentAfterPublishHandler,\n\tContentAfterUnpublishHandler,\n\tMediaBeforeUploadHandler,\n\tMediaAfterUploadHandler,\n\tLifecycleHandler,\n\tUninstallHandler,\n\tCronHandler,\n\tEmailMessage,\n\tCommentBeforeCreateEvent,\n\tCommentBeforeCreateHandler,\n\tCommentModerateHandler,\n\tCommentAfterCreateEvent,\n\tCommentAfterCreateHandler,\n\tCommentAfterModerateEvent,\n\tCommentAfterModerateHandler,\n\tPageMetadataEvent,\n\tPageMetadataHandler,\n\tPageMetadataContribution,\n\tPageFragmentEvent,\n\tPageFragmentHandler,\n\tPageFragmentContribution,\n} from \"./types.js\";\n\n// Hook name type for v2\ntype HookNameV2 =\n\t| \"plugin:install\"\n\t| \"plugin:activate\"\n\t| \"plugin:deactivate\"\n\t| \"plugin:uninstall\"\n\t| \"content:beforeSave\"\n\t| \"content:afterSave\"\n\t| \"content:beforeDelete\"\n\t| \"content:afterDelete\"\n\t| \"content:afterPublish\"\n\t| \"content:afterUnpublish\"\n\t| \"media:beforeUpload\"\n\t| \"media:afterUpload\"\n\t| \"cron\"\n\t| \"email:beforeSend\"\n\t| \"email:deliver\"\n\t| \"email:afterSend\"\n\t| \"comment:beforeCreate\"\n\t| \"comment:moderate\"\n\t| \"comment:afterCreate\"\n\t| \"comment:afterModerate\"\n\t| \"page:metadata\"\n\t| \"page:fragments\";\n\n/**\n * Map from hook name to handler type — used for type-safe hook retrieval\n */\ninterface HookHandlerMap {\n\t\"plugin:install\": LifecycleHandler;\n\t\"plugin:activate\": LifecycleHandler;\n\t\"plugin:deactivate\": LifecycleHandler;\n\t\"plugin:uninstall\": UninstallHandler;\n\t\"content:beforeSave\": ContentBeforeSaveHandler;\n\t\"content:afterSave\": ContentAfterSaveHandler;\n\t\"content:beforeDelete\": ContentBeforeDeleteHandler;\n\t\"content:afterDelete\": ContentAfterDeleteHandler;\n\t\"content:afterPublish\": ContentAfterPublishHandler;\n\t\"content:afterUnpublish\": ContentAfterUnpublishHandler;\n\t\"media:beforeUpload\": MediaBeforeUploadHandler;\n\t\"media:afterUpload\": MediaAfterUploadHandler;\n\tcron: CronHandler;\n\t\"email:beforeSend\": EmailBeforeSendHandler;\n\t\"email:deliver\": EmailDeliverHandler;\n\t\"email:afterSend\": EmailAfterSendHandler;\n\t\"comment:beforeCreate\": CommentBeforeCreateHandler;\n\t\"comment:moderate\": CommentModerateHandler;\n\t\"comment:afterCreate\": CommentAfterCreateHandler;\n\t\"comment:afterModerate\": CommentAfterModerateHandler;\n\t\"page:metadata\": PageMetadataHandler;\n\t\"page:fragments\": PageFragmentHandler;\n}\n\n/**\n * Hook execution result\n */\nexport interface HookResult<T> {\n\tsuccess: boolean;\n\tvalue?: T;\n\terror?: Error;\n\tpluginId: string;\n\tduration: number;\n}\n\n/**\n * Hook pipeline for executing hooks in order\n */\nexport class HookPipeline {\n\tprivate hooks: Map<HookNameV2, Array<ResolvedHook<unknown>>> = new Map();\n\tprivate pluginMap: Map<string, ResolvedPlugin> = new Map();\n\tprivate contextFactory: PluginContextFactory | null = null;\n\t/** Stored so setContextFactory can merge incrementally. */\n\tprivate contextFactoryOptions: Partial<PluginContextFactoryOptions> = {};\n\n\t/** Hook names where at least one handler declared exclusive: true */\n\tprivate exclusiveHookNames: Set<string> = new Set();\n\n\t/**\n\t * Selected provider plugin ID for each exclusive hook.\n\t * Set by the PluginManager after resolution.\n\t */\n\tprivate exclusiveSelections: Map<string, string> = new Map();\n\n\tconstructor(plugins: ResolvedPlugin[], factoryOptions?: PluginContextFactoryOptions) {\n\t\tif (factoryOptions) {\n\t\t\tthis.contextFactory = new PluginContextFactory(factoryOptions);\n\t\t\tthis.contextFactoryOptions = { ...factoryOptions };\n\t\t}\n\n\t\tfor (const plugin of plugins) {\n\t\t\tthis.pluginMap.set(plugin.id, plugin);\n\t\t}\n\t\tthis.registerPlugins(plugins);\n\t}\n\n\t/**\n\t * Set or update the context factory options.\n\t *\n\t * When called on a pipeline that already has a factory, the new options\n\t * are merged on top of the existing ones so that callers don't need to\n\t * repeat every field (e.g. adding `cronReschedule` without losing\n\t * `storage` / `getUploadUrl`).\n\t */\n\tsetContextFactory(options: Partial<PluginContextFactoryOptions>): void {\n\t\tconst merged = { ...this.contextFactoryOptions, ...options };\n\t\t// The first call must include `db`; subsequent calls merge incrementally.\n\t\tthis.contextFactory = new PluginContextFactory(merged as PluginContextFactoryOptions);\n\t\tthis.contextFactoryOptions = merged;\n\t}\n\n\t/**\n\t * Get context for a plugin\n\t */\n\tprivate getContext(pluginId: string): PluginContext {\n\t\tconst plugin = this.pluginMap.get(pluginId);\n\t\tif (!plugin) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\t\tif (!this.contextFactory) {\n\t\t\tthrow new Error(\"Context factory not initialized - call setContextFactory first\");\n\t\t}\n\t\treturn this.contextFactory.createContext(plugin);\n\t}\n\n\t/**\n\t * Get typed hooks for a specific hook name.\n\t * The internal map stores ResolvedHook<unknown>, but we know each name\n\t * maps to a specific handler type via HookHandlerMap.\n\t *\n\t * Exclusive hooks that have a selected provider are filtered out — they\n\t * should only run via invokeExclusiveHook(), not in the regular pipeline.\n\t */\n\tprivate getTypedHooks<N extends HookNameV2>(name: N): Array<ResolvedHook<HookHandlerMap[N]>> {\n\t\t// The map stores hooks as ResolvedHook<unknown>. Each hook name corresponds\n\t\t// to a specific handler type. The cast here is the single point where we\n\t\t// bridge the untyped map to the typed API — callers never need to cast.\n\t\tconst all = (this.hooks.get(name) ?? []) as Array<ResolvedHook<HookHandlerMap[N]>>;\n\n\t\t// If this hook has an exclusive selection, filter out all exclusive handlers\n\t\t// so they don't run in the regular pipeline\n\t\tif (this.exclusiveSelections.has(name)) {\n\t\t\treturn all.filter((h) => !h.exclusive);\n\t\t}\n\n\t\treturn all;\n\t}\n\n\t/**\n\t * Register all hooks from plugins.\n\t *\n\t * Registers each hook name individually to preserve type safety. The\n\t * internal map stores ResolvedHook<unknown> since it's keyed by string,\n\t * but getTypedHooks() restores the correct handler type on retrieval.\n\t */\n\tprivate registerPlugins(plugins: ResolvedPlugin[]): void {\n\t\tfor (const plugin of plugins) {\n\t\t\tthis.registerPluginHook(plugin, \"plugin:install\");\n\t\t\tthis.registerPluginHook(plugin, \"plugin:activate\");\n\t\t\tthis.registerPluginHook(plugin, \"plugin:deactivate\");\n\t\t\tthis.registerPluginHook(plugin, \"plugin:uninstall\");\n\t\t\tthis.registerPluginHook(plugin, \"content:beforeSave\");\n\t\t\tthis.registerPluginHook(plugin, \"content:afterSave\");\n\t\t\tthis.registerPluginHook(plugin, \"content:beforeDelete\");\n\t\t\tthis.registerPluginHook(plugin, \"content:afterDelete\");\n\t\t\tthis.registerPluginHook(plugin, \"content:afterPublish\");\n\t\t\tthis.registerPluginHook(plugin, \"content:afterUnpublish\");\n\t\t\tthis.registerPluginHook(plugin, \"media:beforeUpload\");\n\t\t\tthis.registerPluginHook(plugin, \"media:afterUpload\");\n\t\t\tthis.registerPluginHook(plugin, \"cron\");\n\t\t\tthis.registerPluginHook(plugin, \"email:beforeSend\");\n\t\t\tthis.registerPluginHook(plugin, \"email:deliver\");\n\t\t\tthis.registerPluginHook(plugin, \"email:afterSend\");\n\t\t\tthis.registerPluginHook(plugin, \"comment:beforeCreate\");\n\t\t\tthis.registerPluginHook(plugin, \"comment:moderate\");\n\t\t\tthis.registerPluginHook(plugin, \"comment:afterCreate\");\n\t\t\tthis.registerPluginHook(plugin, \"comment:afterModerate\");\n\t\t\tthis.registerPluginHook(plugin, \"page:metadata\");\n\t\t\tthis.registerPluginHook(plugin, \"page:fragments\");\n\t\t}\n\n\t\t// Sort hooks by priority and dependencies\n\t\tfor (const [hookName, hooks] of this.hooks) {\n\t\t\tthis.hooks.set(hookName, this.sortHooks(hooks));\n\t\t}\n\t}\n\n\t/**\n\t * Maps hook names to the capability required to register them.\n\t *\n\t * Hooks not listed here have no capability requirement (e.g. lifecycle\n\t * hooks, cron). Any plugin declaring a listed hook without the required\n\t * capability will have that hook silently skipped at registration time.\n\t */\n\tprivate static readonly HOOK_REQUIRED_CAPABILITY: ReadonlyMap<string, string> = new Map([\n\t\t// Email — registering email:beforeSend/afterSend/deliver requires the\n\t\t// matching `hooks.email-*:register` capability. These are distinct\n\t\t// from `email:send` (which gates ctx.email) so that \"this plugin\n\t\t// reads/writes email events\" is visible separately from \"this\n\t\t// plugin can send email\".\n\t\t[\"email:beforeSend\", \"hooks.email-events:register\"],\n\t\t[\"email:afterSend\", \"hooks.email-events:register\"],\n\t\t[\"email:deliver\", \"hooks.email-transport:register\"],\n\t\t// Content — beforeSave can mutate content, so requires content:write.\n\t\t// afterSave is read-only notification, so content:read suffices.\n\t\t[\"content:beforeSave\", \"content:write\"],\n\t\t[\"content:afterSave\", \"content:read\"],\n\t\t[\"content:beforeDelete\", \"content:read\"],\n\t\t[\"content:afterDelete\", \"content:read\"],\n\t\t[\"content:afterPublish\", \"content:read\"],\n\t\t[\"content:afterUnpublish\", \"content:read\"],\n\t\t// Media\n\t\t[\"media:beforeUpload\", \"media:write\"],\n\t\t[\"media:afterUpload\", \"media:read\"],\n\t\t// Comments — hooks expose author email, IP hash, user agent\n\t\t[\"comment:beforeCreate\", \"users:read\"],\n\t\t[\"comment:moderate\", \"users:read\"],\n\t\t[\"comment:afterCreate\", \"users:read\"],\n\t\t[\"comment:afterModerate\", \"users:read\"],\n\t\t// Page fragments — can inject arbitrary scripts into every public page\n\t\t[\"page:fragments\", \"hooks.page-fragments:register\"],\n\t]);\n\n\t/**\n\t * Register a single plugin's hook by name\n\t */\n\tprivate registerPluginHook(plugin: ResolvedPlugin, name: HookNameV2): void {\n\t\tconst hook = plugin.hooks[name];\n\t\tif (!hook) return;\n\n\t\t// Hooks that expose sensitive data or inject into pages require specific\n\t\t// capabilities. Plugins without the required capability have the hook\n\t\t// silently skipped to prevent unauthorized data access or page injection.\n\t\tconst requiredCapability = HookPipeline.HOOK_REQUIRED_CAPABILITY.get(name);\n\t\tif (requiredCapability && !plugin.capabilities.includes(requiredCapability as never)) {\n\t\t\tconsole.warn(\n\t\t\t\t`[hooks] Plugin \"${plugin.id}\" declares ${name} hook without ${requiredCapability} capability — skipping`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// Track exclusive hooks\n\t\tif (hook.exclusive) {\n\t\t\tthis.exclusiveHookNames.add(name);\n\t\t}\n\n\t\t// ResolvedHook<SpecificHandler> is assignable to ResolvedHook<unknown>\n\t\t// because the handler property is covariant\n\t\tthis.registerHook(name, hook);\n\t}\n\n\t/**\n\t * Register a single hook\n\t */\n\tprivate registerHook(name: HookNameV2, hook: ResolvedHook<unknown>): void {\n\t\tconst existing = this.hooks.get(name) || [];\n\t\texisting.push(hook);\n\t\tthis.hooks.set(name, existing);\n\t}\n\n\t/**\n\t * Sort hooks by priority and dependencies\n\t */\n\tprivate sortHooks(hooks: Array<ResolvedHook<unknown>>): Array<ResolvedHook<unknown>> {\n\t\tconst sorted: Array<ResolvedHook<unknown>> = [];\n\t\tconst remaining = [...hooks];\n\n\t\t// Simple topological sort with priority as tiebreaker\n\t\twhile (remaining.length > 0) {\n\t\t\t// Find hooks whose dependencies are satisfied\n\t\t\tconst ready = remaining.filter((hook) =>\n\t\t\t\thook.dependencies.every((dep) => sorted.some((s) => s.pluginId === dep)),\n\t\t\t);\n\n\t\t\tif (ready.length === 0) {\n\t\t\t\t// Circular dependency or missing dependency - log warning and fall back to priority\n\t\t\t\tconst pluginIds = remaining.map((h) => h.pluginId).join(\", \");\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[hooks] Hook dependency cycle or missing dependency detected among plugins: ${pluginIds}. Falling back to priority order.`,\n\t\t\t\t);\n\t\t\t\tremaining.sort((a, b) => a.priority - b.priority);\n\t\t\t\tsorted.push(...remaining);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Sort ready hooks by priority and add the first one\n\t\t\tready.sort((a, b) => a.priority - b.priority);\n\t\t\tconst next = ready[0];\n\t\t\tsorted.push(next);\n\t\t\tremaining.splice(remaining.indexOf(next), 1);\n\t\t}\n\n\t\treturn sorted;\n\t}\n\n\t/**\n\t * Execute a hook with timeout\n\t */\n\tprivate async executeWithTimeout<T>(fn: () => Promise<T>, timeout: number): Promise<T> {\n\t\tlet timer: ReturnType<typeof setTimeout>;\n\t\tconst timeoutPromise = new Promise<T>(\n\t\t\t(_, reject) =>\n\t\t\t\t(timer = setTimeout(() => reject(new Error(`Hook timeout after ${timeout}ms`)), timeout)),\n\t\t);\n\t\ttry {\n\t\t\treturn await Promise.race([fn(), timeoutPromise]);\n\t\t} finally {\n\t\t\tclearTimeout(timer!);\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Lifecycle Hooks\n\t// =========================================================================\n\n\t/**\n\t * Run plugin:install hooks\n\t */\n\tasync runPluginInstall(pluginId: string): Promise<HookResult<void>[]> {\n\t\treturn this.runLifecycleHook(\"plugin:install\", pluginId);\n\t}\n\n\t/**\n\t * Run plugin:activate hooks\n\t */\n\tasync runPluginActivate(pluginId: string): Promise<HookResult<void>[]> {\n\t\treturn this.runLifecycleHook(\"plugin:activate\", pluginId);\n\t}\n\n\t/**\n\t * Run plugin:deactivate hooks\n\t */\n\tasync runPluginDeactivate(pluginId: string): Promise<HookResult<void>[]> {\n\t\treturn this.runLifecycleHook(\"plugin:deactivate\", pluginId);\n\t}\n\n\t/**\n\t * Run plugin:uninstall hooks\n\t */\n\tasync runPluginUninstall(pluginId: string, deleteData: boolean): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(\"plugin:uninstall\");\n\t\tconst results: HookResult<void>[] = [];\n\n\t\t// Only run the hook for the specific plugin being uninstalled\n\t\tconst hook = hooks.find((h) => h.pluginId === pluginId);\n\t\tif (!hook) return results;\n\n\t\tconst { handler } = hook;\n\t\tconst event: UninstallEvent = { deleteData };\n\t\tconst ctx = this.getContext(pluginId);\n\t\tconst start = Date.now();\n\n\t\ttry {\n\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\tresults.push({\n\t\t\t\tsuccess: true,\n\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\tduration: Date.now() - start,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tresults.push({\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\tduration: Date.now() - start,\n\t\t\t});\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tprivate async runLifecycleHook(\n\t\thookName: \"plugin:install\" | \"plugin:activate\" | \"plugin:deactivate\",\n\t\tpluginId: string,\n\t): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(hookName);\n\t\tconst results: HookResult<void>[] = [];\n\n\t\t// Only run the hook for the specific plugin\n\t\tconst hook = hooks.find((h) => h.pluginId === pluginId);\n\t\tif (!hook) return results;\n\n\t\tconst { handler } = hook;\n\t\tconst event: LifecycleEvent = {};\n\t\tconst ctx = this.getContext(pluginId);\n\t\tconst start = Date.now();\n\n\t\ttry {\n\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\tresults.push({\n\t\t\t\tsuccess: true,\n\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\tduration: Date.now() - start,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tresults.push({\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\tduration: Date.now() - start,\n\t\t\t});\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t// =========================================================================\n\t// Content Hooks\n\t// =========================================================================\n\n\t/**\n\t * Run content:beforeSave hooks\n\t * Returns modified content from the pipeline\n\t */\n\tasync runContentBeforeSave(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t\tisNew: boolean,\n\t): Promise<{\n\t\tcontent: Record<string, unknown>;\n\t\tresults: HookResult<Record<string, unknown>>[];\n\t}> {\n\t\tconst hooks = this.getTypedHooks(\"content:beforeSave\");\n\t\tconst results: HookResult<Record<string, unknown>>[] = [];\n\t\tlet currentContent = content;\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: ContentHookEvent = {\n\t\t\t\tcontent: currentContent,\n\t\t\t\tcollection,\n\t\t\t\tisNew,\n\t\t\t};\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tconst result = await this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\t// Handler can return modified content or void (keep current)\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tcurrentContent = result;\n\t\t\t\t}\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tvalue: currentContent,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { content: currentContent, results };\n\t}\n\n\t/**\n\t * Run content:afterSave hooks\n\t */\n\tasync runContentAfterSave(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t\tisNew: boolean,\n\t): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(\"content:afterSave\");\n\t\tconst results: HookResult<void>[] = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: ContentHookEvent = { content, collection, isNew };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Run content:beforeDelete hooks\n\t * Returns whether deletion is allowed\n\t */\n\tasync runContentBeforeDelete(\n\t\tid: string,\n\t\tcollection: string,\n\t): Promise<{ allowed: boolean; results: HookResult<boolean>[] }> {\n\t\tconst hooks = this.getTypedHooks(\"content:beforeDelete\");\n\t\tconst results: HookResult<boolean>[] = [];\n\t\tlet allowed = true;\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: ContentDeleteEvent = { id, collection, permanent: false };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tconst result = await this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\t// Handler returns false to block, true or void to allow\n\t\t\t\tif (result === false) {\n\t\t\t\t\tallowed = false;\n\t\t\t\t}\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tvalue: result !== false,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { allowed, results };\n\t}\n\n\t/**\n\t * Run content:afterDelete hooks\n\t */\n\tasync runContentAfterDelete(\n\t\tid: string,\n\t\tcollection: string,\n\t\tpermanent: boolean,\n\t): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(\"content:afterDelete\");\n\t\tconst results: HookResult<void>[] = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: ContentDeleteEvent = { id, collection, permanent };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Run content:afterPublish hooks (fire-and-forget).\n\t */\n\tasync runContentAfterPublish(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(\"content:afterPublish\");\n\t\tconst results: HookResult<void>[] = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: ContentPublishStateChangeEvent = { content, collection };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Run content:afterUnpublish hooks (fire-and-forget).\n\t */\n\tasync runContentAfterUnpublish(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(\"content:afterUnpublish\");\n\t\tconst results: HookResult<void>[] = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: ContentPublishStateChangeEvent = { content, collection };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t// =========================================================================\n\t// Media Hooks\n\t// =========================================================================\n\n\t/**\n\t * Run media:beforeUpload hooks\n\t */\n\tasync runMediaBeforeUpload(file: { name: string; type: string; size: number }): Promise<{\n\t\tfile: { name: string; type: string; size: number };\n\t\tresults: HookResult<{ name: string; type: string; size: number }>[];\n\t}> {\n\t\tconst hooks = this.getTypedHooks(\"media:beforeUpload\");\n\t\tconst results: HookResult<{\n\t\t\tname: string;\n\t\t\ttype: string;\n\t\t\tsize: number;\n\t\t}>[] = [];\n\t\tlet currentFile = file;\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: MediaUploadEvent = { file: currentFile };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tconst result = await this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\t// Handler can return modified file info or void\n\t\t\t\tif (result !== undefined) {\n\t\t\t\t\tcurrentFile = result;\n\t\t\t\t}\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tvalue: currentFile,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { file: currentFile, results };\n\t}\n\n\t/**\n\t * Run media:afterUpload hooks\n\t */\n\tasync runMediaAfterUpload(media: {\n\t\tid: string;\n\t\tfilename: string;\n\t\tmimeType: string;\n\t\tsize: number | null;\n\t\turl: string;\n\t\tcreatedAt: string;\n\t}): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(\"media:afterUpload\");\n\t\tconst results: HookResult<void>[] = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event: MediaAfterUploadEvent = { media };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t// =========================================================================\n\t// Cron Hook (per-plugin dispatch)\n\t// =========================================================================\n\n\t/**\n\t * Invoke the cron hook for a specific plugin.\n\t *\n\t * Unlike other hooks which broadcast to all plugins, the cron hook is\n\t * dispatched only to the target plugin — the one that owns the task.\n\t */\n\tasync invokeCronHook(pluginId: string, event: CronEvent): Promise<HookResult<void>> {\n\t\tconst hooks = this.getTypedHooks(\"cron\");\n\t\tconst hook = hooks.find((h) => h.pluginId === pluginId);\n\n\t\tif (!hook) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: new Error(`Plugin \"${pluginId}\" has no cron hook registered`),\n\t\t\t\tpluginId,\n\t\t\t\tduration: 0,\n\t\t\t};\n\t\t}\n\n\t\tconst { handler } = hook;\n\t\tconst ctx = this.getContext(pluginId);\n\t\tconst start = Date.now();\n\n\t\ttry {\n\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tpluginId,\n\t\t\t\tduration: Date.now() - start,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\tpluginId,\n\t\t\t\tduration: Date.now() - start,\n\t\t\t};\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Email Hooks\n\t// =========================================================================\n\n\t/**\n\t * Run email:beforeSend hooks (middleware pipeline).\n\t *\n\t * Each handler receives the message and returns a modified message or\n\t * `false` to cancel delivery. The pipeline chains message transformations —\n\t * each handler receives the output of the previous one.\n\t */\n\tasync runEmailBeforeSend(\n\t\tmessage: EmailMessage,\n\t\tsource: string,\n\t): Promise<{ message: EmailMessage | false; results: HookResult<EmailMessage | false>[] }> {\n\t\tconst hooks = this.getTypedHooks(\"email:beforeSend\");\n\t\tconst results: HookResult<EmailMessage | false>[] = [];\n\t\tlet currentMessage: EmailMessage = message;\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\t// Shallow-clone message to prevent handlers from mutating\n\t\t\t// the shared reference and leaking changes to subsequent stages\n\t\t\tconst event: EmailBeforeSendEvent = { message: { ...currentMessage }, source };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tconst result = await this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\n\t\t\t\tif (result === false) {\n\t\t\t\t\t// Cancelled\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\tvalue: false,\n\t\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t\t});\n\t\t\t\t\treturn { message: false, results };\n\t\t\t\t}\n\n\t\t\t\t// Handler returned a modified message\n\t\t\t\tif (result && typeof result === \"object\") {\n\t\t\t\t\tcurrentMessage = result;\n\t\t\t\t}\n\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tvalue: currentMessage,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { message: currentMessage, results };\n\t}\n\n\t/**\n\t * Run email:afterSend hooks (fire-and-forget).\n\t *\n\t * Errors are logged but don't propagate — they don't affect the caller.\n\t */\n\tasync runEmailAfterSend(message: EmailMessage, source: string): Promise<HookResult<void>[]> {\n\t\tconst hooks = this.getTypedHooks(\"email:afterSend\");\n\t\tconst results: HookResult<void>[] = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst event = { message, source };\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\t// Fire-and-forget: log but don't propagate\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[email:afterSend] Plugin \"${hook.pluginId}\" error:`,\n\t\t\t\t\terror instanceof Error ? error.message : error,\n\t\t\t\t);\n\t\t\t\tresults.push({\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t\tpluginId: hook.pluginId,\n\t\t\t\t\tduration: Date.now() - start,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t// =========================================================================\n\t// Comment Hooks\n\t// =========================================================================\n\n\t/**\n\t * Run comment:beforeCreate hooks (middleware pipeline).\n\t *\n\t * Each handler receives the event and returns a modified event or\n\t * `false` to reject the comment. The pipeline chains transformations —\n\t * each handler receives the output of the previous one.\n\t */\n\tasync runCommentBeforeCreate(\n\t\tevent: CommentBeforeCreateEvent,\n\t): Promise<CommentBeforeCreateEvent | false> {\n\t\tconst hooks = this.getTypedHooks(\"comment:beforeCreate\");\n\t\tlet currentEvent = event;\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\t\t\tconst start = Date.now();\n\n\t\t\ttry {\n\t\t\t\tconst result = await this.executeWithTimeout(\n\t\t\t\t\t() => handler({ ...currentEvent }, ctx),\n\t\t\t\t\thook.timeout,\n\t\t\t\t);\n\n\t\t\t\tif (result === false) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (result && typeof result === \"object\") {\n\t\t\t\t\tcurrentEvent = result;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[comment:beforeCreate] Plugin \"${hook.pluginId}\" error (${Date.now() - start}ms):`,\n\t\t\t\t\terror instanceof Error ? error.message : error,\n\t\t\t\t);\n\n\t\t\t\tif (hook.errorPolicy === \"abort\") {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn currentEvent;\n\t}\n\n\t/**\n\t * Run comment:afterCreate hooks (fire-and-forget).\n\t *\n\t * Errors are logged but don't propagate — they don't affect the caller.\n\t */\n\tasync runCommentAfterCreate(event: CommentAfterCreateEvent): Promise<void> {\n\t\tconst hooks = this.getTypedHooks(\"comment:afterCreate\");\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[comment:afterCreate] Plugin \"${hook.pluginId}\" error:`,\n\t\t\t\t\terror instanceof Error ? error.message : error,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Run comment:afterModerate hooks (fire-and-forget).\n\t *\n\t * Errors are logged but don't propagate — they don't affect the caller.\n\t */\n\tasync runCommentAfterModerate(event: CommentAfterModerateEvent): Promise<void> {\n\t\tconst hooks = this.getTypedHooks(\"comment:afterModerate\");\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\n\t\t\ttry {\n\t\t\t\tawait this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[comment:afterModerate] Plugin \"${hook.pluginId}\" error:`,\n\t\t\t\t\terror instanceof Error ? error.message : error,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Public Page Hooks\n\t// =========================================================================\n\n\t/**\n\t * Run page:metadata hooks. Each handler returns contributions that are\n\t * merged by the metadata collector. Errors are logged but don't propagate.\n\t */\n\tasync runPageMetadata(\n\t\tevent: PageMetadataEvent,\n\t): Promise<Array<{ pluginId: string; contributions: PageMetadataContribution[] }>> {\n\t\tconst hooks = this.getTypedHooks(\"page:metadata\");\n\t\tconst results: Array<{ pluginId: string; contributions: PageMetadataContribution[] }> = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\n\t\t\ttry {\n\t\t\t\tconst result = await this.executeWithTimeout(\n\t\t\t\t\t() => Promise.resolve(handler(event, ctx)),\n\t\t\t\t\thook.timeout,\n\t\t\t\t);\n\n\t\t\t\tif (result != null) {\n\t\t\t\t\tconst contributions = Array.isArray(result) ? result : [result];\n\t\t\t\t\tresults.push({ pluginId: hook.pluginId, contributions });\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[page:metadata] Plugin \"${hook.pluginId}\" error:`,\n\t\t\t\t\terror instanceof Error ? error.message : error,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Run page:fragments hooks. Only trusted plugins should be registered\n\t * for this hook. Errors are logged but don't propagate.\n\t */\n\tasync runPageFragments(\n\t\tevent: PageFragmentEvent,\n\t): Promise<Array<{ pluginId: string; contributions: PageFragmentContribution[] }>> {\n\t\tconst hooks = this.getTypedHooks(\"page:fragments\");\n\t\tconst results: Array<{ pluginId: string; contributions: PageFragmentContribution[] }> = [];\n\n\t\tfor (const hook of hooks) {\n\t\t\tconst { handler } = hook;\n\t\t\tconst ctx = this.getContext(hook.pluginId);\n\n\t\t\ttry {\n\t\t\t\tconst result = await this.executeWithTimeout(\n\t\t\t\t\t() => Promise.resolve(handler(event, ctx)),\n\t\t\t\t\thook.timeout,\n\t\t\t\t);\n\n\t\t\t\tif (result != null) {\n\t\t\t\t\tconst contributions = Array.isArray(result) ? result : [result];\n\t\t\t\t\tresults.push({ pluginId: hook.pluginId, contributions });\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[page:fragments] Plugin \"${hook.pluginId}\" error:`,\n\t\t\t\t\terror instanceof Error ? error.message : error,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t// =========================================================================\n\t// Utilities\n\t// =========================================================================\n\n\t/**\n\t * Check if any hooks are registered for a given name\n\t */\n\thasHooks(name: HookNameV2): boolean {\n\t\tconst hooks = this.hooks.get(name);\n\t\treturn hooks !== undefined && hooks.length > 0;\n\t}\n\n\t/**\n\t * Get hook count for debugging\n\t */\n\tgetHookCount(name: HookNameV2): number {\n\t\treturn this.hooks.get(name)?.length || 0;\n\t}\n\n\t/**\n\t * Get all registered hook names\n\t */\n\tgetRegisteredHooks(): HookNameV2[] {\n\t\treturn [...this.hooks.keys()];\n\t}\n\n\t// =========================================================================\n\t// Exclusive Hook Support\n\t// =========================================================================\n\n\t/**\n\t * Returns hook names where at least one handler declared exclusive: true\n\t */\n\tgetRegisteredExclusiveHooks(): string[] {\n\t\treturn [...this.exclusiveHookNames];\n\t}\n\n\t/**\n\t * Check if a hook is exclusive\n\t */\n\tisExclusiveHook(name: string): boolean {\n\t\treturn this.exclusiveHookNames.has(name);\n\t}\n\n\t/**\n\t * Set the selected provider for an exclusive hook.\n\t * Called by PluginManager after resolution.\n\t */\n\tsetExclusiveSelection(hookName: string, pluginId: string): void {\n\t\tthis.exclusiveSelections.set(hookName, pluginId);\n\t}\n\n\t/**\n\t * Clear the selected provider for an exclusive hook.\n\t */\n\tclearExclusiveSelection(hookName: string): void {\n\t\tthis.exclusiveSelections.delete(hookName);\n\t}\n\n\t/**\n\t * Get the selected provider for an exclusive hook (if any).\n\t */\n\tgetExclusiveSelection(hookName: string): string | undefined {\n\t\treturn this.exclusiveSelections.get(hookName);\n\t}\n\n\t/**\n\t * Get all plugins that registered a handler for a given exclusive hook.\n\t */\n\tgetExclusiveHookProviders(hookName: string): Array<{ pluginId: string }> {\n\t\tconst hooks = this.hooks.get(hookName as HookNameV2) ?? [];\n\t\treturn hooks.filter((h) => h.exclusive).map((h) => ({ pluginId: h.pluginId }));\n\t}\n\n\t/**\n\t * Get all plugins that registered a non-exclusive handler for a given\n\t * hook (e.g. `email:beforeSend`, `email:afterSend`), preserving priority\n\t * order. Partitions with `getExclusiveHookProviders()`, which returns\n\t * plugins whose registration is marked `exclusive: true`.\n\t */\n\tgetHookProviders(hookName: string): Array<{ pluginId: string }> {\n\t\tconst hooks = this.hooks.get(hookName as HookNameV2) ?? [];\n\t\treturn hooks.filter((h) => !h.exclusive).map((h) => ({ pluginId: h.pluginId }));\n\t}\n\n\t/**\n\t * Invoke an exclusive hook — dispatch only to the selected provider.\n\t * Returns null if no provider is selected or if the selected hook\n\t * is not found in the pipeline.\n\t *\n\t * This is a generic dispatch used by the email pipeline and other\n\t * exclusive hook consumers. The handler type is unknown — callers\n\t * must know the expected signature.\n\t *\n\t * Errors are isolated: a failing handler returns an error result\n\t * instead of propagating the exception to the caller.\n\t */\n\tasync invokeExclusiveHook(\n\t\thookName: string,\n\t\tevent: unknown,\n\t): Promise<{ result: unknown; pluginId: string; error?: Error; duration: number } | null> {\n\t\tconst selectedPluginId = this.exclusiveSelections.get(hookName);\n\t\tif (!selectedPluginId) return null;\n\n\t\tconst hooks = this.hooks.get(hookName as HookNameV2) ?? [];\n\t\tconst hook = hooks.find((h) => h.pluginId === selectedPluginId && h.exclusive);\n\t\tif (!hook) return null;\n\n\t\tconst start = Date.now();\n\t\ttry {\n\t\t\tconst ctx = this.getContext(selectedPluginId);\n\t\t\tconst handler = hook.handler as (event: unknown, ctx: PluginContext) => Promise<unknown>;\n\t\t\tconst result = await this.executeWithTimeout(() => handler(event, ctx), hook.timeout);\n\t\t\treturn { result, pluginId: selectedPluginId, duration: Date.now() - start };\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tresult: undefined,\n\t\t\t\tpluginId: selectedPluginId,\n\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\tduration: Date.now() - start,\n\t\t\t};\n\t\t}\n\t}\n}\n\n/**\n * Create a hook pipeline from plugins\n */\nexport function createHookPipeline(\n\tplugins: ResolvedPlugin[],\n\tfactoryOptions?: PluginContextFactoryOptions,\n): HookPipeline {\n\treturn new HookPipeline(plugins, factoryOptions);\n}\n\n// ── Shared exclusive hook resolution ─────────────────────────────────────────\n\n/**\n * Options for exclusive hook resolution.\n */\nexport interface ExclusiveHookResolutionOptions {\n\tpipeline: HookPipeline;\n\t/**\n\t * Check whether a plugin ID is currently active.\n\t * Used to filter providers — only active providers participate in selection.\n\t */\n\tisActive: (pluginId: string) => boolean;\n\t/** Read an option value from persistent storage. */\n\tgetOption: (key: string) => Promise<string | null>;\n\t/** Write an option value to persistent storage. */\n\tsetOption: (key: string, value: string) => Promise<void>;\n\t/** Delete an option from persistent storage. */\n\tdeleteOption: (key: string) => Promise<void>;\n\t/**\n\t * Map of pluginId → hook names the plugin prefers to handle.\n\t * Used as a tiebreaker when no DB selection exists and multiple providers are active.\n\t */\n\tpreferredHints?: Map<string, string[]>;\n}\n\n/** Options table key prefix for exclusive hook selections */\nconst EXCLUSIVE_HOOK_KEY_PREFIX = \"emdash:exclusive_hook:\";\n\n/**\n * Resolve exclusive hook selections.\n *\n * Shared algorithm used by both PluginManager and EmDashRuntime:\n * 1. If a DB selection exists and that plugin is active → keep it.\n * 2. If DB selection is stale (plugin inactive/gone) → clear it.\n * 3. If no selection and only one active provider → auto-select it.\n * 4. If preferred hints match an active provider → first match wins.\n * 5. If multiple providers and no hint → leave unselected (admin must choose).\n */\nexport async function resolveExclusiveHooks(opts: ExclusiveHookResolutionOptions): Promise<void> {\n\tconst { pipeline, isActive, getOption, setOption, deleteOption, preferredHints } = opts;\n\tconst exclusiveHookNames = pipeline.getRegisteredExclusiveHooks();\n\n\tfor (const hookName of exclusiveHookNames) {\n\t\tconst providers = pipeline.getExclusiveHookProviders(hookName);\n\t\tconst activeProviderIds = new Set(\n\t\t\tproviders.map((p) => p.pluginId).filter((id) => isActive(id)),\n\t\t);\n\n\t\tconst key = `${EXCLUSIVE_HOOK_KEY_PREFIX}${hookName}`;\n\t\tlet currentSelection: string | null = null;\n\t\ttry {\n\t\t\tcurrentSelection = await getOption(key);\n\t\t} catch {\n\t\t\t// Options table may not be ready\n\t\t\tcontinue;\n\t\t}\n\n\t\t// If selection exists and the plugin is still active → keep it\n\t\tif (currentSelection && activeProviderIds.has(currentSelection)) {\n\t\t\tpipeline.setExclusiveSelection(hookName, currentSelection);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Selection is stale or missing — clear it\n\t\tif (currentSelection) {\n\t\t\ttry {\n\t\t\t\tawait deleteOption(key);\n\t\t\t} catch {\n\t\t\t\t// Non-fatal\n\t\t\t}\n\t\t}\n\n\t\t// Auto-select if only one active provider\n\t\tif (activeProviderIds.size === 1) {\n\t\t\tconst [onlyProvider] = activeProviderIds;\n\t\t\ttry {\n\t\t\t\tawait setOption(key, onlyProvider);\n\t\t\t} catch {\n\t\t\t\t// Non-fatal\n\t\t\t}\n\t\t\tpipeline.setExclusiveSelection(hookName, onlyProvider);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check preferred hints\n\t\tif (preferredHints) {\n\t\t\tlet found = false;\n\t\t\tfor (const [pluginId, hooks] of preferredHints) {\n\t\t\t\tif (hooks.includes(hookName) && activeProviderIds.has(pluginId)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait setOption(key, pluginId);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Non-fatal\n\t\t\t\t\t}\n\t\t\t\t\tpipeline.setExclusiveSelection(hookName, pluginId);\n\t\t\t\t\tfound = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (found) continue;\n\t\t}\n\n\t\t// Multiple providers, no hint — leave unselected\n\t\tpipeline.clearExclusiveSelection(hookName);\n\t}\n}\n","/**\n * Email Pipeline\n *\n * Orchestrates the three-stage email pipeline:\n * 1. email:beforeSend hooks (middleware — transform, validate, cancel)\n * 2. email:deliver hook (exclusive — exactly one provider delivers)\n * 3. email:afterSend hooks (logging, analytics, fire-and-forget)\n *\n * Security features:\n * - Recursion guard prevents re-entrant sends (e.g. plugin calling ctx.email.send from a hook)\n * - System emails (source=\"system\") bypass email:beforeSend and email:afterSend hooks entirely\n * to protect auth tokens from exfiltration by plugin hooks\n *\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type { HookPipeline } from \"./hooks.js\";\nimport type { EmailDeliverEvent, EmailMessage } from \"./types.js\";\n\n/** Hook name for the exclusive email delivery hook */\nconst EMAIL_DELIVER_HOOK = \"email:deliver\";\n\n/** Source value used for auth emails (magic links, invites, password resets) */\nconst SYSTEM_SOURCE = \"system\";\n\n/**\n * Error thrown when ctx.email.send() is called but no provider is configured.\n */\nexport class EmailNotConfiguredError extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"No email provider is configured. Install and activate an email provider plugin, \" +\n\t\t\t\t\"then select it in Settings > Email.\",\n\t\t);\n\t\tthis.name = \"EmailNotConfiguredError\";\n\t}\n}\n\n/**\n * Error thrown when a recursive email send is detected.\n */\nexport class EmailRecursionError extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"Recursive email send detected. A plugin hook attempted to send an email \" +\n\t\t\t\t\"from within the email pipeline, which would cause infinite recursion.\",\n\t\t);\n\t\tthis.name = \"EmailRecursionError\";\n\t}\n}\n\n/**\n * Recursion guard using AsyncLocalStorage.\n *\n * EmailPipeline is a singleton (worker-lifetime cached via EmDashRuntime).\n * Instance state like `sendDepth` would false-positive under concurrent\n * requests because two unrelated sends would increment the same counter.\n * ALS scopes the guard to the current async execution context, so concurrent\n * requests each get their own independent recursion tracking.\n */\nconst emailSendALS = new AsyncLocalStorage<{ depth: number }>();\n\n/**\n * EmailPipeline orchestrates email delivery through the plugin hook system.\n *\n * The pipeline runs in three stages:\n * 1. email:beforeSend — middleware hooks that can transform or cancel messages\n * 2. email:deliver — exclusive hook dispatching to the selected provider\n * 3. email:afterSend — fire-and-forget hooks for logging/analytics\n */\nexport class EmailPipeline {\n\tprivate pipeline: HookPipeline;\n\n\tconstructor(pipeline: HookPipeline) {\n\t\tthis.pipeline = pipeline;\n\t}\n\n\t/**\n\t * Replace the underlying hook pipeline.\n\t *\n\t * Called by the runtime when rebuilding the hook pipeline after a\n\t * plugin is enabled or disabled, so the email pipeline dispatches\n\t * to the current set of active hooks.\n\t */\n\tsetPipeline(pipeline: HookPipeline): void {\n\t\tthis.pipeline = pipeline;\n\t}\n\n\t/**\n\t * Send an email through the full pipeline.\n\t *\n\t * @param message - The email to send\n\t * @param source - Where the email originated (\"system\" for auth, plugin ID for plugins)\n\t * @throws EmailNotConfiguredError if no provider is selected\n\t * @throws EmailRecursionError if called re-entrantly from within a hook\n\t * @throws Error if the provider handler throws\n\t */\n\tasync send(message: EmailMessage, source: string): Promise<void> {\n\t\t// Recursion guard: a plugin with email:send + email:intercept calling\n\t\t// ctx.email.send() from an email hook would loop forever.\n\t\t// Uses AsyncLocalStorage so concurrent requests don't interfere —\n\t\t// each async context tracks its own depth independently.\n\t\tconst store = emailSendALS.getStore();\n\t\tif (store && store.depth > 0) {\n\t\t\tthrow new EmailRecursionError();\n\t\t}\n\n\t\tconst run = () => this.sendInner(message, source);\n\t\tif (store) {\n\t\t\t// Already inside an ALS context (e.g. nested call) — increment depth\n\t\t\tstore.depth++;\n\t\t\ttry {\n\t\t\t\tawait run();\n\t\t\t} finally {\n\t\t\t\tstore.depth--;\n\t\t\t}\n\t\t} else {\n\t\t\t// First call — create new ALS context\n\t\t\tawait emailSendALS.run({ depth: 1 }, run);\n\t\t}\n\t}\n\n\t/**\n\t * Inner send implementation, separated from the recursion guard.\n\t */\n\tprivate async sendInner(message: EmailMessage, source: string): Promise<void> {\n\t\t// Validate message fields at the pipeline boundary. TypeScript enforces\n\t\t// this at compile time, but sandboxed plugins cross an RPC boundary\n\t\t// where runtime types aren't guaranteed.\n\t\tif (!message || typeof message !== \"object\") {\n\t\t\tthrow new Error(\"Invalid email message: message must be an object\");\n\t\t}\n\t\tif (!message.to || typeof message.to !== \"string\") {\n\t\t\tthrow new Error(\"Invalid email message: 'to' is required and must be a string\");\n\t\t}\n\t\tif (!message.subject || typeof message.subject !== \"string\") {\n\t\t\tthrow new Error(\"Invalid email message: 'subject' is required and must be a string\");\n\t\t}\n\t\tif (!message.text || typeof message.text !== \"string\") {\n\t\t\tthrow new Error(\"Invalid email message: 'text' is required and must be a string\");\n\t\t}\n\n\t\tconst isSystemEmail = source === SYSTEM_SOURCE;\n\n\t\t// System emails (auth tokens, magic links, invites) skip the\n\t\t// email:beforeSend pipeline entirely. These contain sensitive tokens\n\t\t// that must never be exposed to plugin hooks — a malicious interceptor\n\t\t// could rewrite the body/URL to steal auth tokens even if the `to`\n\t\t// field is protected.\n\t\tlet finalMessage: EmailMessage;\n\t\tif (isSystemEmail) {\n\t\t\tfinalMessage = message;\n\t\t} else {\n\t\t\t// Stage 1: email:beforeSend middleware (can transform or cancel)\n\t\t\tconst beforeResult = await this.pipeline.runEmailBeforeSend(message, source);\n\n\t\t\tif (beforeResult.message === false) {\n\t\t\t\t// Cancelled by middleware — find which plugin cancelled for audit log\n\t\t\t\tconst cancellingResult = beforeResult.results.find((r) => r.value === false);\n\t\t\t\tconst cancelledBy = cancellingResult?.pluginId ?? \"unknown\";\n\n\t\t\t\tconsole.info(`[email] Email to \"${message.to}\" cancelled by plugin \"${cancelledBy}\"`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinalMessage = beforeResult.message;\n\t\t}\n\n\t\t// Stage 2: email:deliver (exclusive hook)\n\t\tconst deliverEvent: EmailDeliverEvent = { message: finalMessage, source };\n\t\tconst deliverResult = await this.pipeline.invokeExclusiveHook(EMAIL_DELIVER_HOOK, deliverEvent);\n\n\t\tif (!deliverResult) {\n\t\t\tthrow new EmailNotConfiguredError();\n\t\t}\n\n\t\tif (deliverResult.error) {\n\t\t\tthrow deliverResult.error;\n\t\t}\n\n\t\t// Stage 3: email:afterSend (fire-and-forget)\n\t\t// System emails skip afterSend for the same reason they skip beforeSend:\n\t\t// the message contains plaintext auth tokens that must not be exposed to\n\t\t// plugin hooks. A logging/analytics hook could exfiltrate magic link URLs.\n\t\t// Errors are logged internally by the pipeline, not propagated.\n\t\tif (!isSystemEmail) {\n\t\t\tthis.pipeline\n\t\t\t\t.runEmailAfterSend(finalMessage, source)\n\t\t\t\t.catch((err) =>\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\"[email] afterSend pipeline error:\",\n\t\t\t\t\t\terr instanceof Error ? err.message : err,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Check if an email provider is configured and available.\n\t *\n\t * Returns true if an email:deliver provider is selected in the exclusive\n\t * hook system. Plugins and auth code use this to decide whether to show\n\t * \"send invite\" vs \"copy invite link\" UI.\n\t */\n\tisAvailable(): boolean {\n\t\treturn this.pipeline.getExclusiveSelection(EMAIL_DELIVER_HOOK) !== undefined;\n\t}\n}\n","/**\n * Plugin Routes v2\n *\n * Handles plugin API route invocation with:\n * - Input validation via Zod schemas\n * - Route context creation\n * - Error handling\n *\n */\n\nimport { PluginContextFactory, type PluginContextFactoryOptions } from \"./context.js\";\nimport { extractRequestMeta } from \"./request-meta.js\";\nimport type { ResolvedPlugin, RouteContext, PluginRoute } from \"./types.js\";\n\n/**\n * Route metadata (public flag) without the handler.\n * Used by the catch-all route to decide auth before dispatch.\n */\nexport interface RouteMeta {\n\tpublic: boolean;\n}\n\n/**\n * Result from a route invocation\n */\nexport interface RouteResult<T = unknown> {\n\tsuccess: boolean;\n\tdata?: T;\n\terror?: {\n\t\tcode: string;\n\t\tmessage: string;\n\t\tdetails?: unknown;\n\t};\n\tstatus: number;\n}\n\n/**\n * Route invocation options\n */\nexport interface InvokeRouteOptions {\n\t/** The original request */\n\trequest: Request;\n\t/** Request body (already parsed) */\n\tbody?: unknown;\n}\n\n/**\n * Route handler for a plugin\n */\nexport class PluginRouteHandler {\n\tprivate contextFactory: PluginContextFactory;\n\tprivate plugin: ResolvedPlugin;\n\tprivate trustedProxyHeaders: string[];\n\n\tconstructor(plugin: ResolvedPlugin, factoryOptions: PluginContextFactoryOptions) {\n\t\tthis.plugin = plugin;\n\t\tthis.contextFactory = new PluginContextFactory(factoryOptions);\n\t\tthis.trustedProxyHeaders = factoryOptions.trustedProxyHeaders ?? [];\n\t}\n\n\t/**\n\t * Invoke a route by name\n\t */\n\tasync invoke(routeName: string, options: InvokeRouteOptions): Promise<RouteResult> {\n\t\tconst route = this.plugin.routes[routeName];\n\n\t\tif (!route) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ROUTE_NOT_FOUND\",\n\t\t\t\t\tmessage: `Route \"${routeName}\" not found in plugin \"${this.plugin.id}\"`,\n\t\t\t\t},\n\t\t\t\tstatus: 404,\n\t\t\t};\n\t\t}\n\n\t\t// Validate input if schema is provided\n\t\tlet validatedInput: unknown;\n\t\tif (route.input) {\n\t\t\tconst parseResult = route.input.safeParse(options.body);\n\t\t\tif (!parseResult.success) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\t\t\tmessage: \"Invalid request body\",\n\t\t\t\t\t\tdetails: parseResult.error.format(),\n\t\t\t\t\t},\n\t\t\t\t\tstatus: 400,\n\t\t\t\t};\n\t\t\t}\n\t\t\tvalidatedInput = parseResult.data;\n\t\t} else {\n\t\t\tvalidatedInput = options.body;\n\t\t}\n\n\t\t// Create route context\n\t\tconst baseContext = this.contextFactory.createContext(this.plugin);\n\t\tconst routeContext: RouteContext = {\n\t\t\t...baseContext,\n\t\t\tinput: validatedInput,\n\t\t\trequest: options.request,\n\t\t\trequestMeta: extractRequestMeta(options.request, this.trustedProxyHeaders),\n\t\t};\n\n\t\t// Execute handler\n\t\ttry {\n\t\t\tconst result = await route.handler(routeContext);\n\t\t\treturn {\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: result,\n\t\t\t\tstatus: 200,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\t// Handle known error types\n\t\t\tif (error instanceof PluginRouteError) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: error.code,\n\t\t\t\t\t\tmessage: error.message,\n\t\t\t\t\t\tdetails: error.details,\n\t\t\t\t\t},\n\t\t\t\t\tstatus: error.status,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Unknown error -- log internally, return generic message\n\t\t\tconsole.error(`[plugin:${this.plugin.id}] Route handler failed:`, error);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INTERNAL_ERROR\",\n\t\t\t\t\tmessage: \"An internal error occurred\",\n\t\t\t\t},\n\t\t\t\tstatus: 500,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Get all route names\n\t */\n\tgetRouteNames(): string[] {\n\t\treturn Object.keys(this.plugin.routes);\n\t}\n\n\t/**\n\t * Check if a route exists\n\t */\n\thasRoute(name: string): boolean {\n\t\treturn name in this.plugin.routes;\n\t}\n\n\t/**\n\t * Get route metadata without invoking the handler.\n\t * Returns null if the route doesn't exist.\n\t */\n\tgetRouteMeta(name: string): RouteMeta | null {\n\t\tconst route: PluginRoute | undefined = this.plugin.routes[name];\n\t\tif (!route) return null;\n\t\treturn { public: route.public === true };\n\t}\n}\n\n/**\n * Error class for plugin routes\n * Allows plugins to return structured errors with specific HTTP status codes\n */\nexport class PluginRouteError extends Error {\n\tconstructor(\n\t\tpublic code: string,\n\t\tmessage: string,\n\t\tpublic status: number = 400,\n\t\tpublic details?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"PluginRouteError\";\n\t}\n\n\t/**\n\t * Create a bad request error (400)\n\t */\n\tstatic badRequest(message: string, details?: unknown): PluginRouteError {\n\t\treturn new PluginRouteError(\"BAD_REQUEST\", message, 400, details);\n\t}\n\n\t/**\n\t * Create an unauthorized error (401)\n\t */\n\tstatic unauthorized(message: string = \"Unauthorized\"): PluginRouteError {\n\t\treturn new PluginRouteError(\"UNAUTHORIZED\", message, 401);\n\t}\n\n\t/**\n\t * Create a forbidden error (403)\n\t */\n\tstatic forbidden(message: string = \"Forbidden\"): PluginRouteError {\n\t\treturn new PluginRouteError(\"FORBIDDEN\", message, 403);\n\t}\n\n\t/**\n\t * Create a not found error (404)\n\t */\n\tstatic notFound(message: string = \"Not found\"): PluginRouteError {\n\t\treturn new PluginRouteError(\"NOT_FOUND\", message, 404);\n\t}\n\n\t/**\n\t * Create a conflict error (409)\n\t */\n\tstatic conflict(message: string, details?: unknown): PluginRouteError {\n\t\treturn new PluginRouteError(\"CONFLICT\", message, 409, details);\n\t}\n\n\t/**\n\t * Create an internal error (500)\n\t */\n\tstatic internal(message: string = \"Internal error\"): PluginRouteError {\n\t\treturn new PluginRouteError(\"INTERNAL_ERROR\", message, 500);\n\t}\n}\n\n/**\n * Registry for all plugin route handlers\n */\nexport class PluginRouteRegistry {\n\tprivate handlers: Map<string, PluginRouteHandler> = new Map();\n\n\tconstructor(private factoryOptions: PluginContextFactoryOptions) {}\n\n\t/**\n\t * Register a plugin's routes\n\t */\n\tregister(plugin: ResolvedPlugin): void {\n\t\tconst handler = new PluginRouteHandler(plugin, this.factoryOptions);\n\t\tthis.handlers.set(plugin.id, handler);\n\t}\n\n\t/**\n\t * Unregister a plugin's routes\n\t */\n\tunregister(pluginId: string): void {\n\t\tthis.handlers.delete(pluginId);\n\t}\n\n\t/**\n\t * Invoke a plugin route\n\t */\n\tasync invoke(\n\t\tpluginId: string,\n\t\trouteName: string,\n\t\toptions: InvokeRouteOptions,\n\t): Promise<RouteResult> {\n\t\tconst handler = this.handlers.get(pluginId);\n\n\t\tif (!handler) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"PLUGIN_NOT_FOUND\",\n\t\t\t\t\tmessage: `Plugin \"${pluginId}\" not found`,\n\t\t\t\t},\n\t\t\t\tstatus: 404,\n\t\t\t};\n\t\t}\n\n\t\treturn handler.invoke(routeName, options);\n\t}\n\n\t/**\n\t * Get all registered plugin IDs\n\t */\n\tgetPluginIds(): string[] {\n\t\treturn [...this.handlers.keys()];\n\t}\n\n\t/**\n\t * Get routes for a plugin\n\t */\n\tgetRoutes(pluginId: string): string[] {\n\t\treturn this.handlers.get(pluginId)?.getRouteNames() ?? [];\n\t}\n\n\t/**\n\t * Get route metadata for a specific plugin route.\n\t * Returns null if the plugin or route doesn't exist.\n\t */\n\tgetRouteMeta(pluginId: string, routeName: string): RouteMeta | null {\n\t\tconst handler = this.handlers.get(pluginId);\n\t\tif (!handler) return null;\n\t\treturn handler.getRouteMeta(routeName);\n\t}\n}\n\n/**\n * Create a route registry\n */\nexport function createRouteRegistry(\n\tfactoryOptions: PluginContextFactoryOptions,\n): PluginRouteRegistry {\n\treturn new PluginRouteRegistry(factoryOptions);\n}\n","/**\n * Plugin Manager v2\n *\n * Central orchestrator for the plugin system:\n * - Loads and resolves plugins\n * - Manages plugin lifecycle (install, activate, deactivate, uninstall)\n * - Dispatches hooks across all plugins\n * - Routes API requests to plugins\n *\n */\n\nimport type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { OptionsRepository } from \"../database/repositories/options.js\";\nimport type { Database } from \"../database/types.js\";\nimport type { Storage } from \"../storage/types.js\";\nimport type { PluginContextFactoryOptions } from \"./context.js\";\nimport { setCronTasksEnabled } from \"./cron.js\";\nimport { definePlugin } from \"./define-plugin.js\";\nimport type { EmailPipeline } from \"./email.js\";\nimport {\n\tHookPipeline,\n\ttype HookResult,\n\tresolveExclusiveHooks as resolveExclusiveHooksShared,\n} from \"./hooks.js\";\nimport { PluginRouteRegistry, type RouteResult, type InvokeRouteOptions } from \"./routes.js\";\nimport type {\n\tPluginDefinition,\n\tResolvedPlugin,\n\tPluginStorageConfig,\n\tMediaItem,\n\tCronEvent,\n} from \"./types.js\";\n\n/** Options table key prefix for exclusive hook DB reads via PluginManager */\nconst EXCLUSIVE_HOOK_KEY_PREFIX = \"emdash:exclusive_hook:\";\n\n/**\n * Plugin state in the manager\n */\nexport type PluginState = \"registered\" | \"installed\" | \"active\" | \"inactive\";\n\n/**\n * Plugin entry in the manager\n */\ninterface PluginEntry {\n\tplugin: ResolvedPlugin;\n\tstate: PluginState;\n}\n\n/**\n * Plugin manager options\n */\nexport interface PluginManagerOptions {\n\t/** Database instance */\n\tdb: Kysely<Database>;\n\t/** Storage backend for direct media uploads */\n\tstorage?: Storage;\n\t/** Function to generate upload URLs for media */\n\tgetUploadUrl?: (\n\t\tfilename: string,\n\t\tcontentType: string,\n\t) => Promise<{ uploadUrl: string; mediaId: string }>;\n\t/**\n\t * Pre-resolved list of trusted proxy header names for client-IP\n\t * resolution in plugin route handlers. Thread through from the runtime.\n\t */\n\ttrustedProxyHeaders?: string[];\n}\n\n/**\n * Plugin Manager v2\n *\n * Manages the full lifecycle of plugins and coordinates hooks/routes.\n */\nexport class PluginManager {\n\tprivate plugins: Map<string, PluginEntry> = new Map();\n\tprivate hookPipeline: HookPipeline | null = null;\n\tprivate routeRegistry: PluginRouteRegistry | null = null;\n\tprivate factoryOptions: PluginContextFactoryOptions;\n\tprivate initialized = false;\n\n\tconstructor(private options: PluginManagerOptions) {\n\t\tthis.factoryOptions = {\n\t\t\tdb: options.db,\n\t\t\tstorage: options.storage,\n\t\t\tgetUploadUrl: options.getUploadUrl,\n\t\t\ttrustedProxyHeaders: options.trustedProxyHeaders,\n\t\t};\n\t}\n\n\t/**\n\t * Set the email pipeline used when creating plugin contexts.\n\t * Reinitializes routes/hooks if already initialized so ctx.email is available immediately.\n\t */\n\tsetEmailPipeline(pipeline: EmailPipeline): void {\n\t\tthis.factoryOptions.emailPipeline = pipeline;\n\t\tif (this.initialized) {\n\t\t\tthis.reinitialize();\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Plugin Registration\n\t// =========================================================================\n\n\t/**\n\t * Register a plugin definition\n\t * This resolves the definition and adds it to the manager, but doesn't install it\n\t */\n\tregister<TStorage extends PluginStorageConfig>(\n\t\tdefinition: PluginDefinition<TStorage>,\n\t): ResolvedPlugin<TStorage> {\n\t\tconst resolved = definePlugin(definition);\n\n\t\tif (this.plugins.has(resolved.id)) {\n\t\t\tthrow new Error(`Plugin \"${resolved.id}\" is already registered`);\n\t\t}\n\n\t\tthis.plugins.set(resolved.id, {\n\t\t\tplugin: resolved,\n\t\t\tstate: \"registered\",\n\t\t});\n\n\t\t// Mark as needing reinitialization\n\t\tthis.initialized = false;\n\n\t\treturn resolved;\n\t}\n\n\t/**\n\t * Register multiple plugins\n\t */\n\tregisterAll(definitions: PluginDefinition[]): void {\n\t\tfor (const def of definitions) {\n\t\t\tthis.register(def);\n\t\t}\n\t}\n\n\t/**\n\t * Unregister a plugin\n\t * Plugin must be inactive or just registered\n\t */\n\tunregister(pluginId: string): boolean {\n\t\tconst entry = this.plugins.get(pluginId);\n\t\tif (!entry) return false;\n\n\t\tif (entry.state === \"active\") {\n\t\t\tthrow new Error(`Cannot unregister active plugin \"${pluginId}\". Deactivate it first.`);\n\t\t}\n\n\t\tthis.plugins.delete(pluginId);\n\t\tthis.initialized = false;\n\t\treturn true;\n\t}\n\n\t// =========================================================================\n\t// Plugin Lifecycle\n\t// =========================================================================\n\n\t/**\n\t * Install a plugin (run install hooks, set up storage)\n\t */\n\tasync install(pluginId: string): Promise<HookResult<void>[]> {\n\t\tconst entry = this.plugins.get(pluginId);\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\n\t\tif (entry.state !== \"registered\") {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" is already installed (state: ${entry.state})`);\n\t\t}\n\n\t\tthis.ensureInitialized();\n\n\t\t// Run install hooks\n\t\tconst results = await this.hookPipeline!.runPluginInstall(pluginId);\n\n\t\t// Check for errors\n\t\tconst failed = results.find((r) => !r.success);\n\t\tif (failed) {\n\t\t\tthrow new Error(`Plugin install failed: ${failed.error?.message ?? \"Unknown error\"}`);\n\t\t}\n\n\t\tentry.state = \"installed\";\n\t\treturn results;\n\t}\n\n\t/**\n\t * Activate a plugin (run activate hooks, enable hooks/routes)\n\t */\n\tasync activate(pluginId: string): Promise<HookResult<void>[]> {\n\t\tconst entry = this.plugins.get(pluginId);\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\n\t\tif (entry.state === \"active\") {\n\t\t\treturn []; // Already active\n\t\t}\n\n\t\tif (entry.state === \"registered\") {\n\t\t\t// Auto-install if not installed\n\t\t\tawait this.install(pluginId);\n\t\t}\n\n\t\tthis.ensureInitialized();\n\n\t\t// Run activate hooks\n\t\tconst results = await this.hookPipeline!.runPluginActivate(pluginId);\n\n\t\t// Check for errors\n\t\tconst failed = results.find((r) => !r.success);\n\t\tif (failed) {\n\t\t\tthrow new Error(`Plugin activation failed: ${failed.error?.message ?? \"Unknown error\"}`);\n\t\t}\n\n\t\tentry.state = \"active\";\n\n\t\t// Re-enable cron tasks for the activated plugin\n\t\tawait setCronTasksEnabled(this.options.db, pluginId, true);\n\n\t\t// Reinitialize pipeline so the newly active plugin's hooks are registered\n\t\tthis.reinitialize();\n\n\t\t// Resolve exclusive hooks (new provider may need auto-selection)\n\t\tawait this.resolveExclusiveHooks();\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Deactivate a plugin (run deactivate hooks, disable hooks/routes)\n\t */\n\tasync deactivate(pluginId: string): Promise<HookResult<void>[]> {\n\t\tconst entry = this.plugins.get(pluginId);\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\n\t\tif (entry.state !== \"active\") {\n\t\t\treturn []; // Not active\n\t\t}\n\n\t\tthis.ensureInitialized();\n\n\t\t// Run deactivate hooks\n\t\tconst results = await this.hookPipeline!.runPluginDeactivate(pluginId);\n\n\t\t// Disable cron tasks for the deactivated plugin\n\t\tawait setCronTasksEnabled(this.options.db, pluginId, false);\n\n\t\tentry.state = \"inactive\";\n\n\t\t// Reinitialize pipeline so the deactivated plugin's hooks are removed\n\t\tthis.reinitialize();\n\n\t\t// Resolve exclusive hooks (deactivated provider may need clearing)\n\t\tawait this.resolveExclusiveHooks();\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Uninstall a plugin (run uninstall hooks, optionally delete data)\n\t */\n\tasync uninstall(pluginId: string, deleteData: boolean = false): Promise<HookResult<void>[]> {\n\t\tconst entry = this.plugins.get(pluginId);\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\n\t\t// Deactivate first if active (this also resolves exclusive hooks)\n\t\tif (entry.state === \"active\") {\n\t\t\tawait this.deactivate(pluginId);\n\t\t}\n\n\t\tthis.ensureInitialized();\n\n\t\t// Run uninstall hooks\n\t\tconst results = await this.hookPipeline!.runPluginUninstall(pluginId, deleteData);\n\n\t\t// Delete all cron tasks for the uninstalled plugin\n\t\tawait this.deleteCronTasks(pluginId);\n\n\t\t// Remove from manager\n\t\tthis.plugins.delete(pluginId);\n\t\tthis.initialized = false;\n\n\t\t// Resolve exclusive hooks after removal\n\t\tawait this.resolveExclusiveHooks();\n\n\t\treturn results;\n\t}\n\n\t// =========================================================================\n\t// Hook Dispatch\n\t// =========================================================================\n\n\t/**\n\t * Run content:beforeSave hooks across all active plugins\n\t */\n\tasync runContentBeforeSave(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t\tisNew: boolean,\n\t): Promise<{\n\t\tcontent: Record<string, unknown>;\n\t\tresults: HookResult<Record<string, unknown>>[];\n\t}> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runContentBeforeSave(content, collection, isNew);\n\t}\n\n\t/**\n\t * Run content:afterSave hooks across all active plugins\n\t */\n\tasync runContentAfterSave(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t\tisNew: boolean,\n\t): Promise<HookResult<void>[]> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runContentAfterSave(content, collection, isNew);\n\t}\n\n\t/**\n\t * Run content:beforeDelete hooks across all active plugins\n\t */\n\tasync runContentBeforeDelete(\n\t\tid: string,\n\t\tcollection: string,\n\t): Promise<{ allowed: boolean; results: HookResult<boolean>[] }> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runContentBeforeDelete(id, collection);\n\t}\n\n\t/**\n\t * Run content:afterDelete hooks across all active plugins\n\t */\n\tasync runContentAfterDelete(\n\t\tid: string,\n\t\tcollection: string,\n\t\tpermanent: boolean,\n\t): Promise<HookResult<void>[]> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runContentAfterDelete(id, collection, permanent);\n\t}\n\n\t/**\n\t * Run content:afterPublish hooks across all active plugins\n\t */\n\tasync runContentAfterPublish(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t): Promise<HookResult<void>[]> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runContentAfterPublish(content, collection);\n\t}\n\n\t/**\n\t * Run content:afterUnpublish hooks across all active plugins\n\t */\n\tasync runContentAfterUnpublish(\n\t\tcontent: Record<string, unknown>,\n\t\tcollection: string,\n\t): Promise<HookResult<void>[]> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runContentAfterUnpublish(content, collection);\n\t}\n\n\t/**\n\t * Run media:beforeUpload hooks across all active plugins\n\t */\n\tasync runMediaBeforeUpload(file: { name: string; type: string; size: number }): Promise<{\n\t\tfile: { name: string; type: string; size: number };\n\t\tresults: HookResult<{ name: string; type: string; size: number }>[];\n\t}> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runMediaBeforeUpload(file);\n\t}\n\n\t/**\n\t * Run media:afterUpload hooks across all active plugins\n\t */\n\tasync runMediaAfterUpload(media: MediaItem): Promise<HookResult<void>[]> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.runMediaAfterUpload(media);\n\t}\n\n\t/**\n\t * Invoke the cron hook for a specific plugin (per-plugin dispatch).\n\t * Used as the InvokeCronHookFn callback for CronExecutor.\n\t */\n\tasync invokeCronHook(pluginId: string, event: CronEvent): Promise<void> {\n\t\tthis.ensureInitialized();\n\t\tconst result = await this.hookPipeline!.invokeCronHook(pluginId, event);\n\t\tif (!result.success && result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t}\n\n\t// =========================================================================\n\t// Route Dispatch\n\t// =========================================================================\n\n\t/**\n\t * Invoke a plugin route\n\t */\n\tasync invokeRoute(\n\t\tpluginId: string,\n\t\trouteName: string,\n\t\toptions: InvokeRouteOptions,\n\t): Promise<RouteResult> {\n\t\tthis.ensureInitialized();\n\t\treturn this.routeRegistry!.invoke(pluginId, routeName, options);\n\t}\n\n\t/**\n\t * Get all routes for a plugin\n\t */\n\tgetPluginRoutes(pluginId: string): string[] {\n\t\tthis.ensureInitialized();\n\t\treturn this.routeRegistry!.getRoutes(pluginId);\n\t}\n\n\t// =========================================================================\n\t// Query Methods\n\t// =========================================================================\n\n\t/**\n\t * Get a plugin by ID\n\t */\n\tgetPlugin(pluginId: string): ResolvedPlugin | undefined {\n\t\treturn this.plugins.get(pluginId)?.plugin;\n\t}\n\n\t/**\n\t * Get plugin state\n\t */\n\tgetPluginState(pluginId: string): PluginState | undefined {\n\t\treturn this.plugins.get(pluginId)?.state;\n\t}\n\n\t/**\n\t * Get all registered plugins\n\t */\n\tgetAllPlugins(): Array<{ plugin: ResolvedPlugin; state: PluginState }> {\n\t\treturn Array.from(this.plugins.values(), (entry) => ({\n\t\t\tplugin: entry.plugin,\n\t\t\tstate: entry.state,\n\t\t}));\n\t}\n\n\t/**\n\t * Get all active plugins\n\t */\n\tgetActivePlugins(): ResolvedPlugin[] {\n\t\treturn [...this.plugins.values()]\n\t\t\t.filter((entry) => entry.state === \"active\")\n\t\t\t.map((entry) => entry.plugin);\n\t}\n\n\t/**\n\t * Check if a plugin exists\n\t */\n\thasPlugin(pluginId: string): boolean {\n\t\treturn this.plugins.has(pluginId);\n\t}\n\n\t/**\n\t * Check if a plugin is active\n\t */\n\tisActive(pluginId: string): boolean {\n\t\treturn this.plugins.get(pluginId)?.state === \"active\";\n\t}\n\n\t// =========================================================================\n\t// Exclusive Hooks\n\t// =========================================================================\n\n\t/**\n\t * Get all plugins that registered a handler for an exclusive hook.\n\t */\n\tgetExclusiveHookProviders(hookName: string): Array<{ pluginId: string; pluginName: string }> {\n\t\tthis.ensureInitialized();\n\t\treturn this.hookPipeline!.getExclusiveHookProviders(hookName).map((p) => {\n\t\t\tconst plugin = this.plugins.get(p.pluginId);\n\t\t\treturn {\n\t\t\t\tpluginId: p.pluginId,\n\t\t\t\tpluginName: plugin?.plugin.id ?? p.pluginId,\n\t\t\t};\n\t\t});\n\t}\n\n\t/**\n\t * Read the selected provider for an exclusive hook from the options table.\n\t */\n\tasync getExclusiveHookSelection(hookName: string): Promise<string | null> {\n\t\tconst optionsRepo = new OptionsRepository(this.options.db);\n\t\treturn optionsRepo.get<string>(`${EXCLUSIVE_HOOK_KEY_PREFIX}${hookName}`);\n\t}\n\n\t/**\n\t * Set the selected provider for an exclusive hook in the options table.\n\t * Pass null to clear the selection.\n\t */\n\tasync setExclusiveHookSelection(hookName: string, pluginId: string | null): Promise<void> {\n\t\tconst optionsRepo = new OptionsRepository(this.options.db);\n\t\tconst key = `${EXCLUSIVE_HOOK_KEY_PREFIX}${hookName}`;\n\n\t\tif (pluginId === null) {\n\t\t\tawait optionsRepo.delete(key);\n\t\t\tthis.hookPipeline?.clearExclusiveSelection(hookName);\n\t\t\treturn;\n\t\t}\n\n\t\t// Validate plugin exists and is active\n\t\tconst entry = this.plugins.get(pluginId);\n\t\tif (!entry) {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" not found`);\n\t\t}\n\t\tif (entry.state !== \"active\") {\n\t\t\tthrow new Error(`Plugin \"${pluginId}\" is not active`);\n\t\t}\n\n\t\tawait optionsRepo.set(key, pluginId);\n\t\tthis.hookPipeline?.setExclusiveSelection(hookName, pluginId);\n\t}\n\n\t/**\n\t * Resolution algorithm for exclusive hooks.\n\t *\n\t * Delegates to the shared resolveExclusiveHooks() function.\n\t * See hooks.ts for the full algorithm description.\n\t */\n\tasync resolveExclusiveHooks(preferredHints?: Map<string, string[]>): Promise<void> {\n\t\tthis.ensureInitialized();\n\n\t\tconst optionsRepo = new OptionsRepository(this.options.db);\n\n\t\tawait resolveExclusiveHooksShared({\n\t\t\tpipeline: this.hookPipeline!,\n\t\t\tisActive: (pluginId) => this.isActive(pluginId),\n\t\t\tgetOption: (key) => optionsRepo.get<string>(key),\n\t\t\tsetOption: (key, value) => optionsRepo.set(key, value),\n\t\t\tdeleteOption: async (key) => {\n\t\t\t\tawait optionsRepo.delete(key);\n\t\t\t},\n\t\t\tpreferredHints,\n\t\t});\n\t}\n\n\t/**\n\t * Get all exclusive hooks with their providers and current selections.\n\t * Used by the admin API.\n\t */\n\tasync getExclusiveHooksInfo(): Promise<\n\t\tArray<{\n\t\t\thookName: string;\n\t\t\tproviders: Array<{ pluginId: string }>;\n\t\t\tselectedPluginId: string | null;\n\t\t}>\n\t> {\n\t\tthis.ensureInitialized();\n\t\tconst exclusiveHookNames = this.hookPipeline!.getRegisteredExclusiveHooks();\n\t\tconst result = [];\n\n\t\tfor (const hookName of exclusiveHookNames) {\n\t\t\tconst providers = this.hookPipeline!.getExclusiveHookProviders(hookName);\n\t\t\tconst selection = await this.getExclusiveHookSelection(hookName);\n\t\t\tresult.push({\n\t\t\t\thookName,\n\t\t\t\tproviders,\n\t\t\t\tselectedPluginId: selection,\n\t\t\t});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// =========================================================================\n\t// Internal Methods\n\t// =========================================================================\n\n\t/**\n\t * Initialize or reinitialize the hook pipeline and route registry\n\t */\n\tprivate ensureInitialized(): void {\n\t\tif (this.initialized) return;\n\n\t\t// Get all active plugins for hooks\n\t\tconst activePlugins = this.getActivePlugins();\n\n\t\t// Create hook pipeline with active plugins\n\t\tthis.hookPipeline = new HookPipeline(activePlugins, this.factoryOptions);\n\n\t\t// Create route registry\n\t\tthis.routeRegistry = new PluginRouteRegistry(this.factoryOptions);\n\n\t\t// Register routes for active plugins\n\t\tfor (const plugin of activePlugins) {\n\t\t\tthis.routeRegistry.register(plugin);\n\t\t}\n\n\t\tthis.initialized = true;\n\t}\n\n\t/**\n\t * Force reinitialization (useful after plugin state changes)\n\t */\n\treinitialize(): void {\n\t\tthis.initialized = false;\n\t\tthis.ensureInitialized();\n\t}\n\n\t/**\n\t * Delete all cron tasks for a plugin.\n\t * Used during uninstall.\n\t */\n\tprivate async deleteCronTasks(pluginId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait sql`\n\t\t\t\tDELETE FROM _emdash_cron_tasks\n\t\t\t\tWHERE plugin_id = ${pluginId}\n\t\t\t`.execute(this.options.db);\n\t\t} catch {\n\t\t\t// Cron table may not exist yet (pre-migration). Non-fatal.\n\t\t}\n\t}\n}\n\n/**\n * Create a plugin manager\n */\nexport function createPluginManager(options: PluginManagerOptions): PluginManager {\n\treturn new PluginManager(options);\n}\n","/**\n * No-op Sandbox Runner\n *\n * Default implementation that doesn't support sandboxing.\n * Used on platforms without Worker Loader (Node.js, Deno, etc.).\n *\n */\n\nimport type { PluginManifest } from \"../types.js\";\nimport type { SandboxRunner, SandboxedPluginInstance, SandboxOptions } from \"./types.js\";\n\n/**\n * Error thrown when attempting to use sandboxing on an unsupported platform.\n */\nexport class SandboxNotAvailableError extends Error {\n\tconstructor() {\n\t\tsuper(\n\t\t\t\"Plugin sandboxing is not available on this platform. \" +\n\t\t\t\t\"Sandboxed plugins require Cloudflare Workers with Worker Loader. \" +\n\t\t\t\t\"Use trusted plugins (from config) instead, or deploy to Cloudflare.\",\n\t\t);\n\t\tthis.name = \"SandboxNotAvailableError\";\n\t}\n}\n\n/**\n * No-op sandbox runner for platforms without isolation support.\n *\n * - `isAvailable()` returns false\n * - `load()` throws SandboxNotAvailableError\n * - `terminateAll()` is a no-op\n *\n * This is the default runner when no platform adapter is configured.\n */\nexport class NoopSandboxRunner implements SandboxRunner {\n\t/**\n\t * Always returns false - sandboxing is not available.\n\t */\n\tisAvailable(): boolean {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Always throws - can't load sandboxed plugins without isolation.\n\t */\n\tasync load(\n\t\t// eslint-disable-next-line @typescript-eslint/no-unused-vars\n\t\t_manifest: PluginManifest,\n\t\t// eslint-disable-next-line @typescript-eslint/no-unused-vars\n\t\t_code: string,\n\t): Promise<SandboxedPluginInstance> {\n\t\tthrow new SandboxNotAvailableError();\n\t}\n\n\t/**\n\t * No-op - sandboxing not available, email callback is irrelevant.\n\t */\n\tsetEmailSend(): void {\n\t\t// Nothing to do\n\t}\n\n\t/**\n\t * No-op - nothing to terminate.\n\t */\n\tasync terminateAll(): Promise<void> {\n\t\t// Nothing to do\n\t}\n}\n\n/**\n * Create a no-op sandbox runner.\n * This is used as the default when no platform adapter is configured.\n */\nexport function createNoopSandboxRunner(_options?: SandboxOptions): SandboxRunner {\n\treturn new NoopSandboxRunner();\n}\n","/**\n * Comment query functions for Astro templates\n *\n * Same pattern as getMenu() — uses getDb() for ambient DB access.\n * These are called from .astro pages/components, not from API routes.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport { CommentRepository } from \"../database/repositories/comment.js\";\nimport type { PublicComment } from \"../database/repositories/comment.js\";\nimport type { Database } from \"../database/types.js\";\nimport { getDb } from \"../loader.js\";\n\nexport interface GetCommentsOptions {\n\tcollection: string;\n\tcontentId: string;\n\tthreaded?: boolean;\n}\n\nexport interface GetCommentsResult {\n\titems: PublicComment[];\n\ttotal: number;\n}\n\n/**\n * Get approved comments for a content item.\n *\n * @example\n * ```ts\n * import { getComments } from \"emdash\";\n *\n * const { items, total } = await getComments({\n * collection: \"posts\",\n * contentId: post.id,\n * threaded: true,\n * });\n * ```\n */\nexport async function getComments(options: GetCommentsOptions): Promise<GetCommentsResult> {\n\tconst db = await getDb();\n\treturn getCommentsWithDb(db, options);\n}\n\n/**\n * Get approved comments with an explicit db handle.\n *\n * @internal Use `getComments()` in templates. This variant is for routes\n * that already have a database handle.\n */\nexport async function getCommentsWithDb(\n\tdb: Kysely<Database>,\n\toptions: GetCommentsOptions,\n): Promise<GetCommentsResult> {\n\tconst repo = new CommentRepository(db);\n\n\tconst total = await repo.countByContent(options.collection, options.contentId, \"approved\");\n\n\t// Server-rendered: fetch all comments (capped for safety).\n\t// The API route handles paginated access; this is for full-page renders.\n\tconst MAX_COMMENTS = 500;\n\n\tconst result = await repo.findByContent(options.collection, options.contentId, {\n\t\tstatus: \"approved\",\n\t\tlimit: MAX_COMMENTS,\n\t});\n\n\tif (options.threaded) {\n\t\tconst threaded = CommentRepository.assembleThreads(result.items);\n\t\tconst items = threaded.map((c) => CommentRepository.toPublicComment(c));\n\t\treturn { items, total };\n\t}\n\n\tconst items = result.items.map((c) => CommentRepository.toPublicComment(c));\n\treturn { items, total };\n}\n\n/**\n * Get the count of approved comments for a content item.\n *\n * @example\n * ```ts\n * import { getCommentCount } from \"emdash\";\n *\n * const count = await getCommentCount(\"posts\", post.id);\n * ```\n */\nexport async function getCommentCount(collection: string, contentId: string): Promise<number> {\n\tconst db = await getDb();\n\treturn getCommentCountWithDb(db, collection, contentId);\n}\n\n/**\n * Get comment count with an explicit db handle.\n *\n * @internal Use `getCommentCount()` in templates.\n */\nexport async function getCommentCountWithDb(\n\tdb: Kysely<Database>,\n\tcollection: string,\n\tcontentId: string,\n): Promise<number> {\n\tconst repo = new CommentRepository(db);\n\treturn repo.countByContent(collection, contentId, \"approved\");\n}\n","/**\n * Navigation menu runtime functions.\n *\n * These are called from templates to query menus and resolve URLs. All queries\n * are locale-aware: when a locale is configured (or passed explicitly) items\n * are filtered to that locale, and menu item references resolve against the\n * referenced content's translation_group so the URL points at the right\n * per-locale row.\n */\n\nimport type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport type { Database } from \"../database/types.js\";\nimport { validateIdentifier } from \"../database/validate.js\";\nimport { resolveLocale, resolveLocaleChain } from \"../i18n/resolve.js\";\nimport { getDb } from \"../loader.js\";\nimport { requestCached } from \"../request-cache.js\";\nimport { sanitizeHref } from \"../utils/url.js\";\nimport type { Menu, MenuItem, MenuItemRow } from \"./types.js\";\n\nexport interface MenuQueryOptions {\n\t/** Override the locale used for the lookup. When omitted, the locale comes\n\t * from the request context or the configured defaultLocale. */\n\tlocale?: string;\n}\n\n/**\n * Get a menu by name with resolved URLs.\n *\n * @example\n * ```ts\n * const menu = await getMenu(\"primary\");\n * const menuEs = await getMenu(\"primary\", { locale: \"es\" });\n * ```\n */\nexport function getMenu(name: string, options: MenuQueryOptions = {}): Promise<Menu | null> {\n\tconst locale = resolveLocale(options.locale);\n\treturn requestCached(`menu:${name}:${locale ?? \"*\"}`, async () => {\n\t\tconst db = await getDb();\n\t\treturn getMenuWithDb(name, db, { locale });\n\t});\n}\n\n/**\n * Get menu by name with resolved URLs (with explicit db). Internal helper for\n * admin routes that already have a database handle.\n */\nexport async function getMenuWithDb(\n\tname: string,\n\tdb: Kysely<Database>,\n\toptions: MenuQueryOptions = {},\n): Promise<Menu | null> {\n\tconst chain = resolveLocaleChain(options.locale);\n\n\tconst selectMenu = () => db.selectFrom(\"_emdash_menus\").selectAll().where(\"name\", \"=\", name);\n\n\tlet menuRow: Awaited<ReturnType<ReturnType<typeof selectMenu>[\"executeTakeFirst\"]>>;\n\tif (chain.length === 0) {\n\t\tmenuRow = await selectMenu().orderBy(\"locale\", \"asc\").executeTakeFirst();\n\t} else {\n\t\tmenuRow = undefined;\n\t\tfor (const locale of chain) {\n\t\t\tmenuRow = await selectMenu().where(\"locale\", \"=\", locale).executeTakeFirst();\n\t\t\tif (menuRow) break;\n\t\t}\n\t}\n\n\tif (!menuRow) return null;\n\n\tconst itemRows = await db\n\t\t.selectFrom(\"_emdash_menu_items\")\n\t\t.selectAll()\n\t\t.$castTo<MenuItemRow>()\n\t\t.where(\"menu_id\", \"=\", menuRow.id)\n\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t.execute();\n\n\tconst items = await buildMenuTree(itemRows, db, menuRow.locale);\n\n\treturn {\n\t\tid: menuRow.id,\n\t\tname: menuRow.name,\n\t\tlabel: menuRow.label,\n\t\titems,\n\t\tlocale: menuRow.locale,\n\t\ttranslationGroup: menuRow.translation_group,\n\t};\n}\n\n/**\n * Get all menus (without items, locale-filtered — for admin list / site nav\n * summaries). When no locale is configured, returns menus across all locales.\n */\nexport async function getMenus(\n\toptions: MenuQueryOptions = {},\n): Promise<Array<{ id: string; name: string; label: string; locale: string }>> {\n\tconst db = await getDb();\n\treturn getMenusWithDb(db, options);\n}\n\n/**\n * Get all menus (with explicit db)\n *\n * @internal Use `getMenus()` in templates. This variant is for admin routes\n * that already have a database handle.\n */\nexport async function getMenusWithDb(\n\tdb: Kysely<Database>,\n\toptions: MenuQueryOptions = {},\n): Promise<Array<{ id: string; name: string; label: string; locale: string }>> {\n\tconst locale = resolveLocale(options.locale);\n\tlet query = db\n\t\t.selectFrom(\"_emdash_menus\")\n\t\t.select([\"id\", \"name\", \"label\", \"locale\"])\n\t\t.orderBy(\"name\", \"asc\");\n\tif (locale !== undefined) query = query.where(\"locale\", \"=\", locale);\n\treturn query.execute();\n}\n\n/**\n * Build a hierarchical menu tree from a flat list of items. Items are\n * resolved against the given `locale` so references land on the right\n * per-locale content rows.\n */\nasync function buildMenuTree(\n\titems: MenuItemRow[],\n\tdb: Kysely<Database>,\n\tlocale: string,\n): Promise<MenuItem[]> {\n\tconst collectionSlugs = new Set<string>();\n\tfor (const item of items) {\n\t\tif (item.reference_collection) collectionSlugs.add(item.reference_collection);\n\t\tif (item.type === \"page\" || item.type === \"post\") {\n\t\t\tcollectionSlugs.add(item.reference_collection || `${item.type}s`);\n\t\t}\n\t}\n\n\tconst urlPatterns = new Map<string, string | null>();\n\tif (collectionSlugs.size > 0) {\n\t\tconst rows = await db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.select([\"slug\", \"url_pattern\"])\n\t\t\t.where(\"slug\", \"in\", [...collectionSlugs])\n\t\t\t.execute();\n\t\tfor (const row of rows) urlPatterns.set(row.slug, row.url_pattern);\n\t}\n\n\tconst resolvedItems = await Promise.all(\n\t\titems.map((item) => resolveMenuItem(item, db, urlPatterns, locale)),\n\t);\n\tconst validItems = resolvedItems.filter((item): item is MenuItem => item !== null);\n\n\tconst itemMap = new Map<string, MenuItem & { children: MenuItem[] }>();\n\tconst rootItems: MenuItem[] = [];\n\n\tfor (const item of validItems) {\n\t\titemMap.set(item.id, { ...item, children: [] });\n\t}\n\n\tfor (const item of items) {\n\t\tconst menuItem = itemMap.get(item.id);\n\t\tif (!menuItem) continue;\n\t\tif (item.parent_id) {\n\t\t\tconst parent = itemMap.get(item.parent_id);\n\t\t\tif (parent) parent.children.push(menuItem);\n\t\t\telse rootItems.push(menuItem);\n\t\t} else {\n\t\t\trootItems.push(menuItem);\n\t\t}\n\t}\n\n\treturn rootItems;\n}\n\n/**\n * Resolve a single menu item's URL. `reference_id` is a translation_group\n * (migration 036 remapped all existing references); we join it against\n * the per-locale ec_* row or per-locale taxonomy row.\n */\nasync function resolveMenuItem(\n\titem: MenuItemRow,\n\tdb: Kysely<Database>,\n\turlPatterns: Map<string, string | null>,\n\tlocale: string,\n): Promise<MenuItem | null> {\n\tlet url: string | null;\n\n\ttry {\n\t\tswitch (item.type) {\n\t\t\tcase \"custom\":\n\t\t\t\turl = item.custom_url || \"#\";\n\t\t\t\tbreak;\n\n\t\t\tcase \"page\":\n\t\t\tcase \"post\":\n\t\t\t\turl = await resolveContentUrl(\n\t\t\t\t\titem.reference_collection || `${item.type}s`,\n\t\t\t\t\titem.reference_id,\n\t\t\t\t\tdb,\n\t\t\t\t\turlPatterns,\n\t\t\t\t\tlocale,\n\t\t\t\t);\n\t\t\t\tif (url === null) return null;\n\t\t\t\tbreak;\n\n\t\t\tcase \"taxonomy\":\n\t\t\t\turl = await resolveTaxonomyUrl(item.reference_id, db, locale);\n\t\t\t\tif (url === null) return null;\n\t\t\t\tbreak;\n\n\t\t\tcase \"collection\":\n\t\t\t\turl = `/${item.reference_collection}/`;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tif (item.reference_collection && item.reference_id) {\n\t\t\t\t\turl = await resolveContentUrl(\n\t\t\t\t\t\titem.reference_collection,\n\t\t\t\t\t\titem.reference_id,\n\t\t\t\t\t\tdb,\n\t\t\t\t\t\turlPatterns,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t);\n\t\t\t\t\tif (url === null) return null;\n\t\t\t\t} else {\n\t\t\t\t\turl = \"#\";\n\t\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(`Failed to resolve menu item ${item.id}:`, error);\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tid: item.id,\n\t\tlabel: item.label,\n\t\turl: sanitizeHref(url),\n\t\ttarget: item.target || undefined,\n\t\ttitleAttr: item.title_attr || undefined,\n\t\tcssClasses: item.css_classes || undefined,\n\t\tchildren: [],\n\t};\n}\n\nconst SLUG_PLACEHOLDER = /\\{slug\\}/g;\nconst ID_PLACEHOLDER = /\\{id\\}/g;\n\n/**\n * Interpolate a URL pattern with entry data\n *\n * Replaces `{slug}` and `{id}` placeholders.\n */\nfunction interpolateUrlPattern(pattern: string, slug: string, id: string): string {\n\treturn pattern.replace(SLUG_PLACEHOLDER, slug).replace(ID_PLACEHOLDER, id);\n}\n\n/**\n * Resolve the URL for a content reference. `referenceGroup` is the content\n * row's translation_group; we look up the row in the requested locale\n * (falling back to the source if no translation exists so the menu link is\n * still clickable).\n */\nasync function resolveContentUrl(\n\tcollection: string,\n\treferenceGroup: string | null,\n\tdb: Kysely<Database>,\n\turlPatterns: Map<string, string | null>,\n\tlocale: string,\n): Promise<string | null> {\n\tif (!referenceGroup) return null;\n\n\ttry {\n\t\tvalidateIdentifier(collection, \"menu item collection\");\n\n\t\t// Try the requested locale first, then any locale (deterministic).\n\t\tlet result = await sql<{ id: string; slug: string }>`\n\t\t\tSELECT id, slug FROM ${sql.ref(`ec_${collection}`)}\n\t\t\tWHERE translation_group = ${referenceGroup} AND locale = ${locale}\n\t\t\tLIMIT 1\n\t\t`.execute(db);\n\t\tlet row = result.rows[0];\n\t\tif (!row) {\n\t\t\tresult = await sql<{ id: string; slug: string }>`\n\t\t\t\tSELECT id, slug FROM ${sql.ref(`ec_${collection}`)}\n\t\t\t\tWHERE translation_group = ${referenceGroup}\n\t\t\t\tORDER BY locale ASC LIMIT 1\n\t\t\t`.execute(db);\n\t\t\trow = result.rows[0];\n\t\t}\n\t\tif (!row) {\n\t\t\t// Legacy rows whose reference_id still points at an id directly\n\t\t\t// (defensive — migration 036 normalised these, but a row inserted\n\t\t\t// between migrations could predate the remap).\n\t\t\tconst legacy = await sql<{ id: string; slug: string }>`\n\t\t\t\tSELECT id, slug FROM ${sql.ref(`ec_${collection}`)}\n\t\t\t\tWHERE id = ${referenceGroup} LIMIT 1\n\t\t\t`.execute(db);\n\t\t\trow = legacy.rows[0];\n\t\t}\n\t\tif (!row) return null;\n\n\t\tconst pattern = urlPatterns.get(collection);\n\t\tif (pattern) return interpolateUrlPattern(pattern, row.slug, row.id);\n\t\treturn `/${collection}/${row.slug}`;\n\t} catch (error) {\n\t\tconsole.error(`Failed to resolve content URL for ${collection}/${referenceGroup}:`, error);\n\t\treturn null;\n\t}\n}\n\n/**\n * Resolve URL for a taxonomy term reference. `referenceGroup` is the term's\n * translation_group; we pick the row in the active locale (or fall back).\n */\nasync function resolveTaxonomyUrl(\n\treferenceGroup: string | null,\n\tdb: Kysely<Database>,\n\tlocale: string,\n): Promise<string | null> {\n\tif (!referenceGroup) return null;\n\n\tlet taxonomy = await db\n\t\t.selectFrom(\"taxonomies\")\n\t\t.select([\"name\", \"slug\"])\n\t\t.where(\"translation_group\", \"=\", referenceGroup)\n\t\t.where(\"locale\", \"=\", locale)\n\t\t.executeTakeFirst();\n\n\tif (!taxonomy) {\n\t\ttaxonomy = await db\n\t\t\t.selectFrom(\"taxonomies\")\n\t\t\t.select([\"name\", \"slug\"])\n\t\t\t.where(\"translation_group\", \"=\", referenceGroup)\n\t\t\t.orderBy(\"locale\", \"asc\")\n\t\t\t.executeTakeFirst();\n\t}\n\n\tif (!taxonomy) {\n\t\t// Legacy: id-based reference that predates the migration remap.\n\t\ttaxonomy = await db\n\t\t\t.selectFrom(\"taxonomies\")\n\t\t\t.select([\"name\", \"slug\"])\n\t\t\t.where(\"id\", \"=\", referenceGroup)\n\t\t\t.executeTakeFirst();\n\t}\n\n\tif (!taxonomy) return null;\n\n\treturn `/${taxonomy.name}/${taxonomy.slug}`;\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ //#region src/media/mime.ts
2
+ function normalizeMime(mime) {
3
+ return mime.split(";")[0].trim().toLowerCase();
4
+ }
5
+ function matchesMimeAllowlist(mime, allowList) {
6
+ const normalized = normalizeMime(mime);
7
+ for (const entry of allowList) {
8
+ if (!entry || !entry.includes("/")) continue;
9
+ const normalizedEntry = normalizeMime(entry);
10
+ if (normalizedEntry.endsWith("/")) {
11
+ if (normalized.startsWith(normalizedEntry)) return true;
12
+ } else if (normalized === normalizedEntry) return true;
13
+ }
14
+ return false;
15
+ }
16
+ /**
17
+ * Extract the `allowedMimeTypes` list from a `_emdash_fields.validation` row
18
+ * (raw JSON string). Returns null when the value is missing, malformed, or the
19
+ * list is empty — callers treat that as "no field-specific constraint".
20
+ */
21
+ function parseAllowedMimeTypes(rawValidation) {
22
+ if (!rawValidation) return null;
23
+ try {
24
+ const parsed = JSON.parse(rawValidation);
25
+ if (typeof parsed !== "object" || parsed === null) return null;
26
+ const list = parsed.allowedMimeTypes;
27
+ if (!Array.isArray(list) || list.length === 0) return null;
28
+ return list.filter((entry) => typeof entry === "string");
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ //#endregion
35
+ export { normalizeMime as n, parseAllowedMimeTypes as r, matchesMimeAllowlist as t };
36
+ //# sourceMappingURL=mime-KV5TqkMN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime-KV5TqkMN.mjs","names":[],"sources":["../src/media/mime.ts"],"sourcesContent":["export function normalizeMime(mime: string): string {\n\treturn mime.split(\";\")[0]!.trim().toLowerCase();\n}\n\nexport function matchesMimeAllowlist(mime: string, allowList: readonly string[]): boolean {\n\tconst normalized = normalizeMime(mime);\n\tfor (const entry of allowList) {\n\t\tif (!entry || !entry.includes(\"/\")) continue;\n\t\tconst normalizedEntry = normalizeMime(entry);\n\t\tif (normalizedEntry.endsWith(\"/\")) {\n\t\t\tif (normalized.startsWith(normalizedEntry)) return true;\n\t\t} else if (normalized === normalizedEntry) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nexport const EXTENSION_TO_MIME: Readonly<Record<string, string>> = {\n\t\".pdf\": \"application/pdf\",\n\t\".png\": \"image/png\",\n\t\".jpg\": \"image/jpeg\",\n\t\".jpeg\": \"image/jpeg\",\n\t\".gif\": \"image/gif\",\n\t\".webp\": \"image/webp\",\n\t\".svg\": \"image/svg+xml\",\n\t\".mp3\": \"audio/mpeg\",\n\t\".wav\": \"audio/wav\",\n\t\".mp4\": \"video/mp4\",\n\t\".webm\": \"video/webm\",\n\t\".zip\": \"application/zip\",\n\t\".tar\": \"application/x-tar\",\n\t\".gz\": \"application/gzip\",\n\t\".csv\": \"text/csv\",\n\t\".doc\": \"application/msword\",\n\t\".docx\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n\t\".xls\": \"application/vnd.ms-excel\",\n\t\".xlsx\": \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n\t\".txt\": \"text/plain\",\n\t\".rtf\": \"application/rtf\",\n\t\".vtt\": \"text/vtt\",\n\t\".srt\": \"application/x-subrip\",\n\t\".woff\": \"font/woff\",\n\t\".woff2\": \"font/woff2\",\n};\n\nconst VALID_MIME_RE = /^[a-z0-9][a-z0-9!#$&^_+\\-.]*\\/[a-z0-9!#$&^_+\\-.]*$/i;\n\nexport function expandExtensionShorthand(entry: string): string | null {\n\tconst trimmed = entry.trim();\n\tif (!trimmed) return null;\n\tif (trimmed.includes(\"/\")) return VALID_MIME_RE.test(trimmed) ? trimmed : null;\n\tif (trimmed.startsWith(\".\")) {\n\t\treturn EXTENSION_TO_MIME[trimmed.toLowerCase()] ?? null;\n\t}\n\treturn null;\n}\n\n/**\n * Extract the `allowedMimeTypes` list from a `_emdash_fields.validation` row\n * (raw JSON string). Returns null when the value is missing, malformed, or the\n * list is empty — callers treat that as \"no field-specific constraint\".\n */\nexport function parseAllowedMimeTypes(rawValidation: string | null | undefined): string[] | null {\n\tif (!rawValidation) return null;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(rawValidation);\n\t\tif (typeof parsed !== \"object\" || parsed === null) return null;\n\t\tconst list = (parsed as { allowedMimeTypes?: unknown }).allowedMimeTypes;\n\t\tif (!Array.isArray(list) || list.length === 0) return null;\n\t\treturn list.filter((entry): entry is string => typeof entry === \"string\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n"],"mappings":";AAAA,SAAgB,cAAc,MAAsB;AACnD,QAAO,KAAK,MAAM,IAAI,CAAC,GAAI,MAAM,CAAC,aAAa;;AAGhD,SAAgB,qBAAqB,MAAc,WAAuC;CACzF,MAAM,aAAa,cAAc,KAAK;AACtC,MAAK,MAAM,SAAS,WAAW;AAC9B,MAAI,CAAC,SAAS,CAAC,MAAM,SAAS,IAAI,CAAE;EACpC,MAAM,kBAAkB,cAAc,MAAM;AAC5C,MAAI,gBAAgB,SAAS,IAAI,EAChC;OAAI,WAAW,WAAW,gBAAgB,CAAE,QAAO;aACzC,eAAe,gBACzB,QAAO;;AAGT,QAAO;;;;;;;AAgDR,SAAgB,sBAAsB,eAA2D;AAChG,KAAI,CAAC,cAAe,QAAO;AAC3B,KAAI;EACH,MAAM,SAAkB,KAAK,MAAM,cAAc;AACjD,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;EAC1D,MAAM,OAAQ,OAA0C;AACxD,MAAI,CAAC,MAAM,QAAQ,KAAK,IAAI,KAAK,WAAW,EAAG,QAAO;AACtD,SAAO,KAAK,QAAQ,UAA2B,OAAO,UAAU,SAAS;SAClE;AACP,SAAO"}
@@ -20,4 +20,4 @@ function getAuthMode(config) {
20
20
 
21
21
  //#endregion
22
22
  export { getAuthMode as t };
23
- //# sourceMappingURL=mode-YhqNVef_.mjs.map
23
+ //# sourceMappingURL=mode-DPRPvJYm.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mode-YhqNVef_.mjs","names":[],"sources":["../src/auth/mode.ts"],"sourcesContent":["/**\n * Auth Mode Detection\n *\n * Determines which authentication provider is active based on config.\n * Supports both passkey (default) and external auth providers via AuthDescriptor.\n */\n\nimport type { EmDashConfig } from \"../astro/integration/runtime.js\";\nimport type {\n\tAuthDescriptor,\n\tAuthProviderDescriptor,\n\tAuthRouteDescriptor,\n\tAuthResult,\n\tExternalAuthConfig,\n} from \"./types.js\";\n\nexport type {\n\tAuthDescriptor,\n\tAuthProviderDescriptor,\n\tAuthRouteDescriptor,\n\tAuthResult,\n\tExternalAuthConfig,\n};\n\n/**\n * Passkey auth mode (default)\n */\nexport interface PasskeyAuthMode {\n\ttype: \"passkey\";\n}\n\n/**\n * External auth provider mode (Cloudflare Access, etc.)\n */\nexport interface ExternalAuthMode {\n\ttype: \"external\";\n\t/** Provider type identifier (e.g., \"cloudflare-access\") */\n\tproviderType: string;\n\t/** Module to import for authentication */\n\tentrypoint: string;\n\t/** Provider-specific configuration */\n\tconfig: unknown;\n}\n\n/**\n * Union of all auth modes\n */\nexport type AuthMode = PasskeyAuthMode | ExternalAuthMode;\n\n/**\n * Extended config type with auth.\n *\n * This is the same as `EmDashConfig` with an optional `auth` field.\n * Kept for backwards compatibility — prefer `EmDashConfig` in new code\n * since `getAuthMode` now accepts `EmDashConfig` directly.\n */\nexport interface EmDashConfigWithAuth extends EmDashConfig {\n\tauth?: AuthDescriptor;\n}\n\n/**\n * Determine the active auth mode from config.\n *\n * Accepts `EmDashConfig` (or subtype) — checks for `auth` field via duck typing.\n *\n * @param config EmDash configuration\n * @returns The active auth mode\n */\nexport function getAuthMode(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): AuthMode {\n\tconst auth = config?.auth;\n\n\t// Check for AuthDescriptor (transparent external auth like Cloudflare Access)\n\tif (auth && \"entrypoint\" in auth && auth.entrypoint) {\n\t\treturn {\n\t\t\ttype: \"external\",\n\t\t\tproviderType: auth.type,\n\t\t\tentrypoint: auth.entrypoint,\n\t\t\tconfig: auth.config,\n\t\t};\n\t}\n\n\t// Default to passkey\n\treturn { type: \"passkey\" };\n}\n\n/**\n * Check if an external auth provider is active\n */\nexport function isExternalAuthEnabled(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): boolean {\n\treturn getAuthMode(config).type === \"external\";\n}\n\n/**\n * Get external auth config if enabled\n */\nexport function getExternalAuthConfig(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): ExternalAuthMode | null {\n\tconst mode = getAuthMode(config);\n\tif (mode.type === \"external\") {\n\t\treturn mode;\n\t}\n\treturn null;\n}\n"],"mappings":";;;;;;;;;AAoEA,SAAgB,YACf,QACW;CACX,MAAM,OAAO,QAAQ;AAGrB,KAAI,QAAQ,gBAAgB,QAAQ,KAAK,WACxC,QAAO;EACN,MAAM;EACN,cAAc,KAAK;EACnB,YAAY,KAAK;EACjB,QAAQ,KAAK;EACb;AAIF,QAAO,EAAE,MAAM,WAAW"}
1
+ {"version":3,"file":"mode-DPRPvJYm.mjs","names":[],"sources":["../src/auth/mode.ts"],"sourcesContent":["/**\n * Auth Mode Detection\n *\n * Determines which authentication provider is active based on config.\n * Supports both passkey (default) and external auth providers via AuthDescriptor.\n */\n\nimport type { EmDashConfig } from \"../astro/integration/runtime.js\";\nimport type {\n\tAuthDescriptor,\n\tAuthProviderDescriptor,\n\tAuthRouteDescriptor,\n\tAuthResult,\n\tExternalAuthConfig,\n} from \"./types.js\";\n\nexport type {\n\tAuthDescriptor,\n\tAuthProviderDescriptor,\n\tAuthRouteDescriptor,\n\tAuthResult,\n\tExternalAuthConfig,\n};\n\n/**\n * Passkey auth mode (default)\n */\nexport interface PasskeyAuthMode {\n\ttype: \"passkey\";\n}\n\n/**\n * External auth provider mode (Cloudflare Access, etc.)\n */\nexport interface ExternalAuthMode {\n\ttype: \"external\";\n\t/** Provider type identifier (e.g., \"cloudflare-access\") */\n\tproviderType: string;\n\t/** Module to import for authentication */\n\tentrypoint: string;\n\t/** Provider-specific configuration */\n\tconfig: unknown;\n}\n\n/**\n * Union of all auth modes\n */\nexport type AuthMode = PasskeyAuthMode | ExternalAuthMode;\n\n/**\n * Extended config type with auth.\n *\n * This is the same as `EmDashConfig` with an optional `auth` field.\n * Kept for backwards compatibility — prefer `EmDashConfig` in new code\n * since `getAuthMode` now accepts `EmDashConfig` directly.\n */\nexport interface EmDashConfigWithAuth extends EmDashConfig {\n\tauth?: AuthDescriptor;\n}\n\n/**\n * Determine the active auth mode from config.\n *\n * Accepts `EmDashConfig` (or subtype) — checks for `auth` field via duck typing.\n *\n * @param config EmDash configuration\n * @returns The active auth mode\n */\nexport function getAuthMode(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): AuthMode {\n\tconst auth = config?.auth;\n\n\t// Check for AuthDescriptor (transparent external auth like Cloudflare Access)\n\tif (auth && \"entrypoint\" in auth && auth.entrypoint) {\n\t\treturn {\n\t\t\ttype: \"external\",\n\t\t\tproviderType: auth.type,\n\t\t\tentrypoint: auth.entrypoint,\n\t\t\tconfig: auth.config,\n\t\t};\n\t}\n\n\t// Default to passkey\n\treturn { type: \"passkey\" };\n}\n\n/**\n * Check if an external auth provider is active\n */\nexport function isExternalAuthEnabled(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): boolean {\n\treturn getAuthMode(config).type === \"external\";\n}\n\n/**\n * Get external auth config if enabled\n */\nexport function getExternalAuthConfig(\n\tconfig: (EmDashConfig & { auth?: AuthDescriptor }) | null | undefined,\n): ExternalAuthMode | null {\n\tconst mode = getAuthMode(config);\n\tif (mode.type === \"external\") {\n\t\treturn mode;\n\t}\n\treturn null;\n}\n"],"mappings":";;;;;;;;;AAoEA,SAAgB,YACf,QACW;CACX,MAAM,OAAO,QAAQ;AAGrB,KAAI,QAAQ,gBAAgB,QAAQ,KAAK,WACxC,QAAO;EACN,MAAM;EACN,cAAc,KAAK;EACnB,YAAY,KAAK;EACjB,QAAQ,KAAK;EACb;AAIF,QAAO,EAAE,MAAM,WAAW"}