emdash 0.32.0 → 0.34.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 (946) hide show
  1. package/dist/{adapters-C0ONzkUP.d.mts → adapters-34EN2eXO.d.mts} +34 -2
  2. package/dist/adapters-34EN2eXO.d.mts.map +1 -0
  3. package/dist/after-CBU2vkoO.mjs +96 -0
  4. package/dist/after-CBU2vkoO.mjs.map +1 -0
  5. package/dist/{allowed-origins-CCEi9bPI.mjs → allowed-origins-DW7sYyqg.mjs} +2 -2
  6. package/dist/{allowed-origins-CCEi9bPI.mjs.map → allowed-origins-DW7sYyqg.mjs.map} +1 -1
  7. package/dist/api/route-utils.d.mts +3 -4
  8. package/dist/api/route-utils.d.mts.map +1 -1
  9. package/dist/api/route-utils.mjs +26 -25
  10. package/dist/api/route-utils.mjs.map +1 -1
  11. package/dist/api/schemas/index.d.mts +2 -2
  12. package/dist/api/schemas/index.mjs +6 -5
  13. package/dist/{api-DgPbMlng.mjs → api-DdAsirBe.mjs} +321 -428
  14. package/dist/api-DdAsirBe.mjs.map +1 -0
  15. package/dist/{api-tokens-p-iMzvXR.mjs → api-tokens-CFXu8F_2.mjs} +2 -2
  16. package/dist/{api-tokens-p-iMzvXR.mjs.map → api-tokens-CFXu8F_2.mjs.map} +1 -1
  17. package/dist/{apply-1_6ra7NP.mjs → apply-D5zfeD09.mjs} +139 -102
  18. package/dist/apply-D5zfeD09.mjs.map +1 -0
  19. package/dist/astro/index.d.mts +12 -12
  20. package/dist/astro/index.d.mts.map +1 -1
  21. package/dist/astro/index.mjs +103 -35
  22. package/dist/astro/index.mjs.map +1 -1
  23. package/dist/astro/middleware/auth.d.mts +10 -10
  24. package/dist/astro/middleware/auth.mjs +9 -9
  25. package/dist/astro/middleware/media-usage-write-fence.d.mts +7 -0
  26. package/dist/astro/middleware/media-usage-write-fence.d.mts.map +1 -0
  27. package/dist/astro/middleware/media-usage-write-fence.mjs +35 -0
  28. package/dist/astro/middleware/media-usage-write-fence.mjs.map +1 -0
  29. package/dist/astro/middleware/redirect.d.mts.map +1 -1
  30. package/dist/astro/middleware/redirect.mjs +13 -10
  31. package/dist/astro/middleware/redirect.mjs.map +1 -1
  32. package/dist/astro/middleware/request-context.mjs +5 -5
  33. package/dist/astro/middleware/setup.mjs +1 -1
  34. package/dist/astro/middleware.d.mts +12 -11
  35. package/dist/astro/middleware.d.mts.map +1 -1
  36. package/dist/astro/middleware.mjs +1915 -223
  37. package/dist/astro/middleware.mjs.map +1 -1
  38. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +9 -8
  39. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs.map +1 -1
  40. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +9 -8
  41. package/dist/astro/routes/api/admin/allowed-domains/index.mjs.map +1 -1
  42. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
  43. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  44. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -6
  45. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +12 -11
  46. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -1
  47. package/dist/astro/routes/api/admin/byline-fields/index.mjs +12 -11
  48. package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -1
  49. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +12 -11
  50. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -1
  51. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +20 -20
  52. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +20 -20
  53. package/dist/astro/routes/api/admin/bylines/index.mjs +20 -20
  54. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +18 -17
  55. package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
  56. package/dist/astro/routes/api/admin/comments/_id_.mjs +8 -8
  57. package/dist/astro/routes/api/admin/comments/bulk.mjs +14 -13
  58. package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -1
  59. package/dist/astro/routes/api/admin/comments/counts.mjs +8 -8
  60. package/dist/astro/routes/api/admin/comments/index.mjs +14 -13
  61. package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
  62. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
  63. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
  64. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.d.mts +8 -0
  65. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.d.mts.map +1 -0
  66. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +36 -0
  67. package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -0
  68. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.d.mts +8 -0
  69. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.d.mts.map +1 -0
  70. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +36 -0
  71. package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -0
  72. package/dist/astro/routes/api/admin/media-usage/repair.mjs +15 -14
  73. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/media-usage/work/index.d.mts +8 -0
  75. package/dist/astro/routes/api/admin/media-usage/work/index.d.mts.map +1 -0
  76. package/dist/astro/routes/api/admin/media-usage/work/index.mjs +37 -0
  77. package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -0
  78. package/dist/astro/routes/api/admin/media-usage/work/retry.d.mts +8 -0
  79. package/dist/astro/routes/api/admin/media-usage/work/retry.d.mts.map +1 -0
  80. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +37 -0
  81. package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -0
  82. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
  83. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
  84. package/dist/astro/routes/api/admin/plugins/_id_/disable.d.mts.map +1 -1
  85. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -47
  86. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  87. package/dist/astro/routes/api/admin/plugins/_id_/enable.d.mts.map +1 -1
  88. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -47
  89. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  90. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +52 -46
  91. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  92. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +6 -6
  93. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +52 -46
  94. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  95. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.d.mts.map +1 -1
  96. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +55 -46
  97. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  98. package/dist/astro/routes/api/admin/plugins/_id_/update.d.mts.map +1 -1
  99. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +55 -46
  100. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  101. package/dist/astro/routes/api/admin/plugins/index.mjs +52 -46
  102. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  103. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
  104. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +52 -46
  105. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  106. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.d.mts.map +1 -1
  107. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +55 -46
  108. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  109. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +52 -46
  110. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  111. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.d.mts.map +1 -1
  112. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +55 -46
  113. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  114. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
  115. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +56 -47
  116. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  117. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +52 -46
  118. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  119. package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
  120. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +56 -47
  121. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  122. package/dist/astro/routes/api/admin/plugins/updates.mjs +52 -46
  123. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  124. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +52 -46
  125. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  126. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
  127. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +52 -46
  128. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  129. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
  130. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -3
  131. package/dist/astro/routes/api/admin/users/_id_/index.mjs +10 -9
  132. package/dist/astro/routes/api/admin/users/_id_/index.mjs.map +1 -1
  133. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +6 -6
  134. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs.map +1 -1
  135. package/dist/astro/routes/api/admin/users/index.mjs +9 -8
  136. package/dist/astro/routes/api/admin/users/index.mjs.map +1 -1
  137. package/dist/astro/routes/api/auth/dev-bypass.mjs +8 -8
  138. package/dist/astro/routes/api/auth/invite/accept.mjs +3 -3
  139. package/dist/astro/routes/api/auth/invite/complete.mjs +14 -13
  140. package/dist/astro/routes/api/auth/invite/complete.mjs.map +1 -1
  141. package/dist/astro/routes/api/auth/invite/index.mjs +12 -11
  142. package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
  143. package/dist/astro/routes/api/auth/invite/register-options.mjs +13 -12
  144. package/dist/astro/routes/api/auth/invite/register-options.mjs.map +1 -1
  145. package/dist/astro/routes/api/auth/logout.mjs +4 -4
  146. package/dist/astro/routes/api/auth/magic-link/send.mjs +14 -13
  147. package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
  148. package/dist/astro/routes/api/auth/magic-link/verify.mjs +4 -4
  149. package/dist/astro/routes/api/auth/me.mjs +10 -9
  150. package/dist/astro/routes/api/auth/me.mjs.map +1 -1
  151. package/dist/astro/routes/api/auth/mode.mjs +4 -4
  152. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
  153. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  154. package/dist/astro/routes/api/auth/passkey/_id_.mjs +9 -8
  155. package/dist/astro/routes/api/auth/passkey/_id_.mjs.map +1 -1
  156. package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
  157. package/dist/astro/routes/api/auth/passkey/options.mjs +15 -14
  158. package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -1
  159. package/dist/astro/routes/api/auth/passkey/register/options.mjs +13 -12
  160. package/dist/astro/routes/api/auth/passkey/register/options.mjs.map +1 -1
  161. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +14 -13
  162. package/dist/astro/routes/api/auth/passkey/register/verify.mjs.map +1 -1
  163. package/dist/astro/routes/api/auth/passkey/verify.mjs +14 -13
  164. package/dist/astro/routes/api/auth/passkey/verify.mjs.map +1 -1
  165. package/dist/astro/routes/api/auth/signup/complete.mjs +14 -13
  166. package/dist/astro/routes/api/auth/signup/complete.mjs.map +1 -1
  167. package/dist/astro/routes/api/auth/signup/request.mjs +14 -13
  168. package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
  169. package/dist/astro/routes/api/auth/signup/verify.mjs +3 -3
  170. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +20 -19
  171. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
  172. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +17 -16
  173. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs.map +1 -1
  174. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -4
  175. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +5 -5
  176. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +5 -5
  177. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -4
  178. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +15 -15
  179. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +11 -10
  180. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
  181. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +21 -20
  182. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  183. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +21 -20
  184. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  185. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +5 -5
  186. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -4
  187. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +11 -10
  188. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
  189. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts.map +1 -1
  190. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +47 -34
  191. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
  192. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -4
  193. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +5 -5
  194. package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -10
  195. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
  196. package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -4
  197. package/dist/astro/routes/api/content/_collection_/index.mjs +11 -10
  198. package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -1
  199. package/dist/astro/routes/api/content/_collection_/trash.mjs +10 -9
  200. package/dist/astro/routes/api/content/_collection_/trash.mjs.map +1 -1
  201. package/dist/astro/routes/api/dashboard.mjs +12 -10
  202. package/dist/astro/routes/api/dashboard.mjs.map +1 -1
  203. package/dist/astro/routes/api/dev/emails.mjs +4 -4
  204. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  205. package/dist/astro/routes/api/import/probe.mjs +22 -20
  206. package/dist/astro/routes/api/import/probe.mjs.map +1 -1
  207. package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -5
  208. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
  209. package/dist/astro/routes/api/import/wordpress/execute.mjs +18 -18
  210. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  211. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
  212. package/dist/astro/routes/api/import/wordpress/media.mjs +14 -12
  213. package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
  214. package/dist/astro/routes/api/import/wordpress/prepare.mjs +13 -12
  215. package/dist/astro/routes/api/import/wordpress/prepare.mjs.map +1 -1
  216. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
  217. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +15 -15
  218. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  219. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +22 -20
  220. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
  221. package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
  222. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
  223. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +32 -31
  224. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  225. package/dist/astro/routes/api/manifest.mjs +6 -6
  226. package/dist/astro/routes/api/mcp.d.mts.map +1 -1
  227. package/dist/astro/routes/api/mcp.mjs +174 -72
  228. package/dist/astro/routes/api/mcp.mjs.map +1 -1
  229. package/dist/astro/routes/api/media/_id_/confirm.mjs +14 -12
  230. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
  231. package/dist/astro/routes/api/media/_id_/upload.mjs +7 -7
  232. package/dist/astro/routes/api/media/_id_/usage.mjs +17 -16
  233. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
  234. package/dist/astro/routes/api/media/_id_.mjs +16 -15
  235. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  236. package/dist/astro/routes/api/media/file/_...key_.mjs +3 -3
  237. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
  238. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
  239. package/dist/astro/routes/api/media/providers/index.mjs +4 -4
  240. package/dist/astro/routes/api/media/upload-url.mjs +11 -10
  241. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  242. package/dist/astro/routes/api/media.mjs +19 -16
  243. package/dist/astro/routes/api/media.mjs.map +1 -1
  244. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +13 -13
  245. package/dist/astro/routes/api/menus/_name_/items.mjs +13 -13
  246. package/dist/astro/routes/api/menus/_name_/reorder.mjs +13 -13
  247. package/dist/astro/routes/api/menus/_name_/translations.mjs +13 -13
  248. package/dist/astro/routes/api/menus/_name_.mjs +13 -13
  249. package/dist/astro/routes/api/menus/index.mjs +13 -13
  250. package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
  251. package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
  252. package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
  253. package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
  254. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  255. package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
  256. package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
  257. package/dist/astro/routes/api/oauth/token.mjs +7 -7
  258. package/dist/astro/routes/api/openapi.json.mjs +183 -6
  259. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  260. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts.map +1 -1
  261. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +8 -7
  262. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs.map +1 -1
  263. package/dist/astro/routes/api/redirects/404s/index.mjs +14 -13
  264. package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
  265. package/dist/astro/routes/api/redirects/404s/summary.mjs +14 -13
  266. package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
  267. package/dist/astro/routes/api/redirects/_id_.mjs +16 -14
  268. package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
  269. package/dist/astro/routes/api/redirects/index.mjs +16 -14
  270. package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
  271. package/dist/astro/routes/api/relations/_id_/index.mjs +21 -20
  272. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  273. package/dist/astro/routes/api/relations/_id_/translations.mjs +16 -15
  274. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
  275. package/dist/astro/routes/api/relations/index.mjs +21 -20
  276. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  277. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
  278. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +5 -5
  279. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +52 -46
  280. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  281. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +52 -46
  282. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  283. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +52 -46
  284. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  285. package/dist/astro/routes/api/schema/collections/_slug_/index.d.mts.map +1 -1
  286. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +52 -46
  287. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  288. package/dist/astro/routes/api/schema/collections/index.mjs +52 -46
  289. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  290. package/dist/astro/routes/api/schema/collections/reorder.d.mts +8 -0
  291. package/dist/astro/routes/api/schema/collections/reorder.d.mts.map +1 -0
  292. package/dist/astro/routes/api/schema/collections/reorder.mjs +71 -0
  293. package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -0
  294. package/dist/astro/routes/api/schema/index.mjs +14 -11
  295. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  296. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +52 -46
  297. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  298. package/dist/astro/routes/api/schema/orphans/index.mjs +52 -46
  299. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  300. package/dist/astro/routes/api/search/enable.d.mts +1 -0
  301. package/dist/astro/routes/api/search/enable.d.mts.map +1 -1
  302. package/dist/astro/routes/api/search/enable.mjs +19 -15
  303. package/dist/astro/routes/api/search/enable.mjs.map +1 -1
  304. package/dist/astro/routes/api/search/index.mjs +13 -13
  305. package/dist/astro/routes/api/search/rebuild.mjs +15 -15
  306. package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
  307. package/dist/astro/routes/api/search/stats.mjs +9 -9
  308. package/dist/astro/routes/api/search/suggest.mjs +13 -13
  309. package/dist/astro/routes/api/sections/_slug_.mjs +13 -12
  310. package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -1
  311. package/dist/astro/routes/api/sections/index.mjs +13 -12
  312. package/dist/astro/routes/api/sections/index.mjs.map +1 -1
  313. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +8 -8
  314. package/dist/astro/routes/api/settings/backups/archives/index.mjs +7 -7
  315. package/dist/astro/routes/api/settings/backups/export.mjs +8 -8
  316. package/dist/astro/routes/api/settings/backups/index.mjs +9 -9
  317. package/dist/astro/routes/api/settings/email.mjs +6 -6
  318. package/dist/astro/routes/api/settings.mjs +18 -17
  319. package/dist/astro/routes/api/settings.mjs.map +1 -1
  320. package/dist/astro/routes/api/setup/admin-verify.mjs +15 -14
  321. package/dist/astro/routes/api/setup/admin-verify.mjs.map +1 -1
  322. package/dist/astro/routes/api/setup/admin.mjs +14 -13
  323. package/dist/astro/routes/api/setup/admin.mjs.map +1 -1
  324. package/dist/astro/routes/api/setup/dev-bypass.mjs +34 -32
  325. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  326. package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
  327. package/dist/astro/routes/api/setup/index.mjs +36 -34
  328. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  329. package/dist/astro/routes/api/setup/status.mjs +5 -5
  330. package/dist/astro/routes/api/snapshot.mjs +10 -10
  331. package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts +8 -0
  332. package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -0
  333. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +44 -0
  334. package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -0
  335. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +19 -18
  336. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  337. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +19 -18
  338. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  339. package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
  340. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +21 -19
  341. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  342. package/dist/astro/routes/api/taxonomies/_name_/translations.d.mts +8 -0
  343. package/dist/astro/routes/api/taxonomies/_name_/translations.d.mts.map +1 -0
  344. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +46 -0
  345. package/dist/astro/routes/api/taxonomies/_name_/translations.mjs.map +1 -0
  346. package/dist/astro/routes/api/taxonomies/_name_.d.mts +19 -0
  347. package/dist/astro/routes/api/taxonomies/_name_.d.mts.map +1 -0
  348. package/dist/astro/routes/api/taxonomies/_name_.mjs +88 -0
  349. package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -0
  350. package/dist/astro/routes/api/taxonomies/index.mjs +19 -18
  351. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  352. package/dist/astro/routes/api/themes/preview.mjs +8 -8
  353. package/dist/astro/routes/api/typegen.mjs +6 -6
  354. package/dist/astro/routes/api/well-known/auth.mjs +2 -2
  355. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
  356. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
  357. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +10 -9
  358. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
  359. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +13 -12
  360. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
  361. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +13 -12
  362. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
  363. package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -7
  364. package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
  365. package/dist/astro/routes/api/widget-areas/index.mjs +13 -12
  366. package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
  367. package/dist/astro/routes/api/widget-components.mjs +4 -4
  368. package/dist/astro/routes/robots.txt.mjs +11 -10
  369. package/dist/astro/routes/robots.txt.mjs.map +1 -1
  370. package/dist/astro/routes/sitemap-_collection_.xml.mjs +14 -14
  371. package/dist/astro/routes/sitemap.xml.mjs +12 -11
  372. package/dist/astro/routes/sitemap.xml.mjs.map +1 -1
  373. package/dist/astro/types.d.mts +41 -17
  374. package/dist/astro/types.d.mts.map +1 -1
  375. package/dist/auth/providers/github.d.mts +1 -1
  376. package/dist/auth/providers/google.d.mts +1 -1
  377. package/dist/{authorize-R5iYYrpa.mjs → authorize-1iFJzPaZ.mjs} +2 -2
  378. package/dist/{authorize-R5iYYrpa.mjs.map → authorize-1iFJzPaZ.mjs.map} +1 -1
  379. package/dist/{backup-LpQ1V_sR.mjs → backup-LHjIAVNP.mjs} +5 -5
  380. package/dist/{backup-LpQ1V_sR.mjs.map → backup-LHjIAVNP.mjs.map} +1 -1
  381. package/dist/{base64-B-PsqheR.mjs → base64-CvLhUKQv.mjs} +1 -1
  382. package/dist/{base64-B-PsqheR.mjs.map → base64-CvLhUKQv.mjs.map} +1 -1
  383. package/dist/bundle-CBEXrBnb.mjs +663 -0
  384. package/dist/bundle-CBEXrBnb.mjs.map +1 -0
  385. package/dist/{byline-fields-T9ZLZMxJ.mjs → byline-fields-C5S8wygO.mjs} +2 -2
  386. package/dist/{byline-fields-T9ZLZMxJ.mjs.map → byline-fields-C5S8wygO.mjs.map} +1 -1
  387. package/dist/{byline-C5TAqs8N.mjs → byline-pEcoDps8.mjs} +9 -9
  388. package/dist/{byline-C5TAqs8N.mjs.map → byline-pEcoDps8.mjs.map} +1 -1
  389. package/dist/{byline-registry-BCuOp4UF.mjs → byline-registry-DDun8sxh.mjs} +4 -4
  390. package/dist/{byline-registry-BCuOp4UF.mjs.map → byline-registry-DDun8sxh.mjs.map} +1 -1
  391. package/dist/{bylines-B8-WGdja.mjs → bylines-BWemdX2k.mjs} +8 -8
  392. package/dist/{bylines-B8-WGdja.mjs.map → bylines-BWemdX2k.mjs.map} +1 -1
  393. package/dist/{bylines-DGuqBLjV.mjs → bylines-DJfSfXCL.mjs} +5 -5
  394. package/dist/{bylines-DGuqBLjV.mjs.map → bylines-DJfSfXCL.mjs.map} +1 -1
  395. package/dist/cache-CBJaDy_E.mjs +108 -0
  396. package/dist/cache-CBJaDy_E.mjs.map +1 -0
  397. package/dist/{challenge-store-BFzgFRog.mjs → challenge-store-CZyFVaad.mjs} +1 -1
  398. package/dist/{challenge-store-BFzgFRog.mjs.map → challenge-store-CZyFVaad.mjs.map} +1 -1
  399. package/dist/{chunks-D5dlPeRb.mjs → chunks-DfPax1I0.mjs} +2 -2
  400. package/dist/{chunks-D5dlPeRb.mjs.map → chunks-DfPax1I0.mjs.map} +1 -1
  401. package/dist/cli/index.mjs +193 -707
  402. package/dist/cli/index.mjs.map +1 -1
  403. package/dist/client/cf-access.d.mts +1 -1
  404. package/dist/client/index.d.mts +74 -4
  405. package/dist/client/index.d.mts.map +1 -1
  406. package/dist/client/index.mjs +25 -2
  407. package/dist/client/index.mjs.map +1 -1
  408. package/dist/collection-deletion-C8BugSGz.mjs +779 -0
  409. package/dist/collection-deletion-C8BugSGz.mjs.map +1 -0
  410. package/dist/{comment-DnTxxVHv.mjs → comment-Dxlep6cE.mjs} +3 -3
  411. package/dist/{comment-DnTxxVHv.mjs.map → comment-Dxlep6cE.mjs.map} +1 -1
  412. package/dist/{comment-reaction-BUlUz2bh.mjs → comment-reaction-D6qfHKBc.mjs} +2 -2
  413. package/dist/{comment-reaction-BUlUz2bh.mjs.map → comment-reaction-D6qfHKBc.mjs.map} +1 -1
  414. package/dist/{comments-QPKgK3nk.mjs → comments-CTOuhTSX.mjs} +3 -3
  415. package/dist/{comments-QPKgK3nk.mjs.map → comments-CTOuhTSX.mjs.map} +1 -1
  416. package/dist/{components-DZkL4Fx0.mjs → components-CQy3lh9x.mjs} +1 -1
  417. package/dist/{components-DZkL4Fx0.mjs.map → components-CQy3lh9x.mjs.map} +1 -1
  418. package/dist/{config-DyF3OKg_.mjs → config-C3EBstHi.mjs} +18 -2
  419. package/dist/config-C3EBstHi.mjs.map +1 -0
  420. package/dist/{content-CpfKV9QE.mjs → content-BNfwh7xX.mjs} +667 -183
  421. package/dist/content-BNfwh7xX.mjs.map +1 -0
  422. package/dist/content-list-query-DhQ84eVV.d.mts +26 -0
  423. package/dist/content-list-query-DhQ84eVV.d.mts.map +1 -0
  424. package/dist/content-refresh-DNERP4-f.mjs +2560 -0
  425. package/dist/content-refresh-DNERP4-f.mjs.map +1 -0
  426. package/dist/{context-kO4YZB03.mjs → context-DRgOvSnW.mjs} +28 -20
  427. package/dist/context-DRgOvSnW.mjs.map +1 -0
  428. package/dist/{cron-BlKIMD_e.mjs → cron-Y9aLtgrA.mjs} +1 -1
  429. package/dist/{cron-BlKIMD_e.mjs.map → cron-Y9aLtgrA.mjs.map} +1 -1
  430. package/dist/{dashboard-DoBwPnON.mjs → dashboard-CB8J89rJ.mjs} +45 -11
  431. package/dist/dashboard-CB8J89rJ.mjs.map +1 -0
  432. package/dist/db/index.d.mts +4 -4
  433. package/dist/db/index.mjs +3 -3
  434. package/dist/db/index.mjs.map +1 -1
  435. package/dist/db/libsql.d.mts +1 -1
  436. package/dist/db/postgres.d.mts +1 -1
  437. package/dist/db/sqlite.d.mts +1 -1
  438. package/dist/{db-errors-CcWLaRiR.mjs → db-errors-Cl-sKYTm.mjs} +1 -1
  439. package/dist/{db-errors-CcWLaRiR.mjs.map → db-errors-Cl-sKYTm.mjs.map} +1 -1
  440. package/dist/{default-C4v9KP56.mjs → default-DdDl3fxv.mjs} +1 -1
  441. package/dist/{default-C4v9KP56.mjs.map → default-DdDl3fxv.mjs.map} +1 -1
  442. package/dist/deferred-tasks-BMsa9F64.d.mts +9 -0
  443. package/dist/deferred-tasks-BMsa9F64.d.mts.map +1 -0
  444. package/dist/{device-flow-DhUsyJU6.mjs → device-flow-CaioYGK3.mjs} +5 -5
  445. package/dist/{device-flow-DhUsyJU6.mjs.map → device-flow-CaioYGK3.mjs.map} +1 -1
  446. package/dist/{dialect-helpers-DFwyWjTM.mjs → dialect-helpers-BSbfaO3F.mjs} +24 -16
  447. package/dist/dialect-helpers-BSbfaO3F.mjs.map +1 -0
  448. package/dist/dist-D_6CKR8z.mjs +810 -0
  449. package/dist/dist-D_6CKR8z.mjs.map +1 -0
  450. package/dist/{email-console-C-9Ng8DM.mjs → email-console-BHNjIwiB.mjs} +1 -1
  451. package/dist/{email-console-C-9Ng8DM.mjs.map → email-console-BHNjIwiB.mjs.map} +1 -1
  452. package/dist/{enrich-CFJJgxs_.mjs → enrich-_ySMEvEE.mjs} +2 -2
  453. package/dist/{enrich-CFJJgxs_.mjs.map → enrich-_ySMEvEE.mjs.map} +1 -1
  454. package/dist/{error-DmmN74gW.mjs → error-DupyJZE2.mjs} +3 -3
  455. package/dist/{error-DmmN74gW.mjs.map → error-DupyJZE2.mjs.map} +1 -1
  456. package/dist/{errors-DtEXIQQV.mjs → errors-CLUPm7aD.mjs} +15 -2
  457. package/dist/errors-CLUPm7aD.mjs.map +1 -0
  458. package/dist/{escape-CdW31WsG.mjs → escape-C_aLKP3H.mjs} +1 -1
  459. package/dist/{escape-CdW31WsG.mjs.map → escape-C_aLKP3H.mjs.map} +1 -1
  460. package/dist/{field-defs-cache-QMVnzTH6.mjs → field-defs-cache-hg6QkjV9.mjs} +5 -5
  461. package/dist/{field-defs-cache-QMVnzTH6.mjs.map → field-defs-cache-hg6QkjV9.mjs.map} +1 -1
  462. package/dist/{fts-manager-vncsZIJm.mjs → fts-manager-CZ-5dVwJ.mjs} +111 -61
  463. package/dist/fts-manager-CZ-5dVwJ.mjs.map +1 -0
  464. package/dist/{hash-Cb7U1b5M.mjs → hash-Cg0TXJPV.mjs} +1 -1
  465. package/dist/{hash-Cb7U1b5M.mjs.map → hash-Cg0TXJPV.mjs.map} +1 -1
  466. package/dist/{hreflang-CZbSx6Bg.d.mts → hreflang-D3Uehfv2.d.mts} +2 -2
  467. package/dist/{hreflang-CZbSx6Bg.d.mts.map → hreflang-D3Uehfv2.d.mts.map} +1 -1
  468. package/dist/{hreflang-Bg-zMpkr.mjs → hreflang-DNswoYsi.mjs} +7 -7
  469. package/dist/{hreflang-Bg-zMpkr.mjs.map → hreflang-DNswoYsi.mjs.map} +1 -1
  470. package/dist/{import-PKhLeXvn.mjs → import-B41u8Rot.mjs} +7 -7
  471. package/dist/{import-PKhLeXvn.mjs.map → import-B41u8Rot.mjs.map} +1 -1
  472. package/dist/{index-j9rTh3cp.d.mts → index-C3Y-rsyN.d.mts} +286 -93
  473. package/dist/index-C3Y-rsyN.d.mts.map +1 -0
  474. package/dist/{index-BeNto_Vs.d.mts → index-Cof4-Fab.d.mts} +5 -10
  475. package/dist/index-Cof4-Fab.d.mts.map +1 -0
  476. package/dist/index.d.mts +20 -19
  477. package/dist/index.mjs +79 -71
  478. package/dist/{init-lock-DJkX6Hto.mjs → init-lock-6b309ZrF.mjs} +1 -1
  479. package/dist/{init-lock-DJkX6Hto.mjs.map → init-lock-6b309ZrF.mjs.map} +1 -1
  480. package/dist/{load-BwTdWE8B.mjs → load-DmvJrtxS.mjs} +2 -2
  481. package/dist/{load-BwTdWE8B.mjs.map → load-DmvJrtxS.mjs.map} +1 -1
  482. package/dist/{loader-C1XOLV5b.mjs → loader-DLBqRiGP.mjs} +120 -64
  483. package/dist/loader-DLBqRiGP.mjs.map +1 -0
  484. package/dist/{manifest-schema-bCq54i7F.mjs → manifest-schema-BXbUAHod.mjs} +1 -1
  485. package/dist/{manifest-schema-bCq54i7F.mjs.map → manifest-schema-BXbUAHod.mjs.map} +1 -1
  486. package/dist/media/image-endpoint.mjs +1 -1
  487. package/dist/media/index.d.mts +1 -1
  488. package/dist/media/index.mjs +3 -2
  489. package/dist/media/index.mjs.map +1 -1
  490. package/dist/media/local-runtime.d.mts +12 -12
  491. package/dist/media/local-runtime.mjs +10 -9
  492. package/dist/media/local-runtime.mjs.map +1 -1
  493. package/dist/{media-DWIzoVFL.mjs → media-BWWWb8kg.mjs} +92 -3
  494. package/dist/media-BWWWb8kg.mjs.map +1 -0
  495. package/dist/{media-tQDZEdu7.mjs → media-CzhAn2--.mjs} +2 -2
  496. package/dist/{media-tQDZEdu7.mjs.map → media-CzhAn2--.mjs.map} +1 -1
  497. package/dist/{media-allowlist-B7TPD6aq.mjs → media-allowlist-BD6HhgXA.mjs} +14 -3
  498. package/dist/media-allowlist-BD6HhgXA.mjs.map +1 -0
  499. package/dist/{media-upload-Bc9Nc5Zf.mjs → media-upload-9yas9zGH.mjs} +11 -9
  500. package/dist/{media-upload-Bc9Nc5Zf.mjs.map → media-upload-9yas9zGH.mjs.map} +1 -1
  501. package/dist/{media-url-BCm5vBn6.mjs → media-url-BvoX88FA.mjs} +1 -1
  502. package/dist/{media-url-BCm5vBn6.mjs.map → media-url-BvoX88FA.mjs.map} +1 -1
  503. package/dist/{media-usage-DPJcNaAq.mjs → media-usage-De3qvwjc.mjs} +138 -61
  504. package/dist/media-usage-De3qvwjc.mjs.map +1 -0
  505. package/dist/media-usage-work-B9Bksh_b.mjs +152 -0
  506. package/dist/media-usage-work-B9Bksh_b.mjs.map +1 -0
  507. package/dist/media-usage-work-DrCbqBcl.mjs +424 -0
  508. package/dist/media-usage-work-DrCbqBcl.mjs.map +1 -0
  509. package/dist/media-usage-write-fence-aHEgN1zB.mjs +42 -0
  510. package/dist/media-usage-write-fence-aHEgN1zB.mjs.map +1 -0
  511. package/dist/{menus-C6qhqP4X.mjs → menus-CC-yn1F9.mjs} +554 -121
  512. package/dist/menus-CC-yn1F9.mjs.map +1 -0
  513. package/dist/{menus-BxPLFfIc.mjs → menus-fxvEDYE6.mjs} +5 -5
  514. package/dist/{menus-BxPLFfIc.mjs.map → menus-fxvEDYE6.mjs.map} +1 -1
  515. package/dist/{mode-fiXRMfeA.mjs → mode-Bpe-tNNy.mjs} +1 -1
  516. package/dist/{mode-fiXRMfeA.mjs.map → mode-Bpe-tNNy.mjs.map} +1 -1
  517. package/dist/{normalize-C-SHXmra.mjs → normalize-Bc-s_r-a.mjs} +1 -1
  518. package/dist/{normalize-C-SHXmra.mjs.map → normalize-Bc-s_r-a.mjs.map} +1 -1
  519. package/dist/{oauth-authorization-C9tFinly.mjs → oauth-authorization-BXO1MKej.mjs} +5 -5
  520. package/dist/{oauth-authorization-C9tFinly.mjs.map → oauth-authorization-BXO1MKej.mjs.map} +1 -1
  521. package/dist/{oauth-clients-BwTxE1aE.mjs → oauth-clients-C_DNqtRu.mjs} +1 -1
  522. package/dist/{oauth-clients-BwTxE1aE.mjs.map → oauth-clients-C_DNqtRu.mjs.map} +1 -1
  523. package/dist/{oauth-state-store-DaRUwPWO.mjs → oauth-state-store-DwAxMtk4.mjs} +1 -1
  524. package/dist/{oauth-state-store-DaRUwPWO.mjs.map → oauth-state-store-DwAxMtk4.mjs.map} +1 -1
  525. package/dist/{oauth-user-lookup-CzuzOSlF.mjs → oauth-user-lookup-BxfZizdk.mjs} +1 -1
  526. package/dist/{oauth-user-lookup-CzuzOSlF.mjs.map → oauth-user-lookup-BxfZizdk.mjs.map} +1 -1
  527. package/dist/object-cache/memory.d.mts +1 -1
  528. package/dist/{object-cache-Bok5j2ae.mjs → object-cache-71tPfV4A.mjs} +106 -5
  529. package/dist/object-cache-71tPfV4A.mjs.map +1 -0
  530. package/dist/{options-BlmBHTvX.mjs → options-CBXIHUoT.mjs} +1 -1
  531. package/dist/{options-BlmBHTvX.mjs.map → options-CBXIHUoT.mjs.map} +1 -1
  532. package/dist/{options-aeIs8WFu.d.mts → options-DRe1pIBL.d.mts} +4 -3
  533. package/dist/options-DRe1pIBL.d.mts.map +1 -0
  534. package/dist/page/index.d.mts +2 -2
  535. package/dist/{parse-BL49yb9D.mjs → parse-DFbuc-Fh.mjs} +2 -2
  536. package/dist/{parse-BL49yb9D.mjs.map → parse-DFbuc-Fh.mjs.map} +1 -1
  537. package/dist/{passkey-config-KMhgFjdQ.mjs → passkey-config-_2GOwwK8.mjs} +1 -1
  538. package/dist/{passkey-config-KMhgFjdQ.mjs.map → passkey-config-_2GOwwK8.mjs.map} +1 -1
  539. package/dist/{placeholder-Dxo5Zt58.d.mts → placeholder-CzW7Lshs.d.mts} +1 -1
  540. package/dist/{placeholder-Dxo5Zt58.d.mts.map → placeholder-CzW7Lshs.d.mts.map} +1 -1
  541. package/dist/plugin-types.d.mts +8 -1
  542. package/dist/plugin-types.d.mts.map +1 -1
  543. package/dist/plugin-utils.d.mts +10 -10
  544. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
  545. package/dist/plugins/adapt-sandbox-entry.mjs +7 -4
  546. package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
  547. package/dist/{portable-text-CNahFfj6.mjs → portable-text-DhKbDAGr.mjs} +1 -1
  548. package/dist/{portable-text-CNahFfj6.mjs.map → portable-text-DhKbDAGr.mjs.map} +1 -1
  549. package/dist/{preview-D4Jnbfx7.mjs → preview-PW-nOnq4.mjs} +2 -2
  550. package/dist/{preview-D4Jnbfx7.mjs.map → preview-PW-nOnq4.mjs.map} +1 -1
  551. package/dist/{public-url-DSGTnJFw.mjs → public-url-CWoFhQT8.mjs} +14 -3
  552. package/dist/public-url-CWoFhQT8.mjs.map +1 -0
  553. package/dist/{query-DCiXI7OZ.mjs → query-BgjfiLUQ.mjs} +38 -77
  554. package/dist/query-BgjfiLUQ.mjs.map +1 -0
  555. package/dist/{query-1xOcEy18.mjs → query-vgZNwIhh.mjs} +4 -4
  556. package/dist/{query-1xOcEy18.mjs.map → query-vgZNwIhh.mjs.map} +1 -1
  557. package/dist/{rate-limit-Vkp9gQ1Y.mjs → rate-limit-L5anykgo.mjs} +2 -2
  558. package/dist/{rate-limit-Vkp9gQ1Y.mjs.map → rate-limit-L5anykgo.mjs.map} +1 -1
  559. package/dist/{redirect-BJ6d1yHh.mjs → redirect-COsS6SWJ.mjs} +3 -3
  560. package/dist/{redirect-BJ6d1yHh.mjs.map → redirect-COsS6SWJ.mjs.map} +1 -1
  561. package/dist/{redirect-CDgfv9vh.mjs → redirect-KBNE3UEs.mjs} +1 -1
  562. package/dist/{redirect-CDgfv9vh.mjs.map → redirect-KBNE3UEs.mjs.map} +1 -1
  563. package/dist/{redirects-Cwkq2H-G.mjs → redirects-CufRQt0Q.mjs} +5 -5
  564. package/dist/{redirects-Cwkq2H-G.mjs.map → redirects-CufRQt0Q.mjs.map} +1 -1
  565. package/dist/{redirects-BOn2DIUs.mjs → redirects-cV9g7V-0.mjs} +149 -16
  566. package/dist/redirects-cV9g7V-0.mjs.map +1 -0
  567. package/dist/registry-C581V4TP.mjs +1275 -0
  568. package/dist/registry-C581V4TP.mjs.map +1 -0
  569. package/dist/{relation-DJ5DZh7B.mjs → relation-DtmAQH7s.mjs} +3 -3
  570. package/dist/{relation-DJ5DZh7B.mjs.map → relation-DtmAQH7s.mjs.map} +1 -1
  571. package/dist/{relations-C4duJnwI.mjs → relations-BBApI4e2.mjs} +3 -3
  572. package/dist/{relations-C4duJnwI.mjs.map → relations-BBApI4e2.mjs.map} +1 -1
  573. package/dist/{relations-DpTjftCl.mjs → relations-DICSyfsh.mjs} +6 -6
  574. package/dist/{relations-DpTjftCl.mjs.map → relations-DICSyfsh.mjs.map} +1 -1
  575. package/dist/{relations-DHFQGX0h.d.mts → relations-DItI3MN9.d.mts} +401 -40
  576. package/dist/relations-DItI3MN9.d.mts.map +1 -0
  577. package/dist/request-context.d.mts +3 -0
  578. package/dist/request-context.d.mts.map +1 -1
  579. package/dist/request-context.mjs.map +1 -1
  580. package/dist/{request-meta-DzXYYI-n.mjs → request-meta-DgZJh3zG.mjs} +2 -2
  581. package/dist/{request-meta-DzXYYI-n.mjs.map → request-meta-DgZJh3zG.mjs.map} +1 -1
  582. package/dist/{resolve-BUvFE0Lr.mjs → resolve-DOhVEvty.mjs} +2 -2
  583. package/dist/{resolve-BUvFE0Lr.mjs.map → resolve-DOhVEvty.mjs.map} +1 -1
  584. package/dist/{runner-BsI18UgP.mjs → runner-BiDvq5dv.mjs} +1404 -235
  585. package/dist/runner-BiDvq5dv.mjs.map +1 -0
  586. package/dist/{runner-CHdkERbt.d.mts → runner-CgQbELYC.d.mts} +2 -2
  587. package/dist/{runner-CHdkERbt.d.mts.map → runner-CgQbELYC.d.mts.map} +1 -1
  588. package/dist/runtime.d.mts +11 -11
  589. package/dist/runtime.mjs +5 -4
  590. package/dist/runtime.mjs.map +1 -1
  591. package/dist/{schema-eqZhkR0S.mjs → schema-BFyTg38T.mjs} +3 -3
  592. package/dist/{schema-eqZhkR0S.mjs.map → schema-BFyTg38T.mjs.map} +1 -1
  593. package/dist/schema-CeQSNwB3.mjs +390 -0
  594. package/dist/schema-CeQSNwB3.mjs.map +1 -0
  595. package/dist/{scopes-Be2d8bSb.mjs → scopes-BVeuhGBE.mjs} +3 -3
  596. package/dist/{scopes-Be2d8bSb.mjs.map → scopes-BVeuhGBE.mjs.map} +1 -1
  597. package/dist/{search-CeO678cp.mjs → search-BpuPKZb0.mjs} +26 -16
  598. package/dist/search-BpuPKZb0.mjs.map +1 -0
  599. package/dist/{secrets-870d-7yA.mjs → secrets-dtYs2hDo.mjs} +5 -5
  600. package/dist/{secrets-870d-7yA.mjs.map → secrets-dtYs2hDo.mjs.map} +1 -1
  601. package/dist/{sections-DSuEfirn.mjs → sections-COctk6hf.mjs} +3 -3
  602. package/dist/{sections-DSuEfirn.mjs.map → sections-COctk6hf.mjs.map} +1 -1
  603. package/dist/seed/index.d.mts +2 -2
  604. package/dist/seed/index.mjs +28 -26
  605. package/dist/seo/index.d.mts +3 -3
  606. package/dist/seo/index.mjs +20 -19
  607. package/dist/seo/index.mjs.map +1 -1
  608. package/dist/{seo-C3GDfT0V.mjs → seo-AFpVvTqr.mjs} +6 -4
  609. package/dist/seo-AFpVvTqr.mjs.map +1 -0
  610. package/dist/{seo-BAPKiED8.mjs → seo-CvDxfvFb.mjs} +3 -3
  611. package/dist/{seo-BAPKiED8.mjs.map → seo-CvDxfvFb.mjs.map} +1 -1
  612. package/dist/{service-DyFT-k-R.mjs → service-xMiMSTsR.mjs} +4 -4
  613. package/dist/{service-DyFT-k-R.mjs.map → service-xMiMSTsR.mjs.map} +1 -1
  614. package/dist/{session-user-BrK2zz7S.mjs → session-user-BzVemLkw.mjs} +2 -2
  615. package/dist/{session-user-BrK2zz7S.mjs.map → session-user-BzVemLkw.mjs.map} +1 -1
  616. package/dist/{settings-DgKouY2S.mjs → settings-B372MV0p.mjs} +7 -7
  617. package/dist/{settings-DgKouY2S.mjs.map → settings-B372MV0p.mjs.map} +1 -1
  618. package/dist/{settings-CREKyj8I.mjs → settings-BE9aqn_9.mjs} +3 -3
  619. package/dist/{settings-CREKyj8I.mjs.map → settings-BE9aqn_9.mjs.map} +1 -1
  620. package/dist/{setup-complete-L7bMK2VI.mjs → setup-complete-DF7TAfD5.mjs} +2 -2
  621. package/dist/{setup-complete-L7bMK2VI.mjs.map → setup-complete-DF7TAfD5.mjs.map} +1 -1
  622. package/dist/{setup-nonce-Ct5tjWcq.mjs → setup-nonce-Do-U_cg9.mjs} +1 -1
  623. package/dist/{setup-nonce-Ct5tjWcq.mjs.map → setup-nonce-Do-U_cg9.mjs.map} +1 -1
  624. package/dist/{single-flight-cache-C2exrGAi.mjs → single-flight-cache-B0eLKS6K.mjs} +2 -2
  625. package/dist/{single-flight-cache-C2exrGAi.mjs.map → single-flight-cache-B0eLKS6K.mjs.map} +1 -1
  626. package/dist/site-url-Bowsi9T-.mjs +16 -0
  627. package/dist/site-url-Bowsi9T-.mjs.map +1 -0
  628. package/dist/slugify-B994zkXC.mjs +17 -0
  629. package/dist/slugify-B994zkXC.mjs.map +1 -0
  630. package/dist/{snapshot-BdpUJKD-.mjs → snapshot-B5h6aPMh.mjs} +2 -2
  631. package/dist/{snapshot-BdpUJKD-.mjs.map → snapshot-B5h6aPMh.mjs.map} +1 -1
  632. package/dist/{ssrf-CviKqWmq.mjs → ssrf-Cpvg_n3A.mjs} +1 -1
  633. package/dist/{ssrf-CviKqWmq.mjs.map → ssrf-Cpvg_n3A.mjs.map} +1 -1
  634. package/dist/{state-xxv6ZTMv.mjs → state-DYwPue40.mjs} +1 -1
  635. package/dist/{state-xxv6ZTMv.mjs.map → state-DYwPue40.mjs.map} +1 -1
  636. package/dist/{status-COfycGIU.mjs → status-CWVYpbD8.mjs} +1 -1
  637. package/dist/{status-COfycGIU.mjs.map → status-CWVYpbD8.mjs.map} +1 -1
  638. package/dist/storage/local.d.mts +1 -1
  639. package/dist/storage/local.mjs +1 -1
  640. package/dist/storage/s3.d.mts +1 -1
  641. package/dist/storage/s3.mjs +1 -1
  642. package/dist/{taxonomies-B61CRSha.mjs → taxonomies-B0o9ExNV.mjs} +85 -75
  643. package/dist/taxonomies-B0o9ExNV.mjs.map +1 -0
  644. package/dist/{taxonomies-Dbdki3Un.mjs → taxonomies-Dk3MAklL.mjs} +360 -56
  645. package/dist/taxonomies-Dk3MAklL.mjs.map +1 -0
  646. package/dist/taxonomy-CPeM8d2o.mjs +484 -0
  647. package/dist/taxonomy-CPeM8d2o.mjs.map +1 -0
  648. package/dist/{tokens-DVltpO2D.mjs → tokens-C39klX8R.mjs} +2 -2
  649. package/dist/{tokens-DVltpO2D.mjs.map → tokens-C39klX8R.mjs.map} +1 -1
  650. package/dist/{transaction-D0FOsb3X.mjs → transaction-NQj4VJ7Z.mjs} +1 -1
  651. package/dist/{transaction-D0FOsb3X.mjs.map → transaction-NQj4VJ7Z.mjs.map} +1 -1
  652. package/dist/{transport-BWQo0Foy.mjs → transport--CfOflrp.mjs} +1 -1
  653. package/dist/{transport-BWQo0Foy.mjs.map → transport--CfOflrp.mjs.map} +1 -1
  654. package/dist/{transport-DokJOScS.d.mts → transport-B_rfeU-a.d.mts} +1 -1
  655. package/dist/{transport-DokJOScS.d.mts.map → transport-B_rfeU-a.d.mts.map} +1 -1
  656. package/dist/{trusted-proxy-CwjQj0YG.mjs → trusted-proxy-CEVekpby.mjs} +1 -1
  657. package/dist/{trusted-proxy-CwjQj0YG.mjs.map → trusted-proxy-CEVekpby.mjs.map} +1 -1
  658. package/dist/{types-o7xo7VgH.mjs → types-75KIqzL9.mjs} +20 -3
  659. package/dist/types-75KIqzL9.mjs.map +1 -0
  660. package/dist/{types-C9P3ysxq.d.mts → types-BPuloofb.d.mts} +1 -1
  661. package/dist/{types-C9P3ysxq.d.mts.map → types-BPuloofb.d.mts.map} +1 -1
  662. package/dist/{types-BD40g7zz.mjs → types-BW1syrlw.mjs} +1 -1
  663. package/dist/{types-BD40g7zz.mjs.map → types-BW1syrlw.mjs.map} +1 -1
  664. package/dist/{types-jISEalTX.d.mts → types-C3_AbkDP.d.mts} +53 -10
  665. package/dist/types-C3_AbkDP.d.mts.map +1 -0
  666. package/dist/{types-84Bque3-.d.mts → types-CAGdePf3.d.mts} +133 -1
  667. package/dist/types-CAGdePf3.d.mts.map +1 -0
  668. package/dist/{types-CcSZCcF1.d.mts → types-CFUfyt1s.d.mts} +1 -1
  669. package/dist/{types-CcSZCcF1.d.mts.map → types-CFUfyt1s.d.mts.map} +1 -1
  670. package/dist/{types-DQLZ58GV.d.mts → types-CPx2lIGo.d.mts} +1 -1
  671. package/dist/{types-DQLZ58GV.d.mts.map → types-CPx2lIGo.d.mts.map} +1 -1
  672. package/dist/{types-XrQQ-Aex.mjs → types-CQPWhPxE.mjs} +15 -11
  673. package/dist/types-CQPWhPxE.mjs.map +1 -0
  674. package/dist/{types-BQY7VYw6.d.mts → types-Cs-TFLNf.d.mts} +28 -8
  675. package/dist/types-Cs-TFLNf.d.mts.map +1 -0
  676. package/dist/types-CtzytVXn.mjs +18 -0
  677. package/dist/types-CtzytVXn.mjs.map +1 -0
  678. package/dist/{types-DqZVPILm.d.mts → types-D4_1Ga4L.d.mts} +44 -5
  679. package/dist/types-D4_1Ga4L.d.mts.map +1 -0
  680. package/dist/{types-Dr2GLlwu.d.mts → types-DefV3OiO.d.mts} +1 -1
  681. package/dist/{types-Dr2GLlwu.d.mts.map → types-DefV3OiO.d.mts.map} +1 -1
  682. package/dist/{types-DsAMyD_o.d.mts → types-X0MThr7x.d.mts} +1 -1
  683. package/dist/{types-DsAMyD_o.d.mts.map → types-X0MThr7x.d.mts.map} +1 -1
  684. package/dist/{upload-attempts-C5yd6Gae.mjs → upload-attempts-hlne3iA1.mjs} +1 -1
  685. package/dist/{upload-attempts-C5yd6Gae.mjs.map → upload-attempts-hlne3iA1.mjs.map} +1 -1
  686. package/dist/url-pattern-BHS0V5Ng.mjs +33 -0
  687. package/dist/url-pattern-BHS0V5Ng.mjs.map +1 -0
  688. package/dist/{user-BAumEmpA.mjs → user-sHgJ62SP.mjs} +3 -3
  689. package/dist/{user-BAumEmpA.mjs.map → user-sHgJ62SP.mjs.map} +1 -1
  690. package/dist/{utils-BbUgmeZG.mjs → utils-D4vD_Y98.mjs} +3 -3
  691. package/dist/{utils-BbUgmeZG.mjs.map → utils-D4vD_Y98.mjs.map} +1 -1
  692. package/dist/{validate-Bs_wT2ul.mjs → validate-BkqIG_Uj.mjs} +24 -5
  693. package/dist/validate-BkqIG_Uj.mjs.map +1 -0
  694. package/dist/{validate-VPnKoIzW.mjs → validate-BpwoDOgt.mjs} +25 -2
  695. package/dist/validate-BpwoDOgt.mjs.map +1 -0
  696. package/dist/{validate-CdYcY-Ln.d.mts → validate-DeGtBN84.d.mts} +25 -7
  697. package/dist/validate-DeGtBN84.d.mts.map +1 -0
  698. package/dist/{validation-BngR0nit.mjs → validation-CiJb_X5o.mjs} +14 -13
  699. package/dist/{validation-BngR0nit.mjs.map → validation-CiJb_X5o.mjs.map} +1 -1
  700. package/dist/version-x0NjTScG.mjs +7 -0
  701. package/dist/{version-Bdfb34kk.mjs.map → version-x0NjTScG.mjs.map} +1 -1
  702. package/dist/{widgets-L-VndTKD.mjs → widgets-Cwzvww0W.mjs} +3 -3
  703. package/dist/{widgets-L-VndTKD.mjs.map → widgets-Cwzvww0W.mjs.map} +1 -1
  704. package/dist/{wxr-taxonomies-DueaLFd9.d.mts → wxr-taxonomies-CmC0ZsDr.d.mts} +1 -1
  705. package/dist/{wxr-taxonomies-DueaLFd9.d.mts.map → wxr-taxonomies-CmC0ZsDr.d.mts.map} +1 -1
  706. package/dist/{wxr-taxonomies-BexjtNid.mjs → wxr-taxonomies-QZjWxjyk.mjs} +13 -32
  707. package/dist/{wxr-taxonomies-BexjtNid.mjs.map → wxr-taxonomies-QZjWxjyk.mjs.map} +1 -1
  708. package/dist/{zod-generator-DMzfga3g.mjs → zod-generator-D11MhxG4.mjs} +53 -6
  709. package/dist/zod-generator-D11MhxG4.mjs.map +1 -0
  710. package/package.json +18 -7
  711. package/src/after.ts +6 -1
  712. package/src/api/errors.ts +13 -0
  713. package/src/api/handlers/content.ts +172 -33
  714. package/src/api/handlers/dashboard.ts +12 -2
  715. package/src/api/handlers/index.ts +16 -0
  716. package/src/api/handlers/manifest.ts +2 -0
  717. package/src/api/handlers/marketplace.ts +14 -0
  718. package/src/api/handlers/media-allowlist.ts +13 -2
  719. package/src/api/handlers/media-usage-work.ts +188 -0
  720. package/src/api/handlers/media-usage.ts +5 -3
  721. package/src/api/handlers/registry.ts +14 -0
  722. package/src/api/handlers/revision.ts +19 -5
  723. package/src/api/handlers/schema.ts +46 -3
  724. package/src/api/handlers/seo.ts +5 -2
  725. package/src/api/handlers/snapshot.ts +1 -1
  726. package/src/api/handlers/taxonomies.ts +446 -83
  727. package/src/api/media-usage-write-fence.ts +64 -0
  728. package/src/api/openapi/document.ts +247 -0
  729. package/src/api/public-url.ts +14 -2
  730. package/src/api/schemas/common.ts +3 -1
  731. package/src/api/schemas/content.ts +122 -1
  732. package/src/api/schemas/media-usage.ts +133 -0
  733. package/src/api/schemas/schema.ts +91 -3
  734. package/src/api/schemas/search.ts +2 -0
  735. package/src/api/schemas/taxonomies.ts +58 -8
  736. package/src/api/site-url.ts +16 -6
  737. package/src/api/types.ts +1 -0
  738. package/src/astro/integration/index.ts +55 -42
  739. package/src/astro/integration/routes.ts +48 -4
  740. package/src/astro/integration/runtime.ts +28 -0
  741. package/src/astro/integration/virtual-modules.ts +27 -1
  742. package/src/astro/integration/vite-config.ts +16 -1
  743. package/src/astro/middleware/media-usage-write-fence.ts +30 -0
  744. package/src/astro/middleware/redirect.ts +14 -12
  745. package/src/astro/middleware/scoped-db.ts +35 -4
  746. package/src/astro/middleware.ts +131 -30
  747. package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -1
  748. package/src/astro/routes/api/admin/media-usage/collection-deletions/index.ts +22 -0
  749. package/src/astro/routes/api/admin/media-usage/collection-deletions/retry.ts +22 -0
  750. package/src/astro/routes/api/admin/media-usage/work/index.ts +26 -0
  751. package/src/astro/routes/api/admin/media-usage/work/retry.ts +26 -0
  752. package/src/astro/routes/api/admin/plugins/[id]/disable.ts +4 -0
  753. package/src/astro/routes/api/admin/plugins/[id]/enable.ts +4 -0
  754. package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +4 -0
  755. package/src/astro/routes/api/admin/plugins/[id]/update.ts +4 -0
  756. package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +4 -0
  757. package/src/astro/routes/api/admin/plugins/registry/[id]/uninstall.ts +4 -0
  758. package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
  759. package/src/astro/routes/api/admin/plugins/registry/install.ts +4 -0
  760. package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +2 -2
  761. package/src/astro/routes/api/auth/invite/index.ts +2 -2
  762. package/src/astro/routes/api/auth/magic-link/send.ts +2 -2
  763. package/src/astro/routes/api/auth/signup/request.ts +2 -2
  764. package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +1 -1
  765. package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +67 -31
  766. package/src/astro/routes/api/import/wordpress/execute.ts +3 -10
  767. package/src/astro/routes/api/mcp.ts +1 -0
  768. package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +7 -1
  769. package/src/astro/routes/api/schema/collections/[slug]/index.ts +1 -7
  770. package/src/astro/routes/api/schema/collections/reorder.ts +31 -0
  771. package/src/astro/routes/api/schema/index.ts +2 -0
  772. package/src/astro/routes/api/search/enable.ts +5 -1
  773. package/src/astro/routes/api/search/rebuild.ts +6 -1
  774. package/src/astro/routes/api/taxonomies/[name]/reorder.ts +41 -0
  775. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +1 -0
  776. package/src/astro/routes/api/taxonomies/[name]/translations.ts +48 -0
  777. package/src/astro/routes/api/taxonomies/[name].ts +102 -0
  778. package/src/astro/types.ts +31 -4
  779. package/src/cleanup.ts +25 -26
  780. package/src/cli/commands/dev.ts +13 -3
  781. package/src/cli/commands/doctor.ts +193 -6
  782. package/src/cli/commands/export-seed.ts +6 -1
  783. package/src/cli/index.ts +1 -1
  784. package/src/client/index.ts +119 -0
  785. package/src/components/Block.astro +12 -0
  786. package/src/components/CommentForm.astro +1 -1
  787. package/src/components/Comments.astro +1 -1
  788. package/src/components/InlinePortableTextEditor.tsx +260 -32
  789. package/src/components/OrderedList.astro +14 -0
  790. package/src/components/PortableText.astro +26 -4
  791. package/src/components/index.ts +12 -1
  792. package/src/components/ordered-list.ts +549 -0
  793. package/src/content/converters/mark-safety.ts +120 -0
  794. package/src/content/converters/numbered-list.ts +148 -0
  795. package/src/content/converters/portable-text-to-prosemirror.ts +84 -25
  796. package/src/content/converters/prosemirror-to-portable-text.ts +35 -14
  797. package/src/content/converters/types.ts +2 -0
  798. package/src/content/portable-text-lists.ts +205 -0
  799. package/src/content-list-query.ts +29 -0
  800. package/src/database/dialect-helpers.ts +3 -22
  801. package/src/database/migrations/055_content_translation_group_locale_index.ts +67 -0
  802. package/src/database/migrations/056_taxonomy_term_sort_order.ts +189 -0
  803. package/src/database/migrations/057_collection_hidden.ts +24 -0
  804. package/src/database/migrations/058_collection_sort_order.ts +20 -0
  805. package/src/database/migrations/059_revision_prune_queue.ts +36 -0
  806. package/src/database/migrations/060_collection_admin_config.ts +16 -0
  807. package/src/database/migrations/061_media_usage_cleanup.ts +77 -0
  808. package/src/database/migrations/062_media_usage_cleanup_fence.ts +295 -0
  809. package/src/database/migrations/063_media_usage_incremental_work.ts +397 -0
  810. package/src/database/migrations/064_fts_plain_text.ts +253 -0
  811. package/src/database/migrations/065_media_usage_collection_deletion.ts +85 -0
  812. package/src/database/migrations/066_media_usage_reconciliation.ts +115 -0
  813. package/src/database/migrations/067_indexed_content_fields.ts +39 -0
  814. package/src/database/migrations/068_content_taxonomy_entry_groups.ts +61 -0
  815. package/src/database/migrations/069_collection_title_date_fields.ts +45 -0
  816. package/src/database/migrations/070_collection_routable.ts +18 -0
  817. package/src/database/migrations/runner.ts +32 -0
  818. package/src/database/repositories/content.ts +1051 -273
  819. package/src/database/repositories/media-usage-work.ts +848 -0
  820. package/src/database/repositories/media-usage.ts +1502 -138
  821. package/src/database/repositories/revision.ts +98 -22
  822. package/src/database/repositories/taxonomy.ts +429 -103
  823. package/src/database/repositories/types.ts +58 -2
  824. package/src/database/types.ts +147 -17
  825. package/src/database/validate.ts +39 -0
  826. package/src/db/adapters.ts +36 -0
  827. package/src/db/index.ts +3 -0
  828. package/src/deferred-tasks.ts +71 -0
  829. package/src/emdash-runtime.ts +493 -110
  830. package/src/fields/file.ts +7 -4
  831. package/src/fields/image.ts +8 -1
  832. package/src/fields/types.ts +18 -5
  833. package/src/i18n/config.ts +32 -0
  834. package/src/i18n/repair-locale-casing.ts +0 -14
  835. package/src/i18n/taxonomy-locale-diagnostic.ts +47 -0
  836. package/src/import/wxr-taxonomies.ts +7 -47
  837. package/src/index.ts +27 -2
  838. package/src/loader.ts +165 -89
  839. package/src/mcp/server.ts +242 -34
  840. package/src/media/usage/activation.ts +626 -0
  841. package/src/media/usage/capture-triggers.ts +543 -0
  842. package/src/media/usage/cleanup.ts +305 -0
  843. package/src/media/usage/collection-deletion-processor.ts +356 -0
  844. package/src/media/usage/collection-deletion.ts +836 -0
  845. package/src/media/usage/content-fields.ts +40 -3
  846. package/src/media/usage/content-refresh.ts +356 -47
  847. package/src/media/usage/content-repair.ts +180 -61
  848. package/src/media/usage/content-snapshots.ts +85 -105
  849. package/src/media/usage/gc.ts +50 -0
  850. package/src/media/usage/projection-fingerprint.ts +129 -0
  851. package/src/media/usage/reconciliation-processor.ts +372 -0
  852. package/src/media/usage/reconciliation.ts +1117 -0
  853. package/src/media/usage/source-key.ts +4 -0
  854. package/src/media/usage/types.ts +2 -0
  855. package/src/media/usage/work-processor.ts +248 -0
  856. package/src/menus/index.ts +246 -141
  857. package/src/object-cache/index.ts +160 -3
  858. package/src/plugin-types.ts +8 -0
  859. package/src/plugins/adapt-sandbox-entry.ts +24 -5
  860. package/src/plugins/context.ts +24 -7
  861. package/src/plugins/index.ts +9 -0
  862. package/src/plugins/routes.ts +77 -1
  863. package/src/plugins/sandbox/index.ts +10 -1
  864. package/src/plugins/sandbox/types.ts +78 -1
  865. package/src/plugins/scheduler/node.ts +13 -1
  866. package/src/plugins/scheduler/types.ts +2 -0
  867. package/src/plugins/storage-indexes.ts +50 -11
  868. package/src/plugins/types.ts +39 -2
  869. package/src/query.ts +50 -79
  870. package/src/redirects/cache.ts +79 -19
  871. package/src/request-context.ts +3 -0
  872. package/src/scheduled-publish.ts +6 -1
  873. package/src/scheduler-health.ts +46 -0
  874. package/src/schema/registry.ts +863 -249
  875. package/src/schema/types.ts +74 -8
  876. package/src/schema/url-pattern.ts +33 -0
  877. package/src/schema/zod-generator.ts +77 -5
  878. package/src/search/fts-manager.ts +167 -86
  879. package/src/search/index.ts +2 -0
  880. package/src/search/query.ts +32 -11
  881. package/src/search/types.ts +8 -0
  882. package/src/seed/apply.ts +89 -16
  883. package/src/seed/types.ts +21 -3
  884. package/src/seed/validate.ts +52 -2
  885. package/src/taxonomies/index.ts +88 -82
  886. package/src/taxonomies/term-counts.ts +23 -12
  887. package/src/ui-comments.ts +19 -0
  888. package/src/ui.ts +11 -3
  889. package/src/utils/slugify.ts +1 -29
  890. package/src/virtual-modules.d.ts +24 -0
  891. package/dist/adapters-C0ONzkUP.d.mts.map +0 -1
  892. package/dist/after-B1IIdH3Y.mjs +0 -29
  893. package/dist/after-B1IIdH3Y.mjs.map +0 -1
  894. package/dist/api-DgPbMlng.mjs.map +0 -1
  895. package/dist/apply-1_6ra7NP.mjs.map +0 -1
  896. package/dist/cache-B26cufFd.mjs +0 -65
  897. package/dist/cache-B26cufFd.mjs.map +0 -1
  898. package/dist/config-DyF3OKg_.mjs.map +0 -1
  899. package/dist/content-CpfKV9QE.mjs.map +0 -1
  900. package/dist/content-refresh-DgqUBeTv.mjs +0 -1629
  901. package/dist/content-refresh-DgqUBeTv.mjs.map +0 -1
  902. package/dist/context-kO4YZB03.mjs.map +0 -1
  903. package/dist/dashboard-DoBwPnON.mjs.map +0 -1
  904. package/dist/dialect-helpers-DFwyWjTM.mjs.map +0 -1
  905. package/dist/errors-DtEXIQQV.mjs.map +0 -1
  906. package/dist/fts-manager-vncsZIJm.mjs.map +0 -1
  907. package/dist/index-BeNto_Vs.d.mts.map +0 -1
  908. package/dist/index-j9rTh3cp.d.mts.map +0 -1
  909. package/dist/loader-C1XOLV5b.mjs.map +0 -1
  910. package/dist/media-DWIzoVFL.mjs.map +0 -1
  911. package/dist/media-allowlist-B7TPD6aq.mjs.map +0 -1
  912. package/dist/media-usage-DPJcNaAq.mjs.map +0 -1
  913. package/dist/menus-C6qhqP4X.mjs.map +0 -1
  914. package/dist/object-cache-Bok5j2ae.mjs.map +0 -1
  915. package/dist/options-aeIs8WFu.d.mts.map +0 -1
  916. package/dist/public-url-DSGTnJFw.mjs.map +0 -1
  917. package/dist/query-DCiXI7OZ.mjs.map +0 -1
  918. package/dist/redirects-BOn2DIUs.mjs.map +0 -1
  919. package/dist/registry-BP1JK2xh.mjs +0 -795
  920. package/dist/registry-BP1JK2xh.mjs.map +0 -1
  921. package/dist/relations-DHFQGX0h.d.mts.map +0 -1
  922. package/dist/runner-BsI18UgP.mjs.map +0 -1
  923. package/dist/search-CeO678cp.mjs.map +0 -1
  924. package/dist/seo-C3GDfT0V.mjs.map +0 -1
  925. package/dist/site-url-8N3kSyVZ.mjs +0 -13
  926. package/dist/site-url-8N3kSyVZ.mjs.map +0 -1
  927. package/dist/slugify-C_tqlU4G.mjs +0 -30
  928. package/dist/slugify-C_tqlU4G.mjs.map +0 -1
  929. package/dist/taxonomies-B61CRSha.mjs.map +0 -1
  930. package/dist/taxonomies-Dbdki3Un.mjs.map +0 -1
  931. package/dist/taxonomy-DvwWAPvA.mjs +0 -304
  932. package/dist/taxonomy-DvwWAPvA.mjs.map +0 -1
  933. package/dist/types-84Bque3-.d.mts.map +0 -1
  934. package/dist/types-BQY7VYw6.d.mts.map +0 -1
  935. package/dist/types-DqZVPILm.d.mts.map +0 -1
  936. package/dist/types-XrQQ-Aex.mjs.map +0 -1
  937. package/dist/types-jISEalTX.d.mts.map +0 -1
  938. package/dist/types-o7xo7VgH.mjs.map +0 -1
  939. package/dist/validate-Bs_wT2ul.mjs.map +0 -1
  940. package/dist/validate-CdYcY-Ln.d.mts.map +0 -1
  941. package/dist/validate-VPnKoIzW.mjs.map +0 -1
  942. package/dist/version-Bdfb34kk.mjs +0 -7
  943. package/dist/zod-generator-DMzfga3g.mjs.map +0 -1
  944. /package/dist/{api-tokens-CEsW_jCg.mjs → api-tokens-CyTsLszZ.mjs} +0 -0
  945. /package/dist/{ssrf-BB__xfNM.mjs → ssrf-DpXgKsna.mjs} +0 -0
  946. /package/dist/{types-CK0LxPUQ.mjs → types-CP1CflWf.mjs} +0 -0
@@ -13,6 +13,7 @@ import { Kysely, sql, type Dialect } from "kysely";
13
13
  import virtualConfig from "virtual:emdash/config";
14
14
  import { z } from "zod";
15
15
 
16
+ import { assertMediaUsageActivationWriteAllowed } from "./api/media-usage-write-fence.js";
16
17
  import { validateRev } from "./api/rev.js";
17
18
  import type {
18
19
  EmDashConfig,
@@ -22,6 +23,7 @@ import type {
22
23
  import type { EmDashManifest, ManifestCollection } from "./astro/types.js";
23
24
  import { getAuthMode } from "./auth/mode.js";
24
25
  import { getTrustedProxyHeaders } from "./auth/trusted-proxy.js";
26
+ import type { ContentFieldFilters } from "./content-list-query.js";
25
27
  import { isSqlite } from "./database/dialect-helpers.js";
26
28
  import { kyselyLogOption } from "./database/instrumentation.js";
27
29
  import {
@@ -30,23 +32,39 @@ import {
30
32
  runMigrations,
31
33
  } from "./database/migrations/runner.js";
32
34
  import { AuditRepository } from "./database/repositories/audit.js";
35
+ import { ContentRepository } from "./database/repositories/content.js";
33
36
  import { RevisionRepository } from "./database/repositories/revision.js";
37
+ import { ContentMutationConflictError } from "./database/repositories/types.js";
34
38
  import type {
35
39
  ContentItem as ContentItemInternal,
36
40
  ContentDateField,
37
41
  } from "./database/repositories/types.js";
38
- import { validateIdentifier } from "./database/validate.js";
39
42
  import { getI18nConfig } from "./i18n/config.js";
40
43
  import { repairLocaleCasing } from "./i18n/repair-locale-casing.js";
44
+ import { warnAboutUnconfiguredTaxonomyLocales } from "./i18n/taxonomy-locale-diagnostic.js";
41
45
  import { normalizeMediaValue } from "./media/normalize.js";
42
46
  import type { MediaProvider, MediaProviderCapabilities } from "./media/types.js";
47
+ import {
48
+ MEDIA_USAGE_COLLECTION_DELETION_LIMITS,
49
+ processDueMediaUsageCollectionDeletions,
50
+ } from "./media/usage/collection-deletion-processor.js";
43
51
  import {
44
52
  deleteContentMediaUsage,
45
53
  findNonTranslatableSiblingContentIds,
46
54
  markContentMediaUsageCollectionStale,
47
55
  refreshContentMediaUsageAfterWrite,
48
56
  } from "./media/usage/content-refresh.js";
57
+ import {
58
+ MEDIA_USAGE_RECONCILIATION_LIMITS,
59
+ processDueMediaUsageReconciliation,
60
+ } from "./media/usage/reconciliation-processor.js";
61
+ import {
62
+ MEDIA_USAGE_WORK_PROCESSING_LIMITS,
63
+ processDueMediaUsageWork,
64
+ processMediaUsageWorkAfterWrite,
65
+ } from "./media/usage/work-processor.js";
49
66
  import { createSandboxRunnerOptions } from "./plugins/sandbox/runner-options.js";
67
+ import { getSandboxRouteErrorDetails } from "./plugins/sandbox/types.js";
50
68
  import type {
51
69
  SandboxedPluginInstance,
52
70
  SandboxRunner,
@@ -65,8 +83,10 @@ import type {
65
83
  PortableTextBlockConfig,
66
84
  FieldWidgetConfig,
67
85
  SettingField,
86
+ UserInfo,
68
87
  } from "./plugins/types.js";
69
- import type { FieldType } from "./schema/types.js";
88
+ import { recordSchedulerHeartbeatSafely } from "./scheduler-health.js";
89
+ import { MAX_COLLECTION_LIST_COLUMNS, type FieldType } from "./schema/types.js";
70
90
  import { hashString } from "./utils/hash.js";
71
91
  import { createInitLock, type InitLock, initWithLock } from "./utils/init-lock.js";
72
92
  import { createSingleFlightCache, singleFlightCached } from "./utils/single-flight-cache.js";
@@ -182,6 +202,7 @@ import {
182
202
  type Storage,
183
203
  } from "./index.js";
184
204
  import { getDb } from "./loader.js";
205
+ import { isRecord } from "./plugin-utils.js";
185
206
  import { CronExecutor, type InvokeCronHookFn } from "./plugins/cron.js";
186
207
  import { definePlugin } from "./plugins/define-plugin.js";
187
208
  import { DEV_CONSOLE_EMAIL_PLUGIN_ID, devConsoleEmailDeliver } from "./plugins/email-console.js";
@@ -193,9 +214,17 @@ import {
193
214
  } from "./plugins/hooks.js";
194
215
  import { normalizeManifestRoute } from "./plugins/manifest-schema.js";
195
216
  import { extractRequestMeta, sanitizeHeadersForSandbox } from "./plugins/request-meta.js";
196
- import { buildRouteMeta, PluginRouteRegistry, type RouteMeta } from "./plugins/routes.js";
217
+ import {
218
+ buildRouteMeta,
219
+ parseRouteInput,
220
+ PluginRouteRegistry,
221
+ toRouteCallerInfo,
222
+ type RouteCallerInput,
223
+ type RouteMeta,
224
+ } from "./plugins/routes.js";
197
225
  import type { CronScheduler } from "./plugins/scheduler/types.js";
198
226
  import { PluginStateRepository } from "./plugins/state.js";
227
+ import { syncDeclaredStorageIndexes } from "./plugins/storage-indexes.js";
199
228
  import { normalizeRegistryConfig } from "./registry/config.js";
200
229
  import { requestCached } from "./request-cache.js";
201
230
  import { getRequestContext } from "./request-context.js";
@@ -226,6 +255,17 @@ const FIELD_TYPE_TO_KIND: Record<FieldType, string> = {
226
255
  };
227
256
 
228
257
  const DRAFT_ONLY_UPDATE_KEYS = new Set(["data", "slug", "locale", "skipRevision"]);
258
+ const MAX_DRAFT_STAGE_ATTEMPTS = 32;
259
+
260
+ const LIST_COLUMN_FIELD_TYPES: ReadonlySet<FieldType> = new Set([
261
+ "string",
262
+ "number",
263
+ "integer",
264
+ "boolean",
265
+ "datetime",
266
+ "select",
267
+ "multiSelect",
268
+ ]);
229
269
 
230
270
  /**
231
271
  * Sandboxed plugin entry from virtual module
@@ -243,6 +283,10 @@ export interface SandboxedPluginEntry {
243
283
  storage: PluginStorageConfig;
244
284
  /** Serialized MCP declarations emitted at plugin build time. */
245
285
  mcp?: PluginMcpManifestConfig;
286
+ /** Route declarations (name + public/permission/cacheControl), used for route auth decisions */
287
+ routes?: PluginManifest["routes"];
288
+ /** Hook declarations this plugin implements */
289
+ hooks?: PluginManifest["hooks"];
246
290
  /** Admin pages */
247
291
  adminPages?: Array<{ path: string; label?: string; icon?: string }>;
248
292
  /** Dashboard widgets */
@@ -370,6 +414,7 @@ export interface EmDashRuntimeParts {
370
414
  pipelineFactoryOptions: {
371
415
  db: Kysely<Database>;
372
416
  getDb?: () => Kysely<Database>;
417
+ beforeContentWrite?: () => Promise<void>;
373
418
  storage?: Storage;
374
419
  siteInfo?: {
375
420
  siteName?: string;
@@ -494,12 +539,69 @@ const marketplaceManifestCache = new Map<
494
539
  settingsSchema?: Record<string, SettingField>;
495
540
  };
496
541
  mcp?: PluginMcpManifestConfig;
542
+ storage?: PluginManifest["storage"];
497
543
  }
498
544
  >();
499
545
  /** Route metadata for sandboxed plugins: pluginId -> routeName -> RouteMeta */
500
546
  const sandboxedRouteMetaCache = new Map<string, Map<string, RouteMeta>>();
501
547
  let sandboxRunner: SandboxRunner | null = null;
502
548
 
549
+ export const MEDIA_USAGE_MAINTENANCE_QUERY_RESERVATIONS = Object.freeze({
550
+ entryWork: MEDIA_USAGE_WORK_PROCESSING_LIMITS.ordinaryStatementsPerJob,
551
+ collectionDeletion: MEDIA_USAGE_COLLECTION_DELETION_LIMITS.maxQueriesPerTick,
552
+ reconciliation: MEDIA_USAGE_RECONCILIATION_LIMITS.maxQueriesPerTick,
553
+ maxClassQueries: Math.max(
554
+ MEDIA_USAGE_WORK_PROCESSING_LIMITS.ordinaryStatementsPerJob,
555
+ MEDIA_USAGE_COLLECTION_DELETION_LIMITS.maxQueriesPerTick,
556
+ MEDIA_USAGE_RECONCILIATION_LIMITS.maxQueriesPerTick,
557
+ ),
558
+ eventCeiling: 40,
559
+ });
560
+
561
+ export type MediaUsageMaintenanceTaskClass =
562
+ | "entry_work"
563
+ | "collection_deletion"
564
+ | "reconciliation";
565
+
566
+ export type MediaUsageMaintenanceResult =
567
+ | { outcome: "inactive" | "admission_closed"; taskClass: null; turn: null }
568
+ | { outcome: "processed"; taskClass: MediaUsageMaintenanceTaskClass; turn: number };
569
+
570
+ async function runScheduledMediaUsageLane(
571
+ db: Kysely<Database>,
572
+ ): Promise<MediaUsageMaintenanceResult> {
573
+ const queriesAlreadySpent = getRequestContext()?.metrics?.dbCount ?? 0;
574
+ if (
575
+ queriesAlreadySpent + 1 + MEDIA_USAGE_MAINTENANCE_QUERY_RESERVATIONS.maxClassQueries >
576
+ MEDIA_USAGE_MAINTENANCE_QUERY_RESERVATIONS.eventCeiling
577
+ ) {
578
+ return { outcome: "admission_closed", taskClass: null, turn: null };
579
+ }
580
+
581
+ const activation = await db
582
+ .updateTable("_emdash_media_usage_activation")
583
+ .set({
584
+ media_usage_maintenance_turn: sql<number>`(media_usage_maintenance_turn + 1) % 3`,
585
+ })
586
+ .where("task_key", "=", "incremental_capture")
587
+ .where("state", "=", "active")
588
+ .returning("media_usage_maintenance_turn")
589
+ .executeTakeFirst();
590
+ if (!activation) return { outcome: "inactive", taskClass: null, turn: null };
591
+
592
+ const turn = activation.media_usage_maintenance_turn;
593
+ if (turn === 0) {
594
+ await processDueMediaUsageWork(db);
595
+ return { outcome: "processed", taskClass: "entry_work", turn };
596
+ }
597
+ if (turn === 1) {
598
+ await processDueMediaUsageCollectionDeletions(db);
599
+ return { outcome: "processed", taskClass: "collection_deletion", turn };
600
+ }
601
+ await processDueMediaUsageReconciliation(db);
602
+ return { outcome: "processed", taskClass: "reconciliation", turn };
603
+ }
604
+
503
605
  /**
504
606
  * EmDashRuntime - singleton per worker
505
607
  */
@@ -555,10 +657,13 @@ export class EmDashRuntime {
555
657
  /** All plugins eligible for the hook pipeline (includes built-in plugins).
556
658
  * Stored so we can rebuild the pipeline when plugins are enabled/disabled. */
557
659
  private allPipelinePlugins: ResolvedPlugin[];
660
+ /** Guards the once-per-process plugin storage-index sync. */
661
+ private storageIndexesSynced = false;
558
662
  /** Factory options for the hook pipeline context factory */
559
663
  private pipelineFactoryOptions: {
560
664
  db: Kysely<Database>;
561
665
  getDb?: () => Kysely<Database>;
666
+ beforeContentWrite?: () => Promise<void>;
562
667
  storage?: Storage;
563
668
  siteInfo?: {
564
669
  siteName?: string;
@@ -631,8 +736,16 @@ export class EmDashRuntime {
631
736
  * Returns the items promoted so callers can invalidate their cache tags.
632
737
  */
633
738
  async publishScheduled(): Promise<PublishedRef[]> {
739
+ return this.publishScheduledWithFence();
740
+ }
741
+
742
+ private async publishScheduledWithFence(
743
+ onPublished?: (refs: PublishedRef[]) => Promise<void>,
744
+ ): Promise<PublishedRef[]> {
745
+ await assertMediaUsageActivationWriteAllowed(this.db);
634
746
  return publishDueContent(this.db, {
635
747
  publish: (collection, id, options) => this.handleContentPublish(collection, id, options),
748
+ onPublished,
636
749
  });
637
750
  }
638
751
 
@@ -670,11 +783,7 @@ export class EmDashRuntime {
670
783
 
671
784
  let published: PublishedRef[] = [];
672
785
  try {
673
- // Route through the runtime wrapper so content:afterPublish hooks fire.
674
- published = await publishDueContent(this.db, {
675
- publish: (collection, id, opts) => this.handleContentPublish(collection, id, opts),
676
- onPublished: options.onPublished,
677
- });
786
+ published = await this.publishScheduledWithFence(options.onPublished);
678
787
  } catch (error) {
679
788
  console.error("[scheduled-publish] Sweep failed:", error);
680
789
  }
@@ -685,12 +794,41 @@ export class EmDashRuntime {
685
794
  console.error("[cleanup] System cleanup failed:", error);
686
795
  }
687
796
 
797
+ try {
798
+ await this.syncPluginStorageIndexesOnce();
799
+ } catch (error) {
800
+ console.error("[plugins] Storage index sync failed:", error);
801
+ }
802
+
688
803
  // Never throws; no-op unless scheduled backups are enabled and due.
689
804
  await maybeRunScheduledBackup(this.db, this.storage ?? undefined);
805
+ await recordSchedulerHeartbeatSafely(this.db);
690
806
 
691
807
  return { published };
692
808
  }
693
809
 
810
+ async runScheduledMediaUsageTasks(): Promise<MediaUsageMaintenanceResult> {
811
+ return runScheduledMediaUsageLane(this.db);
812
+ }
813
+
814
+ /**
815
+ * Materialize plugin-declared storage indexes, once per process.
816
+ *
817
+ * Called from the scheduler path, not from request handlers — configured
818
+ * plugins have no install handler, so the tick is their only sync moment.
819
+ */
820
+ async syncPluginStorageIndexesOnce(): Promise<void> {
821
+ if (this.storageIndexesSynced) return;
822
+ this.storageIndexesSynced = true;
823
+ // Sandboxed marketplace/registry plugins never join allPipelinePlugins;
824
+ // their manifests are cached at bundle load. Without them, plugins
825
+ // installed before this feature shipped would never get their indexes.
826
+ await syncDeclaredStorageIndexes(this.db, [
827
+ ...this.allPipelinePlugins,
828
+ ...marketplaceManifestCache.values(),
829
+ ]);
830
+ }
831
+
694
832
  /**
695
833
  * Stop the cron scheduler gracefully.
696
834
  * Call during worker shutdown or hot-reload.
@@ -898,6 +1036,7 @@ export class EmDashRuntime {
898
1036
  version: bundle.manifest.version,
899
1037
  admin: bundle.manifest.admin,
900
1038
  mcp: bundle.manifest.mcp,
1039
+ storage: bundle.manifest.storage,
901
1040
  });
902
1041
 
903
1042
  // Cache route metadata from manifest for auth decisions
@@ -1013,6 +1152,7 @@ export class EmDashRuntime {
1013
1152
  version: bundle.manifest.version,
1014
1153
  admin: bundle.manifest.admin,
1015
1154
  mcp: bundle.manifest.mcp,
1155
+ storage: bundle.manifest.storage,
1016
1156
  });
1017
1157
  if (bundle.manifest.routes.length > 0) {
1018
1158
  const routeMetaMap = new Map<string, RouteMeta>();
@@ -1444,6 +1584,7 @@ export class EmDashRuntime {
1444
1584
  const pipelineFactoryOptions = {
1445
1585
  db,
1446
1586
  getDb: resolveDb,
1587
+ beforeContentWrite: () => assertMediaUsageActivationWriteAllowed(resolveDb()),
1447
1588
  storage: storage ?? undefined,
1448
1589
  siteInfo,
1449
1590
  };
@@ -1589,6 +1730,14 @@ export class EmDashRuntime {
1589
1730
  if (deps.createScheduler) {
1590
1731
  const scheduler = deps.createScheduler(cronExecutor);
1591
1732
  cronScheduler = scheduler;
1733
+ const runMediaUsageMaintenance = async () => {
1734
+ const runtime = runtimeRef.current;
1735
+ if (runtime) {
1736
+ await runtime.runScheduledMediaUsageTasks();
1737
+ } else {
1738
+ await runScheduledMediaUsageLane(db);
1739
+ }
1740
+ };
1592
1741
 
1593
1742
  // Run scheduled publishing and system cleanup alongside each tick.
1594
1743
  // Pass storage so cleanupPendingUploads can delete orphaned files.
@@ -1598,12 +1747,12 @@ export class EmDashRuntime {
1598
1747
  // Falls back to the raw handler if (improbably) the tick beats
1599
1748
  // the post-construction ref assignment.
1600
1749
  const runtime = runtimeRef.current;
1601
- await publishDueContent(db, {
1602
- publish: runtime
1603
- ? (collection, id, options) =>
1604
- runtime.handleContentPublish(collection, id, options)
1605
- : undefined,
1606
- });
1750
+ if (runtime) {
1751
+ await runtime.publishScheduled();
1752
+ } else {
1753
+ await assertMediaUsageActivationWriteAllowed(db);
1754
+ await publishDueContent(db);
1755
+ }
1607
1756
  } catch (error) {
1608
1757
  console.error("[scheduled-publish] Sweep failed:", error);
1609
1758
  }
@@ -1614,9 +1763,23 @@ export class EmDashRuntime {
1614
1763
  // by runSystemCleanup. This catches unexpected errors.
1615
1764
  console.error("[cleanup] System cleanup failed:", error);
1616
1765
  }
1766
+ try {
1767
+ await runtimeRef.current?.syncPluginStorageIndexesOnce();
1768
+ } catch (error) {
1769
+ console.error("[plugins] Storage index sync failed:", error);
1770
+ }
1617
1771
  // Never throws; no-op unless scheduled backups are enabled and due.
1618
1772
  await maybeRunScheduledBackup(db, storage ?? undefined);
1773
+ await recordSchedulerHeartbeatSafely(db);
1774
+ if (!scheduler.setMediaUsageMaintenance) {
1775
+ try {
1776
+ await runMediaUsageMaintenance();
1777
+ } catch (error) {
1778
+ console.error("[media-usage] Scheduled maintenance failed:", error);
1779
+ }
1780
+ }
1619
1781
  });
1782
+ scheduler.setMediaUsageMaintenance?.(runMediaUsageMaintenance);
1620
1783
 
1621
1784
  // start() is void on the timer scheduler but the interface
1622
1785
  // allows a promise (alarm-backed schedulers); we don't block on it.
@@ -1920,6 +2083,7 @@ export class EmDashRuntime {
1920
2083
  createSandboxRunnerOptions(
1921
2084
  {
1922
2085
  db,
2086
+ beforeContentWrite: () => assertMediaUsageActivationWriteAllowed(db),
1923
2087
  mediaStorage: mediaStorage
1924
2088
  ? {
1925
2089
  upload: (opts) =>
@@ -1979,8 +2143,8 @@ export class EmDashRuntime {
1979
2143
  capabilities: entry.capabilities ?? [],
1980
2144
  allowedHosts: entry.allowedHosts ?? [],
1981
2145
  storage: entry.storage ?? {},
1982
- hooks: [],
1983
- routes: [],
2146
+ hooks: entry.hooks ?? [],
2147
+ routes: entry.routes ?? [],
1984
2148
  admin: {},
1985
2149
  mcp: entry.mcp,
1986
2150
  };
@@ -1990,6 +2154,17 @@ export class EmDashRuntime {
1990
2154
  console.log(
1991
2155
  `EmDash: Loaded sandboxed plugin ${pluginKey} with capabilities: [${manifest.capabilities.join(", ")}]`,
1992
2156
  );
2157
+
2158
+ if (manifest.routes.length > 0) {
2159
+ const routeMetaMap = new Map<string, RouteMeta>();
2160
+ for (const routeEntry of manifest.routes) {
2161
+ const normalized = normalizeManifestRoute(routeEntry);
2162
+ routeMetaMap.set(normalized.name, buildRouteMeta(normalized));
2163
+ }
2164
+ sandboxedRouteMetaCache.set(entry.id, routeMetaMap);
2165
+ } else {
2166
+ sandboxedRouteMetaCache.delete(entry.id);
2167
+ }
1993
2168
  } catch (error) {
1994
2169
  console.error(`EmDash: Failed to load sandboxed plugin ${entry.id}:`, error);
1995
2170
  }
@@ -2032,6 +2207,7 @@ export class EmDashRuntime {
2032
2207
  createSandboxRunnerOptions(
2033
2208
  {
2034
2209
  db,
2210
+ beforeContentWrite: () => assertMediaUsageActivationWriteAllowed(db),
2035
2211
  mediaStorage: {
2036
2212
  upload: (opts) =>
2037
2213
  storage.upload({
@@ -2092,6 +2268,7 @@ export class EmDashRuntime {
2092
2268
  version: bundle.manifest.version,
2093
2269
  admin: bundle.manifest.admin,
2094
2270
  mcp: bundle.manifest.mcp,
2271
+ storage: bundle.manifest.storage,
2095
2272
  });
2096
2273
 
2097
2274
  // Cache route metadata from manifest for auth decisions
@@ -2164,6 +2341,7 @@ export class EmDashRuntime {
2164
2341
  version: bundle.manifest.version,
2165
2342
  admin: bundle.manifest.admin,
2166
2343
  mcp: bundle.manifest.mcp,
2344
+ storage: bundle.manifest.storage,
2167
2345
  });
2168
2346
  if (bundle.manifest.routes.length > 0) {
2169
2347
  const routeMeta = new Map<string, RouteMeta>();
@@ -2358,12 +2536,38 @@ export class EmDashRuntime {
2358
2536
  fields[field.slug] = entry;
2359
2537
  }
2360
2538
 
2539
+ const configuredListColumns = collection.admin?.listColumns ?? [];
2540
+ const fieldTypes = new Map(collection.fields.map((field) => [field.slug, field.type]));
2541
+ const listColumns: string[] = [];
2542
+ for (const slug of configuredListColumns) {
2543
+ if (listColumns.includes(slug)) continue;
2544
+ const fieldType = fieldTypes.get(slug);
2545
+ if (!fieldType || !LIST_COLUMN_FIELD_TYPES.has(fieldType)) {
2546
+ console.warn(
2547
+ `EmDash: Ignoring unsupported or unknown list column "${slug}" in collection "${collection.slug}".`,
2548
+ );
2549
+ continue;
2550
+ }
2551
+ if (listColumns.length >= MAX_COLLECTION_LIST_COLUMNS) {
2552
+ console.warn(
2553
+ `EmDash: Collection "${collection.slug}" declares more than ${MAX_COLLECTION_LIST_COLUMNS} list columns; extra columns are ignored.`,
2554
+ );
2555
+ break;
2556
+ }
2557
+ listColumns.push(slug);
2558
+ }
2559
+
2361
2560
  manifestCollections[collection.slug] = {
2362
2561
  label: collection.label,
2363
2562
  labelSingular: collection.labelSingular || collection.label,
2364
2563
  supports: collection.supports || [],
2365
2564
  hasSeo: collection.hasSeo,
2366
2565
  urlPattern: collection.urlPattern,
2566
+ routable: collection.routable !== false,
2567
+ titleField: collection.titleField,
2568
+ dateField: collection.dateField,
2569
+ ...(collection.hidden ? { hidden: true } : {}),
2570
+ listColumns: listColumns.length > 0 ? listColumns : undefined,
2367
2571
  fields,
2368
2572
  };
2369
2573
  }
@@ -2478,29 +2682,48 @@ export class EmDashRuntime {
2478
2682
 
2479
2683
  // Build taxonomies from database
2480
2684
  let manifestTaxonomies: Array<{
2685
+ id: string;
2481
2686
  name: string;
2482
2687
  label: string;
2483
2688
  labelSingular?: string;
2484
2689
  hierarchical: boolean;
2485
2690
  collections: string[];
2691
+ locale: string;
2692
+ translationGroup: string;
2486
2693
  }> = [];
2694
+ let taxonomyDefinitionLocales: string[] = [];
2487
2695
  try {
2488
2696
  const rows = await this.db
2489
2697
  .selectFrom("_emdash_taxonomy_defs")
2490
2698
  .selectAll()
2491
2699
  .orderBy("name")
2492
2700
  .execute();
2701
+ taxonomyDefinitionLocales = rows.map((row) => row.locale);
2493
2702
  manifestTaxonomies = rows.map((row) => ({
2703
+ id: row.id,
2494
2704
  name: row.name,
2495
2705
  label: row.label,
2496
2706
  labelSingular: row.label_singular ?? undefined,
2497
2707
  hierarchical: row.hierarchical === 1,
2498
2708
  collections: parseStringArray(row.collections).toSorted(),
2709
+ locale: row.locale,
2710
+ translationGroup: row.translation_group ?? row.id,
2499
2711
  }));
2500
2712
  } catch (error) {
2501
2713
  console.debug("EmDash: Could not load taxonomy definitions:", error);
2502
2714
  }
2503
2715
 
2716
+ try {
2717
+ const configuredLocales = virtualConfig?.i18n?.locales ?? getI18nConfig()?.locales ?? [];
2718
+ await warnAboutUnconfiguredTaxonomyLocales(
2719
+ this.db,
2720
+ configuredLocales,
2721
+ taxonomyDefinitionLocales,
2722
+ );
2723
+ } catch (error) {
2724
+ console.warn("[i18n] taxonomy locale diagnostic failed:", error);
2725
+ }
2726
+
2504
2727
  // Build manifest hash
2505
2728
  const manifestHash = await hashString(
2506
2729
  JSON.stringify(manifestCollections) +
@@ -2513,7 +2736,7 @@ export class EmDashRuntime {
2513
2736
  const authModeValue = authMode.type === "external" ? authMode.providerType : "passkey";
2514
2737
 
2515
2738
  // Include i18n config if enabled (read from virtual module to avoid SSR module singleton mismatch)
2516
- const i18nConfig = virtualConfig?.i18n;
2739
+ const i18nConfig = virtualConfig?.i18n ?? getI18nConfig();
2517
2740
  const i18n =
2518
2741
  i18nConfig && i18nConfig.locales && i18nConfig.locales.length > 1
2519
2742
  ? { defaultLocale: i18nConfig.defaultLocale, locales: i18nConfig.locales }
@@ -2535,6 +2758,10 @@ export class EmDashRuntime {
2535
2758
  taxonomies: manifestTaxonomies,
2536
2759
  authMode: authModeValue,
2537
2760
  i18n,
2761
+ contentLocale: {
2762
+ defaultLocale: i18nConfig?.defaultLocale ?? "en",
2763
+ implicit: i18nConfig === null,
2764
+ },
2538
2765
  marketplace: !!this.config.marketplace,
2539
2766
  registry,
2540
2767
  };
@@ -2612,6 +2839,10 @@ export class EmDashRuntime {
2612
2839
  dateField?: ContentDateField;
2613
2840
  dateFrom?: string;
2614
2841
  dateTo?: string;
2842
+ bylines?: string[];
2843
+ bylinesNone?: boolean;
2844
+ includeInferredBylines?: boolean;
2845
+ fieldFilters?: ContentFieldFilters;
2615
2846
  },
2616
2847
  ) {
2617
2848
  return handleContentList(this.db, collection, params);
@@ -2697,7 +2928,7 @@ export class EmDashRuntime {
2697
2928
  collection: string,
2698
2929
  body: {
2699
2930
  data: Record<string, unknown>;
2700
- slug?: string;
2931
+ slug?: string | null;
2701
2932
  status?: string;
2702
2933
  authorId?: string;
2703
2934
  bylines?: Array<{ bylineId: string; roleLabel?: string | null }>;
@@ -2757,7 +2988,7 @@ export class EmDashRuntime {
2757
2988
  id: string,
2758
2989
  body: {
2759
2990
  data?: Record<string, unknown>;
2760
- slug?: string;
2991
+ slug?: string | null;
2761
2992
  status?: string;
2762
2993
  authorId?: string | null;
2763
2994
  bylines?: Array<{ bylineId: string; roleLabel?: string | null }>;
@@ -2771,13 +3002,12 @@ export class EmDashRuntime {
2771
3002
  taxonomies?: Record<string, string[]>;
2772
3003
  publishedAt?: string | null;
2773
3004
  locale?: string;
2774
- /** Skip revision creation (used by autosave) */
3005
+ /** Replace the previous autosave revision after staging this save. */
2775
3006
  skipRevision?: boolean;
2776
3007
  _rev?: string;
2777
3008
  },
2778
3009
  ) {
2779
3010
  // Resolve slug → ID if needed (before any lookups)
2780
- const { ContentRepository } = await import("./database/repositories/content.js");
2781
3011
  const repo = new ContentRepository(this.db);
2782
3012
  const resolvedItem = await repo.findByIdOrSlug(collection, id, body.locale);
2783
3013
  const resolvedId = resolvedItem?.id ?? id;
@@ -2840,81 +3070,119 @@ export class EmDashRuntime {
2840
3070
  let usesDraftRevisions = false;
2841
3071
  let draftStorageChanged = false;
2842
3072
  if (processedData) {
2843
- try {
2844
- const collectionInfo = await this.schemaRegistry.getCollectionWithFields(collection);
2845
- if (collectionInfo?.supports?.includes("revisions")) {
2846
- usesDraftRevisions = true;
2847
- const revisionRepo = new RevisionRepository(this.db);
2848
- // Re-fetch to get latest state (resolvedItem may be stale after _rev check)
2849
- const existing = await repo.findById(collection, resolvedId);
2850
-
2851
- if (existing) {
2852
- // Build the draft data: merge with existing draft revision if one exists,
2853
- // otherwise merge with the published data from the content table
2854
- let baseData: Record<string, unknown>;
2855
- if (existing.draftRevisionId) {
2856
- const draftRevision = await revisionRepo.findById(existing.draftRevisionId);
2857
- baseData = draftRevision?.data ?? existing.data;
2858
- } else {
2859
- baseData = existing.data;
3073
+ const collectionInfo = await this.schemaRegistry.getCollectionWithFields(collection);
3074
+ if (collectionInfo?.supports?.includes("revisions")) {
3075
+ usesDraftRevisions = true;
3076
+ const revisionRepo = new RevisionRepository(this.db);
3077
+ let existing = await repo.findById(collection, resolvedId);
3078
+
3079
+ for (let attempt = 0; existing && attempt < MAX_DRAFT_STAGE_ATTEMPTS; attempt++) {
3080
+ let baseData: Record<string, unknown>;
3081
+ if (existing.draftRevisionId) {
3082
+ const draftRevision = await revisionRepo.findById(existing.draftRevisionId);
3083
+ baseData = draftRevision?.data ?? existing.data;
3084
+ } else {
3085
+ baseData = existing.data;
3086
+ }
3087
+
3088
+ const mergedData = { ...baseData, ...processedData };
3089
+ if (bodyWithoutRev.slug !== undefined) {
3090
+ mergedData._slug = bodyWithoutRev.slug;
3091
+ }
3092
+
3093
+ const revision = await revisionRepo.create({
3094
+ collection,
3095
+ entryId: resolvedId,
3096
+ data: mergedData,
3097
+ authorId: bodyWithoutRev.authorId ?? undefined,
3098
+ });
3099
+
3100
+ let staged: boolean;
3101
+ try {
3102
+ staged = await repo.replaceDraftRevision(collection, resolvedId, revision.id, existing);
3103
+ } catch (error) {
3104
+ try {
3105
+ await revisionRepo.deleteIfUnreferenced(collection, resolvedId, revision.id);
3106
+ } catch (cleanupError) {
3107
+ console.error(
3108
+ `[emdash] Failed to clean up unstaged revision ${revision.id}:`,
3109
+ cleanupError,
3110
+ );
2860
3111
  }
3112
+ throw error;
3113
+ }
2861
3114
 
2862
- // Include slug in the revision data if it changed
2863
- const mergedData = { ...baseData, ...processedData };
2864
- if (bodyWithoutRev.slug !== undefined) {
2865
- mergedData._slug = bodyWithoutRev.slug;
3115
+ if (!staged) {
3116
+ try {
3117
+ await revisionRepo.deleteIfUnreferenced(collection, resolvedId, revision.id);
3118
+ } catch (cleanupError) {
3119
+ console.error(
3120
+ `[emdash] Failed to clean up unstaged revision ${revision.id}:`,
3121
+ cleanupError,
3122
+ );
3123
+ }
3124
+ if (body._rev || attempt === MAX_DRAFT_STAGE_ATTEMPTS - 1) {
3125
+ const error = new ContentMutationConflictError();
3126
+ return {
3127
+ success: false as const,
3128
+ error: { code: "CONFLICT", message: error.message },
3129
+ };
2866
3130
  }
3131
+ existing = await repo.findById(collection, resolvedId);
3132
+ continue;
3133
+ }
2867
3134
 
2868
- if (bodyWithoutRev.skipRevision && existing.draftRevisionId) {
2869
- // Autosave: update existing draft revision in place
2870
- await revisionRepo.updateData(existing.draftRevisionId, mergedData);
2871
- draftStorageChanged = true;
2872
- } else {
2873
- // Create new draft revision
2874
- const revision = await revisionRepo.create({
3135
+ draftStorageChanged = true;
3136
+
3137
+ if (bodyWithoutRev.skipRevision && existing.draftRevisionId) {
3138
+ try {
3139
+ await revisionRepo.deleteIfUnreferenced(
2875
3140
  collection,
2876
- entryId: resolvedId,
2877
- data: mergedData,
2878
- authorId: bodyWithoutRev.authorId ?? undefined,
2879
- });
2880
-
2881
- // Update entry to point to new draft (metadata only, not data columns).
2882
- // No updated_at stamp: draft staging leaves live content untouched,
2883
- // so public "last modified" consumers must not see a change (#2143).
2884
- validateIdentifier(collection, "collection");
2885
- const tableName = `ec_${collection}`;
2886
- await sql`
2887
- UPDATE ${sql.ref(tableName)}
2888
- SET draft_revision_id = ${revision.id}
2889
- WHERE id = ${resolvedId}
2890
- `.execute(this.db);
2891
- draftStorageChanged = true;
2892
-
2893
- // Fire-and-forget: prune old revisions to prevent unbounded growth
2894
- void revisionRepo.pruneOldRevisions(collection, resolvedId, 50).catch(() => {});
3141
+ resolvedId,
3142
+ existing.draftRevisionId,
3143
+ );
3144
+ } catch (error) {
3145
+ console.error(
3146
+ `[emdash] Failed to clean up superseded revision ${existing.draftRevisionId}:`,
3147
+ error,
3148
+ );
2895
3149
  }
3150
+ } else {
3151
+ after(async () => {
3152
+ try {
3153
+ await revisionRepo.pruneQueuedEntry(collection, resolvedId, revision.id, 50);
3154
+ } catch (error) {
3155
+ console.error(
3156
+ `[revisions] Failed to prune revisions for ${collection}/${resolvedId}:`,
3157
+ error,
3158
+ );
3159
+ }
3160
+ });
2896
3161
  }
3162
+ break;
2897
3163
  }
2898
- } catch {
2899
- // Don't fail the update if revision creation fails
2900
3164
  }
2901
3165
  }
2902
3166
 
2903
- // Update the content table:
2904
- // - If collection uses draft revisions: only update metadata (no data fields, no slug)
2905
- // - Otherwise: update everything as before
2906
- const result = await handleContentUpdate(this.db, collection, resolvedId, {
2907
- ...bodyWithoutRev,
2908
- data: usesDraftRevisions ? undefined : processedData,
2909
- slug: usesDraftRevisions ? undefined : bodyWithoutRev.slug,
2910
- authorId: bodyWithoutRev.authorId,
2911
- bylines: bodyWithoutRev.bylines,
2912
- });
2913
-
2914
3167
  // Public HTML comes from live columns / SEO / taxonomies, not draft revisions.
2915
3168
  const liveMetaTouched = Object.entries(bodyWithoutRev).some(
2916
3169
  ([key, value]) => value !== undefined && !DRAFT_ONLY_UPDATE_KEYS.has(key),
2917
3170
  );
3171
+
3172
+ // Update the content table:
3173
+ // - If collection uses draft revisions: only update metadata (no data fields, no slug)
3174
+ // - Otherwise: update everything as before
3175
+ const result =
3176
+ usesDraftRevisions && !liveMetaTouched
3177
+ ? await handleContentGet(this.db, collection, resolvedId)
3178
+ : await handleContentUpdate(this.db, collection, resolvedId, {
3179
+ ...bodyWithoutRev,
3180
+ data: usesDraftRevisions ? undefined : processedData,
3181
+ slug: usesDraftRevisions ? undefined : bodyWithoutRev.slug,
3182
+ authorId: bodyWithoutRev.authorId,
3183
+ bylines: bodyWithoutRev.bylines,
3184
+ });
3185
+
2918
3186
  const liveContentChanged = usesDraftRevisions
2919
3187
  ? liveMetaTouched
2920
3188
  : Boolean(processedData || bodyWithoutRev.slug !== undefined || liveMetaTouched);
@@ -3072,7 +3340,11 @@ export class EmDashRuntime {
3072
3340
  async handleContentPublish(
3073
3341
  collection: string,
3074
3342
  id: string,
3075
- options: { publishedAt?: string; requireScheduledDue?: boolean } = {},
3343
+ options: {
3344
+ publishedAt?: string;
3345
+ requireScheduledDue?: boolean;
3346
+ expectedScheduledAt?: string;
3347
+ } = {},
3076
3348
  ) {
3077
3349
  const result = await handleContentPublish(this.db, collection, id, options);
3078
3350
  if (result.success && result.data) {
@@ -3295,6 +3567,18 @@ export class EmDashRuntime {
3295
3567
  // must then `content_publish` to promote the restored draft to
3296
3568
  // live, matching the documented tool contract.
3297
3569
  try {
3570
+ const contentRepo = new ContentRepository(this.db);
3571
+ const existing = await contentRepo.findById(revision.collection, revision.entryId);
3572
+ if (!existing) {
3573
+ return {
3574
+ success: false as const,
3575
+ error: {
3576
+ code: "NOT_FOUND",
3577
+ message: `Content item not found: ${revision.entryId}`,
3578
+ },
3579
+ };
3580
+ }
3581
+
3298
3582
  const newDraft = await revisionRepo.create({
3299
3583
  collection: revision.collection,
3300
3584
  entryId: revision.entryId,
@@ -3302,18 +3586,45 @@ export class EmDashRuntime {
3302
3586
  authorId: callerUserId,
3303
3587
  });
3304
3588
 
3305
- validateIdentifier(revision.collection, "collection");
3306
- const tableName = `ec_${revision.collection}`;
3307
- await sql`
3308
- UPDATE ${sql.ref(tableName)}
3309
- SET draft_revision_id = ${newDraft.id}
3310
- WHERE id = ${revision.entryId}
3311
- `.execute(this.db);
3589
+ try {
3590
+ const staged = await contentRepo.replaceDraftRevision(
3591
+ revision.collection,
3592
+ revision.entryId,
3593
+ newDraft.id,
3594
+ existing,
3595
+ );
3596
+ if (!staged) throw new ContentMutationConflictError();
3597
+ } catch (error) {
3598
+ try {
3599
+ await revisionRepo.deleteIfUnreferenced(
3600
+ revision.collection,
3601
+ revision.entryId,
3602
+ newDraft.id,
3603
+ );
3604
+ } catch (cleanupError) {
3605
+ console.error(
3606
+ `[emdash] Failed to clean up unrestored revision ${newDraft.id}:`,
3607
+ cleanupError,
3608
+ );
3609
+ }
3610
+ throw error;
3611
+ }
3312
3612
 
3313
- // Fire-and-forget: prune old revisions to prevent unbounded growth
3314
- void revisionRepo
3315
- .pruneOldRevisions(revision.collection, revision.entryId, 50)
3316
- .catch(() => {});
3613
+ after(async () => {
3614
+ try {
3615
+ await revisionRepo.pruneQueuedEntry(
3616
+ revision.collection,
3617
+ revision.entryId,
3618
+ newDraft.id,
3619
+ 50,
3620
+ );
3621
+ } catch (error) {
3622
+ console.error(
3623
+ `[revisions] Failed to prune revisions for ${revision.collection}/${revision.entryId}:`,
3624
+ error,
3625
+ );
3626
+ }
3627
+ });
3317
3628
 
3318
3629
  // Return the freshly-fetched item with the new draft hydrated
3319
3630
  // onto `data`. Without this the response would echo the live
@@ -3326,6 +3637,12 @@ export class EmDashRuntime {
3326
3637
  }
3327
3638
  return hydrated;
3328
3639
  } catch (error) {
3640
+ if (error instanceof ContentMutationConflictError) {
3641
+ return {
3642
+ success: false as const,
3643
+ error: { code: "CONFLICT", message: error.message },
3644
+ };
3645
+ }
3329
3646
  console.error("[emdash] revision restore failed:", error);
3330
3647
  return {
3331
3648
  success: false as const,
@@ -3343,12 +3660,15 @@ export class EmDashRuntime {
3343
3660
  ): Promise<void> {
3344
3661
  for (const contentId of new Set(contentIds)) {
3345
3662
  try {
3663
+ const work = await processMediaUsageWorkAfterWrite(this.db, collection, contentId);
3664
+ if (work.outcome !== "inactive") return;
3346
3665
  await refreshContentMediaUsageAfterWrite(this.db, collection, contentId);
3347
3666
  } catch (error) {
3348
3667
  console.error(
3349
3668
  `[media-usage] Failed after content write ${collection}/${contentId}:`,
3350
3669
  error,
3351
3670
  );
3671
+ return;
3352
3672
  }
3353
3673
  }
3354
3674
  }
@@ -3358,6 +3678,8 @@ export class EmDashRuntime {
3358
3678
  contentId: string,
3359
3679
  ): Promise<void> {
3360
3680
  try {
3681
+ const work = await processMediaUsageWorkAfterWrite(this.db, collection, contentId);
3682
+ if (work.outcome !== "inactive") return;
3361
3683
  const result = await deleteContentMediaUsage(this.db, collection, contentId);
3362
3684
  if (!result.success) {
3363
3685
  console.error(
@@ -3437,7 +3759,13 @@ export class EmDashRuntime {
3437
3759
  return meta.admin?.settingsSchema ?? {};
3438
3760
  }
3439
3761
 
3440
- async handlePluginApiRoute(pluginId: string, _method: string, path: string, request: Request) {
3762
+ async handlePluginApiRoute(
3763
+ pluginId: string,
3764
+ _method: string,
3765
+ path: string,
3766
+ request: Request,
3767
+ user?: RouteCallerInput | null,
3768
+ ) {
3441
3769
  if (!this.isPluginEnabled(pluginId)) {
3442
3770
  return {
3443
3771
  success: false,
@@ -3445,6 +3773,11 @@ export class EmDashRuntime {
3445
3773
  };
3446
3774
  }
3447
3775
 
3776
+ // Authenticated caller for `ctx.user`. Undefined for public routes
3777
+ // (the catch-all only forwards the caller after private-route auth)
3778
+ // and for machine tokens with no bound user.
3779
+ const caller = user ? toRouteCallerInfo(user) : undefined;
3780
+
3448
3781
  // Check trusted (configured) plugins first — this must match the
3449
3782
  // resolution order in getPluginRouteMeta to avoid auth/execution mismatches.
3450
3783
  const trustedPlugin = this.configuredPlugins.find((p) => p.id === pluginId);
@@ -3459,20 +3792,16 @@ export class EmDashRuntime {
3459
3792
 
3460
3793
  const routeKey = path.replace(LEADING_SLASH_PATTERN, "");
3461
3794
 
3462
- let body: unknown = undefined;
3463
- try {
3464
- body = await request.json();
3465
- } catch {
3466
- // No body or not JSON
3467
- }
3795
+ // Body methods parse JSON; GET/HEAD/DELETE parse the query string (#2146).
3796
+ const body = await parseRouteInput(request);
3468
3797
 
3469
- return routeRegistry.invoke(pluginId, routeKey, { request, body });
3798
+ return routeRegistry.invoke(pluginId, routeKey, { request, body, user: caller });
3470
3799
  }
3471
3800
 
3472
3801
  // Check sandboxed (marketplace) plugins second
3473
3802
  const sandboxedPlugin = this.findSandboxedPlugin(pluginId);
3474
3803
  if (sandboxedPlugin) {
3475
- return this.handleSandboxedRoute(sandboxedPlugin, path, request);
3804
+ return this.handleSandboxedRoute(sandboxedPlugin, path, request, caller);
3476
3805
  }
3477
3806
 
3478
3807
  return {
@@ -3599,6 +3928,7 @@ export class EmDashRuntime {
3599
3928
  input: unknown,
3600
3929
  actorId: string,
3601
3930
  request: Request,
3931
+ caller?: RouteCallerInput | null,
3602
3932
  ) {
3603
3933
  const requestMeta = extractRequestMeta(request, getTrustedProxyHeaders(this.config));
3604
3934
  const audit = new AuditRepository(this.db);
@@ -3610,7 +3940,13 @@ export class EmDashRuntime {
3610
3940
  headers,
3611
3941
  body: JSON.stringify(input),
3612
3942
  });
3613
- const result = await this.handlePluginApiRoute(pluginId, "POST", route, internalRequest);
3943
+ const result = await this.handlePluginApiRoute(
3944
+ pluginId,
3945
+ "POST",
3946
+ route,
3947
+ internalRequest,
3948
+ caller,
3949
+ );
3614
3950
  await audit.log({
3615
3951
  actorId,
3616
3952
  actorIp: requestMeta.ip ?? undefined,
@@ -3675,7 +4011,13 @@ export class EmDashRuntime {
3675
4011
  const imageFields = collectionInfo.fields.filter(
3676
4012
  (f) => f.type === "image" || f.type === "file",
3677
4013
  );
3678
- if (imageFields.length === 0) return data;
4014
+ // Repeater fields can contain image sub-fields, whose values need the same normalization
4015
+ // (a bare media id posted inside a repeater item would otherwise be stored verbatim and
4016
+ // render as "Image not found" in the admin).
4017
+ const repeaterFields = collectionInfo.fields.filter(
4018
+ (f) => f.type === "repeater" && Array.isArray(f.validation?.subFields),
4019
+ );
4020
+ if (imageFields.length === 0 && repeaterFields.length === 0) return data;
3679
4021
 
3680
4022
  const getProvider = (id: string) => this.getMediaProvider(id);
3681
4023
  const result = { ...data };
@@ -3694,6 +4036,37 @@ export class EmDashRuntime {
3694
4036
  }
3695
4037
  }
3696
4038
 
4039
+ for (const field of repeaterFields) {
4040
+ const value = result[field.slug];
4041
+ if (!Array.isArray(value)) continue;
4042
+
4043
+ const mediaSubFieldSlugs = (field.validation?.subFields ?? [])
4044
+ .filter((sub) => sub.type === "image")
4045
+ .map((sub) => sub.slug);
4046
+ if (mediaSubFieldSlugs.length === 0) continue;
4047
+
4048
+ const items: unknown[] = value;
4049
+ result[field.slug] = await Promise.all(
4050
+ items.map(async (item) => {
4051
+ if (!isRecord(item)) return item;
4052
+ const normalizedItem: Record<string, unknown> = { ...item };
4053
+ for (const slug of mediaSubFieldSlugs) {
4054
+ const subValue = normalizedItem[slug];
4055
+ if (subValue == null) continue;
4056
+ try {
4057
+ const normalized = await normalizeMediaValue(subValue, getProvider);
4058
+ if (normalized) {
4059
+ normalizedItem[slug] = normalized;
4060
+ }
4061
+ } catch {
4062
+ // Don't fail the save if normalization fails for a single sub-field
4063
+ }
4064
+ }
4065
+ return normalizedItem;
4066
+ }),
4067
+ );
4068
+ }
4069
+
3697
4070
  return result;
3698
4071
  }
3699
4072
 
@@ -3899,19 +4272,17 @@ export class EmDashRuntime {
3899
4272
  plugin: SandboxedPluginInstance,
3900
4273
  path: string,
3901
4274
  request: Request,
4275
+ user?: UserInfo,
3902
4276
  ): Promise<{
3903
4277
  success: boolean;
3904
4278
  data?: unknown;
3905
4279
  error?: { code: string; message: string };
4280
+ status?: number;
3906
4281
  }> {
3907
4282
  const routeName = path.replace(LEADING_SLASH_PATTERN, "");
3908
4283
 
3909
- let body: unknown = undefined;
3910
- try {
3911
- body = await request.json();
3912
- } catch {
3913
- // No body or not JSON
3914
- }
4284
+ // Body methods parse JSON; GET/HEAD/DELETE parse the query string (#2146).
4285
+ const body = await parseRouteInput(request);
3915
4286
 
3916
4287
  try {
3917
4288
  const headers = sanitizeHeadersForSandbox(request.headers);
@@ -3921,10 +4292,22 @@ export class EmDashRuntime {
3921
4292
  method: request.method,
3922
4293
  headers,
3923
4294
  meta,
4295
+ user,
3924
4296
  });
3925
4297
  return { success: true, data: result };
3926
4298
  } catch (error) {
3927
4299
  console.error(`EmDash: Sandboxed plugin route error:`, error);
4300
+ const sandboxRouteError = getSandboxRouteErrorDetails(error);
4301
+ if (sandboxRouteError) {
4302
+ return {
4303
+ success: false,
4304
+ status: sandboxRouteError.status,
4305
+ error: {
4306
+ code: sandboxRouteError.code,
4307
+ message: sandboxRouteError.message,
4308
+ },
4309
+ };
4310
+ }
3928
4311
  return {
3929
4312
  success: false,
3930
4313
  error: {