emdash 0.19.0 → 0.21.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-C5AWLJSD.d.mts → adapters-BxSmgtbF.d.mts} +1 -1
- package/dist/{adapters-C5AWLJSD.d.mts.map → adapters-BxSmgtbF.d.mts.map} +1 -1
- package/dist/{allowed-origins-CyYLEJkp.mjs → allowed-origins-BqC8cul8.mjs} +2 -2
- package/dist/{allowed-origins-CyYLEJkp.mjs.map → allowed-origins-BqC8cul8.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +13 -12
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +3 -2
- package/dist/{api-BZ6bhjYs.mjs → api-DxjIV2o8.mjs} +46 -15
- package/dist/api-DxjIV2o8.mjs.map +1 -0
- package/dist/{api-tokens-VrXNiNvV.mjs → api-tokens-BFFkB0jB.mjs} +2 -2
- package/dist/{api-tokens-VrXNiNvV.mjs.map → api-tokens-BFFkB0jB.mjs.map} +1 -1
- package/dist/{apply-hQkKKBCf.mjs → apply-CLjxheyb.mjs} +12 -12
- package/dist/{apply-hQkKKBCf.mjs.map → apply-CLjxheyb.mjs.map} +1 -1
- package/dist/astro/index.d.mts +10 -10
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +50 -15
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +9 -9
- package/dist/astro/middleware/auth.mjs +5 -5
- package/dist/astro/middleware/redirect.d.mts.map +1 -1
- package/dist/astro/middleware/redirect.mjs +11 -2
- package/dist/astro/middleware/redirect.mjs.map +1 -1
- package/dist/astro/middleware/request-context.mjs +3 -2
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +91 -137
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -4
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -4
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs.map +1 -1
- 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 +8 -7
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +8 -7
- package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +8 -7
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +14 -12
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +14 -12
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/index.mjs +14 -12
- package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +9 -8
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/bulk.mjs +7 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/counts.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/index.mjs +7 -6
- package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +3 -3
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +2 -2
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +29 -27
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +29 -27
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +28 -26
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +28 -26
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +28 -26
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +28 -26
- 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 +28 -26
- 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 +28 -26
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +28 -26
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +28 -26
- 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 +29 -27
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +28 -26
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +29 -27
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +28 -26
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +28 -26
- 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 +28 -26
- 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 +5 -4
- package/dist/astro/routes/api/admin/users/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- package/dist/astro/routes/api/admin/users/index.mjs +5 -4
- package/dist/astro/routes/api/admin/users/index.mjs.map +1 -1
- 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 +9 -8
- package/dist/astro/routes/api/auth/invite/complete.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/index.mjs +6 -5
- package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/register-options.mjs +8 -7
- package/dist/astro/routes/api/auth/invite/register-options.mjs.map +1 -1
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +8 -7
- package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +5 -4
- package/dist/astro/routes/api/auth/me.mjs.map +1 -1
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -4
- package/dist/astro/routes/api/auth/passkey/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +10 -9
- package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +8 -7
- package/dist/astro/routes/api/auth/passkey/register/options.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +9 -8
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/verify.mjs +9 -8
- package/dist/astro/routes/api/auth/passkey/verify.mjs.map +1 -1
- package/dist/astro/routes/api/auth/signup/complete.mjs +9 -8
- package/dist/astro/routes/api/auth/signup/complete.mjs.map +1 -1
- package/dist/astro/routes/api/auth/signup/request.mjs +8 -7
- package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
- package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +11 -9
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +10 -8
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +10 -9
- 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.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +6 -5
- 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 +6 -5
- package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/trash.mjs.map +1 -1
- package/dist/astro/routes/api/dashboard.mjs +3 -3
- 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 +10 -9
- package/dist/astro/routes/api/import/probe.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs +10 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +8 -7
- package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -8
- package/dist/astro/routes/api/import/wordpress/prepare.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -7
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +10 -9
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +14 -12
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +20 -19
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +6 -5
- package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +6 -5
- package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +8 -7
- package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
- package/dist/astro/routes/api/media.mjs +10 -9
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +6 -5
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items.mjs +6 -5
- package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +6 -5
- package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/translations.mjs +6 -5
- package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_.mjs +6 -5
- package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/index.mjs +6 -5
- package/dist/astro/routes/api/menus/index.mjs.map +1 -1
- 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 +3 -2
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/index.mjs +7 -6
- package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/404s/summary.mjs +7 -6
- package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/_id_.mjs +8 -7
- package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/index.mjs +8 -7
- package/dist/astro/routes/api/redirects/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 +2 -2
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +28 -26
- 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 +28 -26
- 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 +28 -26
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +28 -26
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +28 -26
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +8 -13
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +28 -26
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +28 -26
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +9 -8
- package/dist/astro/routes/api/search/enable.mjs.map +1 -1
- package/dist/astro/routes/api/search/index.mjs +8 -7
- package/dist/astro/routes/api/search/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/rebuild.mjs +9 -8
- package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
- package/dist/astro/routes/api/search/stats.mjs +5 -5
- package/dist/astro/routes/api/search/suggest.mjs +8 -7
- package/dist/astro/routes/api/search/suggest.mjs.map +1 -1
- package/dist/astro/routes/api/sections/_slug_.mjs +8 -7
- package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/sections/index.mjs +8 -7
- package/dist/astro/routes/api/sections/index.mjs.map +1 -1
- package/dist/astro/routes/api/settings/email.mjs +3 -3
- package/dist/astro/routes/api/settings.mjs +11 -9
- package/dist/astro/routes/api/settings.mjs.map +1 -1
- package/dist/astro/routes/api/setup/admin-verify.mjs +10 -9
- package/dist/astro/routes/api/setup/admin-verify.mjs.map +1 -1
- package/dist/astro/routes/api/setup/admin.mjs +9 -8
- package/dist/astro/routes/api/setup/admin.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +19 -18
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +1 -1
- package/dist/astro/routes/api/setup/index.mjs +20 -18
- 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 +5 -4
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +11 -10
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +11 -10
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +11 -10
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.mjs +11 -10
- package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
- package/dist/astro/routes/api/themes/preview.mjs +5 -4
- package/dist/astro/routes/api/themes/preview.mjs.map +1 -1
- package/dist/astro/routes/api/typegen.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- 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 +6 -5
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +9 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +9 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -4
- package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.mjs +9 -7
- package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +5 -4
- package/dist/astro/routes/robots.txt.mjs.map +1 -1
- package/dist/astro/routes/sitemap-_collection_.xml.d.mts.map +1 -1
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +15 -7
- package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
- package/dist/astro/routes/sitemap.xml.mjs +6 -5
- package/dist/astro/routes/sitemap.xml.mjs.map +1 -1
- package/dist/astro/types.d.mts +12 -12
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-C_8t2KGa.mjs → authorize-D5gfBVU5.mjs} +2 -2
- package/dist/{authorize-C_8t2KGa.mjs.map → authorize-D5gfBVU5.mjs.map} +1 -1
- package/dist/{byline-DUx48sJp.mjs → byline-V_Qp1Ziw.mjs} +27 -14
- package/dist/byline-V_Qp1Ziw.mjs.map +1 -0
- package/dist/{byline-fields-51kg6Vuv.mjs → byline-fields-B0NO1yUB.mjs} +3 -3
- package/dist/{byline-fields-51kg6Vuv.mjs.map → byline-fields-B0NO1yUB.mjs.map} +1 -1
- package/dist/{byline-fields-DYXKDuNX.d.mts → byline-fields-CQJRIQkn.d.mts} +36 -32
- package/dist/byline-fields-CQJRIQkn.d.mts.map +1 -0
- package/dist/{byline-fields-C_OsR-KF.mjs → byline-fields-nBVqK_Ff.mjs} +2 -2
- package/dist/{byline-fields-C_OsR-KF.mjs.map → byline-fields-nBVqK_Ff.mjs.map} +1 -1
- package/dist/{byline-registry-CWP7I71B.mjs → byline-registry-DedidtqC.mjs} +2 -2
- package/dist/{byline-registry-CWP7I71B.mjs.map → byline-registry-DedidtqC.mjs.map} +1 -1
- package/dist/{bylines-Cx5n-WqP.mjs → bylines-B2NWnIwS.mjs} +2 -2
- package/dist/{bylines-Cx5n-WqP.mjs.map → bylines-B2NWnIwS.mjs.map} +1 -1
- package/dist/{bylines-wurS258E.mjs → bylines-DfGDnred.mjs} +7 -7
- package/dist/{bylines-wurS258E.mjs.map → bylines-DfGDnred.mjs.map} +1 -1
- package/dist/{cache-B_HzASVT.mjs → cache-DTTHWD8n.mjs} +1 -1
- package/dist/{cache-B_HzASVT.mjs.map → cache-DTTHWD8n.mjs.map} +1 -1
- package/dist/{challenge-store-DGwuCc4R.mjs → challenge-store-woE0bbCf.mjs} +1 -1
- package/dist/{challenge-store-DGwuCc4R.mjs.map → challenge-store-woE0bbCf.mjs.map} +1 -1
- package/dist/cli/index.mjs +22 -20
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/{comments-CJ0RZsYR.mjs → comments-D2hNuxNa.mjs} +1 -1
- package/dist/{comments-CJ0RZsYR.mjs.map → comments-D2hNuxNa.mjs.map} +1 -1
- package/dist/{components-CTfpu3PZ.mjs → components-DYKp2gmo.mjs} +1 -1
- package/dist/{components-CTfpu3PZ.mjs.map → components-DYKp2gmo.mjs.map} +1 -1
- package/dist/{context-GG52SPgh.mjs → context-Cm4pt1Ws.mjs} +5 -5
- package/dist/{context-GG52SPgh.mjs.map → context-Cm4pt1Ws.mjs.map} +1 -1
- package/dist/{cron-BJ2ClIlj.mjs → cron-DdEVrQ2Y.mjs} +1 -1
- package/dist/{cron-BJ2ClIlj.mjs.map → cron-DdEVrQ2Y.mjs.map} +1 -1
- package/dist/{dashboard-2JgAMWxK.mjs → dashboard-C-UYpps0.mjs} +1 -1
- package/dist/{dashboard-2JgAMWxK.mjs.map → dashboard-C-UYpps0.mjs.map} +1 -1
- package/dist/database/instrumentation.d.mts +10 -1
- package/dist/database/instrumentation.d.mts.map +1 -1
- package/dist/database/instrumentation.mjs +13 -1
- package/dist/database/instrumentation.mjs.map +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{db-errors-CtzxKBxe.mjs → db-errors-BluWkwGI.mjs} +1 -1
- package/dist/{db-errors-CtzxKBxe.mjs.map → db-errors-BluWkwGI.mjs.map} +1 -1
- package/dist/{default-xLFNSsZ9.mjs → default-NHGuJzQ3.mjs} +1 -1
- package/dist/{default-xLFNSsZ9.mjs.map → default-NHGuJzQ3.mjs.map} +1 -1
- package/dist/{device-flow-s6_q3T7A.mjs → device-flow-BQApWgnW.mjs} +4 -4
- package/dist/{device-flow-s6_q3T7A.mjs.map → device-flow-BQApWgnW.mjs.map} +1 -1
- package/dist/{email-console-DHT2Fbpj.mjs → email-console-BbU3RbWv.mjs} +1 -1
- package/dist/{email-console-DHT2Fbpj.mjs.map → email-console-BbU3RbWv.mjs.map} +1 -1
- package/dist/{error-RwM4dD35.mjs → error-CNn_w7jf.mjs} +1 -1
- package/dist/{error-RwM4dD35.mjs.map → error-CNn_w7jf.mjs.map} +1 -1
- package/dist/{escape-bIyGoW5W.mjs → escape-DPgcxcpL.mjs} +1 -1
- package/dist/{escape-bIyGoW5W.mjs.map → escape-DPgcxcpL.mjs.map} +1 -1
- package/dist/{fts-manager-1RgHmopc.mjs → fts-manager-Cx5z8jdA.mjs} +1 -1
- package/dist/{fts-manager-1RgHmopc.mjs.map → fts-manager-Cx5z8jdA.mjs.map} +1 -1
- package/dist/{hash-9w3pd3-m.mjs → hash-DlvIFn0b.mjs} +1 -1
- package/dist/{hash-9w3pd3-m.mjs.map → hash-DlvIFn0b.mjs.map} +1 -1
- package/dist/{import-Dh8bWmyq.mjs → import-KyxT1Mbs.mjs} +3 -3
- package/dist/{import-Dh8bWmyq.mjs.map → import-KyxT1Mbs.mjs.map} +1 -1
- package/dist/{index-FfiTQJq2.d.mts → index-D2VAiumu.d.mts} +46 -15
- package/dist/{index-FfiTQJq2.d.mts.map → index-D2VAiumu.d.mts.map} +1 -1
- package/dist/{index-BpYeJO1E.d.mts → index-uT2yR66F.d.mts} +3 -3
- package/dist/{index-BpYeJO1E.d.mts.map → index-uT2yR66F.d.mts.map} +1 -1
- package/dist/index.d.mts +16 -16
- package/dist/index.mjs +48 -46
- package/dist/init-lock-DlBHjf9-.mjs +83 -0
- package/dist/init-lock-DlBHjf9-.mjs.map +1 -0
- package/dist/{load-B84ohfBk.mjs → load-Dq91b_DK.mjs} +1 -1
- package/dist/{load-B84ohfBk.mjs.map → load-Dq91b_DK.mjs.map} +1 -1
- package/dist/{loader-CpZKpFz0.mjs → loader-BqWjcH3h.mjs} +12 -15
- package/dist/loader-BqWjcH3h.mjs.map +1 -0
- package/dist/{manifest-schema-Cj-YrzrF.mjs → manifest-schema-DFPeqMAn.mjs} +55 -2
- package/dist/manifest-schema-DFPeqMAn.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +2 -2
- package/dist/media/local-runtime.d.mts +11 -11
- package/dist/media/local-runtime.mjs +4 -3
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-allowlist-CMcoYIjQ.mjs → media-allowlist-_A0SuDn4.mjs} +2 -2
- package/dist/{media-allowlist-CMcoYIjQ.mjs.map → media-allowlist-_A0SuDn4.mjs.map} +1 -1
- package/dist/media-url-CqLd69IO.mjs +26 -0
- package/dist/media-url-CqLd69IO.mjs.map +1 -0
- package/dist/{menus-Dp9xporj.mjs → menus-Ryk9L7fT.mjs} +10 -37
- package/dist/menus-Ryk9L7fT.mjs.map +1 -0
- package/dist/{mime-CCEzze7W.mjs → mime-YbtlEtvS.mjs} +1 -1
- package/dist/{mime-CCEzze7W.mjs.map → mime-YbtlEtvS.mjs.map} +1 -1
- package/dist/{mode-BjlXswIw.mjs → mode-CGXzIbD8.mjs} +1 -1
- package/dist/{mode-BjlXswIw.mjs.map → mode-CGXzIbD8.mjs.map} +1 -1
- package/dist/{normalize-CK5o04zr.mjs → normalize-DKsg36ty.mjs} +1 -1
- package/dist/{normalize-CK5o04zr.mjs.map → normalize-DKsg36ty.mjs.map} +1 -1
- package/dist/{oauth-authorization-1aPAYjiC.mjs → oauth-authorization-C2kVyjXI.mjs} +4 -4
- package/dist/{oauth-authorization-1aPAYjiC.mjs.map → oauth-authorization-C2kVyjXI.mjs.map} +1 -1
- package/dist/{oauth-clients-8mPDStMv.mjs → oauth-clients-BC873NCV.mjs} +1 -1
- package/dist/{oauth-clients-8mPDStMv.mjs.map → oauth-clients-BC873NCV.mjs.map} +1 -1
- package/dist/{oauth-state-store-BJ7YtrfD.mjs → oauth-state-store-Cd--TUaq.mjs} +1 -1
- package/dist/{oauth-state-store-BJ7YtrfD.mjs.map → oauth-state-store-Cd--TUaq.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-BdDSDvjF.mjs → oauth-user-lookup-e4wOvDud.mjs} +1 -1
- package/dist/{oauth-user-lookup-BdDSDvjF.mjs.map → oauth-user-lookup-e4wOvDud.mjs.map} +1 -1
- package/dist/{options-D4MnavW_.d.mts → options-9kLgkE8m.d.mts} +3 -3
- package/dist/{options-D4MnavW_.d.mts.map → options-9kLgkE8m.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-CrGndy1A.mjs → parse-DzSrk1t8.mjs} +2 -2
- package/dist/{parse-CrGndy1A.mjs.map → parse-DzSrk1t8.mjs.map} +1 -1
- package/dist/{passkey-config-BDVM86Tj.mjs → passkey-config-BpjbE_Uv.mjs} +1 -1
- package/dist/{passkey-config-BDVM86Tj.mjs.map → passkey-config-BpjbE_Uv.mjs.map} +1 -1
- package/dist/{placeholder-BZxr8W1j.mjs → placeholder-2xumZh4g.mjs} +1 -1
- package/dist/{placeholder-BZxr8W1j.mjs.map → placeholder-2xumZh4g.mjs.map} +1 -1
- package/dist/{placeholder-B9lUUEmj.d.mts → placeholder-BevVKfay.d.mts} +1 -1
- package/dist/{placeholder-B9lUUEmj.d.mts.map → placeholder-BevVKfay.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{preview-BfuRkVKW.mjs → preview-Dqv2hwXr.mjs} +2 -2
- package/dist/{preview-BfuRkVKW.mjs.map → preview-Dqv2hwXr.mjs.map} +1 -1
- package/dist/{public-url-egRHCy1m.mjs → public-url-D_zARuvZ.mjs} +1 -1
- package/dist/{public-url-egRHCy1m.mjs.map → public-url-D_zARuvZ.mjs.map} +1 -1
- package/dist/{query-BFQ029Ts.mjs → query-Crm038Mc.mjs} +21 -11
- package/dist/query-Crm038Mc.mjs.map +1 -0
- package/dist/{rate-limit-ClFFUga6.mjs → rate-limit-hRTBqmw1.mjs} +2 -2
- package/dist/{rate-limit-ClFFUga6.mjs.map → rate-limit-hRTBqmw1.mjs.map} +1 -1
- package/dist/{redirect-Cw3JTlmj.mjs → redirect-C-OOkyku.mjs} +1 -1
- package/dist/{redirect-Cw3JTlmj.mjs.map → redirect-C-OOkyku.mjs.map} +1 -1
- package/dist/{redirects-DEygMrRO.mjs → redirects-6Zg2SoYo.mjs} +11 -10
- package/dist/redirects-6Zg2SoYo.mjs.map +1 -0
- package/dist/{redirects-OIu6vQ2i.mjs → redirects-CP3TnTLO.mjs} +20 -14
- package/dist/redirects-CP3TnTLO.mjs.map +1 -0
- package/dist/{registry-brYh-rAT.mjs → registry-diMzD1Wf.mjs} +3 -3
- package/dist/{registry-brYh-rAT.mjs.map → registry-diMzD1Wf.mjs.map} +1 -1
- package/dist/{request-cache-D32LpnmI.mjs → request-cache-UwmBAiUK.mjs} +1 -1
- package/dist/{request-cache-D32LpnmI.mjs.map → request-cache-UwmBAiUK.mjs.map} +1 -1
- package/dist/request-context.d.mts +7 -0
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs +2 -1
- package/dist/request-context.mjs.map +1 -1
- package/dist/{request-meta-7ByVLxB-.mjs → request-meta-DPechd0W.mjs} +2 -2
- package/dist/{request-meta-7ByVLxB-.mjs.map → request-meta-DPechd0W.mjs.map} +1 -1
- package/dist/{resolve-BqYMVG0D.mjs → resolve-B3NUUtVY.mjs} +1 -1
- package/dist/{resolve-BqYMVG0D.mjs.map → resolve-B3NUUtVY.mjs.map} +1 -1
- package/dist/{runner-BcRuXq_h.d.mts → runner-C8vcbvCe.d.mts} +2 -2
- package/dist/{runner-BcRuXq_h.d.mts.map → runner-C8vcbvCe.d.mts.map} +1 -1
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-CS7Eg5gh.mjs → schema-BDOkd3OU.mjs} +4 -4
- package/dist/{schema-CS7Eg5gh.mjs.map → schema-BDOkd3OU.mjs.map} +1 -1
- package/dist/{search-o-aQzHI1.mjs → search-Bs_J_EW-.mjs} +3 -3
- package/dist/{search-o-aQzHI1.mjs.map → search-Bs_J_EW-.mjs.map} +1 -1
- package/dist/{secrets-C_ZtRos3.mjs → secrets-C8xmE6mR.mjs} +21 -11
- package/dist/secrets-C8xmE6mR.mjs.map +1 -0
- package/dist/{sections-DhsZ0ns9.mjs → sections-P0zuBlyz.mjs} +2 -2
- package/dist/{sections-DhsZ0ns9.mjs.map → sections-P0zuBlyz.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +14 -13
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.d.mts.map +1 -1
- package/dist/seo/index.mjs +3 -12
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-DfjLvu8i.mjs → seo-CLhm-Fmb.mjs} +4 -3
- package/dist/seo-CLhm-Fmb.mjs.map +1 -0
- package/dist/{seo-B5e6y9Wk.mjs → seo-DpNgGQjF.mjs} +1 -1
- package/dist/{seo-B5e6y9Wk.mjs.map → seo-DpNgGQjF.mjs.map} +1 -1
- package/dist/{service-DAxg8RPR.mjs → service-CDQQnT8W.mjs} +2 -2
- package/dist/{service-DAxg8RPR.mjs.map → service-CDQQnT8W.mjs.map} +1 -1
- package/dist/{settings-B1p-gPUK.mjs → settings-BjBsmVAo.mjs} +32 -30
- package/dist/settings-BjBsmVAo.mjs.map +1 -0
- package/dist/{settings-DIsbHTRE.mjs → settings-sO0Fif4p.mjs} +2 -2
- package/dist/{settings-DIsbHTRE.mjs.map → settings-sO0Fif4p.mjs.map} +1 -1
- package/dist/{setup-complete-Yuv78yua.mjs → setup-complete-CMMr-oZU.mjs} +1 -1
- package/dist/{setup-complete-Yuv78yua.mjs.map → setup-complete-CMMr-oZU.mjs.map} +1 -1
- package/dist/{setup-nonce-Bm0uKqmf.mjs → setup-nonce-169xl4fV.mjs} +1 -1
- package/dist/{setup-nonce-Bm0uKqmf.mjs.map → setup-nonce-169xl4fV.mjs.map} +1 -1
- package/dist/single-flight-cache-C0UV1Npg.mjs +104 -0
- package/dist/single-flight-cache-C0UV1Npg.mjs.map +1 -0
- package/dist/{site-url-mEVmwIFi.mjs → site-url-vtsuOvSD.mjs} +1 -1
- package/dist/{site-url-mEVmwIFi.mjs.map → site-url-vtsuOvSD.mjs.map} +1 -1
- package/dist/{ssrf-BsVGIE0Z.mjs → ssrf-XO05Voq6.mjs} +1 -1
- package/dist/{ssrf-BsVGIE0Z.mjs.map → ssrf-XO05Voq6.mjs.map} +1 -1
- package/dist/status-2gZklYuj.mjs +30 -0
- package/dist/status-2gZklYuj.mjs.map +1 -0
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +2 -2
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-BEW7S5AI.mjs → taxonomies-BBxYA38v.mjs} +49 -12
- package/dist/taxonomies-BBxYA38v.mjs.map +1 -0
- package/dist/{taxonomies-UusDXv3C.mjs → taxonomies-DuESHWKI.mjs} +2 -2
- package/dist/{taxonomies-UusDXv3C.mjs.map → taxonomies-DuESHWKI.mjs.map} +1 -1
- package/dist/{tokens-Bx2afeT-.mjs → tokens-DMkVjxrx.mjs} +1 -1
- package/dist/{tokens-Bx2afeT-.mjs.map → tokens-DMkVjxrx.mjs.map} +1 -1
- package/dist/{transport--Ck3RBin.mjs → transport-1cIrOb1Y.mjs} +1 -1
- package/dist/{transport--Ck3RBin.mjs.map → transport-1cIrOb1Y.mjs.map} +1 -1
- package/dist/{transport-BwQeeY2p.d.mts → transport-jdvsZEIt.d.mts} +1 -1
- package/dist/{transport-BwQeeY2p.d.mts.map → transport-jdvsZEIt.d.mts.map} +1 -1
- package/dist/{trusted-proxy-B4AfnoAp.mjs → trusted-proxy-CHp41Fjj.mjs} +1 -1
- package/dist/{trusted-proxy-B4AfnoAp.mjs.map → trusted-proxy-CHp41Fjj.mjs.map} +1 -1
- package/dist/{types-DWnN7weG.d.mts → types-BFgYtuKd.d.mts} +1 -1
- package/dist/{types-DWnN7weG.d.mts.map → types-BFgYtuKd.d.mts.map} +1 -1
- package/dist/{types-DZk_y-MU.mjs → types-BIduXPJk.mjs} +1 -1
- package/dist/types-BIduXPJk.mjs.map +1 -0
- package/dist/{types-WVmpZBJV.d.mts → types-BTnnBYVX.d.mts} +2 -2
- package/dist/{types-WVmpZBJV.d.mts.map → types-BTnnBYVX.d.mts.map} +1 -1
- package/dist/types-BoRm8-pp.mjs +3 -0
- package/dist/{types-DbCWhHet.d.mts → types-Bzfk2yC8.d.mts} +2 -2
- package/dist/types-Bzfk2yC8.d.mts.map +1 -0
- package/dist/{types-Qa7-HJJC.d.mts → types-CkEuk-Zr.d.mts} +1 -1
- package/dist/{types-Qa7-HJJC.d.mts.map → types-CkEuk-Zr.d.mts.map} +1 -1
- package/dist/{types-DMwSpvcw.d.mts → types-DO7whVYU.d.mts} +9 -3
- package/dist/{types-DMwSpvcw.d.mts.map → types-DO7whVYU.d.mts.map} +1 -1
- package/dist/{types-DX6v9KzJ.d.mts → types-DdkL6fyv.d.mts} +1 -1
- package/dist/{types-DX6v9KzJ.d.mts.map → types-DdkL6fyv.d.mts.map} +1 -1
- package/dist/{types-DpFmlNyB.mjs → types-DejCHqWT.mjs} +1 -1
- package/dist/{types-DpFmlNyB.mjs.map → types-DejCHqWT.mjs.map} +1 -1
- package/dist/{types-OT_Es5mp.d.mts → types-Del0VMij.d.mts} +1 -1
- package/dist/{types-OT_Es5mp.d.mts.map → types-Del0VMij.d.mts.map} +1 -1
- package/dist/{types-kwqCOUxj.d.mts → types-u_XxjbS8.d.mts} +1 -1
- package/dist/{types-kwqCOUxj.d.mts.map → types-u_XxjbS8.d.mts.map} +1 -1
- package/dist/{utils-C4Ih4DML.mjs → utils-C4M981Br.mjs} +1 -1
- package/dist/{utils-C4Ih4DML.mjs.map → utils-C4M981Br.mjs.map} +1 -1
- package/dist/{validate-ZP9Dvg0P.mjs → validate-DGhQPXzI.mjs} +2 -2
- package/dist/{validate-ZP9Dvg0P.mjs.map → validate-DGhQPXzI.mjs.map} +1 -1
- package/dist/{validate-BPAHUSge.d.mts → validate-cJOiOvT2.d.mts} +5 -5
- package/dist/{validate-BPAHUSge.d.mts.map → validate-cJOiOvT2.d.mts.map} +1 -1
- package/dist/{validation-CE5i4q0c.mjs → validation-DVHjPM1M.mjs} +5 -5
- package/dist/{validation-CE5i4q0c.mjs.map → validation-DVHjPM1M.mjs.map} +1 -1
- package/dist/version-BOjj_cfz.mjs +7 -0
- package/dist/{version-Dw0JXu45.mjs.map → version-BOjj_cfz.mjs.map} +1 -1
- package/dist/{widgets-ClEnYQCH.mjs → widgets-Ci6hLwfO.mjs} +47 -44
- package/dist/widgets-Ci6hLwfO.mjs.map +1 -0
- package/dist/{zod-generator-Djo_VHCt.mjs → zod-generator-CarzgPAu.mjs} +2 -2
- package/dist/{zod-generator-Djo_VHCt.mjs.map → zod-generator-CarzgPAu.mjs.map} +1 -1
- package/package.json +10 -10
- package/src/api/handlers/marketplace.ts +2 -5
- package/src/api/handlers/redirects.ts +24 -13
- package/src/api/handlers/registry.ts +70 -0
- package/src/api/handlers/seo.ts +9 -1
- package/src/api/schemas/redirects.ts +11 -4
- package/src/api/schemas/schema.ts +13 -1
- package/src/astro/integration/index.ts +44 -8
- package/src/astro/integration/routes.ts +46 -9
- package/src/astro/middleware/redirect.ts +12 -0
- package/src/astro/middleware.ts +20 -6
- package/src/astro/routes/api/schema/index.ts +7 -15
- package/src/astro/routes/sitemap-[collection].xml.ts +13 -2
- package/src/bylines/field-defs-cache.ts +70 -20
- package/src/cli/commands/bundle-utils.ts +2 -0
- package/src/cli/commands/doctor.ts +1 -1
- package/src/cli/commands/secrets.ts +2 -2
- package/src/config/secrets.ts +28 -14
- package/src/database/instrumentation.ts +13 -0
- package/src/emdash-runtime.ts +31 -25
- package/src/loader.ts +24 -15
- package/src/plugins/manifest-schema.ts +75 -0
- package/src/plugins/marketplace.ts +2 -5
- package/src/plugins/types.ts +12 -0
- package/src/query.ts +13 -2
- package/src/redirects/status.ts +27 -0
- package/src/request-context.ts +8 -0
- package/src/schema/types.ts +11 -1
- package/src/seo/index.ts +2 -28
- package/src/seo/media-url.ts +32 -0
- package/src/settings/index.ts +32 -40
- package/src/taxonomies/index.ts +78 -12
- package/src/utils/single-flight-cache.ts +194 -0
- package/src/widgets/index.ts +57 -54
- package/dist/api-BZ6bhjYs.mjs.map +0 -1
- package/dist/byline-DUx48sJp.mjs.map +0 -1
- package/dist/byline-fields-DYXKDuNX.d.mts.map +0 -1
- package/dist/loader-CpZKpFz0.mjs.map +0 -1
- package/dist/manifest-schema-Cj-YrzrF.mjs.map +0 -1
- package/dist/menus-Dp9xporj.mjs.map +0 -1
- package/dist/query-BFQ029Ts.mjs.map +0 -1
- package/dist/redirects-DEygMrRO.mjs.map +0 -1
- package/dist/redirects-OIu6vQ2i.mjs.map +0 -1
- package/dist/secrets-C_ZtRos3.mjs.map +0 -1
- package/dist/seo-DfjLvu8i.mjs.map +0 -1
- package/dist/settings-B1p-gPUK.mjs.map +0 -1
- package/dist/taxonomies-BEW7S5AI.mjs.map +0 -1
- package/dist/types-Cj2S6FuC.mjs +0 -3
- package/dist/types-DZk_y-MU.mjs.map +0 -1
- package/dist/types-DbCWhHet.d.mts.map +0 -1
- package/dist/version-Dw0JXu45.mjs +0 -7
- package/dist/widgets-ClEnYQCH.mjs.map +0 -1
- /package/dist/{api-tokens-B6VgoE6M.mjs → api-tokens-C7ywRx7l.mjs} +0 -0
- /package/dist/{ssrf-BvgVcfNQ.mjs → ssrf-CRZGzjdL.mjs} +0 -0
|
@@ -8,8 +8,14 @@
|
|
|
8
8
|
* - Marketplace ingest extends this with publishing-specific fields
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import {
|
|
12
|
+
capabilitiesToDeclaredAccess,
|
|
13
|
+
declaredAccessToCapabilities,
|
|
14
|
+
} from "@emdash-cms/plugin-types";
|
|
11
15
|
import { z } from "zod";
|
|
12
16
|
|
|
17
|
+
import type { PluginManifest } from "./types.js";
|
|
18
|
+
|
|
13
19
|
// ── Enum values (must stay in sync with types.ts) ───────────────
|
|
14
20
|
|
|
15
21
|
/**
|
|
@@ -219,16 +225,63 @@ const pluginAdminConfigSchema = z.object({
|
|
|
219
225
|
.optional(),
|
|
220
226
|
});
|
|
221
227
|
|
|
228
|
+
// ── declaredAccess ──────────────────────────────────────────────
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* An operation's constraint object. Open vocabulary: keys the runtime
|
|
232
|
+
* recognises are enforced, others are advisory. The bundler emits `{}` for a
|
|
233
|
+
* granted operation; presence (not value) signals the grant.
|
|
234
|
+
*/
|
|
235
|
+
const accessConstraints = z.record(z.string(), z.unknown());
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Structured trust contract embedded in the bundle manifest. Mirrors
|
|
239
|
+
* `DeclaredAccess` in `@emdash-cms/plugin-types`. Categories are host
|
|
240
|
+
* subsystems; operations are modes of participation.
|
|
241
|
+
*/
|
|
242
|
+
const declaredAccessSchema = z.object({
|
|
243
|
+
content: z
|
|
244
|
+
.object({ read: accessConstraints.optional(), write: accessConstraints.optional() })
|
|
245
|
+
.optional(),
|
|
246
|
+
media: z
|
|
247
|
+
.object({ read: accessConstraints.optional(), write: accessConstraints.optional() })
|
|
248
|
+
.optional(),
|
|
249
|
+
network: z
|
|
250
|
+
.object({
|
|
251
|
+
// allowedHosts: absent = unrestricted; present = host-restricted. Reject
|
|
252
|
+
// an empty array (which the decoder would otherwise have to treat as
|
|
253
|
+
// deny-all) to match the record lexicon's `minLength: 1` and keep the
|
|
254
|
+
// "absent vs empty" distinction from ever reaching enforcement ambiguous.
|
|
255
|
+
request: z.object({ allowedHosts: z.array(z.string()).min(1).optional() }).optional(),
|
|
256
|
+
})
|
|
257
|
+
.optional(),
|
|
258
|
+
email: z
|
|
259
|
+
.object({
|
|
260
|
+
send: accessConstraints.optional(),
|
|
261
|
+
events: accessConstraints.optional(),
|
|
262
|
+
transport: accessConstraints.optional(),
|
|
263
|
+
})
|
|
264
|
+
.optional(),
|
|
265
|
+
page: z.object({ fragments: accessConstraints.optional() }).optional(),
|
|
266
|
+
users: z.object({ read: accessConstraints.optional() }).optional(),
|
|
267
|
+
});
|
|
268
|
+
|
|
222
269
|
// ── Main schema ─────────────────────────────────────────────────
|
|
223
270
|
|
|
224
271
|
/**
|
|
225
272
|
* Zod schema matching the PluginManifest interface from types.ts.
|
|
226
273
|
*
|
|
227
274
|
* Every JSON.parse of a manifest.json should validate through this.
|
|
275
|
+
*
|
|
276
|
+
* `declaredAccess` is the trust contract; `capabilities`/`allowedHosts` are the
|
|
277
|
+
* runtime's enforcement currency. Apply `reconcileManifestAccess` after parsing
|
|
278
|
+
* to make them consistent (declaredAccess authoritative when present). Kept a
|
|
279
|
+
* plain object (no `.transform`) because callers `.pick()`/`.extend()` it.
|
|
228
280
|
*/
|
|
229
281
|
export const pluginManifestSchema = z.object({
|
|
230
282
|
id: z.string().min(1),
|
|
231
283
|
version: z.string().min(1),
|
|
284
|
+
declaredAccess: declaredAccessSchema.optional(),
|
|
232
285
|
capabilities: z.array(z.enum(PLUGIN_CAPABILITIES)),
|
|
233
286
|
allowedHosts: z.array(z.string()),
|
|
234
287
|
storage: z.record(z.string(), storageCollectionSchema),
|
|
@@ -254,6 +307,28 @@ export const pluginManifestSchema = z.object({
|
|
|
254
307
|
|
|
255
308
|
export type ValidatedPluginManifest = z.infer<typeof pluginManifestSchema>;
|
|
256
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Reconcile a parsed manifest's trust contract with its enforcement currency.
|
|
312
|
+
* `declaredAccess` is authoritative: when present, `capabilities`/`allowedHosts`
|
|
313
|
+
* are re-derived from it so what the runtime enforces always matches what was
|
|
314
|
+
* recorded and consented to. A pre-migration bundle without `declaredAccess`
|
|
315
|
+
* has it derived from the legacy capability list instead. The result always
|
|
316
|
+
* carries both, mutually consistent. Apply this at every bundle-parse site.
|
|
317
|
+
*/
|
|
318
|
+
export function reconcileManifestAccess(manifest: ValidatedPluginManifest): PluginManifest {
|
|
319
|
+
const reconciled: ValidatedPluginManifest = manifest.declaredAccess
|
|
320
|
+
? { ...manifest, ...declaredAccessToCapabilities(manifest.declaredAccess) }
|
|
321
|
+
: {
|
|
322
|
+
...manifest,
|
|
323
|
+
declaredAccess: capabilitiesToDeclaredAccess(manifest.capabilities, manifest.allowedHosts),
|
|
324
|
+
};
|
|
325
|
+
// Block Kit admin elements are typed as `unknown` by the Zod schema (their
|
|
326
|
+
// Element shape is validated at render time), so the validated manifest
|
|
327
|
+
// needs a structural cast up to the runtime PluginManifest.
|
|
328
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- admin elements are unknown[] in Zod; Element type checked at render time
|
|
329
|
+
return reconciled as unknown as PluginManifest;
|
|
330
|
+
}
|
|
331
|
+
|
|
257
332
|
/**
|
|
258
333
|
* Normalize a manifest hook entry — plain strings become `{ name }` objects.
|
|
259
334
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { createGzipDecoder, unpackTar } from "modern-tar";
|
|
10
10
|
|
|
11
|
-
import { pluginManifestSchema } from "./manifest-schema.js";
|
|
11
|
+
import { pluginManifestSchema, reconcileManifestAccess } from "./manifest-schema.js";
|
|
12
12
|
import type { PluginManifest } from "./types.js";
|
|
13
13
|
|
|
14
14
|
// ── Module-level regex patterns ───────────────────────────────────
|
|
@@ -495,10 +495,7 @@ export async function extractBundle(tarballBytes: Uint8Array): Promise<PluginBun
|
|
|
495
495
|
"INVALID_BUNDLE",
|
|
496
496
|
);
|
|
497
497
|
}
|
|
498
|
-
|
|
499
|
-
// for the Element[] type (Block Kit validation happens at render time).
|
|
500
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Zod types elements as unknown[]; Element type validated at render time
|
|
501
|
-
manifest = result.data as unknown as PluginManifest;
|
|
498
|
+
manifest = reconcileManifestAccess(result.data);
|
|
502
499
|
} catch (err) {
|
|
503
500
|
if (err instanceof MarketplaceError) throw err;
|
|
504
501
|
throw new MarketplaceError(
|
package/src/plugins/types.ts
CHANGED
|
@@ -19,10 +19,13 @@ import type { Element } from "@emdash-cms/blocks";
|
|
|
19
19
|
// (e.g. `import { PluginCapability } from "../plugins/types.js"`).
|
|
20
20
|
import {
|
|
21
21
|
CAPABILITY_RENAMES,
|
|
22
|
+
capabilitiesToDeclaredAccess,
|
|
23
|
+
declaredAccessToCapabilities,
|
|
22
24
|
isDeprecatedCapability,
|
|
23
25
|
normalizeCapabilities,
|
|
24
26
|
normalizeCapability,
|
|
25
27
|
type CurrentPluginCapability,
|
|
28
|
+
type DeclaredAccess,
|
|
26
29
|
type DeprecatedPluginCapability,
|
|
27
30
|
type ManifestHookEntry,
|
|
28
31
|
type ManifestRouteEntry,
|
|
@@ -40,10 +43,13 @@ import type { FieldType } from "../schema/types.js";
|
|
|
40
43
|
|
|
41
44
|
export {
|
|
42
45
|
CAPABILITY_RENAMES,
|
|
46
|
+
capabilitiesToDeclaredAccess,
|
|
47
|
+
declaredAccessToCapabilities,
|
|
43
48
|
isDeprecatedCapability,
|
|
44
49
|
normalizeCapabilities,
|
|
45
50
|
normalizeCapability,
|
|
46
51
|
type CurrentPluginCapability,
|
|
52
|
+
type DeclaredAccess,
|
|
47
53
|
type DeprecatedPluginCapability,
|
|
48
54
|
type ManifestHookEntry,
|
|
49
55
|
type ManifestRouteEntry,
|
|
@@ -1336,6 +1342,12 @@ export interface PluginAdminExports {
|
|
|
1336
1342
|
export interface PluginManifest {
|
|
1337
1343
|
id: string;
|
|
1338
1344
|
version: string;
|
|
1345
|
+
/**
|
|
1346
|
+
* The trust contract (see `@emdash-cms/plugin-types`). Authoritative;
|
|
1347
|
+
* `capabilities`/`allowedHosts` are derived from it at the parse boundary
|
|
1348
|
+
* via `reconcileManifestAccess`. Optional during the wire-format migration.
|
|
1349
|
+
*/
|
|
1350
|
+
declaredAccess?: DeclaredAccess;
|
|
1339
1351
|
capabilities: PluginCapability[];
|
|
1340
1352
|
allowedHosts: string[];
|
|
1341
1353
|
storage: PluginStorageConfig;
|
package/src/query.ts
CHANGED
|
@@ -247,6 +247,17 @@ function dataStr(data: Record<string, unknown>, key: string, fallback = ""): str
|
|
|
247
247
|
return typeof val === "string" ? val : fallback;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
/** Safely read a date-like field from a Record */
|
|
251
|
+
function dataDate(data: Record<string, unknown>, key: string): Date | undefined {
|
|
252
|
+
const val = data[key];
|
|
253
|
+
if (val instanceof Date) {
|
|
254
|
+
return Number.isNaN(val.getTime()) ? undefined : val;
|
|
255
|
+
}
|
|
256
|
+
if (typeof val !== "string" && typeof val !== "number") return undefined;
|
|
257
|
+
const date = new Date(val);
|
|
258
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
259
|
+
}
|
|
260
|
+
|
|
250
261
|
/** Type guard for Record<string, unknown> */
|
|
251
262
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
252
263
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -602,10 +613,10 @@ export async function getEmDashEntry<T extends string, D = InferCollectionData<T
|
|
|
602
613
|
function isVisible(entry: ContentEntry<D>): boolean {
|
|
603
614
|
const data = entryData(entry);
|
|
604
615
|
const status = dataStr(data, "status");
|
|
605
|
-
const scheduledAt =
|
|
616
|
+
const scheduledAt = dataDate(data, "scheduledAt");
|
|
606
617
|
const isPublished = status === "published";
|
|
607
618
|
const isScheduledAndReady =
|
|
608
|
-
status === "scheduled" && scheduledAt &&
|
|
619
|
+
status === "scheduled" && scheduledAt !== undefined && scheduledAt.getTime() <= Date.now();
|
|
609
620
|
return isPublished || !!isScheduledAndReady;
|
|
610
621
|
}
|
|
611
622
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redirect rule status codes.
|
|
3
|
+
*
|
|
4
|
+
* A redirect rule's `type` is either a *redirect* status (issues a `Location`
|
|
5
|
+
* header) or a *terminal* status (serves the status with no target). Terminal
|
|
6
|
+
* statuses let editors mark a URL as intentionally gone:
|
|
7
|
+
* - `410 Gone` — permanently and intentionally deleted (Google deindexes it
|
|
8
|
+
* faster than a 404).
|
|
9
|
+
* - `451 Unavailable For Legal Reasons`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Statuses that issue an HTTP redirect (require a destination). */
|
|
13
|
+
export const REDIRECT_STATUSES = [301, 302, 307, 308] as const;
|
|
14
|
+
|
|
15
|
+
/** Terminal statuses that serve a status with no `Location` / no destination. */
|
|
16
|
+
export const TERMINAL_STATUSES = [410, 451] as const;
|
|
17
|
+
|
|
18
|
+
/** All values accepted as a redirect rule `type`. */
|
|
19
|
+
export const REDIRECT_RULE_STATUSES: readonly number[] = [
|
|
20
|
+
...REDIRECT_STATUSES,
|
|
21
|
+
...TERMINAL_STATUSES,
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/** True for terminal statuses (410/451) — served directly, with no target. */
|
|
25
|
+
export function isTerminalStatus(type: number): boolean {
|
|
26
|
+
return (TERMINAL_STATUSES as readonly number[]).includes(type);
|
|
27
|
+
}
|
package/src/request-context.ts
CHANGED
|
@@ -39,6 +39,13 @@ export interface RequestMetrics {
|
|
|
39
39
|
dbLastOffset: number | null;
|
|
40
40
|
cacheHits: number;
|
|
41
41
|
cacheMisses: number;
|
|
42
|
+
/**
|
|
43
|
+
* Physical database round trips. Differs from `dbCount` (logical queries)
|
|
44
|
+
* when a backend batches: the DO SQL driver coalesces same-turn SELECTs into
|
|
45
|
+
* one RPC, so `rpcCount` can be far lower than `dbCount`. Bumped by the
|
|
46
|
+
* adapter, not the Kysely log hook.
|
|
47
|
+
*/
|
|
48
|
+
rpcCount: number;
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
export function createRequestMetrics(start: number): RequestMetrics {
|
|
@@ -50,6 +57,7 @@ export function createRequestMetrics(start: number): RequestMetrics {
|
|
|
50
57
|
dbLastOffset: null,
|
|
51
58
|
cacheHits: 0,
|
|
52
59
|
cacheMisses: 0,
|
|
60
|
+
rpcCount: 0,
|
|
53
61
|
};
|
|
54
62
|
}
|
|
55
63
|
|
package/src/schema/types.ts
CHANGED
|
@@ -102,7 +102,16 @@ export type CollectionSource =
|
|
|
102
102
|
/** Sub-field definition for repeater fields */
|
|
103
103
|
export interface RepeaterSubField {
|
|
104
104
|
slug: string;
|
|
105
|
-
type:
|
|
105
|
+
type:
|
|
106
|
+
| "string"
|
|
107
|
+
| "text"
|
|
108
|
+
| "url"
|
|
109
|
+
| "number"
|
|
110
|
+
| "integer"
|
|
111
|
+
| "boolean"
|
|
112
|
+
| "datetime"
|
|
113
|
+
| "select"
|
|
114
|
+
| "image";
|
|
106
115
|
label: string;
|
|
107
116
|
required?: boolean;
|
|
108
117
|
options?: string[]; // For select sub-fields
|
|
@@ -118,6 +127,7 @@ export const REPEATER_SUB_FIELD_TYPES = [
|
|
|
118
127
|
"boolean",
|
|
119
128
|
"datetime",
|
|
120
129
|
"select",
|
|
130
|
+
"image",
|
|
121
131
|
] as const;
|
|
122
132
|
|
|
123
133
|
export interface FieldValidation {
|
package/src/seo/index.ts
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import type { ContentSeo } from "../database/repositories/types.js";
|
|
33
|
+
import { buildSeoImageUrl } from "./media-url.js";
|
|
33
34
|
|
|
34
35
|
const TRAILING_SLASH_RE = /\/$/;
|
|
35
36
|
const ABSOLUTE_URL_RE = /^https?:\/\//i;
|
|
@@ -117,7 +118,7 @@ export function getSeoMeta<T>(content: SeoContentInput<T>, options: SeoMetaOptio
|
|
|
117
118
|
null;
|
|
118
119
|
|
|
119
120
|
// OG image: SEO image > default
|
|
120
|
-
const ogImage = seo.image ?
|
|
121
|
+
const ogImage = seo.image ? buildSeoImageUrl(seo.image, siteUrl) : (defaultOgImage ?? null);
|
|
121
122
|
|
|
122
123
|
// Canonical: explicit > path-based > null
|
|
123
124
|
let canonical: string | null = null;
|
|
@@ -159,30 +160,3 @@ export function getSeoMeta<T>(content: SeoContentInput<T>, options: SeoMetaOptio
|
|
|
159
160
|
export function getContentSeo<T>(content: SeoContentInput<T>): ContentSeo | undefined {
|
|
160
161
|
return content.seo ?? content.data.seo;
|
|
161
162
|
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Build a media URL from a media reference ID.
|
|
165
|
-
* If it's already an absolute URL, return as-is.
|
|
166
|
-
*/
|
|
167
|
-
function buildMediaUrl(imageRef: string, siteUrl?: string): string {
|
|
168
|
-
// If already an absolute URL, return as-is
|
|
169
|
-
if (ABSOLUTE_URL_RE.test(imageRef)) {
|
|
170
|
-
return imageRef;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Root-relative path — the CMS SEO panel stores seo_image as
|
|
174
|
-
// "/_emdash/api/media/file/01KS....svg" (already includes the API
|
|
175
|
-
// prefix). Without this branch we'd re-prefix and produce
|
|
176
|
-
// "${siteUrl}/_emdash/api/media/file//_emdash/api/media/file/<id>"
|
|
177
|
-
// which 404s and breaks <meta property="og:image">.
|
|
178
|
-
if (imageRef.startsWith("/")) {
|
|
179
|
-
return siteUrl ? `${siteUrl.replace(TRAILING_SLASH_RE, "")}${imageRef}` : imageRef;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Bare media_id — build the full media API path
|
|
183
|
-
const mediaPath = `/_emdash/api/media/file/${imageRef}`;
|
|
184
|
-
if (siteUrl) {
|
|
185
|
-
return `${siteUrl.replace(TRAILING_SLASH_RE, "")}${mediaPath}`;
|
|
186
|
-
}
|
|
187
|
-
return mediaPath;
|
|
188
|
-
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a stored SEO image reference to a URL.
|
|
3
|
+
*
|
|
4
|
+
* The CMS SEO panel stores `seo_image` in one of these shapes:
|
|
5
|
+
* - an absolute URL (`https://...`) — returned as-is;
|
|
6
|
+
* - a root-relative path that already includes the media API prefix
|
|
7
|
+
* (`/_emdash/api/media/file/01KS....webp`) — prefixed with `siteUrl`;
|
|
8
|
+
* - a bare media id (`01KS...`) — expanded to the media API path, then
|
|
9
|
+
* prefixed with `siteUrl`.
|
|
10
|
+
*
|
|
11
|
+
* Shared by the SEO meta builder (`og:image`) and the sitemap route
|
|
12
|
+
* (`<image:image>`) so both resolve image references identically.
|
|
13
|
+
*/
|
|
14
|
+
const TRAILING_SLASH_RE = /\/$/;
|
|
15
|
+
const ABSOLUTE_URL_RE = /^https?:\/\//i;
|
|
16
|
+
|
|
17
|
+
export function buildSeoImageUrl(imageRef: string, siteUrl?: string): string {
|
|
18
|
+
// Already absolute — use as-is.
|
|
19
|
+
if (ABSOLUTE_URL_RE.test(imageRef)) {
|
|
20
|
+
return imageRef;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Root-relative path (already includes the media API prefix). Without
|
|
24
|
+
// this branch we'd re-prefix and produce a doubled path that 404s.
|
|
25
|
+
if (imageRef.startsWith("/")) {
|
|
26
|
+
return siteUrl ? `${siteUrl.replace(TRAILING_SLASH_RE, "")}${imageRef}` : imageRef;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Bare media id — build the full media API path.
|
|
30
|
+
const mediaPath = `/_emdash/api/media/file/${imageRef}`;
|
|
31
|
+
return siteUrl ? `${siteUrl.replace(TRAILING_SLASH_RE, "")}${mediaPath}` : mediaPath;
|
|
32
|
+
}
|
package/src/settings/index.ts
CHANGED
|
@@ -7,12 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Kysely } from "kysely";
|
|
9
9
|
|
|
10
|
+
import { after } from "../after.js";
|
|
10
11
|
import { MediaRepository } from "../database/repositories/media.js";
|
|
11
12
|
import { OptionsRepository } from "../database/repositories/options.js";
|
|
12
13
|
import type { Database } from "../database/types.js";
|
|
13
14
|
import { getDb } from "../loader.js";
|
|
14
15
|
import { peekRequestCache, requestCached } from "../request-cache.js";
|
|
15
16
|
import type { Storage } from "../storage/types.js";
|
|
17
|
+
import {
|
|
18
|
+
createSingleFlightCache,
|
|
19
|
+
type SingleFlightCache,
|
|
20
|
+
invalidateSingleFlightCache,
|
|
21
|
+
singleFlightCached,
|
|
22
|
+
} from "../utils/single-flight-cache.js";
|
|
16
23
|
import type { SiteSettings, SiteSettingKey, MediaReference, SeoSettings } from "./types.js";
|
|
17
24
|
|
|
18
25
|
/** Prefix for site settings in the options table */
|
|
@@ -27,29 +34,22 @@ const SETTINGS_PREFIX = "site:";
|
|
|
27
34
|
* once-per-isolate. Cross-isolate staleness is bounded by isolate lifetime
|
|
28
35
|
* (workerd typically recycles within minutes); acceptable for chrome.
|
|
29
36
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* cold-isolate readers share the in-flight query.
|
|
37
|
+
* Backed by single-flight-cache.ts: concurrent cold reads coalesce onto one
|
|
38
|
+
* query via a reclaimable single-flight lock and the resolved *value* is
|
|
39
|
+
* cached — never a shared in-flight promise, so a cancelled request can't
|
|
40
|
+
* poison the isolate (see that file's header). Stored on globalThis with a
|
|
41
|
+
* Symbol.for key so Vite SSR chunk duplication doesn't produce two
|
|
42
|
+
* independent caches (same pattern as request-context.ts).
|
|
37
43
|
*/
|
|
38
|
-
interface SiteSettingsHolder {
|
|
39
|
-
version: number;
|
|
40
|
-
cached: Promise<Partial<SiteSettings>> | null;
|
|
41
|
-
cachedVersion: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
44
|
const SITE_SETTINGS_CACHE_KEY = Symbol.for("emdash:site-settings");
|
|
45
45
|
const g = globalThis as Record<symbol, unknown>;
|
|
46
|
-
const
|
|
46
|
+
const settingsCache: SingleFlightCache<Partial<SiteSettings>> =
|
|
47
47
|
// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis singleton pattern (see request-context.ts)
|
|
48
|
-
(g[SITE_SETTINGS_CACHE_KEY] as
|
|
48
|
+
(g[SITE_SETTINGS_CACHE_KEY] as SingleFlightCache<Partial<SiteSettings>> | undefined) ??
|
|
49
49
|
(() => {
|
|
50
|
-
const
|
|
51
|
-
g[SITE_SETTINGS_CACHE_KEY] =
|
|
52
|
-
return
|
|
50
|
+
const c = createSingleFlightCache<Partial<SiteSettings>>();
|
|
51
|
+
g[SITE_SETTINGS_CACHE_KEY] = c;
|
|
52
|
+
return c;
|
|
53
53
|
})();
|
|
54
54
|
|
|
55
55
|
/**
|
|
@@ -60,9 +60,7 @@ const holder: SiteSettingsHolder =
|
|
|
60
60
|
* own cached copy until they expire — staleness bounded by isolate lifetime.
|
|
61
61
|
*/
|
|
62
62
|
export function invalidateSiteSettingsCache(): void {
|
|
63
|
-
|
|
64
|
-
holder.cached = null;
|
|
65
|
-
holder.cachedVersion = -1;
|
|
63
|
+
invalidateSingleFlightCache(settingsCache);
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
/**
|
|
@@ -210,25 +208,19 @@ export async function getSiteSettingWithDb<K extends SiteSettingKey>(
|
|
|
210
208
|
* ```
|
|
211
209
|
*/
|
|
212
210
|
export function getSiteSettings(): Promise<Partial<SiteSettings>> {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
throw error;
|
|
227
|
-
});
|
|
228
|
-
holder.cached = fetchPromise;
|
|
229
|
-
holder.cachedVersion = versionAtCall;
|
|
230
|
-
return fetchPromise;
|
|
231
|
-
});
|
|
211
|
+
// requestCached dedupes within a single request; singleFlightCached
|
|
212
|
+
// coalesces across requests and caches the resolved value for the
|
|
213
|
+
// global scope's lifetime without ever sharing an awaitable promise.
|
|
214
|
+
return requestCached("siteSettings", () =>
|
|
215
|
+
singleFlightCached(
|
|
216
|
+
settingsCache,
|
|
217
|
+
async () => {
|
|
218
|
+
const db = await getDb();
|
|
219
|
+
return getSiteSettingsWithDb(db);
|
|
220
|
+
},
|
|
221
|
+
{ anchor: (promise) => after(() => promise), ownerTimeoutMs: 30_000 },
|
|
222
|
+
),
|
|
223
|
+
);
|
|
232
224
|
}
|
|
233
225
|
|
|
234
226
|
/**
|
package/src/taxonomies/index.ts
CHANGED
|
@@ -120,15 +120,10 @@ export async function getTaxonomyTerms(
|
|
|
120
120
|
if (locale !== undefined) termsQuery = termsQuery.where("locale", "=", locale);
|
|
121
121
|
const rows = await termsQuery.execute();
|
|
122
122
|
|
|
123
|
-
// Counts are keyed by translation_group (what the pivot stores)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.select((eb) => eb.fn.count<number>("entry_id").as("count"))
|
|
128
|
-
.groupBy("taxonomy_id")
|
|
129
|
-
.execute();
|
|
130
|
-
const counts = new Map<string, number>();
|
|
131
|
-
for (const row of countsResult) counts.set(row.taxonomy_id, row.count);
|
|
123
|
+
// Counts are keyed by translation_group (what the pivot stores) and are
|
|
124
|
+
// locale-independent, so the aggregate is shared across every taxonomy
|
|
125
|
+
// rendered in this request (Categories + Tags widgets, etc.).
|
|
126
|
+
const counts = await getTaxonomyTermCounts();
|
|
132
127
|
|
|
133
128
|
const flatTerms: TaxonomyTermRow[] = rows.map((row) => ({
|
|
134
129
|
id: row.id,
|
|
@@ -157,6 +152,27 @@ export async function getTaxonomyTerms(
|
|
|
157
152
|
});
|
|
158
153
|
}
|
|
159
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Per-translation-group usage counts across all taxonomies, in one aggregate
|
|
157
|
+
* scan of `content_taxonomies`. Counts are locale-independent (the pivot stores
|
|
158
|
+
* translation_group), so a single request-cached entry serves every taxonomy
|
|
159
|
+
* that renders during the request.
|
|
160
|
+
*/
|
|
161
|
+
function getTaxonomyTermCounts(): Promise<Map<string, number>> {
|
|
162
|
+
return requestCached("taxonomy-term-counts", async () => {
|
|
163
|
+
const db = await getDb();
|
|
164
|
+
const countsResult = await db
|
|
165
|
+
.selectFrom("content_taxonomies")
|
|
166
|
+
.select(["taxonomy_id"])
|
|
167
|
+
.select((eb) => eb.fn.count<number>("entry_id").as("count"))
|
|
168
|
+
.groupBy("taxonomy_id")
|
|
169
|
+
.execute();
|
|
170
|
+
const counts = new Map<string, number>();
|
|
171
|
+
for (const row of countsResult) counts.set(row.taxonomy_id, row.count);
|
|
172
|
+
return counts;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
160
176
|
/**
|
|
161
177
|
* Get a single term by (taxonomy, slug). Honours the fallback chain — if the
|
|
162
178
|
* slug exists in a fallback locale, we return that row (useful for deep-linking
|
|
@@ -290,10 +306,57 @@ export async function getTermsForEntries(
|
|
|
290
306
|
for (const id of uniqueIds) result.set(id, []);
|
|
291
307
|
if (uniqueIds.length === 0) return result;
|
|
292
308
|
|
|
293
|
-
const db = await getDb();
|
|
294
309
|
const locale = resolveLocale(options.locale);
|
|
310
|
+
const localeKey = locale ?? "*";
|
|
295
311
|
|
|
296
|
-
|
|
312
|
+
// Entry-term hydration (getAllTermsForEntries -> primeEntryTermsCache)
|
|
313
|
+
// seeds the per-entry cache under the same key getEntryTerms uses:
|
|
314
|
+
// `terms:${collection}:${entryId}:${taxonomyName}:${localeKey}`, storing a
|
|
315
|
+
// TaxonomyTerm[] (including `[]` for entries with no terms). Satisfy those
|
|
316
|
+
// from cache and run the batched query only for the ids that missed.
|
|
317
|
+
const missedIds: string[] = [];
|
|
318
|
+
type CacheRead = { id: string; terms: TaxonomyTerm[] } | { id: string; miss: true };
|
|
319
|
+
const cacheReads: Array<Promise<CacheRead>> = [];
|
|
320
|
+
for (const id of uniqueIds) {
|
|
321
|
+
const cached = peekRequestCache<TaxonomyTerm[]>(
|
|
322
|
+
`terms:${collection}:${id}:${taxonomyName}:${localeKey}`,
|
|
323
|
+
);
|
|
324
|
+
if (cached) {
|
|
325
|
+
// A peeked promise can reject (e.g. a sibling getEntryTerms hit a
|
|
326
|
+
// missing table). Treat a rejection as a cache miss so the batched
|
|
327
|
+
// query path -- and its isMissingTableError guard below -- still runs,
|
|
328
|
+
// rather than propagating an uncaught error.
|
|
329
|
+
cacheReads.push(
|
|
330
|
+
cached.then(
|
|
331
|
+
(terms): CacheRead => ({ id, terms }),
|
|
332
|
+
(): CacheRead => ({ id, miss: true }),
|
|
333
|
+
),
|
|
334
|
+
);
|
|
335
|
+
} else {
|
|
336
|
+
missedIds.push(id);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
for (const read of await Promise.all(cacheReads)) {
|
|
340
|
+
if ("miss" in read) {
|
|
341
|
+
missedIds.push(read.id);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
// Return a private copy. The cached array and its term objects are shared
|
|
345
|
+
// with getEntryTerms/getAllTermsForEntries (primeEntryTermsCache stores
|
|
346
|
+
// the same references), so a caller that mutates the result -- sorting in
|
|
347
|
+
// place, pushing into `children` -- must not poison the cache. The
|
|
348
|
+
// pre-cache implementation always returned freshly built arrays.
|
|
349
|
+
result.set(
|
|
350
|
+
read.id,
|
|
351
|
+
read.terms.map((t) => ({ ...t, children: [...t.children] })),
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (missedIds.length === 0) return result;
|
|
356
|
+
|
|
357
|
+
const db = await getDb();
|
|
358
|
+
|
|
359
|
+
for (const chunk of chunks(missedIds, SQL_BATCH_SIZE)) {
|
|
297
360
|
let rows;
|
|
298
361
|
try {
|
|
299
362
|
let query = db
|
|
@@ -311,7 +374,10 @@ export async function getTermsForEntries(
|
|
|
311
374
|
])
|
|
312
375
|
.where("content_taxonomies.collection", "=", collection)
|
|
313
376
|
.where("content_taxonomies.entry_id", "in", chunk)
|
|
314
|
-
.where("taxonomies.name", "=", taxonomyName)
|
|
377
|
+
.where("taxonomies.name", "=", taxonomyName)
|
|
378
|
+
// Match the order getAllTermsForEntries (the cache primer) uses, so
|
|
379
|
+
// cache-hit and DB-miss entries in one result are ordered consistently.
|
|
380
|
+
.orderBy("taxonomies.label", "asc");
|
|
315
381
|
if (locale !== undefined) query = query.where("taxonomies.locale", "=", locale);
|
|
316
382
|
rows = await query.execute();
|
|
317
383
|
} catch (error) {
|