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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-CeglrBT9.mjs","names":[],"sources":["../src/media/normalize.ts"],"sourcesContent":["/**\n * Media Value Normalization\n *\n * Normalizes media field values into a consistent shape regardless of\n * creation path (seed scripts, media picker, WP import, URL input).\n *\n * Called at content create/update time when a media provider is available,\n * filling in missing dimensions, storageKey, mimeType, and filename from\n * the provider's `get()` method.\n */\n\nimport type { MediaProvider, MediaProviderItem, MediaValue } from \"./types.js\";\n\nexport const INTERNAL_MEDIA_PREFIX = \"/_emdash/api/media/file/\";\nconst URL_PATTERN = /^https?:\\/\\//;\n\n/**\n * Normalize a media field value into a consistent MediaValue shape.\n *\n * - `null`/`undefined` → `null`\n * - Bare URL string → `{ provider: \"external\", id: \"\", src: url }`\n * - Bare internal media URL → resolved via local provider's `get()`\n * - Bare local media ID → resolved via local provider's `get()`\n * - Object with `provider` + `id` → enriched with missing fields from provider\n */\nexport async function normalizeMediaValue(\n\tvalue: unknown,\n\tgetProvider: (id: string) => MediaProvider | undefined,\n): Promise<MediaValue | null> {\n\tif (value == null) return null;\n\n\t// Bare string URL\n\tif (typeof value === \"string\") {\n\t\treturn normalizeStringUrl(value, getProvider);\n\t}\n\n\t// Not an object — can't normalize\n\tif (!isRecord(value)) return null;\n\n\t// Must have at least an id to be a valid media value\n\tif (!(\"id\" in value) && !(\"src\" in value)) return null;\n\n\tconst provider = (typeof value.provider === \"string\" ? value.provider : undefined) || \"local\";\n\tconst id = typeof value.id === \"string\" ? value.id : \"\";\n\n\t// External URLs — return as-is, no server-side dimension detection\n\tif (provider === \"external\") {\n\t\treturn recordToMediaValue(value);\n\t}\n\n\t// Build the base value from the input\n\tconst result: MediaValue = { ...recordToMediaValue(value), provider };\n\n\t// For local media, strip `src` — it's derived at display time from storageKey\n\tif (provider === \"local\") {\n\t\tdelete result.src;\n\t}\n\n\t// Determine if we need to call the provider\n\tconst needsDimensions = result.width == null || result.height == null;\n\tconst needsStorageKey = provider === \"local\" && !result.meta?.storageKey;\n\tconst needsFileInfo = !result.mimeType || !result.filename;\n\t// LQIP placeholders are immutable facts of the bytes: if an image record is\n\t// missing them, the provider may have gained them since (e.g. content saved\n\t// before LQIP backfill ran, or the row's blurhash was populated later). Pull\n\t// them on every image lookup so the LQIP backfill in mergeProviderData runs.\n\tconst needsLqip =\n\t\t(result.mimeType ?? \"\").startsWith(\"image/\") &&\n\t\t(result.blurhash == null || result.dominantColor == null);\n\tconst needsLookup = needsDimensions || needsStorageKey || needsFileInfo || needsLqip;\n\n\tif (!needsLookup || !id) return result;\n\n\t// Try to enrich from provider\n\tconst mediaProvider = getProvider(provider);\n\tif (!mediaProvider?.get) return result;\n\n\tlet providerItem: MediaProviderItem | null;\n\ttry {\n\t\tproviderItem = await mediaProvider.get(id);\n\t} catch {\n\t\treturn result;\n\t}\n\n\tif (!providerItem) return result;\n\n\treturn mergeProviderData(result, providerItem);\n}\n\nasync function normalizeStringUrl(\n\turl: string,\n\tgetProvider: (id: string) => MediaProvider | undefined,\n): Promise<MediaValue | null> {\n\t// Internal media URL — try to resolve via local provider\n\tif (url.startsWith(INTERNAL_MEDIA_PREFIX)) {\n\t\treturn resolveInternalUrl(url, getProvider);\n\t}\n\n\t// External HTTP(S) URL\n\tif (URL_PATTERN.test(url)) {\n\t\treturn Promise.resolve({\n\t\t\tprovider: \"external\",\n\t\t\tid: \"\",\n\t\t\tsrc: url,\n\t\t});\n\t}\n\n\tconst localMedia = await resolveLocalId(url, getProvider);\n\tif (localMedia) return localMedia;\n\n\t// Unrecognized string — preserve legacy behavior and treat as external\n\treturn {\n\t\tprovider: \"external\",\n\t\tid: \"\",\n\t\tsrc: url,\n\t};\n}\n\nasync function resolveInternalUrl(\n\turl: string,\n\tgetProvider: (id: string) => MediaProvider | undefined,\n): Promise<MediaValue> {\n\tconst storageKey = url.slice(INTERNAL_MEDIA_PREFIX.length);\n\tconst localProvider = getProvider(\"local\");\n\n\tif (!localProvider?.get) {\n\t\treturn { provider: \"external\", id: \"\", src: url };\n\t}\n\n\tlet item: MediaProviderItem | null;\n\ttry {\n\t\titem = await localProvider.get(storageKey);\n\t} catch {\n\t\treturn { provider: \"external\", id: \"\", src: url };\n\t}\n\n\tif (!item) {\n\t\treturn { provider: \"external\", id: \"\", src: url };\n\t}\n\n\treturn {\n\t\tprovider: \"local\",\n\t\tid: item.id,\n\t\tfilename: item.filename,\n\t\tmimeType: item.mimeType,\n\t\twidth: item.width,\n\t\theight: item.height,\n\t\tblurhash: item.blurhash,\n\t\tdominantColor: item.dominantColor,\n\t\talt: item.alt,\n\t\tmeta: item.meta,\n\t};\n}\n\nasync function resolveLocalId(\n\tid: string,\n\tgetProvider: (id: string) => MediaProvider | undefined,\n): Promise<MediaValue | null> {\n\tconst localProvider = getProvider(\"local\");\n\n\tif (!localProvider?.get) return null;\n\n\tlet item: MediaProviderItem | null;\n\ttry {\n\t\titem = await localProvider.get(id);\n\t} catch {\n\t\treturn null;\n\t}\n\n\tif (!item) return null;\n\n\treturn {\n\t\tprovider: \"local\",\n\t\tid: item.id,\n\t\tfilename: item.filename,\n\t\tmimeType: item.mimeType,\n\t\twidth: item.width,\n\t\theight: item.height,\n\t\tblurhash: item.blurhash,\n\t\tdominantColor: item.dominantColor,\n\t\talt: item.alt,\n\t\tmeta: item.meta,\n\t};\n}\n\n/**\n * Merge provider data into an existing MediaValue, preserving caller-supplied fields.\n * Caller `alt` takes priority over provider `alt` (per-usage, not per-image).\n */\nfunction mergeProviderData(existing: MediaValue, item: MediaProviderItem): MediaValue {\n\tconst result = { ...existing };\n\n\t// Fill missing dimensions\n\tif (result.width == null && item.width != null) result.width = item.width;\n\tif (result.height == null && item.height != null) result.height = item.height;\n\n\t// Fill missing LQIP placeholders (immutable facts of the bytes; caller wins)\n\tif (result.blurhash == null && item.blurhash != null) result.blurhash = item.blurhash;\n\tif (result.dominantColor == null && item.dominantColor != null) {\n\t\tresult.dominantColor = item.dominantColor;\n\t}\n\n\t// Fill missing file info\n\tif (!result.filename && item.filename) result.filename = item.filename;\n\tif (!result.mimeType && item.mimeType) result.mimeType = item.mimeType;\n\n\t// Fill missing alt (provider alt is fallback, not override)\n\tif (!result.alt && item.alt) result.alt = item.alt;\n\n\t// Fill missing meta (merge, don't replace)\n\tif (item.meta) {\n\t\tresult.meta = { ...item.meta, ...result.meta };\n\t}\n\n\treturn result;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Extract known MediaValue fields from a runtime-checked record.\n * Avoids unsafe `as MediaValue` cast by reading each property explicitly.\n */\nfunction recordToMediaValue(obj: Record<string, unknown>): MediaValue {\n\tconst result: MediaValue = {\n\t\tid: typeof obj.id === \"string\" ? obj.id : \"\",\n\t};\n\tif (typeof obj.provider === \"string\") result.provider = obj.provider;\n\tif (typeof obj.src === \"string\") result.src = obj.src;\n\tif (typeof obj.previewUrl === \"string\") result.previewUrl = obj.previewUrl;\n\tif (typeof obj.filename === \"string\") result.filename = obj.filename;\n\tif (typeof obj.mimeType === \"string\") result.mimeType = obj.mimeType;\n\tif (typeof obj.width === \"number\") result.width = obj.width;\n\tif (typeof obj.height === \"number\") result.height = obj.height;\n\tif (typeof obj.blurhash === \"string\") result.blurhash = obj.blurhash;\n\tif (typeof obj.dominantColor === \"string\") result.dominantColor = obj.dominantColor;\n\tif (typeof obj.alt === \"string\") result.alt = obj.alt;\n\tif (isRecord(obj.meta)) result.meta = obj.meta;\n\treturn result;\n}\n"],"mappings":"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"}
|
|
@@ -159,7 +159,7 @@ async function getBackend() {
|
|
|
159
159
|
};
|
|
160
160
|
holder.backend = typeof mod.createObjectCache === "function" ? mod.createObjectCache(config) : null;
|
|
161
161
|
} catch (error) {
|
|
162
|
-
if (import.meta.env
|
|
162
|
+
if (import.meta.env?.DEV) console.warn("[object-cache] backend unavailable:", error);
|
|
163
163
|
holder.backend = null;
|
|
164
164
|
}
|
|
165
165
|
holder.initialized = true;
|
|
@@ -275,12 +275,16 @@ async function cachedQuery(options) {
|
|
|
275
275
|
});
|
|
276
276
|
await backend.set(fullKey, encoded, ttl);
|
|
277
277
|
} catch (error) {
|
|
278
|
-
if (import.meta.env
|
|
278
|
+
if (import.meta.env?.DEV) console.warn("[object-cache] set failed:", error);
|
|
279
279
|
}
|
|
280
280
|
});
|
|
281
281
|
}
|
|
282
282
|
return value;
|
|
283
283
|
}
|
|
284
|
+
/** Whether object-cache reads are active for the current request. */
|
|
285
|
+
async function isObjectCacheActive() {
|
|
286
|
+
return await getBackend() !== null && !shouldBypass();
|
|
287
|
+
}
|
|
284
288
|
/**
|
|
285
289
|
* Invalidate every cached value in `namespace` by bumping its epoch.
|
|
286
290
|
*
|
|
@@ -369,5 +373,5 @@ function invalidateCommentObjectCache() {
|
|
|
369
373
|
}
|
|
370
374
|
|
|
371
375
|
//#endregion
|
|
372
|
-
export { invalidateBylineObjectCache as a, invalidateMenuObjectCache as c, invalidateTaxonomyObjectCache as d, contentNamespaces as i, invalidateObjectCache as l, cachedQuery as n, invalidateCollectionCache as o, contentNamespace as r, invalidateCommentObjectCache as s, CacheNamespace as t, invalidateSchemaObjectCache as u };
|
|
373
|
-
//# sourceMappingURL=object-cache-
|
|
376
|
+
export { invalidateBylineObjectCache as a, invalidateMenuObjectCache as c, invalidateTaxonomyObjectCache as d, isObjectCacheActive as f, contentNamespaces as i, invalidateObjectCache as l, cachedQuery as n, invalidateCollectionCache as o, contentNamespace as r, invalidateCommentObjectCache as s, CacheNamespace as t, invalidateSchemaObjectCache as u };
|
|
377
|
+
//# sourceMappingURL=object-cache-CHbHv83-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object-cache-CHbHv83-.mjs","names":[],"sources":["../src/object-cache/codec.ts","../src/object-cache/index.ts"],"sourcesContent":["/**\n * Object-cache serialization codec.\n *\n * Cached values are JSON, with one extension: `Date` instances are preserved\n * across the round-trip. EmDash content entries carry `Date` objects for the\n * system timestamp columns (`createdAt`, `updatedAt`, `publishedAt`,\n * `scheduledAt`) and on `cacheHint.lastModified`; plain `JSON.stringify` would\n * silently flatten those to ISO strings, so a value read from cache would no\n * longer be `=== instanceof Date` and downstream `value instanceof Date`\n * branches (cursor encoding, scheduled-visibility checks) would diverge from a\n * fresh database read.\n *\n * Functions and symbol-keyed properties are NOT preserved — callers that cache\n * values carrying either (e.g. content entries with their `.edit` proxy and\n * the non-enumerable `CURSOR_RAW_VALUES` symbol) must reduce to a serializable\n * snapshot before caching and rebuild the non-serializable parts on read. See\n * `query.ts` content snapshot helpers.\n */\n\n/** Tag used to mark a serialized `Date`. Deliberately unlikely to collide. */\nconst DATE_TAG = \"$$emdashDate\";\n\ninterface TaggedDate {\n\t[DATE_TAG]: string;\n}\n\nfunction isTaggedDate(value: unknown): value is TaggedDate {\n\tif (typeof value !== \"object\" || value === null) return false;\n\t// encode() always emits the tag as the object's *only* key, so requiring\n\t// exactly one key keeps a user object that merely happens to carry a\n\t// `$$emdashDate` string alongside other fields from being collapsed to a\n\t// Date (which would silently drop those other fields).\n\tconst keys = Object.keys(value);\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowing a JSON-parsed value to read the date tag\n\treturn keys.length === 1 && typeof (value as Record<string, unknown>)[DATE_TAG] === \"string\";\n}\n\n/**\n * Serialize a value to a cache string, preserving `Date` instances.\n *\n * Uses the JSON replacer's `this` binding to inspect the *original* property\n * value: `JSON.stringify` invokes `Date.prototype.toJSON` before the replacer\n * sees it, so by the time `value` arrives it is already an ISO string. Reading\n * `this[key]` recovers the live `Date` so we can tag it.\n */\nexport function encode(value: unknown): string {\n\treturn JSON.stringify(value, function (this: Record<string, unknown>, key, val) {\n\t\tconst original = this[key];\n\t\tif (original instanceof Date) {\n\t\t\treturn { [DATE_TAG]: original.toISOString() } satisfies TaggedDate;\n\t\t}\n\t\treturn val;\n\t});\n}\n\n/**\n * Parse a cache string produced by {@link encode}, rehydrating tagged `Date`s.\n *\n * Returns `undefined` if the input is not valid JSON (treated as a cache miss\n * by the read-through layer rather than throwing).\n */\nexport function decode(raw: string): unknown {\n\ttry {\n\t\treturn JSON.parse(raw, (_key, value) => {\n\t\t\tif (isTaggedDate(value)) {\n\t\t\t\treturn new Date(value[DATE_TAG]);\n\t\t\t}\n\t\t\treturn value;\n\t\t});\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","/**\n * Object cache — distributed read-through query cache.\n *\n * Layering (per query):\n *\n * requestCached → in-request dedupe (per render, WeakMap on ALS context)\n * cachedQuery → THIS layer: distributed L2 (KV / memory), epoch-keyed\n * database → source of truth\n *\n * Optional and off by default: when no `objectCache` descriptor is configured,\n * `virtual:emdash/object-cache` exports `createObjectCache = undefined`,\n * {@link getBackend} resolves to `null`, and {@link cachedQuery} is a\n * transparent passthrough to its `load` function. Configure with\n * `memoryCache()` (Node) or `kvCache()` from `@emdash-cms/cloudflare`.\n *\n * Invalidation is epoch-based: each cache key embeds a per-namespace epoch\n * (\"last changed\" marker) read from the backend. A write calls\n * {@link invalidateObjectCache}, which stamps the namespace epoch to\n * `Date.now()`; every previously-stored key for that namespace is instantly\n * orphaned and reclaimed by its TTL. This is O(1) and needs no key\n * enumeration (KV has no prefix delete).\n *\n * The singleton backend/config and the per-isolate epoch cache live on\n * `globalThis` behind `Symbol.for` keys so Vite SSR chunk duplication can't\n * fork them (same pattern as `request-context.ts`).\n */\n\nimport { after } from \"../after.js\";\nimport { getRequestContext } from \"../request-context.js\";\nimport { decode, encode } from \"./codec.js\";\nimport type {\n\tCreateObjectCacheBackendFn,\n\tObjectCacheBackend,\n\tObjectCacheRuntimeConfig,\n} from \"./types.js\";\n\nconst DEFAULT_KEY_PREFIX = \"em\";\nconst DEFAULT_TTL_SECONDS = 3600;\nconst DEFAULT_REVALIDATE_MS = 1000;\nconst DEFAULT_TIMEOUT_MS = 2000;\n\ninterface BackendHolder {\n\t/** Whether the virtual module has been loaded and the backend resolved. */\n\tinitialized: boolean;\n\t/** Resolved backend, or `null` when no object cache is configured. */\n\tbackend: ObjectCacheBackend | null;\n\t/** In-flight initialization promise (dedupes concurrent first calls). */\n\tinitPromise: Promise<ObjectCacheBackend | null> | null;\n\tconfig: Required<Pick<ObjectCacheRuntimeConfig, \"keyPrefix\">> & {\n\t\tdefaultTtl: number;\n\t\trevalidate: number;\n\t\ttimeout: number;\n\t};\n}\n\n/**\n * Race a backend operation against a timeout so a stalled call (e.g. a KV read\n * that never resolves *and* never rejects — a cold cross-region read, or one\n * queued behind the Workers simultaneous-connection limit) degrades to a\n * rejection instead of hanging the isolate. A rejection is benign: callers\n * already treat a failed read as a cache miss / last-known epoch.\n */\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n\tif (!(ms > 0)) return promise;\n\tlet timer: ReturnType<typeof setTimeout>;\n\tconst timeout = new Promise<never>((_resolve, reject) => {\n\t\ttimer = setTimeout(() => {\n\t\t\treject(new Error(`object-cache ${label} timed out after ${ms}ms`));\n\t\t}, ms);\n\t});\n\treturn Promise.race([promise, timeout]).finally(() => clearTimeout(timer));\n}\n\ninterface EpochEntry {\n\tvalue: number;\n\t/** `Date.now()` at which this epoch was read from the backend. */\n\tat: number;\n\t/** In-flight read, so concurrent callers share one backend round-trip. */\n\tpromise?: Promise<number>;\n}\n\nconst BACKEND_KEY = Symbol.for(\"emdash:object-cache:backend\");\nconst EPOCH_KEY = Symbol.for(\"emdash:object-cache:epochs\");\nconst PENDING_KEY = Symbol.for(\"emdash:object-cache:pending-bumps\");\nconst g = globalThis as Record<symbol, unknown>;\n\nconst holder: BackendHolder =\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis singleton pattern (see request-context.ts)\n\t(g[BACKEND_KEY] as BackendHolder | undefined) ??\n\t(() => {\n\t\tconst h: BackendHolder = {\n\t\t\tinitialized: false,\n\t\t\tbackend: null,\n\t\t\tinitPromise: null,\n\t\t\tconfig: {\n\t\t\t\tkeyPrefix: DEFAULT_KEY_PREFIX,\n\t\t\t\tdefaultTtl: DEFAULT_TTL_SECONDS,\n\t\t\t\trevalidate: DEFAULT_REVALIDATE_MS,\n\t\t\t\ttimeout: DEFAULT_TIMEOUT_MS,\n\t\t\t},\n\t\t};\n\t\tg[BACKEND_KEY] = h;\n\t\treturn h;\n\t})();\n\nconst epochCache: Map<string, EpochEntry> =\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis singleton pattern (see request-context.ts)\n\t(g[EPOCH_KEY] as Map<string, EpochEntry> | undefined) ??\n\t(() => {\n\t\tconst m = new Map<string, EpochEntry>();\n\t\tg[EPOCH_KEY] = m;\n\t\treturn m;\n\t})();\n\n/** Namespaces with a backend epoch write already scheduled this tick. */\nconst pendingBumps: Set<string> =\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis singleton pattern (see request-context.ts)\n\t(g[PENDING_KEY] as Set<string> | undefined) ??\n\t(() => {\n\t\tconst s = new Set<string>();\n\t\tg[PENDING_KEY] = s;\n\t\treturn s;\n\t})();\n\n/**\n * Resolve (once per isolate) the configured object-cache backend.\n *\n * Loads `virtual:emdash/object-cache`, which exports `createObjectCache`\n * (`undefined` when no cache is configured) and the serialized\n * `objectCacheConfig`. Returns `null` when the cache is disabled.\n */\nasync function getBackend(): Promise<ObjectCacheBackend | null> {\n\tif (holder.initialized) return holder.backend;\n\tif (holder.initPromise) return holder.initPromise;\n\n\tholder.initPromise = (async () => {\n\t\ttry {\n\t\t\tconst mod: {\n\t\t\t\tcreateObjectCache?: CreateObjectCacheBackendFn;\n\t\t\t\tobjectCacheConfig?: ObjectCacheRuntimeConfig;\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n\t\t\t\t// @ts-ignore - virtual module\n\t\t\t} = await import(\"virtual:emdash/object-cache\");\n\n\t\t\tconst config = mod.objectCacheConfig ?? {};\n\t\t\tholder.config = {\n\t\t\t\tkeyPrefix:\n\t\t\t\t\ttypeof config.keyPrefix === \"string\" && config.keyPrefix.length > 0\n\t\t\t\t\t\t? config.keyPrefix\n\t\t\t\t\t\t: DEFAULT_KEY_PREFIX,\n\t\t\t\tdefaultTtl:\n\t\t\t\t\ttypeof config.defaultTtl === \"number\" && config.defaultTtl > 0\n\t\t\t\t\t\t? config.defaultTtl\n\t\t\t\t\t\t: DEFAULT_TTL_SECONDS,\n\t\t\t\trevalidate:\n\t\t\t\t\ttypeof config.revalidate === \"number\" && config.revalidate >= 0\n\t\t\t\t\t\t? config.revalidate\n\t\t\t\t\t\t: DEFAULT_REVALIDATE_MS,\n\t\t\t\ttimeout:\n\t\t\t\t\ttypeof config.timeout === \"number\" && config.timeout >= 0\n\t\t\t\t\t\t? config.timeout\n\t\t\t\t\t\t: DEFAULT_TIMEOUT_MS,\n\t\t\t};\n\n\t\t\tholder.backend =\n\t\t\t\ttypeof mod.createObjectCache === \"function\" ? mod.createObjectCache(config) : null;\n\t\t} catch (error) {\n\t\t\t// Importing the virtual module fails outside an Astro/Vite context\n\t\t\t// (e.g. unit tests, CLI). Treat as \"no cache configured\".\n\t\t\tif (import.meta.env?.DEV) {\n\t\t\t\tconsole.warn(\"[object-cache] backend unavailable:\", error);\n\t\t\t}\n\t\t\tholder.backend = null;\n\t\t}\n\t\tholder.initialized = true;\n\t\tholder.initPromise = null;\n\t\treturn holder.backend;\n\t})();\n\n\treturn holder.initPromise;\n}\n\n/**\n * Test-only override of the backend, bypassing the virtual module.\n *\n * Lets unit tests inject an in-memory backend (and optional config) without a\n * full Astro/Vite build. Pass `null` to simulate \"no cache configured\".\n *\n * @internal\n */\nexport function __setObjectCacheBackendForTests(\n\tbackend: ObjectCacheBackend | null,\n\tconfig?: Partial<BackendHolder[\"config\"]>,\n): void {\n\tholder.initialized = true;\n\tholder.initPromise = null;\n\tholder.backend = backend;\n\tholder.config = { ...holder.config, ...config };\n\tepochCache.clear();\n}\n\n/** Build the backend key for a namespace's epoch anchor. */\nfunction epochKey(namespace: string): string {\n\treturn `${holder.config.keyPrefix}:epoch:${namespace}`;\n}\n\n/**\n * Build the (epoch-independent) backend key for a cached value.\n *\n * The key is stable across invalidations — the namespace epochs are stored\n * *inside* the value envelope and validated on read, not baked into the key.\n * This lets the value and the epochs be fetched in one parallel round-trip\n * (instead of \"read epoch, then read value\"), and means an invalidated value\n * is overwritten in place rather than orphaned under a dead epoch-keyed name.\n */\nfunction valueKey(namespaces: readonly string[], key: string): string {\n\treturn `${holder.config.keyPrefix}:${namespaces.join(\",\")}:${key}`;\n}\n\n/**\n * Stored cache envelope: the namespace epochs captured at write time alongside\n * the cached value. A read is a HIT only when every stored epoch still matches\n * the current epoch for its namespace.\n */\ninterface CacheEnvelope<T> {\n\t/** Epoch per namespace, in the query's namespace order. */\n\te: number[];\n\t/** The cached value. */\n\tv: T;\n}\n\nfunction epochsMatch(stored: readonly number[], current: readonly number[]): boolean {\n\tif (stored.length !== current.length) return false;\n\tfor (let i = 0; i < stored.length; i++) {\n\t\tif (stored[i] !== current[i]) return false;\n\t}\n\treturn true;\n}\n\n/**\n * Requests that must always read live data and never populate the cache:\n * visual edit mode, preview tokens, and isolated databases (playground / DO\n * preview, whose schema and content diverge from the configured site).\n */\nfunction shouldBypass(): boolean {\n\tconst ctx = getRequestContext();\n\tif (!ctx) return false;\n\treturn ctx.editMode === true || ctx.preview !== undefined || ctx.dbIsIsolated === true;\n}\n\n/**\n * Read the current epoch for `namespace`, reusing an isolate-cached value for\n * up to `revalidate` ms. A missing epoch (never bumped) is treated as `0`.\n *\n * Backend errors and stalls are non-fatal: the read is bounded by a timeout,\n * and on failure we fall back to the last known epoch (or `0`), so a flaky or\n * hung cache degrades to \"serve whatever's keyed\" rather than throwing or\n * hanging.\n */\nasync function getEpoch(namespace: string, backend: ObjectCacheBackend): Promise<number> {\n\tconst now = Date.now();\n\tconst cached = epochCache.get(namespace);\n\tif (cached && now - cached.at < holder.config.revalidate) {\n\t\treturn cached.value;\n\t}\n\tif (cached?.promise) return cached.promise;\n\n\tconst promise = (async () => {\n\t\tlet value: number;\n\t\ttry {\n\t\t\tconst raw = await withTimeout(\n\t\t\t\tbackend.get(epochKey(namespace)),\n\t\t\t\tholder.config.timeout,\n\t\t\t\t\"epoch read\",\n\t\t\t);\n\t\t\tconst parsed = raw === null ? 0 : Number(raw);\n\t\t\tvalue = Number.isFinite(parsed) ? parsed : 0;\n\t\t} catch {\n\t\t\tvalue = cached?.value ?? 0;\n\t\t}\n\t\t// A concurrent invalidateObjectCache may have bumped the epoch while this\n\t\t// read was in flight. Epochs are monotonic, so never let a stale backend\n\t\t// read lower a freshly-bumped local epoch — that would resurrect the very\n\t\t// values the bump just invalidated.\n\t\tconst merged = Math.max(value, epochCache.get(namespace)?.value ?? 0);\n\t\tepochCache.set(namespace, { value: merged, at: Date.now() });\n\t\treturn merged;\n\t})();\n\n\t// Concurrent callers share this in-flight read (dedup). The timeout above\n\t// guarantees `promise` settles — its success/catch handler then replaces\n\t// this entry with a fresh, promise-free one — so a stalled backend can no\n\t// longer pin the namespace to a never-settling promise (the bug that\n\t// poisoned an isolate until it was recycled).\n\tepochCache.set(namespace, { value: cached?.value ?? 0, at: cached?.at ?? 0, promise });\n\treturn promise;\n}\n\n/** Options for {@link cachedQuery}. */\nexport interface CachedQueryOptions<T> {\n\t/**\n\t * Invalidation namespace(s). A single string for self-contained data\n\t * (`settings`, `menus`), or several when the cached value depends on data\n\t * owned by other namespaces — e.g. a content entry hydrates bylines and\n\t * taxonomy terms, so it caches under\n\t * `[content:posts, \"bylines\", \"taxonomies\"]` and is invalidated when *any*\n\t * of them is bumped. Every namespace's epoch is folded into the key.\n\t */\n\tnamespace: string | readonly string[];\n\t/** Stable, fully-qualifying cache key *within* the namespace. */\n\tkey: string;\n\t/** Loader run on a miss (or when caching is disabled/bypassed). */\n\tload: () => Promise<T>;\n\t/** TTL override in seconds. Falls back to the configured `defaultTtl`. */\n\tttl?: number;\n\t/**\n\t * Predicate gating whether a freshly-loaded value is stored. Defaults to\n\t * always-cache. Use it to skip caching error/empty sentinels.\n\t */\n\tcacheable?: (value: T) => boolean;\n}\n\n/**\n * Distributed read-through cache around `load`.\n *\n * `T` must be the value as it should be *stored* — i.e. JSON-serializable with\n * the codec's `Date` support, carrying no functions or symbol-keyed props.\n * Callers caching richer objects (content entries) reduce to a serializable\n * snapshot here and rebuild on the way out; see `query.ts`.\n *\n * On a miss or when the cache is disabled/bypassed, this is equivalent to\n * `await load()`. Backend errors never propagate: a failing `get` is a miss, a\n * failing `set` is dropped.\n */\nexport async function cachedQuery<T>(options: CachedQueryOptions<T>): Promise<T> {\n\tconst backend = await getBackend();\n\tif (!backend || shouldBypass()) {\n\t\treturn options.load();\n\t}\n\n\tconst namespaces =\n\t\ttypeof options.namespace === \"string\" ? [options.namespace] : options.namespace;\n\tconst fullKey = valueKey(namespaces, options.key);\n\n\t// Kick off the value read and every namespace epoch read concurrently — one\n\t// round-trip instead of \"read epochs, then read value\". getEpoch never\n\t// rejects, so awaiting the epochs separately from the value read guarantees\n\t// we hold the pre-load epochs even when the value read errors or times out.\n\t// Storing a value under an epoch read *after* load() would mask a write that\n\t// landed during load(): the stale value would match and be served as a HIT.\n\tconst epochsPromise = Promise.all(namespaces.map((ns) => getEpoch(ns, backend)));\n\tconst rawPromise = withTimeout(backend.get(fullKey), holder.config.timeout, \"read\").catch(\n\t\t() => null,\n\t);\n\tconst currentEpochs = await epochsPromise;\n\tconst raw = await rawPromise;\n\tif (raw !== null) {\n\t\tconst decoded = decode(raw);\n\t\tif (decoded !== undefined) {\n\t\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- value envelope written by this function\n\t\t\tconst envelope = decoded as CacheEnvelope<T>;\n\t\t\tif (epochsMatch(envelope.e, currentEpochs)) {\n\t\t\t\treturn envelope.v;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst value = await options.load();\n\n\tconst cacheable = options.cacheable ? options.cacheable(value) : true;\n\tif (cacheable) {\n\t\tconst ttl = options.ttl ?? holder.config.defaultTtl;\n\t\t// Defer the write so it never adds to TTFB. The epochs were captured\n\t\t// before load() ran, so a write that invalidated this namespace mid-load\n\t\t// correctly orphans the value stored here.\n\t\tafter(async () => {\n\t\t\ttry {\n\t\t\t\tconst encoded = encode({ e: currentEpochs, v: value } satisfies CacheEnvelope<T>);\n\t\t\t\tawait backend.set(fullKey, encoded, ttl);\n\t\t\t} catch (error) {\n\t\t\t\tif (import.meta.env?.DEV) {\n\t\t\t\t\tconsole.warn(\"[object-cache] set failed:\", error);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\treturn value;\n}\n\n/** Whether object-cache reads are active for the current request. */\nexport async function isObjectCacheActive(): Promise<boolean> {\n\tconst backend = await getBackend();\n\treturn backend !== null && !shouldBypass();\n}\n\n/**\n * Invalidate every cached value in `namespace` by bumping its epoch.\n *\n * Sync and non-blocking: the local epoch is stamped immediately (so the\n * writing isolate is instantly consistent) and the backend write is deferred\n * via `after`. Other isolates pick up the new epoch within their `revalidate`\n * window. No-ops when the cache is disabled.\n */\nexport function invalidateObjectCache(namespace: string): void {\n\t// Monotonic so two writes in the same millisecond still produce distinct\n\t// epochs — otherwise the second write reuses the first's stamp and its\n\t// stale entries survive.\n\tconst prev = epochCache.get(namespace)?.value ?? 0;\n\tconst stamp = Math.max(prev + 1, Date.now());\n\t// Optimistic local bump: keep this isolate consistent without a round-trip.\n\tepochCache.set(namespace, { value: stamp, at: stamp });\n\n\t// Coalesce repeated bumps of the same namespace within a tick (e.g. a bulk\n\t// publish loop) into a single backend write that persists the latest epoch.\n\tif (pendingBumps.has(namespace)) return;\n\tpendingBumps.add(namespace);\n\tafter(async () => {\n\t\tpendingBumps.delete(namespace);\n\t\ttry {\n\t\t\tconst backend = await getBackend();\n\t\t\tif (!backend) return;\n\t\t\tconst latest = epochCache.get(namespace)?.value ?? stamp;\n\t\t\t// Epoch anchors are persistent (no TTL) — they must outlive the\n\t\t\t// value keys they invalidate.\n\t\t\tawait backend.set(epochKey(namespace), String(latest));\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[object-cache] epoch bump failed for\", namespace, error);\n\t\t}\n\t});\n}\n\n/**\n * Fixed namespaces for data shared across collections. Content reads fold the\n * `BYLINES` and `TAXONOMIES` epochs into their keys (via {@link cachedQuery})\n * because entries hydrate byline and taxonomy-term data — so renaming an\n * author or a category correctly invalidates every cached entry that displays\n * it, without tracking which collections reference it.\n */\nexport const CacheNamespace = {\n\tSETTINGS: \"settings\",\n\tMENUS: \"menus\",\n\tTAXONOMIES: \"taxonomies\",\n\tBYLINES: \"bylines\",\n\t/** Collection schema/metadata (label, supports, commentsEnabled, fields). */\n\tSCHEMA: \"schema\",\n\t/** Public (approved) comments. */\n\tCOMMENTS: \"comments\",\n} as const;\n\n/** Namespace for a content collection's cached queries. */\nexport function contentNamespace(collection: string): string {\n\treturn `content:${collection}`;\n}\n\n/**\n * Namespaces a content read depends on: the collection itself plus the shared\n * byline/taxonomy data folded into each entry.\n */\nexport function contentNamespaces(collection: string): readonly string[] {\n\treturn [contentNamespace(collection), CacheNamespace.BYLINES, CacheNamespace.TAXONOMIES];\n}\n\n/**\n * Invalidate all cached reads (list + entry) for a content collection.\n * Call from every write path that mutates rows in `ec_<collection>`.\n */\nexport function invalidateCollectionCache(collection: string): void {\n\tinvalidateObjectCache(contentNamespace(collection));\n}\n\n/** Invalidate cached taxonomy definitions/terms and all content that hydrates them. */\nexport function invalidateTaxonomyObjectCache(): void {\n\tinvalidateObjectCache(CacheNamespace.TAXONOMIES);\n}\n\n/** Invalidate cached bylines and all content that hydrates them. */\nexport function invalidateBylineObjectCache(): void {\n\tinvalidateObjectCache(CacheNamespace.BYLINES);\n}\n\n/** Invalidate cached navigation menus. */\nexport function invalidateMenuObjectCache(): void {\n\tinvalidateObjectCache(CacheNamespace.MENUS);\n}\n\n/** Invalidate cached collection schema/metadata reads (e.g. getCollectionInfo). */\nexport function invalidateSchemaObjectCache(): void {\n\tinvalidateObjectCache(CacheNamespace.SCHEMA);\n}\n\n/** Invalidate cached public comment reads. */\nexport function invalidateCommentObjectCache(): void {\n\tinvalidateObjectCache(CacheNamespace.COMMENTS);\n}\n\nexport type {\n\tObjectCacheBackend,\n\tObjectCacheDescriptor,\n\tObjectCacheRuntimeConfig,\n} from \"./types.js\";\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as ContentItem } from "./types-
|
|
2
|
-
import { t as Database } from "./types-
|
|
1
|
+
import { i as ContentItem } from "./types-ETmO_jQr.mjs";
|
|
2
|
+
import { t as Database } from "./types-xIfVRNLp.mjs";
|
|
3
3
|
import { Kysely } from "kysely";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
@@ -204,4 +204,4 @@ declare class OptionsRepository {
|
|
|
204
204
|
}
|
|
205
205
|
//#endregion
|
|
206
206
|
export { parseQuery as a, handleError as c, ContentListResponse as d, ContentResponse as f, ManifestResponse as h, parseBody as i, ApiContext as l, ListResponse as m, ParseResult as n, apiError as o, FieldDescriptor as p, isParseError as r, apiSuccess as s, OptionsRepository as t, ApiResult as u };
|
|
207
|
-
//# sourceMappingURL=options-
|
|
207
|
+
//# sourceMappingURL=options-41nCWqi9.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options-
|
|
1
|
+
{"version":3,"file":"options-41nCWqi9.d.mts","names":[],"sources":["../src/api/types.ts","../src/api/error.ts","../src/api/parse.ts","../src/database/repositories/options.ts"],"mappings":";;;;;;;;AASA;UAAiB,YAAA;EAChB,KAAA,EAAO,CAAA;EACP,UAAA;EAF6B;;;;;EAQ7B,KAAA;AAAA;AAMD;;;AAAA,UAAiB,mBAAA,SAA4B,YAAA,CAAa,WAAA;AAAA,UAEzC,eAAA;EAChB,IAAA,EAAM,WAAA;;EAEN,IAAA;AAAA;;;;UAMgB,gBAAA;EAChB,OAAA;EACA,IAAA;EACA,WAAA,EAAa,MAAA;IAGX,KAAA;IACA,aAAA;IACA,QAAA;IACA,MAAA,EAAQ,MAAA,SAAe,eAAA;EAAA;EAGzB,OAAA,EAAS,MAAA;IAGP,UAAA,GAAa,KAAA;MAAQ,IAAA;MAAc,SAAA;IAAA;IACnC,OAAA;EAAA;AAAA;AAAA,UAKc,eAAA;EAChB,IAAA;EACA,KAAA;EACA,QAAA;EAZA;;;;EAiBA,OAAA,GAAU,KAAA;IAAQ,KAAA;IAAe,KAAA;EAAA,KAAmB,MAAA;AAAA;AARrD;;;;;;;;;;;;AAAA,KAuBY,SAAA;EACP,OAAA;EAAe,IAAA,EAAM,CAAA;AAAA;EAEvB,OAAA;EACA,KAAA;IAAS,IAAA,EAAM,CAAA;IAAG,OAAA;IAAiB,OAAA,GAAU,MAAA;EAAA;AAAA;;;;UAM/B,UAAA;EAChB,MAAA;EACA,QAAA;AAAA;;;;;;;;;iBC5De,QAAA,CACf,IAAA,UACA,OAAA,UACA,MAAA,UACA,OAAA,GAAU,MAAA,oBACR,QAAA;;;ADZH;;;;iBC2BgB,UAAA,GAAA,CAAc,IAAA,EAAM,CAAA,EAAG,MAAA,YAAe,QAAA;ADzBtD;;;;;;;;AAAA,iBCqCgB,WAAA,CACf,KAAA,WACA,eAAA,UACA,YAAA,WACE,QAAA;;;;;;;KChDS,WAAA,MAAiB,CAAA,GAAI,QAAA;;;;;;AFKjC;iBEGsB,SAAA,WAAoB,CAAA,CAAE,OAAA,CAAA,CAC3C,OAAA,EAAS,OAAA,EACT,MAAA,EAAQ,CAAA,GACN,OAAA,CAAQ,WAAA,CAAY,CAAA,CAAE,KAAA,CAAM,CAAA;;;;;;AFK/B;;iBEyDgB,UAAA,WAAqB,CAAA,CAAE,OAAA,CAAA,CAAS,GAAA,EAAK,GAAA,EAAK,MAAA,EAAQ,CAAA,GAAI,WAAA,CAAY,CAAA,CAAE,KAAA,CAAM,CAAA;;;;;iBA6C1E,YAAA,GAAA,CAAgB,MAAA,EAAQ,WAAA,CAAY,CAAA,IAAK,MAAA,IAAU,QAAA;;;;;;;AF/HnE;;cGKa,iBAAA;EAAA,QACQ,EAAA;cAAA,EAAA,EAAI,MAAA,CAAO,QAAA;EHL/B;;;EGUM,GAAA,aAAA,CAAiB,IAAA,WAAe,OAAA,CAAQ,CAAA;EHHzC;;AAMN;EGYO,YAAA,GAAA,CAAgB,IAAA,UAAc,YAAA,EAAc,CAAA,GAAI,OAAA,CAAQ,CAAA;;;;EAQxD,GAAA,aAAA,CAAiB,IAAA,UAAc,KAAA,EAAO,CAAA,GAAI,OAAA;EHlBjB;;;;;;;;EGwCzB,WAAA,aAAA,CAAyB,IAAA,UAAc,KAAA,EAAO,CAAA,GAAI,OAAA;EH/BxB;;;EGmD1B,MAAA,CAAO,IAAA,WAAe,OAAA;EHhDf;;;EGyDP,MAAA,CAAO,IAAA,WAAe,OAAA;EHhDb;;;EG6DT,OAAA,aAAA,CAAqB,KAAA,aAAkB,OAAA,CAAQ,GAAA,SAAY,CAAA;EHtEpD;;;EG0FP,OAAA,aAAA,CAAqB,OAAA,EAAS,MAAA,SAAe,CAAA,IAAK,OAAA;EHpFtD;;;EGgGI,MAAA,CAAA,GAAU,OAAA,CAAQ,GAAA;EH7Ff;;;EG0GH,WAAA,aAAA,CAAyB,MAAA,WAAiB,OAAA,CAAQ,GAAA,SAAY,CAAA;EHvG/B;;;EG0H/B,cAAA,CAAe,MAAA,WAAiB,OAAA;AAAA"}
|
package/dist/page/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { et as PageFragmentContribution, ot as PageMetadataLinkRel, rt as PageMetadataContribution, st as PagePlacement, t as BreadcrumbItem, xt as PublicPageContext } from "../types-BYnDBVwe.mjs";
|
|
2
2
|
import { n as SeoSettings } from "../types-BG8_XTLj.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/page/context.d.ts
|
|
@@ -78,6 +78,9 @@ interface ResolvedPageMetadata {
|
|
|
78
78
|
json: string;
|
|
79
79
|
}>;
|
|
80
80
|
}
|
|
81
|
+
interface RenderPageMetadataOptions {
|
|
82
|
+
includeJsonLd?: boolean;
|
|
83
|
+
}
|
|
81
84
|
/** Escape a string for safe use in an HTML attribute value */
|
|
82
85
|
declare function escapeHtmlAttr(value: string): string;
|
|
83
86
|
/**
|
|
@@ -95,7 +98,7 @@ declare function safeJsonLdSerialize(value: unknown): string;
|
|
|
95
98
|
*/
|
|
96
99
|
declare function resolvePageMetadata(contributions: PageMetadataContribution[]): ResolvedPageMetadata;
|
|
97
100
|
/** Render resolved metadata to an HTML string for embedding in <head> */
|
|
98
|
-
declare function renderPageMetadata(metadata: ResolvedPageMetadata): string;
|
|
101
|
+
declare function renderPageMetadata(metadata: ResolvedPageMetadata, options?: RenderPageMetadataOptions): string;
|
|
99
102
|
//#endregion
|
|
100
103
|
//#region src/page/fragments.d.ts
|
|
101
104
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/page/context.ts","../../src/page/metadata.ts","../../src/page/fragments.ts","../../src/page/seo-contributions.ts","../../src/page/jsonld.ts","../../src/page/index.ts"],"mappings":";;;;;UAUU,iBAAA;EACT,IAAA;EACA,QAAA;EACA,KAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;EACA,KAAA;EACA,OAAA;IAAY,UAAA;IAAoB,EAAA;IAAY,IAAA;EAAA;EAI3C;EAFD,GAAA;IACC,OAAA;IACA,aAAA;IACA,OAAA;IACA,MAAA;EAAA;EASD;EANA,WAAA;IACC,aAAA;IACA,YAAA;IACA,MAAA;EAAA;EAeQ;EAZT,QAAA;;;;;;EAMA,WAAA,GAAc,cAAA;EAOK;EALnB,OAAA;AAAA;AAKgC;AAAA,UADvB,UAAA,SAAmB,iBAAA;EAC5B,KAAA;IAAS,GAAA,EAAK,GAAA;IAAK,aAAA;EAAA;AAAA;;UAIV,QAAA,SAAiB,iBAAA;EAC1B,GAAA,EAAK,GAAA;EACL,MAAA;AAAA;AAAA,KAGW,4BAAA,GAA+B,UAAA,GAAa,QAAA;;;AASxD;iBAAgB,uBAAA,CAAwB,KAAA,EAAO,4BAAA,GAA+B,iBAAA;;;UCrD7D,oBAAA;EAChB,IAAA,EAAM,KAAA;IAAQ,IAAA;IAAc,OAAA;EAAA;EAC5B,UAAA,EAAY,KAAA;IAAQ,QAAA;IAAkB,OAAA;EAAA;EACtC,KAAA,EAAO,KAAA;IACN,GAAA,EAAK,mBAAA;IACL,IAAA;IACA,QAAA;EAAA;EAED,MAAA,EAAQ,KAAA;IAAQ,EAAA;IAAa,IAAA;EAAA;AAAA;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/page/context.ts","../../src/page/metadata.ts","../../src/page/fragments.ts","../../src/page/seo-contributions.ts","../../src/page/jsonld.ts","../../src/page/index.ts"],"mappings":";;;;;UAUU,iBAAA;EACT,IAAA;EACA,QAAA;EACA,KAAA;EACA,SAAA;EACA,WAAA;EACA,SAAA;EACA,KAAA;EACA,OAAA;IAAY,UAAA;IAAoB,EAAA;IAAY,IAAA;EAAA;EAI3C;EAFD,GAAA;IACC,OAAA;IACA,aAAA;IACA,OAAA;IACA,MAAA;EAAA;EASD;EANA,WAAA;IACC,aAAA;IACA,YAAA;IACA,MAAA;EAAA;EAeQ;EAZT,QAAA;;;;;;EAMA,WAAA,GAAc,cAAA;EAOK;EALnB,OAAA;AAAA;AAKgC;AAAA,UADvB,UAAA,SAAmB,iBAAA;EAC5B,KAAA;IAAS,GAAA,EAAK,GAAA;IAAK,aAAA;EAAA;AAAA;;UAIV,QAAA,SAAiB,iBAAA;EAC1B,GAAA,EAAK,GAAA;EACL,MAAA;AAAA;AAAA,KAGW,4BAAA,GAA+B,UAAA,GAAa,QAAA;;;AASxD;iBAAgB,uBAAA,CAAwB,KAAA,EAAO,4BAAA,GAA+B,iBAAA;;;UCrD7D,oBAAA;EAChB,IAAA,EAAM,KAAA;IAAQ,IAAA;IAAc,OAAA;EAAA;EAC5B,UAAA,EAAY,KAAA;IAAQ,QAAA;IAAkB,OAAA;EAAA;EACtC,KAAA,EAAO,KAAA;IACN,GAAA,EAAK,mBAAA;IACL,IAAA;IACA,QAAA;EAAA;EAED,MAAA,EAAQ,KAAA;IAAQ,EAAA;IAAa,IAAA;EAAA;AAAA;AAAA,UAGpB,yBAAA;EACT,aAAA;AAAA;;iBAiBe,cAAA,CAAe,KAAA;;;;ADCvB;;;;;iBCuBQ,mBAAA,CAAoB,KAAA;;;;;iBAwBpB,mBAAA,CACf,aAAA,EAAe,wBAAA,KACb,oBAAA;;iBAiFa,kBAAA,CACf,QAAA,EAAU,oBAAA,EACV,OAAA,GAAS,yBAAA;;;;;;;;iBCzJM,gBAAA,CACf,aAAA,EAAe,wBAAA,IACf,SAAA,EAAW,aAAA,GACT,wBAAA;;iBA2Da,eAAA,CACf,aAAA,EAAe,wBAAA,IACf,SAAA,EAAW,aAAA;;;;;;;;;;;;;iBCzDI,4BAAA,CACf,IAAA,EAAM,iBAAA,EACN,cAAA,mBACE,wBAAA;;;;;;;;iBA8Ha,4BAAA,CACf,WAAA,EAAa,WAAA,eACX,wBAAA;;;;;;;iBCjJa,WAAA,CAAY,GAAA,EAAK,MAAA,oBAA0B,MAAA;;;;;;;;;;iBAwB3C,sBAAA,CACf,IAAA,EAAM,iBAAA,EACN,cAAA,mBACE,MAAA;;;;;iBA2Ca,kBAAA,CAAmB,IAAA,EAAM,iBAAA,GAAoB,MAAA;;;;;;;UClD5C,iBAAA;EAChB,mBAAA,GAAsB,IAAA,EAAM,iBAAA,KAAsB,OAAA,CAAQ,wBAAA;EAC1D,oBAAA,GAAuB,IAAA,EAAM,iBAAA,KAAsB,OAAA,CAAQ,wBAAA;AAAA;;;;;iBAO5C,cAAA,CAAe,MAAA,EAAQ,MAAA,oBAA0B,iBAAA"}
|
package/dist/page/index.mjs
CHANGED
|
@@ -148,8 +148,9 @@ function resolvePageMetadata(contributions) {
|
|
|
148
148
|
return result;
|
|
149
149
|
}
|
|
150
150
|
/** Render resolved metadata to an HTML string for embedding in <head> */
|
|
151
|
-
function renderPageMetadata(metadata) {
|
|
151
|
+
function renderPageMetadata(metadata, options = {}) {
|
|
152
152
|
const parts = [];
|
|
153
|
+
const includeJsonLd = options.includeJsonLd ?? true;
|
|
153
154
|
for (const m of metadata.meta) parts.push(`<meta name="${escapeHtmlAttr(m.name)}" content="${escapeHtmlAttr(m.content)}">`);
|
|
154
155
|
for (const p of metadata.properties) parts.push(`<meta property="${escapeHtmlAttr(p.property)}" content="${escapeHtmlAttr(p.content)}">`);
|
|
155
156
|
for (const l of metadata.links) {
|
|
@@ -158,7 +159,7 @@ function renderPageMetadata(metadata) {
|
|
|
158
159
|
tag += ">";
|
|
159
160
|
parts.push(tag);
|
|
160
161
|
}
|
|
161
|
-
for (const j of metadata.jsonld) parts.push(`<script type="application/ld+json">${j.json}<\/script>`);
|
|
162
|
+
if (includeJsonLd) for (const j of metadata.jsonld) parts.push(`<script type="application/ld+json">${j.json}<\/script>`);
|
|
162
163
|
return parts.join("\n");
|
|
163
164
|
}
|
|
164
165
|
|
package/dist/page/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/page/context.ts","../../src/page/metadata.ts","../../src/page/fragments.ts","../../src/page/jsonld.ts","../../src/page/seo-contributions.ts","../../src/page/index.ts"],"sourcesContent":["/**\n * Public page context builder\n *\n * Templates call this to describe the page being rendered.\n * The resulting context is passed to EmDashHead / EmDashBodyStart / EmDashBodyEnd.\n */\n\nimport type { BreadcrumbItem, PublicPageContext } from \"../plugins/types.js\";\n\n/** Fields shared by both input forms */\ninterface PageContextFields {\n\tkind: \"content\" | \"custom\";\n\tpageType?: string;\n\ttitle?: string | null;\n\tpageTitle?: string | null;\n\tdescription?: string | null;\n\tcanonical?: string | null;\n\timage?: string | null;\n\tcontent?: { collection: string; id: string; slug?: string | null };\n\t/** SEO overrides for OG/Twitter meta generation */\n\tseo?: {\n\t\togTitle?: string | null;\n\t\togDescription?: string | null;\n\t\togImage?: string | null;\n\t\trobots?: string | null;\n\t};\n\t/** Article metadata for Open Graph article: tags */\n\tarticleMeta?: {\n\t\tpublishedTime?: string | null;\n\t\tmodifiedTime?: string | null;\n\t\tauthor?: string | null;\n\t};\n\t/** Site name for structured data and og:site_name */\n\tsiteName?: string;\n\t/**\n\t * Breadcrumb trail for this page, root first. Pass an empty array\n\t * to explicitly opt out of breadcrumbs (e.g. homepage), or omit the\n\t * field to let consumers fall back to their own derivation.\n\t */\n\tbreadcrumbs?: BreadcrumbItem[];\n\t/** Public-facing site URL (origin) for structured data */\n\tsiteUrl?: string;\n}\n\n/** Input with Astro global -- used in .astro files */\ninterface AstroInput extends PageContextFields {\n\tAstro: { url: URL; currentLocale?: string };\n}\n\n/** Input with explicit URL -- used outside .astro files */\ninterface UrlInput extends PageContextFields {\n\turl: URL | string;\n\tlocale?: string;\n}\n\nexport type CreatePublicPageContextInput = AstroInput | UrlInput;\n\nfunction isAstroInput(input: CreatePublicPageContextInput): input is AstroInput {\n\treturn \"Astro\" in input;\n}\n\n/**\n * Build a PublicPageContext from template input.\n */\nexport function createPublicPageContext(input: CreatePublicPageContextInput): PublicPageContext {\n\tlet url: string;\n\tlet path: string;\n\tlet locale: string | null;\n\n\tif (isAstroInput(input)) {\n\t\turl = input.Astro.url.href;\n\t\tpath = input.Astro.url.pathname;\n\t\tlocale = input.Astro.currentLocale ?? null;\n\t} else {\n\t\tconst parsed = typeof input.url === \"string\" ? new URL(input.url) : input.url;\n\t\turl = parsed.href;\n\t\tpath = parsed.pathname;\n\t\tlocale = input.locale ?? null;\n\t}\n\n\treturn {\n\t\turl,\n\t\tpath,\n\t\tlocale,\n\t\tkind: input.kind,\n\t\tpageType: input.pageType ?? (input.kind === \"content\" ? \"article\" : \"website\"),\n\t\ttitle: input.title ?? null,\n\t\tpageTitle: input.pageTitle ?? null,\n\t\tdescription: input.description ?? null,\n\t\tcanonical: input.canonical ?? null,\n\t\timage: input.image ?? null,\n\t\tcontent: input.content\n\t\t\t? {\n\t\t\t\t\tcollection: input.content.collection,\n\t\t\t\t\tid: input.content.id,\n\t\t\t\t\tslug: input.content.slug ?? null,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tseo: input.seo,\n\t\tarticleMeta: input.articleMeta,\n\t\tsiteName: input.siteName,\n\t\tbreadcrumbs: input.breadcrumbs,\n\t\tsiteUrl: input.siteUrl,\n\t};\n}\n","/**\n * Page metadata collection and rendering\n *\n * Collects typed metadata contributions from plugins via the page:metadata hook,\n * validates them, and resolves them into a deduplicated structure ready to render.\n */\n\nimport type { PageMetadataContribution, PageMetadataLinkRel } from \"../plugins/types.js\";\n\n// ── Resolved output ─────────────────────────────────────────────\n\nexport interface ResolvedPageMetadata {\n\tmeta: Array<{ name: string; content: string }>;\n\tproperties: Array<{ property: string; content: string }>;\n\tlinks: Array<{\n\t\trel: PageMetadataLinkRel;\n\t\thref: string;\n\t\threflang?: string;\n\t}>;\n\tjsonld: Array<{ id?: string; json: string }>;\n}\n\n// ── Validation ──────────────────────────────────────────────────\n\n/** Schemes safe for use in link href attributes */\nconst SAFE_HREF_RE = /^(https?|at):\\/\\//i;\nconst HTML_ESCAPE_MAP: Record<string, string> = {\n\t\"&\": \"&\",\n\t\"<\": \"<\",\n\t\">\": \">\",\n\t'\"': \""\",\n\t\"'\": \"'\",\n};\nconst HTML_ESCAPE_RE = /[&<>\"']/g;\n\n/** Escape a string for safe use in an HTML attribute value */\nexport function escapeHtmlAttr(value: string): string {\n\treturn value.replace(HTML_ESCAPE_RE, (ch) => HTML_ESCAPE_MAP[ch] ?? ch);\n}\n\n/** Validate that a URL uses a safe scheme (http, https, at) */\nfunction isSafeHref(url: string): boolean {\n\treturn SAFE_HREF_RE.test(url);\n}\n\n// ── JSON-LD serialization ───────────────────────────────────────\n\nconst JSONLD_LT_RE = /</g;\nconst JSONLD_GT_RE = />/g;\nconst JSONLD_U2028_RE = /\\u2028/g;\nconst JSONLD_U2029_RE = /\\u2029/g;\n\n/**\n * Safely serialize a value for embedding in a <script type=\"application/ld+json\"> tag.\n *\n * Plain JSON.stringify is not sufficient because:\n * - \"</script>\" in a nested string breaks out of the script tag\n * - \"<!--\" can open an HTML comment\n * - U+2028/U+2029 are line terminators in some JS engines\n */\nexport function safeJsonLdSerialize(value: unknown): string {\n\treturn JSON.stringify(value)\n\t\t.replace(JSONLD_LT_RE, \"\\\\u003c\")\n\t\t.replace(JSONLD_GT_RE, \"\\\\u003e\")\n\t\t.replace(JSONLD_U2028_RE, \"\\\\u2028\")\n\t\t.replace(JSONLD_U2029_RE, \"\\\\u2029\");\n}\n\n// ── Merge / dedupe ──────────────────────────────────────────────\n\n/**\n * Resolve a flat list of contributions into deduplicated metadata.\n * First contribution wins for any given dedupe key.\n */\nexport function resolvePageMetadata(\n\tcontributions: PageMetadataContribution[],\n): ResolvedPageMetadata {\n\tconst result: ResolvedPageMetadata = {\n\t\tmeta: [],\n\t\tproperties: [],\n\t\tlinks: [],\n\t\tjsonld: [],\n\t};\n\n\tconst seenMeta = new Set<string>();\n\tconst seenProperties = new Set<string>();\n\tconst seenLinks = new Set<string>();\n\tconst seenJsonLd = new Set<string>();\n\n\tfor (const c of contributions) {\n\t\tswitch (c.kind) {\n\t\t\tcase \"meta\": {\n\t\t\t\tconst dedupeKey = c.key ?? c.name;\n\t\t\t\tif (seenMeta.has(dedupeKey)) continue;\n\t\t\t\tseenMeta.add(dedupeKey);\n\t\t\t\tresult.meta.push({ name: c.name, content: c.content });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"property\": {\n\t\t\t\tconst dedupeKey = c.key ?? c.property;\n\t\t\t\tif (seenProperties.has(dedupeKey)) continue;\n\t\t\t\tseenProperties.add(dedupeKey);\n\t\t\t\tresult.properties.push({\n\t\t\t\t\tproperty: c.property,\n\t\t\t\t\tcontent: c.content,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"link\": {\n\t\t\t\tif (!isSafeHref(c.href)) {\n\t\t\t\t\tif (import.meta.env?.DEV) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[page:metadata] Rejected link contribution with unsafe href scheme: ${c.href}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (c.rel === \"canonical\") {\n\t\t\t\t\tif (seenLinks.has(\"canonical\")) continue;\n\t\t\t\t\tseenLinks.add(\"canonical\");\n\t\t\t\t} else {\n\t\t\t\t\tconst dedupeKey = c.key ?? c.hreflang ?? c.href;\n\t\t\t\t\tif (seenLinks.has(dedupeKey)) continue;\n\t\t\t\t\tseenLinks.add(dedupeKey);\n\t\t\t\t}\n\t\t\t\tresult.links.push({\n\t\t\t\t\trel: c.rel,\n\t\t\t\t\thref: c.href,\n\t\t\t\t\t...(c.hreflang && { hreflang: c.hreflang }),\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"jsonld\": {\n\t\t\t\tif (c.id) {\n\t\t\t\t\tif (seenJsonLd.has(c.id)) continue;\n\t\t\t\t\tseenJsonLd.add(c.id);\n\t\t\t\t}\n\t\t\t\tresult.jsonld.push({\n\t\t\t\t\tid: c.id,\n\t\t\t\t\tjson: safeJsonLdSerialize(c.graph),\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\t// Unknown contribution kind -- skip silently at runtime.\n\t\t\t\t// TypeScript catches this at compile time for typed callers,\n\t\t\t\t// but sandboxed plugins may return unexpected shapes.\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ── HTML rendering ──────────────────────────────────────────────\n\n/** Render resolved metadata to an HTML string for embedding in <head> */\nexport function renderPageMetadata(metadata: ResolvedPageMetadata): string {\n\tconst parts: string[] = [];\n\n\tfor (const m of metadata.meta) {\n\t\tparts.push(`<meta name=\"${escapeHtmlAttr(m.name)}\" content=\"${escapeHtmlAttr(m.content)}\">`);\n\t}\n\n\tfor (const p of metadata.properties) {\n\t\tparts.push(\n\t\t\t`<meta property=\"${escapeHtmlAttr(p.property)}\" content=\"${escapeHtmlAttr(p.content)}\">`,\n\t\t);\n\t}\n\n\tfor (const l of metadata.links) {\n\t\tlet tag = `<link rel=\"${escapeHtmlAttr(l.rel)}\" href=\"${escapeHtmlAttr(l.href)}\"`;\n\t\tif (l.hreflang) {\n\t\t\ttag += ` hreflang=\"${escapeHtmlAttr(l.hreflang)}\"`;\n\t\t}\n\t\ttag += \">\";\n\t\tparts.push(tag);\n\t}\n\n\tfor (const j of metadata.jsonld) {\n\t\tparts.push(`<script type=\"application/ld+json\">${j.json}</script>`);\n\t}\n\n\treturn parts.join(\"\\n\");\n}\n","/**\n * Page fragment collection and rendering\n *\n * Collects raw markup / script contributions from trusted plugins via\n * the page:fragments hook. Sandboxed plugins are never invoked.\n */\n\nimport type { PageFragmentContribution, PagePlacement } from \"../plugins/types.js\";\nimport { escapeHtmlAttr } from \"./metadata.js\";\n\n/** Escape sequences that would break out of a script tag */\nconst SCRIPT_CLOSE_RE = /<\\//g;\n\n// ── Dedupe and filter ───────────────────────────────────────────\n\n/**\n * Filter contributions to a specific placement and deduplicate.\n * - Contributions with the same `key + placement` are deduped (first wins).\n * - External scripts with the same `src + placement` are deduped.\n */\nexport function resolveFragments(\n\tcontributions: PageFragmentContribution[],\n\tplacement: PagePlacement,\n): PageFragmentContribution[] {\n\tconst filtered = contributions.filter((c) => c.placement === placement);\n\tconst seen = new Set<string>();\n\tconst result: PageFragmentContribution[] = [];\n\n\tfor (const c of filtered) {\n\t\t// Key-based dedupe\n\t\tif (c.key) {\n\t\t\tconst dedupeKey = `key:${c.key}`;\n\t\t\tif (seen.has(dedupeKey)) continue;\n\t\t\tseen.add(dedupeKey);\n\t\t} else if (c.kind === \"external-script\") {\n\t\t\tconst dedupeKey = `src:${c.src}`;\n\t\t\tif (seen.has(dedupeKey)) continue;\n\t\t\tseen.add(dedupeKey);\n\t\t}\n\n\t\tresult.push(c);\n\t}\n\n\treturn result;\n}\n\n// ── HTML rendering ──────────────────────────────────────────────\n\nconst EVENT_HANDLER_RE = /^on/i;\n\nfunction renderAttributes(attrs: Record<string, string>): string {\n\treturn Object.entries(attrs)\n\t\t.filter(([k]) => !EVENT_HANDLER_RE.test(k))\n\t\t.map(([k, v]) => ` ${escapeHtmlAttr(k)}=\"${escapeHtmlAttr(v)}\"`)\n\t\t.join(\"\");\n}\n\n/** Render a single fragment contribution to HTML */\nfunction renderFragment(c: PageFragmentContribution): string {\n\tswitch (c.kind) {\n\t\tcase \"external-script\": {\n\t\t\tlet tag = `<script src=\"${escapeHtmlAttr(c.src)}\"`;\n\t\t\tif (c.async) tag += \" async\";\n\t\t\tif (c.defer) tag += \" defer\";\n\t\t\tif (c.attributes) tag += renderAttributes(c.attributes);\n\t\t\ttag += \"></script>\";\n\t\t\treturn tag;\n\t\t}\n\t\tcase \"inline-script\": {\n\t\t\tlet tag = \"<script\";\n\t\t\tif (c.attributes) tag += renderAttributes(c.attributes);\n\t\t\t// Escape </ to <\\/ to prevent breaking out of the script tag.\n\t\t\t// This is valid JS and protects against code built from user data.\n\t\t\ttag += `>${c.code.replace(SCRIPT_CLOSE_RE, \"<\\\\/\")}</script>`;\n\t\t\treturn tag;\n\t\t}\n\t\tcase \"html\":\n\t\t\treturn c.html;\n\t}\n}\n\n/** Render a list of fragment contributions to an HTML string */\nexport function renderFragments(\n\tcontributions: PageFragmentContribution[],\n\tplacement: PagePlacement,\n): string {\n\tconst resolved = resolveFragments(contributions, placement);\n\treturn resolved.map(renderFragment).join(\"\\n\");\n}\n","/**\n * JSON-LD structured data builders\n *\n * Moved from template SEO.astro components into core so all JSON-LD\n * is serialized via safeJsonLdSerialize() and never hand-rolled in templates.\n */\n\nimport type { PublicPageContext } from \"../plugins/types.js\";\n\n/**\n * Remove null/undefined values from a JSON-LD object recursively.\n * JSON-LD validators prefer absent keys over null values.\n */\nexport function cleanJsonLd(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst cleaned: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (value !== undefined && value !== null) {\n\t\t\tif (typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- non-null, non-array object is safely treated as Record<string, unknown> for JSON-LD traversal\n\t\t\t\tcleaned[key] = cleanJsonLd(value as Record<string, unknown>);\n\t\t\t} else {\n\t\t\t\tcleaned[key] = value;\n\t\t\t}\n\t\t}\n\t}\n\treturn cleaned;\n}\n\n/**\n * Build a BlogPosting JSON-LD graph from page context.\n * Used for article-type content pages.\n *\n * @param page - Page context for the current request.\n * @param defaultOgImage - Optional site-wide fallback image URL, used when\n * the page has no own OG image. Matches the fallback applied to `og:image`\n * in `generateBaseSeoContributions`.\n */\nexport function buildBlogPostingJsonLd(\n\tpage: PublicPageContext,\n\tdefaultOgImage?: string | null,\n): Record<string, unknown> | null {\n\tif (page.pageType !== \"article\" || !page.canonical) return null;\n\n\tconst ogTitle = page.seo?.ogTitle ?? page.pageTitle ?? page.title;\n\tconst description = page.seo?.ogDescription || page.description;\n\tconst ogImage = page.seo?.ogImage || page.image || defaultOgImage || null;\n\tconst publishedTime = page.articleMeta?.publishedTime;\n\tconst modifiedTime = page.articleMeta?.modifiedTime;\n\tconst author = page.articleMeta?.author;\n\tconst siteName = page.siteName;\n\n\treturn cleanJsonLd({\n\t\t\"@context\": \"https://schema.org\",\n\t\t\"@type\": \"BlogPosting\",\n\t\theadline: ogTitle,\n\t\tdescription,\n\t\timage: ogImage || undefined,\n\t\turl: page.canonical,\n\t\tdatePublished: publishedTime || undefined,\n\t\tdateModified: modifiedTime || publishedTime || undefined,\n\t\tauthor: author\n\t\t\t? {\n\t\t\t\t\t\"@type\": \"Person\",\n\t\t\t\t\tname: author,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tpublisher: siteName\n\t\t\t? {\n\t\t\t\t\t\"@type\": \"Organization\",\n\t\t\t\t\tname: siteName,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tmainEntityOfPage: {\n\t\t\t\"@type\": \"WebPage\",\n\t\t\t\"@id\": page.canonical,\n\t\t},\n\t});\n}\n\n/**\n * Build a WebSite JSON-LD graph from page context.\n * Used for non-article pages (homepage, listing pages, etc.)\n */\nexport function buildWebSiteJsonLd(page: PublicPageContext): Record<string, unknown> | null {\n\tconst siteName = page.siteName;\n\tif (!siteName) return null;\n\n\t// Use configured public origin, falling back to page URL origin\n\tlet siteUrl: string;\n\tif (page.siteUrl) {\n\t\tsiteUrl = page.siteUrl;\n\t} else {\n\t\ttry {\n\t\t\tsiteUrl = new URL(page.url).origin;\n\t\t} catch {\n\t\t\tsiteUrl = page.canonical || page.url;\n\t\t}\n\t}\n\n\treturn cleanJsonLd({\n\t\t\"@context\": \"https://schema.org\",\n\t\t\"@type\": \"WebSite\",\n\t\tname: siteName,\n\t\turl: siteUrl,\n\t});\n}\n","/**\n * Generate base SEO metadata contributions from PublicPageContext.\n *\n * EmDashHead.astro composes the final contribution list as\n * `[...plugin, ...site, ...base]` and feeds it to `resolvePageMetadata()`,\n * which is first-wins. That ordering means plugin contributions override\n * site-level ones override base ones for any given key — base values are\n * the fallback, not the source of truth.\n *\n * This replaces the per-template SEO.astro components, eliminating\n * the class of XSS bugs where templates hand-rolled JSON-LD serialization.\n */\n\nimport type { PageMetadataContribution, PublicPageContext } from \"../plugins/types.js\";\nimport type { SeoSettings } from \"../settings/types.js\";\nimport { buildBlogPostingJsonLd, buildWebSiteJsonLd } from \"./jsonld.js\";\n\n/**\n * Generate base metadata contributions from a page context's SEO data.\n *\n * @param page - Page context produced by the runtime for the current request.\n * @param defaultOgImage - Optional site-wide fallback OG image URL, used when\n * the page has no own OG image (i.e., neither `seo.ogImage` nor `image`).\n * Sourced from `SiteSettings.seo.defaultOgImage` by `EmDashHead`.\n *\n * Returns an empty array if no SEO-relevant data is present.\n */\nexport function generateBaseSeoContributions(\n\tpage: PublicPageContext,\n\tdefaultOgImage?: string | null,\n): PageMetadataContribution[] {\n\tconst contributions: PageMetadataContribution[] = [];\n\n\tconst description = page.description;\n\tconst ogTitle = page.seo?.ogTitle ?? page.pageTitle ?? page.title;\n\tconst ogDescription = page.seo?.ogDescription || description;\n\tconst ogImage = page.seo?.ogImage || page.image || defaultOgImage || null;\n\tconst robots = page.seo?.robots;\n\tconst canonical = page.canonical;\n\tconst siteName = page.siteName;\n\n\t// -- Meta tags --\n\n\tif (description) {\n\t\tcontributions.push({ kind: \"meta\", name: \"description\", content: description });\n\t}\n\n\tif (robots) {\n\t\tcontributions.push({ kind: \"meta\", name: \"robots\", content: robots });\n\t}\n\n\t// -- Canonical link --\n\n\tif (canonical) {\n\t\tcontributions.push({ kind: \"link\", rel: \"canonical\", href: canonical });\n\t}\n\n\t// -- Open Graph --\n\n\tcontributions.push({\n\t\tkind: \"property\",\n\t\tproperty: \"og:type\",\n\t\tcontent: page.pageType === \"article\" ? \"article\" : \"website\",\n\t});\n\n\tif (ogTitle) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:title\", content: ogTitle });\n\t}\n\n\tif (ogDescription) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:description\", content: ogDescription });\n\t}\n\n\tif (ogImage) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:image\", content: ogImage });\n\t}\n\n\tif (canonical) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:url\", content: canonical });\n\t}\n\n\tif (siteName) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:site_name\", content: siteName });\n\t}\n\n\t// -- Twitter Card --\n\n\tcontributions.push({\n\t\tkind: \"meta\",\n\t\tname: \"twitter:card\",\n\t\tcontent: ogImage ? \"summary_large_image\" : \"summary\",\n\t});\n\n\tif (ogTitle) {\n\t\tcontributions.push({ kind: \"meta\", name: \"twitter:title\", content: ogTitle });\n\t}\n\n\tif (ogDescription) {\n\t\tcontributions.push({ kind: \"meta\", name: \"twitter:description\", content: ogDescription });\n\t}\n\n\tif (ogImage) {\n\t\tcontributions.push({ kind: \"meta\", name: \"twitter:image\", content: ogImage });\n\t}\n\n\t// -- Article metadata --\n\n\tif (page.pageType === \"article\" && page.articleMeta) {\n\t\tconst { publishedTime, modifiedTime, author } = page.articleMeta;\n\t\tif (publishedTime) {\n\t\t\tcontributions.push({\n\t\t\t\tkind: \"property\",\n\t\t\t\tproperty: \"article:published_time\",\n\t\t\t\tcontent: publishedTime,\n\t\t\t});\n\t\t}\n\t\tif (modifiedTime) {\n\t\t\tcontributions.push({\n\t\t\t\tkind: \"property\",\n\t\t\t\tproperty: \"article:modified_time\",\n\t\t\t\tcontent: modifiedTime,\n\t\t\t});\n\t\t}\n\t\tif (author) {\n\t\t\tcontributions.push({\n\t\t\t\tkind: \"property\",\n\t\t\t\tproperty: \"article:author\",\n\t\t\t\tcontent: author,\n\t\t\t});\n\t\t}\n\t}\n\n\t// -- JSON-LD --\n\n\tif (page.pageType === \"article\") {\n\t\tconst blogPosting = buildBlogPostingJsonLd(page, defaultOgImage ?? null);\n\t\tif (blogPosting) {\n\t\t\tcontributions.push({ kind: \"jsonld\", id: \"primary\", graph: blogPosting });\n\t\t}\n\t} else if (siteName) {\n\t\tconst webSite = buildWebSiteJsonLd(page);\n\t\tif (webSite) {\n\t\t\tcontributions.push({ kind: \"jsonld\", id: \"primary\", graph: webSite });\n\t\t}\n\t}\n\n\treturn contributions;\n}\n\n/**\n * Generate site-level SEO metadata contributions from SiteSettings.seo.\n *\n * These tags apply to every page (search engine ownership verification),\n * so they're sourced from site settings rather than per-page context.\n * Returns an empty array when no relevant settings are configured.\n */\nexport function generateSiteSeoContributions(\n\tseoSettings: SeoSettings | undefined,\n): PageMetadataContribution[] {\n\tconst contributions: PageMetadataContribution[] = [];\n\n\tif (!seoSettings) {\n\t\treturn contributions;\n\t}\n\n\tif (seoSettings.googleVerification) {\n\t\tcontributions.push({\n\t\t\tkind: \"meta\",\n\t\t\tname: \"google-site-verification\",\n\t\t\tcontent: seoSettings.googleVerification,\n\t\t});\n\t}\n\n\tif (seoSettings.bingVerification) {\n\t\tcontributions.push({\n\t\t\tkind: \"meta\",\n\t\t\tname: \"msvalidate.01\",\n\t\t\tcontent: seoSettings.bingVerification,\n\t\t});\n\t}\n\n\treturn contributions;\n}\n","/**\n * emdash/page — Public page contribution API\n *\n * Template integration points for plugin-driven head metadata\n * and trusted body fragments.\n */\n\nimport type {\n\tPublicPageContext,\n\tPageMetadataContribution,\n\tPageFragmentContribution,\n} from \"../plugins/types.js\";\n\nexport { createPublicPageContext } from \"./context.js\";\nexport type { CreatePublicPageContextInput } from \"./context.js\";\n\nexport {\n\tresolvePageMetadata,\n\trenderPageMetadata,\n\tsafeJsonLdSerialize,\n\tescapeHtmlAttr,\n} from \"./metadata.js\";\nexport type { ResolvedPageMetadata } from \"./metadata.js\";\n\nexport { resolveFragments, renderFragments } from \"./fragments.js\";\n\nexport { generateBaseSeoContributions, generateSiteSeoContributions } from \"./seo-contributions.js\";\nexport { cleanJsonLd, buildBlogPostingJsonLd, buildWebSiteJsonLd } from \"./jsonld.js\";\n\n/**\n * Shape of the EmDash runtime methods used by the render components.\n * Extracted here so all three components share a single type definition.\n */\nexport interface EmDashPageRuntime {\n\tcollectPageMetadata: (page: PublicPageContext) => Promise<PageMetadataContribution[]>;\n\tcollectPageFragments: (page: PublicPageContext) => Promise<PageFragmentContribution[]>;\n}\n\n/**\n * Get the page runtime from Astro locals. Returns undefined when\n * EmDash is not initialized (components render nothing in that case).\n */\nexport function getPageRuntime(locals: Record<string, unknown>): EmDashPageRuntime | undefined {\n\tconst emdash = locals.emdash;\n\tif (\n\t\temdash &&\n\t\ttypeof emdash === \"object\" &&\n\t\t\"collectPageMetadata\" in emdash &&\n\t\t\"collectPageFragments\" in emdash\n\t) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- structural check above confirms presence of required methods\n\t\treturn emdash as EmDashPageRuntime;\n\t}\n\treturn undefined;\n}\n\n// Astro render components are exported from \"emdash/ui\":\n// import { EmDashHead, EmDashBodyStart, EmDashBodyEnd } from \"emdash/ui\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/page/context.ts","../../src/page/metadata.ts","../../src/page/fragments.ts","../../src/page/jsonld.ts","../../src/page/seo-contributions.ts","../../src/page/index.ts"],"sourcesContent":["/**\n * Public page context builder\n *\n * Templates call this to describe the page being rendered.\n * The resulting context is passed to EmDashHead / EmDashBodyStart / EmDashBodyEnd.\n */\n\nimport type { BreadcrumbItem, PublicPageContext } from \"../plugins/types.js\";\n\n/** Fields shared by both input forms */\ninterface PageContextFields {\n\tkind: \"content\" | \"custom\";\n\tpageType?: string;\n\ttitle?: string | null;\n\tpageTitle?: string | null;\n\tdescription?: string | null;\n\tcanonical?: string | null;\n\timage?: string | null;\n\tcontent?: { collection: string; id: string; slug?: string | null };\n\t/** SEO overrides for OG/Twitter meta generation */\n\tseo?: {\n\t\togTitle?: string | null;\n\t\togDescription?: string | null;\n\t\togImage?: string | null;\n\t\trobots?: string | null;\n\t};\n\t/** Article metadata for Open Graph article: tags */\n\tarticleMeta?: {\n\t\tpublishedTime?: string | null;\n\t\tmodifiedTime?: string | null;\n\t\tauthor?: string | null;\n\t};\n\t/** Site name for structured data and og:site_name */\n\tsiteName?: string;\n\t/**\n\t * Breadcrumb trail for this page, root first. Pass an empty array\n\t * to explicitly opt out of breadcrumbs (e.g. homepage), or omit the\n\t * field to let consumers fall back to their own derivation.\n\t */\n\tbreadcrumbs?: BreadcrumbItem[];\n\t/** Public-facing site URL (origin) for structured data */\n\tsiteUrl?: string;\n}\n\n/** Input with Astro global -- used in .astro files */\ninterface AstroInput extends PageContextFields {\n\tAstro: { url: URL; currentLocale?: string };\n}\n\n/** Input with explicit URL -- used outside .astro files */\ninterface UrlInput extends PageContextFields {\n\turl: URL | string;\n\tlocale?: string;\n}\n\nexport type CreatePublicPageContextInput = AstroInput | UrlInput;\n\nfunction isAstroInput(input: CreatePublicPageContextInput): input is AstroInput {\n\treturn \"Astro\" in input;\n}\n\n/**\n * Build a PublicPageContext from template input.\n */\nexport function createPublicPageContext(input: CreatePublicPageContextInput): PublicPageContext {\n\tlet url: string;\n\tlet path: string;\n\tlet locale: string | null;\n\n\tif (isAstroInput(input)) {\n\t\turl = input.Astro.url.href;\n\t\tpath = input.Astro.url.pathname;\n\t\tlocale = input.Astro.currentLocale ?? null;\n\t} else {\n\t\tconst parsed = typeof input.url === \"string\" ? new URL(input.url) : input.url;\n\t\turl = parsed.href;\n\t\tpath = parsed.pathname;\n\t\tlocale = input.locale ?? null;\n\t}\n\n\treturn {\n\t\turl,\n\t\tpath,\n\t\tlocale,\n\t\tkind: input.kind,\n\t\tpageType: input.pageType ?? (input.kind === \"content\" ? \"article\" : \"website\"),\n\t\ttitle: input.title ?? null,\n\t\tpageTitle: input.pageTitle ?? null,\n\t\tdescription: input.description ?? null,\n\t\tcanonical: input.canonical ?? null,\n\t\timage: input.image ?? null,\n\t\tcontent: input.content\n\t\t\t? {\n\t\t\t\t\tcollection: input.content.collection,\n\t\t\t\t\tid: input.content.id,\n\t\t\t\t\tslug: input.content.slug ?? null,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tseo: input.seo,\n\t\tarticleMeta: input.articleMeta,\n\t\tsiteName: input.siteName,\n\t\tbreadcrumbs: input.breadcrumbs,\n\t\tsiteUrl: input.siteUrl,\n\t};\n}\n","/**\n * Page metadata collection and rendering\n *\n * Collects typed metadata contributions from plugins via the page:metadata hook,\n * validates them, and resolves them into a deduplicated structure ready to render.\n */\n\nimport type { PageMetadataContribution, PageMetadataLinkRel } from \"../plugins/types.js\";\n\n// ── Resolved output ─────────────────────────────────────────────\n\nexport interface ResolvedPageMetadata {\n\tmeta: Array<{ name: string; content: string }>;\n\tproperties: Array<{ property: string; content: string }>;\n\tlinks: Array<{\n\t\trel: PageMetadataLinkRel;\n\t\thref: string;\n\t\threflang?: string;\n\t}>;\n\tjsonld: Array<{ id?: string; json: string }>;\n}\n\ninterface RenderPageMetadataOptions {\n\tincludeJsonLd?: boolean;\n}\n\n// ── Validation ──────────────────────────────────────────────────\n\n/** Schemes safe for use in link href attributes */\nconst SAFE_HREF_RE = /^(https?|at):\\/\\//i;\nconst HTML_ESCAPE_MAP: Record<string, string> = {\n\t\"&\": \"&\",\n\t\"<\": \"<\",\n\t\">\": \">\",\n\t'\"': \""\",\n\t\"'\": \"'\",\n};\nconst HTML_ESCAPE_RE = /[&<>\"']/g;\n\n/** Escape a string for safe use in an HTML attribute value */\nexport function escapeHtmlAttr(value: string): string {\n\treturn value.replace(HTML_ESCAPE_RE, (ch) => HTML_ESCAPE_MAP[ch] ?? ch);\n}\n\n/** Validate that a URL uses a safe scheme (http, https, at) */\nfunction isSafeHref(url: string): boolean {\n\treturn SAFE_HREF_RE.test(url);\n}\n\n// ── JSON-LD serialization ───────────────────────────────────────\n\nconst JSONLD_LT_RE = /</g;\nconst JSONLD_GT_RE = />/g;\nconst JSONLD_U2028_RE = /\\u2028/g;\nconst JSONLD_U2029_RE = /\\u2029/g;\n\n/**\n * Safely serialize a value for embedding in a <script type=\"application/ld+json\"> tag.\n *\n * Plain JSON.stringify is not sufficient because:\n * - \"</script>\" in a nested string breaks out of the script tag\n * - \"<!--\" can open an HTML comment\n * - U+2028/U+2029 are line terminators in some JS engines\n */\nexport function safeJsonLdSerialize(value: unknown): string {\n\treturn JSON.stringify(value)\n\t\t.replace(JSONLD_LT_RE, \"\\\\u003c\")\n\t\t.replace(JSONLD_GT_RE, \"\\\\u003e\")\n\t\t.replace(JSONLD_U2028_RE, \"\\\\u2028\")\n\t\t.replace(JSONLD_U2029_RE, \"\\\\u2029\");\n}\n\nexport async function createSha256CspHash(value: string): Promise<string> {\n\tconst digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(value));\n\tconst bytes = new Uint8Array(digest);\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn `sha256-${btoa(binary)}`;\n}\n\n// ── Merge / dedupe ──────────────────────────────────────────────\n\n/**\n * Resolve a flat list of contributions into deduplicated metadata.\n * First contribution wins for any given dedupe key.\n */\nexport function resolvePageMetadata(\n\tcontributions: PageMetadataContribution[],\n): ResolvedPageMetadata {\n\tconst result: ResolvedPageMetadata = {\n\t\tmeta: [],\n\t\tproperties: [],\n\t\tlinks: [],\n\t\tjsonld: [],\n\t};\n\n\tconst seenMeta = new Set<string>();\n\tconst seenProperties = new Set<string>();\n\tconst seenLinks = new Set<string>();\n\tconst seenJsonLd = new Set<string>();\n\n\tfor (const c of contributions) {\n\t\tswitch (c.kind) {\n\t\t\tcase \"meta\": {\n\t\t\t\tconst dedupeKey = c.key ?? c.name;\n\t\t\t\tif (seenMeta.has(dedupeKey)) continue;\n\t\t\t\tseenMeta.add(dedupeKey);\n\t\t\t\tresult.meta.push({ name: c.name, content: c.content });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"property\": {\n\t\t\t\tconst dedupeKey = c.key ?? c.property;\n\t\t\t\tif (seenProperties.has(dedupeKey)) continue;\n\t\t\t\tseenProperties.add(dedupeKey);\n\t\t\t\tresult.properties.push({\n\t\t\t\t\tproperty: c.property,\n\t\t\t\t\tcontent: c.content,\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"link\": {\n\t\t\t\tif (!isSafeHref(c.href)) {\n\t\t\t\t\tif (import.meta.env?.DEV) {\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[page:metadata] Rejected link contribution with unsafe href scheme: ${c.href}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (c.rel === \"canonical\") {\n\t\t\t\t\tif (seenLinks.has(\"canonical\")) continue;\n\t\t\t\t\tseenLinks.add(\"canonical\");\n\t\t\t\t} else {\n\t\t\t\t\tconst dedupeKey = c.key ?? c.hreflang ?? c.href;\n\t\t\t\t\tif (seenLinks.has(dedupeKey)) continue;\n\t\t\t\t\tseenLinks.add(dedupeKey);\n\t\t\t\t}\n\t\t\t\tresult.links.push({\n\t\t\t\t\trel: c.rel,\n\t\t\t\t\thref: c.href,\n\t\t\t\t\t...(c.hreflang && { hreflang: c.hreflang }),\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"jsonld\": {\n\t\t\t\tif (c.id) {\n\t\t\t\t\tif (seenJsonLd.has(c.id)) continue;\n\t\t\t\t\tseenJsonLd.add(c.id);\n\t\t\t\t}\n\t\t\t\tresult.jsonld.push({\n\t\t\t\t\tid: c.id,\n\t\t\t\t\tjson: safeJsonLdSerialize(c.graph),\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\t// Unknown contribution kind -- skip silently at runtime.\n\t\t\t\t// TypeScript catches this at compile time for typed callers,\n\t\t\t\t// but sandboxed plugins may return unexpected shapes.\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n// ── HTML rendering ──────────────────────────────────────────────\n\n/** Render resolved metadata to an HTML string for embedding in <head> */\nexport function renderPageMetadata(\n\tmetadata: ResolvedPageMetadata,\n\toptions: RenderPageMetadataOptions = {},\n): string {\n\tconst parts: string[] = [];\n\tconst includeJsonLd = options.includeJsonLd ?? true;\n\n\tfor (const m of metadata.meta) {\n\t\tparts.push(`<meta name=\"${escapeHtmlAttr(m.name)}\" content=\"${escapeHtmlAttr(m.content)}\">`);\n\t}\n\n\tfor (const p of metadata.properties) {\n\t\tparts.push(\n\t\t\t`<meta property=\"${escapeHtmlAttr(p.property)}\" content=\"${escapeHtmlAttr(p.content)}\">`,\n\t\t);\n\t}\n\n\tfor (const l of metadata.links) {\n\t\tlet tag = `<link rel=\"${escapeHtmlAttr(l.rel)}\" href=\"${escapeHtmlAttr(l.href)}\"`;\n\t\tif (l.hreflang) {\n\t\t\ttag += ` hreflang=\"${escapeHtmlAttr(l.hreflang)}\"`;\n\t\t}\n\t\ttag += \">\";\n\t\tparts.push(tag);\n\t}\n\n\tif (includeJsonLd) {\n\t\tfor (const j of metadata.jsonld) {\n\t\t\tparts.push(`<script type=\"application/ld+json\">${j.json}</script>`);\n\t\t}\n\t}\n\n\treturn parts.join(\"\\n\");\n}\n","/**\n * Page fragment collection and rendering\n *\n * Collects raw markup / script contributions from trusted plugins via\n * the page:fragments hook. Sandboxed plugins are never invoked.\n */\n\nimport type { PageFragmentContribution, PagePlacement } from \"../plugins/types.js\";\nimport { escapeHtmlAttr } from \"./metadata.js\";\n\n/** Escape sequences that would break out of a script tag */\nconst SCRIPT_CLOSE_RE = /<\\//g;\n\n// ── Dedupe and filter ───────────────────────────────────────────\n\n/**\n * Filter contributions to a specific placement and deduplicate.\n * - Contributions with the same `key + placement` are deduped (first wins).\n * - External scripts with the same `src + placement` are deduped.\n */\nexport function resolveFragments(\n\tcontributions: PageFragmentContribution[],\n\tplacement: PagePlacement,\n): PageFragmentContribution[] {\n\tconst filtered = contributions.filter((c) => c.placement === placement);\n\tconst seen = new Set<string>();\n\tconst result: PageFragmentContribution[] = [];\n\n\tfor (const c of filtered) {\n\t\t// Key-based dedupe\n\t\tif (c.key) {\n\t\t\tconst dedupeKey = `key:${c.key}`;\n\t\t\tif (seen.has(dedupeKey)) continue;\n\t\t\tseen.add(dedupeKey);\n\t\t} else if (c.kind === \"external-script\") {\n\t\t\tconst dedupeKey = `src:${c.src}`;\n\t\t\tif (seen.has(dedupeKey)) continue;\n\t\t\tseen.add(dedupeKey);\n\t\t}\n\n\t\tresult.push(c);\n\t}\n\n\treturn result;\n}\n\n// ── HTML rendering ──────────────────────────────────────────────\n\nconst EVENT_HANDLER_RE = /^on/i;\n\nfunction renderAttributes(attrs: Record<string, string>): string {\n\treturn Object.entries(attrs)\n\t\t.filter(([k]) => !EVENT_HANDLER_RE.test(k))\n\t\t.map(([k, v]) => ` ${escapeHtmlAttr(k)}=\"${escapeHtmlAttr(v)}\"`)\n\t\t.join(\"\");\n}\n\n/** Render a single fragment contribution to HTML */\nfunction renderFragment(c: PageFragmentContribution): string {\n\tswitch (c.kind) {\n\t\tcase \"external-script\": {\n\t\t\tlet tag = `<script src=\"${escapeHtmlAttr(c.src)}\"`;\n\t\t\tif (c.async) tag += \" async\";\n\t\t\tif (c.defer) tag += \" defer\";\n\t\t\tif (c.attributes) tag += renderAttributes(c.attributes);\n\t\t\ttag += \"></script>\";\n\t\t\treturn tag;\n\t\t}\n\t\tcase \"inline-script\": {\n\t\t\tlet tag = \"<script\";\n\t\t\tif (c.attributes) tag += renderAttributes(c.attributes);\n\t\t\t// Escape </ to <\\/ to prevent breaking out of the script tag.\n\t\t\t// This is valid JS and protects against code built from user data.\n\t\t\ttag += `>${c.code.replace(SCRIPT_CLOSE_RE, \"<\\\\/\")}</script>`;\n\t\t\treturn tag;\n\t\t}\n\t\tcase \"html\":\n\t\t\treturn c.html;\n\t}\n}\n\n/** Render a list of fragment contributions to an HTML string */\nexport function renderFragments(\n\tcontributions: PageFragmentContribution[],\n\tplacement: PagePlacement,\n): string {\n\tconst resolved = resolveFragments(contributions, placement);\n\treturn resolved.map(renderFragment).join(\"\\n\");\n}\n","/**\n * JSON-LD structured data builders\n *\n * Moved from template SEO.astro components into core so all JSON-LD\n * is serialized via safeJsonLdSerialize() and never hand-rolled in templates.\n */\n\nimport type { PublicPageContext } from \"../plugins/types.js\";\n\n/**\n * Remove null/undefined values from a JSON-LD object recursively.\n * JSON-LD validators prefer absent keys over null values.\n */\nexport function cleanJsonLd(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst cleaned: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (value !== undefined && value !== null) {\n\t\t\tif (typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- non-null, non-array object is safely treated as Record<string, unknown> for JSON-LD traversal\n\t\t\t\tcleaned[key] = cleanJsonLd(value as Record<string, unknown>);\n\t\t\t} else {\n\t\t\t\tcleaned[key] = value;\n\t\t\t}\n\t\t}\n\t}\n\treturn cleaned;\n}\n\n/**\n * Build a BlogPosting JSON-LD graph from page context.\n * Used for article-type content pages.\n *\n * @param page - Page context for the current request.\n * @param defaultOgImage - Optional site-wide fallback image URL, used when\n * the page has no own OG image. Matches the fallback applied to `og:image`\n * in `generateBaseSeoContributions`.\n */\nexport function buildBlogPostingJsonLd(\n\tpage: PublicPageContext,\n\tdefaultOgImage?: string | null,\n): Record<string, unknown> | null {\n\tif (page.pageType !== \"article\" || !page.canonical) return null;\n\n\tconst ogTitle = page.seo?.ogTitle ?? page.pageTitle ?? page.title;\n\tconst description = page.seo?.ogDescription || page.description;\n\tconst ogImage = page.seo?.ogImage || page.image || defaultOgImage || null;\n\tconst publishedTime = page.articleMeta?.publishedTime;\n\tconst modifiedTime = page.articleMeta?.modifiedTime;\n\tconst author = page.articleMeta?.author;\n\tconst siteName = page.siteName;\n\n\treturn cleanJsonLd({\n\t\t\"@context\": \"https://schema.org\",\n\t\t\"@type\": \"BlogPosting\",\n\t\theadline: ogTitle,\n\t\tdescription,\n\t\timage: ogImage || undefined,\n\t\turl: page.canonical,\n\t\tdatePublished: publishedTime || undefined,\n\t\tdateModified: modifiedTime || publishedTime || undefined,\n\t\tauthor: author\n\t\t\t? {\n\t\t\t\t\t\"@type\": \"Person\",\n\t\t\t\t\tname: author,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tpublisher: siteName\n\t\t\t? {\n\t\t\t\t\t\"@type\": \"Organization\",\n\t\t\t\t\tname: siteName,\n\t\t\t\t}\n\t\t\t: undefined,\n\t\tmainEntityOfPage: {\n\t\t\t\"@type\": \"WebPage\",\n\t\t\t\"@id\": page.canonical,\n\t\t},\n\t});\n}\n\n/**\n * Build a WebSite JSON-LD graph from page context.\n * Used for non-article pages (homepage, listing pages, etc.)\n */\nexport function buildWebSiteJsonLd(page: PublicPageContext): Record<string, unknown> | null {\n\tconst siteName = page.siteName;\n\tif (!siteName) return null;\n\n\t// Use configured public origin, falling back to page URL origin\n\tlet siteUrl: string;\n\tif (page.siteUrl) {\n\t\tsiteUrl = page.siteUrl;\n\t} else {\n\t\ttry {\n\t\t\tsiteUrl = new URL(page.url).origin;\n\t\t} catch {\n\t\t\tsiteUrl = page.canonical || page.url;\n\t\t}\n\t}\n\n\treturn cleanJsonLd({\n\t\t\"@context\": \"https://schema.org\",\n\t\t\"@type\": \"WebSite\",\n\t\tname: siteName,\n\t\turl: siteUrl,\n\t});\n}\n","/**\n * Generate base SEO metadata contributions from PublicPageContext.\n *\n * EmDashHead.astro composes the final contribution list as\n * `[...plugin, ...site, ...base]` and feeds it to `resolvePageMetadata()`,\n * which is first-wins. That ordering means plugin contributions override\n * site-level ones override base ones for any given key — base values are\n * the fallback, not the source of truth.\n *\n * This replaces the per-template SEO.astro components, eliminating\n * the class of XSS bugs where templates hand-rolled JSON-LD serialization.\n */\n\nimport type { PageMetadataContribution, PublicPageContext } from \"../plugins/types.js\";\nimport type { SeoSettings } from \"../settings/types.js\";\nimport { buildBlogPostingJsonLd, buildWebSiteJsonLd } from \"./jsonld.js\";\n\n/**\n * Generate base metadata contributions from a page context's SEO data.\n *\n * @param page - Page context produced by the runtime for the current request.\n * @param defaultOgImage - Optional site-wide fallback OG image URL, used when\n * the page has no own OG image (i.e., neither `seo.ogImage` nor `image`).\n * Sourced from `SiteSettings.seo.defaultOgImage` by `EmDashHead`.\n *\n * Returns an empty array if no SEO-relevant data is present.\n */\nexport function generateBaseSeoContributions(\n\tpage: PublicPageContext,\n\tdefaultOgImage?: string | null,\n): PageMetadataContribution[] {\n\tconst contributions: PageMetadataContribution[] = [];\n\n\tconst description = page.description;\n\tconst ogTitle = page.seo?.ogTitle ?? page.pageTitle ?? page.title;\n\tconst ogDescription = page.seo?.ogDescription || description;\n\tconst ogImage = page.seo?.ogImage || page.image || defaultOgImage || null;\n\tconst robots = page.seo?.robots;\n\tconst canonical = page.canonical;\n\tconst siteName = page.siteName;\n\n\t// -- Meta tags --\n\n\tif (description) {\n\t\tcontributions.push({ kind: \"meta\", name: \"description\", content: description });\n\t}\n\n\tif (robots) {\n\t\tcontributions.push({ kind: \"meta\", name: \"robots\", content: robots });\n\t}\n\n\t// -- Canonical link --\n\n\tif (canonical) {\n\t\tcontributions.push({ kind: \"link\", rel: \"canonical\", href: canonical });\n\t}\n\n\t// -- Open Graph --\n\n\tcontributions.push({\n\t\tkind: \"property\",\n\t\tproperty: \"og:type\",\n\t\tcontent: page.pageType === \"article\" ? \"article\" : \"website\",\n\t});\n\n\tif (ogTitle) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:title\", content: ogTitle });\n\t}\n\n\tif (ogDescription) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:description\", content: ogDescription });\n\t}\n\n\tif (ogImage) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:image\", content: ogImage });\n\t}\n\n\tif (canonical) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:url\", content: canonical });\n\t}\n\n\tif (siteName) {\n\t\tcontributions.push({ kind: \"property\", property: \"og:site_name\", content: siteName });\n\t}\n\n\t// -- Twitter Card --\n\n\tcontributions.push({\n\t\tkind: \"meta\",\n\t\tname: \"twitter:card\",\n\t\tcontent: ogImage ? \"summary_large_image\" : \"summary\",\n\t});\n\n\tif (ogTitle) {\n\t\tcontributions.push({ kind: \"meta\", name: \"twitter:title\", content: ogTitle });\n\t}\n\n\tif (ogDescription) {\n\t\tcontributions.push({ kind: \"meta\", name: \"twitter:description\", content: ogDescription });\n\t}\n\n\tif (ogImage) {\n\t\tcontributions.push({ kind: \"meta\", name: \"twitter:image\", content: ogImage });\n\t}\n\n\t// -- Article metadata --\n\n\tif (page.pageType === \"article\" && page.articleMeta) {\n\t\tconst { publishedTime, modifiedTime, author } = page.articleMeta;\n\t\tif (publishedTime) {\n\t\t\tcontributions.push({\n\t\t\t\tkind: \"property\",\n\t\t\t\tproperty: \"article:published_time\",\n\t\t\t\tcontent: publishedTime,\n\t\t\t});\n\t\t}\n\t\tif (modifiedTime) {\n\t\t\tcontributions.push({\n\t\t\t\tkind: \"property\",\n\t\t\t\tproperty: \"article:modified_time\",\n\t\t\t\tcontent: modifiedTime,\n\t\t\t});\n\t\t}\n\t\tif (author) {\n\t\t\tcontributions.push({\n\t\t\t\tkind: \"property\",\n\t\t\t\tproperty: \"article:author\",\n\t\t\t\tcontent: author,\n\t\t\t});\n\t\t}\n\t}\n\n\t// -- JSON-LD --\n\n\tif (page.pageType === \"article\") {\n\t\tconst blogPosting = buildBlogPostingJsonLd(page, defaultOgImage ?? null);\n\t\tif (blogPosting) {\n\t\t\tcontributions.push({ kind: \"jsonld\", id: \"primary\", graph: blogPosting });\n\t\t}\n\t} else if (siteName) {\n\t\tconst webSite = buildWebSiteJsonLd(page);\n\t\tif (webSite) {\n\t\t\tcontributions.push({ kind: \"jsonld\", id: \"primary\", graph: webSite });\n\t\t}\n\t}\n\n\treturn contributions;\n}\n\n/**\n * Generate site-level SEO metadata contributions from SiteSettings.seo.\n *\n * These tags apply to every page (search engine ownership verification),\n * so they're sourced from site settings rather than per-page context.\n * Returns an empty array when no relevant settings are configured.\n */\nexport function generateSiteSeoContributions(\n\tseoSettings: SeoSettings | undefined,\n): PageMetadataContribution[] {\n\tconst contributions: PageMetadataContribution[] = [];\n\n\tif (!seoSettings) {\n\t\treturn contributions;\n\t}\n\n\tif (seoSettings.googleVerification) {\n\t\tcontributions.push({\n\t\t\tkind: \"meta\",\n\t\t\tname: \"google-site-verification\",\n\t\t\tcontent: seoSettings.googleVerification,\n\t\t});\n\t}\n\n\tif (seoSettings.bingVerification) {\n\t\tcontributions.push({\n\t\t\tkind: \"meta\",\n\t\t\tname: \"msvalidate.01\",\n\t\t\tcontent: seoSettings.bingVerification,\n\t\t});\n\t}\n\n\treturn contributions;\n}\n","/**\n * emdash/page — Public page contribution API\n *\n * Template integration points for plugin-driven head metadata\n * and trusted body fragments.\n */\n\nimport type {\n\tPublicPageContext,\n\tPageMetadataContribution,\n\tPageFragmentContribution,\n} from \"../plugins/types.js\";\n\nexport { createPublicPageContext } from \"./context.js\";\nexport type { CreatePublicPageContextInput } from \"./context.js\";\n\nexport {\n\tresolvePageMetadata,\n\trenderPageMetadata,\n\tsafeJsonLdSerialize,\n\tescapeHtmlAttr,\n} from \"./metadata.js\";\nexport type { ResolvedPageMetadata } from \"./metadata.js\";\n\nexport { resolveFragments, renderFragments } from \"./fragments.js\";\n\nexport { generateBaseSeoContributions, generateSiteSeoContributions } from \"./seo-contributions.js\";\nexport { cleanJsonLd, buildBlogPostingJsonLd, buildWebSiteJsonLd } from \"./jsonld.js\";\n\n/**\n * Shape of the EmDash runtime methods used by the render components.\n * Extracted here so all three components share a single type definition.\n */\nexport interface EmDashPageRuntime {\n\tcollectPageMetadata: (page: PublicPageContext) => Promise<PageMetadataContribution[]>;\n\tcollectPageFragments: (page: PublicPageContext) => Promise<PageFragmentContribution[]>;\n}\n\n/**\n * Get the page runtime from Astro locals. Returns undefined when\n * EmDash is not initialized (components render nothing in that case).\n */\nexport function getPageRuntime(locals: Record<string, unknown>): EmDashPageRuntime | undefined {\n\tconst emdash = locals.emdash;\n\tif (\n\t\temdash &&\n\t\ttypeof emdash === \"object\" &&\n\t\t\"collectPageMetadata\" in emdash &&\n\t\t\"collectPageFragments\" in emdash\n\t) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- structural check above confirms presence of required methods\n\t\treturn emdash as EmDashPageRuntime;\n\t}\n\treturn undefined;\n}\n\n// Astro render components are exported from \"emdash/ui\":\n// import { EmDashHead, EmDashBodyStart, EmDashBodyEnd } from \"emdash/ui\";\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as apiError } from "./error-
|
|
1
|
+
import { t as apiError } from "./error-rVGiqWDt.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/api/parse.ts
|
|
@@ -86,4 +86,4 @@ function isParseError(result) {
|
|
|
86
86
|
|
|
87
87
|
//#endregion
|
|
88
88
|
export { parseQuery as i, parseBody as n, parseOptionalBody as r, isParseError as t };
|
|
89
|
-
//# sourceMappingURL=parse-
|
|
89
|
+
//# sourceMappingURL=parse-BwAdS7FZ.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-
|
|
1
|
+
{"version":3,"file":"parse-BwAdS7FZ.mjs","names":[],"sources":["../src/api/parse.ts"],"sourcesContent":["/**\n * Request body and query parameter parsing with Zod validation.\n *\n * All API routes should use these utilities instead of `request.json() as T`\n * or raw `url.searchParams.get()` with manual coercion.\n */\n\nimport { z } from \"zod\";\n\nimport { apiError } from \"./error.js\";\n\n/** Maximum allowed JSON request body size (10 MB). */\nconst MAX_BODY_SIZE = 10 * 1024 * 1024;\n\n/**\n * Result of parsing: either the validated data or an error Response.\n * Routes should check `if (result instanceof Response) return result;`\n */\nexport type ParseResult<T> = T | Response;\n\n/**\n * Parse and validate a JSON request body against a Zod schema.\n *\n * Returns the validated data on success, or a 400 Response on failure.\n * Replaces all `(await request.json()) as T` casts.\n */\nexport async function parseBody<T extends z.ZodType>(\n\trequest: Request,\n\tschema: T,\n): Promise<ParseResult<z.infer<T>>> {\n\t// Best-effort size check via Content-Length (can be absent with chunked encoding)\n\tconst contentLength = request.headers.get(\"Content-Length\");\n\tif (contentLength && parseInt(contentLength, 10) > MAX_BODY_SIZE) {\n\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Request body too large\", 413);\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = await request.json();\n\t} catch {\n\t\treturn apiError(\"INVALID_JSON\", \"Request body must be valid JSON\", 400);\n\t}\n\n\treturn validate(schema, raw);\n}\n\n/**\n * Parse and validate an optional JSON request body.\n *\n * Returns `defaultValue` if the body is empty, or the validated data if present.\n * For endpoints where the body is optional (e.g., preview-url, confirm).\n */\nexport async function parseOptionalBody<T extends z.ZodType>(\n\trequest: Request,\n\tschema: T,\n\tdefaultValue: z.infer<T>,\n): Promise<ParseResult<z.infer<T>>> {\n\t// Best-effort size check via Content-Length (can be absent with chunked encoding)\n\tconst contentLength = request.headers.get(\"Content-Length\");\n\tif (contentLength && parseInt(contentLength, 10) > MAX_BODY_SIZE) {\n\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Request body too large\", 413);\n\t}\n\n\tlet text: string;\n\ttry {\n\t\ttext = await request.text();\n\t} catch {\n\t\treturn defaultValue;\n\t}\n\n\tif (!text.trim()) {\n\t\treturn defaultValue;\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = JSON.parse(text);\n\t} catch {\n\t\treturn apiError(\"INVALID_JSON\", \"Request body must be valid JSON\", 400);\n\t}\n\n\treturn validate(schema, raw);\n}\n\n/**\n * Parse and validate URL search params against a Zod schema.\n *\n * Converts searchParams to a plain object before validation.\n * Zod coercion handles string -> number/boolean conversion.\n * Replaces manual `url.searchParams.get()` + `parseInt()` patterns.\n */\nexport function parseQuery<T extends z.ZodType>(url: URL, schema: T): ParseResult<z.infer<T>> {\n\tconst raw: Record<string, string> = {};\n\tfor (const [key, value] of url.searchParams) {\n\t\traw[key] = value;\n\t}\n\treturn validate(schema, raw);\n}\n\n/**\n * Validate raw data against a schema. Returns data or error Response.\n */\nfunction validate<T extends z.ZodType>(schema: T, data: unknown): ParseResult<z.infer<T>> {\n\tconst result = schema.safeParse(data);\n\n\tif (result.success) {\n\t\treturn result.data as z.infer<T>;\n\t}\n\n\t// Format Zod errors into a readable structure\n\tconst issues = result.error.issues.map((issue: z.ZodIssue) => ({\n\t\tpath: issue.path.join(\".\"),\n\t\tmessage: issue.message,\n\t}));\n\n\treturn Response.json(\n\t\t{\n\t\t\terror: {\n\t\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\t\tmessage: \"Invalid request data\",\n\t\t\t\tdetails: { issues },\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tstatus: 400,\n\t\t\theaders: {\n\t\t\t\t\"Cache-Control\": \"private, no-store\",\n\t\t\t},\n\t\t},\n\t);\n}\n\n/**\n * Type guard to check if a ParseResult is an error Response.\n * Usage: `if (isParseError(result)) return result;`\n */\nexport function isParseError<T>(result: ParseResult<T>): result is Response {\n\treturn result instanceof Response;\n}\n"],"mappings":";;;;;AAYA,MAAM,gBAAgB,KAAK,OAAO;;;;;;;AAclC,eAAsB,UACrB,SACA,QACmC;CAEnC,MAAM,gBAAgB,QAAQ,QAAQ,IAAI,iBAAiB;AAC3D,KAAI,iBAAiB,SAAS,eAAe,GAAG,GAAG,cAClD,QAAO,SAAS,qBAAqB,0BAA0B,IAAI;CAGpE,IAAI;AACJ,KAAI;AACH,QAAM,MAAM,QAAQ,MAAM;SACnB;AACP,SAAO,SAAS,gBAAgB,mCAAmC,IAAI;;AAGxE,QAAO,SAAS,QAAQ,IAAI;;;;;;;;AAS7B,eAAsB,kBACrB,SACA,QACA,cACmC;CAEnC,MAAM,gBAAgB,QAAQ,QAAQ,IAAI,iBAAiB;AAC3D,KAAI,iBAAiB,SAAS,eAAe,GAAG,GAAG,cAClD,QAAO,SAAS,qBAAqB,0BAA0B,IAAI;CAGpE,IAAI;AACJ,KAAI;AACH,SAAO,MAAM,QAAQ,MAAM;SACpB;AACP,SAAO;;AAGR,KAAI,CAAC,KAAK,MAAM,CACf,QAAO;CAGR,IAAI;AACJ,KAAI;AACH,QAAM,KAAK,MAAM,KAAK;SACf;AACP,SAAO,SAAS,gBAAgB,mCAAmC,IAAI;;AAGxE,QAAO,SAAS,QAAQ,IAAI;;;;;;;;;AAU7B,SAAgB,WAAgC,KAAU,QAAoC;CAC7F,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,KAAK,UAAU,IAAI,aAC9B,KAAI,OAAO;AAEZ,QAAO,SAAS,QAAQ,IAAI;;;;;AAM7B,SAAS,SAA8B,QAAW,MAAwC;CACzF,MAAM,SAAS,OAAO,UAAU,KAAK;AAErC,KAAI,OAAO,QACV,QAAO,OAAO;CAIf,MAAM,SAAS,OAAO,MAAM,OAAO,KAAK,WAAuB;EAC9D,MAAM,MAAM,KAAK,KAAK,IAAI;EAC1B,SAAS,MAAM;EACf,EAAE;AAEH,QAAO,SAAS,KACf,EACC,OAAO;EACN,MAAM;EACN,SAAS;EACT,SAAS,EAAE,QAAQ;EACnB,EACD,EACD;EACC,QAAQ;EACR,SAAS,EACR,iBAAiB,qBACjB;EACD,CACD;;;;;;AAOF,SAAgB,aAAgB,QAA4C;AAC3E,QAAO,kBAAkB"}
|
|
@@ -75,6 +75,10 @@ interface MediaProviderItem {
|
|
|
75
75
|
/** Dimensions (for images/video) */
|
|
76
76
|
width?: number;
|
|
77
77
|
height?: number;
|
|
78
|
+
/** LQIP blurhash placeholder (images only) */
|
|
79
|
+
blurhash?: string;
|
|
80
|
+
/** LQIP dominant-color placeholder, as a CSS color (images only) */
|
|
81
|
+
dominantColor?: string;
|
|
78
82
|
/** Accessibility text */
|
|
79
83
|
alt?: string;
|
|
80
84
|
/** Preview URL for admin UI thumbnail */
|
|
@@ -112,6 +116,10 @@ interface ImageEmbed {
|
|
|
112
116
|
sizes?: string;
|
|
113
117
|
width?: number;
|
|
114
118
|
height?: number;
|
|
119
|
+
/** LQIP blurhash placeholder for rendering before the image loads */
|
|
120
|
+
blurhash?: string;
|
|
121
|
+
/** LQIP dominant-color placeholder, as a CSS color */
|
|
122
|
+
dominantColor?: string;
|
|
115
123
|
alt?: string;
|
|
116
124
|
/** Base URL without transforms, for responsive image generation */
|
|
117
125
|
cdnBaseUrl?: string;
|
|
@@ -234,6 +242,10 @@ interface MediaValue {
|
|
|
234
242
|
mimeType?: string;
|
|
235
243
|
width?: number;
|
|
236
244
|
height?: number;
|
|
245
|
+
/** Cached LQIP blurhash placeholder (images only) */
|
|
246
|
+
blurhash?: string;
|
|
247
|
+
/** Cached LQIP dominant-color placeholder, as a CSS color (images only) */
|
|
248
|
+
dominantColor?: string;
|
|
237
249
|
alt?: string;
|
|
238
250
|
/** Provider-specific data needed for embedding */
|
|
239
251
|
meta?: Record<string, unknown>;
|
|
@@ -271,10 +283,13 @@ interface PlaceholderData {
|
|
|
271
283
|
* Generate blurhash and dominant color from an image buffer.
|
|
272
284
|
* Returns null for non-image MIME types or on failure.
|
|
273
285
|
*
|
|
274
|
-
* @param dimensions - Optional pre-known dimensions.
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
286
|
+
* @param dimensions - Optional pre-known dimensions. When present they are
|
|
287
|
+
* trusted verbatim (the caller has typically already read them via
|
|
288
|
+
* readDimensions); otherwise dimensions are read from this buffer's header.
|
|
289
|
+
* Generation is skipped (returns null) when no dimensions are available at
|
|
290
|
+
* all, or when the decoded size (width * height * 4) exceeds
|
|
291
|
+
* MAX_DECODED_BYTES — both guards avoid OOM from unbounded decodes on
|
|
292
|
+
* memory-constrained runtimes.
|
|
278
293
|
*/
|
|
279
294
|
declare function generatePlaceholder(buffer: Uint8Array, mimeType: string, dimensions?: {
|
|
280
295
|
width: number;
|
|
@@ -282,4 +297,4 @@ declare function generatePlaceholder(buffer: Uint8Array, mimeType: string, dimen
|
|
|
282
297
|
}): Promise<PlaceholderData | null>;
|
|
283
298
|
//#endregion
|
|
284
299
|
export { MediaValue as _, ComponentEmbed as a, mediaItemToValue as b, EmbedResult as c, MediaListResult as d, MediaProvider as f, MediaUploadInput as g, MediaProviderItem as h, AudioEmbed as i, ImageEmbed as l, MediaProviderDescriptor as m, generatePlaceholder as n, CreateMediaProviderFn as o, MediaProviderCapabilities as p, normalizeMediaValue as r, EmbedOptions as s, PlaceholderData as t, MediaListOptions as u, ThumbnailOptions as v, VideoEmbed as y };
|
|
285
|
-
//# sourceMappingURL=placeholder-
|
|
300
|
+
//# sourceMappingURL=placeholder-Cuce9U-m.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"placeholder-Cuce9U-m.d.mts","names":[],"sources":["../src/media/types.ts","../src/media/normalize.ts","../src/media/placeholder.ts"],"mappings":"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"}
|
package/dist/plugin-types.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as ContentHookEvent, D as ContentStateChangeEvent, E as ContentScheduleStateChangeEvent, F as EmailBeforeSendHandler, G as LifecycleHandler, I as EmailDeliverEvent, J as MediaAfterUploadEvent, L as EmailDeliverHandler, M as EmailAfterSendEvent, Mt as UninstallHandler, N as EmailAfterSendHandler, O as CronEvent, P as EmailBeforeSendEvent, Q as MediaUploadEvent, S as ContentDeleteEvent, T as ContentRestoreStateChangeEvent, W as LifecycleEvent, X as MediaBeforeUploadHandler, Y as MediaAfterUploadHandler, _ as ContentAfterScheduleHandler, a as CommentAfterCreateHandler, at as PageMetadataHandler, b as ContentBeforeDeleteHandler, c as CommentBeforeCreateEvent, d as CommentModerateHandler, g as ContentAfterSaveHandler, h as ContentAfterRestoreHandler, i as CommentAfterCreateEvent, it as PageMetadataEvent, jt as UninstallEvent, k as CronHandler, l as CommentBeforeCreateHandler, m as ContentAfterPublishHandler, nt as PageFragmentHandler, o as CommentAfterModerateEvent, p as ContentAfterDeleteHandler, pt as PluginContext, s as CommentAfterModerateHandler, tt as PageFragmentEvent, u as CommentModerateEvent, v as ContentAfterUnpublishHandler, w as ContentPublishStateChangeEvent, x as ContentBeforeSaveHandler, y as ContentAfterUnscheduleHandler } from "./types-BYnDBVwe.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/plugin-types.d.ts
|
|
4
4
|
/**
|
|
@@ -19,6 +19,9 @@ interface HookHandlers {
|
|
|
19
19
|
"content:afterDelete": ContentAfterDeleteHandler;
|
|
20
20
|
"content:afterPublish": ContentAfterPublishHandler;
|
|
21
21
|
"content:afterUnpublish": ContentAfterUnpublishHandler;
|
|
22
|
+
"content:afterRestore": ContentAfterRestoreHandler;
|
|
23
|
+
"content:afterSchedule": ContentAfterScheduleHandler;
|
|
24
|
+
"content:afterUnschedule": ContentAfterUnscheduleHandler;
|
|
22
25
|
"media:beforeUpload": MediaBeforeUploadHandler;
|
|
23
26
|
"media:afterUpload": MediaAfterUploadHandler;
|
|
24
27
|
cron: CronHandler;
|
|
@@ -118,5 +121,5 @@ interface SandboxedPlugin {
|
|
|
118
121
|
routes?: Record<string, RouteEntry>;
|
|
119
122
|
}
|
|
120
123
|
//#endregion
|
|
121
|
-
export { type CommentAfterCreateEvent, type CommentAfterModerateEvent, type CommentBeforeCreateEvent, type CommentModerateEvent, type ContentDeleteEvent, type ContentHookEvent, type ContentPublishStateChangeEvent, type CronEvent, type EmailAfterSendEvent, type EmailBeforeSendEvent, type EmailDeliverEvent, HookConfig, HookEntry, HookHandlers, type LifecycleEvent, type MediaAfterUploadEvent, type MediaUploadEvent, type PageFragmentEvent, type PageMetadataEvent, type PluginContext, RouteEntry, RouteHandler, SandboxedPlugin, SandboxedRequest, SandboxedRouteContext, type UninstallEvent };
|
|
124
|
+
export { type CommentAfterCreateEvent, type CommentAfterModerateEvent, type CommentBeforeCreateEvent, type CommentModerateEvent, type ContentDeleteEvent, type ContentHookEvent, type ContentPublishStateChangeEvent, type ContentRestoreStateChangeEvent, type ContentScheduleStateChangeEvent, type ContentStateChangeEvent, type CronEvent, type EmailAfterSendEvent, type EmailBeforeSendEvent, type EmailDeliverEvent, HookConfig, HookEntry, HookHandlers, type LifecycleEvent, type MediaAfterUploadEvent, type MediaUploadEvent, type PageFragmentEvent, type PageMetadataEvent, type PluginContext, RouteEntry, RouteHandler, SandboxedPlugin, SandboxedRequest, SandboxedRouteContext, type UninstallEvent };
|
|
122
125
|
//# sourceMappingURL=plugin-types.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-types.d.mts","names":[],"sources":["../src/plugin-types.ts"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"plugin-types.d.mts","names":[],"sources":["../src/plugin-types.ts"],"mappings":";;;;;;;;;;UA8FiB,YAAA;EAChB,gBAAA,EAAkB,gBAAA;EAClB,iBAAA,EAAmB,gBAAA;EACnB,mBAAA,EAAqB,gBAAA;EACrB,kBAAA,EAAoB,gBAAA;EACpB,oBAAA,EAAsB,wBAAA;EACtB,mBAAA,EAAqB,uBAAA;EACrB,sBAAA,EAAwB,0BAAA;EACxB,qBAAA,EAAuB,yBAAA;EACvB,sBAAA,EAAwB,0BAAA;EACxB,wBAAA,EAA0B,4BAAA;EAC1B,sBAAA,EAAwB,0BAAA;EACxB,uBAAA,EAAyB,2BAAA;EACzB,yBAAA,EAA2B,6BAAA;EAC3B,oBAAA,EAAsB,wBAAA;EACtB,mBAAA,EAAqB,uBAAA;EACrB,IAAA,EAAM,WAAA;EACN,kBAAA,EAAoB,sBAAA;EACpB,eAAA,EAAiB,mBAAA;EACjB,iBAAA,EAAmB,qBAAA;EACnB,sBAAA,EAAwB,0BAAA;EACxB,kBAAA,EAAoB,sBAAA;EACpB,qBAAA,EAAuB,yBAAA;EACvB,uBAAA,EAAyB,2BAAA;EACzB,eAAA,EAAiB,mBAAA;EACjB,gBAAA,EAAkB,mBAAA;AAAA;;;;;;;UASF,UAAA,iBAA2B,YAAA;EAC3C,OAAA,EAAS,YAAA,CAAa,CAAA;EACtB,QAAA;EACA,OAAA;EACA,YAAA;EACA,WAAA;EACA,SAAA;AAAA;;;;;;KAQW,SAAA,iBAA0B,YAAA,IAAgB,YAAA,CAAa,CAAA,IAAK,UAAA,CAAW,CAAA;;;;;;;;;;;;AAAnF;;;;UAiBiB,gBAAA;EAChB,GAAA;EACA,MAAA;EACA,OAAA,EAAS,MAAA;AAAA;;;;;;;;;UAWO,qBAAA;EAChB,KAAA;EACA,OAAA,EAAS,gBAAA;EACT,WAAA;AAAA;;;;;;;AAHD;;;KAeY,YAAA,IACX,QAAA,EAAU,qBAAA,EACV,GAAA,EAAK,aAAA,KACD,OAAA;;;;;KAMO,UAAA,GACT,YAAA;EAEA,OAAA,EAAS,YAAA;EACT,MAAA;EACA,KAAA;AAAA;;;;;;;;;;UAYc,eAAA;EAChB,KAAA,iBACa,YAAA,IAAgB,SAAA,CAAU,CAAA;EAEvC,MAAA,GAAS,MAAA,SAAe,UAAA;AAAA"}
|
package/dist/plugin-utils.d.mts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import "./options-
|
|
2
|
-
import "./types-
|
|
3
|
-
import "./types-
|
|
4
|
-
import "./byline-fields-
|
|
5
|
-
import "./index-
|
|
6
|
-
import "./runner-
|
|
7
|
-
import "./index-
|
|
1
|
+
import "./options-41nCWqi9.mjs";
|
|
2
|
+
import "./types-xIfVRNLp.mjs";
|
|
3
|
+
import "./types-BYnDBVwe.mjs";
|
|
4
|
+
import "./byline-fields-KD3JFRuH.mjs";
|
|
5
|
+
import "./index-BRNabKrx.mjs";
|
|
6
|
+
import "./runner-BbR3DfrL.mjs";
|
|
7
|
+
import "./index-Dt0OKJB4.mjs";
|
|
8
8
|
import "./types-CUsXGRVj.mjs";
|
|
9
|
-
import "./validate-
|
|
9
|
+
import "./validate-CG0V0wNR.mjs";
|
|
10
10
|
import { EmDashHandlers } from "./astro/types.mjs";
|
|
11
11
|
|
|
12
12
|
//#region src/plugin-utils.d.ts
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import "../options-
|
|
2
|
-
import "../types-
|
|
3
|
-
import {
|
|
4
|
-
import "../byline-fields-
|
|
5
|
-
import { Vt as PluginDescriptor } from "../index-
|
|
6
|
-
import "../runner-
|
|
7
|
-
import "../index-
|
|
1
|
+
import "../options-41nCWqi9.mjs";
|
|
2
|
+
import "../types-xIfVRNLp.mjs";
|
|
3
|
+
import { Tt as ResolvedPlugin } from "../types-BYnDBVwe.mjs";
|
|
4
|
+
import "../byline-fields-KD3JFRuH.mjs";
|
|
5
|
+
import { Vt as PluginDescriptor } from "../index-BRNabKrx.mjs";
|
|
6
|
+
import "../runner-BbR3DfrL.mjs";
|
|
7
|
+
import "../index-Dt0OKJB4.mjs";
|
|
8
8
|
import { SandboxedPlugin } from "../plugin-types.mjs";
|
|
9
9
|
import "../types-CUsXGRVj.mjs";
|
|
10
|
-
import "../validate-
|
|
10
|
+
import "../validate-CG0V0wNR.mjs";
|
|
11
11
|
|
|
12
12
|
//#region src/plugins/adapt-sandbox-entry.d.ts
|
|
13
13
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as PLUGIN_CAPABILITIES, t as HOOK_NAMES } from "../manifest-schema-
|
|
1
|
+
import { n as PLUGIN_CAPABILITIES, t as HOOK_NAMES } from "../manifest-schema-kPGX7VS-.mjs";
|
|
2
2
|
import { a as normalizeCapabilities } from "../types-BvbeGEtr.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/plugins/adapt-sandbox-entry.ts
|