emdash 0.31.1 → 0.32.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 (554) hide show
  1. package/dist/api/route-utils.d.mts +3 -3
  2. package/dist/api/route-utils.mjs +16 -14
  3. package/dist/api/route-utils.mjs.map +1 -1
  4. package/dist/api/schemas/index.d.mts +2 -2
  5. package/dist/api/schemas/index.mjs +4 -4
  6. package/dist/{api-BkqFzIu4.mjs → api-DgPbMlng.mjs} +24 -20
  7. package/dist/api-DgPbMlng.mjs.map +1 -0
  8. package/dist/{api-tokens-uPt8UDpx.mjs → api-tokens-p-iMzvXR.mjs} +2 -2
  9. package/dist/{api-tokens-uPt8UDpx.mjs.map → api-tokens-p-iMzvXR.mjs.map} +1 -1
  10. package/dist/{apply-DuXsqll7.mjs → apply-1_6ra7NP.mjs} +19 -19
  11. package/dist/{apply-DuXsqll7.mjs.map → apply-1_6ra7NP.mjs.map} +1 -1
  12. package/dist/astro/index.d.mts +12 -12
  13. package/dist/astro/index.mjs +5 -1
  14. package/dist/astro/index.mjs.map +1 -1
  15. package/dist/astro/middleware/auth.d.mts +10 -10
  16. package/dist/astro/middleware/auth.mjs +5 -5
  17. package/dist/astro/middleware/redirect.mjs +5 -5
  18. package/dist/astro/middleware/request-context.mjs +1 -1
  19. package/dist/astro/middleware.d.mts +10 -10
  20. package/dist/astro/middleware.mjs +87 -59
  21. package/dist/astro/middleware.mjs.map +1 -1
  22. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
  23. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
  24. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  25. package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
  26. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  27. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +8 -8
  28. package/dist/astro/routes/api/admin/byline-fields/index.mjs +8 -8
  29. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +8 -8
  30. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +15 -14
  31. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
  32. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +15 -14
  33. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
  34. package/dist/astro/routes/api/admin/bylines/index.mjs +15 -15
  35. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +11 -11
  36. package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
  37. package/dist/astro/routes/api/admin/comments/bulk.mjs +9 -9
  38. package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
  39. package/dist/astro/routes/api/admin/comments/index.mjs +9 -9
  40. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
  41. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  42. package/dist/astro/routes/api/admin/media-usage/repair.mjs +12 -10
  43. package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
  44. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
  45. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
  46. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +36 -34
  47. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
  48. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +36 -34
  49. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
  50. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +36 -34
  51. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
  52. package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
  53. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +36 -34
  54. package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
  55. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +36 -34
  56. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
  57. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +36 -34
  58. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
  59. package/dist/astro/routes/api/admin/plugins/index.mjs +36 -34
  60. package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
  61. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
  62. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +36 -34
  63. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
  64. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +36 -34
  65. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
  66. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +36 -34
  67. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
  68. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +36 -34
  69. package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
  70. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +37 -35
  71. package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
  72. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +36 -34
  73. package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
  74. package/dist/astro/routes/api/admin/plugins/registry/install.mjs +37 -35
  75. package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
  76. package/dist/astro/routes/api/admin/plugins/updates.mjs +36 -34
  77. package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
  78. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +36 -34
  79. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
  80. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
  81. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +36 -34
  82. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
  83. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
  84. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  85. package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
  86. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
  87. package/dist/astro/routes/api/admin/users/index.mjs +6 -6
  88. package/dist/astro/routes/api/auth/dev-bypass.mjs +7 -6
  89. package/dist/astro/routes/api/auth/dev-bypass.mjs.map +1 -1
  90. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  91. package/dist/astro/routes/api/auth/invite/complete.mjs +7 -7
  92. package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
  93. package/dist/astro/routes/api/auth/invite/register-options.mjs +7 -7
  94. package/dist/astro/routes/api/auth/logout.mjs +3 -3
  95. package/dist/astro/routes/api/auth/magic-link/send.mjs +8 -8
  96. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  97. package/dist/astro/routes/api/auth/me.mjs +7 -7
  98. package/dist/astro/routes/api/auth/mode.mjs +2 -2
  99. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +2 -2
  100. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +1 -1
  101. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
  102. package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
  103. package/dist/astro/routes/api/auth/passkey/options.mjs +8 -8
  104. package/dist/astro/routes/api/auth/passkey/register/options.mjs +7 -7
  105. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +7 -7
  106. package/dist/astro/routes/api/auth/passkey/verify.mjs +7 -7
  107. package/dist/astro/routes/api/auth/signup/complete.mjs +7 -7
  108. package/dist/astro/routes/api/auth/signup/request.mjs +8 -8
  109. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  110. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +11 -11
  111. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +10 -10
  112. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
  113. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
  114. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
  115. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
  116. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +9 -9
  117. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +7 -7
  118. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +16 -14
  119. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
  120. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +16 -14
  121. package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
  122. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
  123. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
  124. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +7 -7
  125. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +14 -13
  126. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
  127. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
  128. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
  129. package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -1
  130. package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
  131. package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
  132. package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
  133. package/dist/astro/routes/api/content/_collection_/index.mjs +7 -7
  134. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
  135. package/dist/astro/routes/api/dashboard.mjs +7 -7
  136. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  137. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  138. package/dist/astro/routes/api/import/probe.mjs +12 -12
  139. package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
  140. package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
  141. package/dist/astro/routes/api/import/wordpress/execute.mjs +12 -11
  142. package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
  143. package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
  144. package/dist/astro/routes/api/import/wordpress/media.mjs +7 -7
  145. package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
  146. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
  147. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +11 -9
  148. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
  149. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  150. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +12 -12
  151. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
  152. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +20 -19
  153. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
  154. package/dist/astro/routes/api/manifest.mjs +3 -3
  155. package/dist/astro/routes/api/mcp.mjs +41 -41
  156. package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -1
  157. package/dist/astro/routes/api/media/_id_/confirm.mjs +105 -36
  158. package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
  159. package/dist/astro/routes/api/media/_id_/upload.d.mts +8 -0
  160. package/dist/astro/routes/api/media/_id_/upload.d.mts.map +1 -0
  161. package/dist/astro/routes/api/media/_id_/upload.mjs +168 -0
  162. package/dist/astro/routes/api/media/_id_/upload.mjs.map +1 -0
  163. package/dist/astro/routes/api/media/_id_/usage.mjs +14 -12
  164. package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
  165. package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
  166. package/dist/astro/routes/api/media/_id_.mjs +21 -14
  167. package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
  168. package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
  169. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
  170. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
  171. package/dist/astro/routes/api/media/providers/index.mjs +3 -3
  172. package/dist/astro/routes/api/media/upload-url.d.mts.map +1 -1
  173. package/dist/astro/routes/api/media/upload-url.mjs +34 -23
  174. package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
  175. package/dist/astro/routes/api/media.mjs +13 -12
  176. package/dist/astro/routes/api/media.mjs.map +1 -1
  177. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +9 -8
  178. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
  179. package/dist/astro/routes/api/menus/_name_/items.mjs +9 -8
  180. package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
  181. package/dist/astro/routes/api/menus/_name_/reorder.mjs +9 -8
  182. package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
  183. package/dist/astro/routes/api/menus/_name_/translations.mjs +9 -8
  184. package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
  185. package/dist/astro/routes/api/menus/_name_.mjs +9 -8
  186. package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
  187. package/dist/astro/routes/api/menus/index.mjs +9 -8
  188. package/dist/astro/routes/api/menus/index.mjs.map +1 -1
  189. package/dist/astro/routes/api/oauth/authorize.mjs +5 -5
  190. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  191. package/dist/astro/routes/api/oauth/device/code.mjs +7 -7
  192. package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
  193. package/dist/astro/routes/api/oauth/register.mjs +3 -3
  194. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  195. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  196. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  197. package/dist/astro/routes/api/openapi.json.mjs +30 -8
  198. package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
  199. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +5 -5
  200. package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
  201. package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
  202. package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
  203. package/dist/astro/routes/api/redirects/index.mjs +11 -11
  204. package/dist/astro/routes/api/relations/_id_/index.mjs +16 -14
  205. package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
  206. package/dist/astro/routes/api/relations/_id_/translations.mjs +12 -10
  207. package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
  208. package/dist/astro/routes/api/relations/index.mjs +16 -14
  209. package/dist/astro/routes/api/relations/index.mjs.map +1 -1
  210. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
  211. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
  212. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +36 -34
  213. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
  214. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +36 -34
  215. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
  216. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +36 -34
  217. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
  218. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +36 -34
  219. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
  220. package/dist/astro/routes/api/schema/collections/index.mjs +36 -34
  221. package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
  222. package/dist/astro/routes/api/schema/index.mjs +10 -8
  223. package/dist/astro/routes/api/schema/index.mjs.map +1 -1
  224. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +36 -34
  225. package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
  226. package/dist/astro/routes/api/schema/orphans/index.mjs +36 -34
  227. package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
  228. package/dist/astro/routes/api/search/enable.mjs +12 -11
  229. package/dist/astro/routes/api/search/enable.mjs.map +1 -1
  230. package/dist/astro/routes/api/search/index.mjs +11 -10
  231. package/dist/astro/routes/api/search/index.mjs.map +1 -1
  232. package/dist/astro/routes/api/search/rebuild.mjs +12 -11
  233. package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
  234. package/dist/astro/routes/api/search/stats.mjs +8 -7
  235. package/dist/astro/routes/api/search/stats.mjs.map +1 -1
  236. package/dist/astro/routes/api/search/suggest.mjs +11 -10
  237. package/dist/astro/routes/api/search/suggest.mjs.map +1 -1
  238. package/dist/astro/routes/api/sections/_slug_.mjs +10 -10
  239. package/dist/astro/routes/api/sections/index.mjs +10 -10
  240. package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +4 -4
  241. package/dist/astro/routes/api/settings/backups/archives/index.mjs +4 -4
  242. package/dist/astro/routes/api/settings/backups/export.mjs +4 -4
  243. package/dist/astro/routes/api/settings/backups/index.mjs +5 -5
  244. package/dist/astro/routes/api/settings/email.mjs +4 -4
  245. package/dist/astro/routes/api/settings.mjs +12 -12
  246. package/dist/astro/routes/api/setup/admin-verify.mjs +8 -8
  247. package/dist/astro/routes/api/setup/admin.mjs +8 -8
  248. package/dist/astro/routes/api/setup/dev-bypass.mjs +24 -22
  249. package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
  250. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  251. package/dist/astro/routes/api/setup/index.mjs +24 -22
  252. package/dist/astro/routes/api/setup/index.mjs.map +1 -1
  253. package/dist/astro/routes/api/setup/status.mjs +3 -3
  254. package/dist/astro/routes/api/snapshot.mjs +5 -5
  255. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +14 -13
  256. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
  257. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +14 -13
  258. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
  259. package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
  260. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +19 -15
  261. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
  262. package/dist/astro/routes/api/taxonomies/index.mjs +14 -13
  263. package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
  264. package/dist/astro/routes/api/themes/preview.mjs +4 -4
  265. package/dist/astro/routes/api/typegen.mjs +5 -5
  266. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +1 -1
  267. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +1 -1
  268. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +7 -7
  269. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +10 -10
  270. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +10 -10
  271. package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -6
  272. package/dist/astro/routes/api/widget-areas/index.mjs +10 -10
  273. package/dist/astro/routes/api/widget-components.mjs +2 -2
  274. package/dist/astro/routes/robots.txt.mjs +5 -5
  275. package/dist/astro/routes/sitemap-_collection_.xml.mjs +7 -7
  276. package/dist/astro/routes/sitemap.xml.mjs +5 -5
  277. package/dist/astro/types.d.mts +14 -13
  278. package/dist/astro/types.d.mts.map +1 -1
  279. package/dist/auth/providers/github.d.mts +1 -1
  280. package/dist/auth/providers/google.d.mts +1 -1
  281. package/dist/{authorize-6WPZa1xZ.mjs → authorize-R5iYYrpa.mjs} +2 -2
  282. package/dist/{authorize-6WPZa1xZ.mjs.map → authorize-R5iYYrpa.mjs.map} +1 -1
  283. package/dist/{backup-DoW-H0r4.mjs → backup-LpQ1V_sR.mjs} +2 -2
  284. package/dist/{backup-DoW-H0r4.mjs.map → backup-LpQ1V_sR.mjs.map} +1 -1
  285. package/dist/{byline-BTOxUlFF.mjs → byline-C5TAqs8N.mjs} +6 -6
  286. package/dist/{byline-BTOxUlFF.mjs.map → byline-C5TAqs8N.mjs.map} +1 -1
  287. package/dist/{byline-fields-CdU_LTF1.mjs → byline-fields-T9ZLZMxJ.mjs} +1 -1
  288. package/dist/{byline-fields-CdU_LTF1.mjs.map → byline-fields-T9ZLZMxJ.mjs.map} +1 -1
  289. package/dist/{bylines-Bc-3xtpz.mjs → bylines-B8-WGdja.mjs} +6 -6
  290. package/dist/{bylines-Bc-3xtpz.mjs.map → bylines-B8-WGdja.mjs.map} +1 -1
  291. package/dist/{bylines-oVN0iPwX.mjs → bylines-DGuqBLjV.mjs} +5 -5
  292. package/dist/{bylines-oVN0iPwX.mjs.map → bylines-DGuqBLjV.mjs.map} +1 -1
  293. package/dist/{cache-DDwy3JDW.mjs → cache-B26cufFd.mjs} +2 -2
  294. package/dist/{cache-DDwy3JDW.mjs.map → cache-B26cufFd.mjs.map} +1 -1
  295. package/dist/{chunks-Bq-5DPrD.mjs → chunks-D5dlPeRb.mjs} +2 -2
  296. package/dist/{chunks-Bq-5DPrD.mjs.map → chunks-D5dlPeRb.mjs.map} +1 -1
  297. package/dist/cli/index.mjs +20 -19
  298. package/dist/cli/index.mjs.map +1 -1
  299. package/dist/client/cf-access.d.mts +1 -1
  300. package/dist/client/index.d.mts +1 -1
  301. package/dist/client/index.mjs +2 -2
  302. package/dist/{comment-B9cUud8H.mjs → comment-DnTxxVHv.mjs} +2 -2
  303. package/dist/{comment-B9cUud8H.mjs.map → comment-DnTxxVHv.mjs.map} +1 -1
  304. package/dist/{comment-reaction-VUXO981F.mjs → comment-reaction-BUlUz2bh.mjs} +2 -2
  305. package/dist/{comment-reaction-VUXO981F.mjs.map → comment-reaction-BUlUz2bh.mjs.map} +1 -1
  306. package/dist/{comments-PXj1E9Si.mjs → comments-QPKgK3nk.mjs} +3 -3
  307. package/dist/{comments-PXj1E9Si.mjs.map → comments-QPKgK3nk.mjs.map} +1 -1
  308. package/dist/{config-B6b_BobR.mjs → config-DyF3OKg_.mjs} +15 -10
  309. package/dist/config-DyF3OKg_.mjs.map +1 -0
  310. package/dist/{content-BC43pICM.mjs → content-CpfKV9QE.mjs} +9 -9
  311. package/dist/content-CpfKV9QE.mjs.map +1 -0
  312. package/dist/{content-refresh-CPMSauD3.mjs → content-refresh-DgqUBeTv.mjs} +5 -5
  313. package/dist/{content-refresh-CPMSauD3.mjs.map → content-refresh-DgqUBeTv.mjs.map} +1 -1
  314. package/dist/{context-DOurRIrb.mjs → context-kO4YZB03.mjs} +10 -10
  315. package/dist/{context-DOurRIrb.mjs.map → context-kO4YZB03.mjs.map} +1 -1
  316. package/dist/{dashboard-CBm4zhc3.mjs → dashboard-DoBwPnON.mjs} +4 -4
  317. package/dist/{dashboard-CBm4zhc3.mjs.map → dashboard-DoBwPnON.mjs.map} +1 -1
  318. package/dist/db/index.d.mts +2 -2
  319. package/dist/db/index.mjs +3 -2
  320. package/dist/db/index.mjs.map +1 -1
  321. package/dist/{device-flow-BeA6lUS1.mjs → device-flow-DhUsyJU6.mjs} +4 -4
  322. package/dist/{device-flow-BeA6lUS1.mjs.map → device-flow-DhUsyJU6.mjs.map} +1 -1
  323. package/dist/{dialect-helpers-BtOwo2us.mjs → dialect-helpers-DFwyWjTM.mjs} +21 -2
  324. package/dist/dialect-helpers-DFwyWjTM.mjs.map +1 -0
  325. package/dist/{error-DJ98TRbQ.mjs → error-DmmN74gW.mjs} +2 -2
  326. package/dist/{error-DJ98TRbQ.mjs.map → error-DmmN74gW.mjs.map} +1 -1
  327. package/dist/{escape-CFedIB1C.mjs → escape-CdW31WsG.mjs} +1 -1
  328. package/dist/{escape-CFedIB1C.mjs.map → escape-CdW31WsG.mjs.map} +1 -1
  329. package/dist/{field-defs-cache-KfNJ_qLQ.mjs → field-defs-cache-QMVnzTH6.mjs} +2 -2
  330. package/dist/{field-defs-cache-KfNJ_qLQ.mjs.map → field-defs-cache-QMVnzTH6.mjs.map} +1 -1
  331. package/dist/{fts-manager-hyCU9OOp.mjs → fts-manager-vncsZIJm.mjs} +3 -3
  332. package/dist/{fts-manager-hyCU9OOp.mjs.map → fts-manager-vncsZIJm.mjs.map} +1 -1
  333. package/dist/{hash-DFFrkivP.mjs → hash-Cb7U1b5M.mjs} +8 -4
  334. package/dist/hash-Cb7U1b5M.mjs.map +1 -0
  335. package/dist/{hreflang-CbYeCzl9.mjs → hreflang-Bg-zMpkr.mjs} +7 -7
  336. package/dist/{hreflang-CbYeCzl9.mjs.map → hreflang-Bg-zMpkr.mjs.map} +1 -1
  337. package/dist/{hreflang-CIxH_LKz.d.mts → hreflang-CZbSx6Bg.d.mts} +2 -2
  338. package/dist/{hreflang-CIxH_LKz.d.mts.map → hreflang-CZbSx6Bg.d.mts.map} +1 -1
  339. package/dist/{import-oDYCgNYQ.mjs → import-PKhLeXvn.mjs} +2 -2
  340. package/dist/{import-oDYCgNYQ.mjs.map → import-PKhLeXvn.mjs.map} +1 -1
  341. package/dist/{index-BEui-cRd.d.mts → index-BeNto_Vs.d.mts} +3 -3
  342. package/dist/{index-BEui-cRd.d.mts.map → index-BeNto_Vs.d.mts.map} +1 -1
  343. package/dist/{index-BZJ_5FOX.d.mts → index-j9rTh3cp.d.mts} +52 -17
  344. package/dist/index-j9rTh3cp.d.mts.map +1 -0
  345. package/dist/index.d.mts +18 -18
  346. package/dist/index.mjs +49 -49
  347. package/dist/{load-Dg5gtWma.mjs → load-BwTdWE8B.mjs} +2 -2
  348. package/dist/{load-Dg5gtWma.mjs.map → load-BwTdWE8B.mjs.map} +1 -1
  349. package/dist/{loader-D4rigvjR.mjs → loader-C1XOLV5b.mjs} +4 -4
  350. package/dist/{loader-D4rigvjR.mjs.map → loader-C1XOLV5b.mjs.map} +1 -1
  351. package/dist/media/index.d.mts +1 -1
  352. package/dist/media/local-runtime.d.mts +12 -12
  353. package/dist/media/local-runtime.mjs +5 -5
  354. package/dist/{media-BJ6SRY3g.mjs → media-DWIzoVFL.mjs} +8 -4
  355. package/dist/{media-BJ6SRY3g.mjs.map → media-DWIzoVFL.mjs.map} +1 -1
  356. package/dist/{media-allowlist-jUppPWFy.mjs → media-allowlist-B7TPD6aq.mjs} +1 -1
  357. package/dist/{media-allowlist-jUppPWFy.mjs.map → media-allowlist-B7TPD6aq.mjs.map} +1 -1
  358. package/dist/{media-its1WLYv.mjs → media-tQDZEdu7.mjs} +60 -15
  359. package/dist/media-tQDZEdu7.mjs.map +1 -0
  360. package/dist/{media-upload-DNu3QySc.mjs → media-upload-Bc9Nc5Zf.mjs} +6 -6
  361. package/dist/{media-upload-DNu3QySc.mjs.map → media-upload-Bc9Nc5Zf.mjs.map} +1 -1
  362. package/dist/{media-usage-DPQ3K4xB.mjs → media-usage-DPJcNaAq.mjs} +6 -6
  363. package/dist/{media-usage-DPQ3K4xB.mjs.map → media-usage-DPJcNaAq.mjs.map} +1 -1
  364. package/dist/{menus-BMGzWyWH.mjs → menus-BxPLFfIc.mjs} +3 -3
  365. package/dist/{menus-BMGzWyWH.mjs.map → menus-BxPLFfIc.mjs.map} +1 -1
  366. package/dist/{menus-CPf_SW7u.mjs → menus-C6qhqP4X.mjs} +8 -8
  367. package/dist/{menus-CPf_SW7u.mjs.map → menus-C6qhqP4X.mjs.map} +1 -1
  368. package/dist/{oauth-authorization-oqfbBuMH.mjs → oauth-authorization-C9tFinly.mjs} +4 -4
  369. package/dist/{oauth-authorization-oqfbBuMH.mjs.map → oauth-authorization-C9tFinly.mjs.map} +1 -1
  370. package/dist/{oauth-clients-BlrUHAsf.mjs → oauth-clients-BwTxE1aE.mjs} +1 -1
  371. package/dist/{oauth-clients-BlrUHAsf.mjs.map → oauth-clients-BwTxE1aE.mjs.map} +1 -1
  372. package/dist/{oauth-state-store-DnvjRMy6.mjs → oauth-state-store-DaRUwPWO.mjs} +1 -1
  373. package/dist/{oauth-state-store-DnvjRMy6.mjs.map → oauth-state-store-DaRUwPWO.mjs.map} +1 -1
  374. package/dist/{oauth-user-lookup-oUllxvAv.mjs → oauth-user-lookup-CzuzOSlF.mjs} +1 -1
  375. package/dist/{oauth-user-lookup-oUllxvAv.mjs.map → oauth-user-lookup-CzuzOSlF.mjs.map} +1 -1
  376. package/dist/object-cache/memory.d.mts +1 -1
  377. package/dist/{options-BqMkWfYa.d.mts → options-aeIs8WFu.d.mts} +3 -3
  378. package/dist/{options-BqMkWfYa.d.mts.map → options-aeIs8WFu.d.mts.map} +1 -1
  379. package/dist/page/index.d.mts +2 -2
  380. package/dist/{parse-53zyxlpv.mjs → parse-BL49yb9D.mjs} +2 -2
  381. package/dist/{parse-53zyxlpv.mjs.map → parse-BL49yb9D.mjs.map} +1 -1
  382. package/dist/{passkey-config-C-SKpc0-.mjs → passkey-config-KMhgFjdQ.mjs} +1 -1
  383. package/dist/{passkey-config-C-SKpc0-.mjs.map → passkey-config-KMhgFjdQ.mjs.map} +1 -1
  384. package/dist/{placeholder-Dop47JMM.d.mts → placeholder-Dxo5Zt58.d.mts} +1 -1
  385. package/dist/{placeholder-Dop47JMM.d.mts.map → placeholder-Dxo5Zt58.d.mts.map} +1 -1
  386. package/dist/plugin-types.d.mts +1 -1
  387. package/dist/plugin-utils.d.mts +10 -10
  388. package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
  389. package/dist/{portable-text-sDj92Kxv.mjs → portable-text-CNahFfj6.mjs} +1 -1
  390. package/dist/{portable-text-sDj92Kxv.mjs.map → portable-text-CNahFfj6.mjs.map} +1 -1
  391. package/dist/{query-DdFGHzQF.mjs → query-1xOcEy18.mjs} +3 -3
  392. package/dist/{query-DdFGHzQF.mjs.map → query-1xOcEy18.mjs.map} +1 -1
  393. package/dist/{query-Bz9bWZfm.mjs → query-DCiXI7OZ.mjs} +36 -16
  394. package/dist/query-DCiXI7OZ.mjs.map +1 -0
  395. package/dist/{rate-limit-D3IEiUi2.mjs → rate-limit-Vkp9gQ1Y.mjs} +2 -2
  396. package/dist/{rate-limit-D3IEiUi2.mjs.map → rate-limit-Vkp9gQ1Y.mjs.map} +1 -1
  397. package/dist/{redirect-BoUac4NT.mjs → redirect-BJ6d1yHh.mjs} +3 -3
  398. package/dist/{redirect-BoUac4NT.mjs.map → redirect-BJ6d1yHh.mjs.map} +1 -1
  399. package/dist/{redirect-Botom7X6.mjs → redirect-CDgfv9vh.mjs} +1 -1
  400. package/dist/{redirect-Botom7X6.mjs.map → redirect-CDgfv9vh.mjs.map} +1 -1
  401. package/dist/{redirects-D457qwt0.mjs → redirects-BOn2DIUs.mjs} +8 -4
  402. package/dist/redirects-BOn2DIUs.mjs.map +1 -0
  403. package/dist/{redirects-ChbpqKiS.mjs → redirects-Cwkq2H-G.mjs} +3 -3
  404. package/dist/{redirects-ChbpqKiS.mjs.map → redirects-Cwkq2H-G.mjs.map} +1 -1
  405. package/dist/{registry-y21kjhfG.mjs → registry-BP1JK2xh.mjs} +6 -6
  406. package/dist/{registry-y21kjhfG.mjs.map → registry-BP1JK2xh.mjs.map} +1 -1
  407. package/dist/{relation-Du0r04E0.mjs → relation-DJ5DZh7B.mjs} +3 -3
  408. package/dist/{relation-Du0r04E0.mjs.map → relation-DJ5DZh7B.mjs.map} +1 -1
  409. package/dist/{relations-CZMpBqEd.mjs → relations-C4duJnwI.mjs} +2 -2
  410. package/dist/{relations-CZMpBqEd.mjs.map → relations-C4duJnwI.mjs.map} +1 -1
  411. package/dist/{relations-qKmSj9KM.d.mts → relations-DHFQGX0h.d.mts} +28 -17
  412. package/dist/relations-DHFQGX0h.d.mts.map +1 -0
  413. package/dist/{relations-DtX1UMBH.mjs → relations-DpTjftCl.mjs} +6 -6
  414. package/dist/{relations-DtX1UMBH.mjs.map → relations-DpTjftCl.mjs.map} +1 -1
  415. package/dist/{resolve-DFr4kq72.mjs → resolve-BUvFE0Lr.mjs} +2 -2
  416. package/dist/{resolve-DFr4kq72.mjs.map → resolve-BUvFE0Lr.mjs.map} +1 -1
  417. package/dist/{runner-BdeLEgad.mjs → runner-BsI18UgP.mjs} +230 -212
  418. package/dist/runner-BsI18UgP.mjs.map +1 -0
  419. package/dist/{runner-Dd8m8k9F.d.mts → runner-CHdkERbt.d.mts} +2 -2
  420. package/dist/{runner-Dd8m8k9F.d.mts.map → runner-CHdkERbt.d.mts.map} +1 -1
  421. package/dist/runtime.d.mts +11 -11
  422. package/dist/runtime.mjs +3 -3
  423. package/dist/{schema-BH8P8vaC.mjs → schema-eqZhkR0S.mjs} +3 -3
  424. package/dist/{schema-BH8P8vaC.mjs.map → schema-eqZhkR0S.mjs.map} +1 -1
  425. package/dist/{scopes-DTIkIaGA.mjs → scopes-Be2d8bSb.mjs} +3 -3
  426. package/dist/{scopes-DTIkIaGA.mjs.map → scopes-Be2d8bSb.mjs.map} +1 -1
  427. package/dist/{search-D-Yhpmvs.mjs → search-CeO678cp.mjs} +6 -6
  428. package/dist/{search-D-Yhpmvs.mjs.map → search-CeO678cp.mjs.map} +1 -1
  429. package/dist/{secrets-CQ-69kF9.mjs → secrets-870d-7yA.mjs} +1 -1
  430. package/dist/{secrets-CQ-69kF9.mjs.map → secrets-870d-7yA.mjs.map} +1 -1
  431. package/dist/{sections-CzVBAKjw.mjs → sections-DSuEfirn.mjs} +3 -3
  432. package/dist/{sections-CzVBAKjw.mjs.map → sections-DSuEfirn.mjs.map} +1 -1
  433. package/dist/seed/index.d.mts +2 -2
  434. package/dist/seed/index.mjs +18 -16
  435. package/dist/seo/index.d.mts +3 -3
  436. package/dist/seo/index.mjs +14 -12
  437. package/dist/seo/index.mjs.map +1 -1
  438. package/dist/{seo-kJzVAmmu.mjs → seo-BAPKiED8.mjs} +2 -2
  439. package/dist/{seo-kJzVAmmu.mjs.map → seo-BAPKiED8.mjs.map} +1 -1
  440. package/dist/{service-CQnc276a.mjs → service-DyFT-k-R.mjs} +3 -3
  441. package/dist/{service-CQnc276a.mjs.map → service-DyFT-k-R.mjs.map} +1 -1
  442. package/dist/{session-user-DbHqKDKe.mjs → session-user-BrK2zz7S.mjs} +1 -1
  443. package/dist/{session-user-DbHqKDKe.mjs.map → session-user-BrK2zz7S.mjs.map} +1 -1
  444. package/dist/{settings-Dtc0DU31.mjs → settings-CREKyj8I.mjs} +3 -3
  445. package/dist/{settings-Dtc0DU31.mjs.map → settings-CREKyj8I.mjs.map} +1 -1
  446. package/dist/{settings-C2PaBS49.mjs → settings-DgKouY2S.mjs} +3 -3
  447. package/dist/{settings-C2PaBS49.mjs.map → settings-DgKouY2S.mjs.map} +1 -1
  448. package/dist/{setup-complete-CdIlqloh.mjs → setup-complete-L7bMK2VI.mjs} +1 -1
  449. package/dist/{setup-complete-CdIlqloh.mjs.map → setup-complete-L7bMK2VI.mjs.map} +1 -1
  450. package/dist/{setup-nonce-DqiIPr-J.mjs → setup-nonce-Ct5tjWcq.mjs} +1 -1
  451. package/dist/{setup-nonce-DqiIPr-J.mjs.map → setup-nonce-Ct5tjWcq.mjs.map} +1 -1
  452. package/dist/{site-url-NGJT6NTU.mjs → site-url-8N3kSyVZ.mjs} +1 -1
  453. package/dist/{site-url-NGJT6NTU.mjs.map → site-url-8N3kSyVZ.mjs.map} +1 -1
  454. package/dist/storage/local.d.mts +1 -1
  455. package/dist/storage/s3.d.mts +1 -1
  456. package/dist/storage/s3.mjs +1 -1
  457. package/dist/storage/s3.mjs.map +1 -1
  458. package/dist/{taxonomies-r7pqLBCn.mjs → taxonomies-B61CRSha.mjs} +48 -22
  459. package/dist/taxonomies-B61CRSha.mjs.map +1 -0
  460. package/dist/{taxonomies-BN6M9Nrv.mjs → taxonomies-Dbdki3Un.mjs} +9 -8
  461. package/dist/taxonomies-Dbdki3Un.mjs.map +1 -0
  462. package/dist/{taxonomy-0mCI25HN.mjs → taxonomy-DvwWAPvA.mjs} +3 -3
  463. package/dist/{taxonomy-0mCI25HN.mjs.map → taxonomy-DvwWAPvA.mjs.map} +1 -1
  464. package/dist/{transport-CKrlqSpD.mjs → transport-BWQo0Foy.mjs} +1 -1
  465. package/dist/{transport-CKrlqSpD.mjs.map → transport-BWQo0Foy.mjs.map} +1 -1
  466. package/dist/{transport-Vd7VdqHv.d.mts → transport-DokJOScS.d.mts} +1 -1
  467. package/dist/{transport-Vd7VdqHv.d.mts.map → transport-DokJOScS.d.mts.map} +1 -1
  468. package/dist/{types-DPKhI5ib.d.mts → types-84Bque3-.d.mts} +9 -1
  469. package/dist/types-84Bque3-.d.mts.map +1 -0
  470. package/dist/{types-BvB7gDOD.d.mts → types-BQY7VYw6.d.mts} +2 -2
  471. package/dist/{types-BvB7gDOD.d.mts.map → types-BQY7VYw6.d.mts.map} +1 -1
  472. package/dist/{types-B4vm81VH.d.mts → types-C9P3ysxq.d.mts} +1 -1
  473. package/dist/{types-B4vm81VH.d.mts.map → types-C9P3ysxq.d.mts.map} +1 -1
  474. package/dist/{types-B97jg3aW.d.mts → types-CcSZCcF1.d.mts} +1 -1
  475. package/dist/{types-B97jg3aW.d.mts.map → types-CcSZCcF1.d.mts.map} +1 -1
  476. package/dist/{types-BNfPHfay.d.mts → types-DQLZ58GV.d.mts} +1 -1
  477. package/dist/{types-BNfPHfay.d.mts.map → types-DQLZ58GV.d.mts.map} +1 -1
  478. package/dist/{types-BfYUPJdP.d.mts → types-DqZVPILm.d.mts} +2 -2
  479. package/dist/{types-BfYUPJdP.d.mts.map → types-DqZVPILm.d.mts.map} +1 -1
  480. package/dist/{types-C7U0HlEr.d.mts → types-Dr2GLlwu.d.mts} +1 -1
  481. package/dist/{types-C7U0HlEr.d.mts.map → types-Dr2GLlwu.d.mts.map} +1 -1
  482. package/dist/{types-iVeZ5g6N.d.mts → types-DsAMyD_o.d.mts} +1 -1
  483. package/dist/{types-iVeZ5g6N.d.mts.map → types-DsAMyD_o.d.mts.map} +1 -1
  484. package/dist/{types-CrRUFdej.mjs → types-XrQQ-Aex.mjs} +2 -2
  485. package/dist/{types-CrRUFdej.mjs.map → types-XrQQ-Aex.mjs.map} +1 -1
  486. package/dist/{types-Kmaxdarz.d.mts → types-jISEalTX.d.mts} +1 -1
  487. package/dist/{types-Kmaxdarz.d.mts.map → types-jISEalTX.d.mts.map} +1 -1
  488. package/dist/upload-attempts-C5yd6Gae.mjs +27 -0
  489. package/dist/upload-attempts-C5yd6Gae.mjs.map +1 -0
  490. package/dist/{user-aTXqmXBz.mjs → user-BAumEmpA.mjs} +3 -3
  491. package/dist/{user-aTXqmXBz.mjs.map → user-BAumEmpA.mjs.map} +1 -1
  492. package/dist/{validate-CDT1jl6_.mjs → validate-Bs_wT2ul.mjs} +3 -3
  493. package/dist/{validate-CDT1jl6_.mjs.map → validate-Bs_wT2ul.mjs.map} +1 -1
  494. package/dist/{validate-DULfgpJP.d.mts → validate-CdYcY-Ln.d.mts} +5 -5
  495. package/dist/{validate-DULfgpJP.d.mts.map → validate-CdYcY-Ln.d.mts.map} +1 -1
  496. package/dist/{validation-DlD0E35W.mjs → validation-BngR0nit.mjs} +10 -8
  497. package/dist/{validation-DlD0E35W.mjs.map → validation-BngR0nit.mjs.map} +1 -1
  498. package/dist/version-Bdfb34kk.mjs +7 -0
  499. package/dist/{version-BRMngh9I.mjs.map → version-Bdfb34kk.mjs.map} +1 -1
  500. package/dist/{widgets-5fipNkMT.mjs → widgets-L-VndTKD.mjs} +2 -2
  501. package/dist/{widgets-5fipNkMT.mjs.map → widgets-L-VndTKD.mjs.map} +1 -1
  502. package/dist/{wxr-taxonomies-DrihupaC.mjs → wxr-taxonomies-BexjtNid.mjs} +4 -4
  503. package/dist/{wxr-taxonomies-DrihupaC.mjs.map → wxr-taxonomies-BexjtNid.mjs.map} +1 -1
  504. package/dist/{wxr-taxonomies-B4tMuN5T.d.mts → wxr-taxonomies-DueaLFd9.d.mts} +1 -1
  505. package/dist/{wxr-taxonomies-B4tMuN5T.d.mts.map → wxr-taxonomies-DueaLFd9.d.mts.map} +1 -1
  506. package/dist/{zod-generator-CwkHWz4f.mjs → zod-generator-DMzfga3g.mjs} +3 -3
  507. package/dist/{zod-generator-CwkHWz4f.mjs.map → zod-generator-DMzfga3g.mjs.map} +1 -1
  508. package/package.json +6 -5
  509. package/src/api/handlers/media.ts +4 -4
  510. package/src/api/handlers/taxonomies.ts +10 -9
  511. package/src/api/openapi/document.ts +36 -3
  512. package/src/api/schemas/media.ts +9 -2
  513. package/src/api/schemas/taxonomies.ts +16 -1
  514. package/src/astro/integration/routes.ts +5 -0
  515. package/src/astro/routes/api/content/[collection]/[id].ts +3 -1
  516. package/src/astro/routes/api/media/[id]/confirm.ts +148 -59
  517. package/src/astro/routes/api/media/[id]/upload.ts +262 -0
  518. package/src/astro/routes/api/media/[id].ts +19 -10
  519. package/src/astro/routes/api/media/upload-url.ts +28 -31
  520. package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +6 -3
  521. package/src/astro/types.ts +1 -0
  522. package/src/cleanup.ts +24 -1
  523. package/src/components/InlinePortableTextEditor.tsx +4 -0
  524. package/src/components/code-mark.ts +11 -0
  525. package/src/database/dialect-helpers.ts +38 -0
  526. package/src/database/migrations/054_media_upload_attempts.ts +41 -0
  527. package/src/database/migrations/runner.ts +2 -0
  528. package/src/database/repositories/content.ts +2 -5
  529. package/src/database/repositories/media.ts +158 -28
  530. package/src/database/types.ts +10 -0
  531. package/src/emdash-runtime.ts +13 -0
  532. package/src/i18n/config.ts +17 -10
  533. package/src/index.ts +1 -0
  534. package/src/media/upload-attempts.ts +33 -0
  535. package/src/query.ts +29 -2
  536. package/src/storage/s3.ts +1 -1
  537. package/src/taxonomies/term-counts.ts +54 -24
  538. package/src/utils/hash.ts +14 -3
  539. package/dist/api-BkqFzIu4.mjs.map +0 -1
  540. package/dist/config-B6b_BobR.mjs.map +0 -1
  541. package/dist/content-BC43pICM.mjs.map +0 -1
  542. package/dist/dialect-helpers-BtOwo2us.mjs.map +0 -1
  543. package/dist/hash-DFFrkivP.mjs.map +0 -1
  544. package/dist/index-BZJ_5FOX.d.mts.map +0 -1
  545. package/dist/media-its1WLYv.mjs.map +0 -1
  546. package/dist/query-Bz9bWZfm.mjs.map +0 -1
  547. package/dist/redirects-D457qwt0.mjs.map +0 -1
  548. package/dist/relations-qKmSj9KM.d.mts.map +0 -1
  549. package/dist/runner-BdeLEgad.mjs.map +0 -1
  550. package/dist/taxonomies-BN6M9Nrv.mjs.map +0 -1
  551. package/dist/taxonomies-r7pqLBCn.mjs.map +0 -1
  552. package/dist/types-DPKhI5ib.d.mts.map +0 -1
  553. package/dist/version-BRMngh9I.mjs +0 -7
  554. /package/dist/{api-tokens-Cvmixds7.mjs → api-tokens-CEsW_jCg.mjs} +0 -0
@@ -8,14 +8,16 @@
8
8
  */
9
9
 
10
10
  import type { APIRoute } from "astro";
11
- import { MediaRepository } from "emdash";
11
+ import type { DownloadResult } from "emdash";
12
12
 
13
13
  import { requireOwnerPerm, requirePerm } from "#api/authorize.js";
14
14
  import { apiError, apiSuccess, handleError } from "#api/error.js";
15
15
  import { isParseError, parseOptionalBody } from "#api/parse.js";
16
16
  import { mediaConfirmBody } from "#api/schemas.js";
17
+ import { MediaRepository } from "#db/repositories/media.js";
17
18
  import { enrichImageMetadata } from "#media/enrich.js";
18
19
  import type { MediaItem } from "#types";
20
+ import { computeContentHash, MAX_CONTENT_HASH_BYTES } from "#utils/hash.js";
19
21
 
20
22
  export const prerender = false;
21
23
 
@@ -26,7 +28,7 @@ export const prerender = false;
26
28
  * on the very uploads that flow was designed for. LQIP is progressive
27
29
  * enhancement: large images simply ship without a server-generated placeholder.
28
30
  */
29
- const MAX_PLACEHOLDER_DOWNLOAD_BYTES = 8 * 1024 * 1024;
31
+ const MAX_PLACEHOLDER_DOWNLOAD_BYTES = MAX_CONTENT_HASH_BYTES;
30
32
 
31
33
  /**
32
34
  * Add URL to media item (relative URL for portability)
@@ -38,6 +40,67 @@ function addUrlToMedia(item: MediaItem): MediaItem & { url: string } {
38
40
  };
39
41
  }
40
42
 
43
+ async function cancelDownload(download: DownloadResult): Promise<void> {
44
+ try {
45
+ await download.body.cancel();
46
+ } catch (error) {
47
+ console.error("[media] confirm download cancellation failed:", error);
48
+ }
49
+ }
50
+
51
+ async function forgetUploadAttempt(repo: MediaRepository, storageKey: string): Promise<void> {
52
+ try {
53
+ await repo.deleteUploadAttempt(storageKey);
54
+ } catch (error) {
55
+ console.error("[media] confirm upload attempt cleanup failed:", error);
56
+ }
57
+ }
58
+
59
+ async function confirmationConflict(repo: MediaRepository, id: string): Promise<Response> {
60
+ const current = await repo.findById(id);
61
+ if (!current) {
62
+ return apiError("NOT_FOUND", `Media item not found: ${id}`, 404);
63
+ }
64
+ if (current.status === "ready") {
65
+ await forgetUploadAttempt(repo, current.storageKey);
66
+ return apiSuccess({ item: addUrlToMedia(current) });
67
+ }
68
+ if (current.status === "pending") {
69
+ return apiError("INVALID_STATE", "Media item changed during confirmation", 409);
70
+ }
71
+ return apiError("INVALID_STATE", `Media item is not pending: ${current.status}`, 400);
72
+ }
73
+
74
+ async function consumeDownload(download: DownloadResult): Promise<Uint8Array> {
75
+ const reader = download.body.getReader();
76
+ try {
77
+ const bytes = new Uint8Array(download.size);
78
+ let receivedSize = 0;
79
+ while (true) {
80
+ const { done, value } = await reader.read();
81
+ if (done) break;
82
+ if (receivedSize + value.byteLength > bytes.byteLength) {
83
+ throw new Error("Stored file exceeds its reported size");
84
+ }
85
+ bytes.set(value, receivedSize);
86
+ receivedSize += value.byteLength;
87
+ }
88
+ if (receivedSize !== download.size) {
89
+ throw new Error("Stored file size does not match its reported size");
90
+ }
91
+ return bytes;
92
+ } catch (error) {
93
+ try {
94
+ await reader.cancel(error);
95
+ } catch (cancelError) {
96
+ console.error("[media] confirm download cancellation failed:", cancelError);
97
+ }
98
+ throw error;
99
+ } finally {
100
+ reader.releaseLock();
101
+ }
102
+ }
103
+
41
104
  /**
42
105
  * Confirm upload completion
43
106
  */
@@ -68,90 +131,116 @@ export const POST: APIRoute = async ({ params, request, locals }) => {
68
131
  return apiError("NOT_FOUND", `Media item not found: ${id}`, 404);
69
132
  }
70
133
 
71
- if (existing.status !== "pending") {
72
- return apiError("INVALID_STATE", `Media item is not pending: ${existing.status}`, 400);
73
- }
74
-
75
134
  // Only the uploader or a user with media:edit_any can confirm/fail a pending upload
76
135
  const ownerDenied = requireOwnerPerm(
77
136
  user,
78
137
  existing.authorId ?? "",
79
- "media:edit_own",
138
+ "media:upload",
80
139
  "media:edit_any",
81
140
  );
82
141
  if (ownerDenied) return ownerDenied;
83
142
 
84
- // Optionally verify the file exists in storage
143
+ if (existing.status === "ready") {
144
+ await forgetUploadAttempt(repo, existing.storageKey);
145
+ return apiSuccess({ item: addUrlToMedia(existing) });
146
+ }
147
+ if (existing.status !== "pending") {
148
+ return apiError("INVALID_STATE", `Media item is not pending: ${existing.status}`, 400);
149
+ }
150
+
151
+ if (body.size !== undefined && existing.size !== null && body.size !== existing.size) {
152
+ return apiError(
153
+ "UPLOAD_SIZE_MISMATCH",
154
+ "Confirmed size does not match the pending media item",
155
+ 400,
156
+ );
157
+ }
158
+
159
+ let confirmedSize = existing.size ?? body.size;
160
+ let contentHash = existing.contentHash;
161
+ let imageBytes: Uint8Array | undefined;
162
+
85
163
  if (emdash.storage) {
86
164
  const exists = await emdash.storage.exists(existing.storageKey);
87
165
  if (!exists) {
88
- // Mark as failed
89
- await repo.markFailed(id);
166
+ const failed = await repo.markFailed(id, existing.storageKey);
167
+ if (!failed) return await confirmationConflict(repo, id);
90
168
  return apiError("FILE_NOT_FOUND", "File was not uploaded to storage", 400);
91
169
  }
170
+
171
+ const storedFile = await emdash.storage.download(existing.storageKey);
172
+ if (confirmedSize !== undefined && storedFile.size !== confirmedSize) {
173
+ await cancelDownload(storedFile);
174
+ return apiError(
175
+ "UPLOAD_SIZE_MISMATCH",
176
+ "Stored file size does not match the pending media item",
177
+ 400,
178
+ );
179
+ }
180
+ confirmedSize = storedFile.size;
181
+
182
+ const isImage = existing.mimeType.startsWith("image/");
183
+ const canBuffer = storedFile.size <= MAX_PLACEHOLDER_DOWNLOAD_BYTES;
184
+ const hasServerHash =
185
+ contentHash !== null && (await repo.hasUploadAttempt(existing.storageKey));
186
+ if (canBuffer && (isImage || !hasServerHash)) {
187
+ const bytes = await consumeDownload(storedFile);
188
+ contentHash = bytes.byteLength > 0 ? await computeContentHash(bytes) : null;
189
+ if (isImage && bytes.byteLength > 0) imageBytes = bytes;
190
+ } else {
191
+ if (!hasServerHash) contentHash = null;
192
+ await cancelDownload(storedFile);
193
+ }
194
+
195
+ if (isImage && !canBuffer) {
196
+ console.warn(
197
+ `[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${storedFile.size} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,
198
+ );
199
+ }
92
200
  }
93
201
 
94
- // For images, read the just-uploaded bytes back from storage once to
95
- // generate LQIP placeholders (and server-side dimensions as a fallback).
96
- // The signed-URL flow uploads directly to storage, so this confirm is the
97
- // only point at which the server sees the bytes. Best-effort: a decode
98
- // failure must not block the upload from being marked ready. We also cap
99
- // the download size — buffering a large original into a Worker heap to
100
- // compute a 32px blurhash would OOM on the uploads the signed-URL path
101
- // exists to support, so oversized files skip the server-side placeholder.
202
+ // LQIP is best-effort; oversized images skip server-side placeholders.
102
203
  let blurhash: string | undefined;
103
204
  let dominantColor: string | undefined;
104
205
  let width = body.width;
105
206
  let height = body.height;
106
- if (emdash.storage && existing.mimeType.startsWith("image/")) {
107
- const knownSize = body.size ?? existing.size ?? undefined;
108
- const tooLarge = knownSize != null && knownSize > MAX_PLACEHOLDER_DOWNLOAD_BYTES;
109
- if (!tooLarge) {
110
- try {
111
- const { body: stream } = await emdash.storage.download(existing.storageKey);
112
- const bytes = new Uint8Array(await new Response(stream).arrayBuffer());
113
- // Defense-in-depth for the unknown-size case: even though we
114
- // already buffered it, refuse the decode so we don't also pay
115
- // the (larger) RGBA allocation.
116
- if (bytes.byteLength > MAX_PLACEHOLDER_DOWNLOAD_BYTES) {
117
- console.warn(
118
- `[media] confirm skipping placeholder: object ${existing.storageKey} is ${bytes.byteLength} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,
119
- );
120
- } else {
121
- const enriched = await enrichImageMetadata(bytes, existing.mimeType, {
122
- knownDimensions:
123
- body.width != null && body.height != null
124
- ? { width: body.width, height: body.height }
125
- : undefined,
126
- });
127
- blurhash = enriched.blurhash;
128
- dominantColor = enriched.dominantColor;
129
- width = width ?? enriched.width;
130
- height = height ?? enriched.height;
131
- }
132
- } catch (error) {
133
- console.error("[media] confirm placeholder generation failed:", error);
134
- }
135
- } else {
136
- console.warn(
137
- `[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${knownSize} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,
138
- );
207
+ if (imageBytes) {
208
+ try {
209
+ const enriched = await enrichImageMetadata(imageBytes, existing.mimeType, {
210
+ knownDimensions:
211
+ body.width != null && body.height != null
212
+ ? { width: body.width, height: body.height }
213
+ : undefined,
214
+ });
215
+ blurhash = enriched.blurhash;
216
+ dominantColor = enriched.dominantColor;
217
+ width = width ?? enriched.width;
218
+ height = height ?? enriched.height;
219
+ } catch (error) {
220
+ console.error("[media] confirm placeholder generation failed:", error);
139
221
  }
140
222
  }
141
223
 
142
224
  // Confirm the upload
143
- const item = await repo.confirmUpload(id, {
144
- size: body.size,
145
- width,
146
- height,
147
- blurhash,
148
- dominantColor,
149
- });
225
+ const item = await repo.confirmUpload(
226
+ id,
227
+ {
228
+ size: confirmedSize,
229
+ width,
230
+ height,
231
+ blurhash,
232
+ dominantColor,
233
+ contentHash,
234
+ },
235
+ existing.storageKey,
236
+ );
150
237
 
151
238
  if (!item) {
152
- return apiError("CONFIRM_FAILED", "Failed to confirm upload", 500);
239
+ return await confirmationConflict(repo, id);
153
240
  }
154
241
 
242
+ await forgetUploadAttempt(repo, item.storageKey);
243
+
155
244
  // Add URL to the response (relative URL for portability)
156
245
  const itemWithUrl = addUrlToMedia(item);
157
246
 
@@ -0,0 +1,262 @@
1
+ import type { APIRoute } from "astro";
2
+ import type { Storage } from "emdash";
3
+ import { ulid } from "ulidx";
4
+
5
+ import { requireOwnerPerm, requirePerm } from "#api/authorize.js";
6
+ import { apiError, apiSuccess, handleError } from "#api/error.js";
7
+ import { MediaRepository } from "#db/repositories/media.js";
8
+ import { normalizeMime } from "#media/mime.js";
9
+ import { removeUploadAttempt } from "#media/upload-attempts.js";
10
+ import { computeContentHash, MAX_CONTENT_HASH_BYTES } from "#utils/hash.js";
11
+
12
+ export const prerender = false;
13
+
14
+ const INITIAL_HASH_BUFFER_BYTES = 64 * 1024;
15
+
16
+ type FixedLengthStreamConstructor = new (
17
+ expectedLength: number | bigint,
18
+ ) => TransformStream<ArrayBuffer | ArrayBufferView, Uint8Array>;
19
+
20
+ declare const FixedLengthStream: FixedLengthStreamConstructor | undefined;
21
+
22
+ class UploadBodyError extends Error {
23
+ readonly code: "PAYLOAD_TOO_LARGE" | "UPLOAD_SIZE_MISMATCH";
24
+
25
+ constructor(code: "PAYLOAD_TOO_LARGE" | "UPLOAD_SIZE_MISMATCH") {
26
+ super(code);
27
+ this.code = code;
28
+ }
29
+ }
30
+
31
+ function findUploadBodyError(error: unknown): UploadBodyError | null {
32
+ let current = error;
33
+ for (let depth = 0; depth < 5 && current instanceof Error; depth++) {
34
+ if (current instanceof UploadBodyError) return current;
35
+ current = current.cause;
36
+ }
37
+ return null;
38
+ }
39
+
40
+ function preserveKnownLength(
41
+ body: ReadableStream<Uint8Array>,
42
+ expectedLength: number,
43
+ ): ReadableStream<Uint8Array> {
44
+ if (typeof FixedLengthStream === "undefined") return body;
45
+ return body.pipeThrough(new FixedLengthStream(expectedLength));
46
+ }
47
+
48
+ function createUploadAttemptKey(key: string): string {
49
+ const pathSeparator = key.lastIndexOf("/");
50
+ const extensionSeparator = key.lastIndexOf(".");
51
+ if (extensionSeparator > pathSeparator) {
52
+ return `${key.slice(0, extensionSeparator)}.${ulid()}${key.slice(extensionSeparator)}`;
53
+ }
54
+ return `${key}.${ulid()}`;
55
+ }
56
+
57
+ async function getStoredSize(storage: Storage, key: string): Promise<number | null> {
58
+ if (!(await storage.exists(key))) return null;
59
+ const download = await storage.download(key);
60
+ try {
61
+ return download.size;
62
+ } finally {
63
+ try {
64
+ await download.body.cancel();
65
+ } catch (error) {
66
+ console.error("[media] upload download cancellation failed:", error);
67
+ }
68
+ }
69
+ }
70
+
71
+ export const PUT: APIRoute = async ({ params, request, locals }) => {
72
+ const { emdash, user } = locals;
73
+ const { id } = params;
74
+
75
+ const denied = requirePerm(user, "media:upload");
76
+ if (denied) return denied;
77
+
78
+ if (!id) {
79
+ return apiError("INVALID_REQUEST", "Media ID is required", 400);
80
+ }
81
+
82
+ if (!emdash?.db) {
83
+ return apiError("NOT_CONFIGURED", "EmDash is not initialized", 500);
84
+ }
85
+
86
+ if (!emdash.storage) {
87
+ return apiError("NO_STORAGE", "Storage not configured", 500);
88
+ }
89
+
90
+ try {
91
+ const repo = new MediaRepository(emdash.db);
92
+ const media = await repo.findById(id);
93
+ if (!media) {
94
+ return apiError("NOT_FOUND", `Media item not found: ${id}`, 404);
95
+ }
96
+
97
+ if (media.status !== "pending") {
98
+ return apiError("INVALID_STATE", `Media item is not pending: ${media.status}`, 400);
99
+ }
100
+
101
+ const ownerDenied = requireOwnerPerm(
102
+ user,
103
+ media.authorId ?? "",
104
+ "media:upload",
105
+ "media:edit_any",
106
+ );
107
+ if (ownerDenied) return ownerDenied;
108
+
109
+ if (!Number.isSafeInteger(media.size) || media.size === null || media.size < 0) {
110
+ return apiError("INVALID_STATE", "Pending media item has no valid upload size", 400);
111
+ }
112
+ const expectedSize = media.size;
113
+
114
+ const contentType = request.headers.get("Content-Type");
115
+ if (!contentType || normalizeMime(contentType) !== media.mimeType) {
116
+ return apiError("INVALID_TYPE", "Upload content type does not match the media item", 400);
117
+ }
118
+
119
+ const requestBody =
120
+ request.body ??
121
+ (expectedSize === 0
122
+ ? new ReadableStream<Uint8Array>({
123
+ start(controller) {
124
+ controller.close();
125
+ },
126
+ })
127
+ : null);
128
+ if (!requestBody) {
129
+ return apiError("NO_FILE", "No file provided", 400);
130
+ }
131
+
132
+ const contentLength = request.headers.get("Content-Length");
133
+ if (contentLength !== null) {
134
+ const declaredSize = Number(contentLength);
135
+ if (!Number.isSafeInteger(declaredSize) || declaredSize < 0) {
136
+ return apiError("INVALID_REQUEST", "Invalid Content-Length header", 400);
137
+ }
138
+ if (declaredSize > expectedSize) {
139
+ return apiError("PAYLOAD_TOO_LARGE", "Upload exceeds the expected size", 413);
140
+ }
141
+ if (declaredSize !== expectedSize) {
142
+ return apiError("UPLOAD_SIZE_MISMATCH", "Upload size does not match the media item", 400);
143
+ }
144
+ }
145
+
146
+ let receivedSize = 0;
147
+ const shouldHash = expectedSize > 0 && expectedSize <= MAX_CONTENT_HASH_BYTES;
148
+ let hashBytes: Uint8Array | null = null;
149
+ const checkedBody = requestBody.pipeThrough(
150
+ new TransformStream<Uint8Array, Uint8Array>({
151
+ transform(chunk, controller) {
152
+ const offset = receivedSize;
153
+ receivedSize += chunk.byteLength;
154
+ if (receivedSize > expectedSize) {
155
+ controller.error(new UploadBodyError("PAYLOAD_TOO_LARGE"));
156
+ return;
157
+ }
158
+ if (shouldHash && chunk.byteLength > 0) {
159
+ if (!hashBytes) {
160
+ hashBytes = new Uint8Array(
161
+ Math.min(expectedSize, Math.max(INITIAL_HASH_BUFFER_BYTES, receivedSize)),
162
+ );
163
+ } else if (receivedSize > hashBytes.byteLength) {
164
+ const grown = new Uint8Array(
165
+ Math.min(expectedSize, Math.max(receivedSize, hashBytes.byteLength * 2)),
166
+ );
167
+ grown.set(hashBytes);
168
+ hashBytes = grown;
169
+ }
170
+ hashBytes.set(chunk, offset);
171
+ }
172
+ controller.enqueue(chunk);
173
+ },
174
+ flush(controller) {
175
+ if (receivedSize !== expectedSize) {
176
+ controller.error(new UploadBodyError("UPLOAD_SIZE_MISMATCH"));
177
+ }
178
+ },
179
+ }),
180
+ );
181
+ const body = preserveKnownLength(checkedBody, expectedSize);
182
+
183
+ const attemptKey = createUploadAttemptKey(media.storageKey);
184
+ await repo.createUploadAttempt(id, attemptKey);
185
+
186
+ let attemptSize: number;
187
+ try {
188
+ const result = await emdash.storage.upload({
189
+ key: attemptKey,
190
+ body,
191
+ contentType: media.mimeType,
192
+ });
193
+ attemptSize = result.size;
194
+ } catch (error) {
195
+ await removeUploadAttempt(emdash.storage, repo, attemptKey);
196
+ const bodyError = findUploadBodyError(error);
197
+ if (bodyError?.code === "PAYLOAD_TOO_LARGE") {
198
+ return apiError("PAYLOAD_TOO_LARGE", "Upload exceeds the expected size", 413);
199
+ }
200
+ if (bodyError?.code === "UPLOAD_SIZE_MISMATCH") {
201
+ return apiError("UPLOAD_SIZE_MISMATCH", "Upload size does not match the media item", 400);
202
+ }
203
+ return handleError(error, "Upload failed", "UPLOAD_ERROR");
204
+ }
205
+
206
+ if (receivedSize !== expectedSize || attemptSize !== expectedSize) {
207
+ await removeUploadAttempt(emdash.storage, repo, attemptKey);
208
+ return apiError("UPLOAD_SIZE_MISMATCH", "Upload size does not match the media item", 400);
209
+ }
210
+ const contentHash = hashBytes ? await computeContentHash(hashBytes) : undefined;
211
+
212
+ let published: boolean;
213
+ try {
214
+ published = await repo.publishPendingStorageKey(
215
+ id,
216
+ media.storageKey,
217
+ attemptKey,
218
+ contentHash,
219
+ );
220
+ } catch (error) {
221
+ try {
222
+ const current = await repo.findById(id);
223
+ if (
224
+ current?.storageKey === attemptKey &&
225
+ (current.status === "pending" || current.status === "ready") &&
226
+ current.size === expectedSize &&
227
+ (await getStoredSize(emdash.storage, attemptKey)) === expectedSize
228
+ ) {
229
+ return apiSuccess({ uploaded: true, size: expectedSize });
230
+ }
231
+ } catch (verificationError) {
232
+ console.error("[media] upload publication verification failed:", verificationError);
233
+ }
234
+ return handleError(error, "Upload failed", "UPLOAD_ERROR");
235
+ }
236
+
237
+ if (!published) {
238
+ const current = await repo.findById(id);
239
+ if (
240
+ current &&
241
+ (current.status === "pending" || current.status === "ready") &&
242
+ current.size === expectedSize &&
243
+ (current.storageKey === attemptKey ||
244
+ expectedSize === 0 ||
245
+ (contentHash !== undefined && current.contentHash === contentHash)) &&
246
+ (await getStoredSize(emdash.storage, current.storageKey)) === expectedSize
247
+ ) {
248
+ if (current.storageKey !== attemptKey) {
249
+ await removeUploadAttempt(emdash.storage, repo, attemptKey);
250
+ }
251
+ return apiSuccess({ uploaded: true, size: expectedSize });
252
+ }
253
+ await removeUploadAttempt(emdash.storage, repo, attemptKey);
254
+ return apiError("INVALID_STATE", "Media item is no longer pending", 400);
255
+ }
256
+
257
+ await removeUploadAttempt(emdash.storage, repo, media.storageKey);
258
+ return apiSuccess({ uploaded: true, size: receivedSize });
259
+ } catch (error) {
260
+ return handleError(error, "Upload failed", "UPLOAD_ERROR");
261
+ }
262
+ };
@@ -13,6 +13,8 @@ import { apiError, apiSuccess, handleError, unwrapResult } from "#api/error.js";
13
13
  import { handleMediaUsageSummaries } from "#api/handlers/media-usage.js";
14
14
  import { isParseError, parseBody, parseQuery } from "#api/parse.js";
15
15
  import { mediaGetQuery, mediaUpdateBody } from "#api/schemas.js";
16
+ import { MediaRepository } from "#db/repositories/media.js";
17
+ import { removeUploadAttempt } from "#media/upload-attempts.js";
16
18
 
17
19
  export const prerender = false;
18
20
 
@@ -137,20 +139,27 @@ export const DELETE: APIRoute = async ({ params, locals }) => {
137
139
  );
138
140
  if (ownerDenied) return ownerDenied;
139
141
 
140
- // Delete file from storage via the storage adapter
141
- if (emdash.storage) {
142
- try {
143
- await emdash.storage.delete(media.storageKey);
144
- } catch {
145
- // Best-effort — continue with database deletion
146
- }
147
- }
148
-
149
142
  // Delete from database — site-settings cache invalidation happens
150
143
  // in `EmDashRuntime.handleMediaDelete` so MCP/plugin paths inherit it.
151
144
  const result = await emdash.handleMediaDelete(id);
145
+ if (!result.success) return unwrapResult(result);
146
+ if (
147
+ typeof result.data !== "object" ||
148
+ result.data === null ||
149
+ !("storageKey" in result.data) ||
150
+ typeof result.data.storageKey !== "string"
151
+ ) {
152
+ return apiError("MEDIA_DELETE_ERROR", "Failed to delete media", 500);
153
+ }
152
154
 
153
- return unwrapResult(result);
155
+ if (emdash.storage) {
156
+ const repo = new MediaRepository(emdash.db);
157
+ await removeUploadAttempt(emdash.storage, repo, result.data.storageKey, {
158
+ allowUntracked: true,
159
+ });
160
+ }
161
+
162
+ return apiSuccess({ deleted: true });
154
163
  } catch (error) {
155
164
  return handleError(error, "Failed to delete media", "MEDIA_DELETE_ERROR");
156
165
  }
@@ -39,6 +39,10 @@ interface ExistingMediaResponse {
39
39
  url: string;
40
40
  }
41
41
 
42
+ function isUnsupportedSignedUpload(error: unknown): boolean {
43
+ return error instanceof Error && "code" in error && error.code === "NOT_SUPPORTED";
44
+ }
45
+
42
46
  /**
43
47
  * Get a signed upload URL for direct-to-storage upload
44
48
  */
@@ -71,6 +75,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
71
75
  }
72
76
  const body = await parseBody(request, mediaUploadUrlBody(maxSize));
73
77
  if (isParseError(body)) return body;
78
+ const normalizedContentType = normalizeMime(body.contentType);
74
79
 
75
80
  // Validate content type (field-aware widening)
76
81
  const fieldAllowlist = body.fieldId
@@ -85,9 +90,9 @@ export const POST: APIRoute = async ({ request, locals }) => {
85
90
  const repo = new MediaRepository(emdash.db);
86
91
 
87
92
  // Check for existing content with same hash (deduplication)
88
- if (body.contentHash) {
93
+ if (body.contentHash && body.size > 0) {
89
94
  const existing = await repo.findByContentHash(body.contentHash);
90
- if (existing) {
95
+ if (existing && existing.mimeType === normalizedContentType && existing.size === body.size) {
91
96
  const response: ExistingMediaResponse = {
92
97
  existing: true,
93
98
  mediaId: existing.id,
@@ -103,49 +108,41 @@ export const POST: APIRoute = async ({ request, locals }) => {
103
108
  const ext = path.extname(body.filename) || "";
104
109
  const storageKey = `${id}${ext}`;
105
110
 
106
- // Create pending media record with content hash
111
+ let signedUrl: Awaited<ReturnType<typeof emdash.storage.getSignedUploadUrl>> | null;
112
+ try {
113
+ signedUrl = await emdash.storage.getSignedUploadUrl({
114
+ key: storageKey,
115
+ contentType: body.contentType,
116
+ size: body.size,
117
+ expiresIn: 3600,
118
+ });
119
+ } catch (error) {
120
+ if (!isUnsupportedSignedUpload(error)) throw error;
121
+ signedUrl = null;
122
+ }
123
+
107
124
  const mediaItem = await repo.createPending({
108
125
  filename: body.filename,
109
- mimeType: normalizeMime(body.contentType),
126
+ mimeType: normalizedContentType,
110
127
  size: body.size,
111
128
  storageKey,
112
- contentHash: body.contentHash,
113
129
  authorId: user?.id,
114
130
  });
115
131
 
116
- // Get signed upload URL from storage
117
- const signedUrl = await emdash.storage.getSignedUploadUrl({
118
- key: storageKey,
119
- contentType: body.contentType,
120
- size: body.size,
121
- expiresIn: 3600, // 1 hour
122
- });
123
-
124
132
  const response: UploadUrlResponse = {
125
- uploadUrl: signedUrl.url,
126
- method: signedUrl.method,
127
- headers: signedUrl.headers,
133
+ uploadUrl: signedUrl?.url ?? `/_emdash/api/media/${mediaItem.id}/upload`,
134
+ method: signedUrl?.method ?? "PUT",
135
+ headers: signedUrl?.headers ?? {
136
+ "Content-Type": normalizedContentType,
137
+ "X-EmDash-Request": "1",
138
+ },
128
139
  mediaId: mediaItem.id,
129
140
  storageKey,
130
- expiresAt: signedUrl.expiresAt,
141
+ expiresAt: signedUrl?.expiresAt ?? new Date(Date.now() + 3600 * 1000).toISOString(),
131
142
  };
132
143
 
133
144
  return apiSuccess(response);
134
145
  } catch (error) {
135
- // Check if storage doesn't support signed URLs (e.g., local storage)
136
- if (
137
- error instanceof Error &&
138
- "code" in error &&
139
- // eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowing error to check custom code property after "code" in error guard
140
- (error as { code: string }).code === "NOT_SUPPORTED"
141
- ) {
142
- return apiError(
143
- "NOT_SUPPORTED",
144
- "Storage does not support signed upload URLs. Use direct upload.",
145
- 501,
146
- );
147
- }
148
-
149
146
  return handleError(error, "Failed to generate upload URL", "UPLOAD_URL_ERROR");
150
147
  }
151
148
  };