emdash 0.28.1 → 0.29.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/{adapters-hUy8vOB-.d.mts → adapters-DPAkL6QF.d.mts} +1 -1
- package/dist/{adapters-hUy8vOB-.d.mts.map → adapters-DPAkL6QF.d.mts.map} +1 -1
- package/dist/{allowed-origins-CRsJ6BPM.mjs → allowed-origins-FXB-FplB.mjs} +2 -2
- package/dist/{allowed-origins-CRsJ6BPM.mjs.map → allowed-origins-FXB-FplB.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +22 -21
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +4 -4
- package/dist/{api-DxFSorgk.mjs → api-DVbHNApo.mjs} +75 -23
- package/dist/api-DVbHNApo.mjs.map +1 -0
- package/dist/{api-tokens-DvqeZmVq.mjs → api-tokens-UYBkl17d.mjs} +2 -2
- package/dist/{api-tokens-DvqeZmVq.mjs.map → api-tokens-UYBkl17d.mjs.map} +1 -1
- package/dist/{apply-CDJqIjPU.mjs → apply-D23iUV4q.mjs} +20 -20
- package/dist/{apply-CDJqIjPU.mjs.map → apply-D23iUV4q.mjs.map} +1 -1
- package/dist/astro/index.d.mts +13 -12
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +10 -5
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +11 -10
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +8 -8
- package/dist/astro/middleware/redirect.mjs +7 -7
- package/dist/astro/middleware/request-context.d.mts +3 -0
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +257 -30
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +2 -2
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +125 -79
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -7
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -7
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +10 -10
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +10 -10
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +10 -10
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -17
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -17
- package/dist/astro/routes/api/admin/bylines/index.mjs +17 -17
- package/dist/astro/routes/api/admin/comments/_id_/status.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -13
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/index.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
- package/dist/astro/routes/api/admin/media-usage/repair.d.mts +8 -0
- package/dist/astro/routes/api/admin/media-usage/repair.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +28 -0
- package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -0
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +43 -41
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +43 -41
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +43 -41
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +43 -41
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +42 -40
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +42 -40
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +42 -40
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +5 -5
- package/dist/astro/routes/api/admin/users/index.mjs +7 -7
- package/dist/astro/routes/api/auth/dev-bypass.mjs +7 -7
- package/dist/astro/routes/api/auth/invite/accept.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/complete.mjs +12 -12
- package/dist/astro/routes/api/auth/invite/index.mjs +9 -9
- package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -11
- package/dist/astro/routes/api/auth/logout.mjs +4 -4
- package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -11
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +4 -4
- package/dist/astro/routes/api/auth/me.mjs +8 -8
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs.map +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +7 -7
- package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/options.mjs +13 -13
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +12 -12
- package/dist/astro/routes/api/auth/passkey/verify.mjs +12 -12
- package/dist/astro/routes/api/auth/signup/complete.mjs +12 -12
- package/dist/astro/routes/api/auth/signup/request.mjs +11 -11
- package/dist/astro/routes/api/auth/signup/verify.mjs +3 -3
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.d.mts.map +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +69 -14
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +12 -12
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts +9 -0
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.d.mts.map +1 -0
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +71 -0
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -0
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.d.mts +8 -0
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.d.mts.map +1 -0
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +50 -0
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -0
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +15 -15
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/trash.mjs +8 -8
- package/dist/astro/routes/api/dashboard.mjs +8 -8
- package/dist/astro/routes/api/dev/emails.mjs +4 -4
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +18 -18
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +12 -11
- package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/execute.mjs +16 -16
- package/dist/astro/routes/api/import/wordpress/media.d.mts +11 -10
- package/dist/astro/routes/api/import/wordpress/media.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +12 -16
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +18 -18
- package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +12 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +28 -28
- package/dist/astro/routes/api/manifest.mjs +6 -6
- package/dist/astro/routes/api/mcp.mjs +56 -47
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +9 -9
- package/dist/astro/routes/api/media/_id_.mjs +8 -8
- package/dist/astro/routes/api/media/file/_...key_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
- package/dist/astro/routes/api/media/providers/index.mjs +4 -4
- package/dist/astro/routes/api/media/upload-url.mjs +10 -10
- package/dist/astro/routes/api/media.mjs +13 -13
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/items.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/translations.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_.mjs +9 -9
- package/dist/astro/routes/api/menus/index.mjs +9 -9
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
- package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
- package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
- package/dist/astro/routes/api/oauth/register.mjs +4 -4
- package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
- package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
- package/dist/astro/routes/api/oauth/token.mjs +7 -7
- package/dist/astro/routes/api/openapi.json.mjs +24 -5
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +5 -5
- package/dist/astro/routes/api/redirects/404s/index.mjs +12 -12
- package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -12
- package/dist/astro/routes/api/redirects/_id_.mjs +13 -13
- package/dist/astro/routes/api/redirects/index.mjs +13 -13
- package/dist/astro/routes/api/relations/_id_/index.d.mts +10 -0
- package/dist/astro/routes/api/relations/_id_/index.d.mts.map +1 -0
- package/dist/astro/routes/api/relations/_id_/index.mjs +69 -0
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -0
- package/dist/astro/routes/api/relations/_id_/translations.d.mts +8 -0
- package/dist/astro/routes/api/relations/_id_/translations.d.mts.map +1 -0
- package/dist/astro/routes/api/relations/_id_/translations.mjs +34 -0
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -0
- package/dist/astro/routes/api/relations/index.d.mts +9 -0
- package/dist/astro/routes/api/relations/index.d.mts.map +1 -0
- package/dist/astro/routes/api/relations/index.mjs +53 -0
- package/dist/astro/routes/api/relations/index.mjs.map +1 -0
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +4 -4
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.d.mts.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +42 -40
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.d.mts.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +42 -40
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +42 -40
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +42 -40
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +42 -40
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +9 -9
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +42 -40
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +42 -40
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +12 -12
- package/dist/astro/routes/api/search/index.mjs +13 -12
- package/dist/astro/routes/api/search/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/rebuild.mjs +12 -12
- package/dist/astro/routes/api/search/stats.mjs +8 -8
- package/dist/astro/routes/api/search/suggest.mjs +11 -11
- package/dist/astro/routes/api/sections/_slug_.mjs +11 -11
- package/dist/astro/routes/api/sections/index.mjs +11 -11
- package/dist/astro/routes/api/settings/email.mjs +6 -6
- package/dist/astro/routes/api/settings.mjs +15 -15
- package/dist/astro/routes/api/setup/admin-verify.mjs +13 -13
- package/dist/astro/routes/api/setup/admin.mjs +12 -12
- package/dist/astro/routes/api/setup/dev-bypass.mjs +29 -29
- package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
- package/dist/astro/routes/api/setup/index.mjs +30 -30
- package/dist/astro/routes/api/setup/status.mjs +5 -5
- package/dist/astro/routes/api/snapshot.mjs +8 -8
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +15 -15
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +15 -15
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +15 -15
- package/dist/astro/routes/api/taxonomies/index.mjs +15 -15
- package/dist/astro/routes/api/themes/preview.mjs +7 -7
- package/dist/astro/routes/api/typegen.mjs +6 -6
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +12 -12
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +12 -12
- package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/index.mjs +12 -12
- package/dist/astro/routes/api/widget-components.mjs +4 -4
- package/dist/astro/routes/robots.txt.mjs +9 -9
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +12 -12
- package/dist/astro/routes/sitemap.xml.mjs +10 -10
- package/dist/astro/types.d.mts +16 -13
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-Dz88F_Fw.mjs → authorize-BmCwQCib.mjs} +2 -2
- package/dist/{authorize-Dz88F_Fw.mjs.map → authorize-BmCwQCib.mjs.map} +1 -1
- package/dist/{base64-CmWvODNW.mjs → base64-C74Msx8h.mjs} +1 -1
- package/dist/{base64-CmWvODNW.mjs.map → base64-C74Msx8h.mjs.map} +1 -1
- package/dist/{byline-Bm8ZgUP9.mjs → byline-BWb5H-65.mjs} +8 -8
- package/dist/{byline-Bm8ZgUP9.mjs.map → byline-BWb5H-65.mjs.map} +1 -1
- package/dist/{byline-fields-CIx_GuN4.mjs → byline-fields-DO6bAYK8.mjs} +2 -2
- package/dist/{byline-fields-CIx_GuN4.mjs.map → byline-fields-DO6bAYK8.mjs.map} +1 -1
- package/dist/{byline-registry-BOjqDOim.mjs → byline-registry-BCuOp4UF.mjs} +3 -3
- package/dist/{byline-registry-BOjqDOim.mjs.map → byline-registry-BCuOp4UF.mjs.map} +1 -1
- package/dist/{bylines-6tm9mgQb.mjs → bylines-BGuKFaBy.mjs} +4 -4
- package/dist/{bylines-6tm9mgQb.mjs.map → bylines-BGuKFaBy.mjs.map} +1 -1
- package/dist/{bylines-CAZxGSrm.mjs → bylines-CHojDMpj.mjs} +8 -8
- package/dist/{bylines-CAZxGSrm.mjs.map → bylines-CHojDMpj.mjs.map} +1 -1
- package/dist/{cache-CEBAZjir.mjs → cache-BeR7RfN8.mjs} +3 -3
- package/dist/{cache-CEBAZjir.mjs.map → cache-BeR7RfN8.mjs.map} +1 -1
- package/dist/{challenge-store-anhwVER0.mjs → challenge-store-B426wQ6O.mjs} +1 -1
- package/dist/{challenge-store-anhwVER0.mjs.map → challenge-store-B426wQ6O.mjs.map} +1 -1
- package/dist/{chunks-B2isWQhr.mjs → chunks-BU5_nrAC.mjs} +2 -2
- package/dist/{chunks-B2isWQhr.mjs.map → chunks-BU5_nrAC.mjs.map} +1 -1
- package/dist/cli/index.mjs +28 -28
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +34 -2
- package/dist/client/index.d.mts.map +1 -1
- package/dist/client/index.mjs +6 -2
- package/dist/client/index.mjs.map +1 -1
- package/dist/{comment-CzeatwBV.mjs → comment-SWh31kS0.mjs} +2 -2
- package/dist/{comment-CzeatwBV.mjs.map → comment-SWh31kS0.mjs.map} +1 -1
- package/dist/{comment-reaction-Cj9QJY7I.mjs → comment-reaction-DgcpziXI.mjs} +2 -2
- package/dist/{comment-reaction-Cj9QJY7I.mjs.map → comment-reaction-DgcpziXI.mjs.map} +1 -1
- package/dist/{comments-BLE-jv5k.mjs → comments-kZYGppMS.mjs} +3 -3
- package/dist/{comments-BLE-jv5k.mjs.map → comments-kZYGppMS.mjs.map} +1 -1
- package/dist/{components-B_sATNUc.mjs → components-CliOkZAs.mjs} +1 -1
- package/dist/{components-B_sATNUc.mjs.map → components-CliOkZAs.mjs.map} +1 -1
- package/dist/{content-DhgEth4k.mjs → content-BO61Sx4j.mjs} +179 -20
- package/dist/content-BO61Sx4j.mjs.map +1 -0
- package/dist/{content-refresh-Br4gMzh8.mjs → content-refresh-BkkApzeS.mjs} +8 -8
- package/dist/{content-refresh-Br4gMzh8.mjs.map → content-refresh-BkkApzeS.mjs.map} +1 -1
- package/dist/{context-BRIpHARC.mjs → context-DtSFYFuS.mjs} +71 -14
- package/dist/context-DtSFYFuS.mjs.map +1 -0
- package/dist/{cron-v4FTEFR_.mjs → cron-YFJ7uNVA.mjs} +1 -1
- package/dist/{cron-v4FTEFR_.mjs.map → cron-YFJ7uNVA.mjs.map} +1 -1
- package/dist/{dashboard-EvEKF5en.mjs → dashboard-xo_gPs_D.mjs} +6 -5
- package/dist/dashboard-xo_gPs_D.mjs.map +1 -0
- package/dist/database/pg-migration-lock.d.mts +21 -0
- package/dist/database/pg-migration-lock.d.mts.map +1 -0
- package/dist/database/pg-migration-lock.mjs +45 -0
- package/dist/database/pg-migration-lock.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +2 -2
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/postgres.d.mts.map +1 -1
- package/dist/db/postgres.mjs +2 -8
- package/dist/db/postgres.mjs.map +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{db-errors-CK46D-ly.mjs → db-errors-BKuXh_fi.mjs} +1 -1
- package/dist/{db-errors-CK46D-ly.mjs.map → db-errors-BKuXh_fi.mjs.map} +1 -1
- package/dist/{default-CM_D83Mg.mjs → default-DY5ovGUG.mjs} +1 -1
- package/dist/{default-CM_D83Mg.mjs.map → default-DY5ovGUG.mjs.map} +1 -1
- package/dist/{device-flow-CfGP18Uw.mjs → device-flow-CPXQ_QfW.mjs} +5 -5
- package/dist/{device-flow-CfGP18Uw.mjs.map → device-flow-CPXQ_QfW.mjs.map} +1 -1
- package/dist/{dialect-helpers-DRI5pyY3.mjs → dialect-helpers-Cue7QQOR.mjs} +21 -2
- package/dist/dialect-helpers-Cue7QQOR.mjs.map +1 -0
- package/dist/{email-console-CIf4Lfvv.mjs → email-console-CjkQc7oY.mjs} +1 -1
- package/dist/{email-console-CIf4Lfvv.mjs.map → email-console-CjkQc7oY.mjs.map} +1 -1
- package/dist/{enrich-BQ0mxJRs.mjs → enrich-CFJJgxs_.mjs} +2 -2
- package/dist/{enrich-BQ0mxJRs.mjs.map → enrich-CFJJgxs_.mjs.map} +1 -1
- package/dist/{error-CkAseSqZ.mjs → error-CfJMLSkd.mjs} +12 -3
- package/dist/error-CfJMLSkd.mjs.map +1 -0
- package/dist/{escape-DqD0aCKu.mjs → escape-r8_GbEU2.mjs} +1 -1
- package/dist/{escape-DqD0aCKu.mjs.map → escape-r8_GbEU2.mjs.map} +1 -1
- package/dist/{field-defs-cache-WUI88LX5.mjs → field-defs-cache-BVrNCgSj.mjs} +5 -5
- package/dist/{field-defs-cache-WUI88LX5.mjs.map → field-defs-cache-BVrNCgSj.mjs.map} +1 -1
- package/dist/{fts-manager-BbFaF_V8.mjs → fts-manager-BD9s77qh.mjs} +27 -3
- package/dist/{fts-manager-BbFaF_V8.mjs.map → fts-manager-BD9s77qh.mjs.map} +1 -1
- package/dist/{hash-DrvzIXcz.mjs → hash-DFFrkivP.mjs} +1 -1
- package/dist/{hash-DrvzIXcz.mjs.map → hash-DFFrkivP.mjs.map} +1 -1
- package/dist/hreflang-CgIxhH4k.d.mts +48 -0
- package/dist/hreflang-CgIxhH4k.d.mts.map +1 -0
- package/dist/hreflang-pAWUiPd_.mjs +102 -0
- package/dist/hreflang-pAWUiPd_.mjs.map +1 -0
- package/dist/{import-fYLf14Co.mjs → import-2jHtLzsO.mjs} +6 -6
- package/dist/{import-fYLf14Co.mjs.map → import-2jHtLzsO.mjs.map} +1 -1
- package/dist/{index-B9sIxwe4.d.mts → index-BXOCyLj0.d.mts} +115 -21
- package/dist/index-BXOCyLj0.d.mts.map +1 -0
- package/dist/{index-Bm-Gy8Mh.d.mts → index-DxjWLKGT.d.mts} +3 -3
- package/dist/{index-Bm-Gy8Mh.d.mts.map → index-DxjWLKGT.d.mts.map} +1 -1
- package/dist/index.d.mts +20 -19
- package/dist/index.mjs +68 -64
- package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
- package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
- package/dist/{load-Djn3HWQS.mjs → load-DgoWMdRX.mjs} +2 -2
- package/dist/{load-Djn3HWQS.mjs.map → load-DgoWMdRX.mjs.map} +1 -1
- package/dist/{loader-CB6YHeul.mjs → loader-Rb8QlGZp.mjs} +5 -20
- package/dist/loader-Rb8QlGZp.mjs.map +1 -0
- package/dist/{manifest-schema-BHlgZM1W.mjs → manifest-schema-DeGNma2O.mjs} +3 -1
- package/dist/manifest-schema-DeGNma2O.mjs.map +1 -0
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +2 -2
- package/dist/media/local-runtime.d.mts +13 -12
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +8 -8
- package/dist/{media-BrXIaMFN.mjs → media-D2ZFowpn.mjs} +2 -2
- package/dist/media-D2ZFowpn.mjs.map +1 -0
- package/dist/{media-allowlist-CRHEeJ_i.mjs → media-allowlist-Dc05vKtt.mjs} +2 -2
- package/dist/{media-allowlist-CRHEeJ_i.mjs.map → media-allowlist-Dc05vKtt.mjs.map} +1 -1
- package/dist/{media-url-CKi5XSw4.mjs → media-url-DgIEYZr4.mjs} +1 -1
- package/dist/{media-url-CKi5XSw4.mjs.map → media-url-DgIEYZr4.mjs.map} +1 -1
- package/dist/media-usage-B__v9NMm.mjs +421 -0
- package/dist/media-usage-B__v9NMm.mjs.map +1 -0
- package/dist/{menus-Cfivc2Ad.mjs → menus-BpfevlJD.mjs} +3 -3
- package/dist/{menus-Cfivc2Ad.mjs.map → menus-BpfevlJD.mjs.map} +1 -1
- package/dist/{menus-b2T4clbO.mjs → menus-BtvH7Z7t.mjs} +31 -12
- package/dist/menus-BtvH7Z7t.mjs.map +1 -0
- package/dist/{mime-CCEzze7W.mjs → mime-DfIVjNkr.mjs} +1 -1
- package/dist/{mime-CCEzze7W.mjs.map → mime-DfIVjNkr.mjs.map} +1 -1
- package/dist/{mode-BM9QgEmt.mjs → mode-BVnhGZ2o.mjs} +1 -1
- package/dist/{mode-BM9QgEmt.mjs.map → mode-BVnhGZ2o.mjs.map} +1 -1
- package/dist/{normalize-CeglrBT9.mjs → normalize-C-SHXmra.mjs} +1 -1
- package/dist/{normalize-CeglrBT9.mjs.map → normalize-C-SHXmra.mjs.map} +1 -1
- package/dist/{oauth-authorization-CXWkIGT4.mjs → oauth-authorization-BWqWTHBK.mjs} +5 -5
- package/dist/{oauth-authorization-CXWkIGT4.mjs.map → oauth-authorization-BWqWTHBK.mjs.map} +1 -1
- package/dist/{oauth-clients-BmTI5rsl.mjs → oauth-clients-43_XADoa.mjs} +1 -1
- package/dist/{oauth-clients-BmTI5rsl.mjs.map → oauth-clients-43_XADoa.mjs.map} +1 -1
- package/dist/{oauth-state-store-YLFLcH5F.mjs → oauth-state-store-BRSUEkmx.mjs} +1 -1
- package/dist/{oauth-state-store-YLFLcH5F.mjs.map → oauth-state-store-BRSUEkmx.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-Bsc_GsX-.mjs → oauth-user-lookup-Ctgemb0k.mjs} +1 -1
- package/dist/{oauth-user-lookup-Bsc_GsX-.mjs.map → oauth-user-lookup-Ctgemb0k.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{options-DTTML-Tx.mjs → options-BlmBHTvX.mjs} +1 -1
- package/dist/{options-DTTML-Tx.mjs.map → options-BlmBHTvX.mjs.map} +1 -1
- package/dist/{options-iMpRRxIe.d.mts → options-COXGTtqd.d.mts} +3 -3
- package/dist/{options-iMpRRxIe.d.mts.map → options-COXGTtqd.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-GmjecGct.mjs → parse-GLksnn-B.mjs} +2 -2
- package/dist/{parse-GmjecGct.mjs.map → parse-GLksnn-B.mjs.map} +1 -1
- package/dist/{passkey-config-De0ahnRg.mjs → passkey-config-CjA1UbDj.mjs} +1 -1
- package/dist/{passkey-config-De0ahnRg.mjs.map → passkey-config-CjA1UbDj.mjs.map} +1 -1
- package/dist/{patterns-BKmjvM7K.mjs → patterns-CiyXeDgr.mjs} +1 -1
- package/dist/{patterns-BKmjvM7K.mjs.map → patterns-CiyXeDgr.mjs.map} +1 -1
- package/dist/{placeholder-BSKwzRe4.d.mts → placeholder-DR-eeZr-.d.mts} +1 -1
- package/dist/{placeholder-BSKwzRe4.d.mts.map → placeholder-DR-eeZr-.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +11 -10
- package/dist/plugin-utils.d.mts.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +11 -10
- package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{portable-text-cCa0cGV6.mjs → portable-text-BAkgEKcv.mjs} +1 -1
- package/dist/{portable-text-cCa0cGV6.mjs.map → portable-text-BAkgEKcv.mjs.map} +1 -1
- package/dist/{preview-SSXNVPAC.mjs → preview-Rloa09Wp.mjs} +2 -2
- package/dist/{preview-SSXNVPAC.mjs.map → preview-Rloa09Wp.mjs.map} +1 -1
- package/dist/{public-url-Danv47cS.mjs → public-url-C0cEvrRu.mjs} +1 -1
- package/dist/{public-url-Danv47cS.mjs.map → public-url-C0cEvrRu.mjs.map} +1 -1
- package/dist/{schema-CX9cfNkZ.mjs → query-BxT95opY.mjs} +5 -10
- package/dist/query-BxT95opY.mjs.map +1 -0
- package/dist/{query-1Cp6U9oo.mjs → query-ZzvXyxWq.mjs} +15 -15
- package/dist/query-ZzvXyxWq.mjs.map +1 -0
- package/dist/{rate-limit-C_Lf63Dd.mjs → rate-limit-Cg4Rvc_-.mjs} +2 -2
- package/dist/{rate-limit-C_Lf63Dd.mjs.map → rate-limit-Cg4Rvc_-.mjs.map} +1 -1
- package/dist/{redirect-CoW3G-0U.mjs → redirect-Ce_6Yb6z.mjs} +1 -1
- package/dist/{redirect-CoW3G-0U.mjs.map → redirect-Ce_6Yb6z.mjs.map} +1 -1
- package/dist/{redirect-IyfNirgS.mjs → redirect-Ch3E98qZ.mjs} +4 -4
- package/dist/{redirect-IyfNirgS.mjs.map → redirect-Ch3E98qZ.mjs.map} +1 -1
- package/dist/{redirects-Bs1g42Ri.mjs → redirects-CXmVGRNZ.mjs} +6 -6
- package/dist/{redirects-Bs1g42Ri.mjs.map → redirects-CXmVGRNZ.mjs.map} +1 -1
- package/dist/{redirects-DXEXBAII.mjs → redirects-CeD7K7EX.mjs} +43 -5
- package/dist/redirects-CeD7K7EX.mjs.map +1 -0
- package/dist/{registry-B57vp_X9.mjs → registry-CyIXPi4E.mjs} +8 -8
- package/dist/{registry-B57vp_X9.mjs.map → registry-CyIXPi4E.mjs.map} +1 -1
- package/dist/relation-Ba68Tm4Z.mjs +334 -0
- package/dist/relation-Ba68Tm4Z.mjs.map +1 -0
- package/dist/{byline-fields-2Lp5Hbgf.mjs → relations-BmBmX4Eb.mjs} +60 -4
- package/dist/relations-BmBmX4Eb.mjs.map +1 -0
- package/dist/relations-C8hVyEy0.mjs +397 -0
- package/dist/relations-C8hVyEy0.mjs.map +1 -0
- package/dist/{byline-fields-CHE8UStY.d.mts → relations-CbBpoOys.d.mts} +182 -29
- package/dist/relations-CbBpoOys.d.mts.map +1 -0
- package/dist/{request-cache-KCNHp_RJ.mjs → request-cache-BSUptuJR.mjs} +1 -1
- package/dist/{request-cache-KCNHp_RJ.mjs.map → request-cache-BSUptuJR.mjs.map} +1 -1
- package/dist/{request-meta-DbvXaAvU.mjs → request-meta-DCwktC5P.mjs} +2 -2
- package/dist/{request-meta-DbvXaAvU.mjs.map → request-meta-DCwktC5P.mjs.map} +1 -1
- package/dist/{resolve-DR3wqMTy.mjs → resolve-Cd9dzclN.mjs} +1 -1
- package/dist/{resolve-DR3wqMTy.mjs.map → resolve-Cd9dzclN.mjs.map} +1 -1
- package/dist/{runner-DvFWoz8-.d.mts → runner-CiNdIk3y.d.mts} +8 -2
- package/dist/runner-CiNdIk3y.d.mts.map +1 -0
- package/dist/{runner-K8XK101B.mjs → runner-jt65bF7F.mjs} +24 -7
- package/dist/{runner-K8XK101B.mjs.map → runner-jt65bF7F.mjs.map} +1 -1
- package/dist/runtime.d.mts +12 -11
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +4 -4
- package/dist/{scheduled-publish-Drffrf2a.d.mts → scheduled-publish-DlJHGcuL.d.mts} +1 -1
- package/dist/scheduled-publish-DlJHGcuL.d.mts.map +1 -0
- package/dist/schema-DwjufZ1S.mjs +9 -0
- package/dist/schema-DwjufZ1S.mjs.map +1 -0
- package/dist/{search-DZN2HZ-V.mjs → search-Cl875CVE.mjs} +62 -12
- package/dist/search-Cl875CVE.mjs.map +1 -0
- package/dist/{secrets-kp760LlD.mjs → secrets-CO_bEmII.mjs} +4 -4
- package/dist/{secrets-kp760LlD.mjs.map → secrets-CO_bEmII.mjs.map} +1 -1
- package/dist/{sections-CpF5SaGr.mjs → sections-DTKzgoiS.mjs} +3 -3
- package/dist/{sections-CpF5SaGr.mjs.map → sections-DTKzgoiS.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +23 -23
- package/dist/seo/index.d.mts +4 -2
- package/dist/seo/index.d.mts.map +1 -1
- package/dist/seo/index.mjs +19 -2
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-DXu2UQnC.mjs → seo-BLm6SXzS.mjs} +2 -2
- package/dist/{seo-DXu2UQnC.mjs.map → seo-BLm6SXzS.mjs.map} +1 -1
- package/dist/{seo-89-53DDs.mjs → seo-BjrbXAd_.mjs} +1 -1
- package/dist/{seo-89-53DDs.mjs.map → seo-BjrbXAd_.mjs.map} +1 -1
- package/dist/{service-DQmiBMzt.mjs → service-D6zDmKXy.mjs} +3 -3
- package/dist/{service-DQmiBMzt.mjs.map → service-D6zDmKXy.mjs.map} +1 -1
- package/dist/{session-user-Cascbax7.mjs → session-user-DoSs6YM8.mjs} +1 -1
- package/dist/{session-user-Cascbax7.mjs.map → session-user-DoSs6YM8.mjs.map} +1 -1
- package/dist/{settings-zhmdKyJQ.mjs → settings-BuQYvLfy.mjs} +6 -6
- package/dist/{settings-zhmdKyJQ.mjs.map → settings-BuQYvLfy.mjs.map} +1 -1
- package/dist/{settings-rZyHEcNT.mjs → settings-CmS3TwXf.mjs} +3 -3
- package/dist/{settings-rZyHEcNT.mjs.map → settings-CmS3TwXf.mjs.map} +1 -1
- package/dist/{setup-complete-Cb3LRyzy.mjs → setup-complete-BPNgLHlU.mjs} +2 -2
- package/dist/{setup-complete-Cb3LRyzy.mjs.map → setup-complete-BPNgLHlU.mjs.map} +1 -1
- package/dist/{setup-nonce-Z8x0tHf8.mjs → setup-nonce-DEPZX96T.mjs} +1 -1
- package/dist/{setup-nonce-Z8x0tHf8.mjs.map → setup-nonce-DEPZX96T.mjs.map} +1 -1
- package/dist/{single-flight-cache-BG3B_GhN.mjs → single-flight-cache-DEtW-OEG.mjs} +2 -2
- package/dist/{single-flight-cache-BG3B_GhN.mjs.map → single-flight-cache-DEtW-OEG.mjs.map} +1 -1
- package/dist/{site-url-BBfDTgZB.mjs → site-url-CcLy5D5s.mjs} +2 -2
- package/dist/{site-url-BBfDTgZB.mjs.map → site-url-CcLy5D5s.mjs.map} +1 -1
- package/dist/{slugify-Cce3dTdg.mjs → slugify-C_tqlU4G.mjs} +1 -1
- package/dist/{slugify-Cce3dTdg.mjs.map → slugify-C_tqlU4G.mjs.map} +1 -1
- package/dist/{ssrf-1g7usGBg.mjs → ssrf-TrKYJAy0.mjs} +1 -1
- package/dist/{ssrf-1g7usGBg.mjs.map → ssrf-TrKYJAy0.mjs.map} +1 -1
- package/dist/{status-CqJQX1QN.mjs → status-D3Gy1Zc6.mjs} +1 -1
- package/dist/{status-CqJQX1QN.mjs.map → status-D3Gy1Zc6.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-Bs13SN-F.mjs → taxonomies-ClS4RKaZ.mjs} +13 -9
- package/dist/taxonomies-ClS4RKaZ.mjs.map +1 -0
- package/dist/{taxonomies-kH9ABDUF.mjs → taxonomies-pUkuD6nj.mjs} +115 -36
- package/dist/taxonomies-pUkuD6nj.mjs.map +1 -0
- package/dist/{taxonomy-BxGxZZdu.mjs → taxonomy-BkxbUMSw.mjs} +12 -3
- package/dist/{taxonomy-BxGxZZdu.mjs.map → taxonomy-BkxbUMSw.mjs.map} +1 -1
- package/dist/{tokens-BGT0TUUo.mjs → tokens-BV6fgl5A.mjs} +2 -2
- package/dist/{tokens-BGT0TUUo.mjs.map → tokens-BV6fgl5A.mjs.map} +1 -1
- package/dist/{transaction-qfqpPVpu.mjs → transaction-D0FOsb3X.mjs} +1 -1
- package/dist/{transaction-qfqpPVpu.mjs.map → transaction-D0FOsb3X.mjs.map} +1 -1
- package/dist/{transport-DPefPMcz.mjs → transport-DTFhhPOg.mjs} +1 -1
- package/dist/{transport-DPefPMcz.mjs.map → transport-DTFhhPOg.mjs.map} +1 -1
- package/dist/{transport-BMDWHSgZ.d.mts → transport-DmlmzD2U.d.mts} +1 -1
- package/dist/{transport-BMDWHSgZ.d.mts.map → transport-DmlmzD2U.d.mts.map} +1 -1
- package/dist/{trusted-proxy-VTPRiX2s.mjs → trusted-proxy-CSgZVESw.mjs} +1 -1
- package/dist/{trusted-proxy-VTPRiX2s.mjs.map → trusted-proxy-CSgZVESw.mjs.map} +1 -1
- package/dist/{types-CgvhU-hF.d.mts → types-B6-N9cBB.d.mts} +1 -1
- package/dist/{types-CgvhU-hF.d.mts.map → types-B6-N9cBB.d.mts.map} +1 -1
- package/dist/{types-g_aFIdW0.d.mts → types-BCVuUPrL.d.mts} +1 -1
- package/dist/{types-g_aFIdW0.d.mts.map → types-BCVuUPrL.d.mts.map} +1 -1
- package/dist/{types-Jo3UYd6b.mjs → types-BNZsPL9z.mjs} +1 -1
- package/dist/{types-Jo3UYd6b.mjs.map → types-BNZsPL9z.mjs.map} +1 -1
- package/dist/{types-vgEWF18A.d.mts → types-B_pmP7Yq.d.mts} +1 -1
- package/dist/{types-vgEWF18A.d.mts.map → types-B_pmP7Yq.d.mts.map} +1 -1
- package/dist/{types-CuW0zWDA.mjs → types-Ch8pFMEm.mjs} +3 -3
- package/dist/{types-CuW0zWDA.mjs.map → types-Ch8pFMEm.mjs.map} +1 -1
- package/dist/{types-ea9sAPck.d.mts → types-Cm4Cz-8M.d.mts} +1 -1
- package/dist/{types-ea9sAPck.d.mts.map → types-Cm4Cz-8M.d.mts.map} +1 -1
- package/dist/{types-CGbYowGQ.d.mts → types-CtDVagM4.d.mts} +55 -3
- package/dist/types-CtDVagM4.d.mts.map +1 -0
- package/dist/{types-BcLUUbbR.d.mts → types-DLIrNtv5.d.mts} +11 -3
- package/dist/{types-BcLUUbbR.d.mts.map → types-DLIrNtv5.d.mts.map} +1 -1
- package/dist/{types-DmHk7vAt.d.mts → types-QnlQIyPE.d.mts} +1 -1
- package/dist/{types-DmHk7vAt.d.mts.map → types-QnlQIyPE.d.mts.map} +1 -1
- package/dist/{types-_HFLRzTn.d.mts → types-momGysZk.d.mts} +1 -1
- package/dist/{types-_HFLRzTn.d.mts.map → types-momGysZk.d.mts.map} +1 -1
- package/dist/{types-tM44hEcf.mjs → types-o7xo7VgH.mjs} +1 -1
- package/dist/{types-tM44hEcf.mjs.map → types-o7xo7VgH.mjs.map} +1 -1
- package/dist/{types-ClUkdbWb.d.mts → types-qljd1hsL.d.mts} +1 -1
- package/dist/{types-ClUkdbWb.d.mts.map → types-qljd1hsL.d.mts.map} +1 -1
- package/dist/{user-BjAOvU3Z.mjs → user-DTxX_Fha.mjs} +3 -3
- package/dist/{user-BjAOvU3Z.mjs.map → user-DTxX_Fha.mjs.map} +1 -1
- package/dist/{utils-BJXElxdw.mjs → utils-Cz8PVrl4.mjs} +3 -3
- package/dist/{utils-BJXElxdw.mjs.map → utils-Cz8PVrl4.mjs.map} +1 -1
- package/dist/{validate-Z1b86_3B.d.mts → validate-9jLuE301.d.mts} +5 -5
- package/dist/{validate-Z1b86_3B.d.mts.map → validate-9jLuE301.d.mts.map} +1 -1
- package/dist/{validate-CWSnKVIJ.mjs → validate-C3CpWlNt.mjs} +3 -3
- package/dist/{validate-CWSnKVIJ.mjs.map → validate-C3CpWlNt.mjs.map} +1 -1
- package/dist/{validation-0l8-RYj4.mjs → validation-DM2K_NJC.mjs} +10 -10
- package/dist/{validation-0l8-RYj4.mjs.map → validation-DM2K_NJC.mjs.map} +1 -1
- package/dist/version-BlIJvNAZ.mjs +7 -0
- package/dist/{version-qmeO1XZY.mjs.map → version-BlIJvNAZ.mjs.map} +1 -1
- package/dist/{widgets-hWcIicX8.mjs → widgets-B_X6_UXJ.mjs} +4 -4
- package/dist/{widgets-hWcIicX8.mjs.map → widgets-B_X6_UXJ.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-CNFcsNCr.mjs → wxr-taxonomies-oNJB2S81.mjs} +4 -4
- package/dist/{wxr-taxonomies-CNFcsNCr.mjs.map → wxr-taxonomies-oNJB2S81.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-0_uxSrmf.d.mts → wxr-taxonomies-tADW4KTa.d.mts} +1 -1
- package/dist/{wxr-taxonomies-0_uxSrmf.d.mts.map → wxr-taxonomies-tADW4KTa.d.mts.map} +1 -1
- package/dist/{zod-generator-CacrR-i8.mjs → zod-generator-CrfV79De.mjs} +3 -3
- package/dist/{zod-generator-CacrR-i8.mjs.map → zod-generator-CrfV79De.mjs.map} +1 -1
- package/package.json +11 -7
- package/src/api/errors.ts +11 -0
- package/src/api/handlers/content.ts +111 -1
- package/src/api/handlers/dashboard.ts +2 -0
- package/src/api/handlers/index.ts +6 -0
- package/src/api/handlers/media-usage.ts +80 -0
- package/src/api/handlers/relations.ts +474 -0
- package/src/api/handlers/taxonomies.ts +26 -4
- package/src/api/handlers/validate-media-fields.ts +1 -1
- package/src/api/openapi/document.ts +25 -0
- package/src/api/parse.ts +1 -1
- package/src/api/schemas/comments.ts +6 -0
- package/src/api/schemas/content.ts +8 -0
- package/src/api/schemas/index.ts +2 -0
- package/src/api/schemas/media-usage.ts +48 -0
- package/src/api/schemas/relations.ts +109 -0
- package/src/api/schemas/search.ts +1 -0
- package/src/astro/integration/index.ts +1 -0
- package/src/astro/integration/routes.ts +5 -0
- package/src/astro/integration/runtime.ts +24 -0
- package/src/astro/middleware/request-context.ts +162 -20
- package/src/astro/middleware.ts +22 -9
- package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -2
- package/src/astro/routes/api/admin/comments/index.ts +1 -2
- package/src/astro/routes/api/admin/media-usage/repair.ts +24 -0
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +1 -2
- package/src/astro/routes/api/auth/oauth/[provider].ts +1 -2
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +13 -0
- package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/children.ts +97 -0
- package/src/astro/routes/api/content/[collection]/[id]/references/[relation]/parents.ts +48 -0
- package/src/astro/routes/api/relations/[id]/index.ts +84 -0
- package/src/astro/routes/api/relations/[id]/translations.ts +33 -0
- package/src/astro/routes/api/relations/index.ts +56 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +1 -7
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +1 -2
- package/src/astro/routes/api/search/index.ts +2 -0
- package/src/astro/types.ts +2 -0
- package/src/cli/commands/export-seed.ts +1 -10
- package/src/client/index.ts +39 -0
- package/src/comments/service.ts +1 -1
- package/src/comments/turnstile.ts +64 -0
- package/src/components/BlockquoteGroup.astro +34 -0
- package/src/components/CommentForm.astro +12 -5
- package/src/components/EmDashHead.astro +27 -1
- package/src/components/InlinePortableTextEditor.tsx +34 -0
- package/src/components/PortableText.astro +7 -1
- package/src/components/index.ts +2 -0
- package/src/components/portable-text-blockquote-group.ts +68 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +32 -1
- package/src/database/dialect-helpers.ts +34 -0
- package/src/database/migrations/039_fix_fts5_triggers.ts +1 -1
- package/src/database/migrations/runner.ts +46 -6
- package/src/database/pg-migration-lock.ts +72 -0
- package/src/database/repositories/comment.ts +1 -1
- package/src/database/repositories/content.ts +172 -21
- package/src/database/repositories/media.ts +1 -1
- package/src/database/repositories/relation.ts +130 -5
- package/src/database/repositories/taxonomy.ts +9 -0
- package/src/database/repositories/types.ts +8 -0
- package/src/db/postgres.ts +5 -2
- package/src/emdash-runtime.ts +72 -4
- package/src/index.ts +6 -2
- package/src/loader.ts +1 -31
- package/src/mcp/server.ts +29 -3
- package/src/plugins/context.ts +85 -0
- package/src/plugins/define-plugin.ts +1 -0
- package/src/plugins/index.ts +4 -0
- package/src/plugins/manifest-schema.ts +2 -0
- package/src/plugins/types.ts +59 -0
- package/src/schema/byline-registry.ts +1 -1
- package/src/search/fts-manager.ts +34 -0
- package/src/search/match.ts +50 -0
- package/src/search/query.ts +94 -9
- package/src/seo/hreflang.ts +192 -0
- package/src/seo/index.ts +3 -0
- package/src/taxonomies/index.ts +62 -45
- package/src/taxonomies/term-counts.ts +117 -0
- package/src/virtual-modules.d.ts +1 -0
- package/src/visual-editing/editable.ts +41 -35
- package/src/visual-editing/toolbar-bootstrap.ts +120 -0
- package/src/visual-editing/toolbar.ts +46 -0
- package/dist/api-DxFSorgk.mjs.map +0 -1
- package/dist/byline-fields-2Lp5Hbgf.mjs.map +0 -1
- package/dist/byline-fields-CHE8UStY.d.mts.map +0 -1
- package/dist/content-DhgEth4k.mjs.map +0 -1
- package/dist/context-BRIpHARC.mjs.map +0 -1
- package/dist/dashboard-EvEKF5en.mjs.map +0 -1
- package/dist/dialect-helpers-DRI5pyY3.mjs.map +0 -1
- package/dist/error-CkAseSqZ.mjs.map +0 -1
- package/dist/index-B9sIxwe4.d.mts.map +0 -1
- package/dist/loader-CB6YHeul.mjs.map +0 -1
- package/dist/manifest-schema-BHlgZM1W.mjs.map +0 -1
- package/dist/media-BrXIaMFN.mjs.map +0 -1
- package/dist/menus-b2T4clbO.mjs.map +0 -1
- package/dist/query-1Cp6U9oo.mjs.map +0 -1
- package/dist/redirects-DXEXBAII.mjs.map +0 -1
- package/dist/runner-DvFWoz8-.d.mts.map +0 -1
- package/dist/scheduled-publish-Drffrf2a.d.mts.map +0 -1
- package/dist/schema-CX9cfNkZ.mjs.map +0 -1
- package/dist/search-DZN2HZ-V.mjs.map +0 -1
- package/dist/taxonomies-Bs13SN-F.mjs.map +0 -1
- package/dist/taxonomies-kH9ABDUF.mjs.map +0 -1
- package/dist/types-CGbYowGQ.d.mts.map +0 -1
- package/dist/version-qmeO1XZY.mjs +0 -7
- /package/dist/{api-tokens-DsvvCtdh.mjs → api-tokens-Dk2GrJMY.mjs} +0 -0
- /package/dist/{ssrf-CmuGgCq3.mjs → ssrf-B5DOv5E5.mjs} +0 -0
- /package/dist/{types-DROWRxYV.mjs → types-DeCZBZxn.mjs} +0 -0
package/src/search/query.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { Kysely } from "kysely";
|
|
8
8
|
import { sql } from "kysely";
|
|
9
9
|
|
|
10
|
+
import { encodeCursor, decodeCursor, InvalidCursorError } from "../database/repositories/types.js";
|
|
10
11
|
import type { Database } from "../database/types.js";
|
|
11
12
|
import { validateIdentifier } from "../database/validate.js";
|
|
12
13
|
import { getDb } from "../loader.js";
|
|
@@ -21,6 +22,44 @@ import type {
|
|
|
21
22
|
SearchStats,
|
|
22
23
|
} from "./types.js";
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Marker stored in the `id` slot of a search pagination cursor.
|
|
27
|
+
*
|
|
28
|
+
* Search results are merged from per-collection FTS queries and re-sorted by
|
|
29
|
+
* score, so there is no single stable keyset column to encode (the way
|
|
30
|
+
* `getEmDashCollection` encodes `(orderValue, id)`). We page over the merged,
|
|
31
|
+
* score-sorted set by offset instead, carried as the cursor's `orderValue`.
|
|
32
|
+
* Reusing `encodeCursor`/`decodeCursor` keeps the opaque base64-JSON shape and
|
|
33
|
+
* the `InvalidCursorError` handling consistent with the rest of the API.
|
|
34
|
+
*/
|
|
35
|
+
const SEARCH_CURSOR_MARKER = "search";
|
|
36
|
+
|
|
37
|
+
/** Encode the next-page offset into an opaque search cursor. */
|
|
38
|
+
function encodeSearchCursor(offset: number): string {
|
|
39
|
+
return encodeCursor(String(offset), SEARCH_CURSOR_MARKER);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Decode a search cursor back to its offset. Throws `InvalidCursorError` for a
|
|
44
|
+
* malformed cursor or one that doesn't carry a non-negative integer offset, so
|
|
45
|
+
* a client pagination bug surfaces immediately rather than silently restarting
|
|
46
|
+
* from the first page.
|
|
47
|
+
*/
|
|
48
|
+
/** Upper bound on a decoded search offset, to cap the per-collection row fetch a forged cursor can trigger. */
|
|
49
|
+
const MAX_SEARCH_OFFSET = 10_000;
|
|
50
|
+
|
|
51
|
+
function decodeSearchOffset(cursor: string): number {
|
|
52
|
+
const { orderValue, id } = decodeCursor(cursor);
|
|
53
|
+
if (id !== SEARCH_CURSOR_MARKER) {
|
|
54
|
+
throw new InvalidCursorError(cursor);
|
|
55
|
+
}
|
|
56
|
+
const offset = Number(orderValue);
|
|
57
|
+
if (!Number.isInteger(offset) || offset < 0 || offset > MAX_SEARCH_OFFSET) {
|
|
58
|
+
throw new InvalidCursorError(cursor);
|
|
59
|
+
}
|
|
60
|
+
return offset;
|
|
61
|
+
}
|
|
62
|
+
|
|
24
63
|
/** Pattern to split on whitespace for query term extraction */
|
|
25
64
|
const WHITESPACE_SPLIT_PATTERN = /\s+/;
|
|
26
65
|
const FTS_OPERATORS_PATTERN = /\b(AND|OR|NOT|NEAR)\b/i;
|
|
@@ -86,6 +125,7 @@ export async function searchWithDb(
|
|
|
86
125
|
const ftsManager = new FTSManager(db);
|
|
87
126
|
const limit = options.limit ?? 20;
|
|
88
127
|
const status = options.status ?? "published";
|
|
128
|
+
const offset = options.cursor ? decodeSearchOffset(options.cursor) : 0;
|
|
89
129
|
|
|
90
130
|
// Get searchable collections
|
|
91
131
|
let collections = options.collections;
|
|
@@ -97,8 +137,15 @@ export async function searchWithDb(
|
|
|
97
137
|
return { items: [] };
|
|
98
138
|
}
|
|
99
139
|
|
|
140
|
+
// To rank the merged window [offset, offset + limit) correctly, each
|
|
141
|
+
// collection must contribute its own top (offset + limit) rows — in the
|
|
142
|
+
// worst case the entire window comes from one collection. The extra +1
|
|
143
|
+
// row detects whether a further page exists.
|
|
144
|
+
const perCollectionLimit = offset + limit + 1;
|
|
145
|
+
|
|
100
146
|
// Search each collection and merge results
|
|
101
147
|
const allResults: SearchResult[] = [];
|
|
148
|
+
const titleColumns = await ftsManager.getCollectionsWithTitleColumn(collections);
|
|
102
149
|
|
|
103
150
|
for (const collection of collections) {
|
|
104
151
|
const config = await ftsManager.getSearchConfig(collection);
|
|
@@ -113,9 +160,10 @@ export async function searchWithDb(
|
|
|
113
160
|
{
|
|
114
161
|
status,
|
|
115
162
|
locale: options.locale,
|
|
116
|
-
limit:
|
|
163
|
+
limit: perCollectionLimit,
|
|
117
164
|
},
|
|
118
165
|
config.weights,
|
|
166
|
+
titleColumns.has(collection),
|
|
119
167
|
);
|
|
120
168
|
|
|
121
169
|
allResults.push(...collectionResults);
|
|
@@ -124,10 +172,13 @@ export async function searchWithDb(
|
|
|
124
172
|
// Sort by score descending
|
|
125
173
|
allResults.sort((a, b) => b.score - a.score);
|
|
126
174
|
|
|
127
|
-
//
|
|
128
|
-
|
|
175
|
+
// Page the merged set by offset. A nextCursor is issued only when at least
|
|
176
|
+
// one result exists past this page.
|
|
177
|
+
const items = allResults.slice(offset, offset + limit);
|
|
178
|
+
const hasMore = allResults.length > offset + limit;
|
|
179
|
+
const nextCursor = hasMore ? encodeSearchCursor(offset + limit) : undefined;
|
|
129
180
|
|
|
130
|
-
return { items };
|
|
181
|
+
return { items, nextCursor };
|
|
131
182
|
}
|
|
132
183
|
|
|
133
184
|
/**
|
|
@@ -159,9 +210,25 @@ export async function searchCollection(
|
|
|
159
210
|
return { items: [] };
|
|
160
211
|
}
|
|
161
212
|
|
|
162
|
-
const
|
|
213
|
+
const limit = options.limit ?? 20;
|
|
214
|
+
const offset = options.cursor ? decodeSearchOffset(options.cursor) : 0;
|
|
215
|
+
|
|
216
|
+
// Over-fetch the [offset, offset + limit) window plus one row to detect a
|
|
217
|
+
// further page, then slice. Keeps the FTS SQL (LIMIT-only) unchanged and
|
|
218
|
+
// the cursor contract identical to the cross-collection path.
|
|
219
|
+
const fetched = await searchSingleCollection(
|
|
220
|
+
db,
|
|
221
|
+
collection,
|
|
222
|
+
query,
|
|
223
|
+
{ status: options.status, locale: options.locale, limit: offset + limit + 1 },
|
|
224
|
+
config.weights,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const items = fetched.slice(offset, offset + limit);
|
|
228
|
+
const hasMore = fetched.length > offset + limit;
|
|
229
|
+
const nextCursor = hasMore ? encodeSearchCursor(offset + limit) : undefined;
|
|
163
230
|
|
|
164
|
-
return { items };
|
|
231
|
+
return { items, nextCursor };
|
|
165
232
|
}
|
|
166
233
|
|
|
167
234
|
/**
|
|
@@ -173,6 +240,7 @@ async function searchSingleCollection(
|
|
|
173
240
|
query: string,
|
|
174
241
|
options: CollectionSearchOptions,
|
|
175
242
|
weights?: Record<string, number>,
|
|
243
|
+
hasTitle?: boolean,
|
|
176
244
|
): Promise<SearchResult[]> {
|
|
177
245
|
// Validate before any raw SQL interpolation
|
|
178
246
|
validateIdentifier(collection, "collection slug");
|
|
@@ -198,6 +266,14 @@ async function searchSingleCollection(
|
|
|
198
266
|
// Get searchable fields for snippet generation
|
|
199
267
|
const searchableFields = await ftsManager.getSearchableFields(collection);
|
|
200
268
|
|
|
269
|
+
// `title` is an optional user-defined field, not a system column. Only
|
|
270
|
+
// select it when the collection actually has one; otherwise the query
|
|
271
|
+
// errors with "no such column: c.title" (#1178). Multi-collection callers
|
|
272
|
+
// precompute this in bulk and pass it in; single-collection callers fall
|
|
273
|
+
// back to the per-collection check.
|
|
274
|
+
const collectionHasTitle = hasTitle ?? (await ftsManager.hasTitleColumn(collection));
|
|
275
|
+
const titleExpr = collectionHasTitle ? sql`c.title` : sql`NULL`;
|
|
276
|
+
|
|
201
277
|
// Build weight string for bm25 if weights provided
|
|
202
278
|
// Format: bm25(table, weight1, weight2, ...)
|
|
203
279
|
// First two weights are for 'id' and 'locale' columns (UNINDEXED, so 0)
|
|
@@ -225,11 +301,11 @@ async function searchSingleCollection(
|
|
|
225
301
|
snippet: string | null;
|
|
226
302
|
score: number;
|
|
227
303
|
}>`
|
|
228
|
-
SELECT
|
|
304
|
+
SELECT
|
|
229
305
|
c.id,
|
|
230
306
|
c.slug,
|
|
231
307
|
c.locale,
|
|
232
|
-
|
|
308
|
+
${titleExpr} as title,
|
|
233
309
|
snippet("${sql.raw(ftsTable)}", 2, '<mark>', '</mark>', '...', 32) as snippet,
|
|
234
310
|
${sql.raw(bm25Expr)} as score
|
|
235
311
|
FROM "${sql.raw(ftsTable)}" f
|
|
@@ -334,14 +410,23 @@ export async function getSuggestions(
|
|
|
334
410
|
}
|
|
335
411
|
|
|
336
412
|
const suggestions: Suggestion[] = [];
|
|
413
|
+
const ftsManager = new FTSManager(db);
|
|
414
|
+
const titleColumns = await ftsManager.getCollectionsWithTitleColumn(collections);
|
|
337
415
|
|
|
338
416
|
for (const collection of collections) {
|
|
339
|
-
const ftsManager = new FTSManager(db);
|
|
340
417
|
const config = await ftsManager.getSearchConfig(collection);
|
|
341
418
|
if (!config?.enabled) {
|
|
342
419
|
continue;
|
|
343
420
|
}
|
|
344
421
|
|
|
422
|
+
// Suggestions are title-based (Suggestion.title is required and the
|
|
423
|
+
// query filters on `c.title IS NOT NULL`). Collections without a
|
|
424
|
+
// `title` field can't produce one, and selecting `c.title` would
|
|
425
|
+
// error, so skip them. See #1178.
|
|
426
|
+
if (!titleColumns.has(collection)) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
345
430
|
// Validate before raw SQL interpolation
|
|
346
431
|
validateIdentifier(collection, "collection slug");
|
|
347
432
|
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hreflang alternate resolution for translated content (#1690).
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the sitemap route's semantics so the render path and the
|
|
5
|
+
* sitemap always agree:
|
|
6
|
+
*
|
|
7
|
+
* - Alternates are emitted per published locale variant of a
|
|
8
|
+
* translation group, including a self-referencing entry (Google
|
|
9
|
+
* recommends the page annotate itself).
|
|
10
|
+
* - `x-default` points at the default-locale variant, falling back to
|
|
11
|
+
* the first routable variant when the default locale has no
|
|
12
|
+
* translation.
|
|
13
|
+
* - Variants whose locale isn't in the configured `i18n.locales` list
|
|
14
|
+
* are dropped: linking to a route the site can't serve is worse than
|
|
15
|
+
* no link at all.
|
|
16
|
+
* - Variants flagged `noindex` in the SEO panel are excluded — the
|
|
17
|
+
* sitemap doesn't list them, and pointing search engines at a page
|
|
18
|
+
* that asks not to be indexed is a contradictory signal. When the
|
|
19
|
+
* entry itself is noindex the result is empty: a page opting out of
|
|
20
|
+
* indexing shouldn't announce an hreflang set at all.
|
|
21
|
+
* - When i18n is disabled (or no absolute site URL can be resolved,
|
|
22
|
+
* which hreflang requires) the result is empty and no queries run.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { Kysely } from "kysely";
|
|
26
|
+
|
|
27
|
+
import { ContentRepository } from "../database/repositories/content.js";
|
|
28
|
+
import type { Database } from "../database/types.js";
|
|
29
|
+
import { getI18nConfig, isI18nEnabled } from "../i18n/config.js";
|
|
30
|
+
import { interpolateUrlPattern, localizePath } from "../i18n/resolve.js";
|
|
31
|
+
import { requestCached } from "../request-cache.js";
|
|
32
|
+
import { getCollectionInfoWithDb } from "../schema/query.js";
|
|
33
|
+
import { getSiteSettingsWithDb } from "../settings/index.js";
|
|
34
|
+
|
|
35
|
+
const TRAILING_SLASH_RE = /\/$/;
|
|
36
|
+
const ABSOLUTE_URL_RE = /^https?:\/\//i;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* IDs of variants flagged `noindex` in the SEO panel. Entries without
|
|
40
|
+
* an `_emdash_seo` row are indexable by default (same as the sitemap).
|
|
41
|
+
* The id list is bounded by the number of configured locales, so no
|
|
42
|
+
* chunking is needed.
|
|
43
|
+
*/
|
|
44
|
+
async function findNoindexIds(
|
|
45
|
+
db: Kysely<Database>,
|
|
46
|
+
collection: string,
|
|
47
|
+
ids: string[],
|
|
48
|
+
): Promise<Set<string>> {
|
|
49
|
+
if (ids.length === 0) return new Set();
|
|
50
|
+
const rows = await db
|
|
51
|
+
.selectFrom("_emdash_seo")
|
|
52
|
+
.select("content_id")
|
|
53
|
+
.where("collection", "=", collection)
|
|
54
|
+
.where("content_id", "in", ids)
|
|
55
|
+
.where("seo_no_index", "=", 1)
|
|
56
|
+
.execute();
|
|
57
|
+
return new Set(rows.map((r) => r.content_id));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** A single alternate link, ready to render as `<link rel="alternate">`. */
|
|
61
|
+
export interface HreflangAlternate {
|
|
62
|
+
/** Locale code, or `"x-default"` for the default variant */
|
|
63
|
+
hreflang: string;
|
|
64
|
+
/** Absolute URL of the variant */
|
|
65
|
+
href: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface HreflangOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Absolute site origin (e.g. `https://example.com`) used to build
|
|
71
|
+
* the alternate URLs. Falls back to the site settings URL; when
|
|
72
|
+
* neither is available the result is empty, since hreflang
|
|
73
|
+
* requires fully-qualified URLs.
|
|
74
|
+
*/
|
|
75
|
+
siteUrl?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Resolve hreflang alternates for a content entry.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```astro
|
|
83
|
+
* ---
|
|
84
|
+
* import { getHreflangAlternates } from "emdash";
|
|
85
|
+
*
|
|
86
|
+
* const alternates = await getHreflangAlternates("posts", entry.data.id, {
|
|
87
|
+
* siteUrl: Astro.url.origin,
|
|
88
|
+
* });
|
|
89
|
+
* ---
|
|
90
|
+
* <head>
|
|
91
|
+
* {alternates.map((a) => <link rel="alternate" hreflang={a.hreflang} href={a.href} />)}
|
|
92
|
+
* </head>
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export async function getHreflangAlternates(
|
|
96
|
+
collection: string,
|
|
97
|
+
entryId: string,
|
|
98
|
+
options: HreflangOptions = {},
|
|
99
|
+
): Promise<HreflangAlternate[]> {
|
|
100
|
+
if (!isI18nEnabled()) return [];
|
|
101
|
+
const key = `hreflang:${collection}:${entryId}:${options.siteUrl ?? ""}`;
|
|
102
|
+
return requestCached(key, async () => {
|
|
103
|
+
const { getDb } = await import("../loader.js");
|
|
104
|
+
const db = await getDb();
|
|
105
|
+
return getHreflangAlternatesWithDb(db, collection, entryId, options);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolve hreflang alternates with an explicit db handle.
|
|
111
|
+
*
|
|
112
|
+
* @internal Use `getHreflangAlternates()` in templates. This variant is
|
|
113
|
+
* for routes/components that already have a database handle.
|
|
114
|
+
*/
|
|
115
|
+
export async function getHreflangAlternatesWithDb(
|
|
116
|
+
db: Kysely<Database>,
|
|
117
|
+
collection: string,
|
|
118
|
+
entryId: string,
|
|
119
|
+
options: HreflangOptions = {},
|
|
120
|
+
): Promise<HreflangAlternate[]> {
|
|
121
|
+
if (!isI18nEnabled()) return [];
|
|
122
|
+
|
|
123
|
+
let siteUrl = options.siteUrl;
|
|
124
|
+
if (!siteUrl) {
|
|
125
|
+
const settings = await getSiteSettingsWithDb(db);
|
|
126
|
+
siteUrl = settings.url;
|
|
127
|
+
}
|
|
128
|
+
// hreflang requires fully-qualified URLs; a relative site URL would
|
|
129
|
+
// produce invalid output (and EmDashHead's isSafeHref would drop it).
|
|
130
|
+
if (!siteUrl || !ABSOLUTE_URL_RE.test(siteUrl)) return [];
|
|
131
|
+
siteUrl = siteUrl.replace(TRAILING_SLASH_RE, "");
|
|
132
|
+
|
|
133
|
+
const repo = new ContentRepository(db);
|
|
134
|
+
const item = await repo.findByIdOrSlug(collection, entryId);
|
|
135
|
+
if (!item) return [];
|
|
136
|
+
|
|
137
|
+
// Legacy rows imported before i18n may have no translation_group;
|
|
138
|
+
// treat them as a single-variant group.
|
|
139
|
+
const group = item.translationGroup || item.id;
|
|
140
|
+
let variants = await repo.findTranslations(collection, group);
|
|
141
|
+
if (variants.length === 0) variants = [item];
|
|
142
|
+
|
|
143
|
+
let published = variants.filter((v) => v.status === "published");
|
|
144
|
+
if (published.length === 0) return [];
|
|
145
|
+
|
|
146
|
+
// Exclude noindex variants, matching the sitemap's `_emdash_seo`
|
|
147
|
+
// filter so head and sitemap agree on which variants are
|
|
148
|
+
// discoverable. When the requested entry itself is noindex, emit
|
|
149
|
+
// nothing: a page opting out of indexing shouldn't announce an
|
|
150
|
+
// hreflang set.
|
|
151
|
+
const noindexIds = await findNoindexIds(
|
|
152
|
+
db,
|
|
153
|
+
collection,
|
|
154
|
+
published.map((v) => v.id),
|
|
155
|
+
);
|
|
156
|
+
if (noindexIds.has(item.id)) return [];
|
|
157
|
+
published = published.filter((v) => !noindexIds.has(v.id));
|
|
158
|
+
|
|
159
|
+
const info = await getCollectionInfoWithDb(db, collection);
|
|
160
|
+
const urlPattern = info?.urlPattern ?? null;
|
|
161
|
+
|
|
162
|
+
const resolved: Array<{ locale: string; href: string }> = [];
|
|
163
|
+
for (const variant of published) {
|
|
164
|
+
const locale = variant.locale || "en";
|
|
165
|
+
const path = interpolateUrlPattern({
|
|
166
|
+
pattern: urlPattern,
|
|
167
|
+
collection,
|
|
168
|
+
slug: variant.slug || variant.id,
|
|
169
|
+
id: variant.id,
|
|
170
|
+
});
|
|
171
|
+
const localized = await localizePath(path, locale);
|
|
172
|
+
if (localized === null) continue;
|
|
173
|
+
resolved.push({ locale, href: `${siteUrl}${localized}` });
|
|
174
|
+
}
|
|
175
|
+
if (resolved.length === 0) return [];
|
|
176
|
+
|
|
177
|
+
resolved.sort((a, b) => a.locale.localeCompare(b.locale));
|
|
178
|
+
const alternates: HreflangAlternate[] = resolved.map((r) => ({
|
|
179
|
+
hreflang: r.locale,
|
|
180
|
+
href: r.href,
|
|
181
|
+
}));
|
|
182
|
+
|
|
183
|
+
// x-default: default-locale variant, else the first routable one.
|
|
184
|
+
// Same fallback the sitemap route uses.
|
|
185
|
+
const defaultLocale = getI18nConfig()?.defaultLocale;
|
|
186
|
+
const xDefault = resolved.find((r) => r.locale === defaultLocale) ?? resolved[0];
|
|
187
|
+
if (xDefault) {
|
|
188
|
+
alternates.push({ hreflang: "x-default", href: xDefault.href });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return alternates;
|
|
192
|
+
}
|
package/src/seo/index.ts
CHANGED
|
@@ -32,6 +32,9 @@
|
|
|
32
32
|
import type { ContentSeo } from "../database/repositories/types.js";
|
|
33
33
|
import { buildSeoImageUrl } from "./media-url.js";
|
|
34
34
|
|
|
35
|
+
export { getHreflangAlternates, getHreflangAlternatesWithDb } from "./hreflang.js";
|
|
36
|
+
export type { HreflangAlternate, HreflangOptions } from "./hreflang.js";
|
|
37
|
+
|
|
35
38
|
const TRAILING_SLASH_RE = /\/$/;
|
|
36
39
|
const ABSOLUTE_URL_RE = /^https?:\/\//i;
|
|
37
40
|
|
package/src/taxonomies/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { peekRequestCache, requestCached, setRequestCacheEntry } from "../reques
|
|
|
24
24
|
import { getRequestContext } from "../request-context.js";
|
|
25
25
|
import { chunks, SQL_BATCH_SIZE } from "../utils/chunks.js";
|
|
26
26
|
import { isMissingTableError } from "../utils/db-errors.js";
|
|
27
|
+
import { fetchVisibleTermCounts } from "./term-counts.js";
|
|
27
28
|
import type { TaxonomyDef, TaxonomyTerm, TaxonomyTermRow } from "./types.js";
|
|
28
29
|
|
|
29
30
|
export interface TaxonomyQueryOptions {
|
|
@@ -214,6 +215,18 @@ export async function getTaxonomyDef(
|
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Object-cache namespaces for values that embed visible term counts: the
|
|
220
|
+
* taxonomy epoch (term/assignment writes) plus each counted collection's
|
|
221
|
+
* content epoch, so publishing, unpublishing, or trashing an entry
|
|
222
|
+
* invalidates the cached count promptly. A scheduled entry becoming due
|
|
223
|
+
* flips visibility without a write, so that staleness stays bounded by the
|
|
224
|
+
* cache entry's TTL.
|
|
225
|
+
*/
|
|
226
|
+
function termCountNamespaces(collections: string[]): string[] {
|
|
227
|
+
return [...Array.from(new Set(collections), contentNamespace), CacheNamespace.TAXONOMIES];
|
|
228
|
+
}
|
|
229
|
+
|
|
217
230
|
/**
|
|
218
231
|
* All terms of a taxonomy in a specific locale (flat for non-hierarchical,
|
|
219
232
|
* tree for hierarchical).
|
|
@@ -223,37 +236,38 @@ export async function getTaxonomyTerms(
|
|
|
223
236
|
options: TaxonomyQueryOptions = {},
|
|
224
237
|
): Promise<TaxonomyTerm[]> {
|
|
225
238
|
const locale = resolveLocale(options.locale);
|
|
226
|
-
return requestCached(`taxonomy-terms:${taxonomyName}:${locale ?? "*"}`, () =>
|
|
227
|
-
|
|
228
|
-
|
|
239
|
+
return requestCached(`taxonomy-terms:${taxonomyName}:${locale ?? "*"}`, async () => {
|
|
240
|
+
const def = await getTaxonomyDef(taxonomyName, options);
|
|
241
|
+
if (!def) return [];
|
|
242
|
+
return cachedQuery({
|
|
243
|
+
namespace: termCountNamespaces(def.collections),
|
|
229
244
|
key: `terms:${taxonomyName}:${locale ?? "*"}`,
|
|
230
|
-
load: () => loadTaxonomyTerms(
|
|
231
|
-
})
|
|
232
|
-
);
|
|
245
|
+
load: () => loadTaxonomyTerms(def, locale),
|
|
246
|
+
});
|
|
247
|
+
});
|
|
233
248
|
}
|
|
234
249
|
|
|
235
250
|
async function loadTaxonomyTerms(
|
|
236
|
-
|
|
251
|
+
def: TaxonomyDef,
|
|
237
252
|
locale: string | undefined,
|
|
238
|
-
options: TaxonomyQueryOptions,
|
|
239
253
|
): Promise<TaxonomyTerm[]> {
|
|
240
254
|
const db = await getDb();
|
|
241
255
|
|
|
242
|
-
const def = await getTaxonomyDef(taxonomyName, options);
|
|
243
|
-
if (!def) return [];
|
|
244
|
-
|
|
245
256
|
let termsQuery = db
|
|
246
257
|
.selectFrom("taxonomies")
|
|
247
258
|
.selectAll()
|
|
248
|
-
.where("name", "=",
|
|
259
|
+
.where("name", "=", def.name)
|
|
249
260
|
.orderBy("label", "asc");
|
|
250
261
|
if (locale !== undefined) termsQuery = termsQuery.where("locale", "=", locale);
|
|
251
|
-
const rows = await termsQuery.execute();
|
|
252
262
|
|
|
253
263
|
// Counts are keyed by translation_group (what the pivot stores) and are
|
|
254
|
-
// locale-independent
|
|
255
|
-
//
|
|
256
|
-
|
|
264
|
+
// locale-independent. Only publicly visible entries are counted (#581);
|
|
265
|
+
// the map is request-cached so a term detail rendered on the same page
|
|
266
|
+
// reuses it. Independent of the terms query, so run both concurrently.
|
|
267
|
+
const [rows, counts] = await Promise.all([
|
|
268
|
+
termsQuery.execute(),
|
|
269
|
+
getVisibleTermCounts(def.name, def.collections),
|
|
270
|
+
]);
|
|
257
271
|
|
|
258
272
|
const flatTerms: TaxonomyTermRow[] = rows.map((row) => ({
|
|
259
273
|
id: row.id,
|
|
@@ -282,23 +296,24 @@ async function loadTaxonomyTerms(
|
|
|
282
296
|
}
|
|
283
297
|
|
|
284
298
|
/**
|
|
285
|
-
* Per-translation-group usage counts
|
|
286
|
-
*
|
|
287
|
-
* translation_group),
|
|
288
|
-
*
|
|
299
|
+
* Per-translation-group visible-usage counts for one taxonomy, in a single
|
|
300
|
+
* round-trip (see `fetchVisibleTermCounts`). Counts are locale-independent
|
|
301
|
+
* (the pivot stores translation_group), and the request-cached map is shared
|
|
302
|
+
* by every consumer in the render — the widget (`getTaxonomyTerms`) and the
|
|
303
|
+
* single-term page (`getTerm`) never issue separate count queries.
|
|
289
304
|
*/
|
|
290
|
-
function
|
|
291
|
-
|
|
305
|
+
function getVisibleTermCounts(
|
|
306
|
+
taxonomyName: string,
|
|
307
|
+
collections: string[],
|
|
308
|
+
): Promise<Map<string, number>> {
|
|
309
|
+
// The collection scope is part of the key: per-locale rows of the same def
|
|
310
|
+
// can drift in their declared collections, and a caller may pass a narrower
|
|
311
|
+
// scope. Identical inputs (the widget + term-page hot path) still share one
|
|
312
|
+
// entry.
|
|
313
|
+
const scope = [...new Set(collections)].toSorted().join(",");
|
|
314
|
+
return requestCached(`taxonomy-term-counts:${taxonomyName}:${scope}`, async () => {
|
|
292
315
|
const db = await getDb();
|
|
293
|
-
|
|
294
|
-
.selectFrom("content_taxonomies")
|
|
295
|
-
.select(["taxonomy_id"])
|
|
296
|
-
.select((eb) => eb.fn.count<number>("entry_id").as("count"))
|
|
297
|
-
.groupBy("taxonomy_id")
|
|
298
|
-
.execute();
|
|
299
|
-
const counts = new Map<string, number>();
|
|
300
|
-
for (const row of countsResult) counts.set(row.taxonomy_id, row.count);
|
|
301
|
-
return counts;
|
|
316
|
+
return fetchVisibleTermCounts(db, taxonomyName, collections);
|
|
302
317
|
});
|
|
303
318
|
}
|
|
304
319
|
|
|
@@ -313,13 +328,16 @@ export async function getTerm(
|
|
|
313
328
|
options: TaxonomyQueryOptions = {},
|
|
314
329
|
): Promise<TaxonomyTerm | null> {
|
|
315
330
|
const chain = resolveLocaleChain(options.locale);
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
331
|
+
// The def supplies the collections the visible count is scoped to. It is
|
|
332
|
+
// resolved before cachedQuery so the entry lives under each collection's
|
|
333
|
+
// content namespace — publishing or unpublishing an entry invalidates the
|
|
334
|
+
// embedded count (see termCountNamespaces).
|
|
335
|
+
const def = await getTaxonomyDef(taxonomyName, options);
|
|
336
|
+
const collections = def?.collections ?? [];
|
|
319
337
|
return cachedQuery({
|
|
320
|
-
namespace:
|
|
338
|
+
namespace: termCountNamespaces(collections),
|
|
321
339
|
key: `term:${taxonomyName}:${slug}:${chain.join(",")}`,
|
|
322
|
-
load: () => loadTerm(taxonomyName, slug, chain),
|
|
340
|
+
load: () => loadTerm(taxonomyName, slug, chain, collections),
|
|
323
341
|
});
|
|
324
342
|
}
|
|
325
343
|
|
|
@@ -327,6 +345,7 @@ async function loadTerm(
|
|
|
327
345
|
taxonomyName: string,
|
|
328
346
|
slug: string,
|
|
329
347
|
chain: string[],
|
|
348
|
+
collections: string[],
|
|
330
349
|
): Promise<TaxonomyTerm | null> {
|
|
331
350
|
const db = await getDb();
|
|
332
351
|
|
|
@@ -360,17 +379,15 @@ async function loadTerm(
|
|
|
360
379
|
const termLocale = row.locale;
|
|
361
380
|
if (termLocale) childrenQuery = childrenQuery.where("locale", "=", termLocale);
|
|
362
381
|
|
|
363
|
-
// The usage
|
|
364
|
-
// so run them concurrently to save a round trip on remote
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
.where("taxonomy_id", "=", row.translation_group ?? row.id)
|
|
370
|
-
.executeTakeFirst(),
|
|
382
|
+
// The visible-usage counts and children queries both depend only on the
|
|
383
|
+
// term row, so run them concurrently to save a round trip on remote
|
|
384
|
+
// databases. The counts map is request-cached per taxonomy — on a page
|
|
385
|
+
// that also renders the taxonomy widget it's a free Map lookup.
|
|
386
|
+
const [counts, childRows] = await Promise.all([
|
|
387
|
+
getVisibleTermCounts(taxonomyName, collections),
|
|
371
388
|
childrenQuery.execute(),
|
|
372
389
|
]);
|
|
373
|
-
const count =
|
|
390
|
+
const count = counts.get(row.translation_group ?? row.id) ?? 0;
|
|
374
391
|
|
|
375
392
|
const children = childRows.map<TaxonomyTerm>((child) => ({
|
|
376
393
|
id: child.id,
|