emdash 0.24.1 → 0.26.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.
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +12 -11
- 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-BMfveMoB.mjs} +24 -20
- package/dist/api-BMfveMoB.mjs.map +1 -0
- package/dist/{apply-BE9UaWX0.mjs → apply--IVNSdXn.mjs} +19 -16
- package/dist/apply--IVNSdXn.mjs.map +1 -0
- package/dist/astro/index.d.mts +9 -9
- package/dist/astro/index.mjs +3 -2
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +8 -8
- package/dist/astro/middleware/auth.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware.d.mts +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +99 -70
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -5
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +6 -6
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/index.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs +9 -9
- package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/index.mjs +9 -9
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +32 -32
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +32 -32
- package/dist/astro/routes/api/admin/plugins/updates.mjs +31 -31
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +31 -31
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +31 -31
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +5 -5
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- package/dist/astro/routes/api/admin/users/index.mjs +5 -5
- package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +5 -5
- package/dist/astro/routes/api/auth/invite/index.mjs +5 -5
- package/dist/astro/routes/api/auth/invite/register-options.mjs +5 -5
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +6 -6
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/verify.mjs +5 -5
- package/dist/astro/routes/api/auth/signup/complete.mjs +5 -5
- package/dist/astro/routes/api/auth/signup/request.mjs +6 -6
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +9 -9
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/index.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -6
- package/dist/astro/routes/api/dashboard.mjs +8 -8
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +2 -2
- package/dist/astro/routes/api/import/probe.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +8 -8
- package/dist/astro/routes/api/import/wordpress/execute.mjs +9 -9
- 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 +13 -9
- package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +9 -9
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +36 -36
- package/dist/astro/routes/api/media/_id_/confirm.d.mts.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +44 -8
- package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +6 -6
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.mjs +8 -8
- package/dist/astro/routes/api/media.mjs +22 -21
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/items.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/translations.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_.mjs +8 -8
- package/dist/astro/routes/api/menus/index.mjs +8 -8
- package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
- package/dist/astro/routes/api/oauth/token.mjs +2 -2
- package/dist/astro/routes/api/openapi.json.mjs +3 -3
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/index.mjs +8 -8
- package/dist/astro/routes/api/redirects/404s/summary.mjs +8 -8
- package/dist/astro/routes/api/redirects/_id_.mjs +9 -9
- package/dist/astro/routes/api/redirects/index.mjs +9 -9
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +31 -31
- package/dist/astro/routes/api/schema/collections/index.mjs +31 -31
- package/dist/astro/routes/api/schema/index.mjs +6 -6
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +31 -31
- package/dist/astro/routes/api/schema/orphans/index.mjs +31 -31
- package/dist/astro/routes/api/search/enable.mjs +9 -9
- package/dist/astro/routes/api/search/index.mjs +8 -8
- package/dist/astro/routes/api/search/rebuild.mjs +9 -9
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +8 -8
- package/dist/astro/routes/api/sections/_slug_.mjs +8 -8
- package/dist/astro/routes/api/sections/index.mjs +8 -8
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +11 -11
- package/dist/astro/routes/api/setup/admin-verify.mjs +5 -5
- package/dist/astro/routes/api/setup/admin.mjs +5 -5
- package/dist/astro/routes/api/setup/dev-bypass.mjs +17 -17
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +20 -20
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +3 -3
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/index.mjs +11 -11
- package/dist/astro/routes/api/themes/preview.mjs +3 -3
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +8 -8
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +5 -5
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +11 -10
- package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
- package/dist/astro/routes/sitemap.xml.mjs +6 -6
- package/dist/astro/types.d.mts +10 -10
- package/dist/{authorize-B__u-iZJ.mjs → authorize-BZnOGJFo.mjs} +2 -2
- package/dist/{authorize-B__u-iZJ.mjs.map → authorize-BZnOGJFo.mjs.map} +1 -1
- package/dist/{byline-C-YajfV7.mjs → byline-LL8lyqdb.mjs} +6 -6
- package/dist/{byline-C-YajfV7.mjs.map → byline-LL8lyqdb.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-D4KADDAi.mjs → bylines-BmJ7Vg7J.mjs} +4 -4
- package/dist/{bylines-D4KADDAi.mjs.map → bylines-BmJ7Vg7J.mjs.map} +1 -1
- package/dist/{bylines-BMtJnPnG.mjs → bylines-DE47vP8d.mjs} +6 -6
- package/dist/{bylines-BMtJnPnG.mjs.map → bylines-DE47vP8d.mjs.map} +1 -1
- package/dist/{cache-BUJe2w6Z.mjs → cache-D9jF5bYw.mjs} +2 -2
- package/dist/{cache-BUJe2w6Z.mjs.map → cache-D9jF5bYw.mjs.map} +1 -1
- package/dist/{chunks-BHgcHh2C.mjs → chunks-OCG163rN.mjs} +2 -2
- package/dist/{chunks-BHgcHh2C.mjs.map → chunks-OCG163rN.mjs.map} +1 -1
- package/dist/cli/index.mjs +17 -17
- package/dist/{comment-BDx50EX5.mjs → comment-D9BLDpZI.mjs} +3 -3
- package/dist/{comment-BDx50EX5.mjs.map → comment-D9BLDpZI.mjs.map} +1 -1
- package/dist/{comment-reaction-Gv_pgfI3.mjs → comment-reaction-Drl9SkNu.mjs} +2 -2
- package/dist/{comment-reaction-Gv_pgfI3.mjs.map → comment-reaction-Drl9SkNu.mjs.map} +1 -1
- package/dist/{comments-CXHuQ6jI.mjs → comments-BjJ9SKGU.mjs} +3 -3
- package/dist/{comments-CXHuQ6jI.mjs.map → comments-BjJ9SKGU.mjs.map} +1 -1
- package/dist/{content-PoVGKjp0.mjs → content-ncUWWjna.mjs} +10 -8
- package/dist/content-ncUWWjna.mjs.map +1 -0
- package/dist/{context-Dvws41_A.mjs → context-C_g-iJVA.mjs} +14 -8
- package/dist/context-C_g-iJVA.mjs.map +1 -0
- package/dist/{dashboard-DsYU8JKS.mjs → dashboard-CotyqQrW.mjs} +4 -4
- package/dist/{dashboard-DsYU8JKS.mjs.map → dashboard-CotyqQrW.mjs.map} +1 -1
- package/dist/db/index.d.mts +2 -2
- package/dist/db/index.mjs +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-rVGiqWDt.mjs} +3 -2
- package/dist/error-rVGiqWDt.mjs.map +1 -0
- package/dist/{field-defs-cache-CYYx8xlG.mjs → field-defs-cache-ljXzJCfc.mjs} +2 -2
- package/dist/{field-defs-cache-CYYx8xlG.mjs.map → field-defs-cache-ljXzJCfc.mjs.map} +1 -1
- package/dist/{fts-manager-DW_NR-Fs.mjs → fts-manager-BbCCbEKA.mjs} +2 -2
- package/dist/{fts-manager-DW_NR-Fs.mjs.map → fts-manager-BbCCbEKA.mjs.map} +1 -1
- package/dist/{index-BroXT-vD.d.mts → index-BRNabKrx.d.mts} +53 -15
- package/dist/index-BRNabKrx.d.mts.map +1 -0
- package/dist/{index-qbL02uhs.d.mts → index-Dt0OKJB4.d.mts} +2 -2
- package/dist/{index-qbL02uhs.d.mts.map → index-Dt0OKJB4.d.mts.map} +1 -1
- package/dist/index.d.mts +12 -12
- package/dist/index.mjs +43 -43
- package/dist/{load-DMFi4n1p.mjs → load-DMjXghbZ.mjs} +2 -2
- package/dist/{load-DMFi4n1p.mjs.map → load-DMjXghbZ.mjs.map} +1 -1
- package/dist/{loader-BR2SgfEc.mjs → loader-BA_zhkS_.mjs} +82 -19
- package/dist/loader-BA_zhkS_.mjs.map +1 -0
- package/dist/{manifest-schema-BWKnQYQF.mjs → manifest-schema-kPGX7VS-.mjs} +4 -1
- package/dist/manifest-schema-kPGX7VS-.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 +9 -9
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +17 -5
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-CTjJoJTh.mjs → media-C5t_eCnk.mjs} +4 -2
- package/dist/media-C5t_eCnk.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-8O6xNcYW.mjs → menus-CBb6UrMU.mjs} +3 -3
- package/dist/{menus-8O6xNcYW.mjs.map → menus-CBb6UrMU.mjs.map} +1 -1
- package/dist/{menus-GvRSpgIA.mjs → menus-CYH09a41.mjs} +65 -41
- package/dist/menus-CYH09a41.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/{object-cache-SEb2IM4Z.mjs → object-cache-CHbHv83-.mjs} +8 -4
- package/dist/object-cache-CHbHv83-.mjs.map +1 -0
- package/dist/{options-CqECG0gM.d.mts → options-41nCWqi9.d.mts} +3 -3
- package/dist/{options-CqECG0gM.d.mts.map → options-41nCWqi9.d.mts.map} +1 -1
- package/dist/page/index.d.mts +5 -2
- package/dist/page/index.d.mts.map +1 -1
- package/dist/page/index.mjs +3 -2
- package/dist/page/index.mjs.map +1 -1
- package/dist/{parse-DHI873qU.mjs → parse-BwAdS7FZ.mjs} +2 -2
- package/dist/{parse-DHI873qU.mjs.map → parse-BwAdS7FZ.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 +5 -2
- package/dist/plugin-types.d.mts.map +1 -1
- package/dist/plugin-utils.d.mts +8 -8
- package/dist/plugins/adapt-sandbox-entry.d.mts +8 -8
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-DZdlS6Cu.mjs → query-BpQb-blG.mjs} +15 -15
- package/dist/{query-DZdlS6Cu.mjs.map → query-BpQb-blG.mjs.map} +1 -1
- package/dist/{rate-limit-D1Y6VFVP.mjs → rate-limit-Dap6HMNM.mjs} +2 -2
- package/dist/{rate-limit-D1Y6VFVP.mjs.map → rate-limit-Dap6HMNM.mjs.map} +1 -1
- package/dist/{redirect-C1KVGYWe.mjs → redirect-_M0trWoo.mjs} +2 -2
- package/dist/{redirect-C1KVGYWe.mjs.map → redirect-_M0trWoo.mjs.map} +1 -1
- package/dist/{redirects-x9eFPeyi.mjs → redirects-Dcbuisoj.mjs} +2 -1
- package/dist/redirects-Dcbuisoj.mjs.map +1 -0
- package/dist/{redirects-DgnypVir.mjs → redirects-w0V9vj_j.mjs} +3 -3
- package/dist/{redirects-DgnypVir.mjs.map → redirects-w0V9vj_j.mjs.map} +1 -1
- package/dist/{registry-CJkCubOJ.mjs → registry-g8yYt4cC.mjs} +14 -4
- package/dist/registry-g8yYt4cC.mjs.map +1 -0
- package/dist/{runner-CEtq-6Sm.d.mts → runner-BbR3DfrL.d.mts} +2 -2
- package/dist/{runner-CEtq-6Sm.d.mts.map → runner-BbR3DfrL.d.mts.map} +1 -1
- package/dist/{runner-BsquExlK.mjs → runner-DPdt_Wpa.mjs} +250 -178
- package/dist/runner-DPdt_Wpa.mjs.map +1 -0
- package/dist/runtime.d.mts +9 -9
- package/dist/runtime.mjs +2 -2
- package/dist/{schema-DMjXuvNU.mjs → schema-DoLd3j2e.mjs} +5 -5
- package/dist/{schema-DMjXuvNU.mjs.map → schema-DoLd3j2e.mjs.map} +1 -1
- package/dist/{search-D1wKk3lc.mjs → search-BR0ZeZmh.mjs} +4 -4
- package/dist/{search-D1wKk3lc.mjs.map → search-BR0ZeZmh.mjs.map} +1 -1
- package/dist/{sections-CCMic8dB.mjs → sections-5J2YJPqJ.mjs} +3 -3
- package/dist/{sections-CCMic8dB.mjs.map → sections-5J2YJPqJ.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +15 -15
- package/dist/seo/index.d.mts +1 -1
- package/dist/{seo-DCEF-AVr.mjs → seo-BkhuuaaE.mjs} +25 -3
- package/dist/seo-BkhuuaaE.mjs.map +1 -0
- package/dist/{seo-3QrZ1EPT.mjs → seo-DKeXHkzE.mjs} +3 -3
- package/dist/{seo-3QrZ1EPT.mjs.map → seo-DKeXHkzE.mjs.map} +1 -1
- package/dist/{service-CQHK_10l.mjs → service-BbO2s8gT.mjs} +2 -2
- package/dist/{service-CQHK_10l.mjs.map → service-BbO2s8gT.mjs.map} +1 -1
- package/dist/{settings-C3Yi5Co3.mjs → settings-BMNr9Dmi.mjs} +3 -3
- package/dist/{settings-C3Yi5Co3.mjs.map → settings-BMNr9Dmi.mjs.map} +1 -1
- package/dist/{settings-D9rvaLuY.mjs → settings-DJeki-97.mjs} +4 -4
- package/dist/{settings-D9rvaLuY.mjs.map → settings-DJeki-97.mjs.map} +1 -1
- package/dist/{taxonomies-DhLaNJji.mjs → taxonomies-CM_fUqLU.mjs} +5 -4
- package/dist/taxonomies-CM_fUqLU.mjs.map +1 -0
- package/dist/{taxonomies-CurpLtNC.mjs → taxonomies-D13i2ctt.mjs} +76 -21
- package/dist/taxonomies-D13i2ctt.mjs.map +1 -0
- package/dist/{taxonomy-BBUlGug5.mjs → taxonomy-BU0LjdVb.mjs} +4 -4
- package/dist/{taxonomy-BBUlGug5.mjs.map → taxonomy-BU0LjdVb.mjs.map} +1 -1
- package/dist/{types-TTx8uP7V.d.mts → types-BYnDBVwe.d.mts} +27 -5
- package/dist/{types-TTx8uP7V.d.mts.map → types-BYnDBVwe.d.mts.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-CmI1OyQg.mjs → types-CZgK3EMW.mjs} +2 -2
- package/dist/{types-CmI1OyQg.mjs.map → types-CZgK3EMW.mjs.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-tM44hEcf.mjs.map +1 -1
- package/dist/{types-B-gIxFCE.d.mts → types-xIfVRNLp.d.mts} +38 -1
- package/dist/types-xIfVRNLp.d.mts.map +1 -0
- package/dist/{user-JAuc8VUf.mjs → user-DkG2jKCp.mjs} +3 -3
- package/dist/{user-JAuc8VUf.mjs.map → user-DkG2jKCp.mjs.map} +1 -1
- package/dist/{validate-DOFrIb-c.d.mts → validate-CG0V0wNR.d.mts} +3 -3
- package/dist/{validate-DOFrIb-c.d.mts.map → validate-CG0V0wNR.d.mts.map} +1 -1
- package/dist/{validate-BzXHWnkV.mjs → validate-CuJ1Ps9j.mjs} +2 -2
- package/dist/{validate-BzXHWnkV.mjs.map → validate-CuJ1Ps9j.mjs.map} +1 -1
- package/dist/{validation-CY1gk4Ar.mjs → validation-CwUfTdGF.mjs} +6 -6
- package/dist/{validation-CY1gk4Ar.mjs.map → validation-CwUfTdGF.mjs.map} +1 -1
- package/dist/version-BoNBItP_.mjs +7 -0
- package/dist/{version-C0JPFUG8.mjs.map → version-BoNBItP_.mjs.map} +1 -1
- package/dist/{widgets-Dri0spI4.mjs → widgets-BfOZ-ajY.mjs} +2 -2
- package/dist/{widgets-Dri0spI4.mjs.map → widgets-BfOZ-ajY.mjs.map} +1 -1
- package/dist/{zod-generator-xEHODMJf.mjs → zod-generator-gFsRuNNy.mjs} +6 -3
- package/dist/zod-generator-gFsRuNNy.mjs.map +1 -0
- 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/handlers/taxonomies.ts +5 -1
- package/src/api/schemas/schema.ts +1 -0
- package/src/astro/integration/virtual-modules.ts +15 -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/astro/routes/sitemap-[collection].xml.ts +5 -4
- package/src/components/EmDashHead.astro +23 -3
- package/src/components/EmDashImage.astro +4 -7
- package/src/components/Image.astro +14 -4
- package/src/components/InlinePortableTextEditor.tsx +48 -1
- package/src/components/JsonLdScript.astro +9 -0
- package/src/database/migrations/036_i18n_menus_and_taxonomies.ts +16 -0
- package/src/database/migrations/046_media_usage_index.ts +115 -0
- package/src/database/migrations/047_restore_taxonomy_parent_index.ts +29 -0
- package/src/database/migrations/runner.ts +4 -0
- package/src/database/repositories/content.ts +8 -5
- package/src/database/repositories/media-usage.ts +444 -0
- package/src/database/repositories/media.ts +9 -1
- package/src/database/types.ts +39 -0
- package/src/emdash-runtime.ts +72 -25
- package/src/index.ts +3 -0
- package/src/loader.ts +110 -31
- 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/media/usage/extractor.ts +302 -0
- package/src/media/usage/types.ts +46 -0
- package/src/object-cache/index.ts +8 -2
- package/src/page/metadata.ts +23 -3
- package/src/plugin-types.ts +12 -0
- package/src/plugins/context.ts +8 -0
- package/src/plugins/define-plugin.ts +14 -5
- package/src/plugins/hooks.ts +66 -33
- package/src/plugins/index.ts +6 -0
- package/src/plugins/manager.ts +33 -0
- package/src/plugins/manifest-schema.ts +3 -0
- package/src/plugins/types.ts +41 -2
- package/src/schema/registry.ts +25 -0
- package/src/schema/types.ts +10 -0
- package/src/schema/zod-generator.ts +9 -1
- package/src/seed/apply.ts +7 -0
- package/src/taxonomies/index.ts +121 -18
- 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/loader-BR2SgfEc.mjs.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/object-cache-SEb2IM4Z.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/runner-BsquExlK.mjs.map +0 -1
- package/dist/seo-DCEF-AVr.mjs.map +0 -1
- package/dist/taxonomies-CurpLtNC.mjs.map +0 -1
- package/dist/taxonomies-DhLaNJji.mjs.map +0 -1
- package/dist/types-B-gIxFCE.d.mts.map +0 -1
- package/dist/version-C0JPFUG8.mjs +0 -7
- package/dist/zod-generator-xEHODMJf.mjs.map +0 -1
package/src/media/placeholder.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import { encode } from "blurhash";
|
|
10
10
|
import { imageSize } from "image-size";
|
|
11
11
|
|
|
12
|
+
import { normalizeMime } from "./mime.js";
|
|
13
|
+
|
|
12
14
|
export interface PlaceholderData {
|
|
13
15
|
blurhash: string;
|
|
14
16
|
dominantColor: string;
|
|
@@ -85,8 +87,12 @@ function extractDominantColor(data: Uint8Array, width: number, height: number):
|
|
|
85
87
|
|
|
86
88
|
/**
|
|
87
89
|
* Read image dimensions from headers without decoding pixel data.
|
|
90
|
+
* Returns null when the header cannot be parsed.
|
|
91
|
+
*
|
|
92
|
+
* Shared by every caller that needs pixel dimensions so the header is parsed
|
|
93
|
+
* once per buffer, not re-read inside generatePlaceholder.
|
|
88
94
|
*/
|
|
89
|
-
function
|
|
95
|
+
export function readDimensions(buffer: Uint8Array): { width: number; height: number } | null {
|
|
90
96
|
try {
|
|
91
97
|
const result = imageSize(buffer);
|
|
92
98
|
if (result.width != null && result.height != null) {
|
|
@@ -102,23 +108,37 @@ function getImageDimensions(buffer: Uint8Array): { width: number; height: number
|
|
|
102
108
|
* Generate blurhash and dominant color from an image buffer.
|
|
103
109
|
* Returns null for non-image MIME types or on failure.
|
|
104
110
|
*
|
|
105
|
-
* @param dimensions - Optional pre-known dimensions.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
111
|
+
* @param dimensions - Optional pre-known dimensions. When present they are
|
|
112
|
+
* trusted verbatim (the caller has typically already read them via
|
|
113
|
+
* readDimensions); otherwise dimensions are read from this buffer's header.
|
|
114
|
+
* Generation is skipped (returns null) when no dimensions are available at
|
|
115
|
+
* all, or when the decoded size (width * height * 4) exceeds
|
|
116
|
+
* MAX_DECODED_BYTES — both guards avoid OOM from unbounded decodes on
|
|
117
|
+
* memory-constrained runtimes.
|
|
109
118
|
*/
|
|
110
119
|
export async function generatePlaceholder(
|
|
111
120
|
buffer: Uint8Array,
|
|
112
121
|
mimeType: string,
|
|
113
122
|
dimensions?: { width: number; height: number },
|
|
114
123
|
): Promise<PlaceholderData | null> {
|
|
115
|
-
const format = SUPPORTED_TYPES[mimeType];
|
|
124
|
+
const format = SUPPORTED_TYPES[normalizeMime(mimeType)];
|
|
116
125
|
if (!format) return null;
|
|
117
126
|
|
|
118
127
|
try {
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
// Trust caller-supplied dimensions when present (the caller has usually
|
|
129
|
+
// already read them via readDimensions); otherwise read them from this
|
|
130
|
+
// buffer. The header is parsed at most once per call.
|
|
131
|
+
const dims = dimensions ?? readDimensions(buffer);
|
|
132
|
+
|
|
133
|
+
// Safety net: the decoders allocate the full RGBA buffer with no internal
|
|
134
|
+
// cap, so refuse to decode unless we can bound the output size. When we
|
|
135
|
+
// have no parseable header AND no known dimensions, the decoded size is
|
|
136
|
+
// unbounded — a crafted/truncated PNG whose header image-size can't read
|
|
137
|
+
// could still be decodable by upng-js, so we must bail to avoid OOM on
|
|
138
|
+
// memory-constrained runtimes. LQIP is progressive enhancement; missing
|
|
139
|
+
// it is preferable to crashing the request.
|
|
140
|
+
if (!dims) return null;
|
|
141
|
+
if (dims.width * dims.height * 4 > MAX_DECODED_BYTES) {
|
|
122
142
|
return null;
|
|
123
143
|
}
|
|
124
144
|
|
package/src/media/types.ts
CHANGED
|
@@ -85,6 +85,10 @@ export interface MediaProviderItem {
|
|
|
85
85
|
/** Dimensions (for images/video) */
|
|
86
86
|
width?: number;
|
|
87
87
|
height?: number;
|
|
88
|
+
/** LQIP blurhash placeholder (images only) */
|
|
89
|
+
blurhash?: string;
|
|
90
|
+
/** LQIP dominant-color placeholder, as a CSS color (images only) */
|
|
91
|
+
dominantColor?: string;
|
|
88
92
|
/** Accessibility text */
|
|
89
93
|
alt?: string;
|
|
90
94
|
/** Preview URL for admin UI thumbnail */
|
|
@@ -126,6 +130,10 @@ export interface ImageEmbed {
|
|
|
126
130
|
sizes?: string;
|
|
127
131
|
width?: number;
|
|
128
132
|
height?: number;
|
|
133
|
+
/** LQIP blurhash placeholder for rendering before the image loads */
|
|
134
|
+
blurhash?: string;
|
|
135
|
+
/** LQIP dominant-color placeholder, as a CSS color */
|
|
136
|
+
dominantColor?: string;
|
|
129
137
|
alt?: string;
|
|
130
138
|
/** Base URL without transforms, for responsive image generation */
|
|
131
139
|
cdnBaseUrl?: string;
|
|
@@ -256,6 +264,10 @@ export interface MediaValue {
|
|
|
256
264
|
mimeType?: string;
|
|
257
265
|
width?: number;
|
|
258
266
|
height?: number;
|
|
267
|
+
/** Cached LQIP blurhash placeholder (images only) */
|
|
268
|
+
blurhash?: string;
|
|
269
|
+
/** Cached LQIP dominant-color placeholder, as a CSS color (images only) */
|
|
270
|
+
dominantColor?: string;
|
|
259
271
|
alt?: string;
|
|
260
272
|
|
|
261
273
|
/** Provider-specific data needed for embedding */
|
|
@@ -273,6 +285,8 @@ export function mediaItemToValue(providerId: string, item: MediaProviderItem): M
|
|
|
273
285
|
mimeType: item.mimeType,
|
|
274
286
|
width: item.width,
|
|
275
287
|
height: item.height,
|
|
288
|
+
blurhash: item.blurhash,
|
|
289
|
+
dominantColor: item.dominantColor,
|
|
276
290
|
alt: item.alt,
|
|
277
291
|
meta: item.meta,
|
|
278
292
|
};
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { normalizeMime } from "../mime.js";
|
|
2
|
+
import type {
|
|
3
|
+
ExtractedMediaUsageOccurrence,
|
|
4
|
+
ExtractMediaUsageOccurrencesInput,
|
|
5
|
+
MediaKind,
|
|
6
|
+
MediaUsageExtractionSubField,
|
|
7
|
+
MediaUsageReferenceType,
|
|
8
|
+
} from "./types.js";
|
|
9
|
+
|
|
10
|
+
const INTERNAL_MEDIA_PREFIX = "/_emdash/api/media/file/";
|
|
11
|
+
const URL_LIKE_RE = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
12
|
+
|
|
13
|
+
interface MediaRef {
|
|
14
|
+
mediaId: string | null;
|
|
15
|
+
provider: string;
|
|
16
|
+
providerAssetId: string;
|
|
17
|
+
mediaKind: MediaKind | null;
|
|
18
|
+
mimeType: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface AddOccurrenceInput {
|
|
22
|
+
fieldSlug: string;
|
|
23
|
+
fieldPath: string;
|
|
24
|
+
referenceType: MediaUsageReferenceType;
|
|
25
|
+
value: unknown;
|
|
26
|
+
fallbackKind: MediaKind | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function extractMediaUsageOccurrences({
|
|
30
|
+
fields,
|
|
31
|
+
data,
|
|
32
|
+
}: ExtractMediaUsageOccurrencesInput): ExtractedMediaUsageOccurrence[] {
|
|
33
|
+
const occurrences: ExtractedMediaUsageOccurrence[] = [];
|
|
34
|
+
const seen = new Set<string>();
|
|
35
|
+
|
|
36
|
+
for (const field of fields) {
|
|
37
|
+
const value = data[field.slug];
|
|
38
|
+
|
|
39
|
+
if (field.type === "image") {
|
|
40
|
+
addOccurrence(occurrences, seen, {
|
|
41
|
+
fieldSlug: field.slug,
|
|
42
|
+
fieldPath: field.slug,
|
|
43
|
+
referenceType: "image_field",
|
|
44
|
+
value,
|
|
45
|
+
fallbackKind: "image",
|
|
46
|
+
});
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (field.type === "file") {
|
|
51
|
+
addOccurrence(occurrences, seen, {
|
|
52
|
+
fieldSlug: field.slug,
|
|
53
|
+
fieldPath: field.slug,
|
|
54
|
+
referenceType: "file_field",
|
|
55
|
+
value,
|
|
56
|
+
fallbackKind: null,
|
|
57
|
+
});
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (field.type === "repeater") {
|
|
62
|
+
extractRepeaterOccurrences(occurrences, seen, field.slug, value, field.validation?.subFields);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (field.type === "portableText") {
|
|
67
|
+
extractPortableTextOccurrences(occurrences, seen, field.slug, value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return occurrences;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function extractRepeaterOccurrences(
|
|
75
|
+
occurrences: ExtractedMediaUsageOccurrence[],
|
|
76
|
+
seen: Set<string>,
|
|
77
|
+
fieldSlug: string,
|
|
78
|
+
value: unknown,
|
|
79
|
+
subFields: readonly MediaUsageExtractionSubField[] | undefined,
|
|
80
|
+
): void {
|
|
81
|
+
if (!Array.isArray(value) || !Array.isArray(subFields)) return;
|
|
82
|
+
|
|
83
|
+
for (const [itemIndex, item] of value.entries()) {
|
|
84
|
+
if (!isRecord(item)) continue;
|
|
85
|
+
|
|
86
|
+
for (const subField of subFields) {
|
|
87
|
+
if (subField.type !== "image" && subField.type !== "file") continue;
|
|
88
|
+
|
|
89
|
+
addOccurrence(occurrences, seen, {
|
|
90
|
+
fieldSlug,
|
|
91
|
+
fieldPath: `${fieldSlug}[${itemIndex}].${subField.slug}`,
|
|
92
|
+
referenceType: subField.type === "image" ? "image_field" : "file_field",
|
|
93
|
+
value: item[subField.slug],
|
|
94
|
+
fallbackKind: subField.type === "image" ? "image" : null,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function extractPortableTextOccurrences(
|
|
101
|
+
occurrences: ExtractedMediaUsageOccurrence[],
|
|
102
|
+
seen: Set<string>,
|
|
103
|
+
fieldSlug: string,
|
|
104
|
+
value: unknown,
|
|
105
|
+
): void {
|
|
106
|
+
if (!Array.isArray(value)) return;
|
|
107
|
+
|
|
108
|
+
for (const [blockIndex, block] of value.entries()) {
|
|
109
|
+
if (!isRecord(block) || block._type !== "image" || !isRecord(block.asset)) continue;
|
|
110
|
+
|
|
111
|
+
const provider = normalizeProvider(block.asset.provider);
|
|
112
|
+
const ref = readPortableTextAssetRef(block.asset, provider);
|
|
113
|
+
if (!ref) continue;
|
|
114
|
+
|
|
115
|
+
addRefOccurrence(occurrences, seen, {
|
|
116
|
+
fieldSlug,
|
|
117
|
+
fieldPath: `${fieldSlug}[${blockIndex}].asset.${ref.key}`,
|
|
118
|
+
referenceType: "portable_text_image",
|
|
119
|
+
ref: buildMediaRef({
|
|
120
|
+
id: ref.id,
|
|
121
|
+
provider,
|
|
122
|
+
mimeType: normalizeMimeValue(block.asset.mimeType),
|
|
123
|
+
fallbackKind: "image",
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function addOccurrence(
|
|
130
|
+
occurrences: ExtractedMediaUsageOccurrence[],
|
|
131
|
+
seen: Set<string>,
|
|
132
|
+
input: AddOccurrenceInput,
|
|
133
|
+
): void {
|
|
134
|
+
const ref = readMediaRef(input.value, input.fallbackKind);
|
|
135
|
+
if (!ref) return;
|
|
136
|
+
|
|
137
|
+
addRefOccurrence(occurrences, seen, {
|
|
138
|
+
fieldSlug: input.fieldSlug,
|
|
139
|
+
fieldPath: input.fieldPath,
|
|
140
|
+
referenceType: input.referenceType,
|
|
141
|
+
ref,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function addRefOccurrence(
|
|
146
|
+
occurrences: ExtractedMediaUsageOccurrence[],
|
|
147
|
+
seen: Set<string>,
|
|
148
|
+
input: {
|
|
149
|
+
fieldSlug: string;
|
|
150
|
+
fieldPath: string;
|
|
151
|
+
referenceType: MediaUsageReferenceType;
|
|
152
|
+
ref: MediaRef | null;
|
|
153
|
+
},
|
|
154
|
+
): void {
|
|
155
|
+
if (!input.ref) return;
|
|
156
|
+
|
|
157
|
+
const occurrence: ExtractedMediaUsageOccurrence = {
|
|
158
|
+
fieldSlug: input.fieldSlug,
|
|
159
|
+
fieldPath: input.fieldPath,
|
|
160
|
+
occurrenceIndex: 0,
|
|
161
|
+
referenceType: input.referenceType,
|
|
162
|
+
mediaId: input.ref.mediaId,
|
|
163
|
+
provider: input.ref.provider,
|
|
164
|
+
providerAssetId: input.ref.providerAssetId,
|
|
165
|
+
mediaKind: input.ref.mediaKind,
|
|
166
|
+
mimeType: input.ref.mimeType,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const key = [
|
|
170
|
+
occurrence.fieldSlug,
|
|
171
|
+
occurrence.fieldPath,
|
|
172
|
+
occurrence.occurrenceIndex,
|
|
173
|
+
occurrence.referenceType,
|
|
174
|
+
occurrence.provider,
|
|
175
|
+
occurrence.providerAssetId,
|
|
176
|
+
occurrence.mediaId ?? "",
|
|
177
|
+
].join("\0");
|
|
178
|
+
|
|
179
|
+
if (seen.has(key)) return;
|
|
180
|
+
seen.add(key);
|
|
181
|
+
occurrences.push(occurrence);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function readMediaRef(value: unknown, fallbackKind: MediaKind | null): MediaRef | null {
|
|
185
|
+
if (typeof value === "string") {
|
|
186
|
+
const id = normalizeLocalMediaId(value);
|
|
187
|
+
return id ? buildMediaRef({ id, provider: "local", mimeType: null, fallbackKind }) : null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!isRecord(value)) return null;
|
|
191
|
+
|
|
192
|
+
const provider = normalizeProvider(value.provider);
|
|
193
|
+
const id = provider === "local" ? normalizeLocalMediaId(value.id) : normalizeStableId(value.id);
|
|
194
|
+
if (!id) return null;
|
|
195
|
+
|
|
196
|
+
return buildMediaRef({
|
|
197
|
+
id,
|
|
198
|
+
provider,
|
|
199
|
+
mimeType: normalizeMimeValue(value.mimeType),
|
|
200
|
+
fallbackKind,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function buildMediaRef(input: {
|
|
205
|
+
id: string;
|
|
206
|
+
provider: string;
|
|
207
|
+
mimeType: string | null;
|
|
208
|
+
fallbackKind: MediaKind | null;
|
|
209
|
+
}): MediaRef | null {
|
|
210
|
+
const provider = normalizeProvider(input.provider);
|
|
211
|
+
if (provider === "external") return null;
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
mediaId: provider === "local" ? input.id : null,
|
|
215
|
+
provider,
|
|
216
|
+
providerAssetId: input.id,
|
|
217
|
+
mediaKind: mediaKindFromMime(input.mimeType) ?? input.fallbackKind,
|
|
218
|
+
mimeType: input.mimeType,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function readPortableTextAssetRef(
|
|
223
|
+
asset: Record<string, unknown>,
|
|
224
|
+
provider: string,
|
|
225
|
+
): { key: "_ref" | "id"; id: string } | null {
|
|
226
|
+
const normalizeId = provider === "local" ? normalizeLocalMediaId : normalizeStableId;
|
|
227
|
+
const ref = normalizeId(asset._ref);
|
|
228
|
+
if (ref) return { key: "_ref", id: ref };
|
|
229
|
+
|
|
230
|
+
const id = normalizeId(asset.id);
|
|
231
|
+
if (id) return { key: "id", id };
|
|
232
|
+
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function normalizeProvider(value: unknown): string {
|
|
237
|
+
const provider = readString(value)?.trim();
|
|
238
|
+
return provider || "local";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function normalizeLocalMediaId(value: unknown): string | null {
|
|
242
|
+
const id = normalizeStableId(value);
|
|
243
|
+
if (!id) return null;
|
|
244
|
+
return id.includes("/") ? null : id;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function normalizeStableId(value: unknown): string | null {
|
|
248
|
+
if (typeof value !== "string") return null;
|
|
249
|
+
const trimmed = value.trim();
|
|
250
|
+
if (!trimmed) return null;
|
|
251
|
+
if (URL_LIKE_RE.test(trimmed)) return null;
|
|
252
|
+
if (trimmed.startsWith(INTERNAL_MEDIA_PREFIX)) return null;
|
|
253
|
+
return trimmed;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function normalizeMimeValue(value: unknown): string | null {
|
|
257
|
+
if (typeof value !== "string") return null;
|
|
258
|
+
const normalized = normalizeMime(value);
|
|
259
|
+
return normalized.includes("/") ? normalized : null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function mediaKindFromMime(mimeType: string | null): MediaKind | null {
|
|
263
|
+
if (!mimeType) return null;
|
|
264
|
+
if (mimeType.startsWith("image/")) return "image";
|
|
265
|
+
if (mimeType.startsWith("video/")) return "video";
|
|
266
|
+
if (mimeType.startsWith("audio/")) return "audio";
|
|
267
|
+
if (mimeType.startsWith("font/") || mimeType.startsWith("application/font-")) return "font";
|
|
268
|
+
if (mimeType.startsWith("text/")) return "text";
|
|
269
|
+
if (isDocumentMime(mimeType)) return "document";
|
|
270
|
+
if (isArchiveMime(mimeType)) return "archive";
|
|
271
|
+
return "other";
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function isDocumentMime(mimeType: string): boolean {
|
|
275
|
+
return (
|
|
276
|
+
mimeType === "application/pdf" ||
|
|
277
|
+
mimeType === "application/msword" ||
|
|
278
|
+
mimeType === "application/rtf" ||
|
|
279
|
+
mimeType === "application/vnd.ms-excel" ||
|
|
280
|
+
mimeType === "application/vnd.ms-powerpoint" ||
|
|
281
|
+
mimeType.startsWith("application/vnd.openxmlformats-officedocument.")
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function isArchiveMime(mimeType: string): boolean {
|
|
286
|
+
return (
|
|
287
|
+
mimeType === "application/zip" ||
|
|
288
|
+
mimeType === "application/gzip" ||
|
|
289
|
+
mimeType === "application/x-tar" ||
|
|
290
|
+
mimeType === "application/x-7z-compressed" ||
|
|
291
|
+
mimeType === "application/x-rar-compressed" ||
|
|
292
|
+
mimeType === "application/vnd.rar"
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function readString(value: unknown): string | null {
|
|
297
|
+
return typeof value === "string" ? value : null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
301
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
302
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FieldType } from "../../schema/types.js";
|
|
2
|
+
|
|
3
|
+
export type MediaKind =
|
|
4
|
+
| "image"
|
|
5
|
+
| "video"
|
|
6
|
+
| "audio"
|
|
7
|
+
| "document"
|
|
8
|
+
| "archive"
|
|
9
|
+
| "font"
|
|
10
|
+
| "text"
|
|
11
|
+
| "other";
|
|
12
|
+
|
|
13
|
+
export type MediaUsageReferenceType = "image_field" | "file_field" | "portable_text_image";
|
|
14
|
+
|
|
15
|
+
export interface MediaUsageExtractionSubField {
|
|
16
|
+
slug: string;
|
|
17
|
+
type: FieldType;
|
|
18
|
+
label?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MediaUsageExtractionValidation {
|
|
22
|
+
subFields?: readonly MediaUsageExtractionSubField[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface MediaUsageExtractionField {
|
|
26
|
+
slug: string;
|
|
27
|
+
type: FieldType;
|
|
28
|
+
validation?: MediaUsageExtractionValidation | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ExtractMediaUsageOccurrencesInput {
|
|
32
|
+
fields: readonly MediaUsageExtractionField[];
|
|
33
|
+
data: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ExtractedMediaUsageOccurrence {
|
|
37
|
+
fieldSlug: string;
|
|
38
|
+
fieldPath: string;
|
|
39
|
+
occurrenceIndex: number;
|
|
40
|
+
referenceType: MediaUsageReferenceType;
|
|
41
|
+
mediaId: string | null;
|
|
42
|
+
provider: string;
|
|
43
|
+
providerAssetId: string;
|
|
44
|
+
mediaKind: MediaKind | null;
|
|
45
|
+
mimeType: string | null;
|
|
46
|
+
}
|
|
@@ -167,7 +167,7 @@ async function getBackend(): Promise<ObjectCacheBackend | null> {
|
|
|
167
167
|
} catch (error) {
|
|
168
168
|
// Importing the virtual module fails outside an Astro/Vite context
|
|
169
169
|
// (e.g. unit tests, CLI). Treat as "no cache configured".
|
|
170
|
-
if (import.meta.env
|
|
170
|
+
if (import.meta.env?.DEV) {
|
|
171
171
|
console.warn("[object-cache] backend unavailable:", error);
|
|
172
172
|
}
|
|
173
173
|
holder.backend = null;
|
|
@@ -378,7 +378,7 @@ export async function cachedQuery<T>(options: CachedQueryOptions<T>): Promise<T>
|
|
|
378
378
|
const encoded = encode({ e: currentEpochs, v: value } satisfies CacheEnvelope<T>);
|
|
379
379
|
await backend.set(fullKey, encoded, ttl);
|
|
380
380
|
} catch (error) {
|
|
381
|
-
if (import.meta.env
|
|
381
|
+
if (import.meta.env?.DEV) {
|
|
382
382
|
console.warn("[object-cache] set failed:", error);
|
|
383
383
|
}
|
|
384
384
|
}
|
|
@@ -388,6 +388,12 @@ export async function cachedQuery<T>(options: CachedQueryOptions<T>): Promise<T>
|
|
|
388
388
|
return value;
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/** Whether object-cache reads are active for the current request. */
|
|
392
|
+
export async function isObjectCacheActive(): Promise<boolean> {
|
|
393
|
+
const backend = await getBackend();
|
|
394
|
+
return backend !== null && !shouldBypass();
|
|
395
|
+
}
|
|
396
|
+
|
|
391
397
|
/**
|
|
392
398
|
* Invalidate every cached value in `namespace` by bumping its epoch.
|
|
393
399
|
*
|
package/src/page/metadata.ts
CHANGED
|
@@ -20,6 +20,10 @@ export interface ResolvedPageMetadata {
|
|
|
20
20
|
jsonld: Array<{ id?: string; json: string }>;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
interface RenderPageMetadataOptions {
|
|
24
|
+
includeJsonLd?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
// ── Validation ──────────────────────────────────────────────────
|
|
24
28
|
|
|
25
29
|
/** Schemes safe for use in link href attributes */
|
|
@@ -66,6 +70,16 @@ export function safeJsonLdSerialize(value: unknown): string {
|
|
|
66
70
|
.replace(JSONLD_U2029_RE, "\\u2029");
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
export async function createSha256CspHash(value: string): Promise<string> {
|
|
74
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
|
|
75
|
+
const bytes = new Uint8Array(digest);
|
|
76
|
+
let binary = "";
|
|
77
|
+
for (const byte of bytes) {
|
|
78
|
+
binary += String.fromCharCode(byte);
|
|
79
|
+
}
|
|
80
|
+
return `sha256-${btoa(binary)}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
69
83
|
// ── Merge / dedupe ──────────────────────────────────────────────
|
|
70
84
|
|
|
71
85
|
/**
|
|
@@ -155,8 +169,12 @@ export function resolvePageMetadata(
|
|
|
155
169
|
// ── HTML rendering ──────────────────────────────────────────────
|
|
156
170
|
|
|
157
171
|
/** Render resolved metadata to an HTML string for embedding in <head> */
|
|
158
|
-
export function renderPageMetadata(
|
|
172
|
+
export function renderPageMetadata(
|
|
173
|
+
metadata: ResolvedPageMetadata,
|
|
174
|
+
options: RenderPageMetadataOptions = {},
|
|
175
|
+
): string {
|
|
159
176
|
const parts: string[] = [];
|
|
177
|
+
const includeJsonLd = options.includeJsonLd ?? true;
|
|
160
178
|
|
|
161
179
|
for (const m of metadata.meta) {
|
|
162
180
|
parts.push(`<meta name="${escapeHtmlAttr(m.name)}" content="${escapeHtmlAttr(m.content)}">`);
|
|
@@ -177,8 +195,10 @@ export function renderPageMetadata(metadata: ResolvedPageMetadata): string {
|
|
|
177
195
|
parts.push(tag);
|
|
178
196
|
}
|
|
179
197
|
|
|
180
|
-
|
|
181
|
-
|
|
198
|
+
if (includeJsonLd) {
|
|
199
|
+
for (const j of metadata.jsonld) {
|
|
200
|
+
parts.push(`<script type="application/ld+json">${j.json}</script>`);
|
|
201
|
+
}
|
|
182
202
|
}
|
|
183
203
|
|
|
184
204
|
return parts.join("\n");
|
package/src/plugin-types.ts
CHANGED
|
@@ -49,13 +49,19 @@ import type {
|
|
|
49
49
|
CommentModerateHandler,
|
|
50
50
|
ContentAfterDeleteHandler,
|
|
51
51
|
ContentAfterPublishHandler,
|
|
52
|
+
ContentAfterRestoreHandler,
|
|
52
53
|
ContentAfterSaveHandler,
|
|
54
|
+
ContentAfterScheduleHandler,
|
|
53
55
|
ContentAfterUnpublishHandler,
|
|
56
|
+
ContentAfterUnscheduleHandler,
|
|
54
57
|
ContentBeforeDeleteHandler,
|
|
55
58
|
ContentBeforeSaveHandler,
|
|
56
59
|
ContentDeleteEvent,
|
|
57
60
|
ContentHookEvent,
|
|
58
61
|
ContentPublishStateChangeEvent,
|
|
62
|
+
ContentRestoreStateChangeEvent,
|
|
63
|
+
ContentScheduleStateChangeEvent,
|
|
64
|
+
ContentStateChangeEvent,
|
|
59
65
|
CronEvent,
|
|
60
66
|
CronHandler,
|
|
61
67
|
EmailAfterSendEvent,
|
|
@@ -97,6 +103,9 @@ export interface HookHandlers {
|
|
|
97
103
|
"content:afterDelete": ContentAfterDeleteHandler;
|
|
98
104
|
"content:afterPublish": ContentAfterPublishHandler;
|
|
99
105
|
"content:afterUnpublish": ContentAfterUnpublishHandler;
|
|
106
|
+
"content:afterRestore": ContentAfterRestoreHandler;
|
|
107
|
+
"content:afterSchedule": ContentAfterScheduleHandler;
|
|
108
|
+
"content:afterUnschedule": ContentAfterUnscheduleHandler;
|
|
100
109
|
"media:beforeUpload": MediaBeforeUploadHandler;
|
|
101
110
|
"media:afterUpload": MediaAfterUploadHandler;
|
|
102
111
|
cron: CronHandler;
|
|
@@ -226,6 +235,9 @@ export type {
|
|
|
226
235
|
ContentDeleteEvent,
|
|
227
236
|
ContentHookEvent,
|
|
228
237
|
ContentPublishStateChangeEvent,
|
|
238
|
+
ContentRestoreStateChangeEvent,
|
|
239
|
+
ContentScheduleStateChangeEvent,
|
|
240
|
+
ContentStateChangeEvent,
|
|
229
241
|
CronEvent,
|
|
230
242
|
EmailAfterSendEvent,
|
|
231
243
|
EmailBeforeSendEvent,
|
package/src/plugins/context.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
SsrfError,
|
|
22
22
|
stripCredentialHeaders,
|
|
23
23
|
} from "../import/ssrf.js";
|
|
24
|
+
import { enrichImageMetadata } from "../media/enrich.js";
|
|
24
25
|
import { invalidateSiteSettingsCache } from "../settings/index.js";
|
|
25
26
|
import type { Storage } from "../storage/types.js";
|
|
26
27
|
import { CronAccessImpl } from "./cron.js";
|
|
@@ -508,6 +509,9 @@ export function createMediaAccessWithWrite(
|
|
|
508
509
|
contentType,
|
|
509
510
|
});
|
|
510
511
|
|
|
512
|
+
// Derive dimensions + LQIP placeholders (no-op for non-images).
|
|
513
|
+
const enriched = await enrichImageMetadata(new Uint8Array(bytes), contentType);
|
|
514
|
+
|
|
511
515
|
// Create DB record — clean up storage on failure
|
|
512
516
|
let media;
|
|
513
517
|
try {
|
|
@@ -517,6 +521,10 @@ export function createMediaAccessWithWrite(
|
|
|
517
521
|
size: bytes.byteLength,
|
|
518
522
|
storageKey,
|
|
519
523
|
status: "ready",
|
|
524
|
+
width: enriched.width,
|
|
525
|
+
height: enriched.height,
|
|
526
|
+
blurhash: enriched.blurhash,
|
|
527
|
+
dominantColor: enriched.dominantColor,
|
|
520
528
|
});
|
|
521
529
|
} catch (error) {
|
|
522
530
|
try {
|
|
@@ -22,11 +22,6 @@ import type {
|
|
|
22
22
|
PluginStorageConfig,
|
|
23
23
|
} from "./types.js";
|
|
24
24
|
|
|
25
|
-
// Plugin ID validation patterns
|
|
26
|
-
const SIMPLE_ID = /^[a-z0-9-]+$/;
|
|
27
|
-
const SCOPED_ID = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
|
|
28
|
-
const SEMVER_PATTERN = /^\d+\.\d+\.\d+/;
|
|
29
|
-
|
|
30
25
|
/**
|
|
31
26
|
* Define a native EmDash plugin.
|
|
32
27
|
*
|
|
@@ -94,6 +89,20 @@ export function definePlugin<TStorage extends PluginStorageConfig>(
|
|
|
94
89
|
function defineNativePlugin<TStorage extends PluginStorageConfig>(
|
|
95
90
|
definition: PluginDefinition<TStorage>,
|
|
96
91
|
): ResolvedPlugin<TStorage> {
|
|
92
|
+
// Declared function-local (not module scope) on purpose. Under
|
|
93
|
+
// `ssr.noExternal` the worker entry can instantiate native plugins during a
|
|
94
|
+
// circular module init, reaching this function before module-scope consts
|
|
95
|
+
// initialize -> "Cannot access 'SIMPLE_ID' before initialization" -> every
|
|
96
|
+
// route 500s on Cloudflare Workers. Call-time consts evaluate after the
|
|
97
|
+
// literals are parsed, so the temporal dead zone cannot occur regardless of
|
|
98
|
+
// bundle ordering. See #1370.
|
|
99
|
+
// oxlint-disable-next-line e18e/prefer-static-regex -- call-time on purpose (see #1370)
|
|
100
|
+
const SIMPLE_ID = /^[a-z0-9-]+$/;
|
|
101
|
+
// oxlint-disable-next-line e18e/prefer-static-regex -- call-time on purpose (see #1370)
|
|
102
|
+
const SCOPED_ID = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
|
|
103
|
+
// oxlint-disable-next-line e18e/prefer-static-regex -- call-time on purpose (see #1370)
|
|
104
|
+
const SEMVER_PATTERN = /^\d+\.\d+\.\d+/;
|
|
105
|
+
|
|
97
106
|
const {
|
|
98
107
|
id,
|
|
99
108
|
version,
|