emdash 0.37.0 → 0.38.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/{activation-K0kGBWuJ.mjs → activation-DyNTe_Tl.mjs} +3 -3
- package/dist/{activation-K0kGBWuJ.mjs.map → activation-DyNTe_Tl.mjs.map} +1 -1
- package/dist/{adapters-BDQwg236.d.mts → adapters-DCEgOBXi.d.mts} +1 -1
- package/dist/{adapters-BDQwg236.d.mts.map → adapters-DCEgOBXi.d.mts.map} +1 -1
- package/dist/{allowed-origins-BGglilcs.mjs → allowed-origins-GFUVavRj.mjs} +2 -2
- package/dist/{allowed-origins-BGglilcs.mjs.map → allowed-origins-GFUVavRj.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +23 -22
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +5 -5
- package/dist/{api-Dz6rgoa3.mjs → api-BvxJmnFY.mjs} +141 -87
- package/dist/api-BvxJmnFY.mjs.map +1 -0
- package/dist/{api-tokens-BSsQACNe.mjs → api-tokens-xpGqMHrC.mjs} +2 -2
- package/dist/{api-tokens-BSsQACNe.mjs.map → api-tokens-xpGqMHrC.mjs.map} +1 -1
- package/dist/{apply-B4SIOxR1.mjs → apply-DLmM3R7f.mjs} +25 -21
- package/dist/apply-DLmM3R7f.mjs.map +1 -0
- package/dist/{config-it29yDLA.mjs → artifact-fetch-P2JXVwOq.mjs} +8 -199
- package/dist/artifact-fetch-P2JXVwOq.mjs.map +1 -0
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +37 -11
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.mjs +6 -6
- package/dist/astro/middleware/media-usage-write-fence.mjs +3 -3
- package/dist/astro/middleware/redirect.mjs +7 -5
- package/dist/astro/middleware/redirect.mjs.map +1 -1
- package/dist/astro/middleware/request-context.mjs +3 -3
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +10 -10
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +154 -125
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +16 -16
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +16 -16
- package/dist/astro/routes/api/admin/bylines/index.mjs +16 -16
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +11 -11
- package/dist/astro/routes/api/admin/comments/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/bulk.mjs +8 -8
- package/dist/astro/routes/api/admin/comments/counts.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/index.mjs +8 -8
- 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/media-usage/activation.mjs +13 -13
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +15 -15
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +15 -15
- package/dist/astro/routes/api/admin/media-usage/progress.mjs +13 -13
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +16 -16
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs +15 -15
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +15 -15
- 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 +56 -53
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -53
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +54 -51
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +54 -51
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +55 -52
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +55 -52
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +54 -51
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +54 -51
- 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 +55 -52
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +54 -51
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +55 -52
- 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 +56 -53
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +5 -4
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +56 -53
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +55 -52
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +54 -51
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +54 -51
- 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 +2 -2
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +54 -51
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
- package/dist/astro/routes/api/admin/users/index.mjs +6 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
- package/dist/astro/routes/api/auth/invite/complete.mjs +11 -11
- package/dist/astro/routes/api/auth/invite/index.mjs +8 -8
- package/dist/astro/routes/api/auth/invite/register-options.mjs +10 -10
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +10 -10
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +7 -7
- package/dist/astro/routes/api/auth/mode.mjs +2 -2
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +12 -12
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -11
- package/dist/astro/routes/api/auth/signup/complete.mjs +11 -11
- package/dist/astro/routes/api/auth/signup/request.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +12 -12
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +13 -8
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/lock.d.mts +10 -0
- package/dist/astro/routes/api/content/_collection_/_id_/lock.d.mts.map +1 -0
- package/dist/astro/routes/api/content/_collection_/_id_/lock.mjs +66 -0
- package/dist/astro/routes/api/content/_collection_/_id_/lock.mjs.map +1 -0
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/publish.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +14 -9
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +19 -17
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +19 -17
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +15 -8
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +15 -14
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +13 -8
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +23 -11
- package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/index.mjs +14 -9
- package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
- package/dist/astro/routes/api/dashboard.mjs +6 -6
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +16 -16
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +14 -13
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
- package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +10 -10
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +13 -13
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +16 -16
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +27 -26
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/mcp.mjs +71 -55
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +10 -10
- package/dist/astro/routes/api/media/_id_/replace.mjs +8 -8
- package/dist/astro/routes/api/media/_id_/upload.mjs +5 -5
- package/dist/astro/routes/api/media/_id_/usage.mjs +18 -18
- package/dist/astro/routes/api/media/_id_.mjs +17 -17
- package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
- package/dist/astro/routes/api/media/folders/_id_.mjs +8 -8
- package/dist/astro/routes/api/media/folders/index.mjs +8 -8
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +9 -9
- package/dist/astro/routes/api/media.mjs +21 -21
- 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 +5 -5
- package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
- package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +5 -5
- package/dist/astro/routes/api/oauth/token/revoke.mjs +5 -5
- package/dist/astro/routes/api/oauth/token.mjs +5 -5
- package/dist/astro/routes/api/openapi.json.mjs +84 -18
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
- package/dist/astro/routes/api/redirects/404s/index.mjs +12 -11
- package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/404s/summary.mjs +12 -11
- package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/_id_.mjs +13 -12
- package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/index.mjs +13 -12
- package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/index.mjs +19 -17
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/translations.mjs +14 -12
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/relations/index.mjs +19 -17
- package/dist/astro/routes/api/relations/index.mjs.map +1 -1
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +54 -51
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +54 -51
- 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 +54 -51
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +54 -51
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +54 -51
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/reorder.mjs +54 -51
- package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +13 -11
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +54 -51
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +54 -51
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +12 -11
- package/dist/astro/routes/api/search/enable.mjs.map +1 -1
- package/dist/astro/routes/api/search/index.mjs +11 -10
- package/dist/astro/routes/api/search/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/rebuild.mjs +12 -11
- package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
- package/dist/astro/routes/api/search/stats.mjs +7 -6
- package/dist/astro/routes/api/search/stats.mjs.map +1 -1
- package/dist/astro/routes/api/search/suggest.mjs +11 -10
- package/dist/astro/routes/api/search/suggest.mjs.map +1 -1
- package/dist/astro/routes/api/sections/_slug_.mjs +11 -10
- package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/sections/index.mjs +11 -10
- package/dist/astro/routes/api/sections/index.mjs.map +1 -1
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +7 -6
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +6 -5
- package/dist/astro/routes/api/settings/backups/archives/index.mjs.map +1 -1
- package/dist/astro/routes/api/settings/backups/export.mjs +7 -6
- package/dist/astro/routes/api/settings/backups/export.mjs.map +1 -1
- package/dist/astro/routes/api/settings/backups/index.mjs +8 -7
- package/dist/astro/routes/api/settings/backups/index.mjs.map +1 -1
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +14 -14
- package/dist/astro/routes/api/setup/admin-verify.mjs +12 -12
- package/dist/astro/routes/api/setup/admin.mjs +11 -11
- package/dist/astro/routes/api/setup/dev-bypass.mjs +33 -31
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +33 -31
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +8 -7
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +16 -15
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +16 -15
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +16 -15
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +16 -15
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +15 -14
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_.mjs +16 -15
- package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.mjs +16 -15
- package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
- package/dist/astro/routes/api/themes/preview.mjs +5 -5
- package/dist/astro/routes/api/typegen.mjs +4 -4
- 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 +7 -7
- package/dist/astro/routes/api/widget-areas/index.mjs +12 -12
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +6 -6
- 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 +7 -7
- package/dist/astro/types.d.mts +32 -17
- 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-Bkf_UHhD.mjs → authorize-C2QHlWSS.mjs} +2 -2
- package/dist/{authorize-Bkf_UHhD.mjs.map → authorize-C2QHlWSS.mjs.map} +1 -1
- package/dist/{backup-rwVAl9NI.mjs → backup-oS8ft5Cm.mjs} +5 -5
- package/dist/{backup-rwVAl9NI.mjs.map → backup-oS8ft5Cm.mjs.map} +1 -1
- package/dist/{bundle-C8S2Zl6U.mjs → bundle-CL0JjIPx.mjs} +4 -4
- package/dist/{bundle-C8S2Zl6U.mjs.map → bundle-CL0JjIPx.mjs.map} +1 -1
- package/dist/{byline-DuhF7NWl.mjs → byline-Vdi85CsZ.mjs} +6 -6
- package/dist/{byline-DuhF7NWl.mjs.map → byline-Vdi85CsZ.mjs.map} +1 -1
- package/dist/{byline-fields-BovY4KYM.mjs → byline-fields-DPVRFMz2.mjs} +2 -2
- package/dist/{byline-fields-BovY4KYM.mjs.map → byline-fields-DPVRFMz2.mjs.map} +1 -1
- package/dist/{byline-registry-Bpu9-MVs.mjs → byline-registry-BxDGIO0I.mjs} +2 -2
- package/dist/{byline-registry-Bpu9-MVs.mjs.map → byline-registry-BxDGIO0I.mjs.map} +1 -1
- package/dist/{bylines-CJ5nd6Lw.mjs → bylines-BX8QhcTk.mjs} +7 -7
- package/dist/{bylines-CJ5nd6Lw.mjs.map → bylines-BX8QhcTk.mjs.map} +1 -1
- package/dist/{bylines-RwBAeOF1.mjs → bylines-w-E1IEQd.mjs} +3 -3
- package/dist/{bylines-RwBAeOF1.mjs.map → bylines-w-E1IEQd.mjs.map} +1 -1
- package/dist/{cache-BF5Kvn59.mjs → cache--7HKu5Xm.mjs} +4 -4
- package/dist/{cache-BF5Kvn59.mjs.map → cache--7HKu5Xm.mjs.map} +1 -1
- package/dist/{capture-triggers-CNW1q3HK.mjs → capture-triggers-CeAsC76Z.mjs} +2 -2
- package/dist/{capture-triggers-CNW1q3HK.mjs.map → capture-triggers-CeAsC76Z.mjs.map} +1 -1
- package/dist/{challenge-store-DQN_Yrck.mjs → challenge-store-BIOak-tp.mjs} +1 -1
- package/dist/{challenge-store-DQN_Yrck.mjs.map → challenge-store-BIOak-tp.mjs.map} +1 -1
- package/dist/{chrome-tags-D8fiZG94.mjs → chrome-tags-DzDQBCjX.mjs} +1 -1
- package/dist/{chrome-tags-D8fiZG94.mjs.map → chrome-tags-DzDQBCjX.mjs.map} +1 -1
- package/dist/{chunks-C4szrD8o.mjs → chunks-VG_tyqnP.mjs} +2 -2
- package/dist/{chunks-C4szrD8o.mjs.map → chunks-VG_tyqnP.mjs.map} +1 -1
- package/dist/cli/index.mjs +66 -38
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +17 -7
- package/dist/client/index.d.mts.map +1 -1
- package/dist/client/index.mjs +17 -12
- package/dist/client/index.mjs.map +1 -1
- package/dist/{collection-deletion-Bj07NHbj.mjs → collection-deletion-BCwaWTDw.mjs} +4 -4
- package/dist/{collection-deletion-Bj07NHbj.mjs.map → collection-deletion-BCwaWTDw.mjs.map} +1 -1
- package/dist/collection-slugs-cache-7lTmNzeA.mjs +85 -0
- package/dist/collection-slugs-cache-7lTmNzeA.mjs.map +1 -0
- package/dist/{comment-reaction-BhrvSHml.mjs → comment-reaction-CDyvPQRj.mjs} +2 -2
- package/dist/{comment-reaction-BhrvSHml.mjs.map → comment-reaction-CDyvPQRj.mjs.map} +1 -1
- package/dist/{comments-Ct4ensnf.mjs → comments-VzmVLyaC.mjs} +1 -1
- package/dist/{comments-Ct4ensnf.mjs.map → comments-VzmVLyaC.mjs.map} +1 -1
- package/dist/{components-DJbo1z0S.mjs → components-CL6Wkdk-.mjs} +1 -1
- package/dist/{components-DJbo1z0S.mjs.map → components-CL6Wkdk-.mjs.map} +1 -1
- package/dist/config--eWjQJE5.mjs +217 -0
- package/dist/config--eWjQJE5.mjs.map +1 -0
- package/dist/{index-BJNMCWmu.d.mts → config-CGAhyOjP.d.mts} +138 -47
- package/dist/config-CGAhyOjP.d.mts.map +1 -0
- package/dist/{config-B9eZ340f.d.mts → config-ChFzshGd.d.mts} +1 -1
- package/dist/{config-B9eZ340f.d.mts.map → config-ChFzshGd.d.mts.map} +1 -1
- package/dist/{content-DjvM0T92.mjs → content-CNfMWuhY.mjs} +121 -5
- package/dist/content-CNfMWuhY.mjs.map +1 -0
- package/dist/{content-list-query-Btqd4uV2.d.mts → content-list-query-B8d4UQp9.d.mts} +1 -1
- package/dist/{content-list-query-Btqd4uV2.d.mts.map → content-list-query-B8d4UQp9.d.mts.map} +1 -1
- package/dist/{content-refresh-Bwp-8yGy.mjs → content-refresh-GTMyi2Xy.mjs} +8 -8
- package/dist/{content-refresh-Bwp-8yGy.mjs.map → content-refresh-GTMyi2Xy.mjs.map} +1 -1
- package/dist/{content-repair-Czdp7jz5.mjs → content-repair-CIMt5C0U.mjs} +4 -4
- package/dist/{content-repair-Czdp7jz5.mjs.map → content-repair-CIMt5C0U.mjs.map} +1 -1
- package/dist/{context-CX6gxOLW.mjs → context-C9PB8vGd.mjs} +265 -25
- package/dist/context-C9PB8vGd.mjs.map +1 -0
- package/dist/{cron-C6o7VppJ.mjs → cron-DHAy9oyq.mjs} +1 -1
- package/dist/{cron-C6o7VppJ.mjs.map → cron-DHAy9oyq.mjs.map} +1 -1
- package/dist/{dashboard-C2kTeeRI.mjs → dashboard-BNSvAN8A.mjs} +4 -4
- package/dist/{dashboard-C2kTeeRI.mjs.map → dashboard-BNSvAN8A.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +2 -2
- package/dist/db/libsql-migrations.d.mts +1 -1
- package/dist/db/libsql-migrations.mjs +5 -5
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres-migrations.d.mts +1 -1
- package/dist/db/postgres-migrations.mjs +5 -5
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite-migrations.d.mts +1 -1
- package/dist/db/sqlite-migrations.mjs +5 -5
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.mjs +1 -1
- package/dist/{default-Clra32mw.mjs → default-ByZ0JqZo.mjs} +1 -1
- package/dist/{default-Clra32mw.mjs.map → default-ByZ0JqZo.mjs.map} +1 -1
- package/dist/{deferred-tasks-CvDETTJO.d.mts → deferred-tasks-Cu4NSJpa.d.mts} +1 -1
- package/dist/{deferred-tasks-CvDETTJO.d.mts.map → deferred-tasks-Cu4NSJpa.d.mts.map} +1 -1
- package/dist/{dev-typegen-DzOPfsNj.mjs → dev-typegen-DHT7lcMU.mjs} +1 -1
- package/dist/{dev-typegen-DzOPfsNj.mjs.map → dev-typegen-DHT7lcMU.mjs.map} +1 -1
- package/dist/{device-flow-D2oCWvLJ.mjs → device-flow-Cf6bhCCT.mjs} +4 -4
- package/dist/{device-flow-D2oCWvLJ.mjs.map → device-flow-Cf6bhCCT.mjs.map} +1 -1
- package/dist/{dialect-helpers-BSbfaO3F.mjs → dialect-helpers-BcQvkHgn.mjs} +83 -2
- package/dist/dialect-helpers-BcQvkHgn.mjs.map +1 -0
- package/dist/{direct-executor-Bfa5_ToE.mjs → direct-executor-yAyUGSqB.mjs} +3 -3
- package/dist/{direct-executor-Bfa5_ToE.mjs.map → direct-executor-yAyUGSqB.mjs.map} +1 -1
- package/dist/{dist-89xCrGqi.mjs → dist-BAsh8dbf.mjs} +1 -1
- package/dist/{dist-89xCrGqi.mjs.map → dist-BAsh8dbf.mjs.map} +1 -1
- package/dist/{email-console-Dzb6uGYa.mjs → email-console-DNup1Fku.mjs} +1 -1
- package/dist/{email-console-Dzb6uGYa.mjs.map → email-console-DNup1Fku.mjs.map} +1 -1
- package/dist/{enrich-BX0IDp4z.mjs → enrich-t2Xvjdqa.mjs} +3 -3
- package/dist/{enrich-BX0IDp4z.mjs.map → enrich-t2Xvjdqa.mjs.map} +1 -1
- package/dist/entry-lock-BEr8gDkG.mjs +163 -0
- package/dist/entry-lock-BEr8gDkG.mjs.map +1 -0
- package/dist/{relations-DnRK4YIT.d.mts → entry-lock-CGAtrwAR.d.mts} +92 -37
- package/dist/entry-lock-CGAtrwAR.d.mts.map +1 -0
- package/dist/{redirects-BLNOWQTn.mjs → entry-lock-DIYHAaSw.mjs} +57 -14
- package/dist/entry-lock-DIYHAaSw.mjs.map +1 -0
- package/dist/entry-locks-BEcX7ET0.mjs +167 -0
- package/dist/entry-locks-BEcX7ET0.mjs.map +1 -0
- package/dist/{error-DFrQjppn.mjs → error-BDcFyoRt.mjs} +2 -2
- package/dist/{error-DFrQjppn.mjs.map → error-BDcFyoRt.mjs.map} +1 -1
- package/dist/{errors-DsVoDUjJ.mjs → errors-DvMdNrgf.mjs} +8 -1
- package/dist/errors-DvMdNrgf.mjs.map +1 -0
- package/dist/{escape-HmHTC1Is.mjs → escape-B3asJC6W.mjs} +1 -1
- package/dist/{escape-HmHTC1Is.mjs.map → escape-B3asJC6W.mjs.map} +1 -1
- package/dist/{field-defs-cache-BM4SIRqH.mjs → field-defs-cache-Cf5FHK0M.mjs} +5 -5
- package/dist/{field-defs-cache-BM4SIRqH.mjs.map → field-defs-cache-Cf5FHK0M.mjs.map} +1 -1
- package/dist/{fts-manager-Do1BlW2r.mjs → fts-manager-iddym8cd.mjs} +4 -4
- package/dist/{fts-manager-Do1BlW2r.mjs.map → fts-manager-iddym8cd.mjs.map} +1 -1
- package/dist/{hash-3JJPlHF2.mjs → hash-BRweX6jz.mjs} +1 -1
- package/dist/{hash-3JJPlHF2.mjs.map → hash-BRweX6jz.mjs.map} +1 -1
- package/dist/{hook-result-DCh2WUpe.d.mts → hook-result-BrshYHLx.d.mts} +1 -1
- package/dist/{hook-result-DCh2WUpe.d.mts.map → hook-result-BrshYHLx.d.mts.map} +1 -1
- package/dist/{hreflang-B6hbAuuO.d.mts → hreflang-CQpu5bMy.d.mts} +2 -2
- package/dist/{hreflang-B6hbAuuO.d.mts.map → hreflang-CQpu5bMy.d.mts.map} +1 -1
- package/dist/{hreflang-DQbPZliA.mjs → hreflang-DshnweTC.mjs} +9 -8
- package/dist/{hreflang-DQbPZliA.mjs.map → hreflang-DshnweTC.mjs.map} +1 -1
- package/dist/{identity-DksvEazf.mjs → identity-CjGKDq8J.mjs} +3 -3
- package/dist/{identity-DksvEazf.mjs.map → identity-CjGKDq8J.mjs.map} +1 -1
- package/dist/{import-CJpaBBBT.mjs → import-D96g2otw.mjs} +5 -5
- package/dist/{import-CJpaBBBT.mjs.map → import-D96g2otw.mjs.map} +1 -1
- package/dist/{index-Du8K7bWa.d.mts → index-1ECYpfiF.d.mts} +3 -3
- package/dist/{index-Du8K7bWa.d.mts.map → index-1ECYpfiF.d.mts.map} +1 -1
- package/dist/index.d.mts +22 -22
- package/dist/index.mjs +81 -78
- package/dist/{init-lock-DJkX6Hto.mjs → init-lock-CFNBe_iH.mjs} +1 -1
- package/dist/{init-lock-DJkX6Hto.mjs.map → init-lock-CFNBe_iH.mjs.map} +1 -1
- package/dist/{load-CawHjTOH.mjs → load-DPtY9cZo.mjs} +2 -2
- package/dist/{load-CawHjTOH.mjs.map → load-DPtY9cZo.mjs.map} +1 -1
- package/dist/{loader-Z8vl7zTq.mjs → loader-WJi6GFRn.mjs} +46 -8
- package/dist/loader-WJi6GFRn.mjs.map +1 -0
- package/dist/{manifest-builder-COtzuca2.mjs → manifest-builder-BfNlGQIA.mjs} +2 -2
- package/dist/{manifest-builder-COtzuca2.mjs.map → manifest-builder-BfNlGQIA.mjs.map} +1 -1
- package/dist/{manifest-schema-BNWwR8Vd.mjs → manifest-schema-CILooI5J.mjs} +1 -1
- package/dist/{manifest-schema-BNWwR8Vd.mjs.map → manifest-schema-CILooI5J.mjs.map} +1 -1
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +3 -3
- package/dist/media/local-runtime.d.mts +12 -12
- package/dist/media/local-runtime.mjs +5 -5
- package/dist/{media-BHjus86Y.mjs → media-C1EIZKh1.mjs} +1 -1
- package/dist/{media-BHjus86Y.mjs.map → media-C1EIZKh1.mjs.map} +1 -1
- package/dist/{media-allowlist-CoTBWoDp.mjs → media-allowlist-elXJgb_K.mjs} +2 -2
- package/dist/{media-allowlist-CoTBWoDp.mjs.map → media-allowlist-elXJgb_K.mjs.map} +1 -1
- package/dist/{media-folders-BGkATs8n.mjs → media-folders-DASfX_DH.mjs} +1 -1
- package/dist/{media-folders-BGkATs8n.mjs.map → media-folders-DASfX_DH.mjs.map} +1 -1
- package/dist/{media-upload-BCidR-EY.mjs → media-upload-C7_AQJ5v.mjs} +8 -8
- package/dist/{media-upload-BCidR-EY.mjs.map → media-upload-C7_AQJ5v.mjs.map} +1 -1
- package/dist/{media-url-DHoPr0hq.mjs → media-url-8dVl1loq.mjs} +19 -2
- package/dist/media-url-8dVl1loq.mjs.map +1 -0
- package/dist/{media-usage-CdY5jhSR.mjs → media-usage-C1EswMTq.mjs} +9 -9
- package/dist/{media-usage-CdY5jhSR.mjs.map → media-usage-C1EswMTq.mjs.map} +1 -1
- package/dist/{media-usage-activation-BgeMtK5C.mjs → media-usage-activation-CrHNltlL.mjs} +3 -3
- package/dist/{media-usage-activation-BgeMtK5C.mjs.map → media-usage-activation-CrHNltlL.mjs.map} +1 -1
- package/dist/{media-usage-work-D6D9sMCA.mjs → media-usage-work-C5B1zT1c.mjs} +2 -2
- package/dist/{media-usage-work-D6D9sMCA.mjs.map → media-usage-work-C5B1zT1c.mjs.map} +1 -1
- package/dist/{media-usage-work-CnGCdHxx.mjs → media-usage-work-IkQx_Yz6.mjs} +4 -4
- package/dist/{media-usage-work-CnGCdHxx.mjs.map → media-usage-work-IkQx_Yz6.mjs.map} +1 -1
- package/dist/{media-usage-write-fence-hjeD4u7s.mjs → media-usage-write-fence-B9anjOSg.mjs} +3 -3
- package/dist/{media-usage-write-fence-hjeD4u7s.mjs.map → media-usage-write-fence-B9anjOSg.mjs.map} +1 -1
- package/dist/{menus-6qSwTNxG.mjs → menus-D8mC4eaN.mjs} +115 -136
- package/dist/menus-D8mC4eaN.mjs.map +1 -0
- package/dist/{menus-uvPLXMZf.mjs → menus-UdwjHZJC.mjs} +11 -3
- package/dist/{menus-uvPLXMZf.mjs.map → menus-UdwjHZJC.mjs.map} +1 -1
- package/dist/migrations/index.d.mts +1 -1
- package/dist/migrations/index.mjs +4 -4
- package/dist/{mime-CUIG6x84.mjs → mime-DYpsOAny.mjs} +1 -1
- package/dist/{mime-CUIG6x84.mjs.map → mime-DYpsOAny.mjs.map} +1 -1
- package/dist/{mode-DN_WneRx.mjs → mode-Dm4BHhRH.mjs} +1 -1
- package/dist/{mode-DN_WneRx.mjs.map → mode-Dm4BHhRH.mjs.map} +1 -1
- package/dist/{node-sqlite-compat-BuJXIeMz.mjs → node-sqlite-compat-2sezm-Zd.mjs} +1 -1
- package/dist/{node-sqlite-compat-BuJXIeMz.mjs.map → node-sqlite-compat-2sezm-Zd.mjs.map} +1 -1
- package/dist/{normalize-C2xNjTSG.mjs → normalize-4nkKGsuW.mjs} +1 -1
- package/dist/{normalize-C2xNjTSG.mjs.map → normalize-4nkKGsuW.mjs.map} +1 -1
- package/dist/{oauth-authorization-Dj6Kwc21.mjs → oauth-authorization-BxE_lURD.mjs} +4 -4
- package/dist/{oauth-authorization-Dj6Kwc21.mjs.map → oauth-authorization-BxE_lURD.mjs.map} +1 -1
- package/dist/{oauth-clients-FD7I0J8m.mjs → oauth-clients-DJIdOdq0.mjs} +1 -1
- package/dist/{oauth-clients-FD7I0J8m.mjs.map → oauth-clients-DJIdOdq0.mjs.map} +1 -1
- package/dist/{oauth-state-store-DkmDOmHs.mjs → oauth-state-store-CsdxP57n.mjs} +1 -1
- package/dist/{oauth-state-store-DkmDOmHs.mjs.map → oauth-state-store-CsdxP57n.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-CvIwGpYd.mjs → oauth-user-lookup-HzzW-dkF.mjs} +1 -1
- package/dist/{oauth-user-lookup-CvIwGpYd.mjs.map → oauth-user-lookup-HzzW-dkF.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{options-BHPeIbBd.mjs → options-D1FBiAv1.mjs} +60 -5
- package/dist/options-D1FBiAv1.mjs.map +1 -0
- package/dist/{options-CYb4y7hM.d.mts → options-D8Y0VHc7.d.mts} +9 -3
- package/dist/options-D8Y0VHc7.d.mts.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/page/index.d.mts.map +1 -1
- package/dist/page/index.mjs +12 -0
- package/dist/page/index.mjs.map +1 -1
- package/dist/{parse-Ckpg-Qfp.mjs → parse-WRg5C3NX.mjs} +2 -2
- package/dist/{parse-Ckpg-Qfp.mjs.map → parse-WRg5C3NX.mjs.map} +1 -1
- package/dist/{passkey-config-O_lj3y0k.mjs → passkey-config-qIdO-wF6.mjs} +1 -1
- package/dist/{passkey-config-O_lj3y0k.mjs.map → passkey-config-qIdO-wF6.mjs.map} +1 -1
- package/dist/{patterns-D9iDO3j8.mjs → patterns-DC8CLptu.mjs} +1 -1
- package/dist/{patterns-D9iDO3j8.mjs.map → patterns-DC8CLptu.mjs.map} +1 -1
- package/dist/{placeholder-4X9BrRrp.d.mts → placeholder-COrvARyD.d.mts} +1 -1
- package/dist/{placeholder-4X9BrRrp.d.mts.map → placeholder-COrvARyD.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +3 -3
- package/dist/plugin-types.d.mts.map +1 -1
- package/dist/plugin-utils.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{policy-Dhtwntrt.mjs → policy-XCfb-IE9.mjs} +2 -2
- package/dist/{policy-Dhtwntrt.mjs.map → policy-XCfb-IE9.mjs.map} +1 -1
- package/dist/{portable-text-U85hOafW.mjs → portable-text-BS3d5n-0.mjs} +1 -1
- package/dist/{portable-text-U85hOafW.mjs.map → portable-text-BS3d5n-0.mjs.map} +1 -1
- package/dist/{preview-B_vJLvf8.mjs → preview-BxwsGgGp.mjs} +2 -2
- package/dist/{preview-B_vJLvf8.mjs.map → preview-BxwsGgGp.mjs.map} +1 -1
- package/dist/{protocol-JdD2YhY9.d.mts → protocol-CLn58-nw.d.mts} +2 -2
- package/dist/{protocol-JdD2YhY9.d.mts.map → protocol-CLn58-nw.d.mts.map} +1 -1
- package/dist/{public-url-CarhZJgf.mjs → public-url-FWWAvnre.mjs} +1 -1
- package/dist/{public-url-CarhZJgf.mjs.map → public-url-FWWAvnre.mjs.map} +1 -1
- package/dist/{query-BZKZcG5b.mjs → query-DygOte9M.mjs} +25 -16
- package/dist/query-DygOte9M.mjs.map +1 -0
- package/dist/{query-BndaFxxp.mjs → query-gtXT03Wc.mjs} +4 -4
- package/dist/{query-BndaFxxp.mjs.map → query-gtXT03Wc.mjs.map} +1 -1
- package/dist/{rate-limit-BZmATC6W.mjs → rate-limit-CQUPm0dh.mjs} +2 -2
- package/dist/{rate-limit-BZmATC6W.mjs.map → rate-limit-CQUPm0dh.mjs.map} +1 -1
- package/dist/{redirect-CzqnLxK-.mjs → redirect-D1cWafi6.mjs} +1 -1
- package/dist/{redirect-CzqnLxK-.mjs.map → redirect-D1cWafi6.mjs.map} +1 -1
- package/dist/{redirect-CZkbozu9.mjs → redirect-DId44BS1.mjs} +19 -6
- package/dist/redirect-DId44BS1.mjs.map +1 -0
- package/dist/{redirects-CqzNM0KM.mjs → redirects-DN-ZE33W.mjs} +5 -5
- package/dist/{redirects-CqzNM0KM.mjs.map → redirects-DN-ZE33W.mjs.map} +1 -1
- package/dist/{registry-AZAsRsor.mjs → registry-DTqaqxed.mjs} +26 -10
- package/dist/registry-DTqaqxed.mjs.map +1 -0
- package/dist/{relation-04DfKRTO.mjs → relation-DxgCHOBw.mjs} +2 -2
- package/dist/{relation-04DfKRTO.mjs.map → relation-DxgCHOBw.mjs.map} +1 -1
- package/dist/{relations-CcFwDJdJ.mjs → relations-KKDSLUwS.mjs} +4 -4
- package/dist/{relations-CcFwDJdJ.mjs.map → relations-KKDSLUwS.mjs.map} +1 -1
- package/dist/{relations-C4htk5sb.mjs → relations-rOmbndUJ.mjs} +3 -3
- package/dist/{relations-C4htk5sb.mjs.map → relations-rOmbndUJ.mjs.map} +1 -1
- package/dist/{request-cache-DH9zdCOD.mjs → request-cache-DAZZmesc.mjs} +1 -1
- package/dist/{request-cache-DH9zdCOD.mjs.map → request-cache-DAZZmesc.mjs.map} +1 -1
- package/dist/request-context.d.mts +1 -1
- package/dist/{request-meta-DYxLlnmz.mjs → request-meta-BgjUQLQ-.mjs} +2 -2
- package/dist/{request-meta-DYxLlnmz.mjs.map → request-meta-BgjUQLQ-.mjs.map} +1 -1
- package/dist/{resolve-prZ3mP7l.mjs → resolve-Cy0aWSRj.mjs} +37 -4
- package/dist/resolve-Cy0aWSRj.mjs.map +1 -0
- package/dist/{runner-YKfIMnaF.d.mts → runner-CfwysRM0.d.mts} +2 -2
- package/dist/{runner-YKfIMnaF.d.mts.map → runner-CfwysRM0.d.mts.map} +1 -1
- package/dist/{runner-BLIDKpmA.mjs → runner-nEzvnogG.mjs} +419 -295
- package/dist/runner-nEzvnogG.mjs.map +1 -0
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +3 -2
- package/dist/runtime.mjs.map +1 -1
- package/dist/{schema-CGnBeLiW.mjs → schema-CRu38Pr5.mjs} +3 -3
- package/dist/{schema-CGnBeLiW.mjs.map → schema-CRu38Pr5.mjs.map} +1 -1
- package/dist/{schema-DYr-Vd-L.mjs → schema-DifxndJx.mjs} +4 -4
- package/dist/{schema-DYr-Vd-L.mjs.map → schema-DifxndJx.mjs.map} +1 -1
- package/dist/{scopes-BhWvBavj.mjs → scopes-D5A1OZbq.mjs} +3 -3
- package/dist/{scopes-BhWvBavj.mjs.map → scopes-D5A1OZbq.mjs.map} +1 -1
- package/dist/{search-C-qDk4kx.mjs → search-DEPFSBFq.mjs} +4 -4
- package/dist/{search-C-qDk4kx.mjs.map → search-DEPFSBFq.mjs.map} +1 -1
- package/dist/{secrets-rEtHt-6z.mjs → secrets-CdfzLdoO.mjs} +3 -3
- package/dist/{secrets-rEtHt-6z.mjs.map → secrets-CdfzLdoO.mjs.map} +1 -1
- package/dist/{sections-B_nYvlwn.mjs → sections-3X52kmIR.mjs} +2 -2
- package/dist/{sections-B_nYvlwn.mjs.map → sections-3X52kmIR.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +26 -24
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.d.mts.map +1 -1
- package/dist/seo/index.mjs +18 -19
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-926NRIgt.mjs → seo-Bw68KxRc.mjs} +2 -2
- package/dist/{seo-926NRIgt.mjs.map → seo-Bw68KxRc.mjs.map} +1 -1
- package/dist/{seo-CVWkdv6x.mjs → seo-C4AmbsMU.mjs} +3 -2
- package/dist/seo-C4AmbsMU.mjs.map +1 -0
- package/dist/{service-CJl3ZAYs.mjs → service-J_-Vgo7R.mjs} +2 -2
- package/dist/{service-CJl3ZAYs.mjs.map → service-J_-Vgo7R.mjs.map} +1 -1
- package/dist/{session-user-BlMgqCHS.mjs → session-user-CbnMMwk6.mjs} +1 -1
- package/dist/{session-user-BlMgqCHS.mjs.map → session-user-CbnMMwk6.mjs.map} +1 -1
- package/dist/{settings-Cwm6o2jY.mjs → settings-AnEFYICn.mjs} +3 -3
- package/dist/{settings-Cwm6o2jY.mjs.map → settings-AnEFYICn.mjs.map} +1 -1
- package/dist/{settings--wobrkkC.mjs → settings-DcMjREe5.mjs} +6 -6
- package/dist/{settings--wobrkkC.mjs.map → settings-DcMjREe5.mjs.map} +1 -1
- package/dist/{setup-complete-DQbClIN2.mjs → setup-complete-BClXXTvr.mjs} +2 -2
- package/dist/{setup-complete-DQbClIN2.mjs.map → setup-complete-BClXXTvr.mjs.map} +1 -1
- package/dist/{setup-nonce-DZCFS2sn.mjs → setup-nonce-BheRa4RG.mjs} +1 -1
- package/dist/{setup-nonce-DZCFS2sn.mjs.map → setup-nonce-BheRa4RG.mjs.map} +1 -1
- package/dist/{single-flight-cache-BPuGPdcN.mjs → single-flight-cache-jaswDzmQ.mjs} +2 -2
- package/dist/{single-flight-cache-BPuGPdcN.mjs.map → single-flight-cache-jaswDzmQ.mjs.map} +1 -1
- package/dist/{site-url-BrxA_E9H.mjs → site-url-Bqf-ea3N.mjs} +3 -3
- package/dist/{site-url-BrxA_E9H.mjs.map → site-url-Bqf-ea3N.mjs.map} +1 -1
- package/dist/{snapshot--OCDTqdb.mjs → snapshot-C4CtXntb.mjs} +51 -35
- package/dist/snapshot-C4CtXntb.mjs.map +1 -0
- package/dist/{ssrf-5sIM5jzl.mjs → ssrf-gl4gFNhw.mjs} +148 -91
- package/dist/ssrf-gl4gFNhw.mjs.map +1 -0
- package/dist/{state-BpFVsJZB.mjs → state-1F6IOhdQ.mjs} +1 -1
- package/dist/{state-BpFVsJZB.mjs.map → state-1F6IOhdQ.mjs.map} +1 -1
- package/dist/{status-B777-dXz.mjs → status-Cenfk_r5.mjs} +1 -1
- package/dist/{status-B777-dXz.mjs.map → status-Cenfk_r5.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/{target-BmR0iTwz.mjs → target-CRzeTs8G.mjs} +1 -1
- package/dist/{target-BmR0iTwz.mjs.map → target-CRzeTs8G.mjs.map} +1 -1
- package/dist/{taxonomies-DkAkMupr.mjs → taxonomies-BOdxuZpV.mjs} +11 -4
- package/dist/taxonomies-BOdxuZpV.mjs.map +1 -0
- package/dist/{taxonomies-Wfy_Lg12.mjs → taxonomies-NcFDq5Gg.mjs} +23 -14
- package/dist/taxonomies-NcFDq5Gg.mjs.map +1 -0
- package/dist/{taxonomy-tJepplLL.mjs → taxonomy-DP1vny4K.mjs} +11 -3
- package/dist/{taxonomy-tJepplLL.mjs.map → taxonomy-DP1vny4K.mjs.map} +1 -1
- package/dist/{tokens-BCO1oruF.mjs → tokens-CDk8ox9p.mjs} +1 -1
- package/dist/{tokens-BCO1oruF.mjs.map → tokens-CDk8ox9p.mjs.map} +1 -1
- package/dist/{transport-xh6YKW2G.d.mts → transport-BVn4tVb9.d.mts} +1 -1
- package/dist/{transport-xh6YKW2G.d.mts.map → transport-BVn4tVb9.d.mts.map} +1 -1
- package/dist/{transport-Dr3qtwXC.mjs → transport-DP0JI-8i.mjs} +1 -1
- package/dist/{transport-Dr3qtwXC.mjs.map → transport-DP0JI-8i.mjs.map} +1 -1
- package/dist/{trusted-proxy-BR5F1Otp.mjs → trusted-proxy-BTM6UxqD.mjs} +1 -1
- package/dist/{trusted-proxy-BR5F1Otp.mjs.map → trusted-proxy-BTM6UxqD.mjs.map} +1 -1
- package/dist/{types-CXFkwEGC.d.mts → types-2n848aVW.d.mts} +1 -1
- package/dist/{types-CXFkwEGC.d.mts.map → types-2n848aVW.d.mts.map} +1 -1
- package/dist/{types-75KIqzL9.mjs → types-BMXnRIrX.mjs} +4 -2
- package/dist/types-BMXnRIrX.mjs.map +1 -0
- package/dist/{types-B6GrPic4.d.mts → types-BouXpOsa.d.mts} +115 -4
- package/dist/types-BouXpOsa.d.mts.map +1 -0
- package/dist/{types-3qNFPlci.mjs → types-ByG2coLw.mjs} +1 -1
- package/dist/{types-3qNFPlci.mjs.map → types-ByG2coLw.mjs.map} +1 -1
- package/dist/types-CQPWhPxE.mjs.map +1 -1
- package/dist/{types-CDtkNSFQ.d.mts → types-DEfNJ__A.d.mts} +1 -1
- package/dist/{types-CDtkNSFQ.d.mts.map → types-DEfNJ__A.d.mts.map} +1 -1
- package/dist/{types-xDMtUFnO.d.mts → types-DKcKSbtB.d.mts} +23 -7
- package/dist/types-DKcKSbtB.d.mts.map +1 -0
- package/dist/{types-C00oyCZp.d.mts → types-DRYs7uTL.d.mts} +1 -1
- package/dist/{types-C00oyCZp.d.mts.map → types-DRYs7uTL.d.mts.map} +1 -1
- package/dist/{types-DczELMop.d.mts → types-DaEg7KX2.d.mts} +1 -1
- package/dist/{types-DczELMop.d.mts.map → types-DaEg7KX2.d.mts.map} +1 -1
- package/dist/{types-BuHW4xDE.d.mts → types-LU8utpP_.d.mts} +6 -3
- package/dist/types-LU8utpP_.d.mts.map +1 -0
- package/dist/{types-CPeXo1wF.d.mts → types-VpYfPUxN.d.mts} +1 -1
- package/dist/{types-CPeXo1wF.d.mts.map → types-VpYfPUxN.d.mts.map} +1 -1
- package/dist/{types-DM3vioBY.mjs → types-c9GP7Dj5.mjs} +1 -1
- package/dist/{types-DM3vioBY.mjs.map → types-c9GP7Dj5.mjs.map} +1 -1
- package/dist/{types-FV23hEoW.d.mts → types-tJWDK__M.d.mts} +14 -1
- package/dist/types-tJWDK__M.d.mts.map +1 -0
- package/dist/{upload-attempts-BsqoQ1fn.mjs → upload-attempts-C6S2y-lp.mjs} +1 -1
- package/dist/{upload-attempts-BsqoQ1fn.mjs.map → upload-attempts-C6S2y-lp.mjs.map} +1 -1
- package/dist/{url-pattern-D4c2czpJ.mjs → url-pattern-UYtxVTuj.mjs} +1 -1
- package/dist/{url-pattern-D4c2czpJ.mjs.map → url-pattern-UYtxVTuj.mjs.map} +1 -1
- package/dist/{user-BTZ40aq6.mjs → user-DIsIXogT.mjs} +2 -2
- package/dist/{user-BTZ40aq6.mjs.map → user-DIsIXogT.mjs.map} +1 -1
- package/dist/{utils-BKvNjjhY.mjs → utils-BicP5Qbg.mjs} +2 -2
- package/dist/{utils-BKvNjjhY.mjs.map → utils-BicP5Qbg.mjs.map} +1 -1
- package/dist/{validate-DE_LRwo9.mjs → validate-D8VPn4Hv.mjs} +8 -3
- package/dist/validate-D8VPn4Hv.mjs.map +1 -0
- package/dist/{validate-DUP27OYD.d.mts → validate-Df6Dvwbw.d.mts} +12 -7
- package/dist/validate-Df6Dvwbw.d.mts.map +1 -0
- package/dist/{validation-BMImFJ11.mjs → validation-CzMJn9Ku.mjs} +13 -11
- package/dist/{validation-BMImFJ11.mjs.map → validation-CzMJn9Ku.mjs.map} +1 -1
- package/dist/version-CK67_SXX.mjs +7 -0
- package/dist/{version-Bii3suaO.mjs.map → version-CK67_SXX.mjs.map} +1 -1
- package/dist/{widgets-D3gJ10xU.mjs → widgets-RFVAxQl3.mjs} +5 -5
- package/dist/{widgets-D3gJ10xU.mjs.map → widgets-RFVAxQl3.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-B2Og_6rU.d.mts → wxr-taxonomies-BX4yQL9s.d.mts} +1 -1
- package/dist/{wxr-taxonomies-B2Og_6rU.d.mts.map → wxr-taxonomies-BX4yQL9s.d.mts.map} +1 -1
- package/dist/{wxr-taxonomies-Cxngsjwo.mjs → wxr-taxonomies-Csdp7yde.mjs} +4 -4
- package/dist/{wxr-taxonomies-Cxngsjwo.mjs.map → wxr-taxonomies-Csdp7yde.mjs.map} +1 -1
- package/dist/{zod-generator-BuYSQJB7.mjs → zod-generator-CNRGdnBY.mjs} +3 -3
- package/dist/{zod-generator-BuYSQJB7.mjs.map → zod-generator-CNRGdnBY.mjs.map} +1 -1
- package/package.json +12 -10
- package/src/api/errors.ts +7 -0
- package/src/api/handlers/content.ts +66 -79
- package/src/api/handlers/entry-lock.ts +206 -0
- package/src/api/handlers/manifest.ts +1 -0
- package/src/api/handlers/marketplace.ts +28 -20
- package/src/api/handlers/menus.ts +9 -0
- package/src/api/handlers/registry.ts +13 -8
- package/src/api/handlers/revision.ts +2 -1
- package/src/api/handlers/seo.ts +10 -1
- package/src/api/handlers/snapshot.ts +84 -72
- package/src/api/handlers/taxonomies.ts +9 -0
- package/src/api/openapi/document.ts +111 -6
- package/src/api/schemas/content.ts +8 -0
- package/src/api/schemas/entry-lock.ts +48 -0
- package/src/api/schemas/index.ts +1 -0
- package/src/api/schemas/schema.ts +10 -1
- package/src/api/types.ts +2 -0
- package/src/astro/integration/index.ts +9 -6
- package/src/astro/integration/routes.ts +5 -0
- package/src/astro/integration/runtime.ts +13 -5
- package/src/astro/integration/vite-config.ts +30 -0
- package/src/astro/middleware.ts +16 -0
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +10 -0
- package/src/astro/routes/api/content/[collection]/[id]/lock.ts +133 -0
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +11 -1
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +20 -0
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +10 -0
- package/src/astro/routes/api/content/[collection]/[id].ts +27 -2
- package/src/astro/routes/api/content/[collection]/index.ts +3 -0
- package/src/astro/routes/sitemap-[collection].xml.ts +4 -0
- package/src/astro/types.ts +13 -3
- package/src/cli/commands/content.ts +31 -5
- package/src/cli/commands/export-seed.ts +2 -0
- package/src/client/index.ts +34 -7
- package/src/components/EmDashBodyEnd.astro +4 -1
- package/src/components/EmDashBodyStart.astro +4 -1
- package/src/components/EmDashHead.astro +35 -16
- package/src/components/Gallery.astro +18 -4
- package/src/components/InlineEditor.astro +3 -1
- package/src/components/InlinePortableTextEditor.tsx +106 -11
- package/src/components/PortableText.astro +4 -0
- package/src/components/Table.astro +147 -62
- package/src/config/errors.ts +13 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +26 -2
- package/src/content/converters/prosemirror-to-portable-text.ts +35 -6
- package/src/content/converters/types.ts +10 -0
- package/src/database/dialect-helpers.ts +125 -0
- package/src/database/migrations/075_entry_edit_locks.ts +44 -0
- package/src/database/migrations/076_collection_nav_group.ts +22 -0
- package/src/database/migrations/077_plugin_storage_revisions.ts +89 -0
- package/src/database/migrations/runner.ts +6 -0
- package/src/database/repositories/content.ts +170 -1
- package/src/database/repositories/entry-locks.ts +257 -0
- package/src/database/repositories/options.ts +69 -5
- package/src/database/repositories/plugin-storage.ts +159 -0
- package/src/database/repositories/redirect.ts +17 -6
- package/src/database/repositories/taxonomy.ts +8 -0
- package/src/database/repositories/types.ts +3 -0
- package/src/database/types.ts +15 -1
- package/src/emdash-runtime.ts +117 -50
- package/src/i18n/resolve.ts +44 -4
- package/src/index.ts +14 -0
- package/src/loader.ts +35 -4
- package/src/mcp/server.ts +23 -5
- package/src/media/responsive.ts +23 -0
- package/src/media/usage/content-refresh.ts +2 -1
- package/src/menus/index.ts +17 -21
- package/src/page/seo-contributions.ts +78 -1
- package/src/page/seo-panel.ts +47 -0
- package/src/plugin-types.ts +9 -0
- package/src/plugins/conditional-storage.ts +30 -0
- package/src/plugins/context.ts +49 -17
- package/src/plugins/hooks.ts +7 -1
- package/src/plugins/index.ts +9 -0
- package/src/plugins/manager.ts +5 -2
- package/src/plugins/sandbox/types.ts +16 -2
- package/src/plugins/storage-query.ts +45 -0
- package/src/plugins/storage-update.ts +96 -0
- package/src/plugins/types.ts +114 -0
- package/src/query.ts +29 -3
- package/src/registry/config.ts +130 -27
- package/src/registry/types.ts +5 -6
- package/src/schema/collection-slugs-cache.ts +157 -0
- package/src/schema/registry.ts +19 -0
- package/src/schema/types.ts +25 -6
- package/src/security/ssrf.ts +167 -141
- package/src/seed/apply.ts +4 -0
- package/src/seed/types.ts +7 -2
- package/src/seed/validate.ts +18 -1
- package/src/seo/hreflang.ts +1 -0
- package/src/seo/index.ts +5 -10
- package/src/seo/media-url.ts +26 -0
- package/src/taxonomies/term-counts.ts +22 -5
- package/dist/api-Dz6rgoa3.mjs.map +0 -1
- package/dist/apply-B4SIOxR1.mjs.map +0 -1
- package/dist/config-it29yDLA.mjs.map +0 -1
- package/dist/content-DjvM0T92.mjs.map +0 -1
- package/dist/context-CX6gxOLW.mjs.map +0 -1
- package/dist/dialect-helpers-BSbfaO3F.mjs.map +0 -1
- package/dist/errors-DsVoDUjJ.mjs.map +0 -1
- package/dist/index-BJNMCWmu.d.mts.map +0 -1
- package/dist/loader-Z8vl7zTq.mjs.map +0 -1
- package/dist/media-url-DHoPr0hq.mjs.map +0 -1
- package/dist/menus-6qSwTNxG.mjs.map +0 -1
- package/dist/options-BHPeIbBd.mjs.map +0 -1
- package/dist/options-CYb4y7hM.d.mts.map +0 -1
- package/dist/query-BZKZcG5b.mjs.map +0 -1
- package/dist/redirect-CZkbozu9.mjs.map +0 -1
- package/dist/redirects-BLNOWQTn.mjs.map +0 -1
- package/dist/registry-AZAsRsor.mjs.map +0 -1
- package/dist/relations-DnRK4YIT.d.mts.map +0 -1
- package/dist/resolve-prZ3mP7l.mjs.map +0 -1
- package/dist/runner-BLIDKpmA.mjs.map +0 -1
- package/dist/seo-CVWkdv6x.mjs.map +0 -1
- package/dist/snapshot--OCDTqdb.mjs.map +0 -1
- package/dist/ssrf-5sIM5jzl.mjs.map +0 -1
- package/dist/taxonomies-DkAkMupr.mjs.map +0 -1
- package/dist/taxonomies-Wfy_Lg12.mjs.map +0 -1
- package/dist/types-75KIqzL9.mjs.map +0 -1
- package/dist/types-B6GrPic4.d.mts.map +0 -1
- package/dist/types-BuHW4xDE.d.mts.map +0 -1
- package/dist/types-FV23hEoW.d.mts.map +0 -1
- package/dist/types-xDMtUFnO.d.mts.map +0 -1
- package/dist/validate-DE_LRwo9.mjs.map +0 -1
- package/dist/validate-DUP27OYD.d.mts.map +0 -1
- package/dist/version-Bii3suaO.mjs +0 -7
- /package/dist/{api-tokens-BfYts-sq.mjs → api-tokens-B_lfpmH3.mjs} +0 -0
- /package/dist/{ssrf-Dg3cIT-b.mjs → ssrf-TQhaljuI.mjs} +0 -0
- /package/dist/{types-DfuTR6dL.mjs → types-A2cN6a4a.mjs} +0 -0
package/src/security/ssrf.ts
CHANGED
|
@@ -1,69 +1,94 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SSRF protection for
|
|
2
|
+
* SSRF protection for outbound HTTP requests.
|
|
3
3
|
*
|
|
4
|
-
* Validates that URLs
|
|
5
|
-
* Applied before any fetch() call in the import pipeline.
|
|
4
|
+
* Validates that URLs do not target non-public network addresses.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
const IPV4_MAPPED_IPV6_DOTTED_PATTERN = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i;
|
|
9
|
-
const IPV4_MAPPED_IPV6_HEX_PATTERN = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
|
|
10
|
-
const IPV4_TRANSLATED_HEX_PATTERN = /^::ffff:0:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
|
|
11
|
-
const IPV6_EXPANDED_MAPPED_PATTERN =
|
|
12
|
-
/^0{0,4}:0{0,4}:0{0,4}:0{0,4}:0{0,4}:ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* IPv4-compatible (deprecated) addresses: ::XXXX:XXXX
|
|
16
|
-
*
|
|
17
|
-
* The WHATWG URL parser normalizes [::127.0.0.1] to [::7f00:1] (no ffff prefix).
|
|
18
|
-
* These are deprecated but still parsed, and bypass the ffff-based checks.
|
|
19
|
-
*/
|
|
20
|
-
const IPV4_COMPATIBLE_HEX_PATTERN = /^::([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* NAT64 prefix (RFC 6052): 64:ff9b::XXXX:XXXX
|
|
24
|
-
*
|
|
25
|
-
* Used by NAT64 gateways to embed IPv4 addresses in IPv6.
|
|
26
|
-
* [64:ff9b::127.0.0.1] normalizes to [64:ff9b::7f00:1].
|
|
27
|
-
*/
|
|
28
|
-
const NAT64_HEX_PATTERN = /^64:ff9b::([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
|
|
29
|
-
|
|
30
7
|
const IPV6_BRACKET_PATTERN = /^\[|\]$/g;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const IPV6_ULA_FC_PATTERN = /^fc[0-9a-f]{2}:/;
|
|
34
|
-
const IPV6_ULA_FD_PATTERN = /^fd[0-9a-f]{2}:/;
|
|
8
|
+
const IPV4_PART_PATTERN = /^(?:0|[1-9]\d{0,2})$/;
|
|
9
|
+
const IPV6_PART_PATTERN = /^[0-9a-f]{1,4}$/i;
|
|
35
10
|
|
|
36
11
|
/** Strip trailing dots from an FQDN-form hostname ("localhost." -> "localhost"). */
|
|
37
12
|
const TRAILING_DOT_PATTERN = /\.+$/;
|
|
38
13
|
|
|
39
14
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* Includes:
|
|
43
|
-
* - Loopback (127.0.0.0/8)
|
|
44
|
-
* - Private (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
|
|
45
|
-
* - Link-local (169.254.0.0/16)
|
|
46
|
-
* - Cloud metadata (169.254.169.254 — AWS/GCP/Azure)
|
|
47
|
-
* - IPv6 loopback and link-local
|
|
15
|
+
* IPv4 ranges that are not globally reachable unicast destinations.
|
|
48
16
|
*/
|
|
49
17
|
const BLOCKED_PATTERNS: Array<{ start: number; end: number }> = [
|
|
50
|
-
//
|
|
51
|
-
{ start: ip4ToNum(
|
|
52
|
-
//
|
|
18
|
+
// Current network
|
|
19
|
+
{ start: ip4ToNum(0, 0, 0, 0), end: ip4ToNum(0, 255, 255, 255) },
|
|
20
|
+
// Private use
|
|
53
21
|
{ start: ip4ToNum(10, 0, 0, 0), end: ip4ToNum(10, 255, 255, 255) },
|
|
54
|
-
//
|
|
22
|
+
// Shared address space
|
|
23
|
+
{ start: ip4ToNum(100, 64, 0, 0), end: ip4ToNum(100, 127, 255, 255) },
|
|
24
|
+
// Loopback
|
|
25
|
+
{ start: ip4ToNum(127, 0, 0, 0), end: ip4ToNum(127, 255, 255, 255) },
|
|
26
|
+
// Link-local, including common cloud metadata endpoints
|
|
27
|
+
{ start: ip4ToNum(169, 254, 0, 0), end: ip4ToNum(169, 254, 255, 255) },
|
|
28
|
+
// Private use
|
|
55
29
|
{ start: ip4ToNum(172, 16, 0, 0), end: ip4ToNum(172, 31, 255, 255) },
|
|
56
|
-
//
|
|
30
|
+
// IETF protocol assignments. Globally reachable exceptions are handled below.
|
|
31
|
+
{ start: ip4ToNum(192, 0, 0, 0), end: ip4ToNum(192, 0, 0, 255) },
|
|
32
|
+
// Documentation
|
|
33
|
+
{ start: ip4ToNum(192, 0, 2, 0), end: ip4ToNum(192, 0, 2, 255) },
|
|
34
|
+
// Deprecated 6to4 relay anycast
|
|
35
|
+
{ start: ip4ToNum(192, 88, 99, 0), end: ip4ToNum(192, 88, 99, 255) },
|
|
36
|
+
// Private use
|
|
57
37
|
{ start: ip4ToNum(192, 168, 0, 0), end: ip4ToNum(192, 168, 255, 255) },
|
|
58
|
-
//
|
|
59
|
-
{ start: ip4ToNum(
|
|
60
|
-
//
|
|
61
|
-
{ start: ip4ToNum(
|
|
38
|
+
// Benchmarking
|
|
39
|
+
{ start: ip4ToNum(198, 18, 0, 0), end: ip4ToNum(198, 19, 255, 255) },
|
|
40
|
+
// Documentation
|
|
41
|
+
{ start: ip4ToNum(198, 51, 100, 0), end: ip4ToNum(198, 51, 100, 255) },
|
|
42
|
+
{ start: ip4ToNum(203, 0, 113, 0), end: ip4ToNum(203, 0, 113, 255) },
|
|
43
|
+
// Multicast, reserved, and limited broadcast
|
|
44
|
+
{ start: ip4ToNum(224, 0, 0, 0), end: ip4ToNum(255, 255, 255, 255) },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const ALLOWED_IPV4_EXCEPTIONS = new Set([ip4ToNum(192, 0, 0, 9), ip4ToNum(192, 0, 0, 10)]);
|
|
48
|
+
|
|
49
|
+
interface Ipv6Range {
|
|
50
|
+
address: number[];
|
|
51
|
+
prefixLength: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ipv6Range(address: string, prefixLength: number): Ipv6Range {
|
|
55
|
+
const parsed = parseIpv6(address);
|
|
56
|
+
if (parsed === null) throw new Error(`Invalid IPv6 range: ${address}/${prefixLength}`);
|
|
57
|
+
return { address: parsed, prefixLength };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const IPV6_GLOBAL_UNICAST_RANGE = ipv6Range("2000::", 3);
|
|
61
|
+
const IPV4_COMPATIBLE_IPV6_RANGE = ipv6Range("::", 96);
|
|
62
|
+
const IPV4_EMBEDDED_IPV6_RANGES: Ipv6Range[] = [
|
|
63
|
+
IPV4_COMPATIBLE_IPV6_RANGE,
|
|
64
|
+
ipv6Range("::ffff:0:0", 96),
|
|
65
|
+
ipv6Range("::ffff:0:0:0", 96),
|
|
66
|
+
ipv6Range("64:ff9b::", 96),
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
/** Globally reachable allocations inside the otherwise reserved 2001::/23 block. */
|
|
70
|
+
const ALLOWED_IPV6_SPECIAL_RANGES: Ipv6Range[] = [
|
|
71
|
+
ipv6Range("2001:1::1", 128),
|
|
72
|
+
ipv6Range("2001:1::2", 128),
|
|
73
|
+
ipv6Range("2001:1::3", 128),
|
|
74
|
+
ipv6Range("2001:3::", 32),
|
|
75
|
+
ipv6Range("2001:4:112::", 48),
|
|
76
|
+
ipv6Range("2001:20::", 28),
|
|
77
|
+
ipv6Range("2001:30::", 28),
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
/** Non-global allocations inside the ordinary IPv6 global-unicast range. */
|
|
81
|
+
const BLOCKED_IPV6_GLOBAL_UNICAST_RANGES: Ipv6Range[] = [
|
|
82
|
+
ipv6Range("2001::", 23),
|
|
83
|
+
ipv6Range("2001:db8::", 32),
|
|
84
|
+
ipv6Range("2002::", 16),
|
|
85
|
+
ipv6Range("3ffe::", 16),
|
|
86
|
+
ipv6Range("3fff::", 20),
|
|
62
87
|
];
|
|
63
88
|
|
|
64
89
|
// Bracket-stripped form is used for lookups (validateExternalUrl strips
|
|
65
90
|
// brackets from parsed.hostname before checking), so "::1" appears here
|
|
66
|
-
// without brackets. The "::1" case is already covered by
|
|
91
|
+
// without brackets. The "::1" case is already covered by isNonPublicIp, but
|
|
67
92
|
// keeping it here makes the intent explicit and gives a clearer error
|
|
68
93
|
// message for the common `http://[::1]/` form.
|
|
69
94
|
const BLOCKED_HOSTNAMES = new Set([
|
|
@@ -101,80 +126,78 @@ function parseIpv4(ip: string): number | null {
|
|
|
101
126
|
const parts = ip.split(".");
|
|
102
127
|
if (parts.length !== 4) return null;
|
|
103
128
|
|
|
104
|
-
const nums = parts.map(Number);
|
|
105
|
-
if (nums.some((n) =>
|
|
129
|
+
const nums = parts.map((part) => (IPV4_PART_PATTERN.test(part) ? Number(part) : Number.NaN));
|
|
130
|
+
if (nums.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
|
|
106
131
|
|
|
107
132
|
return ip4ToNum(nums[0], nums[1], nums[2], nums[3]);
|
|
108
133
|
}
|
|
109
134
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
*/
|
|
119
|
-
export function normalizeIPv6MappedToIPv4(ip: string): string | null {
|
|
120
|
-
// Match hex-form IPv4-mapped IPv6: ::ffff:XXXX:XXXX
|
|
121
|
-
let match = ip.match(IPV4_MAPPED_IPV6_HEX_PATTERN);
|
|
122
|
-
if (!match) {
|
|
123
|
-
// Match IPv4-translated (RFC 6052): ::ffff:0:XXXX:XXXX
|
|
124
|
-
match = ip.match(IPV4_TRANSLATED_HEX_PATTERN);
|
|
125
|
-
}
|
|
126
|
-
if (!match) {
|
|
127
|
-
// Match fully expanded form: 0000:0000:0000:0000:0000:ffff:XXXX:XXXX
|
|
128
|
-
match = ip.match(IPV6_EXPANDED_MAPPED_PATTERN);
|
|
129
|
-
}
|
|
130
|
-
if (!match) {
|
|
131
|
-
// Match IPv4-compatible (deprecated) form: ::XXXX:XXXX (no ffff prefix)
|
|
132
|
-
match = ip.match(IPV4_COMPATIBLE_HEX_PATTERN);
|
|
135
|
+
function parseIpv6(ip: string): number[] | null {
|
|
136
|
+
let address = ip;
|
|
137
|
+
if (address.includes(".")) {
|
|
138
|
+
const separator = address.lastIndexOf(":");
|
|
139
|
+
if (separator === -1) return null;
|
|
140
|
+
const ipv4 = parseIpv4(address.slice(separator + 1));
|
|
141
|
+
if (ipv4 === null) return null;
|
|
142
|
+
address = `${address.slice(0, separator + 1)}${(ipv4 >>> 16).toString(16)}:${(ipv4 & 0xffff).toString(16)}`;
|
|
133
143
|
}
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
if (address.includes(":::") || address.split("::").length > 2) return null;
|
|
145
|
+
|
|
146
|
+
const [before = "", after = ""] = address.split("::");
|
|
147
|
+
const head = before === "" ? [] : before.split(":");
|
|
148
|
+
const tail = after === "" ? [] : after.split(":");
|
|
149
|
+
const parts = [...head, ...tail];
|
|
150
|
+
if (parts.some((part) => !IPV6_PART_PATTERN.test(part)) || parts.length > 8) return null;
|
|
151
|
+
if (address.includes("::") ? parts.length >= 8 : parts.length !== 8) return null;
|
|
152
|
+
|
|
153
|
+
const zeroCount = address.includes("::") ? 8 - parts.length : 0;
|
|
154
|
+
return [...head, ...Array.from<string>({ length: zeroCount }).fill("0"), ...tail].map((part) =>
|
|
155
|
+
Number.parseInt(part, 16),
|
|
156
|
+
);
|
|
144
157
|
}
|
|
145
158
|
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const normalized = ip.toLowerCase();
|
|
151
|
-
|
|
152
|
-
// Handle IPv6 loopback
|
|
153
|
-
if (normalized === "::1" || normalized === "::ffff:127.0.0.1") return true;
|
|
154
|
-
|
|
155
|
-
// Handle IPv4-mapped IPv6 in hex form (WHATWG URL parser normalizes to this)
|
|
156
|
-
// e.g. ::ffff:7f00:1 -> 127.0.0.1, ::ffff:a9fe:a9fe -> 169.254.169.254
|
|
157
|
-
const hexIpv4 = normalizeIPv6MappedToIPv4(normalized);
|
|
158
|
-
if (hexIpv4) return isPrivateIp(hexIpv4);
|
|
159
|
-
|
|
160
|
-
// Handle IPv4-mapped IPv6 in dotted-decimal form
|
|
161
|
-
const v4Match = normalized.match(IPV4_MAPPED_IPV6_DOTTED_PATTERN);
|
|
162
|
-
const ipv4 = v4Match ? v4Match[1] : normalized;
|
|
163
|
-
|
|
164
|
-
const num = parseIpv4(ipv4);
|
|
165
|
-
if (num === null) {
|
|
166
|
-
// If we can't parse it, block IPv6 addresses that look internal.
|
|
167
|
-
// fc00::/7 is Unique Local (first byte 0xfc or 0xfd), fe80::/10 is
|
|
168
|
-
// link-local. Only match when followed by hex digit + colon to avoid
|
|
169
|
-
// collisions with hypothetical non-address strings.
|
|
170
|
-
return (
|
|
171
|
-
normalized.startsWith("fe80:") ||
|
|
172
|
-
IPV6_ULA_FC_PATTERN.test(normalized) ||
|
|
173
|
-
IPV6_ULA_FD_PATTERN.test(normalized)
|
|
174
|
-
);
|
|
159
|
+
function isIpv6InRange(address: number[], range: Ipv6Range): boolean {
|
|
160
|
+
const completeParts = Math.floor(range.prefixLength / 16);
|
|
161
|
+
for (let i = 0; i < completeParts; i++) {
|
|
162
|
+
if (address[i] !== range.address[i]) return false;
|
|
175
163
|
}
|
|
176
164
|
|
|
177
|
-
|
|
165
|
+
const remainingBits = range.prefixLength % 16;
|
|
166
|
+
if (remainingBits === 0) return true;
|
|
167
|
+
const mask = (0xffff << (16 - remainingBits)) & 0xffff;
|
|
168
|
+
return (address[completeParts] & mask) === (range.address[completeParts] & mask);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function extractEmbeddedIpv4(address: number[]): number | null {
|
|
172
|
+
if (!IPV4_EMBEDDED_IPV6_RANGES.some((range) => isIpv6InRange(address, range))) return null;
|
|
173
|
+
const ipv4 = ((address[6] << 16) | address[7]) >>> 0;
|
|
174
|
+
// The unspecified and loopback addresses retain their native IPv6 meaning.
|
|
175
|
+
if (ipv4 <= 1 && isIpv6InRange(address, IPV4_COMPATIBLE_IPV6_RANGE)) return null;
|
|
176
|
+
return ipv4;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Convert a hex-form IPv6 address with an embedded IPv4 destination to dotted decimal. */
|
|
180
|
+
export function normalizeIPv6MappedToIPv4(ip: string): string | null {
|
|
181
|
+
if (ip.includes(".")) return null;
|
|
182
|
+
const address = parseIpv6(ip);
|
|
183
|
+
if (address === null) return null;
|
|
184
|
+
const ipv4 = extractEmbeddedIpv4(address);
|
|
185
|
+
if (ipv4 === null) return null;
|
|
186
|
+
return `${ipv4 >>> 24}.${(ipv4 >>> 16) & 0xff}.${(ipv4 >>> 8) & 0xff}.${ipv4 & 0xff}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isNonPublicIp(ip: string): boolean {
|
|
190
|
+
const parsedIpv4 = parseIpv4(ip);
|
|
191
|
+
const ipv6 = parsedIpv4 === null ? parseIpv6(ip) : null;
|
|
192
|
+
const ipv4 = ipv6 === null ? parsedIpv4 : extractEmbeddedIpv4(ipv6);
|
|
193
|
+
if (ipv4 !== null) {
|
|
194
|
+
if (ALLOWED_IPV4_EXCEPTIONS.has(ipv4)) return false;
|
|
195
|
+
return BLOCKED_PATTERNS.some((range) => ipv4 >= range.start && ipv4 <= range.end);
|
|
196
|
+
}
|
|
197
|
+
if (ipv6 === null) return true;
|
|
198
|
+
if (ALLOWED_IPV6_SPECIAL_RANGES.some((range) => isIpv6InRange(ipv6, range))) return false;
|
|
199
|
+
if (!isIpv6InRange(ipv6, IPV6_GLOBAL_UNICAST_RANGE)) return true;
|
|
200
|
+
return BLOCKED_IPV6_GLOBAL_UNICAST_RANGES.some((range) => isIpv6InRange(ipv6, range));
|
|
178
201
|
}
|
|
179
202
|
|
|
180
203
|
/**
|
|
@@ -190,17 +213,15 @@ export class SsrfError extends Error {
|
|
|
190
213
|
}
|
|
191
214
|
|
|
192
215
|
/**
|
|
193
|
-
* Validate
|
|
216
|
+
* Validate a URL's scheme, hostname, and literal address.
|
|
194
217
|
*
|
|
195
218
|
* Checks:
|
|
196
219
|
* 1. URL is well-formed with http/https scheme
|
|
197
220
|
* 2. Hostname is not a known internal name (localhost, metadata endpoints)
|
|
198
|
-
* 3. If hostname is an IP literal, it
|
|
221
|
+
* 3. If hostname is an IP literal, it is a public address
|
|
199
222
|
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* before connecting, which needs a custom fetch implementation. This covers
|
|
203
|
-
* the most common SSRF vectors.
|
|
223
|
+
* Hostnames also need DNS validation before dispatch. Resolving and checking
|
|
224
|
+
* addresses does not bind an ordinary fetch() connection to those addresses.
|
|
204
225
|
*
|
|
205
226
|
* @throws SsrfError if the URL targets an internal address
|
|
206
227
|
*/
|
|
@@ -241,11 +262,11 @@ export function validateExternalUrl(url: string): URL {
|
|
|
241
262
|
}
|
|
242
263
|
}
|
|
243
264
|
|
|
244
|
-
// Check if hostname is
|
|
265
|
+
// Check if hostname is a non-public IP address. Use the
|
|
245
266
|
// normalized form so "127.0.0.1.." and friends don't bypass parseIpv4
|
|
246
267
|
// (which rejects extra trailing dots).
|
|
247
|
-
if (
|
|
248
|
-
throw new SsrfError("URLs targeting
|
|
268
|
+
if (isIpLiteral(normalizedHost) && isNonPublicIp(normalizedHost)) {
|
|
269
|
+
throw new SsrfError("URLs targeting non-public IP addresses are not allowed");
|
|
249
270
|
}
|
|
250
271
|
|
|
251
272
|
return parsed;
|
|
@@ -283,6 +304,7 @@ const DOH_TIMEOUT_MS = 3000;
|
|
|
283
304
|
const DEFAULT_DOH_URL = "https://cloudflare-dns.com/dns-query";
|
|
284
305
|
|
|
285
306
|
interface DohAnswer {
|
|
307
|
+
type: number;
|
|
286
308
|
data: string;
|
|
287
309
|
}
|
|
288
310
|
|
|
@@ -307,8 +329,14 @@ function parseDohResponse(raw: unknown): DohResponse {
|
|
|
307
329
|
const answers: DohAnswer[] = [];
|
|
308
330
|
if (hasProperty(raw, "Answer") && Array.isArray(raw.Answer)) {
|
|
309
331
|
for (const entry of raw.Answer) {
|
|
310
|
-
if (
|
|
311
|
-
|
|
332
|
+
if (
|
|
333
|
+
hasProperty(entry, "type") &&
|
|
334
|
+
typeof entry.type === "number" &&
|
|
335
|
+
Number.isInteger(entry.type) &&
|
|
336
|
+
hasProperty(entry, "data") &&
|
|
337
|
+
typeof entry.data === "string"
|
|
338
|
+
) {
|
|
339
|
+
answers.push({ type: entry.type, data: entry.data });
|
|
312
340
|
}
|
|
313
341
|
}
|
|
314
342
|
}
|
|
@@ -324,6 +352,7 @@ function parseDohResponse(raw: unknown): DohResponse {
|
|
|
324
352
|
*/
|
|
325
353
|
export const cloudflareDohResolver: DnsResolver = async (hostname) => {
|
|
326
354
|
async function query(type: "A" | "AAAA"): Promise<string[]> {
|
|
355
|
+
const expectedAnswerType = type === "A" ? 1 : 28;
|
|
327
356
|
const params = new URLSearchParams({ name: hostname, type });
|
|
328
357
|
const controller = new AbortController();
|
|
329
358
|
const timeout = setTimeout(() => controller.abort(), DOH_TIMEOUT_MS);
|
|
@@ -347,8 +376,10 @@ export const cloudflareDohResolver: DnsResolver = async (hostname) => {
|
|
|
347
376
|
}
|
|
348
377
|
// DoH Answer arrays often include CNAME records alongside A/AAAA
|
|
349
378
|
// records. Their `data` is a hostname, not an IP. Filter to just
|
|
350
|
-
// IP literals
|
|
351
|
-
return body.Answer.
|
|
379
|
+
// IP literals of the requested record type.
|
|
380
|
+
return body.Answer.filter((answer) => answer.type === expectedAnswerType)
|
|
381
|
+
.map((answer) => answer.data)
|
|
382
|
+
.filter(isIpLiteral);
|
|
352
383
|
} finally {
|
|
353
384
|
clearTimeout(timeout);
|
|
354
385
|
}
|
|
@@ -359,14 +390,12 @@ export const cloudflareDohResolver: DnsResolver = async (hostname) => {
|
|
|
359
390
|
};
|
|
360
391
|
|
|
361
392
|
/**
|
|
362
|
-
* Validate a URL and
|
|
363
|
-
*
|
|
364
|
-
* attacker-controlled domains that publicly resolve to private addresses,
|
|
365
|
-
* and wildcard DNS services like nip.io used by exploit tooling.
|
|
393
|
+
* Validate a URL and reject hostnames that resolve to non-public addresses,
|
|
394
|
+
* including wildcard DNS services like nip.io used by exploit tooling.
|
|
366
395
|
*
|
|
367
396
|
* Runs `validateExternalUrl` first for cheap pre-flight checks (scheme,
|
|
368
397
|
* literal IP, known-bad hostnames). Then resolves the hostname and rejects
|
|
369
|
-
* if ANY returned address is
|
|
398
|
+
* if ANY returned address is non-public.
|
|
370
399
|
*
|
|
371
400
|
* Fails closed: if resolution fails or returns no records, throws SsrfError.
|
|
372
401
|
*
|
|
@@ -381,9 +410,9 @@ export const cloudflareDohResolver: DnsResolver = async (hostname) => {
|
|
|
381
410
|
* - Host-file overrides or split-horizon corporate DNS on self-hosted Node.
|
|
382
411
|
* - Attacker-controlled rebinding services the caller has allowlisted.
|
|
383
412
|
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
413
|
+
* Connections must use the addresses from resolveAndValidateExternalUrlTarget
|
|
414
|
+
* to avoid a second lookup. Callers using ordinary fetch() need network-level
|
|
415
|
+
* egress controls to prevent these attacks.
|
|
387
416
|
*/
|
|
388
417
|
export async function resolveAndValidateExternalUrl(
|
|
389
418
|
url: string,
|
|
@@ -411,7 +440,7 @@ export async function resolveAndValidateExternalUrlTarget(
|
|
|
411
440
|
const hostname = parsed.hostname.replace(IPV6_BRACKET_PATTERN, "");
|
|
412
441
|
|
|
413
442
|
// If the hostname is already an IP literal, validateExternalUrl has
|
|
414
|
-
// already checked it against the
|
|
443
|
+
// already checked it against the non-public ranges. Skip DNS.
|
|
415
444
|
if (isIpLiteral(hostname)) {
|
|
416
445
|
return { url: parsed, addresses: [hostname] };
|
|
417
446
|
}
|
|
@@ -435,20 +464,17 @@ export async function resolveAndValidateExternalUrlTarget(
|
|
|
435
464
|
if (!isIpLiteral(ip)) {
|
|
436
465
|
throw new SsrfError("Hostname resolver returned a non-IP address");
|
|
437
466
|
}
|
|
438
|
-
if (
|
|
439
|
-
throw new SsrfError("Hostname resolves to a
|
|
467
|
+
if (isNonPublicIp(ip)) {
|
|
468
|
+
throw new SsrfError("Hostname resolves to a non-public IP address");
|
|
440
469
|
}
|
|
441
470
|
}
|
|
442
471
|
|
|
443
472
|
return { url: parsed, addresses };
|
|
444
473
|
}
|
|
445
474
|
|
|
446
|
-
/** True when a string
|
|
475
|
+
/** True when a string is a valid IPv4 or IPv6 literal. */
|
|
447
476
|
function isIpLiteral(host: string): boolean {
|
|
448
|
-
|
|
449
|
-
// Very loose IPv6 heuristic — matches anything with a colon, which is
|
|
450
|
-
// never valid in DNS hostnames, so this is safe.
|
|
451
|
-
return host.includes(":");
|
|
477
|
+
return parseIpv4(host) !== null || parseIpv6(host) !== null;
|
|
452
478
|
}
|
|
453
479
|
|
|
454
480
|
/**
|
package/src/seed/apply.ts
CHANGED
|
@@ -199,7 +199,9 @@ export async function applySeed(
|
|
|
199
199
|
routable: collection.routable,
|
|
200
200
|
hidden: collection.hidden,
|
|
201
201
|
sortOrder: collection.sortOrder,
|
|
202
|
+
group: collection.group,
|
|
202
203
|
commentsEnabled: collection.commentsEnabled,
|
|
204
|
+
editLocking: collection.editLocking,
|
|
203
205
|
});
|
|
204
206
|
result.collections.updated++;
|
|
205
207
|
|
|
@@ -277,7 +279,9 @@ export async function applySeed(
|
|
|
277
279
|
routable: collection.routable,
|
|
278
280
|
hidden: collection.hidden,
|
|
279
281
|
sortOrder: collection.sortOrder,
|
|
282
|
+
group: collection.group,
|
|
280
283
|
commentsEnabled: collection.commentsEnabled,
|
|
284
|
+
editLocking: collection.editLocking,
|
|
281
285
|
},
|
|
282
286
|
fields,
|
|
283
287
|
);
|
package/src/seed/types.ts
CHANGED
|
@@ -78,8 +78,9 @@ export interface SeedCollection {
|
|
|
78
78
|
/** Require a slug before an entry can be published. Defaults to true. */
|
|
79
79
|
routable?: boolean;
|
|
80
80
|
/**
|
|
81
|
-
* Omit this collection from the admin sidebar
|
|
82
|
-
* the API, MCP, plugin hooks, and
|
|
81
|
+
* Omit this collection from the admin sidebar and the dashboard quick
|
|
82
|
+
* actions. It stays reachable through the API, MCP, plugin hooks, and
|
|
83
|
+
* direct `/content/:collection` URLs.
|
|
83
84
|
*/
|
|
84
85
|
hidden?: boolean;
|
|
85
86
|
/**
|
|
@@ -87,8 +88,12 @@ export interface SeedCollection {
|
|
|
87
88
|
* a `sortOrder` keep the alphabetical order and follow the ordered ones.
|
|
88
89
|
*/
|
|
89
90
|
sortOrder?: number;
|
|
91
|
+
/** Admin sidebar folder shared with other collections of the same group. */
|
|
92
|
+
group?: string;
|
|
90
93
|
/** Enable comments on this collection */
|
|
91
94
|
commentsEnabled?: boolean;
|
|
95
|
+
/** Take an edit lock when an entry is opened (defaults to true) */
|
|
96
|
+
editLocking?: boolean;
|
|
92
97
|
/** Field slug powering the admin list Title column (defaults to title display) */
|
|
93
98
|
titleField?: string;
|
|
94
99
|
/** Field slug (a datetime field) powering the admin list Date column (defaults to last-updated) */
|
package/src/seed/validate.ts
CHANGED
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { getI18nConfig, resolveConfiguredLocale } from "../i18n/config.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
FIELD_TYPES,
|
|
10
|
+
isIndexableFieldType,
|
|
11
|
+
MAX_COLLECTION_GROUP_LENGTH,
|
|
12
|
+
MAX_COLLECTION_LIST_COLUMNS,
|
|
13
|
+
} from "../schema/types.js";
|
|
9
14
|
import type { SeedFile, SeedMenuItem, ValidationResult } from "./types.js";
|
|
10
15
|
|
|
11
16
|
const COLLECTION_FIELD_SLUG_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
@@ -107,9 +112,21 @@ export function validateSeed(data: unknown): ValidationResult {
|
|
|
107
112
|
if (!collection.label) {
|
|
108
113
|
errors.push(`${prefix}: label is required`);
|
|
109
114
|
}
|
|
115
|
+
if (collection.editLocking !== undefined && typeof collection.editLocking !== "boolean") {
|
|
116
|
+
errors.push(`${prefix}.editLocking: must be a boolean`);
|
|
117
|
+
}
|
|
110
118
|
if (collection.routable !== undefined && typeof collection.routable !== "boolean") {
|
|
111
119
|
errors.push(`${prefix}.routable: must be a boolean`);
|
|
112
120
|
}
|
|
121
|
+
if (collection.group !== undefined) {
|
|
122
|
+
if (typeof collection.group !== "string") {
|
|
123
|
+
errors.push(`${prefix}.group: must be a string`);
|
|
124
|
+
} else if (collection.group.trim().length > MAX_COLLECTION_GROUP_LENGTH) {
|
|
125
|
+
errors.push(
|
|
126
|
+
`${prefix}.group: must be at most ${MAX_COLLECTION_GROUP_LENGTH} characters`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
113
130
|
|
|
114
131
|
const declaredFieldSlugs = new Set(
|
|
115
132
|
Array.isArray(collection.fields)
|
package/src/seo/hreflang.ts
CHANGED
|
@@ -167,6 +167,7 @@ export async function getHreflangAlternatesWithDb(
|
|
|
167
167
|
collection,
|
|
168
168
|
slug: variant.slug || variant.id,
|
|
169
169
|
id: variant.id,
|
|
170
|
+
date: variant.publishedAt,
|
|
170
171
|
});
|
|
171
172
|
const localized = await localizePath(path, locale);
|
|
172
173
|
if (localized === null) continue;
|
package/src/seo/index.ts
CHANGED
|
@@ -30,13 +30,12 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import type { ContentSeo } from "../database/repositories/types.js";
|
|
33
|
-
import { buildSeoImageUrl } from "./media-url.js";
|
|
33
|
+
import { buildSeoImageUrl, resolveSeoCanonicalUrl } from "./media-url.js";
|
|
34
34
|
|
|
35
35
|
export { getHreflangAlternates, getHreflangAlternatesWithDb } from "./hreflang.js";
|
|
36
36
|
export type { HreflangAlternate, HreflangOptions } from "./hreflang.js";
|
|
37
37
|
|
|
38
38
|
const TRAILING_SLASH_RE = /\/$/;
|
|
39
|
-
const ABSOLUTE_URL_RE = /^https?:\/\//i;
|
|
40
39
|
|
|
41
40
|
/**
|
|
42
41
|
* Content input for SEO functions.
|
|
@@ -138,16 +137,12 @@ export function getSeoMeta<T>(content: SeoContentInput<T>, options: SeoMetaOptio
|
|
|
138
137
|
// OG image: SEO image > default
|
|
139
138
|
const ogImage = seo.image ? buildSeoImageUrl(seo.image, siteUrl) : (defaultOgImage ?? null);
|
|
140
139
|
|
|
141
|
-
// Canonical: explicit > path-based > null
|
|
140
|
+
// Canonical: explicit > path-based > null. The explicit value goes
|
|
141
|
+
// through the same resolver as the <EmDashHead> overlay, so a panel
|
|
142
|
+
// canonical renders identically on both paths.
|
|
142
143
|
let canonical: string | null = null;
|
|
143
144
|
if (seo.canonical) {
|
|
144
|
-
|
|
145
|
-
// produce "https://example.composts/x" when joined with siteUrl
|
|
146
|
-
if (siteUrl && !seo.canonical.startsWith("/") && !ABSOLUTE_URL_RE.test(seo.canonical)) {
|
|
147
|
-
canonical = `${siteUrl.replace(TRAILING_SLASH_RE, "")}/${seo.canonical}`;
|
|
148
|
-
} else {
|
|
149
|
-
canonical = seo.canonical;
|
|
150
|
-
}
|
|
145
|
+
canonical = resolveSeoCanonicalUrl(seo.canonical, siteUrl);
|
|
151
146
|
} else if (siteUrl && path) {
|
|
152
147
|
const safePath = path.startsWith("/") ? path : `/${path}`;
|
|
153
148
|
canonical = `${siteUrl.replace(TRAILING_SLASH_RE, "")}${safePath}`;
|
package/src/seo/media-url.ts
CHANGED
|
@@ -20,6 +20,12 @@ export function buildSeoImageUrl(imageRef: string, siteUrl?: string): string {
|
|
|
20
20
|
return imageRef;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// `//host/path` is protocol-relative — already qualified, not a path
|
|
24
|
+
// to prefix with the site URL.
|
|
25
|
+
if (imageRef.startsWith("//")) {
|
|
26
|
+
return imageRef;
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
// Root-relative path (already includes the media API prefix). Without
|
|
24
30
|
// this branch we'd re-prefix and produce a doubled path that 404s.
|
|
25
31
|
if (imageRef.startsWith("/")) {
|
|
@@ -30,3 +36,23 @@ export function buildSeoImageUrl(imageRef: string, siteUrl?: string): string {
|
|
|
30
36
|
const mediaPath = `/_emdash/api/media/file/${imageRef}`;
|
|
31
37
|
return siteUrl ? `${siteUrl.replace(TRAILING_SLASH_RE, "")}${mediaPath}` : mediaPath;
|
|
32
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a stored SEO canonical value to a URL.
|
|
42
|
+
*
|
|
43
|
+
* The SEO panel accepts absolute URLs, root-relative paths, and bare
|
|
44
|
+
* relative paths. Relative paths are joined with `siteUrl` (adding a
|
|
45
|
+
* leading slash when missing, so we never produce
|
|
46
|
+
* `https://example.composts/x`). Without a `siteUrl` the value is
|
|
47
|
+
* returned as-is. Absolute output matters here: the resolved value feeds
|
|
48
|
+
* `<link rel="canonical">` and `og:url`, both of which search engines and
|
|
49
|
+
* scrapers expect fully qualified.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveSeoCanonicalUrl(canonical: string, siteUrl?: string): string {
|
|
52
|
+
// `//host/path` is protocol-relative — already qualified, not a path.
|
|
53
|
+
if (!siteUrl || ABSOLUTE_URL_RE.test(canonical) || canonical.startsWith("//")) {
|
|
54
|
+
return canonical;
|
|
55
|
+
}
|
|
56
|
+
const path = canonical.startsWith("/") ? canonical : `/${canonical}`;
|
|
57
|
+
return `${siteUrl.replace(TRAILING_SLASH_RE, "")}${path}`;
|
|
58
|
+
}
|
|
@@ -19,6 +19,10 @@ import { sql } from "kysely";
|
|
|
19
19
|
import { buildStatusCondition, compoundSelectLimit } from "../database/dialect-helpers.js";
|
|
20
20
|
import type { Database } from "../database/types.js";
|
|
21
21
|
import { validateIdentifier } from "../database/validate.js";
|
|
22
|
+
import {
|
|
23
|
+
filterToRegisteredCollections,
|
|
24
|
+
noteStaleRegisteredCollections,
|
|
25
|
+
} from "../schema/collection-slugs-cache.js";
|
|
22
26
|
import { chunks } from "../utils/chunks.js";
|
|
23
27
|
import { isMissingTableError } from "../utils/db-errors.js";
|
|
24
28
|
|
|
@@ -100,6 +104,11 @@ async function runBatch(
|
|
|
100
104
|
return await runCounts(db, taxonomyName, collections, locale);
|
|
101
105
|
} catch (error) {
|
|
102
106
|
if (!isMissingTableError(error)) throw error;
|
|
107
|
+
// The slug filter let a missing table through: the collection was
|
|
108
|
+
// deleted on another isolate (stale cache) or the registry drifted
|
|
109
|
+
// from the physical schema. Window-gated, so the delete case stops
|
|
110
|
+
// sending failing statements within one revalidation window.
|
|
111
|
+
noteStaleRegisteredCollections();
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
const counts = new Map<string, number>();
|
|
@@ -120,10 +129,15 @@ async function runBatch(
|
|
|
120
129
|
* it preserves the locale-agnostic API used by legacy callers.
|
|
121
130
|
*
|
|
122
131
|
* Counts are scoped to the taxonomy's declared collections — pass
|
|
123
|
-
* `TaxonomyDef.collections` (`_emdash_taxonomy_defs.collections`).
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
132
|
+
* `TaxonomyDef.collections` (`_emdash_taxonomy_defs.collections`). Declared
|
|
133
|
+
* collections without a `_emdash_collections` row (the migration-seeded
|
|
134
|
+
* defaults declare `posts` unconditionally) are filtered out before any SQL
|
|
135
|
+
* is built, so no statement referencing a missing `ec_*` table is sent — a
|
|
136
|
+
* failed statement is logged by the database even when the caller recovers,
|
|
137
|
+
* flooding D1 observability with phantom errors. The missing-table guards in
|
|
138
|
+
* `runBatch` stay as the backstop for registry/table drift (e.g. a partially
|
|
139
|
+
* applied D1 create) and for a table dropped mid-flight, yielding a
|
|
140
|
+
* partial-but-correct count rather than a throw.
|
|
127
141
|
*
|
|
128
142
|
* One database round-trip for the whole taxonomy (UNION ALL across
|
|
129
143
|
* collections). On a backend that caps compound-SELECT terms — D1 allows five
|
|
@@ -146,8 +160,11 @@ export async function fetchVisibleTermCounts(
|
|
|
146
160
|
for (const collection of unique) validateIdentifier(collection, "collection slug");
|
|
147
161
|
if (unique.length === 0) return new Map();
|
|
148
162
|
|
|
163
|
+
const present = await filterToRegisteredCollections(db, unique);
|
|
164
|
+
if (present.length === 0) return new Map();
|
|
165
|
+
|
|
149
166
|
const limit = compoundSelectLimit(db);
|
|
150
|
-
const batched = limit === null ? [
|
|
167
|
+
const batched = limit === null ? [present] : chunks(present, limit);
|
|
151
168
|
const batches = await Promise.all(
|
|
152
169
|
batched.map((batch) => runBatch(db, taxonomyName, batch, locale)),
|
|
153
170
|
);
|