emdash 0.24.1 → 0.25.1
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.
- package/dist/api/route-utils.d.mts +2 -2
- package/dist/api/route-utils.mjs +9 -8
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +2 -2
- package/dist/{api-d_0wi9fW.mjs → api-D2_sbavn.mjs} +16 -12
- package/dist/api-D2_sbavn.mjs.map +1 -0
- package/dist/{apply-BE9UaWX0.mjs → apply-Bk2mAuLn.mjs} +11 -10
- package/dist/apply-Bk2mAuLn.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.mjs +2 -2
- package/dist/astro/middleware/auth.d.mts +5 -5
- package/dist/astro/middleware/auth.mjs +1 -1
- package/dist/astro/middleware/redirect.mjs +2 -2
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +66 -37
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +4 -4
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +1 -1
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +2 -2
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +2 -2
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +5 -5
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +7 -7
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +7 -7
- package/dist/astro/routes/api/admin/bylines/index.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/comments/bulk.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/counts.mjs +2 -2
- package/dist/astro/routes/api/admin/comments/index.mjs +5 -5
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +3 -3
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +2 -2
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/index.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +21 -21
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +20 -20
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +21 -21
- package/dist/astro/routes/api/admin/plugins/updates.mjs +20 -20
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +20 -20
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +20 -20
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +4 -4
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +1 -1
- package/dist/astro/routes/api/admin/users/index.mjs +4 -4
- package/dist/astro/routes/api/auth/dev-bypass.mjs +1 -1
- package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
- package/dist/astro/routes/api/auth/invite/complete.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/index.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/register-options.mjs +4 -4
- package/dist/astro/routes/api/auth/logout.mjs +1 -1
- package/dist/astro/routes/api/auth/magic-link/send.mjs +5 -5
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +1 -1
- package/dist/astro/routes/api/auth/me.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/verify.mjs +4 -4
- package/dist/astro/routes/api/auth/signup/complete.mjs +4 -4
- package/dist/astro/routes/api/auth/signup/request.mjs +5 -5
- package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +4 -4
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/index.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/trash.mjs +5 -5
- package/dist/astro/routes/api/dashboard.mjs +5 -5
- package/dist/astro/routes/api/dev/emails.mjs +1 -1
- package/dist/astro/routes/api/import/probe.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +5 -5
- package/dist/astro/routes/api/import/wordpress/execute.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/media.d.mts +13 -1
- package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +12 -8
- package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +7 -7
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +5 -5
- package/dist/astro/routes/api/manifest.mjs +2 -2
- package/dist/astro/routes/api/mcp.mjs +20 -20
- package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +43 -7
- package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +5 -5
- package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +7 -7
- package/dist/astro/routes/api/media.mjs +21 -20
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/items.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/translations.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_.mjs +5 -5
- package/dist/astro/routes/api/menus/index.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/authorize.mjs +2 -2
- package/dist/astro/routes/api/oauth/device/code.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/token.mjs +3 -3
- package/dist/astro/routes/api/oauth/register.mjs +1 -1
- package/dist/astro/routes/api/oauth/token/refresh.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/revoke.mjs +2 -2
- package/dist/astro/routes/api/oauth/token.mjs +1 -1
- package/dist/astro/routes/api/openapi.json.mjs +2 -2
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +2 -2
- package/dist/astro/routes/api/redirects/404s/index.mjs +5 -5
- package/dist/astro/routes/api/redirects/404s/summary.mjs +5 -5
- package/dist/astro/routes/api/redirects/_id_.mjs +6 -6
- package/dist/astro/routes/api/redirects/index.mjs +6 -6
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +20 -20
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +20 -20
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +20 -20
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +20 -20
- package/dist/astro/routes/api/schema/collections/index.mjs +20 -20
- package/dist/astro/routes/api/schema/index.mjs +3 -3
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +20 -20
- package/dist/astro/routes/api/schema/orphans/index.mjs +20 -20
- package/dist/astro/routes/api/search/enable.mjs +7 -7
- package/dist/astro/routes/api/search/index.mjs +6 -6
- package/dist/astro/routes/api/search/rebuild.mjs +7 -7
- package/dist/astro/routes/api/search/stats.mjs +4 -4
- package/dist/astro/routes/api/search/suggest.mjs +6 -6
- package/dist/astro/routes/api/sections/_slug_.mjs +7 -7
- package/dist/astro/routes/api/sections/index.mjs +7 -7
- package/dist/astro/routes/api/settings/email.mjs +3 -3
- package/dist/astro/routes/api/settings.mjs +9 -9
- package/dist/astro/routes/api/setup/admin-verify.mjs +4 -4
- package/dist/astro/routes/api/setup/admin.mjs +4 -4
- package/dist/astro/routes/api/setup/dev-bypass.mjs +7 -7
- package/dist/astro/routes/api/setup/dev-reset.mjs +1 -1
- package/dist/astro/routes/api/setup/index.mjs +10 -10
- package/dist/astro/routes/api/setup/status.mjs +1 -1
- package/dist/astro/routes/api/snapshot.mjs +2 -2
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +8 -8
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +8 -8
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +8 -8
- package/dist/astro/routes/api/taxonomies/index.mjs +8 -8
- package/dist/astro/routes/api/themes/preview.mjs +2 -2
- package/dist/astro/routes/api/typegen.mjs +3 -3
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/_name_.mjs +4 -4
- package/dist/astro/routes/api/widget-areas/index.mjs +7 -7
- package/dist/astro/routes/api/widget-components.mjs +1 -1
- package/dist/astro/routes/robots.txt.mjs +3 -3
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +4 -4
- package/dist/astro/routes/sitemap.xml.mjs +4 -4
- package/dist/astro/types.d.mts +7 -7
- package/dist/{authorize-B__u-iZJ.mjs → authorize-CuxZ0i2t.mjs} +2 -2
- package/dist/{authorize-B__u-iZJ.mjs.map → authorize-CuxZ0i2t.mjs.map} +1 -1
- package/dist/{byline-fields-Ca56gaT6.d.mts → byline-fields-KD3JFRuH.d.mts} +24 -6
- package/dist/byline-fields-KD3JFRuH.d.mts.map +1 -0
- package/dist/{byline-fields-B-KXkg5R.mjs → byline-fields-eyJMblSr.mjs} +2 -2
- package/dist/{byline-fields-B-KXkg5R.mjs.map → byline-fields-eyJMblSr.mjs.map} +1 -1
- package/dist/{bylines-BMtJnPnG.mjs → bylines-CPc_q2Vd.mjs} +4 -4
- package/dist/{bylines-BMtJnPnG.mjs.map → bylines-CPc_q2Vd.mjs.map} +1 -1
- package/dist/{cache-BUJe2w6Z.mjs → cache-C35VWD5D.mjs} +1 -1
- package/dist/{cache-BUJe2w6Z.mjs.map → cache-C35VWD5D.mjs.map} +1 -1
- package/dist/cli/index.mjs +8 -8
- package/dist/{content-PoVGKjp0.mjs → content-B43-YrIq.mjs} +7 -5
- package/dist/content-B43-YrIq.mjs.map +1 -0
- package/dist/{context-Dvws41_A.mjs → context-BW3N7wyt.mjs} +12 -6
- package/dist/context-BW3N7wyt.mjs.map +1 -0
- package/dist/{dashboard-DsYU8JKS.mjs → dashboard-FIe0S512.mjs} +3 -3
- package/dist/{dashboard-DsYU8JKS.mjs.map → dashboard-FIe0S512.mjs.map} +1 -1
- package/dist/{db-errors-UXB1CK42.mjs → db-errors-CK46D-ly.mjs} +1 -1
- package/dist/{db-errors-UXB1CK42.mjs.map → db-errors-CK46D-ly.mjs.map} +1 -1
- package/dist/{placeholder-B0s2mWu5.mjs → enrich-BQ0mxJRs.mjs} +61 -10
- package/dist/enrich-BQ0mxJRs.mjs.map +1 -0
- package/dist/{error-lq_fL-RS.mjs → error-DXnK2wF4.mjs} +2 -1
- package/dist/error-DXnK2wF4.mjs.map +1 -0
- package/dist/{index-BroXT-vD.d.mts → index-Cz6_JBL3.d.mts} +21 -8
- package/dist/index-Cz6_JBL3.d.mts.map +1 -0
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +28 -28
- package/dist/{loader-BR2SgfEc.mjs → loader-DrpofRon.mjs} +2 -2
- package/dist/{loader-BR2SgfEc.mjs.map → loader-DrpofRon.mjs.map} +1 -1
- package/dist/{manifest-schema-BWKnQYQF.mjs → manifest-schema-BOfPXse0.mjs} +2 -1
- package/dist/manifest-schema-BOfPXse0.mjs.map +1 -0
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +45 -2
- package/dist/media/index.d.mts.map +1 -1
- package/dist/media/index.mjs +5 -3
- package/dist/media/index.mjs.map +1 -1
- package/dist/media/local-runtime.d.mts +6 -6
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +15 -3
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-CTjJoJTh.mjs → media-BjKxjzk8.mjs} +3 -1
- package/dist/media-BjKxjzk8.mjs.map +1 -0
- package/dist/{media-allowlist-B77I5_bw.mjs → media-allowlist-Du8t1u6a.mjs} +2 -2
- package/dist/{media-allowlist-B77I5_bw.mjs.map → media-allowlist-Du8t1u6a.mjs.map} +1 -1
- package/dist/{menus-GvRSpgIA.mjs → menus-CxqJ-z-N.mjs} +46 -4
- package/dist/menus-CxqJ-z-N.mjs.map +1 -0
- package/dist/{mime-BTzvzJ8M.mjs → mime-CCEzze7W.mjs} +1 -1
- package/dist/{mime-BTzvzJ8M.mjs.map → mime-CCEzze7W.mjs.map} +1 -1
- package/dist/{normalize-ClbNIenm.mjs → normalize-CeglrBT9.mjs} +11 -2
- package/dist/normalize-CeglrBT9.mjs.map +1 -0
- package/dist/{options-CqECG0gM.d.mts → options-BvClpP5Q.d.mts} +2 -2
- package/dist/{options-CqECG0gM.d.mts.map → options-BvClpP5Q.d.mts.map} +1 -1
- package/dist/page/index.d.mts +1 -1
- package/dist/{parse-DHI873qU.mjs → parse-DqMWHQ8x.mjs} +2 -2
- package/dist/{parse-DHI873qU.mjs.map → parse-DqMWHQ8x.mjs.map} +1 -1
- package/dist/{placeholder-QoI4gEzP.d.mts → placeholder-Cuce9U-m.d.mts} +20 -5
- package/dist/placeholder-Cuce9U-m.d.mts.map +1 -0
- package/dist/plugin-types.d.mts +2 -1
- package/dist/plugin-types.d.mts.map +1 -1
- package/dist/plugin-utils.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-DZdlS6Cu.mjs → query-34pfm02Q.mjs} +11 -11
- package/dist/{query-DZdlS6Cu.mjs.map → query-34pfm02Q.mjs.map} +1 -1
- package/dist/{rate-limit-D1Y6VFVP.mjs → rate-limit-CBQK4ygJ.mjs} +2 -2
- package/dist/{rate-limit-D1Y6VFVP.mjs.map → rate-limit-CBQK4ygJ.mjs.map} +1 -1
- package/dist/{redirects-x9eFPeyi.mjs → redirects-Dcbuisoj.mjs} +2 -1
- package/dist/redirects-Dcbuisoj.mjs.map +1 -0
- package/dist/{registry-CJkCubOJ.mjs → registry-3aOAS4My.mjs} +11 -1
- package/dist/registry-3aOAS4My.mjs.map +1 -0
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-DMjXuvNU.mjs → schema-BGwx8AQY.mjs} +3 -3
- package/dist/{schema-DMjXuvNU.mjs.map → schema-BGwx8AQY.mjs.map} +1 -1
- package/dist/{search-D1wKk3lc.mjs → search-Gl747zMP.mjs} +2 -2
- package/dist/{search-D1wKk3lc.mjs.map → search-Gl747zMP.mjs.map} +1 -1
- package/dist/{sections-CCMic8dB.mjs → sections-C-umuuLj.mjs} +2 -2
- package/dist/{sections-CCMic8dB.mjs.map → sections-C-umuuLj.mjs.map} +1 -1
- package/dist/seed/index.d.mts +1 -1
- package/dist/seed/index.mjs +6 -6
- package/dist/seo/index.d.mts +1 -1
- package/dist/{seo-3QrZ1EPT.mjs → seo-BNzkiK_W.mjs} +1 -1
- package/dist/{seo-3QrZ1EPT.mjs.map → seo-BNzkiK_W.mjs.map} +1 -1
- package/dist/{seo-DCEF-AVr.mjs → seo-BkhuuaaE.mjs} +25 -3
- package/dist/seo-BkhuuaaE.mjs.map +1 -0
- package/dist/{settings-D9rvaLuY.mjs → settings-C4tXRYiG.mjs} +3 -3
- package/dist/{settings-D9rvaLuY.mjs.map → settings-C4tXRYiG.mjs.map} +1 -1
- package/dist/{settings-C3Yi5Co3.mjs → settings-fPSR6dJa.mjs} +2 -2
- package/dist/{settings-C3Yi5Co3.mjs.map → settings-fPSR6dJa.mjs.map} +1 -1
- package/dist/{taxonomies-DhLaNJji.mjs → taxonomies-DbAtL6m4.mjs} +2 -2
- package/dist/{taxonomies-DhLaNJji.mjs.map → taxonomies-DbAtL6m4.mjs.map} +1 -1
- package/dist/{taxonomies-CurpLtNC.mjs → taxonomies-n8w72lLQ.mjs} +4 -4
- package/dist/{taxonomies-CurpLtNC.mjs.map → taxonomies-n8w72lLQ.mjs.map} +1 -1
- package/dist/{types-CTLJYMhN.d.mts → types-CNlaBFzx.d.mts} +11 -1
- package/dist/{types-CTLJYMhN.d.mts.map → types-CNlaBFzx.d.mts.map} +1 -1
- package/dist/{types-BWh2MjmA.d.mts → types-ETmO_jQr.d.mts} +2 -2
- package/dist/{types-BWh2MjmA.d.mts.map → types-ETmO_jQr.d.mts.map} +1 -1
- package/dist/{types-TTx8uP7V.d.mts → types-h2Cwbzi2.d.mts} +6 -3
- package/dist/{types-TTx8uP7V.d.mts.map → types-h2Cwbzi2.d.mts.map} +1 -1
- package/dist/types-tM44hEcf.mjs.map +1 -1
- package/dist/{validate-DOFrIb-c.d.mts → validate-Va_An3qb.d.mts} +2 -2
- package/dist/{validate-DOFrIb-c.d.mts.map → validate-Va_An3qb.d.mts.map} +1 -1
- package/dist/{validation-CY1gk4Ar.mjs → validation-B6DKPTtU.mjs} +3 -3
- package/dist/{validation-CY1gk4Ar.mjs.map → validation-B6DKPTtU.mjs.map} +1 -1
- package/dist/version-DHdCA24u.mjs +7 -0
- package/dist/{version-C0JPFUG8.mjs.map → version-DHdCA24u.mjs.map} +1 -1
- package/dist/{widgets-Dri0spI4.mjs → widgets-C2RbFN0G.mjs} +2 -2
- package/dist/{widgets-Dri0spI4.mjs.map → widgets-C2RbFN0G.mjs.map} +1 -1
- package/package.json +6 -6
- package/src/api/errors.ts +1 -0
- package/src/api/handlers/content.ts +4 -4
- package/src/api/handlers/seo.ts +30 -2
- package/src/api/schemas/schema.ts +1 -0
- package/src/astro/middleware.ts +14 -1
- package/src/astro/routes/api/import/wordpress/media.ts +9 -3
- package/src/astro/routes/api/media/[id]/confirm.ts +62 -2
- package/src/astro/routes/api/media.ts +17 -19
- package/src/components/EmDashImage.astro +4 -7
- package/src/components/Image.astro +14 -4
- package/src/components/InlinePortableTextEditor.tsx +48 -1
- package/src/database/repositories/content.ts +8 -5
- package/src/database/repositories/media.ts +9 -1
- package/src/emdash-runtime.ts +39 -1
- package/src/media/enrich.ts +78 -0
- package/src/media/index.ts +1 -0
- package/src/media/local-runtime.ts +19 -0
- package/src/media/normalize.ts +20 -1
- package/src/media/placeholder.ts +29 -9
- package/src/media/types.ts +14 -0
- package/src/plugin-types.ts +2 -0
- package/src/plugins/context.ts +8 -0
- package/src/plugins/hooks.ts +45 -0
- package/src/plugins/index.ts +1 -0
- package/src/plugins/manager.ts +11 -0
- package/src/plugins/manifest-schema.ts +1 -0
- package/src/plugins/types.ts +7 -0
- package/src/schema/registry.ts +25 -0
- package/src/schema/types.ts +10 -0
- package/src/seed/apply.ts +1 -0
- package/dist/api-d_0wi9fW.mjs.map +0 -1
- package/dist/apply-BE9UaWX0.mjs.map +0 -1
- package/dist/byline-fields-Ca56gaT6.d.mts.map +0 -1
- package/dist/content-PoVGKjp0.mjs.map +0 -1
- package/dist/context-Dvws41_A.mjs.map +0 -1
- package/dist/error-lq_fL-RS.mjs.map +0 -1
- package/dist/index-BroXT-vD.d.mts.map +0 -1
- package/dist/manifest-schema-BWKnQYQF.mjs.map +0 -1
- package/dist/media-CTjJoJTh.mjs.map +0 -1
- package/dist/menus-GvRSpgIA.mjs.map +0 -1
- package/dist/normalize-ClbNIenm.mjs.map +0 -1
- package/dist/placeholder-B0s2mWu5.mjs.map +0 -1
- package/dist/placeholder-QoI4gEzP.d.mts.map +0 -1
- package/dist/redirects-x9eFPeyi.mjs.map +0 -1
- package/dist/registry-CJkCubOJ.mjs.map +0 -1
- package/dist/seo-DCEF-AVr.mjs.map +0 -1
- package/dist/version-C0JPFUG8.mjs +0 -7
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import "../../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../../types-CmI1OyQg.mjs";
|
|
3
3
|
import { n as FIELD_TYPES } from "../../../../../types-tM44hEcf.mjs";
|
|
4
|
-
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-
|
|
5
|
-
import { n as parseBody, t as isParseError } from "../../../../../parse-
|
|
6
|
-
import "../../../../../redirects-
|
|
7
|
-
import { g as wpPrepareBody } from "../../../../../byline-fields-
|
|
4
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DXnK2wF4.mjs";
|
|
5
|
+
import { n as parseBody, t as isParseError } from "../../../../../parse-DqMWHQ8x.mjs";
|
|
6
|
+
import "../../../../../redirects-Dcbuisoj.mjs";
|
|
7
|
+
import { g as wpPrepareBody } from "../../../../../byline-fields-eyJMblSr.mjs";
|
|
8
8
|
import "../../../../../status-vUK0SA17.mjs";
|
|
9
9
|
import "../../../../../api/schemas/index.mjs";
|
|
10
|
-
import { n as requirePerm } from "../../../../../authorize-
|
|
10
|
+
import { n as requirePerm } from "../../../../../authorize-CuxZ0i2t.mjs";
|
|
11
11
|
import { capitalize, sanitizeSlug, singularize } from "./analyze.mjs";
|
|
12
12
|
|
|
13
13
|
//#region src/astro/routes/api/import/wordpress/prepare.ts
|
|
@@ -32,7 +32,7 @@ const POST = async ({ request, locals }) => {
|
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
34
|
async function prepareImport(db, request) {
|
|
35
|
-
const { SchemaRegistry } = await import("../../../../../registry-
|
|
35
|
+
const { SchemaRegistry } = await import("../../../../../registry-3aOAS4My.mjs").then((n) => n.r);
|
|
36
36
|
const registry = new SchemaRegistry(db);
|
|
37
37
|
const result = {
|
|
38
38
|
success: true,
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { t as validateIdentifier } from "../../../../../validate-VPnKoIzW.mjs";
|
|
2
2
|
import "../../../../../base64-CmWvODNW.mjs";
|
|
3
3
|
import "../../../../../types-CmI1OyQg.mjs";
|
|
4
|
-
import { n as normalizeMediaValue } from "../../../../../normalize-
|
|
5
|
-
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-
|
|
6
|
-
import { n as parseBody, t as isParseError } from "../../../../../parse-
|
|
7
|
-
import "../../../../../redirects-
|
|
8
|
-
import { _ as wpRewriteUrlsBody } from "../../../../../byline-fields-
|
|
4
|
+
import { n as normalizeMediaValue } from "../../../../../normalize-CeglrBT9.mjs";
|
|
5
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DXnK2wF4.mjs";
|
|
6
|
+
import { n as parseBody, t as isParseError } from "../../../../../parse-DqMWHQ8x.mjs";
|
|
7
|
+
import "../../../../../redirects-Dcbuisoj.mjs";
|
|
8
|
+
import { _ as wpRewriteUrlsBody } from "../../../../../byline-fields-eyJMblSr.mjs";
|
|
9
9
|
import "../../../../../status-vUK0SA17.mjs";
|
|
10
10
|
import "../../../../../api/schemas/index.mjs";
|
|
11
|
-
import { n as requirePerm } from "../../../../../authorize-
|
|
11
|
+
import { n as requirePerm } from "../../../../../authorize-CuxZ0i2t.mjs";
|
|
12
12
|
import { buildBaseUrlMap, extractMediaUrl, findMatchingUrl, rewritePortableTextUrls, rewriteStringUrls } from "./rewrite-url-helpers.mjs";
|
|
13
13
|
import { sql } from "kysely";
|
|
14
14
|
|
|
@@ -35,7 +35,7 @@ const POST = async ({ request, locals }) => {
|
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
37
|
async function rewriteUrls(db, urlMap, getProvider, collections) {
|
|
38
|
-
const { SchemaRegistry } = await import("../../../../../registry-
|
|
38
|
+
const { SchemaRegistry } = await import("../../../../../registry-3aOAS4My.mjs").then((n) => n.r);
|
|
39
39
|
const registry = new SchemaRegistry(db);
|
|
40
40
|
const result = {
|
|
41
41
|
updated: 0,
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import "../../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../../types-CmI1OyQg.mjs";
|
|
3
3
|
import { n as resolveAndValidateExternalUrl, t as SsrfError } from "../../../../../ssrf-BRKb343l.mjs";
|
|
4
|
-
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-
|
|
5
|
-
import { n as parseBody, t as isParseError } from "../../../../../parse-
|
|
6
|
-
import "../../../../../redirects-
|
|
7
|
-
import { m as wpPluginAnalyzeBody } from "../../../../../byline-fields-
|
|
4
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DXnK2wF4.mjs";
|
|
5
|
+
import { n as parseBody, t as isParseError } from "../../../../../parse-DqMWHQ8x.mjs";
|
|
6
|
+
import "../../../../../redirects-Dcbuisoj.mjs";
|
|
7
|
+
import { m as wpPluginAnalyzeBody } from "../../../../../byline-fields-eyJMblSr.mjs";
|
|
8
8
|
import "../../../../../status-vUK0SA17.mjs";
|
|
9
9
|
import "../../../../../api/schemas/index.mjs";
|
|
10
10
|
import { s as getSource } from "../../../../../import-Bb1T9WJS.mjs";
|
|
11
11
|
import "../../../../../ssrf-CcX9zvMK.mjs";
|
|
12
12
|
import "../../../../../utils-B7A57fm9.mjs";
|
|
13
|
-
import { n as requirePerm } from "../../../../../authorize-
|
|
13
|
+
import { n as requirePerm } from "../../../../../authorize-CuxZ0i2t.mjs";
|
|
14
14
|
import { SchemaRegistry } from "emdash";
|
|
15
15
|
|
|
16
16
|
//#region src/astro/routes/api/import/wordpress-plugin/analyze.ts
|
|
@@ -9,16 +9,16 @@ import "../../../../../byline-registry-BOjqDOim.mjs";
|
|
|
9
9
|
import "../../../../../field-defs-cache-CYYx8xlG.mjs";
|
|
10
10
|
import { t as BylineRepository } from "../../../../../byline-C-YajfV7.mjs";
|
|
11
11
|
import { n as resolveAndValidateExternalUrl, t as SsrfError } from "../../../../../ssrf-BRKb343l.mjs";
|
|
12
|
-
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-
|
|
13
|
-
import { n as parseBody, t as isParseError } from "../../../../../parse-
|
|
14
|
-
import "../../../../../redirects-
|
|
15
|
-
import { h as wpPluginExecuteBody } from "../../../../../byline-fields-
|
|
12
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DXnK2wF4.mjs";
|
|
13
|
+
import { n as parseBody, t as isParseError } from "../../../../../parse-DqMWHQ8x.mjs";
|
|
14
|
+
import "../../../../../redirects-Dcbuisoj.mjs";
|
|
15
|
+
import { h as wpPluginExecuteBody } from "../../../../../byline-fields-eyJMblSr.mjs";
|
|
16
16
|
import "../../../../../status-vUK0SA17.mjs";
|
|
17
17
|
import "../../../../../api/schemas/index.mjs";
|
|
18
18
|
import { s as getSource } from "../../../../../import-Bb1T9WJS.mjs";
|
|
19
19
|
import "../../../../../ssrf-CcX9zvMK.mjs";
|
|
20
20
|
import { m as resolveImportByline } from "../../../../../utils-B7A57fm9.mjs";
|
|
21
|
-
import { n as requirePerm } from "../../../../../authorize-
|
|
21
|
+
import { n as requirePerm } from "../../../../../authorize-CuxZ0i2t.mjs";
|
|
22
22
|
import { ContentRepository, SchemaRegistry } from "emdash";
|
|
23
23
|
|
|
24
24
|
//#region src/astro/routes/api/import/wordpress-plugin/execute.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { r as handleError } from "../../../error-
|
|
4
|
-
import { n as VERSION, t as COMMIT } from "../../../version-
|
|
3
|
+
import { r as handleError } from "../../../error-DXnK2wF4.mjs";
|
|
4
|
+
import { n as VERSION, t as COMMIT } from "../../../version-DHdCA24u.mjs";
|
|
5
5
|
import { t as getAuthMode } from "../../../mode-BB0F8xTC.mjs";
|
|
6
6
|
|
|
7
7
|
//#region src/astro/routes/api/manifest.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { t as apiError } from "../../../error-
|
|
4
|
-
import { Dn as contentBylineInputSchema, Tn as bylineUpdateBody, un as contentSeoInput, yn as bylineCreateBody } from "../../../redirects-
|
|
5
|
-
import "../../../byline-fields-
|
|
3
|
+
import { t as apiError } from "../../../error-DXnK2wF4.mjs";
|
|
4
|
+
import { Dn as contentBylineInputSchema, Tn as bylineUpdateBody, un as contentSeoInput, yn as bylineCreateBody } from "../../../redirects-Dcbuisoj.mjs";
|
|
5
|
+
import "../../../byline-fields-eyJMblSr.mjs";
|
|
6
6
|
import "../../../status-vUK0SA17.mjs";
|
|
7
7
|
import "../../../api/schemas/index.mjs";
|
|
8
8
|
import { i as hasScope } from "../../../api-tokens-B4BQybOp.mjs";
|
|
@@ -157,7 +157,7 @@ function getEmDash(extra) {
|
|
|
157
157
|
}
|
|
158
158
|
async function getCollectionFields(ec, collection) {
|
|
159
159
|
try {
|
|
160
|
-
const { SchemaRegistry } = await import("../../../schema-
|
|
160
|
+
const { SchemaRegistry } = await import("../../../schema-BGwx8AQY.mjs").then((n) => n.t);
|
|
161
161
|
const col = await new SchemaRegistry(ec.db).getCollectionWithFields(collection);
|
|
162
162
|
return col ? col.fields : null;
|
|
163
163
|
} catch {
|
|
@@ -186,7 +186,7 @@ async function applyReadMarkdown(ec, collection, items) {
|
|
|
186
186
|
for (const item of items) if (item.data && typeof item.data === "object") item.data = convertDataForRead(item.data, fields, false);
|
|
187
187
|
}
|
|
188
188
|
async function invalidateBylines() {
|
|
189
|
-
const { invalidateBylineCache } = await import("../../../bylines-
|
|
189
|
+
const { invalidateBylineCache } = await import("../../../bylines-CPc_q2Vd.mjs").then((n) => n.t);
|
|
190
190
|
invalidateBylineCache();
|
|
191
191
|
}
|
|
192
192
|
/**
|
|
@@ -813,7 +813,7 @@ function createMcpServer() {
|
|
|
813
813
|
requireRole(extra, Role.EDITOR);
|
|
814
814
|
const ec = getEmDash(extra);
|
|
815
815
|
try {
|
|
816
|
-
const { SchemaRegistry } = await import("../../../schema-
|
|
816
|
+
const { SchemaRegistry } = await import("../../../schema-BGwx8AQY.mjs").then((n) => n.t);
|
|
817
817
|
return jsonResult({ items: await new SchemaRegistry(ec.db).listCollections() });
|
|
818
818
|
} catch (error) {
|
|
819
819
|
return respondHandlerError(error, "SCHEMA_LIST_ERROR");
|
|
@@ -829,7 +829,7 @@ function createMcpServer() {
|
|
|
829
829
|
requireRole(extra, Role.EDITOR);
|
|
830
830
|
const ec = getEmDash(extra);
|
|
831
831
|
try {
|
|
832
|
-
const { SchemaRegistry } = await import("../../../schema-
|
|
832
|
+
const { SchemaRegistry } = await import("../../../schema-BGwx8AQY.mjs").then((n) => n.t);
|
|
833
833
|
const collection = await new SchemaRegistry(ec.db).getCollectionWithFields(args.slug);
|
|
834
834
|
if (!collection) return respondError("NOT_FOUND", `Collection '${args.slug}' not found`);
|
|
835
835
|
return jsonResult(collection);
|
|
@@ -859,7 +859,7 @@ function createMcpServer() {
|
|
|
859
859
|
requireRole(extra, Role.ADMIN);
|
|
860
860
|
const ec = getEmDash(extra);
|
|
861
861
|
try {
|
|
862
|
-
const { SchemaRegistry } = await import("../../../schema-
|
|
862
|
+
const { SchemaRegistry } = await import("../../../schema-BGwx8AQY.mjs").then((n) => n.t);
|
|
863
863
|
const collection = await new SchemaRegistry(ec.db).createCollection({
|
|
864
864
|
slug: args.slug,
|
|
865
865
|
label: args.label,
|
|
@@ -887,7 +887,7 @@ function createMcpServer() {
|
|
|
887
887
|
requireRole(extra, Role.ADMIN);
|
|
888
888
|
const ec = getEmDash(extra);
|
|
889
889
|
try {
|
|
890
|
-
const { SchemaRegistry } = await import("../../../schema-
|
|
890
|
+
const { SchemaRegistry } = await import("../../../schema-BGwx8AQY.mjs").then((n) => n.t);
|
|
891
891
|
await new SchemaRegistry(ec.db).deleteCollection(args.slug, { force: args.force });
|
|
892
892
|
ec.invalidateUrlPatternCache();
|
|
893
893
|
return jsonResult({ deleted: args.slug });
|
|
@@ -941,7 +941,7 @@ function createMcpServer() {
|
|
|
941
941
|
requireRole(extra, Role.ADMIN);
|
|
942
942
|
const ec = getEmDash(extra);
|
|
943
943
|
try {
|
|
944
|
-
const { SchemaRegistry } = await import("../../../schema-
|
|
944
|
+
const { SchemaRegistry } = await import("../../../schema-BGwx8AQY.mjs").then((n) => n.t);
|
|
945
945
|
return jsonResult(await new SchemaRegistry(ec.db).createField(args.collection, {
|
|
946
946
|
slug: args.slug,
|
|
947
947
|
label: args.label,
|
|
@@ -971,7 +971,7 @@ function createMcpServer() {
|
|
|
971
971
|
requireRole(extra, Role.ADMIN);
|
|
972
972
|
const ec = getEmDash(extra);
|
|
973
973
|
try {
|
|
974
|
-
const { SchemaRegistry } = await import("../../../schema-
|
|
974
|
+
const { SchemaRegistry } = await import("../../../schema-BGwx8AQY.mjs").then((n) => n.t);
|
|
975
975
|
await new SchemaRegistry(ec.db).deleteField(args.collection, args.fieldSlug);
|
|
976
976
|
return jsonResult({
|
|
977
977
|
deleted: args.fieldSlug,
|
|
@@ -1092,7 +1092,7 @@ function createMcpServer() {
|
|
|
1092
1092
|
requireScope(extra, "content:read");
|
|
1093
1093
|
const ec = getEmDash(extra);
|
|
1094
1094
|
try {
|
|
1095
|
-
const { searchWithDb } = await import("../../../search-
|
|
1095
|
+
const { searchWithDb } = await import("../../../search-Gl747zMP.mjs").then((n) => n.t);
|
|
1096
1096
|
return jsonResult(await searchWithDb(ec.db, args.query, {
|
|
1097
1097
|
collections: args.collections,
|
|
1098
1098
|
locale: args.locale,
|
|
@@ -1111,7 +1111,7 @@ function createMcpServer() {
|
|
|
1111
1111
|
requireScope(extra, "content:read");
|
|
1112
1112
|
const ec = getEmDash(extra);
|
|
1113
1113
|
try {
|
|
1114
|
-
const { handleTaxonomyList } = await import("../../../taxonomies-
|
|
1114
|
+
const { handleTaxonomyList } = await import("../../../taxonomies-DbAtL6m4.mjs").then((n) => n.l);
|
|
1115
1115
|
return unwrap(await handleTaxonomyList(ec.db, { locale: args.locale }));
|
|
1116
1116
|
} catch (error) {
|
|
1117
1117
|
return respondHandlerError(error, "TAXONOMY_LIST_ERROR");
|
|
@@ -1131,7 +1131,7 @@ function createMcpServer() {
|
|
|
1131
1131
|
requireScope(extra, "content:read");
|
|
1132
1132
|
const ec = getEmDash(extra);
|
|
1133
1133
|
try {
|
|
1134
|
-
const { handleTaxonomyList } = await import("../../../taxonomies-
|
|
1134
|
+
const { handleTaxonomyList } = await import("../../../taxonomies-DbAtL6m4.mjs").then((n) => n.l);
|
|
1135
1135
|
const listResult = await handleTaxonomyList(ec.db, { locale: args.locale });
|
|
1136
1136
|
if (!listResult.success) return unwrap(listResult);
|
|
1137
1137
|
if (!listResult.data.taxonomies.find((t) => t.name === args.taxonomy)) return respondError("NOT_FOUND", `Taxonomy '${args.taxonomy}' not found`);
|
|
@@ -1190,7 +1190,7 @@ function createMcpServer() {
|
|
|
1190
1190
|
requireRole(extra, Role.EDITOR);
|
|
1191
1191
|
const ec = getEmDash(extra);
|
|
1192
1192
|
try {
|
|
1193
|
-
const { handleTermCreate } = await import("../../../taxonomies-
|
|
1193
|
+
const { handleTermCreate } = await import("../../../taxonomies-DbAtL6m4.mjs").then((n) => n.l);
|
|
1194
1194
|
return unwrap(await handleTermCreate(ec.db, args.taxonomy, {
|
|
1195
1195
|
slug: args.slug,
|
|
1196
1196
|
label: args.label,
|
|
@@ -1219,7 +1219,7 @@ function createMcpServer() {
|
|
|
1219
1219
|
requireRole(extra, Role.EDITOR);
|
|
1220
1220
|
const ec = getEmDash(extra);
|
|
1221
1221
|
try {
|
|
1222
|
-
const { handleTermUpdate } = await import("../../../taxonomies-
|
|
1222
|
+
const { handleTermUpdate } = await import("../../../taxonomies-DbAtL6m4.mjs").then((n) => n.l);
|
|
1223
1223
|
return unwrap(await handleTermUpdate(ec.db, args.taxonomy, args.termSlug, {
|
|
1224
1224
|
slug: args.slug,
|
|
1225
1225
|
label: args.label,
|
|
@@ -1243,7 +1243,7 @@ function createMcpServer() {
|
|
|
1243
1243
|
requireRole(extra, Role.EDITOR);
|
|
1244
1244
|
const ec = getEmDash(extra);
|
|
1245
1245
|
try {
|
|
1246
|
-
const { handleTermDelete } = await import("../../../taxonomies-
|
|
1246
|
+
const { handleTermDelete } = await import("../../../taxonomies-DbAtL6m4.mjs").then((n) => n.l);
|
|
1247
1247
|
return unwrap(await handleTermDelete(ec.db, args.taxonomy, args.termSlug));
|
|
1248
1248
|
} catch (error) {
|
|
1249
1249
|
return respondHandlerError(error, "TAXONOMY_TERM_DELETE_ERROR");
|
|
@@ -1258,7 +1258,7 @@ function createMcpServer() {
|
|
|
1258
1258
|
requireScope(extra, "content:read");
|
|
1259
1259
|
const ec = getEmDash(extra);
|
|
1260
1260
|
try {
|
|
1261
|
-
const { handleTermTranslations } = await import("../../../taxonomies-
|
|
1261
|
+
const { handleTermTranslations } = await import("../../../taxonomies-DbAtL6m4.mjs").then((n) => n.l);
|
|
1262
1262
|
return unwrap(await handleTermTranslations(ec.db, args.id));
|
|
1263
1263
|
} catch (error) {
|
|
1264
1264
|
return respondHandlerError(error, "TERM_TRANSLATIONS_ERROR");
|
|
@@ -1454,7 +1454,7 @@ function createMcpServer() {
|
|
|
1454
1454
|
requireRole(extra, Role.EDITOR);
|
|
1455
1455
|
const ec = getEmDash(extra);
|
|
1456
1456
|
try {
|
|
1457
|
-
const { handleSettingsGet } = await import("../../../settings-
|
|
1457
|
+
const { handleSettingsGet } = await import("../../../settings-fPSR6dJa.mjs").then((n) => n.r);
|
|
1458
1458
|
return unwrap(await handleSettingsGet(ec.db, ec.storage));
|
|
1459
1459
|
} catch (error) {
|
|
1460
1460
|
return respondHandlerError(error, "SETTINGS_READ_ERROR");
|
|
@@ -1480,7 +1480,7 @@ function createMcpServer() {
|
|
|
1480
1480
|
requireRole(extra, Role.ADMIN);
|
|
1481
1481
|
const ec = getEmDash(extra);
|
|
1482
1482
|
try {
|
|
1483
|
-
const { handleSettingsUpdate } = await import("../../../settings-
|
|
1483
|
+
const { handleSettingsUpdate } = await import("../../../settings-fPSR6dJa.mjs").then((n) => n.r);
|
|
1484
1484
|
return unwrap(await handleSettingsUpdate(ec.db, ec.storage, args));
|
|
1485
1485
|
} catch (error) {
|
|
1486
1486
|
return respondHandlerError(error, "SETTINGS_UPDATE_ERROR");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm.d.mts","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/confirm.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"confirm.d.mts","names":[],"sources":["../../../../../../src/astro/routes/api/media/[id]/confirm.ts"],"mappings":";;;cAmBa,SAAA;;;;cAwBA,IAAA,EAAM,QAAA"}
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import "../../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { r as
|
|
5
|
-
import {
|
|
6
|
-
import "../../../../../
|
|
3
|
+
import { t as enrichImageMetadata } from "../../../../../enrich-BQ0mxJRs.mjs";
|
|
4
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../error-DXnK2wF4.mjs";
|
|
5
|
+
import { r as parseOptionalBody, t as isParseError } from "../../../../../parse-DqMWHQ8x.mjs";
|
|
6
|
+
import { Ht as mediaConfirmBody } from "../../../../../redirects-Dcbuisoj.mjs";
|
|
7
|
+
import "../../../../../byline-fields-eyJMblSr.mjs";
|
|
7
8
|
import "../../../../../status-vUK0SA17.mjs";
|
|
8
9
|
import "../../../../../api/schemas/index.mjs";
|
|
9
|
-
import { n as requirePerm, t as requireOwnerPerm } from "../../../../../authorize-
|
|
10
|
+
import { n as requirePerm, t as requireOwnerPerm } from "../../../../../authorize-CuxZ0i2t.mjs";
|
|
10
11
|
import { MediaRepository } from "emdash";
|
|
11
12
|
|
|
12
13
|
//#region src/astro/routes/api/media/[id]/confirm.ts
|
|
13
14
|
const prerender = false;
|
|
14
15
|
/**
|
|
16
|
+
* Max raw bytes to buffer for server-side LQIP generation at confirm time. The
|
|
17
|
+
* signed-URL upload flow exists so large files bypass server buffering — re-reading
|
|
18
|
+
* the whole object into a Worker's 128 MB heap to compute a blurhash would OOM
|
|
19
|
+
* on the very uploads that flow was designed for. LQIP is progressive
|
|
20
|
+
* enhancement: large images simply ship without a server-generated placeholder.
|
|
21
|
+
*/
|
|
22
|
+
const MAX_PLACEHOLDER_DOWNLOAD_BYTES = 8 * 1024 * 1024;
|
|
23
|
+
/**
|
|
15
24
|
* Add URL to media item (relative URL for portability)
|
|
16
25
|
*/
|
|
17
26
|
function addUrlToMedia(item) {
|
|
@@ -45,10 +54,37 @@ const POST = async ({ params, request, locals }) => {
|
|
|
45
54
|
return apiError("FILE_NOT_FOUND", "File was not uploaded to storage", 400);
|
|
46
55
|
}
|
|
47
56
|
}
|
|
57
|
+
let blurhash;
|
|
58
|
+
let dominantColor;
|
|
59
|
+
let width = body.width;
|
|
60
|
+
let height = body.height;
|
|
61
|
+
if (emdash.storage && existing.mimeType.startsWith("image/")) {
|
|
62
|
+
const knownSize = body.size ?? existing.size ?? void 0;
|
|
63
|
+
if (!(knownSize != null && knownSize > MAX_PLACEHOLDER_DOWNLOAD_BYTES)) try {
|
|
64
|
+
const { body: stream } = await emdash.storage.download(existing.storageKey);
|
|
65
|
+
const bytes = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
66
|
+
if (bytes.byteLength > MAX_PLACEHOLDER_DOWNLOAD_BYTES) console.warn(`[media] confirm skipping placeholder: object ${existing.storageKey} is ${bytes.byteLength} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`);
|
|
67
|
+
else {
|
|
68
|
+
const enriched = await enrichImageMetadata(bytes, existing.mimeType, { knownDimensions: body.width != null && body.height != null ? {
|
|
69
|
+
width: body.width,
|
|
70
|
+
height: body.height
|
|
71
|
+
} : void 0 });
|
|
72
|
+
blurhash = enriched.blurhash;
|
|
73
|
+
dominantColor = enriched.dominantColor;
|
|
74
|
+
width = width ?? enriched.width;
|
|
75
|
+
height = height ?? enriched.height;
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("[media] confirm placeholder generation failed:", error);
|
|
79
|
+
}
|
|
80
|
+
else console.warn(`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${knownSize} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`);
|
|
81
|
+
}
|
|
48
82
|
const item = await repo.confirmUpload(id, {
|
|
49
83
|
size: body.size,
|
|
50
|
-
width
|
|
51
|
-
height
|
|
84
|
+
width,
|
|
85
|
+
height,
|
|
86
|
+
blurhash,
|
|
87
|
+
dominantColor
|
|
52
88
|
});
|
|
53
89
|
if (!item) return apiError("CONFIRM_FAILED", "Failed to confirm upload", 500);
|
|
54
90
|
return apiSuccess({ item: addUrlToMedia(item) });
|
|
@@ -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 { MediaRepository } 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 type { MediaItem } from \"#types\";\n\nexport const prerender = false;\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\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\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\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:edit_own\",\n\t\t\t\"media:edit_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\t// Optionally verify the file exists in storage\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\t// Mark as failed\n\t\t\t\tawait repo.markFailed(id);\n\t\t\t\treturn apiError(\"FILE_NOT_FOUND\", \"File was not uploaded to storage\", 400);\n\t\t\t}\n\t\t}\n\n\t\t//
|
|
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 { MediaRepository } 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 { enrichImageMetadata } from \"#media/enrich.js\";\nimport type { MediaItem } from \"#types\";\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 = 8 * 1024 * 1024;\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\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\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\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:edit_own\",\n\t\t\t\"media:edit_any\",\n\t\t);\n\t\tif (ownerDenied) return ownerDenied;\n\n\t\t// Optionally verify the file exists in storage\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\t// Mark as failed\n\t\t\t\tawait repo.markFailed(id);\n\t\t\t\treturn apiError(\"FILE_NOT_FOUND\", \"File was not uploaded to storage\", 400);\n\t\t\t}\n\t\t}\n\n\t\t// For images, read the just-uploaded bytes back from storage once to\n\t\t// generate LQIP placeholders (and server-side dimensions as a fallback).\n\t\t// The signed-URL flow uploads directly to storage, so this confirm is the\n\t\t// only point at which the server sees the bytes. Best-effort: a decode\n\t\t// failure must not block the upload from being marked ready. We also cap\n\t\t// the download size — buffering a large original into a Worker heap to\n\t\t// compute a 32px blurhash would OOM on the uploads the signed-URL path\n\t\t// exists to support, so oversized files skip the server-side placeholder.\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 (emdash.storage && existing.mimeType.startsWith(\"image/\")) {\n\t\t\tconst knownSize = body.size ?? existing.size ?? undefined;\n\t\t\tconst tooLarge = knownSize != null && knownSize > MAX_PLACEHOLDER_DOWNLOAD_BYTES;\n\t\t\tif (!tooLarge) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { body: stream } = await emdash.storage.download(existing.storageKey);\n\t\t\t\t\tconst bytes = new Uint8Array(await new Response(stream).arrayBuffer());\n\t\t\t\t\t// Defense-in-depth for the unknown-size case: even though we\n\t\t\t\t\t// already buffered it, refuse the decode so we don't also pay\n\t\t\t\t\t// the (larger) RGBA allocation.\n\t\t\t\t\tif (bytes.byteLength > MAX_PLACEHOLDER_DOWNLOAD_BYTES) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[media] confirm skipping placeholder: object ${existing.storageKey} is ${bytes.byteLength} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst enriched = await enrichImageMetadata(bytes, existing.mimeType, {\n\t\t\t\t\t\t\tknownDimensions:\n\t\t\t\t\t\t\t\tbody.width != null && body.height != null\n\t\t\t\t\t\t\t\t\t? { width: body.width, height: body.height }\n\t\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tblurhash = enriched.blurhash;\n\t\t\t\t\t\tdominantColor = enriched.dominantColor;\n\t\t\t\t\t\twidth = width ?? enriched.width;\n\t\t\t\t\t\theight = height ?? enriched.height;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(\"[media] confirm placeholder generation failed:\", error);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[media] confirm skipping placeholder: object ${existing.storageKey} reported size ${knownSize} bytes (> ${MAX_PLACEHOLDER_DOWNLOAD_BYTES})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Confirm the upload\n\t\tconst item = await repo.confirmUpload(id, {\n\t\t\tsize: body.size,\n\t\t\twidth,\n\t\t\theight,\n\t\t\tblurhash,\n\t\t\tdominantColor,\n\t\t});\n\n\t\tif (!item) {\n\t\t\treturn apiError(\"CONFIRM_FAILED\", \"Failed to confirm upload\", 500);\n\t\t}\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":";;;;;;;;;;;;;AAmBA,MAAa,YAAY;;;;;;;;AASzB,MAAM,iCAAiC,IAAI,OAAO;;;;AAKlD,SAAS,cAAc,MAA8C;AACpE,QAAO;EACN,GAAG;EACH,KAAK,2BAA2B,KAAK;EACrC;;;;;AAMF,MAAa,OAAiB,OAAO,EAAE,QAAQ,SAAS,aAAa;CACpE,MAAM,EAAE,QAAQ,SAAS;CACzB,MAAM,EAAE,OAAO;CAEf,MAAM,SAAS,YAAY,MAAM,eAAe;AAChD,KAAI,OAAQ,QAAO;AAEnB,KAAI,CAAC,GACJ,QAAO,SAAS,mBAAmB,wBAAwB,IAAI;AAGhE,KAAI,CAAC,QAAQ,GACZ,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;AAGpE,KAAI;EACH,MAAM,OAAO,MAAM,kBAAkB,SAAS,kBAAkB,EAAE,CAAC;AACnE,MAAI,aAAa,KAAK,CAAE,QAAO;EAE/B,MAAM,OAAO,IAAI,gBAAgB,OAAO,GAAG;EAG3C,MAAM,WAAW,MAAM,KAAK,SAAS,GAAG;AACxC,MAAI,CAAC,SACJ,QAAO,SAAS,aAAa,yBAAyB,MAAM,IAAI;AAGjE,MAAI,SAAS,WAAW,UACvB,QAAO,SAAS,iBAAiB,8BAA8B,SAAS,UAAU,IAAI;EAIvF,MAAM,cAAc,iBACnB,MACA,SAAS,YAAY,IACrB,kBACA,iBACA;AACD,MAAI,YAAa,QAAO;AAGxB,MAAI,OAAO,SAEV;OAAI,CADW,MAAM,OAAO,QAAQ,OAAO,SAAS,WAAW,EAClD;AAEZ,UAAM,KAAK,WAAW,GAAG;AACzB,WAAO,SAAS,kBAAkB,oCAAoC,IAAI;;;EAY5E,IAAI;EACJ,IAAI;EACJ,IAAI,QAAQ,KAAK;EACjB,IAAI,SAAS,KAAK;AAClB,MAAI,OAAO,WAAW,SAAS,SAAS,WAAW,SAAS,EAAE;GAC7D,MAAM,YAAY,KAAK,QAAQ,SAAS,QAAQ;AAEhD,OAAI,EADa,aAAa,QAAQ,YAAY,gCAEjD,KAAI;IACH,MAAM,EAAE,MAAM,WAAW,MAAM,OAAO,QAAQ,SAAS,SAAS,WAAW;IAC3E,MAAM,QAAQ,IAAI,WAAW,MAAM,IAAI,SAAS,OAAO,CAAC,aAAa,CAAC;AAItE,QAAI,MAAM,aAAa,+BACtB,SAAQ,KACP,gDAAgD,SAAS,WAAW,MAAM,MAAM,WAAW,YAAY,+BAA+B,GACtI;SACK;KACN,MAAM,WAAW,MAAM,oBAAoB,OAAO,SAAS,UAAU,EACpE,iBACC,KAAK,SAAS,QAAQ,KAAK,UAAU,OAClC;MAAE,OAAO,KAAK;MAAO,QAAQ,KAAK;MAAQ,GAC1C,QACJ,CAAC;AACF,gBAAW,SAAS;AACpB,qBAAgB,SAAS;AACzB,aAAQ,SAAS,SAAS;AAC1B,cAAS,UAAU,SAAS;;YAErB,OAAO;AACf,YAAQ,MAAM,kDAAkD,MAAM;;OAGvE,SAAQ,KACP,gDAAgD,SAAS,WAAW,iBAAiB,UAAU,YAAY,+BAA+B,GAC1I;;EAKH,MAAM,OAAO,MAAM,KAAK,cAAc,IAAI;GACzC,MAAM,KAAK;GACX;GACA;GACA;GACA;GACA,CAAC;AAEF,MAAI,CAAC,KACJ,QAAO,SAAS,kBAAkB,4BAA4B,IAAI;AAMnE,SAAO,WAAW,EAAE,MAFA,cAAc,KAAK,EAEA,CAAC;UAChC,OAAO;AACf,SAAO,YAAY,OAAO,4BAA4B,gBAAgB"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import "../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { a as unwrapResult, r as handleError, t as apiError } from "../../../../error-
|
|
4
|
-
import { n as parseBody, t as isParseError } from "../../../../parse-
|
|
5
|
-
import { Xt as mediaUpdateBody } from "../../../../redirects-
|
|
6
|
-
import "../../../../byline-fields-
|
|
3
|
+
import { a as unwrapResult, r as handleError, t as apiError } from "../../../../error-DXnK2wF4.mjs";
|
|
4
|
+
import { n as parseBody, t as isParseError } from "../../../../parse-DqMWHQ8x.mjs";
|
|
5
|
+
import { Xt as mediaUpdateBody } from "../../../../redirects-Dcbuisoj.mjs";
|
|
6
|
+
import "../../../../byline-fields-eyJMblSr.mjs";
|
|
7
7
|
import "../../../../status-vUK0SA17.mjs";
|
|
8
8
|
import "../../../../api/schemas/index.mjs";
|
|
9
|
-
import { n as requirePerm, t as requireOwnerPerm } from "../../../../authorize-
|
|
9
|
+
import { n as requirePerm, t as requireOwnerPerm } from "../../../../authorize-CuxZ0i2t.mjs";
|
|
10
10
|
|
|
11
11
|
//#region src/astro/routes/api/media/[id].ts
|
|
12
12
|
const prerender = false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { r as handleError, t as apiError } from "../../../../../error-
|
|
3
|
+
import { r as handleError, t as apiError } from "../../../../../error-DXnK2wF4.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/astro/routes/api/media/file/[...key].ts
|
|
6
6
|
const prerender = false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../../../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-
|
|
4
|
-
import { n as requirePerm } from "../../../../../../authorize-
|
|
3
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-DXnK2wF4.mjs";
|
|
4
|
+
import { n as requirePerm } from "../../../../../../authorize-CuxZ0i2t.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
1
|
import "../../../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-
|
|
4
|
-
import { n as requirePerm } from "../../../../../../authorize-
|
|
3
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../../../error-DXnK2wF4.mjs";
|
|
4
|
+
import { n as requirePerm } from "../../../../../../authorize-CuxZ0i2t.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
1
|
import "../../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { n as apiSuccess, t as apiError } from "../../../../../error-
|
|
4
|
-
import { n as requirePerm } from "../../../../../authorize-
|
|
3
|
+
import { n as apiSuccess, t as apiError } from "../../../../../error-DXnK2wF4.mjs";
|
|
4
|
+
import { n as requirePerm } from "../../../../../authorize-CuxZ0i2t.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/astro/routes/api/media/providers/index.ts
|
|
7
7
|
const prerender = false;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import "../../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { n as normalizeMime, t as matchesMimeAllowlist } from "../../../../mime-
|
|
4
|
-
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../error-
|
|
5
|
-
import { n as parseBody, t as isParseError } from "../../../../parse-
|
|
6
|
-
import { Bt as DEFAULT_MAX_UPLOAD_SIZE, Zt as mediaUploadUrlBody } from "../../../../redirects-
|
|
7
|
-
import "../../../../byline-fields-
|
|
3
|
+
import { n as normalizeMime, t as matchesMimeAllowlist } from "../../../../mime-CCEzze7W.mjs";
|
|
4
|
+
import { n as apiSuccess, r as handleError, t as apiError } from "../../../../error-DXnK2wF4.mjs";
|
|
5
|
+
import { n as parseBody, t as isParseError } from "../../../../parse-DqMWHQ8x.mjs";
|
|
6
|
+
import { Bt as DEFAULT_MAX_UPLOAD_SIZE, Zt as mediaUploadUrlBody } from "../../../../redirects-Dcbuisoj.mjs";
|
|
7
|
+
import "../../../../byline-fields-eyJMblSr.mjs";
|
|
8
8
|
import "../../../../status-vUK0SA17.mjs";
|
|
9
9
|
import "../../../../api/schemas/index.mjs";
|
|
10
|
-
import { n as requirePerm } from "../../../../authorize-
|
|
11
|
-
import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../../media-allowlist-
|
|
10
|
+
import { n as requirePerm } from "../../../../authorize-CuxZ0i2t.mjs";
|
|
11
|
+
import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../../media-allowlist-Du8t1u6a.mjs";
|
|
12
12
|
import { ulid } from "ulidx";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import { MediaRepository } from "emdash";
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import "../../../base64-CmWvODNW.mjs";
|
|
2
2
|
import "../../../types-CmI1OyQg.mjs";
|
|
3
|
-
import { t as MediaRepository } from "../../../media-
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
3
|
+
import { t as MediaRepository } from "../../../media-BjKxjzk8.mjs";
|
|
4
|
+
import { n as normalizeMime, t as matchesMimeAllowlist } from "../../../mime-CCEzze7W.mjs";
|
|
5
|
+
import { t as enrichImageMetadata } from "../../../enrich-BQ0mxJRs.mjs";
|
|
6
6
|
import { t as computeContentHash } from "../../../hash-DrvzIXcz.mjs";
|
|
7
|
-
import { a as unwrapResult, n as apiSuccess, r as handleError, t as apiError } from "../../../error-
|
|
8
|
-
import { i as parseQuery, t as isParseError } from "../../../parse-
|
|
9
|
-
import { Bt as DEFAULT_MAX_UPLOAD_SIZE, Kt as mediaListQuery, Vt as formatFileSize } from "../../../redirects-
|
|
10
|
-
import "../../../byline-fields-
|
|
7
|
+
import { a as unwrapResult, n as apiSuccess, r as handleError, t as apiError } from "../../../error-DXnK2wF4.mjs";
|
|
8
|
+
import { i as parseQuery, t as isParseError } from "../../../parse-DqMWHQ8x.mjs";
|
|
9
|
+
import { Bt as DEFAULT_MAX_UPLOAD_SIZE, Kt as mediaListQuery, Vt as formatFileSize } from "../../../redirects-Dcbuisoj.mjs";
|
|
10
|
+
import "../../../byline-fields-eyJMblSr.mjs";
|
|
11
11
|
import "../../../status-vUK0SA17.mjs";
|
|
12
12
|
import "../../../api/schemas/index.mjs";
|
|
13
|
-
import { n as requirePerm } from "../../../authorize-
|
|
14
|
-
import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../media-allowlist-
|
|
13
|
+
import { n as requirePerm } from "../../../authorize-CuxZ0i2t.mjs";
|
|
14
|
+
import { n as resolveFieldAllowlist, t as GLOBAL_UPLOAD_ALLOWLIST } from "../../../media-allowlist-Du8t1u6a.mjs";
|
|
15
15
|
import { ulid } from "ulidx";
|
|
16
16
|
import * as path from "node:path";
|
|
17
17
|
|
|
@@ -102,25 +102,26 @@ const POST = async ({ request, locals }) => {
|
|
|
102
102
|
const height = heightStr ? parseInt(heightStr, 10) : void 0;
|
|
103
103
|
const thumbnailEntry = formData.get("thumbnail");
|
|
104
104
|
const thumbnail = thumbnailEntry instanceof File ? thumbnailEntry : null;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
else {
|
|
108
|
-
const clientDims = width && height ? {
|
|
105
|
+
const enriched = await enrichImageMetadata(buffer, file.type, {
|
|
106
|
+
knownDimensions: width != null && height != null ? {
|
|
109
107
|
width,
|
|
110
108
|
height
|
|
111
|
-
} : void 0
|
|
112
|
-
placeholder
|
|
113
|
-
|
|
109
|
+
} : void 0,
|
|
110
|
+
placeholder: thumbnail ? {
|
|
111
|
+
bytes: new Uint8Array(await thumbnail.arrayBuffer()),
|
|
112
|
+
contentType: thumbnail.type
|
|
113
|
+
} : void 0
|
|
114
|
+
});
|
|
114
115
|
const result = await emdash.handleMediaCreate({
|
|
115
116
|
filename: file.name,
|
|
116
117
|
mimeType: normalizeMime(file.type),
|
|
117
118
|
size: file.size,
|
|
118
|
-
width,
|
|
119
|
-
height,
|
|
119
|
+
width: width ?? enriched.width,
|
|
120
|
+
height: height ?? enriched.height,
|
|
120
121
|
storageKey,
|
|
121
122
|
contentHash,
|
|
122
|
-
blurhash:
|
|
123
|
-
dominantColor:
|
|
123
|
+
blurhash: enriched.blurhash,
|
|
124
|
+
dominantColor: enriched.dominantColor,
|
|
124
125
|
authorId: user?.id
|
|
125
126
|
});
|
|
126
127
|
if (!result.success) {
|
|
@@ -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 { 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 { 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 { matchesMimeAllowlist, normalizeMime } from \"#media/mime.js\";\nimport { generatePlaceholder } from \"#media/placeholder.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\n\treturn apiSuccess({ items: itemsWithUrl, 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// Generate placeholder data for images.\n\t\t// If the client sent a thumbnail (small pre-resized image), use that\n\t\t// instead of the full buffer to avoid OOM on memory-constrained runtimes.\n\t\tconst thumbnailEntry = formData.get(\"thumbnail\");\n\t\tconst thumbnail = thumbnailEntry instanceof File ? thumbnailEntry : null;\n\n\t\tlet placeholder: Awaited<ReturnType<typeof generatePlaceholder>> = null;\n\t\tif (file.type.startsWith(\"image/\")) {\n\t\t\tif (thumbnail) {\n\t\t\t\tconst thumbBuffer = new Uint8Array(await thumbnail.arrayBuffer());\n\t\t\t\tplaceholder = await generatePlaceholder(thumbBuffer, thumbnail.type);\n\t\t\t} else {\n\t\t\t\tconst clientDims = width && height ? { width, height } : undefined;\n\t\t\t\tplaceholder = await generatePlaceholder(buffer, file.type, clientDims);\n\t\t\t}\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\twidth,\n\t\t\theight,\n\t\t\tstorageKey,\n\t\t\tcontentHash,\n\t\t\tblurhash: placeholder?.blurhash,\n\t\t\tdominantColor: placeholder?.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 { 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 { 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\n\treturn apiSuccess({ items: itemsWithUrl, 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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"}
|