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
@@ -1,16 +1,18 @@
1
- import "../../../../../base64-B-PsqheR.mjs";
2
- import "../../../../../types-XrQQ-Aex.mjs";
3
- import { t as MediaRepository } from "../../../../../media-tQDZEdu7.mjs";
4
- import { t as enrichImageMetadata } from "../../../../../enrich-CFJJgxs_.mjs";
5
- import { n as computeContentHash, t as MAX_CONTENT_HASH_BYTES } from "../../../../../hash-Cb7U1b5M.mjs";
6
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DmmN74gW.mjs";
7
- import { r as parseOptionalBody, t as isParseError } from "../../../../../parse-BL49yb9D.mjs";
8
- import { i as mediaConfirmBody } from "../../../../../media-DWIzoVFL.mjs";
9
- import "../../../../../redirects-BOn2DIUs.mjs";
10
- import "../../../../../relations-C4duJnwI.mjs";
11
- import "../../../../../status-COfycGIU.mjs";
1
+ import "../../../../../config-C3EBstHi.mjs";
2
+ import "../../../../../base64-CvLhUKQv.mjs";
3
+ import "../../../../../types-CQPWhPxE.mjs";
4
+ import { t as MediaRepository } from "../../../../../media-CzhAn2--.mjs";
5
+ import "../../../../../dist-D_6CKR8z.mjs";
6
+ import { t as enrichImageMetadata } from "../../../../../enrich-_ySMEvEE.mjs";
7
+ import { n as computeContentHash, t as MAX_CONTENT_HASH_BYTES } from "../../../../../hash-Cg0TXJPV.mjs";
8
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DupyJZE2.mjs";
9
+ import { r as parseOptionalBody, t as isParseError } from "../../../../../parse-DFbuc-Fh.mjs";
10
+ import { i as mediaConfirmBody } from "../../../../../media-BWWWb8kg.mjs";
11
+ import "../../../../../redirects-cV9g7V-0.mjs";
12
+ import "../../../../../relations-BBApI4e2.mjs";
13
+ import "../../../../../status-CWVYpbD8.mjs";
12
14
  import "../../../../../api/schemas/index.mjs";
13
- import { n as requireOwnerPerm, r as requirePerm } from "../../../../../authorize-R5iYYrpa.mjs";
15
+ import { n as requireOwnerPerm, r as requirePerm } from "../../../../../authorize-1iFJzPaZ.mjs";
14
16
 
15
17
  //#region src/astro/routes/api/media/[id]/confirm.ts
16
18
  const prerender = false;
@@ -1 +1 @@
1
- {"version":3,"file":"confirm.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/confirm.ts"],"sourcesContent":["/**\n * Confirm media upload endpoint\n *\n * POST /_emdash/api/media/{id}/confirm\n *\n * Confirms that the client has successfully uploaded the file to storage.\n * Marks the media record as ready and optionally updates metadata.\n */\n\nimport type { APIRoute } from \"astro\";\nimport type { DownloadResult } from \"emdash\";\n\nimport { requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { isParseError, parseOptionalBody } from \"#api/parse.js\";\nimport { mediaConfirmBody } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { enrichImageMetadata } from \"#media/enrich.js\";\nimport type { MediaItem } from \"#types\";\nimport { computeContentHash, MAX_CONTENT_HASH_BYTES } from \"#utils/hash.js\";\n\nexport const prerender = false;\n\n/**\n * Max raw bytes to buffer for server-side LQIP generation at confirm time. The\n * signed-URL upload flow exists so large files bypass server buffering — re-reading\n * the whole object into a Worker's 128 MB heap to compute a blurhash would OOM\n * on the very uploads that flow was designed for. LQIP is progressive\n * enhancement: large images simply ship without a server-generated placeholder.\n */\nconst MAX_PLACEHOLDER_DOWNLOAD_BYTES = MAX_CONTENT_HASH_BYTES;\n\n/**\n * Add URL to media item (relative URL for portability)\n */\nfunction addUrlToMedia(item: MediaItem): MediaItem & { url: string } {\n\treturn {\n\t\t...item,\n\t\turl: `/_emdash/api/media/file/${item.storageKey}`,\n\t};\n}\n\nasync function cancelDownload(download: DownloadResult): Promise<void> {\n\ttry {\n\t\tawait download.body.cancel();\n\t} catch (error) {\n\t\tconsole.error(\"[media] confirm download cancellation failed:\", error);\n\t}\n}\n\nasync function forgetUploadAttempt(repo: MediaRepository, storageKey: string): Promise<void> {\n\ttry {\n\t\tawait repo.deleteUploadAttempt(storageKey);\n\t} catch (error) {\n\t\tconsole.error(\"[media] confirm upload attempt cleanup failed:\", error);\n\t}\n}\n\nasync function confirmationConflict(repo: MediaRepository, id: string): Promise<Response> {\n\tconst current = await repo.findById(id);\n\tif (!current) {\n\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t}\n\tif (current.status === \"ready\") {\n\t\tawait forgetUploadAttempt(repo, current.storageKey);\n\t\treturn apiSuccess({ item: addUrlToMedia(current) });\n\t}\n\tif (current.status === \"pending\") {\n\t\treturn apiError(\"INVALID_STATE\", \"Media item changed during confirmation\", 409);\n\t}\n\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${current.status}`, 400);\n}\n\nasync function consumeDownload(download: DownloadResult): Promise<Uint8Array> {\n\tconst reader = download.body.getReader();\n\ttry {\n\t\tconst bytes = new Uint8Array(download.size);\n\t\tlet receivedSize = 0;\n\t\twhile (true) {\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tif (receivedSize + value.byteLength > bytes.byteLength) {\n\t\t\t\tthrow new Error(\"Stored file exceeds its reported size\");\n\t\t\t}\n\t\t\tbytes.set(value, receivedSize);\n\t\t\treceivedSize += value.byteLength;\n\t\t}\n\t\tif (receivedSize !== download.size) {\n\t\t\tthrow new Error(\"Stored file size does not match its reported size\");\n\t\t}\n\t\treturn bytes;\n\t} catch (error) {\n\t\ttry {\n\t\t\tawait reader.cancel(error);\n\t\t} catch (cancelError) {\n\t\t\tconsole.error(\"[media] confirm download cancellation failed:\", cancelError);\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\treader.releaseLock();\n\t}\n}\n\n/**\n * Confirm upload completion\n */\nexport const POST: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID is required\", 400);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\tconst body = await parseOptionalBody(request, mediaConfirmBody, {});\n\t\tif (isParseError(body)) return body;\n\n\t\tconst repo = new MediaRepository(emdash.db);\n\n\t\t// Get the media item first to check status\n\t\tconst existing = await repo.findById(id);\n\t\tif (!existing) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t\t}\n\n\t\t// Only the uploader or a user with media:edit_any can confirm/fail a pending upload\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\texisting.authorId ?? \"\",\n\t\t\t\"media:upload\",\n\t\t\t\"media:edit_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tif (existing.status === \"ready\") {\n\t\t\tawait forgetUploadAttempt(repo, existing.storageKey);\n\t\t\treturn apiSuccess({ item: addUrlToMedia(existing) });\n\t\t}\n\t\tif (existing.status !== \"pending\") {\n\t\t\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${existing.status}`, 400);\n\t\t}\n\n\t\tif (body.size !== undefined && existing.size !== null && body.size !== existing.size) {\n\t\t\treturn apiError(\n\t\t\t\t\"UPLOAD_SIZE_MISMATCH\",\n\t\t\t\t\"Confirmed size does not match the pending media item\",\n\t\t\t\t400,\n\t\t\t);\n\t\t}\n\n\t\tlet confirmedSize = existing.size ?? body.size;\n\t\tlet contentHash = existing.contentHash;\n\t\tlet imageBytes: Uint8Array | undefined;\n\n\t\tif (emdash.storage) {\n\t\t\tconst exists = await emdash.storage.exists(existing.storageKey);\n\t\t\tif (!exists) {\n\t\t\t\tconst failed = await repo.markFailed(id, existing.storageKey);\n\t\t\t\tif (!failed) return await confirmationConflict(repo, id);\n\t\t\t\treturn apiError(\"FILE_NOT_FOUND\", \"File was not uploaded to storage\", 400);\n\t\t\t}\n\n\t\t\tconst storedFile = await emdash.storage.download(existing.storageKey);\n\t\t\tif (confirmedSize !== undefined && storedFile.size !== confirmedSize) {\n\t\t\t\tawait cancelDownload(storedFile);\n\t\t\t\treturn apiError(\n\t\t\t\t\t\"UPLOAD_SIZE_MISMATCH\",\n\t\t\t\t\t\"Stored file size does not match the pending media item\",\n\t\t\t\t\t400,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconfirmedSize = storedFile.size;\n\n\t\t\tconst isImage = existing.mimeType.startsWith(\"image/\");\n\t\t\tconst canBuffer = storedFile.size <= MAX_PLACEHOLDER_DOWNLOAD_BYTES;\n\t\t\tconst hasServerHash =\n\t\t\t\tcontentHash !== null && (await repo.hasUploadAttempt(existing.storageKey));\n\t\t\tif (canBuffer && (isImage || !hasServerHash)) {\n\t\t\t\tconst bytes = await consumeDownload(storedFile);\n\t\t\t\tcontentHash = bytes.byteLength > 0 ? await computeContentHash(bytes) : null;\n\t\t\t\tif (isImage && bytes.byteLength > 0) imageBytes = bytes;\n\t\t\t} else {\n\t\t\t\tif (!hasServerHash) contentHash = null;\n\t\t\t\tawait cancelDownload(storedFile);\n\t\t\t}\n\n\t\t\tif (isImage && !canBuffer) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${storedFile.size} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// LQIP is best-effort; oversized images skip server-side placeholders.\n\t\tlet blurhash: string | undefined;\n\t\tlet dominantColor: string | undefined;\n\t\tlet width = body.width;\n\t\tlet height = body.height;\n\t\tif (imageBytes) {\n\t\t\ttry {\n\t\t\t\tconst enriched = await enrichImageMetadata(imageBytes, existing.mimeType, {\n\t\t\t\t\tknownDimensions:\n\t\t\t\t\t\tbody.width != null && body.height != null\n\t\t\t\t\t\t\t? { width: body.width, height: body.height }\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t});\n\t\t\t\tblurhash = enriched.blurhash;\n\t\t\t\tdominantColor = enriched.dominantColor;\n\t\t\t\twidth = width ?? enriched.width;\n\t\t\t\theight = height ?? enriched.height;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[media] confirm placeholder generation failed:\", error);\n\t\t\t}\n\t\t}\n\n\t\t// Confirm the upload\n\t\tconst item = await repo.confirmUpload(\n\t\t\tid,\n\t\t\t{\n\t\t\t\tsize: confirmedSize,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\tblurhash,\n\t\t\t\tdominantColor,\n\t\t\t\tcontentHash,\n\t\t\t},\n\t\t\texisting.storageKey,\n\t\t);\n\n\t\tif (!item) {\n\t\t\treturn await confirmationConflict(repo, id);\n\t\t}\n\n\t\tawait forgetUploadAttempt(repo, item.storageKey);\n\n\t\t// Add URL to the response (relative URL for portability)\n\t\tconst itemWithUrl = addUrlToMedia(item);\n\n\t\treturn apiSuccess({ item: itemWithUrl });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to confirm upload\", \"CONFIRM_ERROR\");\n\t}\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"confirm.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/confirm.ts"],"sourcesContent":["/**\n * Confirm media upload endpoint\n *\n * POST /_emdash/api/media/{id}/confirm\n *\n * Confirms that the client has successfully uploaded the file to storage.\n * Marks the media record as ready and optionally updates metadata.\n */\n\nimport type { APIRoute } from \"astro\";\nimport type { DownloadResult } from \"emdash\";\n\nimport { requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { isParseError, parseOptionalBody } from \"#api/parse.js\";\nimport { mediaConfirmBody } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { enrichImageMetadata } from \"#media/enrich.js\";\nimport type { MediaItem } from \"#types\";\nimport { computeContentHash, MAX_CONTENT_HASH_BYTES } from \"#utils/hash.js\";\n\nexport const prerender = false;\n\n/**\n * Max raw bytes to buffer for server-side LQIP generation at confirm time. The\n * signed-URL upload flow exists so large files bypass server buffering — re-reading\n * the whole object into a Worker's 128 MB heap to compute a blurhash would OOM\n * on the very uploads that flow was designed for. LQIP is progressive\n * enhancement: large images simply ship without a server-generated placeholder.\n */\nconst MAX_PLACEHOLDER_DOWNLOAD_BYTES = MAX_CONTENT_HASH_BYTES;\n\n/**\n * Add URL to media item (relative URL for portability)\n */\nfunction addUrlToMedia(item: MediaItem): MediaItem & { url: string } {\n\treturn {\n\t\t...item,\n\t\turl: `/_emdash/api/media/file/${item.storageKey}`,\n\t};\n}\n\nasync function cancelDownload(download: DownloadResult): Promise<void> {\n\ttry {\n\t\tawait download.body.cancel();\n\t} catch (error) {\n\t\tconsole.error(\"[media] confirm download cancellation failed:\", error);\n\t}\n}\n\nasync function forgetUploadAttempt(repo: MediaRepository, storageKey: string): Promise<void> {\n\ttry {\n\t\tawait repo.deleteUploadAttempt(storageKey);\n\t} catch (error) {\n\t\tconsole.error(\"[media] confirm upload attempt cleanup failed:\", error);\n\t}\n}\n\nasync function confirmationConflict(repo: MediaRepository, id: string): Promise<Response> {\n\tconst current = await repo.findById(id);\n\tif (!current) {\n\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t}\n\tif (current.status === \"ready\") {\n\t\tawait forgetUploadAttempt(repo, current.storageKey);\n\t\treturn apiSuccess({ item: addUrlToMedia(current) });\n\t}\n\tif (current.status === \"pending\") {\n\t\treturn apiError(\"INVALID_STATE\", \"Media item changed during confirmation\", 409);\n\t}\n\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${current.status}`, 400);\n}\n\nasync function consumeDownload(download: DownloadResult): Promise<Uint8Array> {\n\tconst reader = download.body.getReader();\n\ttry {\n\t\tconst bytes = new Uint8Array(download.size);\n\t\tlet receivedSize = 0;\n\t\twhile (true) {\n\t\t\tconst { done, value } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tif (receivedSize + value.byteLength > bytes.byteLength) {\n\t\t\t\tthrow new Error(\"Stored file exceeds its reported size\");\n\t\t\t}\n\t\t\tbytes.set(value, receivedSize);\n\t\t\treceivedSize += value.byteLength;\n\t\t}\n\t\tif (receivedSize !== download.size) {\n\t\t\tthrow new Error(\"Stored file size does not match its reported size\");\n\t\t}\n\t\treturn bytes;\n\t} catch (error) {\n\t\ttry {\n\t\t\tawait reader.cancel(error);\n\t\t} catch (cancelError) {\n\t\t\tconsole.error(\"[media] confirm download cancellation failed:\", cancelError);\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\treader.releaseLock();\n\t}\n}\n\n/**\n * Confirm upload completion\n */\nexport const POST: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID is required\", 400);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\tconst body = await parseOptionalBody(request, mediaConfirmBody, {});\n\t\tif (isParseError(body)) return body;\n\n\t\tconst repo = new MediaRepository(emdash.db);\n\n\t\t// Get the media item first to check status\n\t\tconst existing = await repo.findById(id);\n\t\tif (!existing) {\n\t\t\treturn apiError(\"NOT_FOUND\", `Media item not found: ${id}`, 404);\n\t\t}\n\n\t\t// Only the uploader or a user with media:edit_any can confirm/fail a pending upload\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\texisting.authorId ?? \"\",\n\t\t\t\"media:upload\",\n\t\t\t\"media:edit_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tif (existing.status === \"ready\") {\n\t\t\tawait forgetUploadAttempt(repo, existing.storageKey);\n\t\t\treturn apiSuccess({ item: addUrlToMedia(existing) });\n\t\t}\n\t\tif (existing.status !== \"pending\") {\n\t\t\treturn apiError(\"INVALID_STATE\", `Media item is not pending: ${existing.status}`, 400);\n\t\t}\n\n\t\tif (body.size !== undefined && existing.size !== null && body.size !== existing.size) {\n\t\t\treturn apiError(\n\t\t\t\t\"UPLOAD_SIZE_MISMATCH\",\n\t\t\t\t\"Confirmed size does not match the pending media item\",\n\t\t\t\t400,\n\t\t\t);\n\t\t}\n\n\t\tlet confirmedSize = existing.size ?? body.size;\n\t\tlet contentHash = existing.contentHash;\n\t\tlet imageBytes: Uint8Array | undefined;\n\n\t\tif (emdash.storage) {\n\t\t\tconst exists = await emdash.storage.exists(existing.storageKey);\n\t\t\tif (!exists) {\n\t\t\t\tconst failed = await repo.markFailed(id, existing.storageKey);\n\t\t\t\tif (!failed) return await confirmationConflict(repo, id);\n\t\t\t\treturn apiError(\"FILE_NOT_FOUND\", \"File was not uploaded to storage\", 400);\n\t\t\t}\n\n\t\t\tconst storedFile = await emdash.storage.download(existing.storageKey);\n\t\t\tif (confirmedSize !== undefined && storedFile.size !== confirmedSize) {\n\t\t\t\tawait cancelDownload(storedFile);\n\t\t\t\treturn apiError(\n\t\t\t\t\t\"UPLOAD_SIZE_MISMATCH\",\n\t\t\t\t\t\"Stored file size does not match the pending media item\",\n\t\t\t\t\t400,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconfirmedSize = storedFile.size;\n\n\t\t\tconst isImage = existing.mimeType.startsWith(\"image/\");\n\t\t\tconst canBuffer = storedFile.size <= MAX_PLACEHOLDER_DOWNLOAD_BYTES;\n\t\t\tconst hasServerHash =\n\t\t\t\tcontentHash !== null && (await repo.hasUploadAttempt(existing.storageKey));\n\t\t\tif (canBuffer && (isImage || !hasServerHash)) {\n\t\t\t\tconst bytes = await consumeDownload(storedFile);\n\t\t\t\tcontentHash = bytes.byteLength > 0 ? await computeContentHash(bytes) : null;\n\t\t\t\tif (isImage && bytes.byteLength > 0) imageBytes = bytes;\n\t\t\t} else {\n\t\t\t\tif (!hasServerHash) contentHash = null;\n\t\t\t\tawait cancelDownload(storedFile);\n\t\t\t}\n\n\t\t\tif (isImage && !canBuffer) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${storedFile.size} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// LQIP is best-effort; oversized images skip server-side placeholders.\n\t\tlet blurhash: string | undefined;\n\t\tlet dominantColor: string | undefined;\n\t\tlet width = body.width;\n\t\tlet height = body.height;\n\t\tif (imageBytes) {\n\t\t\ttry {\n\t\t\t\tconst enriched = await enrichImageMetadata(imageBytes, existing.mimeType, {\n\t\t\t\t\tknownDimensions:\n\t\t\t\t\t\tbody.width != null && body.height != null\n\t\t\t\t\t\t\t? { width: body.width, height: body.height }\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t});\n\t\t\t\tblurhash = enriched.blurhash;\n\t\t\t\tdominantColor = enriched.dominantColor;\n\t\t\t\twidth = width ?? enriched.width;\n\t\t\t\theight = height ?? enriched.height;\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\"[media] confirm placeholder generation failed:\", error);\n\t\t\t}\n\t\t}\n\n\t\t// Confirm the upload\n\t\tconst item = await repo.confirmUpload(\n\t\t\tid,\n\t\t\t{\n\t\t\t\tsize: confirmedSize,\n\t\t\t\twidth,\n\t\t\t\theight,\n\t\t\t\tblurhash,\n\t\t\t\tdominantColor,\n\t\t\t\tcontentHash,\n\t\t\t},\n\t\t\texisting.storageKey,\n\t\t);\n\n\t\tif (!item) {\n\t\t\treturn await confirmationConflict(repo, id);\n\t\t}\n\n\t\tawait forgetUploadAttempt(repo, item.storageKey);\n\n\t\t// Add URL to the response (relative URL for portability)\n\t\tconst itemWithUrl = addUrlToMedia(item);\n\n\t\treturn apiSuccess({ item: itemWithUrl });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to confirm upload\", \"CONFIRM_ERROR\");\n\t}\n};\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import "../../../../../base64-B-PsqheR.mjs";
2
- import "../../../../../types-XrQQ-Aex.mjs";
3
- import { t as MediaRepository } from "../../../../../media-tQDZEdu7.mjs";
1
+ import "../../../../../base64-CvLhUKQv.mjs";
2
+ import "../../../../../types-CQPWhPxE.mjs";
3
+ import { t as MediaRepository } from "../../../../../media-CzhAn2--.mjs";
4
4
  import { n as normalizeMime } from "../../../../../mime-DfIVjNkr.mjs";
5
- import { n as computeContentHash, t as MAX_CONTENT_HASH_BYTES } from "../../../../../hash-Cb7U1b5M.mjs";
6
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DmmN74gW.mjs";
7
- import { t as removeUploadAttempt } from "../../../../../upload-attempts-C5yd6Gae.mjs";
8
- import { n as requireOwnerPerm, r as requirePerm } from "../../../../../authorize-R5iYYrpa.mjs";
5
+ import { n as computeContentHash, t as MAX_CONTENT_HASH_BYTES } from "../../../../../hash-Cg0TXJPV.mjs";
6
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DupyJZE2.mjs";
7
+ import { t as removeUploadAttempt } from "../../../../../upload-attempts-hlne3iA1.mjs";
8
+ import { n as requireOwnerPerm, r as requirePerm } from "../../../../../authorize-1iFJzPaZ.mjs";
9
9
  import { ulid } from "ulidx";
10
10
 
11
11
  //#region src/astro/routes/api/media/[id]/upload.ts
@@ -1,20 +1,21 @@
1
- import "../../../../../config-DyF3OKg_.mjs";
2
- import "../../../../../base64-B-PsqheR.mjs";
3
- import "../../../../../types-XrQQ-Aex.mjs";
4
- import "../../../../../media-tQDZEdu7.mjs";
5
- import "../../../../../hash-Cb7U1b5M.mjs";
6
- import "../../../../../content-refresh-DgqUBeTv.mjs";
7
- import { t as handleMediaUsageDetails } from "../../../../../media-usage-DPJcNaAq.mjs";
8
- import { a as unwrapResult, t as apiError } from "../../../../../error-DmmN74gW.mjs";
9
- import { i as parseQuery, t as isParseError } from "../../../../../parse-BL49yb9D.mjs";
10
- import { C as mediaUsageDetailsQuery } from "../../../../../media-DWIzoVFL.mjs";
11
- import "../../../../../redirects-BOn2DIUs.mjs";
12
- import "../../../../../relations-C4duJnwI.mjs";
13
- import "../../../../../status-COfycGIU.mjs";
1
+ import "../../../../../dialect-helpers-BSbfaO3F.mjs";
2
+ import "../../../../../config-C3EBstHi.mjs";
3
+ import "../../../../../base64-CvLhUKQv.mjs";
4
+ import "../../../../../types-CQPWhPxE.mjs";
5
+ import "../../../../../media-CzhAn2--.mjs";
6
+ import "../../../../../content-refresh-DNERP4-f.mjs";
7
+ import "../../../../../media-usage-work-DrCbqBcl.mjs";
8
+ import { t as handleMediaUsageDetails } from "../../../../../media-usage-De3qvwjc.mjs";
9
+ import { a as unwrapResult, t as apiError } from "../../../../../error-DupyJZE2.mjs";
10
+ import { i as parseQuery, t as isParseError } from "../../../../../parse-DFbuc-Fh.mjs";
11
+ import { A as mediaUsageDetailsQuery } from "../../../../../media-BWWWb8kg.mjs";
12
+ import "../../../../../redirects-cV9g7V-0.mjs";
13
+ import "../../../../../relations-BBApI4e2.mjs";
14
+ import "../../../../../status-CWVYpbD8.mjs";
14
15
  import "../../../../../api/schemas/index.mjs";
15
- import "../../../../../api-tokens-CEsW_jCg.mjs";
16
- import { r as requirePerm } from "../../../../../authorize-R5iYYrpa.mjs";
17
- import { t as requireScope } from "../../../../../scopes-Be2d8bSb.mjs";
16
+ import "../../../../../api-tokens-CyTsLszZ.mjs";
17
+ import { r as requirePerm } from "../../../../../authorize-1iFJzPaZ.mjs";
18
+ import { t as requireScope } from "../../../../../scopes-BVeuhGBE.mjs";
18
19
 
19
20
  //#region src/astro/routes/api/media/[id]/usage.ts
20
21
  const prerender = false;
@@ -1 +1 @@
1
- {"version":3,"file":"usage.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/usage.ts"],"sourcesContent":["import type { APIRoute } from \"astro\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageDetails } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseQuery } from \"#api/parse.js\";\nimport { mediaUsageDetailsQuery } from \"#api/schemas.js\";\nimport { requireScope } from \"#auth/scopes.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst mediaDenied = requirePerm(user, \"media:read\");\n\tif (mediaDenied) return mediaDenied;\n\tconst contentDenied = requirePerm(user, \"content:read_drafts\");\n\tif (contentDenied) return contentDenied;\n\tconst scopeDenied = requireScope(locals, \"admin\");\n\tif (scopeDenied) return scopeDenied;\n\n\tconst { id } = params;\n\tif (!id) return apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\tif (!emdash?.db) return apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\n\tconst query = parseQuery(new URL(request.url), mediaUsageDetailsQuery);\n\tif (isParseError(query)) return query;\n\n\treturn unwrapResult(await handleMediaUsageDetails(emdash.db, id, query));\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AASA,MAAa,YAAY;AAEzB,MAAa,MAAgB,OAAO,EAAE,QAAQ,SAAS,aAAa;CACnE,MAAM,EAAE,QAAQ,SAAS;CAEzB,MAAM,cAAc,YAAY,MAAM,aAAa;AACnD,KAAI,YAAa,QAAO;CACxB,MAAM,gBAAgB,YAAY,MAAM,sBAAsB;AAC9D,KAAI,cAAe,QAAO;CAC1B,MAAM,cAAc,aAAa,QAAQ,QAAQ;AACjD,KAAI,YAAa,QAAO;CAExB,MAAM,EAAE,OAAO;AACf,KAAI,CAAC,GAAI,QAAO,SAAS,mBAAmB,qBAAqB,IAAI;AACrE,KAAI,CAAC,QAAQ,GAAI,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;CAEpF,MAAM,QAAQ,WAAW,IAAI,IAAI,QAAQ,IAAI,EAAE,uBAAuB;AACtE,KAAI,aAAa,MAAM,CAAE,QAAO;AAEhC,QAAO,aAAa,MAAM,wBAAwB,OAAO,IAAI,IAAI,MAAM,CAAC"}
1
+ {"version":3,"file":"usage.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/usage.ts"],"sourcesContent":["import type { APIRoute } from \"astro\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageDetails } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseQuery } from \"#api/parse.js\";\nimport { mediaUsageDetailsQuery } from \"#api/schemas.js\";\nimport { requireScope } from \"#auth/scopes.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst mediaDenied = requirePerm(user, \"media:read\");\n\tif (mediaDenied) return mediaDenied;\n\tconst contentDenied = requirePerm(user, \"content:read_drafts\");\n\tif (contentDenied) return contentDenied;\n\tconst scopeDenied = requireScope(locals, \"admin\");\n\tif (scopeDenied) return scopeDenied;\n\n\tconst { id } = params;\n\tif (!id) return apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\tif (!emdash?.db) return apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\n\tconst query = parseQuery(new URL(request.url), mediaUsageDetailsQuery);\n\tif (isParseError(query)) return query;\n\n\treturn unwrapResult(await handleMediaUsageDetails(emdash.db, id, query));\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AASA,MAAa,YAAY;AAEzB,MAAa,MAAgB,OAAO,EAAE,QAAQ,SAAS,aAAa;CACnE,MAAM,EAAE,QAAQ,SAAS;CAEzB,MAAM,cAAc,YAAY,MAAM,aAAa;AACnD,KAAI,YAAa,QAAO;CACxB,MAAM,gBAAgB,YAAY,MAAM,sBAAsB;AAC9D,KAAI,cAAe,QAAO;CAC1B,MAAM,cAAc,aAAa,QAAQ,QAAQ;AACjD,KAAI,YAAa,QAAO;CAExB,MAAM,EAAE,OAAO;AACf,KAAI,CAAC,GAAI,QAAO,SAAS,mBAAmB,qBAAqB,IAAI;AACrE,KAAI,CAAC,QAAQ,GAAI,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;CAEpF,MAAM,QAAQ,WAAW,IAAI,IAAI,QAAQ,IAAI,EAAE,uBAAuB;AACtE,KAAI,aAAa,MAAM,CAAE,QAAO;AAEhC,QAAO,aAAa,MAAM,wBAAwB,OAAO,IAAI,IAAI,MAAM,CAAC"}
@@ -1,19 +1,20 @@
1
- import "../../../../config-DyF3OKg_.mjs";
2
- import "../../../../base64-B-PsqheR.mjs";
3
- import "../../../../types-XrQQ-Aex.mjs";
4
- import { t as MediaRepository } from "../../../../media-tQDZEdu7.mjs";
5
- import "../../../../hash-Cb7U1b5M.mjs";
6
- import "../../../../content-refresh-DgqUBeTv.mjs";
7
- import { r as handleMediaUsageSummaries } from "../../../../media-usage-DPJcNaAq.mjs";
8
- import { a as unwrapResult, n as apiSuccess, r as handleError, t as apiError } from "../../../../error-DmmN74gW.mjs";
9
- import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../parse-BL49yb9D.mjs";
10
- import { s as mediaGetQuery, v as mediaUpdateBody } from "../../../../media-DWIzoVFL.mjs";
11
- import "../../../../redirects-BOn2DIUs.mjs";
12
- import "../../../../relations-C4duJnwI.mjs";
13
- import "../../../../status-COfycGIU.mjs";
1
+ import "../../../../dialect-helpers-BSbfaO3F.mjs";
2
+ import "../../../../config-C3EBstHi.mjs";
3
+ import "../../../../base64-CvLhUKQv.mjs";
4
+ import "../../../../types-CQPWhPxE.mjs";
5
+ import { t as MediaRepository } from "../../../../media-CzhAn2--.mjs";
6
+ import "../../../../content-refresh-DNERP4-f.mjs";
7
+ import "../../../../media-usage-work-DrCbqBcl.mjs";
8
+ import { r as handleMediaUsageSummaries } from "../../../../media-usage-De3qvwjc.mjs";
9
+ import { a as unwrapResult, n as apiSuccess, r as handleError, t as apiError } from "../../../../error-DupyJZE2.mjs";
10
+ import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../parse-DFbuc-Fh.mjs";
11
+ import { s as mediaGetQuery, v as mediaUpdateBody } from "../../../../media-BWWWb8kg.mjs";
12
+ import "../../../../redirects-cV9g7V-0.mjs";
13
+ import "../../../../relations-BBApI4e2.mjs";
14
+ import "../../../../status-CWVYpbD8.mjs";
14
15
  import "../../../../api/schemas/index.mjs";
15
- import { t as removeUploadAttempt } from "../../../../upload-attempts-C5yd6Gae.mjs";
16
- import { n as requireOwnerPerm, r as requirePerm, t as canReadMediaUsageCount } from "../../../../authorize-R5iYYrpa.mjs";
16
+ import { t as removeUploadAttempt } from "../../../../upload-attempts-hlne3iA1.mjs";
17
+ import { n as requireOwnerPerm, r as requirePerm, t as canReadMediaUsageCount } from "../../../../authorize-1iFJzPaZ.mjs";
17
18
 
18
19
  //#region src/astro/routes/api/media/[id].ts
19
20
  const prerender = false;
@@ -1 +1 @@
1
- {"version":3,"file":"_id_.mjs","names":[],"sources":["../../../../../src/astro/routes/api/media/[id].ts"],"sourcesContent":["/**\n * Single media item endpoint\n *\n * GET /_emdash/api/media/:id - Get media item\n * PUT /_emdash/api/media/:id - Update media metadata\n * DELETE /_emdash/api/media/:id - Delete media item\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { canReadMediaUsageCount, requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageSummaries } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseBody, parseQuery } from \"#api/parse.js\";\nimport { mediaGetQuery, mediaUpdateBody } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { removeUploadAttempt } from \"#media/upload-attempts.js\";\n\nexport const prerender = false;\n\n/**\n * Get media item\n */\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst readDenied = requirePerm(user, \"media:read\");\n\tif (readDenied) return readDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\tconst query = parseQuery(new URL(request.url), mediaGetQuery);\n\tif (isParseError(query)) return query;\n\n\tconst result = await emdash.handleMediaGet(id);\n\tif (!result.success || query.includeUsage !== \"1\") return unwrapResult(result);\n\n\tconst includeCount = canReadMediaUsageCount(user, locals.tokenScopes);\n\tconst usageResult = await handleMediaUsageSummaries(emdash.db, [id], { includeCount });\n\tif (!usageResult.success) return unwrapResult(usageResult);\n\tconst usage = usageResult.data[id];\n\tif (!usage) return apiError(\"MEDIA_USAGE_READ_ERROR\", \"Failed to read media usage\", 500);\n\n\treturn apiSuccess({ item: { ...result.data.item, usage } });\n};\n\n/**\n * Update media metadata\n *\n * Authors can edit their own media; editors+ can edit any.\n */\nexport const PUT: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst editDenied = requirePerm(user, \"media:edit_own\");\n\tif (editDenied) return editDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaUpdate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can edit own, editors+ can edit any\n\t\tconst ownerDenied = requireOwnerPerm(user, media.authorId, \"media:edit_own\", \"media:edit_any\");\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tconst body = await parseBody(request, mediaUpdateBody);\n\t\tif (isParseError(body)) return body;\n\n\t\tconst result = await emdash.handleMediaUpdate(id, {\n\t\t\talt: body.alt,\n\t\t\tcaption: body.caption,\n\t\t\twidth: body.width,\n\t\t\theight: body.height,\n\t\t});\n\n\t\treturn unwrapResult(result);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to update media\", \"MEDIA_UPDATE_ERROR\");\n\t}\n};\n\n/**\n * Delete media item\n *\n * Authors can delete their own media; editors+ can delete any.\n */\nexport const DELETE: APIRoute = async ({ params, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst deleteDenied = requirePerm(user, \"media:delete_own\");\n\tif (deleteDenied) return deleteDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaDelete) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check and storage key\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can delete own, editors+ can delete any\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\tmedia.authorId,\n\t\t\t\"media:delete_own\",\n\t\t\t\"media:delete_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\t// Delete from database — site-settings cache invalidation happens\n\t\t// in `EmDashRuntime.handleMediaDelete` so MCP/plugin paths inherit it.\n\t\tconst result = await emdash.handleMediaDelete(id);\n\t\tif (!result.success) return unwrapResult(result);\n\t\tif (\n\t\t\ttypeof result.data !== \"object\" ||\n\t\t\tresult.data === null ||\n\t\t\t!(\"storageKey\" in result.data) ||\n\t\t\ttypeof result.data.storageKey !== \"string\"\n\t\t) {\n\t\t\treturn apiError(\"MEDIA_DELETE_ERROR\", \"Failed to delete media\", 500);\n\t\t}\n\n\t\tif (emdash.storage) {\n\t\t\tconst repo = new MediaRepository(emdash.db);\n\t\t\tawait removeUploadAttempt(emdash.storage, repo, result.data.storageKey, {\n\t\t\t\tallowUntracked: true,\n\t\t\t});\n\t\t}\n\n\t\treturn apiSuccess({ deleted: true });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to delete media\", \"MEDIA_DELETE_ERROR\");\n\t}\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"_id_.mjs","names":[],"sources":["../../../../../src/astro/routes/api/media/[id].ts"],"sourcesContent":["/**\n * Single media item endpoint\n *\n * GET /_emdash/api/media/:id - Get media item\n * PUT /_emdash/api/media/:id - Update media metadata\n * DELETE /_emdash/api/media/:id - Delete media item\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { canReadMediaUsageCount, requireOwnerPerm, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError, unwrapResult } from \"#api/error.js\";\nimport { handleMediaUsageSummaries } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseBody, parseQuery } from \"#api/parse.js\";\nimport { mediaGetQuery, mediaUpdateBody } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { removeUploadAttempt } from \"#media/upload-attempts.js\";\n\nexport const prerender = false;\n\n/**\n * Get media item\n */\nexport const GET: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\tconst readDenied = requirePerm(user, \"media:read\");\n\tif (readDenied) return readDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\tconst query = parseQuery(new URL(request.url), mediaGetQuery);\n\tif (isParseError(query)) return query;\n\n\tconst result = await emdash.handleMediaGet(id);\n\tif (!result.success || query.includeUsage !== \"1\") return unwrapResult(result);\n\n\tconst includeCount = canReadMediaUsageCount(user, locals.tokenScopes);\n\tconst usageResult = await handleMediaUsageSummaries(emdash.db, [id], { includeCount });\n\tif (!usageResult.success) return unwrapResult(usageResult);\n\tconst usage = usageResult.data[id];\n\tif (!usage) return apiError(\"MEDIA_USAGE_READ_ERROR\", \"Failed to read media usage\", 500);\n\n\treturn apiSuccess({ item: { ...result.data.item, usage } });\n};\n\n/**\n * Update media metadata\n *\n * Authors can edit their own media; editors+ can edit any.\n */\nexport const PUT: APIRoute = async ({ params, request, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst editDenied = requirePerm(user, \"media:edit_own\");\n\tif (editDenied) return editDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaUpdate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can edit own, editors+ can edit any\n\t\tconst ownerDenied = requireOwnerPerm(user, media.authorId, \"media:edit_own\", \"media:edit_any\");\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\tconst body = await parseBody(request, mediaUpdateBody);\n\t\tif (isParseError(body)) return body;\n\n\t\tconst result = await emdash.handleMediaUpdate(id, {\n\t\t\talt: body.alt,\n\t\t\tcaption: body.caption,\n\t\t\twidth: body.width,\n\t\t\theight: body.height,\n\t\t});\n\n\t\treturn unwrapResult(result);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to update media\", \"MEDIA_UPDATE_ERROR\");\n\t}\n};\n\n/**\n * Delete media item\n *\n * Authors can delete their own media; editors+ can delete any.\n */\nexport const DELETE: APIRoute = async ({ params, locals }) => {\n\tconst { emdash, user } = locals;\n\tconst { id } = params;\n\n\t// Minimum permission gate — ownership checked below\n\tconst deleteDenied = requirePerm(user, \"media:delete_own\");\n\tif (deleteDenied) return deleteDenied;\n\n\tif (!id) {\n\t\treturn apiError(\"INVALID_REQUEST\", \"Media ID required\", 400);\n\t}\n\n\tif (!emdash?.handleMediaGet || !emdash?.handleMediaDelete) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\t// Fetch media item for ownership check and storage key\n\t\tconst getResult = await emdash.handleMediaGet(id);\n\t\tif (!getResult.success || !getResult.data?.item) {\n\t\t\treturn apiError(\"NOT_FOUND\", \"Media item not found\", 404);\n\t\t}\n\n\t\tconst media = getResult.data.item;\n\n\t\t// Ownership check: authors can delete own, editors+ can delete any\n\t\tconst ownerDenied = requireOwnerPerm(\n\t\t\tuser,\n\t\t\tmedia.authorId,\n\t\t\t\"media:delete_own\",\n\t\t\t\"media:delete_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\t// Delete from database — site-settings cache invalidation happens\n\t\t// in `EmDashRuntime.handleMediaDelete` so MCP/plugin paths inherit it.\n\t\tconst result = await emdash.handleMediaDelete(id);\n\t\tif (!result.success) return unwrapResult(result);\n\t\tif (\n\t\t\ttypeof result.data !== \"object\" ||\n\t\t\tresult.data === null ||\n\t\t\t!(\"storageKey\" in result.data) ||\n\t\t\ttypeof result.data.storageKey !== \"string\"\n\t\t) {\n\t\t\treturn apiError(\"MEDIA_DELETE_ERROR\", \"Failed to delete media\", 500);\n\t\t}\n\n\t\tif (emdash.storage) {\n\t\t\tconst repo = new MediaRepository(emdash.db);\n\t\t\tawait removeUploadAttempt(emdash.storage, repo, result.data.storageKey, {\n\t\t\t\tallowUntracked: true,\n\t\t\t});\n\t\t}\n\n\t\treturn apiSuccess({ deleted: true });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to delete media\", \"MEDIA_DELETE_ERROR\");\n\t}\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import "../../../../../base64-B-PsqheR.mjs";
2
- import "../../../../../types-XrQQ-Aex.mjs";
3
- import { r as handleError, t as apiError } from "../../../../../error-DmmN74gW.mjs";
1
+ import "../../../../../base64-CvLhUKQv.mjs";
2
+ import "../../../../../types-CQPWhPxE.mjs";
3
+ import { r as handleError, t as apiError } from "../../../../../error-DupyJZE2.mjs";
4
4
 
5
5
  //#region src/astro/routes/api/media/file/[...key].ts
6
6
  const prerender = false;
@@ -1,7 +1,7 @@
1
- import "../../../../../../base64-B-PsqheR.mjs";
2
- import "../../../../../../types-XrQQ-Aex.mjs";
3
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-DmmN74gW.mjs";
4
- import { r as requirePerm } from "../../../../../../authorize-R5iYYrpa.mjs";
1
+ import "../../../../../../base64-CvLhUKQv.mjs";
2
+ import "../../../../../../types-CQPWhPxE.mjs";
3
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-DupyJZE2.mjs";
4
+ import { r as requirePerm } from "../../../../../../authorize-1iFJzPaZ.mjs";
5
5
 
6
6
  //#region src/astro/routes/api/media/providers/[providerId]/[itemId].ts
7
7
  const prerender = false;
@@ -1,7 +1,7 @@
1
- import "../../../../../../base64-B-PsqheR.mjs";
2
- import "../../../../../../types-XrQQ-Aex.mjs";
3
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-DmmN74gW.mjs";
4
- import { r as requirePerm } from "../../../../../../authorize-R5iYYrpa.mjs";
1
+ import "../../../../../../base64-CvLhUKQv.mjs";
2
+ import "../../../../../../types-CQPWhPxE.mjs";
3
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-DupyJZE2.mjs";
4
+ import { r as requirePerm } from "../../../../../../authorize-1iFJzPaZ.mjs";
5
5
 
6
6
  //#region src/astro/routes/api/media/providers/[providerId]/index.ts
7
7
  const prerender = false;
@@ -1,7 +1,7 @@
1
- import "../../../../../base64-B-PsqheR.mjs";
2
- import "../../../../../types-XrQQ-Aex.mjs";
3
- import { n as apiSuccess, t as apiError } from "../../../../../error-DmmN74gW.mjs";
4
- import { r as requirePerm } from "../../../../../authorize-R5iYYrpa.mjs";
1
+ import "../../../../../base64-CvLhUKQv.mjs";
2
+ import "../../../../../types-CQPWhPxE.mjs";
3
+ import { n as apiSuccess, t as apiError } from "../../../../../error-DupyJZE2.mjs";
4
+ import { r as requirePerm } from "../../../../../authorize-1iFJzPaZ.mjs";
5
5
 
6
6
  //#region src/astro/routes/api/media/providers/index.ts
7
7
  const prerender = false;
@@ -1,15 +1,16 @@
1
- import "../../../../base64-B-PsqheR.mjs";
2
- import "../../../../types-XrQQ-Aex.mjs";
1
+ import "../../../../config-C3EBstHi.mjs";
2
+ import "../../../../base64-CvLhUKQv.mjs";
3
+ import "../../../../types-CQPWhPxE.mjs";
3
4
  import { n as normalizeMime, t as matchesMimeAllowlist } from "../../../../mime-DfIVjNkr.mjs";
4
- import { n as apiSuccess, r as handleError, t as apiError } from "../../../../error-DmmN74gW.mjs";
5
- import { n as parseBody, t as isParseError } from "../../../../parse-BL49yb9D.mjs";
6
- import { n as DEFAULT_MAX_UPLOAD_SIZE, y as mediaUploadUrlBody } from "../../../../media-DWIzoVFL.mjs";
7
- import "../../../../redirects-BOn2DIUs.mjs";
8
- import "../../../../relations-C4duJnwI.mjs";
9
- import "../../../../status-COfycGIU.mjs";
5
+ import { n as apiSuccess, r as handleError, t as apiError } from "../../../../error-DupyJZE2.mjs";
6
+ import { n as parseBody, t as isParseError } from "../../../../parse-DFbuc-Fh.mjs";
7
+ import { n as DEFAULT_MAX_UPLOAD_SIZE, y as mediaUploadUrlBody } from "../../../../media-BWWWb8kg.mjs";
8
+ import "../../../../redirects-cV9g7V-0.mjs";
9
+ import "../../../../relations-BBApI4e2.mjs";
10
+ import "../../../../status-CWVYpbD8.mjs";
10
11
  import "../../../../api/schemas/index.mjs";
11
- import { r as requirePerm } from "../../../../authorize-R5iYYrpa.mjs";
12
- import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../../media-allowlist-B7TPD6aq.mjs";
12
+ import { r as requirePerm } from "../../../../authorize-1iFJzPaZ.mjs";
13
+ import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../../media-allowlist-BD6HhgXA.mjs";
13
14
  import { ulid } from "ulidx";
14
15
  import * as path from "node:path";
15
16
  import { MediaRepository } from "emdash";
@@ -1 +1 @@
1
- {"version":3,"file":"upload-url.mjs","names":[],"sources":["../../../../../src/astro/routes/api/media/upload-url.ts"],"sourcesContent":["/**\n * Media upload URL endpoint\n *\n * POST /_emdash/api/media/upload-url\n *\n * Returns a signed URL for direct upload to storage.\n * Creates a pending media record that must be confirmed after upload.\n */\n\nimport * as path from \"node:path\";\n\nimport type { APIRoute } from \"astro\";\nimport { MediaRepository } from \"emdash\";\nimport { ulid } from \"ulidx\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { GLOBAL_UPLOAD_ALLOWLIST, resolveFieldAllowlist } from \"#api/handlers/media-allowlist.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { DEFAULT_MAX_UPLOAD_SIZE, mediaUploadUrlBody } from \"#api/schemas.js\";\nimport { matchesMimeAllowlist, normalizeMime } from \"#media/mime.js\";\n\nexport const prerender = false;\n\ninterface UploadUrlResponse {\n\tuploadUrl: string;\n\tmethod: \"PUT\";\n\theaders: Record<string, string>;\n\tmediaId: string;\n\tstorageKey: string;\n\texpiresAt: string;\n}\n\n/** Response when content already exists (deduplication) */\ninterface ExistingMediaResponse {\n\texisting: true;\n\tmediaId: string;\n\tstorageKey: string;\n\turl: string;\n}\n\nfunction isUnsupportedSignedUpload(error: unknown): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === \"NOT_SUPPORTED\";\n}\n\n/**\n * Get a signed upload URL for direct-to-storage upload\n */\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!emdash?.storage) {\n\t\treturn apiError(\n\t\t\t\"NO_STORAGE\",\n\t\t\t\"Storage not configured. Signed URL uploads require S3-compatible storage.\",\n\t\t\t501,\n\t\t);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\tconst maxSize = emdash.config.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE;\n\t\tif (!Number.isFinite(maxSize) || maxSize <= 0) {\n\t\t\treturn apiError(\n\t\t\t\t\"CONFIGURATION_ERROR\",\n\t\t\t\t\"Invalid maxUploadSize configuration. Expected a positive finite number.\",\n\t\t\t\t500,\n\t\t\t);\n\t\t}\n\t\tconst body = await parseBody(request, mediaUploadUrlBody(maxSize));\n\t\tif (isParseError(body)) return body;\n\t\tconst normalizedContentType = normalizeMime(body.contentType);\n\n\t\t// Validate content type (field-aware widening)\n\t\tconst fieldAllowlist = body.fieldId\n\t\t\t? await resolveFieldAllowlist(emdash.db, body.fieldId)\n\t\t\t: null;\n\t\tconst allowlist = fieldAllowlist ?? [...GLOBAL_UPLOAD_ALLOWLIST];\n\n\t\tif (!matchesMimeAllowlist(body.contentType, allowlist)) {\n\t\t\treturn apiError(\"INVALID_TYPE\", \"File type not allowed\", 400);\n\t\t}\n\n\t\tconst repo = new MediaRepository(emdash.db);\n\n\t\t// Check for existing content with same hash (deduplication)\n\t\tif (body.contentHash && body.size > 0) {\n\t\t\tconst existing = await repo.findByContentHash(body.contentHash);\n\t\t\tif (existing && existing.mimeType === normalizedContentType && existing.size === body.size) {\n\t\t\t\tconst response: ExistingMediaResponse = {\n\t\t\t\t\texisting: true,\n\t\t\t\t\tmediaId: existing.id,\n\t\t\t\t\tstorageKey: existing.storageKey,\n\t\t\t\t\turl: `/_emdash/api/media/file/${existing.storageKey}`,\n\t\t\t\t};\n\t\t\t\treturn apiSuccess(response);\n\t\t\t}\n\t\t}\n\n\t\t// Generate unique storage key\n\t\tconst id = ulid();\n\t\tconst ext = path.extname(body.filename) || \"\";\n\t\tconst storageKey = `${id}${ext}`;\n\n\t\tlet signedUrl: Awaited<ReturnType<typeof emdash.storage.getSignedUploadUrl>> | null;\n\t\ttry {\n\t\t\tsignedUrl = await emdash.storage.getSignedUploadUrl({\n\t\t\t\tkey: storageKey,\n\t\t\t\tcontentType: body.contentType,\n\t\t\t\tsize: body.size,\n\t\t\t\texpiresIn: 3600,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (!isUnsupportedSignedUpload(error)) throw error;\n\t\t\tsignedUrl = null;\n\t\t}\n\n\t\tconst mediaItem = await repo.createPending({\n\t\t\tfilename: body.filename,\n\t\t\tmimeType: normalizedContentType,\n\t\t\tsize: body.size,\n\t\t\tstorageKey,\n\t\t\tauthorId: user?.id,\n\t\t});\n\n\t\tconst response: UploadUrlResponse = {\n\t\t\tuploadUrl: signedUrl?.url ?? `/_emdash/api/media/${mediaItem.id}/upload`,\n\t\t\tmethod: signedUrl?.method ?? \"PUT\",\n\t\t\theaders: signedUrl?.headers ?? {\n\t\t\t\t\"Content-Type\": normalizedContentType,\n\t\t\t\t\"X-EmDash-Request\": \"1\",\n\t\t\t},\n\t\t\tmediaId: mediaItem.id,\n\t\t\tstorageKey,\n\t\t\texpiresAt: signedUrl?.expiresAt ?? new Date(Date.now() + 3600 * 1000).toISOString(),\n\t\t};\n\n\t\treturn apiSuccess(response);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to generate upload URL\", \"UPLOAD_URL_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,YAAY;AAmBzB,SAAS,0BAA0B,OAAyB;AAC3D,QAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;;;;;AAMpE,MAAa,OAAiB,OAAO,EAAE,SAAS,aAAa;CAC5D,MAAM,EAAE,QAAQ,SAAS;CAEzB,MAAM,SAAS,YAAY,MAAM,eAAe;AAChD,KAAI,OAAQ,QAAO;AAEnB,KAAI,CAAC,QAAQ,QACZ,QAAO,SACN,cACA,6EACA,IACA;AAGF,KAAI,CAAC,QAAQ,GACZ,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;AAGpE,KAAI;EACH,MAAM,UAAU,OAAO,OAAO,iBAAiB;AAC/C,MAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,WAAW,EAC3C,QAAO,SACN,uBACA,2EACA,IACA;EAEF,MAAM,OAAO,MAAM,UAAU,SAAS,mBAAmB,QAAQ,CAAC;AAClE,MAAI,aAAa,KAAK,CAAE,QAAO;EAC/B,MAAM,wBAAwB,cAAc,KAAK,YAAY;EAM7D,MAAM,aAHiB,KAAK,UACzB,MAAM,sBAAsB,OAAO,IAAI,KAAK,QAAQ,GACpD,SACiC,CAAC,GAAG,wBAAwB;AAEhE,MAAI,CAAC,qBAAqB,KAAK,aAAa,UAAU,CACrD,QAAO,SAAS,gBAAgB,yBAAyB,IAAI;EAG9D,MAAM,OAAO,IAAI,gBAAgB,OAAO,GAAG;AAG3C,MAAI,KAAK,eAAe,KAAK,OAAO,GAAG;GACtC,MAAM,WAAW,MAAM,KAAK,kBAAkB,KAAK,YAAY;AAC/D,OAAI,YAAY,SAAS,aAAa,yBAAyB,SAAS,SAAS,KAAK,KAOrF,QAAO,WANiC;IACvC,UAAU;IACV,SAAS,SAAS;IAClB,YAAY,SAAS;IACrB,KAAK,2BAA2B,SAAS;IACzC,CAC0B;;EAO7B,MAAM,aAAa,GAFR,MAAM,GACL,KAAK,QAAQ,KAAK,SAAS,IAAI;EAG3C,IAAI;AACJ,MAAI;AACH,eAAY,MAAM,OAAO,QAAQ,mBAAmB;IACnD,KAAK;IACL,aAAa,KAAK;IAClB,MAAM,KAAK;IACX,WAAW;IACX,CAAC;WACM,OAAO;AACf,OAAI,CAAC,0BAA0B,MAAM,CAAE,OAAM;AAC7C,eAAY;;EAGb,MAAM,YAAY,MAAM,KAAK,cAAc;GAC1C,UAAU,KAAK;GACf,UAAU;GACV,MAAM,KAAK;GACX;GACA,UAAU,MAAM;GAChB,CAAC;AAcF,SAAO,WAZ6B;GACnC,WAAW,WAAW,OAAO,sBAAsB,UAAU,GAAG;GAChE,QAAQ,WAAW,UAAU;GAC7B,SAAS,WAAW,WAAW;IAC9B,gBAAgB;IAChB,oBAAoB;IACpB;GACD,SAAS,UAAU;GACnB;GACA,WAAW,WAAW,aAAa,IAAI,KAAK,KAAK,KAAK,GAAG,OAAO,IAAK,CAAC,aAAa;GACnF,CAE0B;UACnB,OAAO;AACf,SAAO,YAAY,OAAO,iCAAiC,mBAAmB"}
1
+ {"version":3,"file":"upload-url.mjs","names":[],"sources":["../../../../../src/astro/routes/api/media/upload-url.ts"],"sourcesContent":["/**\n * Media upload URL endpoint\n *\n * POST /_emdash/api/media/upload-url\n *\n * Returns a signed URL for direct upload to storage.\n * Creates a pending media record that must be confirmed after upload.\n */\n\nimport * as path from \"node:path\";\n\nimport type { APIRoute } from \"astro\";\nimport { MediaRepository } from \"emdash\";\nimport { ulid } from \"ulidx\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { GLOBAL_UPLOAD_ALLOWLIST, resolveFieldAllowlist } from \"#api/handlers/media-allowlist.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { DEFAULT_MAX_UPLOAD_SIZE, mediaUploadUrlBody } from \"#api/schemas.js\";\nimport { matchesMimeAllowlist, normalizeMime } from \"#media/mime.js\";\n\nexport const prerender = false;\n\ninterface UploadUrlResponse {\n\tuploadUrl: string;\n\tmethod: \"PUT\";\n\theaders: Record<string, string>;\n\tmediaId: string;\n\tstorageKey: string;\n\texpiresAt: string;\n}\n\n/** Response when content already exists (deduplication) */\ninterface ExistingMediaResponse {\n\texisting: true;\n\tmediaId: string;\n\tstorageKey: string;\n\turl: string;\n}\n\nfunction isUnsupportedSignedUpload(error: unknown): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === \"NOT_SUPPORTED\";\n}\n\n/**\n * Get a signed upload URL for direct-to-storage upload\n */\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!emdash?.storage) {\n\t\treturn apiError(\n\t\t\t\"NO_STORAGE\",\n\t\t\t\"Storage not configured. Signed URL uploads require S3-compatible storage.\",\n\t\t\t501,\n\t\t);\n\t}\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\ttry {\n\t\tconst maxSize = emdash.config.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE;\n\t\tif (!Number.isFinite(maxSize) || maxSize <= 0) {\n\t\t\treturn apiError(\n\t\t\t\t\"CONFIGURATION_ERROR\",\n\t\t\t\t\"Invalid maxUploadSize configuration. Expected a positive finite number.\",\n\t\t\t\t500,\n\t\t\t);\n\t\t}\n\t\tconst body = await parseBody(request, mediaUploadUrlBody(maxSize));\n\t\tif (isParseError(body)) return body;\n\t\tconst normalizedContentType = normalizeMime(body.contentType);\n\n\t\t// Validate content type (field-aware widening)\n\t\tconst fieldAllowlist = body.fieldId\n\t\t\t? await resolveFieldAllowlist(emdash.db, body.fieldId)\n\t\t\t: null;\n\t\tconst allowlist = fieldAllowlist ?? [...GLOBAL_UPLOAD_ALLOWLIST];\n\n\t\tif (!matchesMimeAllowlist(body.contentType, allowlist)) {\n\t\t\treturn apiError(\"INVALID_TYPE\", \"File type not allowed\", 400);\n\t\t}\n\n\t\tconst repo = new MediaRepository(emdash.db);\n\n\t\t// Check for existing content with same hash (deduplication)\n\t\tif (body.contentHash && body.size > 0) {\n\t\t\tconst existing = await repo.findByContentHash(body.contentHash);\n\t\t\tif (existing && existing.mimeType === normalizedContentType && existing.size === body.size) {\n\t\t\t\tconst response: ExistingMediaResponse = {\n\t\t\t\t\texisting: true,\n\t\t\t\t\tmediaId: existing.id,\n\t\t\t\t\tstorageKey: existing.storageKey,\n\t\t\t\t\turl: `/_emdash/api/media/file/${existing.storageKey}`,\n\t\t\t\t};\n\t\t\t\treturn apiSuccess(response);\n\t\t\t}\n\t\t}\n\n\t\t// Generate unique storage key\n\t\tconst id = ulid();\n\t\tconst ext = path.extname(body.filename) || \"\";\n\t\tconst storageKey = `${id}${ext}`;\n\n\t\tlet signedUrl: Awaited<ReturnType<typeof emdash.storage.getSignedUploadUrl>> | null;\n\t\ttry {\n\t\t\tsignedUrl = await emdash.storage.getSignedUploadUrl({\n\t\t\t\tkey: storageKey,\n\t\t\t\tcontentType: body.contentType,\n\t\t\t\tsize: body.size,\n\t\t\t\texpiresIn: 3600,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (!isUnsupportedSignedUpload(error)) throw error;\n\t\t\tsignedUrl = null;\n\t\t}\n\n\t\tconst mediaItem = await repo.createPending({\n\t\t\tfilename: body.filename,\n\t\t\tmimeType: normalizedContentType,\n\t\t\tsize: body.size,\n\t\t\tstorageKey,\n\t\t\tauthorId: user?.id,\n\t\t});\n\n\t\tconst response: UploadUrlResponse = {\n\t\t\tuploadUrl: signedUrl?.url ?? `/_emdash/api/media/${mediaItem.id}/upload`,\n\t\t\tmethod: signedUrl?.method ?? \"PUT\",\n\t\t\theaders: signedUrl?.headers ?? {\n\t\t\t\t\"Content-Type\": normalizedContentType,\n\t\t\t\t\"X-EmDash-Request\": \"1\",\n\t\t\t},\n\t\t\tmediaId: mediaItem.id,\n\t\t\tstorageKey,\n\t\t\texpiresAt: signedUrl?.expiresAt ?? new Date(Date.now() + 3600 * 1000).toISOString(),\n\t\t};\n\n\t\treturn apiSuccess(response);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to generate upload URL\", \"UPLOAD_URL_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,YAAY;AAmBzB,SAAS,0BAA0B,OAAyB;AAC3D,QAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;;;;;AAMpE,MAAa,OAAiB,OAAO,EAAE,SAAS,aAAa;CAC5D,MAAM,EAAE,QAAQ,SAAS;CAEzB,MAAM,SAAS,YAAY,MAAM,eAAe;AAChD,KAAI,OAAQ,QAAO;AAEnB,KAAI,CAAC,QAAQ,QACZ,QAAO,SACN,cACA,6EACA,IACA;AAGF,KAAI,CAAC,QAAQ,GACZ,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;AAGpE,KAAI;EACH,MAAM,UAAU,OAAO,OAAO,iBAAiB;AAC/C,MAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,WAAW,EAC3C,QAAO,SACN,uBACA,2EACA,IACA;EAEF,MAAM,OAAO,MAAM,UAAU,SAAS,mBAAmB,QAAQ,CAAC;AAClE,MAAI,aAAa,KAAK,CAAE,QAAO;EAC/B,MAAM,wBAAwB,cAAc,KAAK,YAAY;EAM7D,MAAM,aAHiB,KAAK,UACzB,MAAM,sBAAsB,OAAO,IAAI,KAAK,QAAQ,GACpD,SACiC,CAAC,GAAG,wBAAwB;AAEhE,MAAI,CAAC,qBAAqB,KAAK,aAAa,UAAU,CACrD,QAAO,SAAS,gBAAgB,yBAAyB,IAAI;EAG9D,MAAM,OAAO,IAAI,gBAAgB,OAAO,GAAG;AAG3C,MAAI,KAAK,eAAe,KAAK,OAAO,GAAG;GACtC,MAAM,WAAW,MAAM,KAAK,kBAAkB,KAAK,YAAY;AAC/D,OAAI,YAAY,SAAS,aAAa,yBAAyB,SAAS,SAAS,KAAK,KAOrF,QAAO,WANiC;IACvC,UAAU;IACV,SAAS,SAAS;IAClB,YAAY,SAAS;IACrB,KAAK,2BAA2B,SAAS;IACzC,CAC0B;;EAO7B,MAAM,aAAa,GAFR,MAAM,GACL,KAAK,QAAQ,KAAK,SAAS,IAAI;EAG3C,IAAI;AACJ,MAAI;AACH,eAAY,MAAM,OAAO,QAAQ,mBAAmB;IACnD,KAAK;IACL,aAAa,KAAK;IAClB,MAAM,KAAK;IACX,WAAW;IACX,CAAC;WACM,OAAO;AACf,OAAI,CAAC,0BAA0B,MAAM,CAAE,OAAM;AAC7C,eAAY;;EAGb,MAAM,YAAY,MAAM,KAAK,cAAc;GAC1C,UAAU,KAAK;GACf,UAAU;GACV,MAAM,KAAK;GACX;GACA,UAAU,MAAM;GAChB,CAAC;AAcF,SAAO,WAZ6B;GACnC,WAAW,WAAW,OAAO,sBAAsB,UAAU,GAAG;GAChE,QAAQ,WAAW,UAAU;GAC7B,SAAS,WAAW,WAAW;IAC9B,gBAAgB;IAChB,oBAAoB;IACpB;GACD,SAAS,UAAU;GACnB;GACA,WAAW,WAAW,aAAa,IAAI,KAAK,KAAK,KAAK,GAAG,OAAO,IAAK,CAAC,aAAa;GACnF,CAE0B;UACnB,OAAO;AACf,SAAO,YAAY,OAAO,iCAAiC,mBAAmB"}
@@ -1,21 +1,24 @@
1
- import "../../../config-DyF3OKg_.mjs";
2
- import "../../../base64-B-PsqheR.mjs";
3
- import "../../../types-XrQQ-Aex.mjs";
4
- import { t as MediaRepository } from "../../../media-tQDZEdu7.mjs";
1
+ import "../../../dialect-helpers-BSbfaO3F.mjs";
2
+ import "../../../config-C3EBstHi.mjs";
3
+ import "../../../base64-CvLhUKQv.mjs";
4
+ import "../../../types-CQPWhPxE.mjs";
5
+ import { t as MediaRepository } from "../../../media-CzhAn2--.mjs";
6
+ import "../../../dist-D_6CKR8z.mjs";
5
7
  import { n as normalizeMime, t as matchesMimeAllowlist } from "../../../mime-DfIVjNkr.mjs";
6
- import { t as enrichImageMetadata } from "../../../enrich-CFJJgxs_.mjs";
7
- import { n as computeContentHash } from "../../../hash-Cb7U1b5M.mjs";
8
- import "../../../content-refresh-DgqUBeTv.mjs";
9
- import { r as handleMediaUsageSummaries } from "../../../media-usage-DPJcNaAq.mjs";
10
- import { a as unwrapResult, n as apiSuccess, r as handleError, t as apiError } from "../../../error-DmmN74gW.mjs";
11
- import { i as parseQuery, t as isParseError } from "../../../parse-BL49yb9D.mjs";
12
- import { l as mediaListQuery, n as DEFAULT_MAX_UPLOAD_SIZE, r as formatFileSize } from "../../../media-DWIzoVFL.mjs";
13
- import "../../../redirects-BOn2DIUs.mjs";
14
- import "../../../relations-C4duJnwI.mjs";
15
- import "../../../status-COfycGIU.mjs";
8
+ import { t as enrichImageMetadata } from "../../../enrich-_ySMEvEE.mjs";
9
+ import { n as computeContentHash } from "../../../hash-Cg0TXJPV.mjs";
10
+ import "../../../content-refresh-DNERP4-f.mjs";
11
+ import "../../../media-usage-work-DrCbqBcl.mjs";
12
+ import { r as handleMediaUsageSummaries } from "../../../media-usage-De3qvwjc.mjs";
13
+ import { a as unwrapResult, n as apiSuccess, r as handleError, t as apiError } from "../../../error-DupyJZE2.mjs";
14
+ import { i as parseQuery, t as isParseError } from "../../../parse-DFbuc-Fh.mjs";
15
+ import { l as mediaListQuery, n as DEFAULT_MAX_UPLOAD_SIZE, r as formatFileSize } from "../../../media-BWWWb8kg.mjs";
16
+ import "../../../redirects-cV9g7V-0.mjs";
17
+ import "../../../relations-BBApI4e2.mjs";
18
+ import "../../../status-CWVYpbD8.mjs";
16
19
  import "../../../api/schemas/index.mjs";
17
- import { r as requirePerm, t as canReadMediaUsageCount } from "../../../authorize-R5iYYrpa.mjs";
18
- import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../media-allowlist-B7TPD6aq.mjs";
20
+ import { r as requirePerm, t as canReadMediaUsageCount } from "../../../authorize-1iFJzPaZ.mjs";
21
+ import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../media-allowlist-BD6HhgXA.mjs";
19
22
  import { ulid } from "ulidx";
20
23
  import * as path from "node:path";
21
24
 
@@ -1 +1 @@
1
- {"version":3,"file":"media.mjs","names":[],"sources":["../../../../src/astro/routes/api/media.ts"],"sourcesContent":["/**\n * Media list and upload endpoint\n *\n * GET /_emdash/api/media - List all media\n * POST /_emdash/api/media - Upload new media (via configured storage adapter)\n */\n\nimport * as path from \"node:path\";\n\nimport type { APIRoute } from \"astro\";\nimport { ulid } from \"ulidx\";\n\nimport { canReadMediaUsageCount, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError, unwrapResult } from \"#api/error.js\";\nimport { GLOBAL_UPLOAD_ALLOWLIST, resolveFieldAllowlist } from \"#api/handlers/media-allowlist.js\";\nimport { handleMediaUsageSummaries } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseQuery } from \"#api/parse.js\";\nimport { DEFAULT_MAX_UPLOAD_SIZE, formatFileSize, mediaListQuery } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { enrichImageMetadata } from \"#media/enrich.js\";\nimport { matchesMimeAllowlist, normalizeMime } from \"#media/mime.js\";\nimport { computeContentHash } from \"#utils/hash.js\";\n\nimport type { MediaItem } from \"../../types.js\";\n\nexport const prerender = false;\n\n/**\n * Add URL to media items\n * Uses relative URLs to ensure portability across deployments\n */\nfunction addUrlToMedia(item: MediaItem): MediaItem & { url: string } {\n\treturn {\n\t\t...item,\n\t\turl: `/_emdash/api/media/file/${item.storageKey}`,\n\t};\n}\n\n/**\n * List media items\n */\nexport const GET: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"media:read\");\n\tif (denied) return denied;\n\n\tif (!emdash?.handleMediaList) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\tconst url = new URL(request.url);\n\tconst query = parseQuery(url, mediaListQuery);\n\tif (isParseError(query)) return query;\n\n\tconst result = await emdash.handleMediaList({\n\t\tcursor: query.cursor,\n\t\tlimit: query.limit,\n\t\tmimeType: query.mimeType,\n\t\tq: query.q,\n\t});\n\n\tif (!result.success) {\n\t\treturn unwrapResult(result);\n\t}\n\n\t// Add URL to each media item (relative URLs for portability)\n\tconst itemsWithUrl = result.data.items.map((item) => addUrlToMedia(item));\n\tif (query.includeUsage !== \"1\") {\n\t\treturn apiSuccess({ items: itemsWithUrl, nextCursor: result.data.nextCursor });\n\t}\n\n\tconst includeCount = canReadMediaUsageCount(user, locals.tokenScopes);\n\tconst usageResult = await handleMediaUsageSummaries(\n\t\temdash.db,\n\t\titemsWithUrl.map((item) => item.id),\n\t\t{ includeCount },\n\t);\n\tif (!usageResult.success) return unwrapResult(usageResult);\n\n\tconst itemsWithUsage = [];\n\tfor (const item of itemsWithUrl) {\n\t\tconst usage = usageResult.data[item.id];\n\t\tif (!usage) return apiError(\"MEDIA_USAGE_READ_ERROR\", \"Failed to read media usage\", 500);\n\t\titemsWithUsage.push({ ...item, usage });\n\t}\n\n\treturn apiSuccess({ items: itemsWithUsage, nextCursor: result.data.nextCursor });\n};\n\n/**\n * Upload media file\n *\n * Uses the configured storage adapter to store the file.\n */\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!emdash?.handleMediaCreate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\tif (!emdash?.storage) {\n\t\treturn apiError(\"NO_STORAGE\", \"Storage not configured\", 500);\n\t}\n\n\ttry {\n\t\tconst rawMax = emdash.config.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE;\n\t\tif (!Number.isFinite(rawMax) || rawMax <= 0) {\n\t\t\treturn apiError(\"CONFIGURATION_ERROR\", \"Invalid maxUploadSize configuration\", 500);\n\t\t}\n\t\tconst maxUploadSize = rawMax;\n\n\t\t// Best-effort size check before buffering the full multipart body\n\t\tconst contentLength = request.headers.get(\"Content-Length\");\n\t\tif (contentLength && parseInt(contentLength, 10) > maxUploadSize) {\n\t\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Upload too large\", 413);\n\t\t}\n\n\t\tconst formData = await request.formData();\n\t\tconst fileEntry = formData.get(\"file\");\n\t\tconst file = fileEntry instanceof File ? fileEntry : null;\n\n\t\tif (!file) {\n\t\t\treturn apiError(\"NO_FILE\", \"No file provided\", 400);\n\t\t}\n\n\t\t// Validate file type — widen the allowlist when a field-specific list is configured\n\t\tconst fieldIdEntry = formData.get(\"fieldId\");\n\t\tconst fieldId =\n\t\t\ttypeof fieldIdEntry === \"string\" && fieldIdEntry.length > 0 ? fieldIdEntry : null;\n\n\t\tconst fieldAllowlist = fieldId ? await resolveFieldAllowlist(emdash.db, fieldId) : null;\n\t\tconst allowlist = fieldAllowlist ?? [...GLOBAL_UPLOAD_ALLOWLIST];\n\n\t\tif (!matchesMimeAllowlist(file.type, allowlist)) {\n\t\t\treturn apiError(\"INVALID_TYPE\", \"File type not allowed\", 400);\n\t\t}\n\n\t\t// Check file size before buffering\n\t\tif (file.size > maxUploadSize) {\n\t\t\treturn apiError(\n\t\t\t\t\"PAYLOAD_TOO_LARGE\",\n\t\t\t\t`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,\n\t\t\t\t413,\n\t\t\t);\n\t\t}\n\n\t\t// Get file content and compute hash\n\t\tconst buffer = new Uint8Array(await file.arrayBuffer());\n\t\tconst contentHash = await computeContentHash(buffer);\n\n\t\t// Check for existing media with same content hash (deduplication)\n\t\tconst repo = new MediaRepository(emdash.db);\n\t\tconst existing = await repo.findByContentHash(contentHash);\n\t\tif (existing) {\n\t\t\t// Same content already exists - return existing item\n\t\t\tconst itemWithUrl = addUrlToMedia(existing);\n\t\t\treturn apiSuccess({ item: itemWithUrl, deduplicated: true });\n\t\t}\n\n\t\t// Generate unique storage key\n\t\tconst id = ulid();\n\t\tconst ext = path.extname(file.name) || \"\";\n\t\tconst storageKey = `${id}${ext}`;\n\n\t\t// Upload to storage using the configured adapter\n\t\tawait emdash.storage.upload({\n\t\t\tkey: storageKey,\n\t\t\tbody: buffer,\n\t\t\tcontentType: file.type,\n\t\t});\n\n\t\t// Get image dimensions from form data (sent by client)\n\t\tconst widthEntry = formData.get(\"width\");\n\t\tconst widthStr = typeof widthEntry === \"string\" ? widthEntry : null;\n\t\tconst heightEntry = formData.get(\"height\");\n\t\tconst heightStr = typeof heightEntry === \"string\" ? heightEntry : null;\n\t\tconst width = widthStr ? parseInt(widthStr, 10) : undefined;\n\t\tconst height = heightStr ? parseInt(heightStr, 10) : undefined;\n\n\t\t// Derive dimensions + LQIP placeholders via the shared helper.\n\t\t// If the client sent a downscaled thumbnail, decode that for the blurhash\n\t\t// (avoids OOM on large originals on memory-constrained runtimes).\n\t\tconst thumbnailEntry = formData.get(\"thumbnail\");\n\t\tconst thumbnail = thumbnailEntry instanceof File ? thumbnailEntry : null;\n\t\tconst enriched = await enrichImageMetadata(buffer, file.type, {\n\t\t\tknownDimensions: width != null && height != null ? { width, height } : undefined,\n\t\t\tplaceholder: thumbnail\n\t\t\t\t? { bytes: new Uint8Array(await thumbnail.arrayBuffer()), contentType: thumbnail.type }\n\t\t\t\t: undefined,\n\t\t});\n\n\t\t// Create media record\n\t\tconst result = await emdash.handleMediaCreate({\n\t\t\tfilename: file.name,\n\t\t\tmimeType: normalizeMime(file.type),\n\t\t\tsize: file.size,\n\t\t\t// Client dimensions win over server header dimensions: the browser's\n\t\t\t// naturalWidth/Height apply EXIF orientation, while image-size reports\n\t\t\t// raw (pre-orientation) header dims — swapped for 90°/270° JPEGs.\n\t\t\twidth: width ?? enriched.width,\n\t\t\theight: height ?? enriched.height,\n\t\t\tstorageKey,\n\t\t\tcontentHash,\n\t\t\tblurhash: enriched.blurhash,\n\t\t\tdominantColor: enriched.dominantColor,\n\t\t\tauthorId: user?.id,\n\t\t});\n\n\t\tif (!result.success) {\n\t\t\t// Clean up the uploaded file on failure\n\t\t\ttry {\n\t\t\t\tawait emdash.storage.delete(storageKey);\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors\n\t\t\t}\n\t\t\treturn unwrapResult(result);\n\t\t}\n\n\t\t// Add URL to the response (relative URL for portability)\n\t\tconst itemWithUrl = addUrlToMedia(result.data.item);\n\n\t\treturn apiSuccess({ item: itemWithUrl }, 201);\n\t} catch (error) {\n\t\treturn handleError(error, \"Upload failed\", \"UPLOAD_ERROR\");\n\t}\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"media.mjs","names":[],"sources":["../../../../src/astro/routes/api/media.ts"],"sourcesContent":["/**\n * Media list and upload endpoint\n *\n * GET /_emdash/api/media - List all media\n * POST /_emdash/api/media - Upload new media (via configured storage adapter)\n */\n\nimport * as path from \"node:path\";\n\nimport type { APIRoute } from \"astro\";\nimport { ulid } from \"ulidx\";\n\nimport { canReadMediaUsageCount, requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError, unwrapResult } from \"#api/error.js\";\nimport { GLOBAL_UPLOAD_ALLOWLIST, resolveFieldAllowlist } from \"#api/handlers/media-allowlist.js\";\nimport { handleMediaUsageSummaries } from \"#api/handlers/media-usage.js\";\nimport { isParseError, parseQuery } from \"#api/parse.js\";\nimport { DEFAULT_MAX_UPLOAD_SIZE, formatFileSize, mediaListQuery } from \"#api/schemas.js\";\nimport { MediaRepository } from \"#db/repositories/media.js\";\nimport { enrichImageMetadata } from \"#media/enrich.js\";\nimport { matchesMimeAllowlist, normalizeMime } from \"#media/mime.js\";\nimport { computeContentHash } from \"#utils/hash.js\";\n\nimport type { MediaItem } from \"../../types.js\";\n\nexport const prerender = false;\n\n/**\n * Add URL to media items\n * Uses relative URLs to ensure portability across deployments\n */\nfunction addUrlToMedia(item: MediaItem): MediaItem & { url: string } {\n\treturn {\n\t\t...item,\n\t\turl: `/_emdash/api/media/file/${item.storageKey}`,\n\t};\n}\n\n/**\n * List media items\n */\nexport const GET: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"media:read\");\n\tif (denied) return denied;\n\n\tif (!emdash?.handleMediaList) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\tconst url = new URL(request.url);\n\tconst query = parseQuery(url, mediaListQuery);\n\tif (isParseError(query)) return query;\n\n\tconst result = await emdash.handleMediaList({\n\t\tcursor: query.cursor,\n\t\tlimit: query.limit,\n\t\tmimeType: query.mimeType,\n\t\tq: query.q,\n\t});\n\n\tif (!result.success) {\n\t\treturn unwrapResult(result);\n\t}\n\n\t// Add URL to each media item (relative URLs for portability)\n\tconst itemsWithUrl = result.data.items.map((item) => addUrlToMedia(item));\n\tif (query.includeUsage !== \"1\") {\n\t\treturn apiSuccess({ items: itemsWithUrl, nextCursor: result.data.nextCursor });\n\t}\n\n\tconst includeCount = canReadMediaUsageCount(user, locals.tokenScopes);\n\tconst usageResult = await handleMediaUsageSummaries(\n\t\temdash.db,\n\t\titemsWithUrl.map((item) => item.id),\n\t\t{ includeCount },\n\t);\n\tif (!usageResult.success) return unwrapResult(usageResult);\n\n\tconst itemsWithUsage = [];\n\tfor (const item of itemsWithUrl) {\n\t\tconst usage = usageResult.data[item.id];\n\t\tif (!usage) return apiError(\"MEDIA_USAGE_READ_ERROR\", \"Failed to read media usage\", 500);\n\t\titemsWithUsage.push({ ...item, usage });\n\t}\n\n\treturn apiSuccess({ items: itemsWithUsage, nextCursor: result.data.nextCursor });\n};\n\n/**\n * Upload media file\n *\n * Uses the configured storage adapter to store the file.\n */\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tconst denied = requirePerm(user, \"media:upload\");\n\tif (denied) return denied;\n\n\tif (!emdash?.handleMediaCreate) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\tif (!emdash?.storage) {\n\t\treturn apiError(\"NO_STORAGE\", \"Storage not configured\", 500);\n\t}\n\n\ttry {\n\t\tconst rawMax = emdash.config.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE;\n\t\tif (!Number.isFinite(rawMax) || rawMax <= 0) {\n\t\t\treturn apiError(\"CONFIGURATION_ERROR\", \"Invalid maxUploadSize configuration\", 500);\n\t\t}\n\t\tconst maxUploadSize = rawMax;\n\n\t\t// Best-effort size check before buffering the full multipart body\n\t\tconst contentLength = request.headers.get(\"Content-Length\");\n\t\tif (contentLength && parseInt(contentLength, 10) > maxUploadSize) {\n\t\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Upload too large\", 413);\n\t\t}\n\n\t\tconst formData = await request.formData();\n\t\tconst fileEntry = formData.get(\"file\");\n\t\tconst file = fileEntry instanceof File ? fileEntry : null;\n\n\t\tif (!file) {\n\t\t\treturn apiError(\"NO_FILE\", \"No file provided\", 400);\n\t\t}\n\n\t\t// Validate file type — widen the allowlist when a field-specific list is configured\n\t\tconst fieldIdEntry = formData.get(\"fieldId\");\n\t\tconst fieldId =\n\t\t\ttypeof fieldIdEntry === \"string\" && fieldIdEntry.length > 0 ? fieldIdEntry : null;\n\n\t\tconst fieldAllowlist = fieldId ? await resolveFieldAllowlist(emdash.db, fieldId) : null;\n\t\tconst allowlist = fieldAllowlist ?? [...GLOBAL_UPLOAD_ALLOWLIST];\n\n\t\tif (!matchesMimeAllowlist(file.type, allowlist)) {\n\t\t\treturn apiError(\"INVALID_TYPE\", \"File type not allowed\", 400);\n\t\t}\n\n\t\t// Check file size before buffering\n\t\tif (file.size > maxUploadSize) {\n\t\t\treturn apiError(\n\t\t\t\t\"PAYLOAD_TOO_LARGE\",\n\t\t\t\t`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,\n\t\t\t\t413,\n\t\t\t);\n\t\t}\n\n\t\t// Get file content and compute hash\n\t\tconst buffer = new Uint8Array(await file.arrayBuffer());\n\t\tconst contentHash = await computeContentHash(buffer);\n\n\t\t// Check for existing media with same content hash (deduplication)\n\t\tconst repo = new MediaRepository(emdash.db);\n\t\tconst existing = await repo.findByContentHash(contentHash);\n\t\tif (existing) {\n\t\t\t// Same content already exists - return existing item\n\t\t\tconst itemWithUrl = addUrlToMedia(existing);\n\t\t\treturn apiSuccess({ item: itemWithUrl, deduplicated: true });\n\t\t}\n\n\t\t// Generate unique storage key\n\t\tconst id = ulid();\n\t\tconst ext = path.extname(file.name) || \"\";\n\t\tconst storageKey = `${id}${ext}`;\n\n\t\t// Upload to storage using the configured adapter\n\t\tawait emdash.storage.upload({\n\t\t\tkey: storageKey,\n\t\t\tbody: buffer,\n\t\t\tcontentType: file.type,\n\t\t});\n\n\t\t// Get image dimensions from form data (sent by client)\n\t\tconst widthEntry = formData.get(\"width\");\n\t\tconst widthStr = typeof widthEntry === \"string\" ? widthEntry : null;\n\t\tconst heightEntry = formData.get(\"height\");\n\t\tconst heightStr = typeof heightEntry === \"string\" ? heightEntry : null;\n\t\tconst width = widthStr ? parseInt(widthStr, 10) : undefined;\n\t\tconst height = heightStr ? parseInt(heightStr, 10) : undefined;\n\n\t\t// Derive dimensions + LQIP placeholders via the shared helper.\n\t\t// If the client sent a downscaled thumbnail, decode that for the blurhash\n\t\t// (avoids OOM on large originals on memory-constrained runtimes).\n\t\tconst thumbnailEntry = formData.get(\"thumbnail\");\n\t\tconst thumbnail = thumbnailEntry instanceof File ? thumbnailEntry : null;\n\t\tconst enriched = await enrichImageMetadata(buffer, file.type, {\n\t\t\tknownDimensions: width != null && height != null ? { width, height } : undefined,\n\t\t\tplaceholder: thumbnail\n\t\t\t\t? { bytes: new Uint8Array(await thumbnail.arrayBuffer()), contentType: thumbnail.type }\n\t\t\t\t: undefined,\n\t\t});\n\n\t\t// Create media record\n\t\tconst result = await emdash.handleMediaCreate({\n\t\t\tfilename: file.name,\n\t\t\tmimeType: normalizeMime(file.type),\n\t\t\tsize: file.size,\n\t\t\t// Client dimensions win over server header dimensions: the browser's\n\t\t\t// naturalWidth/Height apply EXIF orientation, while image-size reports\n\t\t\t// raw (pre-orientation) header dims — swapped for 90°/270° JPEGs.\n\t\t\twidth: width ?? enriched.width,\n\t\t\theight: height ?? enriched.height,\n\t\t\tstorageKey,\n\t\t\tcontentHash,\n\t\t\tblurhash: enriched.blurhash,\n\t\t\tdominantColor: enriched.dominantColor,\n\t\t\tauthorId: user?.id,\n\t\t});\n\n\t\tif (!result.success) {\n\t\t\t// Clean up the uploaded file on failure\n\t\t\ttry {\n\t\t\t\tawait emdash.storage.delete(storageKey);\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors\n\t\t\t}\n\t\t\treturn unwrapResult(result);\n\t\t}\n\n\t\t// Add URL to the response (relative URL for portability)\n\t\tconst itemWithUrl = addUrlToMedia(result.data.item);\n\n\t\treturn apiSuccess({ item: itemWithUrl }, 201);\n\t} catch (error) {\n\t\treturn handleError(error, \"Upload failed\", \"UPLOAD_ERROR\");\n\t}\n};\n"],"mappings":"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"}
@@ -1,17 +1,17 @@
1
- import "../../../../../../config-DyF3OKg_.mjs";
2
- import "../../../../../../after-B1IIdH3Y.mjs";
3
- import "../../../../../../object-cache-Bok5j2ae.mjs";
4
- import "../../../../../../base64-B-PsqheR.mjs";
5
- import "../../../../../../types-XrQQ-Aex.mjs";
6
- import { a as handleMenuItemDelete, s as handleMenuItemUpdate } from "../../../../../../menus-BxPLFfIc.mjs";
7
- import { a as unwrapResult, r as handleError, t as apiError } from "../../../../../../error-DmmN74gW.mjs";
8
- import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../../../parse-BL49yb9D.mjs";
9
- import { B as localeFilterQuery } from "../../../../../../media-DWIzoVFL.mjs";
10
- import { ut as updateMenuItemBody } from "../../../../../../redirects-BOn2DIUs.mjs";
11
- import "../../../../../../relations-C4duJnwI.mjs";
12
- import "../../../../../../status-COfycGIU.mjs";
1
+ import "../../../../../../config-C3EBstHi.mjs";
2
+ import "../../../../../../after-CBU2vkoO.mjs";
3
+ import "../../../../../../object-cache-71tPfV4A.mjs";
4
+ import "../../../../../../base64-CvLhUKQv.mjs";
5
+ import "../../../../../../types-CQPWhPxE.mjs";
6
+ import { a as handleMenuItemDelete, s as handleMenuItemUpdate } from "../../../../../../menus-fxvEDYE6.mjs";
7
+ import { a as unwrapResult, r as handleError, t as apiError } from "../../../../../../error-DupyJZE2.mjs";
8
+ import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../../../parse-DFbuc-Fh.mjs";
9
+ import { et as localeFilterQuery } from "../../../../../../media-BWWWb8kg.mjs";
10
+ import { mt as updateMenuItemBody } from "../../../../../../redirects-cV9g7V-0.mjs";
11
+ import "../../../../../../relations-BBApI4e2.mjs";
12
+ import "../../../../../../status-CWVYpbD8.mjs";
13
13
  import "../../../../../../api/schemas/index.mjs";
14
- import { r as requirePerm } from "../../../../../../authorize-R5iYYrpa.mjs";
14
+ import { r as requirePerm } from "../../../../../../authorize-1iFJzPaZ.mjs";
15
15
 
16
16
  //#region src/astro/routes/api/menus/[name]/items/[id].ts
17
17
  const prerender = false;
@@ -1,17 +1,17 @@
1
- import "../../../../../config-DyF3OKg_.mjs";
2
- import "../../../../../after-B1IIdH3Y.mjs";
3
- import "../../../../../object-cache-Bok5j2ae.mjs";
4
- import "../../../../../base64-B-PsqheR.mjs";
5
- import "../../../../../types-XrQQ-Aex.mjs";
6
- import { i as handleMenuItemCreate } from "../../../../../menus-BxPLFfIc.mjs";
7
- import { a as unwrapResult, r as handleError } from "../../../../../error-DmmN74gW.mjs";
8
- import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../../parse-BL49yb9D.mjs";
9
- import { B as localeFilterQuery } from "../../../../../media-DWIzoVFL.mjs";
10
- import { tt as createMenuItemBody } from "../../../../../redirects-BOn2DIUs.mjs";
11
- import "../../../../../relations-C4duJnwI.mjs";
12
- import "../../../../../status-COfycGIU.mjs";
1
+ import "../../../../../config-C3EBstHi.mjs";
2
+ import "../../../../../after-CBU2vkoO.mjs";
3
+ import "../../../../../object-cache-71tPfV4A.mjs";
4
+ import "../../../../../base64-CvLhUKQv.mjs";
5
+ import "../../../../../types-CQPWhPxE.mjs";
6
+ import { i as handleMenuItemCreate } from "../../../../../menus-fxvEDYE6.mjs";
7
+ import { a as unwrapResult, r as handleError } from "../../../../../error-DupyJZE2.mjs";
8
+ import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../../parse-DFbuc-Fh.mjs";
9
+ import { et as localeFilterQuery } from "../../../../../media-BWWWb8kg.mjs";
10
+ import { at as createMenuItemBody } from "../../../../../redirects-cV9g7V-0.mjs";
11
+ import "../../../../../relations-BBApI4e2.mjs";
12
+ import "../../../../../status-CWVYpbD8.mjs";
13
13
  import "../../../../../api/schemas/index.mjs";
14
- import { r as requirePerm } from "../../../../../authorize-R5iYYrpa.mjs";
14
+ import { r as requirePerm } from "../../../../../authorize-1iFJzPaZ.mjs";
15
15
 
16
16
  //#region src/astro/routes/api/menus/[name]/items.ts
17
17
  const prerender = false;
@@ -1,17 +1,17 @@
1
- import "../../../../../config-DyF3OKg_.mjs";
2
- import "../../../../../after-B1IIdH3Y.mjs";
3
- import "../../../../../object-cache-Bok5j2ae.mjs";
4
- import "../../../../../base64-B-PsqheR.mjs";
5
- import "../../../../../types-XrQQ-Aex.mjs";
6
- import { o as handleMenuItemReorder } from "../../../../../menus-BxPLFfIc.mjs";
7
- import { a as unwrapResult, r as handleError } from "../../../../../error-DmmN74gW.mjs";
8
- import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../../parse-BL49yb9D.mjs";
9
- import { B as localeFilterQuery } from "../../../../../media-DWIzoVFL.mjs";
10
- import { ct as reorderMenuItemsBody } from "../../../../../redirects-BOn2DIUs.mjs";
11
- import "../../../../../relations-C4duJnwI.mjs";
12
- import "../../../../../status-COfycGIU.mjs";
1
+ import "../../../../../config-C3EBstHi.mjs";
2
+ import "../../../../../after-CBU2vkoO.mjs";
3
+ import "../../../../../object-cache-71tPfV4A.mjs";
4
+ import "../../../../../base64-CvLhUKQv.mjs";
5
+ import "../../../../../types-CQPWhPxE.mjs";
6
+ import { o as handleMenuItemReorder } from "../../../../../menus-fxvEDYE6.mjs";
7
+ import { a as unwrapResult, r as handleError } from "../../../../../error-DupyJZE2.mjs";
8
+ import { i as parseQuery, n as parseBody, t as isParseError } from "../../../../../parse-DFbuc-Fh.mjs";
9
+ import { et as localeFilterQuery } from "../../../../../media-BWWWb8kg.mjs";
10
+ import { ft as reorderMenuItemsBody } from "../../../../../redirects-cV9g7V-0.mjs";
11
+ import "../../../../../relations-BBApI4e2.mjs";
12
+ import "../../../../../status-CWVYpbD8.mjs";
13
13
  import "../../../../../api/schemas/index.mjs";
14
- import { r as requirePerm } from "../../../../../authorize-R5iYYrpa.mjs";
14
+ import { r as requirePerm } from "../../../../../authorize-1iFJzPaZ.mjs";
15
15
 
16
16
  //#region src/astro/routes/api/menus/[name]/reorder.ts
17
17
  const prerender = false;