emdash 0.35.0 → 0.37.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 +382 -0
- package/dist/activation-K0kGBWuJ.mjs.map +1 -0
- package/dist/{adapters-Bb1vG41D.d.mts → adapters-BDQwg236.d.mts} +6 -4
- package/dist/{adapters-Bb1vG41D.d.mts.map → adapters-BDQwg236.d.mts.map} +1 -1
- package/dist/{allowed-origins-DW7sYyqg.mjs → allowed-origins-BGglilcs.mjs} +2 -2
- package/dist/{allowed-origins-DW7sYyqg.mjs.map → allowed-origins-BGglilcs.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +23 -21
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +5 -5
- package/dist/api-Dz6rgoa3.mjs +10040 -0
- package/dist/api-Dz6rgoa3.mjs.map +1 -0
- package/dist/{api-tokens-KO24G1Fd.mjs → api-tokens-BSsQACNe.mjs} +2 -2
- package/dist/{api-tokens-KO24G1Fd.mjs.map → api-tokens-BSsQACNe.mjs.map} +1 -1
- package/dist/{apply-kUDt80-G.mjs → apply-B4SIOxR1.mjs} +34 -35
- package/dist/{apply-kUDt80-G.mjs.map → apply-B4SIOxR1.mjs.map} +1 -1
- package/dist/astro/image-endpoint.mjs +2 -2
- package/dist/astro/image-endpoint.mjs.map +1 -1
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +128 -44
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +10 -7
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/media-usage-write-fence.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware/request-context.mjs +3 -3
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +11 -12
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +237 -1412
- 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.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
- 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 +13 -13
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
- 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.d.mts +9 -0
- package/dist/astro/routes/api/admin/media-usage/activation.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/activation.mjs +46 -0
- package/dist/astro/routes/api/admin/media-usage/activation.mjs.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/progress.d.mts +9 -0
- package/dist/astro/routes/api/admin/media-usage/progress.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/progress.mjs +44 -0
- package/dist/astro/routes/api/admin/media-usage/progress.mjs.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +16 -11
- package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -1
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +55 -49
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +55 -49
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +53 -47
- 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 +53 -47
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +53 -47
- 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 +53 -47
- 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 +54 -48
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +60 -50
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +179 -139
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +61 -51
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts +8 -0
- package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +93 -0
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -0
- package/dist/astro/routes/api/admin/plugins/updates.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +53 -47
- 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 +53 -47
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +7 -7
- 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.d.mts.map +1 -1
- package/dist/astro/routes/api/auth/me.mjs +19 -9
- package/dist/astro/routes/api/auth/me.mjs.map +1 -1
- 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 +14 -14
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
- 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 +14 -5
- 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_/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.mjs +13 -10
- 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 +17 -15
- 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 +17 -15
- 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.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +14 -14
- 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 +14 -5
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
- package/dist/astro/routes/api/dashboard.mjs +8 -8
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +17 -17
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +13 -13
- 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 +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +12 -10
- 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 +17 -17
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +27 -27
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/manifest.mjs.map +1 -1
- package/dist/astro/routes/api/mcp.mjs +188 -65
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +11 -11
- package/dist/astro/routes/api/media/_id_/replace.d.mts +8 -0
- package/dist/astro/routes/api/media/_id_/replace.d.mts.map +1 -0
- package/dist/astro/routes/api/media/_id_/replace.mjs +91 -0
- package/dist/astro/routes/api/media/_id_/replace.mjs.map +1 -0
- package/dist/astro/routes/api/media/_id_/upload.mjs +6 -6
- package/dist/astro/routes/api/media/_id_/usage.mjs +18 -13
- package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +21 -13
- package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -1
- package/dist/astro/routes/api/media/file/_...key_.mjs +3 -2
- package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
- package/dist/astro/routes/api/media/folders/_id_.d.mts +10 -0
- package/dist/astro/routes/api/media/folders/_id_.d.mts.map +1 -0
- package/dist/astro/routes/api/media/folders/_id_.mjs +52 -0
- package/dist/astro/routes/api/media/folders/_id_.mjs.map +1 -0
- package/dist/astro/routes/api/media/folders/index.d.mts +9 -0
- package/dist/astro/routes/api/media/folders/index.d.mts.map +1 -0
- package/dist/astro/routes/api/media/folders/index.mjs +41 -0
- package/dist/astro/routes/api/media/folders/index.mjs.map +1 -0
- 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 +16 -14
- package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
- package/dist/astro/routes/api/media.d.mts.map +1 -1
- package/dist/astro/routes/api/media.mjs +59 -27
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +20 -13
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items.mjs +15 -11
- package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/reorder.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +15 -11
- package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/translations.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/translations.mjs +16 -12
- package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_.mjs +21 -14
- package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/index.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/index.mjs +15 -11
- 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 +253 -40
- 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 +10 -10
- package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
- package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
- package/dist/astro/routes/api/redirects/index.mjs +11 -11
- package/dist/astro/routes/api/relations/_id_/index.mjs +17 -15
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/translations.mjs +12 -10
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/relations/index.mjs +17 -15
- 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 +53 -47
- 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 +53 -47
- 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 +53 -47
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/reorder.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +10 -8
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +53 -47
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +53 -47
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +10 -10
- package/dist/astro/routes/api/search/index.mjs +9 -9
- package/dist/astro/routes/api/search/rebuild.mjs +10 -10
- package/dist/astro/routes/api/search/stats.mjs +5 -5
- package/dist/astro/routes/api/search/suggest.mjs +9 -9
- package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
- package/dist/astro/routes/api/sections/index.mjs +9 -9
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +6 -6
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +5 -5
- package/dist/astro/routes/api/settings/backups/export.mjs +6 -6
- package/dist/astro/routes/api/settings/backups/index.mjs +7 -7
- package/dist/astro/routes/api/settings/email.mjs +5 -5
- package/dist/astro/routes/api/settings/email.mjs.map +1 -1
- package/dist/astro/routes/api/settings.d.mts.map +1 -1
- package/dist/astro/routes/api/settings.mjs +20 -16
- package/dist/astro/routes/api/settings.mjs.map +1 -1
- 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.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +35 -28
- 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 +32 -30
- 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 +7 -7
- package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +20 -16
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +21 -17
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +25 -18
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +20 -16
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +14 -14
- package/dist/astro/routes/api/taxonomies/_name_.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_.mjs +26 -19
- package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.mjs +20 -16
- 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.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +10 -8
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +17 -13
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +15 -12
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -6
- package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.mjs +15 -12
- 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 +7 -7
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +10 -10
- package/dist/astro/routes/sitemap.xml.mjs +8 -8
- package/dist/astro/types.d.mts +37 -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-DUElxgOQ.mjs → authorize-Bkf_UHhD.mjs} +2 -2
- package/dist/{authorize-DUElxgOQ.mjs.map → authorize-Bkf_UHhD.mjs.map} +1 -1
- package/dist/{backup-DWYpJlLj.mjs → backup-rwVAl9NI.mjs} +5 -5
- package/dist/{backup-DWYpJlLj.mjs.map → backup-rwVAl9NI.mjs.map} +1 -1
- package/dist/{bundle-vn56sLos.mjs → bundle-C8S2Zl6U.mjs} +4 -4
- package/dist/{bundle-vn56sLos.mjs.map → bundle-C8S2Zl6U.mjs.map} +1 -1
- package/dist/{byline-yjvWq4ip.mjs → byline-DuhF7NWl.mjs} +27 -14
- package/dist/byline-DuhF7NWl.mjs.map +1 -0
- package/dist/{byline-fields-txaLGoME.mjs → byline-fields-BovY4KYM.mjs} +2 -2
- package/dist/{byline-fields-txaLGoME.mjs.map → byline-fields-BovY4KYM.mjs.map} +1 -1
- package/dist/{byline-registry-DDun8sxh.mjs → byline-registry-Bpu9-MVs.mjs} +2 -2
- package/dist/{byline-registry-DDun8sxh.mjs.map → byline-registry-Bpu9-MVs.mjs.map} +1 -1
- package/dist/{bylines-DjXS5p0P.mjs → bylines-CJ5nd6Lw.mjs} +7 -7
- package/dist/{bylines-DjXS5p0P.mjs.map → bylines-CJ5nd6Lw.mjs.map} +1 -1
- package/dist/{bylines-Bu7pchQ4.mjs → bylines-RwBAeOF1.mjs} +3 -3
- package/dist/{bylines-Bu7pchQ4.mjs.map → bylines-RwBAeOF1.mjs.map} +1 -1
- package/dist/{cache-C46-3Irl.mjs → cache-BF5Kvn59.mjs} +4 -4
- package/dist/{cache-C46-3Irl.mjs.map → cache-BF5Kvn59.mjs.map} +1 -1
- package/dist/capture-triggers-CNW1q3HK.mjs +362 -0
- package/dist/capture-triggers-CNW1q3HK.mjs.map +1 -0
- package/dist/{challenge-store-SIL5DsF2.mjs → challenge-store-DQN_Yrck.mjs} +5 -3
- package/dist/challenge-store-DQN_Yrck.mjs.map +1 -0
- package/dist/chrome-tags-D8fiZG94.mjs +24 -0
- package/dist/chrome-tags-D8fiZG94.mjs.map +1 -0
- package/dist/{chunks-fiZ8G9RJ.mjs → chunks-C4szrD8o.mjs} +2 -2
- package/dist/{chunks-fiZ8G9RJ.mjs.map → chunks-C4szrD8o.mjs.map} +1 -1
- package/dist/cli/index.mjs +37 -53
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +74 -4
- package/dist/client/index.d.mts.map +1 -1
- package/dist/client/index.mjs +54 -2
- package/dist/client/index.mjs.map +1 -1
- package/dist/{collection-deletion-DQPvMD8R.mjs → collection-deletion-Bj07NHbj.mjs} +13 -356
- package/dist/collection-deletion-Bj07NHbj.mjs.map +1 -0
- package/dist/{comment-reaction-D-2J84UE.mjs → comment-reaction-BhrvSHml.mjs} +2 -2
- package/dist/{comment-reaction-D-2J84UE.mjs.map → comment-reaction-BhrvSHml.mjs.map} +1 -1
- package/dist/{comment-Dxlep6cE.mjs → comment-vxOzWbZN.mjs} +2 -2
- package/dist/{comment-Dxlep6cE.mjs.map → comment-vxOzWbZN.mjs.map} +1 -1
- package/dist/{comments-CTfZtLmM.mjs → comments-Ct4ensnf.mjs} +2 -2
- package/dist/{comments-CTfZtLmM.mjs.map → comments-Ct4ensnf.mjs.map} +1 -1
- package/dist/{components-CQy3lh9x.mjs → components-DJbo1z0S.mjs} +1 -1
- package/dist/{components-CQy3lh9x.mjs.map → components-DJbo1z0S.mjs.map} +1 -1
- package/dist/{config-COQzTe5Q.d.mts → config-B9eZ340f.d.mts} +1 -1
- package/dist/{config-COQzTe5Q.d.mts.map → config-B9eZ340f.d.mts.map} +1 -1
- package/dist/config-it29yDLA.mjs +1163 -0
- package/dist/config-it29yDLA.mjs.map +1 -0
- package/dist/{content-ZHCc1ngN.mjs → content-DjvM0T92.mjs} +86 -45
- package/dist/content-DjvM0T92.mjs.map +1 -0
- package/dist/{content-list-query-wJ2WN8Vp.d.mts → content-list-query-Btqd4uV2.d.mts} +1 -1
- package/dist/{content-list-query-wJ2WN8Vp.d.mts.map → content-list-query-Btqd4uV2.d.mts.map} +1 -1
- package/dist/{content-refresh-B_8SJzuU.mjs → content-refresh-Bwp-8yGy.mjs} +1139 -111
- package/dist/content-refresh-Bwp-8yGy.mjs.map +1 -0
- package/dist/{media-usage-BZCydfvu.mjs → content-repair-Czdp7jz5.mjs} +5 -196
- package/dist/content-repair-Czdp7jz5.mjs.map +1 -0
- package/dist/{context-ZldbZcle.mjs → context-CX6gxOLW.mjs} +55 -20
- package/dist/context-CX6gxOLW.mjs.map +1 -0
- package/dist/{cron-Y9aLtgrA.mjs → cron-C6o7VppJ.mjs} +1 -1
- package/dist/{cron-Y9aLtgrA.mjs.map → cron-C6o7VppJ.mjs.map} +1 -1
- package/dist/{dashboard-DvHbk_cv.mjs → dashboard-C2kTeeRI.mjs} +5 -5
- package/dist/{dashboard-DvHbk_cv.mjs.map → dashboard-C2kTeeRI.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +3 -3
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql-migrations.d.mts +1 -1
- package/dist/db/libsql-migrations.mjs +4 -4
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres-migrations.d.mts +1 -1
- package/dist/db/postgres-migrations.mjs +4 -4
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/postgres.d.mts.map +1 -1
- package/dist/db/postgres.mjs +19 -3
- package/dist/db/postgres.mjs.map +1 -1
- package/dist/db/sqlite-migrations.d.mts +1 -1
- package/dist/db/sqlite-migrations.mjs +4 -4
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.mjs +3 -3
- package/dist/db/sqlite.mjs.map +1 -1
- package/dist/{default-DdDl3fxv.mjs → default-Clra32mw.mjs} +1 -1
- package/dist/{default-DdDl3fxv.mjs.map → default-Clra32mw.mjs.map} +1 -1
- package/dist/{deferred-tasks-SDWxrgzJ.d.mts → deferred-tasks-CvDETTJO.d.mts} +1 -1
- package/dist/{deferred-tasks-SDWxrgzJ.d.mts.map → deferred-tasks-CvDETTJO.d.mts.map} +1 -1
- package/dist/dev-typegen-DzOPfsNj.mjs +29 -0
- package/dist/dev-typegen-DzOPfsNj.mjs.map +1 -0
- package/dist/{device-flow-C0GdD_z8.mjs → device-flow-D2oCWvLJ.mjs} +5 -5
- package/dist/{device-flow-C0GdD_z8.mjs.map → device-flow-D2oCWvLJ.mjs.map} +1 -1
- package/dist/{direct-executor-CDXeJCSs.mjs → direct-executor-Bfa5_ToE.mjs} +3 -3
- package/dist/{direct-executor-CDXeJCSs.mjs.map → direct-executor-Bfa5_ToE.mjs.map} +1 -1
- package/dist/{dist-D_6CKR8z.mjs → dist-89xCrGqi.mjs} +1 -1
- package/dist/{dist-D_6CKR8z.mjs.map → dist-89xCrGqi.mjs.map} +1 -1
- package/dist/{email-console-BHNjIwiB.mjs → email-console-Dzb6uGYa.mjs} +1 -1
- package/dist/{email-console-BHNjIwiB.mjs.map → email-console-Dzb6uGYa.mjs.map} +1 -1
- package/dist/{enrich-_ySMEvEE.mjs → enrich-BX0IDp4z.mjs} +3 -3
- package/dist/{enrich-_ySMEvEE.mjs.map → enrich-BX0IDp4z.mjs.map} +1 -1
- package/dist/{error-DupyJZE2.mjs → error-DFrQjppn.mjs} +2 -2
- package/dist/{error-DupyJZE2.mjs.map → error-DFrQjppn.mjs.map} +1 -1
- package/dist/{errors-CLUPm7aD.mjs → errors-DsVoDUjJ.mjs} +24 -2
- package/dist/errors-DsVoDUjJ.mjs.map +1 -0
- package/dist/{escape-DUY2r2Zs.mjs → escape-HmHTC1Is.mjs} +1 -1
- package/dist/{escape-DUY2r2Zs.mjs.map → escape-HmHTC1Is.mjs.map} +1 -1
- package/dist/{field-defs-cache-BzwBlJrR.mjs → field-defs-cache-BM4SIRqH.mjs} +5 -5
- package/dist/{field-defs-cache-BzwBlJrR.mjs.map → field-defs-cache-BM4SIRqH.mjs.map} +1 -1
- package/dist/focal-point-BeWFo6Xx.mjs +23 -0
- package/dist/focal-point-BeWFo6Xx.mjs.map +1 -0
- package/dist/{fts-manager-DyZEohZC.mjs → fts-manager-Do1BlW2r.mjs} +3 -3
- package/dist/{fts-manager-DyZEohZC.mjs.map → fts-manager-Do1BlW2r.mjs.map} +1 -1
- package/dist/{hash-Cg0TXJPV.mjs → hash-3JJPlHF2.mjs} +1 -1
- package/dist/{hash-Cg0TXJPV.mjs.map → hash-3JJPlHF2.mjs.map} +1 -1
- package/dist/hook-result-DCh2WUpe.d.mts +31 -0
- package/dist/hook-result-DCh2WUpe.d.mts.map +1 -0
- package/dist/{hreflang-DwT2E0Sa.d.mts → hreflang-B6hbAuuO.d.mts} +2 -2
- package/dist/{hreflang-DwT2E0Sa.d.mts.map → hreflang-B6hbAuuO.d.mts.map} +1 -1
- package/dist/{hreflang-BBe83kKa.mjs → hreflang-DQbPZliA.mjs} +7 -7
- package/dist/{hreflang-BBe83kKa.mjs.map → hreflang-DQbPZliA.mjs.map} +1 -1
- package/dist/{identity-Ys6Dlc9C.mjs → identity-DksvEazf.mjs} +3 -3
- package/dist/{identity-Ys6Dlc9C.mjs.map → identity-DksvEazf.mjs.map} +1 -1
- package/dist/{import-DPkLmeXO.mjs → import-CJpaBBBT.mjs} +6 -6
- package/dist/{import-DPkLmeXO.mjs.map → import-CJpaBBBT.mjs.map} +1 -1
- package/dist/{index-BVBZj4-7.d.mts → index-BJNMCWmu.d.mts} +267 -82
- package/dist/index-BJNMCWmu.d.mts.map +1 -0
- package/dist/{index-C9KuQyO-.d.mts → index-Du8K7bWa.d.mts} +3 -3
- package/dist/{index-C9KuQyO-.d.mts.map → index-Du8K7bWa.d.mts.map} +1 -1
- package/dist/index.d.mts +22 -21
- package/dist/index.mjs +79 -73
- package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
- package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
- package/dist/{load-YJ2xl3ra.mjs → load-CawHjTOH.mjs} +2 -2
- package/dist/{load-YJ2xl3ra.mjs.map → load-CawHjTOH.mjs.map} +1 -1
- package/dist/{loader-CdG_Ci3H.mjs → loader-Z8vl7zTq.mjs} +48 -13
- package/dist/loader-Z8vl7zTq.mjs.map +1 -0
- package/dist/{manifest-builder-B252yQUf.mjs → manifest-builder-COtzuca2.mjs} +2 -2
- package/dist/{manifest-builder-B252yQUf.mjs.map → manifest-builder-COtzuca2.mjs.map} +1 -1
- package/dist/{manifest-schema-BXbUAHod.mjs → manifest-schema-BNWwR8Vd.mjs} +5 -5
- package/dist/manifest-schema-BNWwR8Vd.mjs.map +1 -0
- package/dist/media/image-endpoint.d.mts +4 -2
- package/dist/media/image-endpoint.d.mts.map +1 -1
- package/dist/media/image-endpoint.mjs +6 -4
- package/dist/media/image-endpoint.mjs.map +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +13 -3
- package/dist/media/index.mjs.map +1 -1
- package/dist/media/local-runtime.d.mts +14 -12
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +12 -6
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-CzhAn2--.mjs → media-BBm8Wg3f.mjs} +69 -12
- package/dist/media-BBm8Wg3f.mjs.map +1 -0
- package/dist/{media-BWWWb8kg.mjs → media-BHjus86Y.mjs} +133 -9
- package/dist/media-BHjus86Y.mjs.map +1 -0
- package/dist/{media-allowlist-D6x-Us02.mjs → media-allowlist-CoTBWoDp.mjs} +3 -2
- package/dist/media-allowlist-CoTBWoDp.mjs.map +1 -0
- package/dist/media-folders-BGkATs8n.mjs +210 -0
- package/dist/media-folders-BGkATs8n.mjs.map +1 -0
- package/dist/{media-upload-CHBVn6m-.mjs → media-upload-BCidR-EY.mjs} +9 -9
- package/dist/{media-upload-CHBVn6m-.mjs.map → media-upload-BCidR-EY.mjs.map} +1 -1
- package/dist/{media-url-BvoX88FA.mjs → media-url-DHoPr0hq.mjs} +1 -1
- package/dist/{media-url-BvoX88FA.mjs.map → media-url-DHoPr0hq.mjs.map} +1 -1
- package/dist/media-usage-CdY5jhSR.mjs +1833 -0
- package/dist/media-usage-CdY5jhSR.mjs.map +1 -0
- package/dist/media-usage-activation-BgeMtK5C.mjs +78 -0
- package/dist/media-usage-activation-BgeMtK5C.mjs.map +1 -0
- package/dist/{media-usage-work-CLo_6tct.mjs → media-usage-work-CnGCdHxx.mjs} +4 -4
- package/dist/{media-usage-work-CLo_6tct.mjs.map → media-usage-work-CnGCdHxx.mjs.map} +1 -1
- package/dist/{media-usage-work-DrCbqBcl.mjs → media-usage-work-D6D9sMCA.mjs} +221 -59
- package/dist/media-usage-work-D6D9sMCA.mjs.map +1 -0
- package/dist/{media-usage-write-fence-aHEgN1zB.mjs → media-usage-write-fence-hjeD4u7s.mjs} +2 -2
- package/dist/{media-usage-write-fence-aHEgN1zB.mjs.map → media-usage-write-fence-hjeD4u7s.mjs.map} +1 -1
- package/dist/{menus-DSo5QeW0.mjs → menus-6qSwTNxG.mjs} +375 -107
- package/dist/menus-6qSwTNxG.mjs.map +1 -0
- package/dist/{menus-TCNSd2vp.mjs → menus-uvPLXMZf.mjs} +4 -4
- package/dist/{menus-TCNSd2vp.mjs.map → menus-uvPLXMZf.mjs.map} +1 -1
- package/dist/migrations/index.d.mts +1 -1
- package/dist/migrations/index.mjs +3 -3
- package/dist/{mime-DfIVjNkr.mjs → mime-CUIG6x84.mjs} +1 -1
- package/dist/mime-CUIG6x84.mjs.map +1 -0
- package/dist/{mode-BDoH_WcF.mjs → mode-DN_WneRx.mjs} +1 -1
- package/dist/{mode-BDoH_WcF.mjs.map → mode-DN_WneRx.mjs.map} +1 -1
- package/dist/node-sqlite-compat-BuJXIeMz.mjs +46 -0
- package/dist/node-sqlite-compat-BuJXIeMz.mjs.map +1 -0
- package/dist/{normalize-Bc-s_r-a.mjs → normalize-C2xNjTSG.mjs} +36 -5
- package/dist/normalize-C2xNjTSG.mjs.map +1 -0
- package/dist/{oauth-authorization-iOikmrJd.mjs → oauth-authorization-Dj6Kwc21.mjs} +5 -5
- package/dist/{oauth-authorization-iOikmrJd.mjs.map → oauth-authorization-Dj6Kwc21.mjs.map} +1 -1
- package/dist/{oauth-clients-CvI0nHMJ.mjs → oauth-clients-FD7I0J8m.mjs} +1 -1
- package/dist/{oauth-clients-CvI0nHMJ.mjs.map → oauth-clients-FD7I0J8m.mjs.map} +1 -1
- package/dist/{oauth-state-store-Ba8KZlwW.mjs → oauth-state-store-DkmDOmHs.mjs} +1 -1
- package/dist/{oauth-state-store-Ba8KZlwW.mjs.map → oauth-state-store-DkmDOmHs.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-C5SQwGYC.mjs → oauth-user-lookup-CvIwGpYd.mjs} +1 -1
- package/dist/{oauth-user-lookup-C5SQwGYC.mjs.map → oauth-user-lookup-CvIwGpYd.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{object-cache-71tPfV4A.mjs → object-cache-DVKnyOfy.mjs} +19 -3
- package/dist/object-cache-DVKnyOfy.mjs.map +1 -0
- package/dist/{options-CBXIHUoT.mjs → options-BHPeIbBd.mjs} +1 -1
- package/dist/{options-CBXIHUoT.mjs.map → options-BHPeIbBd.mjs.map} +1 -1
- package/dist/{options--hhz-jZ1.d.mts → options-CYb4y7hM.d.mts} +24 -11
- package/dist/options-CYb4y7hM.d.mts.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-DFbuc-Fh.mjs → parse-Ckpg-Qfp.mjs} +2 -2
- package/dist/{parse-DFbuc-Fh.mjs.map → parse-Ckpg-Qfp.mjs.map} +1 -1
- package/dist/{passkey-config-DFAmUyuE.mjs → passkey-config-O_lj3y0k.mjs} +1 -1
- package/dist/{passkey-config-DFAmUyuE.mjs.map → passkey-config-O_lj3y0k.mjs.map} +1 -1
- package/dist/{patterns-CiyXeDgr.mjs → patterns-D9iDO3j8.mjs} +1 -1
- package/dist/{patterns-CiyXeDgr.mjs.map → patterns-D9iDO3j8.mjs.map} +1 -1
- package/dist/{placeholder-Dp0K3RkD.d.mts → placeholder-4X9BrRrp.d.mts} +7 -1
- package/dist/placeholder-4X9BrRrp.d.mts.map +1 -0
- package/dist/plugin-types.d.mts +8 -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-TluUIxuu.mjs → policy-Dhtwntrt.mjs} +2 -2
- package/dist/{policy-TluUIxuu.mjs.map → policy-Dhtwntrt.mjs.map} +1 -1
- package/dist/{portable-text-imBW5cHK.mjs → portable-text-U85hOafW.mjs} +1 -1
- package/dist/{portable-text-imBW5cHK.mjs.map → portable-text-U85hOafW.mjs.map} +1 -1
- package/dist/{preview-PW-nOnq4.mjs → preview-B_vJLvf8.mjs} +2 -2
- package/dist/{preview-PW-nOnq4.mjs.map → preview-B_vJLvf8.mjs.map} +1 -1
- package/dist/{protocol-DjsJHTlK.d.mts → protocol-JdD2YhY9.d.mts} +2 -2
- package/dist/{protocol-DjsJHTlK.d.mts.map → protocol-JdD2YhY9.d.mts.map} +1 -1
- package/dist/{public-url-CWoFhQT8.mjs → public-url-CarhZJgf.mjs} +1 -1
- package/dist/{public-url-CWoFhQT8.mjs.map → public-url-CarhZJgf.mjs.map} +1 -1
- package/dist/{query-zTVAR1Sl.mjs → query-BZKZcG5b.mjs} +15 -15
- package/dist/{query-zTVAR1Sl.mjs.map → query-BZKZcG5b.mjs.map} +1 -1
- package/dist/{query-BWs7O5Il.mjs → query-BndaFxxp.mjs} +5 -5
- package/dist/{query-BWs7O5Il.mjs.map → query-BndaFxxp.mjs.map} +1 -1
- package/dist/{rate-limit-Bx4g9m3p.mjs → rate-limit-BZmATC6W.mjs} +2 -2
- package/dist/{rate-limit-Bx4g9m3p.mjs.map → rate-limit-BZmATC6W.mjs.map} +1 -1
- package/dist/{redirect-COsS6SWJ.mjs → redirect-CZkbozu9.mjs} +2 -2
- package/dist/{redirect-COsS6SWJ.mjs.map → redirect-CZkbozu9.mjs.map} +1 -1
- package/dist/{redirect-BgAOGHDq.mjs → redirect-CzqnLxK-.mjs} +1 -1
- package/dist/{redirect-BgAOGHDq.mjs.map → redirect-CzqnLxK-.mjs.map} +1 -1
- package/dist/{redirects-DTDHWH31.mjs → redirects-BLNOWQTn.mjs} +64 -29
- package/dist/redirects-BLNOWQTn.mjs.map +1 -0
- package/dist/{redirects-DyChwDkG.mjs → redirects-CqzNM0KM.mjs} +5 -5
- package/dist/{redirects-DyChwDkG.mjs.map → redirects-CqzNM0KM.mjs.map} +1 -1
- package/dist/{registry-Cqa2I749.mjs → registry-AZAsRsor.mjs} +30 -126
- package/dist/registry-AZAsRsor.mjs.map +1 -0
- package/dist/{relation-BnHB0T4J.mjs → relation-04DfKRTO.mjs} +2 -2
- package/dist/{relation-BnHB0T4J.mjs.map → relation-04DfKRTO.mjs.map} +1 -1
- package/dist/{relations-BBApI4e2.mjs → relations-C4htk5sb.mjs} +7 -7
- package/dist/relations-C4htk5sb.mjs.map +1 -0
- package/dist/{relations-D5sM5fW0.mjs → relations-CcFwDJdJ.mjs} +4 -4
- package/dist/{relations-D5sM5fW0.mjs.map → relations-CcFwDJdJ.mjs.map} +1 -1
- package/dist/{relations-BHo5A8Zf.d.mts → relations-DnRK4YIT.d.mts} +347 -54
- package/dist/relations-DnRK4YIT.d.mts.map +1 -0
- package/dist/{request-cache-BSUptuJR.mjs → request-cache-DH9zdCOD.mjs} +1 -1
- package/dist/{request-cache-BSUptuJR.mjs.map → request-cache-DH9zdCOD.mjs.map} +1 -1
- package/dist/request-context.d.mts +1 -1
- package/dist/{request-meta-DgZJh3zG.mjs → request-meta-DYxLlnmz.mjs} +2 -2
- package/dist/{request-meta-DgZJh3zG.mjs.map → request-meta-DYxLlnmz.mjs.map} +1 -1
- package/dist/{resolve-DOhVEvty.mjs → resolve-prZ3mP7l.mjs} +1 -1
- package/dist/{resolve-DOhVEvty.mjs.map → resolve-prZ3mP7l.mjs.map} +1 -1
- package/dist/{runner-C1T11CfC.mjs → runner-BLIDKpmA.mjs} +471 -290
- package/dist/runner-BLIDKpmA.mjs.map +1 -0
- package/dist/{runner-BYHZn3Jy.d.mts → runner-YKfIMnaF.d.mts} +3 -2
- package/dist/runner-YKfIMnaF.d.mts.map +1 -0
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-DZ5YVVZj.mjs → schema-CGnBeLiW.mjs} +3 -3
- package/dist/{schema-DZ5YVVZj.mjs.map → schema-CGnBeLiW.mjs.map} +1 -1
- package/dist/{schema-2AOsSn2v.mjs → schema-DYr-Vd-L.mjs} +36 -6
- package/dist/{schema-2AOsSn2v.mjs.map → schema-DYr-Vd-L.mjs.map} +1 -1
- package/dist/{scopes-8ClPF2bC.mjs → scopes-BhWvBavj.mjs} +3 -3
- package/dist/{scopes-8ClPF2bC.mjs.map → scopes-BhWvBavj.mjs.map} +1 -1
- package/dist/{search-u1QYdOCG.mjs → search-C-qDk4kx.mjs} +4 -4
- package/dist/{search-u1QYdOCG.mjs.map → search-C-qDk4kx.mjs.map} +1 -1
- package/dist/{secrets-BEgsAQEU.mjs → secrets-rEtHt-6z.mjs} +3 -3
- package/dist/{secrets-BEgsAQEU.mjs.map → secrets-rEtHt-6z.mjs.map} +1 -1
- package/dist/{sections-DmHviGyM.mjs → sections-B_nYvlwn.mjs} +2 -2
- package/dist/{sections-DmHviGyM.mjs.map → sections-B_nYvlwn.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +25 -23
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +17 -15
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-Dq4G-RrO.mjs → seo-926NRIgt.mjs} +3 -3
- package/dist/{seo-Dq4G-RrO.mjs.map → seo-926NRIgt.mjs.map} +1 -1
- package/dist/{seo-AFpVvTqr.mjs → seo-CVWkdv6x.mjs} +1 -1
- package/dist/{seo-AFpVvTqr.mjs.map → seo-CVWkdv6x.mjs.map} +1 -1
- package/dist/{service-BpArayPr.mjs → service-CJl3ZAYs.mjs} +3 -3
- package/dist/{service-BpArayPr.mjs.map → service-CJl3ZAYs.mjs.map} +1 -1
- package/dist/{session-user-BEorKVT-.mjs → session-user-BlMgqCHS.mjs} +1 -1
- package/dist/{session-user-BEorKVT-.mjs.map → session-user-BlMgqCHS.mjs.map} +1 -1
- package/dist/{settings-xRdoUZr0.mjs → settings--wobrkkC.mjs} +21 -8
- package/dist/settings--wobrkkC.mjs.map +1 -0
- package/dist/{settings-DE9xtzqd.mjs → settings-Cwm6o2jY.mjs} +3 -3
- package/dist/{settings-DE9xtzqd.mjs.map → settings-Cwm6o2jY.mjs.map} +1 -1
- package/dist/{setup-complete-BnT8pq_C.mjs → setup-complete-DQbClIN2.mjs} +2 -2
- package/dist/{setup-complete-BnT8pq_C.mjs.map → setup-complete-DQbClIN2.mjs.map} +1 -1
- package/dist/{setup-nonce-CBitf_oQ.mjs → setup-nonce-DZCFS2sn.mjs} +1 -1
- package/dist/{setup-nonce-CBitf_oQ.mjs.map → setup-nonce-DZCFS2sn.mjs.map} +1 -1
- package/dist/{single-flight-cache-B0eLKS6K.mjs → single-flight-cache-BPuGPdcN.mjs} +2 -2
- package/dist/{single-flight-cache-B0eLKS6K.mjs.map → single-flight-cache-BPuGPdcN.mjs.map} +1 -1
- package/dist/{site-url-DjpIXMXJ.mjs → site-url-BrxA_E9H.mjs} +3 -3
- package/dist/{site-url-DjpIXMXJ.mjs.map → site-url-BrxA_E9H.mjs.map} +1 -1
- package/dist/{snapshot-BBlGkb2q.mjs → snapshot--OCDTqdb.mjs} +1 -1
- package/dist/{snapshot-BBlGkb2q.mjs.map → snapshot--OCDTqdb.mjs.map} +1 -1
- package/dist/{ssrf-Cpvg_n3A.mjs → ssrf-5sIM5jzl.mjs} +23 -6
- package/dist/{ssrf-Cpvg_n3A.mjs.map → ssrf-5sIM5jzl.mjs.map} +1 -1
- package/dist/{state-DYwPue40.mjs → state-BpFVsJZB.mjs} +12 -4
- package/dist/state-BpFVsJZB.mjs.map +1 -0
- package/dist/{status-CWVYpbD8.mjs → status-B777-dXz.mjs} +1 -1
- package/dist/{status-CWVYpbD8.mjs.map → status-B777-dXz.mjs.map} +1 -1
- package/dist/storage/local.d.mts +2 -1
- package/dist/storage/local.d.mts.map +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/local.mjs.map +1 -1
- package/dist/storage/s3.d.mts +2 -1
- package/dist/storage/s3.d.mts.map +1 -1
- package/dist/storage/s3.mjs +3 -2
- package/dist/storage/s3.mjs.map +1 -1
- package/dist/{target-DixqqV5i.mjs → target-BmR0iTwz.mjs} +1 -1
- package/dist/{target-DixqqV5i.mjs.map → target-BmR0iTwz.mjs.map} +1 -1
- package/dist/{taxonomies-OHUIiIpI.mjs → taxonomies-DkAkMupr.mjs} +17 -14
- package/dist/taxonomies-DkAkMupr.mjs.map +1 -0
- package/dist/{taxonomies-BlrncHi9.mjs → taxonomies-Wfy_Lg12.mjs} +43 -17
- package/dist/taxonomies-Wfy_Lg12.mjs.map +1 -0
- package/dist/{taxonomy-BqCZ1TdU.mjs → taxonomy-tJepplLL.mjs} +5 -5
- package/dist/{taxonomy-BqCZ1TdU.mjs.map → taxonomy-tJepplLL.mjs.map} +1 -1
- package/dist/{tokens-C39klX8R.mjs → tokens-BCO1oruF.mjs} +1 -1
- package/dist/{tokens-C39klX8R.mjs.map → tokens-BCO1oruF.mjs.map} +1 -1
- package/dist/{transaction-NQj4VJ7Z.mjs → transaction-0KOpOyMu.mjs} +2 -1
- package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-0KOpOyMu.mjs.map} +1 -1
- package/dist/{transport-DBJwa-Si.mjs → transport-Dr3qtwXC.mjs} +4 -3
- package/dist/{transport-DBJwa-Si.mjs.map → transport-Dr3qtwXC.mjs.map} +1 -1
- package/dist/{transport-RQE36APG.d.mts → transport-xh6YKW2G.d.mts} +1 -1
- package/dist/{transport-RQE36APG.d.mts.map → transport-xh6YKW2G.d.mts.map} +1 -1
- package/dist/{trusted-proxy-CEVekpby.mjs → trusted-proxy-BR5F1Otp.mjs} +1 -1
- package/dist/{trusted-proxy-CEVekpby.mjs.map → trusted-proxy-BR5F1Otp.mjs.map} +1 -1
- package/dist/{types-BW1syrlw.mjs → types-3qNFPlci.mjs} +1 -1
- package/dist/{types-BW1syrlw.mjs.map → types-3qNFPlci.mjs.map} +1 -1
- package/dist/types-75KIqzL9.mjs.map +1 -1
- package/dist/{types-DrJrVCT1.d.mts → types-B6GrPic4.d.mts} +9 -3
- package/dist/{types-DrJrVCT1.d.mts.map → types-B6GrPic4.d.mts.map} +1 -1
- package/dist/{types-DN64_zzZ.d.mts → types-BuHW4xDE.d.mts} +11 -10
- package/dist/types-BuHW4xDE.d.mts.map +1 -0
- package/dist/{types-DWRhk9xZ.d.mts → types-C00oyCZp.d.mts} +1 -1
- package/dist/{types-DWRhk9xZ.d.mts.map → types-C00oyCZp.d.mts.map} +1 -1
- package/dist/{types-BntycPTF.d.mts → types-CDtkNSFQ.d.mts} +1 -1
- package/dist/{types-BntycPTF.d.mts.map → types-CDtkNSFQ.d.mts.map} +1 -1
- package/dist/{types-Dfk8KI5R.d.mts → types-CPeXo1wF.d.mts} +1 -1
- package/dist/{types-Dfk8KI5R.d.mts.map → types-CPeXo1wF.d.mts.map} +1 -1
- package/dist/types-CQPWhPxE.mjs.map +1 -1
- package/dist/{types-BcqdcT-r.d.mts → types-CXFkwEGC.d.mts} +3 -2
- package/dist/types-CXFkwEGC.d.mts.map +1 -0
- package/dist/{types-CtzytVXn.mjs → types-DM3vioBY.mjs} +1 -1
- package/dist/{types-CtzytVXn.mjs.map → types-DM3vioBY.mjs.map} +1 -1
- package/dist/{types-CXnOzDe1.d.mts → types-DczELMop.d.mts} +1 -1
- package/dist/{types-CXnOzDe1.d.mts.map → types-DczELMop.d.mts.map} +1 -1
- package/dist/types-DfuTR6dL.mjs +3 -0
- package/dist/{types-DQIDRKF2.d.mts → types-FV23hEoW.d.mts} +10 -1
- package/dist/types-FV23hEoW.d.mts.map +1 -0
- package/dist/{types-C2pMvO62.d.mts → types-xDMtUFnO.d.mts} +2 -1
- package/dist/types-xDMtUFnO.d.mts.map +1 -0
- package/dist/{upload-attempts-D4FS8LT6.mjs → upload-attempts-BsqoQ1fn.mjs} +1 -1
- package/dist/{upload-attempts-D4FS8LT6.mjs.map → upload-attempts-BsqoQ1fn.mjs.map} +1 -1
- package/dist/{url-pattern-BHS0V5Ng.mjs → url-pattern-D4c2czpJ.mjs} +1 -1
- package/dist/{url-pattern-BHS0V5Ng.mjs.map → url-pattern-D4c2czpJ.mjs.map} +1 -1
- package/dist/{user-DJZfUJVE.mjs → user-BTZ40aq6.mjs} +2 -2
- package/dist/{user-DJZfUJVE.mjs.map → user-BTZ40aq6.mjs.map} +1 -1
- package/dist/{utils-D4vD_Y98.mjs → utils-BKvNjjhY.mjs} +1 -1
- package/dist/{utils-D4vD_Y98.mjs.map → utils-BKvNjjhY.mjs.map} +1 -1
- package/dist/{validate-BMLBvxOC.mjs → validate-DE_LRwo9.mjs} +2 -2
- package/dist/{validate-BMLBvxOC.mjs.map → validate-DE_LRwo9.mjs.map} +1 -1
- package/dist/{validate-CTqt5nuE.d.mts → validate-DUP27OYD.d.mts} +5 -5
- package/dist/{validate-CTqt5nuE.d.mts.map → validate-DUP27OYD.d.mts.map} +1 -1
- package/dist/{validation-npvENUCB.mjs → validation-BMImFJ11.mjs} +10 -8
- package/dist/{validation-npvENUCB.mjs.map → validation-BMImFJ11.mjs.map} +1 -1
- package/dist/version-Bii3suaO.mjs +7 -0
- package/dist/{version-DB_fqCSI.mjs.map → version-Bii3suaO.mjs.map} +1 -1
- package/dist/{widgets-BD3ho7Rq.mjs → widgets-D3gJ10xU.mjs} +18 -5
- package/dist/widgets-D3gJ10xU.mjs.map +1 -0
- package/dist/{wxr-taxonomies-Ca68766D.d.mts → wxr-taxonomies-B2Og_6rU.d.mts} +1 -1
- package/dist/{wxr-taxonomies-Ca68766D.d.mts.map → wxr-taxonomies-B2Og_6rU.d.mts.map} +1 -1
- package/dist/{wxr-taxonomies-B1R3vBoD.mjs → wxr-taxonomies-Cxngsjwo.mjs} +4 -4
- package/dist/{wxr-taxonomies-B1R3vBoD.mjs.map → wxr-taxonomies-Cxngsjwo.mjs.map} +1 -1
- package/dist/{zod-generator-LC-on_Vb.mjs → zod-generator-BuYSQJB7.mjs} +32 -22
- package/dist/zod-generator-BuYSQJB7.mjs.map +1 -0
- package/package.json +20 -9
- package/src/api/errors.ts +22 -0
- package/src/api/handlers/content.ts +49 -11
- package/src/api/handlers/index.ts +18 -0
- package/src/api/handlers/manifest.ts +161 -12
- package/src/api/handlers/media-allowlist.ts +1 -0
- package/src/api/handlers/media-folders.ts +130 -0
- package/src/api/handlers/media-usage-activation.ts +102 -0
- package/src/api/handlers/media-usage.ts +108 -0
- package/src/api/handlers/media.ts +92 -2
- package/src/api/handlers/registry.ts +483 -561
- package/src/api/handlers/taxonomies.ts +16 -9
- package/src/api/openapi/document.ts +277 -7
- package/src/api/parse.ts +1 -1
- package/src/api/schemas/auth.ts +4 -4
- package/src/api/schemas/bylines.ts +3 -3
- package/src/api/schemas/comments.ts +1 -1
- package/src/api/schemas/common.ts +3 -2
- package/src/api/schemas/content.ts +78 -22
- package/src/api/schemas/media-usage.ts +77 -0
- package/src/api/schemas/media.ts +121 -0
- package/src/api/schemas/redirects.ts +6 -1
- package/src/api/schemas/schema.ts +4 -0
- package/src/api/schemas/setup.ts +1 -1
- package/src/api/schemas/taxonomies.ts +2 -2
- package/src/api/schemas/users.ts +1 -1
- package/src/api/types.ts +23 -10
- package/src/astro/dev-typegen.ts +35 -0
- package/src/astro/image-endpoint.ts +2 -2
- package/src/astro/integration/dev-typegen.ts +91 -0
- package/src/astro/integration/index.ts +9 -44
- package/src/astro/integration/routes.ts +23 -0
- package/src/astro/integration/vite-config.ts +23 -9
- package/src/astro/middleware/auth.ts +4 -0
- package/src/astro/middleware.ts +5 -7
- package/src/astro/routes/api/admin/api-tokens/index.ts +4 -1
- package/src/astro/routes/api/admin/media-usage/activation.ts +42 -0
- package/src/astro/routes/api/admin/media-usage/progress.ts +37 -0
- package/src/astro/routes/api/admin/oauth-clients/[id].ts +1 -1
- package/src/astro/routes/api/admin/oauth-clients/index.ts +1 -1
- package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/artifact.ts +247 -101
- package/src/astro/routes/api/admin/plugins/registry/install.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/verify.ts +60 -0
- package/src/astro/routes/api/auth/me.ts +14 -2
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +8 -2
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +2 -1
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +6 -2
- package/src/astro/routes/api/manifest.ts +1 -1
- package/src/astro/routes/api/media/[id]/replace.ts +114 -0
- package/src/astro/routes/api/media/[id].ts +3 -0
- package/src/astro/routes/api/media/file/[...key].ts +4 -1
- package/src/astro/routes/api/media/folders/[id].ts +55 -0
- package/src/astro/routes/api/media/folders/index.ts +37 -0
- package/src/astro/routes/api/media/upload-url.ts +8 -4
- package/src/astro/routes/api/media.ts +66 -10
- package/src/astro/routes/api/menus/[name]/items/[id].ts +7 -2
- package/src/astro/routes/api/menus/[name]/items.ts +4 -1
- package/src/astro/routes/api/menus/[name]/reorder.ts +4 -1
- package/src/astro/routes/api/menus/[name]/translations.ts +4 -1
- package/src/astro/routes/api/menus/[name].ts +7 -2
- package/src/astro/routes/api/menus/index.ts +4 -1
- package/src/astro/routes/api/settings/email.ts +1 -1
- package/src/astro/routes/api/settings.ts +4 -1
- package/src/astro/routes/api/setup/dev-bypass.ts +12 -0
- package/src/astro/routes/api/taxonomies/[name]/reorder.ts +4 -1
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +4 -1
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +7 -2
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +4 -1
- package/src/astro/routes/api/taxonomies/[name].ts +7 -2
- package/src/astro/routes/api/taxonomies/index.ts +4 -1
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +3 -1
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +5 -2
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +3 -1
- package/src/astro/routes/api/widget-areas/[name].ts +3 -1
- package/src/astro/routes/api/widget-areas/index.ts +3 -1
- package/src/astro/types.ts +34 -6
- package/src/auth/challenge-store.ts +3 -1
- package/src/aws-sdk.d.ts +1 -0
- package/src/cache/chrome-tags.ts +24 -0
- package/src/client/index.ts +140 -2
- package/src/client/transport.ts +6 -2
- package/src/components/EmDashImage.astro +156 -51
- package/src/components/EmDashMedia.astro +2 -1
- package/src/components/Gallery.astro +7 -0
- package/src/components/InlinePortableTextEditor.tsx +136 -2
- package/src/components/LiveSearch.astro +23 -0
- package/src/components/inline-code-block.tsx +180 -49
- package/src/components/widgets/Archives.astro +2 -37
- package/src/content/converters/gallery.ts +2 -0
- package/src/content/converters/index.ts +2 -0
- package/src/content/converters/mark-safety.ts +1 -0
- package/src/content/converters/portable-text-identity.ts +139 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +202 -69
- package/src/content/converters/prosemirror-to-portable-text.ts +96 -25
- package/src/content/converters/types.ts +3 -0
- package/src/database/connection.ts +2 -31
- package/src/database/index.ts +1 -1
- package/src/database/json-recordset.ts +16 -0
- package/src/database/migrations/017_authorization_codes.ts +14 -4
- package/src/database/migrations/019_i18n.ts +1 -1
- package/src/database/migrations/071_restore_content_bylines_table.ts +43 -0
- package/src/database/migrations/072_media_folders.ts +72 -0
- package/src/database/migrations/073_media_focal_point.ts +57 -0
- package/src/database/migrations/074_content_deleted_scheduled_index.ts +55 -0
- package/src/database/migrations/runner.ts +11 -3
- package/src/database/repositories/byline.ts +32 -16
- package/src/database/repositories/content.ts +119 -51
- package/src/database/repositories/media-folders.ts +115 -0
- package/src/database/repositories/media-usage-work.ts +395 -111
- package/src/database/repositories/media-usage.ts +1104 -47
- package/src/database/repositories/media.ts +156 -29
- package/src/database/repositories/plugin-storage.ts +74 -9
- package/src/database/repositories/types.ts +8 -7
- package/src/database/transaction.ts +1 -0
- package/src/database/types.ts +13 -0
- package/src/db/adapters.ts +10 -3
- package/src/db/node-sqlite-compat.ts +78 -0
- package/src/db/postgres.ts +23 -0
- package/src/db/sqlite.ts +3 -3
- package/src/emdash-runtime.ts +186 -251
- package/src/fields/boolean.ts +1 -1
- package/src/fields/datetime.ts +1 -1
- package/src/fields/file.ts +1 -1
- package/src/fields/image.ts +7 -1
- package/src/fields/integer.ts +1 -1
- package/src/fields/json.ts +1 -1
- package/src/fields/multiselect.ts +1 -1
- package/src/fields/number.ts +1 -1
- package/src/fields/portable-text.ts +1 -1
- package/src/fields/richtext.ts +1 -1
- package/src/fields/select.ts +1 -1
- package/src/fields/slug.ts +1 -1
- package/src/fields/text.ts +1 -1
- package/src/fields/textarea.ts +1 -1
- package/src/fields/types.ts +8 -4
- package/src/index.ts +30 -3
- package/src/loader.ts +63 -10
- package/src/mcp/server.ts +228 -24
- package/src/media/focal-point.ts +37 -0
- package/src/media/image-endpoint.ts +5 -2
- package/src/media/local-runtime.ts +8 -0
- package/src/media/mime.ts +1 -0
- package/src/media/normalize.ts +25 -4
- package/src/media/types.ts +10 -0
- package/src/media/usage/activation.ts +162 -35
- package/src/media/usage/capture-triggers.ts +19 -3
- package/src/media/usage/cleanup.ts +9 -8
- package/src/media/usage/collection-deletion-processor.ts +7 -3
- package/src/media/usage/collection-deletion.ts +10 -0
- package/src/media/usage/content-refresh.ts +258 -39
- package/src/media/usage/content-snapshots.ts +128 -31
- package/src/media/usage/extractor.ts +32 -13
- package/src/media/usage/maintenance-engine.ts +166 -0
- package/src/media/usage/reconciliation-processor.ts +106 -29
- package/src/media/usage/reconciliation.ts +90 -7
- package/src/media/usage/work-processor.ts +342 -101
- package/src/menus/index.ts +16 -0
- package/src/object-cache/index.ts +44 -2
- package/src/plugin-types.ts +18 -1
- package/src/plugins/hooks.ts +4 -1
- package/src/plugins/index.ts +7 -0
- package/src/plugins/manager.ts +2 -1
- package/src/plugins/manifest-schema.ts +1 -1
- package/src/plugins/routes.ts +3 -1
- package/src/plugins/sandbox/hook-result.ts +52 -0
- package/src/plugins/sandbox/index.ts +10 -0
- package/src/plugins/save-rejection.ts +20 -0
- package/src/plugins/scheduler/node.ts +1 -13
- package/src/plugins/scheduler/types.ts +0 -2
- package/src/plugins/state.ts +13 -3
- package/src/plugins/types.ts +6 -0
- package/src/registry/artifact-checksum.ts +15 -0
- package/src/registry/artifact-fetch.ts +530 -0
- package/src/registry/artifact-verification.ts +85 -0
- package/src/registry/authoritative-records.ts +180 -0
- package/src/registry/config.ts +24 -6
- package/src/registry/record-labels.ts +28 -0
- package/src/registry/types.ts +12 -15
- package/src/schema/registry.ts +26 -3
- package/src/schema/types.ts +1 -0
- package/src/schema/zod-generator.ts +28 -17
- package/src/security/ssrf.ts +26 -4
- package/src/seed/apply.ts +4 -4
- package/src/settings/index.ts +16 -0
- package/src/storage/local.ts +1 -0
- package/src/storage/s3.ts +2 -0
- package/src/storage/types.ts +2 -0
- package/src/taxonomies/index.ts +40 -20
- package/src/ui.ts +2 -0
- package/src/widgets/archives.ts +67 -0
- package/src/widgets/index.ts +16 -0
- package/dist/api-BeBglVxU.mjs +0 -4695
- package/dist/api-BeBglVxU.mjs.map +0 -1
- package/dist/byline-yjvWq4ip.mjs.map +0 -1
- package/dist/challenge-store-SIL5DsF2.mjs.map +0 -1
- package/dist/collection-deletion-DQPvMD8R.mjs.map +0 -1
- package/dist/content-ZHCc1ngN.mjs.map +0 -1
- package/dist/content-refresh-B_8SJzuU.mjs.map +0 -1
- package/dist/context-ZldbZcle.mjs.map +0 -1
- package/dist/errors-CLUPm7aD.mjs.map +0 -1
- package/dist/index-BVBZj4-7.d.mts.map +0 -1
- package/dist/loader-CdG_Ci3H.mjs.map +0 -1
- package/dist/manifest-schema-BXbUAHod.mjs.map +0 -1
- package/dist/media-BWWWb8kg.mjs.map +0 -1
- package/dist/media-CzhAn2--.mjs.map +0 -1
- package/dist/media-allowlist-D6x-Us02.mjs.map +0 -1
- package/dist/media-usage-BZCydfvu.mjs.map +0 -1
- package/dist/media-usage-work-DrCbqBcl.mjs.map +0 -1
- package/dist/menus-DSo5QeW0.mjs.map +0 -1
- package/dist/mime-DfIVjNkr.mjs.map +0 -1
- package/dist/normalize-Bc-s_r-a.mjs.map +0 -1
- package/dist/object-cache-71tPfV4A.mjs.map +0 -1
- package/dist/options--hhz-jZ1.d.mts.map +0 -1
- package/dist/placeholder-Dp0K3RkD.d.mts.map +0 -1
- package/dist/redirects-DTDHWH31.mjs.map +0 -1
- package/dist/registry-Cqa2I749.mjs.map +0 -1
- package/dist/relations-BBApI4e2.mjs.map +0 -1
- package/dist/relations-BHo5A8Zf.d.mts.map +0 -1
- package/dist/runner-BYHZn3Jy.d.mts.map +0 -1
- package/dist/runner-C1T11CfC.mjs.map +0 -1
- package/dist/settings-xRdoUZr0.mjs.map +0 -1
- package/dist/state-DYwPue40.mjs.map +0 -1
- package/dist/taxonomies-BlrncHi9.mjs.map +0 -1
- package/dist/taxonomies-OHUIiIpI.mjs.map +0 -1
- package/dist/types-BcqdcT-r.d.mts.map +0 -1
- package/dist/types-C2pMvO62.d.mts.map +0 -1
- package/dist/types-CP1CflWf.mjs +0 -3
- package/dist/types-DN64_zzZ.d.mts.map +0 -1
- package/dist/types-DQIDRKF2.d.mts.map +0 -1
- package/dist/version-DB_fqCSI.mjs +0 -7
- package/dist/widgets-BD3ho7Rq.mjs.map +0 -1
- package/dist/zod-generator-LC-on_Vb.mjs.map +0 -1
- /package/dist/{api-tokens-rIXC6LdE.mjs → api-tokens-BfYts-sq.mjs} +0 -0
- /package/dist/{ssrf-DpXgKsna.mjs → ssrf-Dg3cIT-b.mjs} +0 -0
|
@@ -1,52 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Registry
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* aggregator's `resolvePackage` XRPC.
|
|
9
|
-
* 2. Look up the requested release (or the policy-filtered latest one)
|
|
10
|
-
* via `getLatestRelease` / `listReleases`.
|
|
11
|
-
* 3. Reject the install if the aggregator surfaces a `security:yanked`
|
|
12
|
-
* hard-enforcement label or the release is below the configured
|
|
13
|
-
* minimum release age.
|
|
14
|
-
* 4. Fetch the bundle artifact, walking aggregator mirrors first and
|
|
15
|
-
* falling back to the publisher-declared URL.
|
|
16
|
-
* 5. Verify the artifact's multibase checksum against the signed
|
|
17
|
-
* release record's `artifacts.package.checksum`.
|
|
18
|
-
* 6. Extract `manifest.json` + `backend.js` + optional `admin.js` from
|
|
19
|
-
* the gzipped tar bundle.
|
|
20
|
-
* 7. Store the extracted files in site-local R2 under the
|
|
21
|
-
* `registry/<plugin-id>/<version>/` prefix.
|
|
22
|
-
* 8. Write a `plugin_states` row with `source = "registry"` and the
|
|
23
|
-
* `(publisher_did, slug)` pair so updates can be resolved later.
|
|
24
|
-
* 9. Sync the runtime so the plugin becomes active immediately.
|
|
25
|
-
*
|
|
26
|
-
* Known gaps (tracked separately):
|
|
27
|
-
*
|
|
28
|
-
* - The aggregator-supplied records are not yet cryptographically
|
|
29
|
-
* verified against the publisher's MST signature. The signed bytes
|
|
30
|
-
* and CIDs are passed through verbatim per the lexicon, but full
|
|
31
|
-
* PDS-direct verification with proof traversal is follow-up work.
|
|
32
|
-
* The artifact checksum is verified end-to-end against the value
|
|
33
|
-
* in the (aggregator-relayed) release record, which is the actual
|
|
34
|
-
* trust boundary for the bytes that end up in the sandbox.
|
|
35
|
-
* - `acceptLabelers` is forwarded as-is to the aggregator; this
|
|
36
|
-
* handler does not independently re-fetch and verify labels from
|
|
37
|
-
* each labeller's DID. Aggregator label envelope tampering is
|
|
38
|
-
* mitigated by the artifact checksum but not detected.
|
|
2
|
+
* Registry installs use aggregator results only for discovery and moderation
|
|
3
|
+
* metadata. The handler reads profile and release records from the publisher's
|
|
4
|
+
* PDS, verifies their repository proofs, then validates the bundle checksum,
|
|
5
|
+
* manifest, policy, and consent before storing or activating executable code.
|
|
6
|
+
* Applicable labels can block installation but cannot supply any verified
|
|
7
|
+
* record, checksum, permission, or executable byte.
|
|
39
8
|
*/
|
|
40
9
|
|
|
41
10
|
import { ClientResponseError, ClientValidationError } from "@atcute/client";
|
|
42
11
|
import type { Did } from "@atcute/lexicons";
|
|
12
|
+
import { canonicalizeDeclaredAccess } from "@emdash-cms/plugin-types";
|
|
13
|
+
import type { CanonicalDeclaredAccess } from "@emdash-cms/plugin-types";
|
|
43
14
|
import { checkEnvCompatibility, findSkippedEnvConstraints } from "@emdash-cms/registry-client/env";
|
|
44
15
|
import type { HostEnv } from "@emdash-cms/registry-client/env";
|
|
16
|
+
import { evaluateRegistryReleaseWithdrawal } from "@emdash-cms/registry-client/withdrawal";
|
|
17
|
+
import { NSID } from "@emdash-cms/registry-lexicons";
|
|
18
|
+
import {
|
|
19
|
+
fetchReleaseArtifact,
|
|
20
|
+
type ReleaseArtifactReference,
|
|
21
|
+
} from "@emdash-cms/registry-verification/artifact";
|
|
45
22
|
import type { Kysely } from "kysely";
|
|
46
23
|
|
|
47
24
|
import type { Database } from "../../database/types.js";
|
|
48
|
-
import { extractBundle } from "../../plugins/marketplace.js";
|
|
49
|
-
import type { PluginBundle } from "../../plugins/marketplace.js";
|
|
50
25
|
import type { SandboxRunner } from "../../plugins/sandbox/types.js";
|
|
51
26
|
import { PluginStateRepository } from "../../plugins/state.js";
|
|
52
27
|
import {
|
|
@@ -55,6 +30,20 @@ import {
|
|
|
55
30
|
} from "../../plugins/storage-indexes.js";
|
|
56
31
|
import { declaredAccessToCapabilities } from "../../plugins/types.js";
|
|
57
32
|
import type { DeclaredAccess } from "../../plugins/types.js";
|
|
33
|
+
import { fetchRegistryArtifactUrl } from "../../registry/artifact-fetch.js";
|
|
34
|
+
import {
|
|
35
|
+
validateRegistryArtifact,
|
|
36
|
+
type RegistryArtifactVerificationCode,
|
|
37
|
+
} from "../../registry/artifact-verification.js";
|
|
38
|
+
import {
|
|
39
|
+
readAuthoritativePackageRelease,
|
|
40
|
+
verifyAuthoritativePackageRelease,
|
|
41
|
+
type AuthoritativeRecordErrorCode,
|
|
42
|
+
type AuthoritativeRecordReader,
|
|
43
|
+
type AuthoritativeRecordReadOptions,
|
|
44
|
+
type VerifiedAuthoritativeReleaseReport,
|
|
45
|
+
type VerifiedAuthoritativeRecords,
|
|
46
|
+
} from "../../registry/authoritative-records.js";
|
|
58
47
|
import {
|
|
59
48
|
canonicalCapabilitiesForDriftCheck,
|
|
60
49
|
coerceRegistryConfig,
|
|
@@ -63,8 +52,9 @@ import {
|
|
|
63
52
|
validateAggregatorUrl,
|
|
64
53
|
} from "../../registry/config.js";
|
|
65
54
|
import { makeRegistryPluginId } from "../../registry/plugin-id.js";
|
|
55
|
+
import { hasCurrentRecordLabel } from "../../registry/record-labels.js";
|
|
66
56
|
import type { RegistryConfigInput } from "../../registry/types.js";
|
|
67
|
-
import {
|
|
57
|
+
import { resolveAndValidateExternalUrlTarget } from "../../security/ssrf.js";
|
|
68
58
|
import { EmDashStorageError } from "../../storage/types.js";
|
|
69
59
|
import type { Storage } from "../../storage/types.js";
|
|
70
60
|
import type { ApiResult } from "../types.js";
|
|
@@ -76,7 +66,7 @@ import {
|
|
|
76
66
|
storeBundleInR2,
|
|
77
67
|
} from "./marketplace.js";
|
|
78
68
|
|
|
79
|
-
|
|
69
|
+
export { assertSafeArtifactUrl } from "../../registry/artifact-fetch.js";
|
|
80
70
|
|
|
81
71
|
/**
|
|
82
72
|
* Whether two `declaredAccess` blocks grant exactly the same enforced access --
|
|
@@ -96,6 +86,10 @@ export function enforcedAccessEqual(a: DeclaredAccess, b: DeclaredAccess): boole
|
|
|
96
86
|
);
|
|
97
87
|
}
|
|
98
88
|
|
|
89
|
+
function verifiedAccessEqual(a: CanonicalDeclaredAccess, b: DeclaredAccess): boolean {
|
|
90
|
+
return JSON.stringify(a) === JSON.stringify(canonicalizeDeclaredAccess(b));
|
|
91
|
+
}
|
|
92
|
+
|
|
99
93
|
// ── Types ──────────────────────────────────────────────────────────
|
|
100
94
|
|
|
101
95
|
export interface RegistryInstallInput {
|
|
@@ -126,6 +120,8 @@ export interface RegistryInstallInput {
|
|
|
126
120
|
*/
|
|
127
121
|
acknowledgedDeclaredAccess?: unknown;
|
|
128
122
|
acknowledgedMcpTools?: unknown;
|
|
123
|
+
acknowledgedProfileCid?: string;
|
|
124
|
+
acknowledgedReleaseCid?: string;
|
|
129
125
|
}
|
|
130
126
|
|
|
131
127
|
export interface RegistryInstallResult {
|
|
@@ -139,84 +135,120 @@ export interface RegistryInstallResult {
|
|
|
139
135
|
version: string;
|
|
140
136
|
/** Capabilities surfaced from the bundle's manifest. */
|
|
141
137
|
capabilities: string[];
|
|
138
|
+
declaredAccess: DeclaredAccess;
|
|
139
|
+
mcpTools: RegistryMcpConsentTool[];
|
|
140
|
+
verification: RegistryRecordVerificationSummary;
|
|
142
141
|
}
|
|
143
142
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
export interface RegistryMcpConsentTool {
|
|
144
|
+
name: string;
|
|
145
|
+
description: string;
|
|
146
|
+
route: string;
|
|
147
|
+
permission: string;
|
|
148
|
+
destructive: boolean;
|
|
149
|
+
}
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
export interface RegistryRecordVerificationSummary {
|
|
152
|
+
profileCid: string;
|
|
153
|
+
releaseCid: string;
|
|
154
|
+
provenance: "verified" | "absent-optional";
|
|
155
|
+
policy: {
|
|
156
|
+
requireProvenance: boolean;
|
|
157
|
+
confirmation: "escalation-only" | "always";
|
|
158
|
+
approvers: string[];
|
|
159
|
+
};
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
|
|
158
|
-
const MULTIHASH_SHA256_CODE = 0x12;
|
|
159
|
-
/** sha2-256 digest length in bytes (single-byte varint). */
|
|
160
|
-
const MULTIHASH_SHA256_LENGTH = 0x20;
|
|
162
|
+
// ── Helpers ────────────────────────────────────────────────────────
|
|
161
163
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
164
|
+
export function registryArtifactError(
|
|
165
|
+
code: RegistryArtifactVerificationCode,
|
|
166
|
+
message: string,
|
|
167
|
+
operation: "install" | "update",
|
|
168
|
+
): ApiResult<never> {
|
|
169
|
+
let apiCode: string;
|
|
170
|
+
switch (code) {
|
|
171
|
+
case "BUNDLE_ID_MISMATCH":
|
|
172
|
+
apiCode = operation === "install" ? "MANIFEST_ID_MISMATCH" : "BUNDLE_IDENTITY_MISMATCH";
|
|
173
|
+
break;
|
|
174
|
+
case "BUNDLE_VERSION_MISMATCH":
|
|
175
|
+
apiCode = operation === "install" ? "MANIFEST_VERSION_MISMATCH" : code;
|
|
176
|
+
break;
|
|
177
|
+
case "CHECKSUM_MISMATCH":
|
|
178
|
+
case "INVALID_MULTIHASH":
|
|
179
|
+
case "UNSUPPORTED_MULTIHASH":
|
|
180
|
+
apiCode = "CHECKSUM_MISMATCH";
|
|
181
|
+
break;
|
|
182
|
+
case "FETCH_FAILED":
|
|
183
|
+
case "HOST_REJECTED":
|
|
184
|
+
case "REDIRECT_LIMIT_EXCEEDED":
|
|
185
|
+
case "REDIRECT_LOCATION_MISSING":
|
|
186
|
+
case "RESOURCE_STATUS_ERROR":
|
|
187
|
+
case "RESOURCE_TIMEOUT":
|
|
188
|
+
apiCode = operation === "install" ? "INSTALL_FAILED" : "UPDATE_FAILED";
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
apiCode = "INVALID_BUNDLE";
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
success: false,
|
|
195
|
+
error: {
|
|
196
|
+
code: apiCode,
|
|
197
|
+
message,
|
|
198
|
+
details: { verificationCode: code },
|
|
199
|
+
},
|
|
200
|
+
};
|
|
183
201
|
}
|
|
184
202
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
* Hash functions other than sha2-256 are out of scope for this
|
|
199
|
-
* initial release; the install fails closed.
|
|
200
|
-
*/
|
|
201
|
-
export async function verifyChecksum(bytes: Uint8Array, checksum: string): Promise<boolean> {
|
|
202
|
-
if (SHA256_HEX_PATTERN.test(checksum)) {
|
|
203
|
-
const actual = await sha256Hex(bytes);
|
|
204
|
-
return checksum.toLowerCase() === actual;
|
|
205
|
-
}
|
|
203
|
+
function registryRecordError(
|
|
204
|
+
code: AuthoritativeRecordErrorCode,
|
|
205
|
+
message: string,
|
|
206
|
+
): ApiResult<never> {
|
|
207
|
+
return {
|
|
208
|
+
success: false,
|
|
209
|
+
error: {
|
|
210
|
+
code: "RECORD_VERIFICATION_FAILED",
|
|
211
|
+
message,
|
|
212
|
+
details: { verificationCode: code },
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
206
216
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
function recordConsentError(
|
|
218
|
+
input: { profileCid?: string; releaseCid?: string },
|
|
219
|
+
records: VerifiedAuthoritativeRecords,
|
|
220
|
+
): ApiResult<never> | null {
|
|
221
|
+
if (!input.profileCid || !input.releaseCid) {
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: {
|
|
225
|
+
code: "RECORD_CONSENT_REQUIRED",
|
|
226
|
+
message: "Verify the signed package records before confirming this action.",
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (input.profileCid !== records.profile.cid || input.releaseCid !== records.release.cid) {
|
|
231
|
+
return {
|
|
232
|
+
success: false,
|
|
233
|
+
error: {
|
|
234
|
+
code: "RECORD_VERIFICATION_DRIFT",
|
|
235
|
+
message: "The signed package records changed after review. Verify them again.",
|
|
236
|
+
},
|
|
237
|
+
};
|
|
217
238
|
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
218
241
|
|
|
219
|
-
|
|
242
|
+
function recordVerificationSummary(
|
|
243
|
+
records: VerifiedAuthoritativeRecords,
|
|
244
|
+
report: VerifiedAuthoritativeReleaseReport,
|
|
245
|
+
): RegistryRecordVerificationSummary {
|
|
246
|
+
return {
|
|
247
|
+
profileCid: records.profile.cid,
|
|
248
|
+
releaseCid: records.release.cid,
|
|
249
|
+
provenance: report.provenance.status,
|
|
250
|
+
policy: report.value.policy,
|
|
251
|
+
};
|
|
220
252
|
}
|
|
221
253
|
|
|
222
254
|
/**
|
|
@@ -241,30 +273,21 @@ const MAX_REDIRECTS = 5;
|
|
|
241
273
|
|
|
242
274
|
/**
|
|
243
275
|
* Wall-clock cap on any single artifact fetch attempt (per URL).
|
|
244
|
-
* Defends against slow-loris
|
|
276
|
+
* Defends against slow-loris artifact sources that accept the connection but
|
|
245
277
|
* never finish sending headers or body.
|
|
246
278
|
*/
|
|
247
279
|
const ARTIFACT_FETCH_TIMEOUT_MS = 15_000;
|
|
248
280
|
|
|
249
281
|
/**
|
|
250
282
|
* Total wall-clock budget for the artifact-download phase across all
|
|
251
|
-
*
|
|
252
|
-
*
|
|
283
|
+
* advertised caches, the publisher PDS, and the declared URL. Even with the
|
|
284
|
+
* per-URL timeout, unavailable sources could otherwise tie up the install request for
|
|
253
285
|
* minutes; this caps total time at a budget interactive admins can
|
|
254
286
|
* tolerate. Tuned so a fast happy path takes <1s of budget per
|
|
255
287
|
* attempt and a worst case still completes in under a minute.
|
|
256
288
|
*/
|
|
257
289
|
const ARTIFACT_TOTAL_BUDGET_MS = 45_000;
|
|
258
290
|
|
|
259
|
-
/**
|
|
260
|
-
* Cap on the number of mirror URLs we try before falling back to the
|
|
261
|
-
* publisher-declared URL. Matches the aggregator lexicon's
|
|
262
|
-
* `mirrors` array length cap (16) but enforced here independently so
|
|
263
|
-
* a misbehaving aggregator can't slow-loris us through hundreds of
|
|
264
|
-
* URLs.
|
|
265
|
-
*/
|
|
266
|
-
const MAX_MIRRORS = 16;
|
|
267
|
-
|
|
268
291
|
/**
|
|
269
292
|
* Per-request timeout applied to every aggregator XRPC call
|
|
270
293
|
* (`resolvePackage`, `getLatestRelease`, `listReleases`). Matches the
|
|
@@ -282,6 +305,16 @@ const AGGREGATOR_REQUEST_TIMEOUT_MS = 15_000;
|
|
|
282
305
|
*/
|
|
283
306
|
const AGGREGATOR_TOTAL_BUDGET_MS = 30_000;
|
|
284
307
|
|
|
308
|
+
class RegistryArtifactFetchError extends Error {
|
|
309
|
+
constructor(
|
|
310
|
+
readonly code: RegistryArtifactVerificationCode,
|
|
311
|
+
message: string,
|
|
312
|
+
) {
|
|
313
|
+
super(message);
|
|
314
|
+
this.name = "RegistryArtifactFetchError";
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
285
318
|
/** Build a fetch function that enforces a per-request and per-budget timeout. */
|
|
286
319
|
function timedFetch(totalDeadline: number): typeof fetch {
|
|
287
320
|
return (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {
|
|
@@ -304,249 +337,45 @@ function timedFetch(totalDeadline: number): typeof fetch {
|
|
|
304
337
|
};
|
|
305
338
|
}
|
|
306
339
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
* credentials, any host that's a loopback / private / link-local
|
|
342
|
-
* literal address, and any hostname whose resolved A or AAAA records
|
|
343
|
-
* point at one of those addresses (closes the DNS-rebinding gap).
|
|
344
|
-
*
|
|
345
|
-
* Wraps `resolveAndValidateExternalUrl` from the import-pipeline SSRF
|
|
346
|
-
* module so both code paths share one DoH cache, one resolver, one
|
|
347
|
-
* blocklist, and one set of regression tests. Layers an
|
|
348
|
-
* artifact-specific protocol/dev-localhost policy on top.
|
|
349
|
-
*
|
|
350
|
-
* `import.meta.env.DEV` is a Vite/Astro compile-time constant, so
|
|
351
|
-
* production bundles cannot enable the dev escape hatch at runtime.
|
|
352
|
-
*/
|
|
353
|
-
export async function assertSafeArtifactUrl(urlString: string): Promise<URL> {
|
|
354
|
-
let url: URL;
|
|
355
|
-
try {
|
|
356
|
-
url = new URL(urlString);
|
|
357
|
-
} catch {
|
|
358
|
-
throw new Error(`Invalid artifact URL: ${urlString}`);
|
|
359
|
-
}
|
|
360
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
361
|
-
throw new Error(`Artifact URL protocol not allowed: ${url.protocol}`);
|
|
362
|
-
}
|
|
363
|
-
if (url.username || url.password) {
|
|
364
|
-
throw new Error("Artifact URL must not contain embedded credentials");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const rawHostname = url.hostname.toLowerCase().replace(TRAILING_DOT, "");
|
|
368
|
-
// Strip brackets so the IPv4/IPv6 checks see the canonical form.
|
|
369
|
-
const hostname =
|
|
370
|
-
rawHostname.startsWith("[") && rawHostname.endsWith("]")
|
|
371
|
-
? rawHostname.slice(1, -1)
|
|
372
|
-
: rawHostname;
|
|
373
|
-
const localhost = isLocalhostHostname(hostname);
|
|
374
|
-
|
|
375
|
-
// In production: reject HTTP entirely and reject localhost over any
|
|
376
|
-
// protocol -- a publisher pointing at `https://localhost` is still
|
|
377
|
-
// trying to bounce the server through its own loopback interface.
|
|
378
|
-
if (!import.meta.env.DEV) {
|
|
379
|
-
if (url.protocol === "http:") {
|
|
380
|
-
throw new Error("Artifact URL must use https");
|
|
381
|
-
}
|
|
382
|
-
if (localhost) {
|
|
383
|
-
throw new Error(`Artifact URL points to localhost: ${hostname}`);
|
|
384
|
-
}
|
|
385
|
-
} else if (url.protocol === "http:" && !localhost) {
|
|
386
|
-
// Dev mode: http allowed only for localhost.
|
|
387
|
-
throw new Error("Artifact URL must use https (http allowed only for localhost in dev)");
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (localhost) {
|
|
391
|
-
// Dev-only path; nothing to resolve.
|
|
392
|
-
return url;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Delegate IP-literal + DNS-rebinding validation to the import
|
|
396
|
-
// pipeline's SSRF helper. Adapts the SsrfError to the existing
|
|
397
|
-
// artifact-URL error vocabulary so callers keep their current
|
|
398
|
-
// catch shape.
|
|
399
|
-
try {
|
|
400
|
-
return await resolveAndValidateExternalUrl(url.href);
|
|
401
|
-
} catch (err) {
|
|
402
|
-
if (err instanceof SsrfError) {
|
|
403
|
-
throw new Error(`Artifact URL rejected: ${err.message}`, { cause: err });
|
|
404
|
-
}
|
|
405
|
-
throw err;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Fetch one URL with manual redirect handling so every hop is
|
|
411
|
-
* URL-validated, a hard byte cap so a malicious response body cannot
|
|
412
|
-
* exhaust memory before the checksum check rejects it, and a wall-clock
|
|
413
|
-
* timeout that covers connect, headers, and body together. The timeout
|
|
414
|
-
* is the minimum of the per-URL cap and the remaining total budget so
|
|
415
|
-
* a late-arriving mirror still respects the install's global budget.
|
|
416
|
-
*/
|
|
417
|
-
async function fetchWithLimits(initialUrl: string, totalDeadline: number): Promise<Uint8Array> {
|
|
418
|
-
const now = Date.now();
|
|
419
|
-
const remaining = Math.max(0, totalDeadline - now);
|
|
420
|
-
if (remaining === 0) {
|
|
421
|
-
throw new Error("Artifact download budget exhausted");
|
|
422
|
-
}
|
|
423
|
-
const perUrlTimeout = Math.min(ARTIFACT_FETCH_TIMEOUT_MS, remaining);
|
|
424
|
-
const controller = new AbortController();
|
|
425
|
-
const timer = setTimeout(() => controller.abort(), perUrlTimeout);
|
|
426
|
-
try {
|
|
427
|
-
let current = await assertSafeArtifactUrl(initialUrl);
|
|
428
|
-
let response: Response;
|
|
429
|
-
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|
|
430
|
-
response = await fetch(current.href, { redirect: "manual", signal: controller.signal });
|
|
431
|
-
if (response.status < 300 || response.status >= 400) break;
|
|
432
|
-
const location = response.headers.get("location");
|
|
433
|
-
if (!location) break;
|
|
434
|
-
if (hop === MAX_REDIRECTS) {
|
|
435
|
-
throw new Error(`Too many redirects fetching artifact (>${MAX_REDIRECTS})`);
|
|
436
|
-
}
|
|
437
|
-
const next = new URL(location, current);
|
|
438
|
-
current = await assertSafeArtifactUrl(next.href);
|
|
439
|
-
}
|
|
440
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- response is assigned in the first loop iteration
|
|
441
|
-
const finalResponse = response!;
|
|
442
|
-
if (!finalResponse.ok) {
|
|
443
|
-
throw new Error(`HTTP ${finalResponse.status}`);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Check Content-Length up front when present. Untrusted servers can
|
|
447
|
-
// lie or omit it; the streaming cap below is the real defense.
|
|
448
|
-
const lengthHeader = finalResponse.headers.get("content-length");
|
|
449
|
-
if (lengthHeader) {
|
|
450
|
-
const declared = Number(lengthHeader);
|
|
451
|
-
if (Number.isFinite(declared) && declared > MAX_ARTIFACT_BYTES) {
|
|
452
|
-
throw new Error(
|
|
453
|
-
`Artifact too large (declared ${declared} bytes, limit ${MAX_ARTIFACT_BYTES})`,
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
const body = finalResponse.body;
|
|
459
|
-
if (!body) {
|
|
460
|
-
// Workers can't return a null body for a normal GET; defensive fallback.
|
|
461
|
-
const buf = new Uint8Array(await finalResponse.arrayBuffer());
|
|
462
|
-
if (buf.byteLength > MAX_ARTIFACT_BYTES) {
|
|
463
|
-
throw new Error(`Artifact too large (limit ${MAX_ARTIFACT_BYTES} bytes)`);
|
|
464
|
-
}
|
|
465
|
-
return buf;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
const reader = body.getReader();
|
|
469
|
-
const chunks: Uint8Array[] = [];
|
|
470
|
-
let total = 0;
|
|
471
|
-
while (true) {
|
|
472
|
-
const { done, value } = await reader.read();
|
|
473
|
-
if (done) break;
|
|
474
|
-
if (!value) continue;
|
|
475
|
-
total += value.byteLength;
|
|
476
|
-
if (total > MAX_ARTIFACT_BYTES) {
|
|
477
|
-
try {
|
|
478
|
-
await reader.cancel();
|
|
479
|
-
} catch {
|
|
480
|
-
// nothing to do
|
|
481
|
-
}
|
|
482
|
-
throw new Error(`Artifact too large (limit ${MAX_ARTIFACT_BYTES} bytes)`);
|
|
483
|
-
}
|
|
484
|
-
chunks.push(value);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const out = new Uint8Array(total);
|
|
488
|
-
let offset = 0;
|
|
489
|
-
for (const chunk of chunks) {
|
|
490
|
-
out.set(chunk, offset);
|
|
491
|
-
offset += chunk.byteLength;
|
|
492
|
-
}
|
|
493
|
-
return out;
|
|
494
|
-
} finally {
|
|
495
|
-
clearTimeout(timer);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Strip query string and fragment from a URL for use in
|
|
501
|
-
* client-visible error messages. Registry artifacts are often hosted
|
|
502
|
-
* on storage backends that include presigned tokens in the query
|
|
503
|
-
* string; surfacing the raw URL on a failed install leaks those
|
|
504
|
-
* tokens into the admin's HTTP response and any log drain that
|
|
505
|
-
* captures the error chain. Origin + pathname is enough to identify
|
|
506
|
-
* the host and resource without exposing credentials.
|
|
507
|
-
*
|
|
508
|
-
* Falls back to a generic placeholder when the URL is malformed.
|
|
509
|
-
*/
|
|
510
|
-
function redactUrlForError(raw: string): string {
|
|
511
|
-
try {
|
|
512
|
-
const u = new URL(raw);
|
|
513
|
-
return `${u.origin}${u.pathname}`;
|
|
514
|
-
} catch {
|
|
515
|
-
return "<malformed url>";
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/** Walk artifact source URLs in priority order and return the first that fetches successfully. */
|
|
520
|
-
async function fetchArtifact(mirrors: string[], declaredUrl: string): Promise<Uint8Array> {
|
|
521
|
-
// Clamp mirrors regardless of what the lexicon type says -- a buggy
|
|
522
|
-
// or malicious aggregator could return more than the spec'd limit
|
|
523
|
-
// and slow-loris each one. The declared URL is always tried last.
|
|
524
|
-
const clampedMirrors = mirrors.slice(0, MAX_MIRRORS);
|
|
525
|
-
const urls = [...clampedMirrors, declaredUrl];
|
|
526
|
-
// Client-visible errors carry redacted URLs (origin + path only).
|
|
527
|
-
// The full URL with any query-string token is logged server-side
|
|
528
|
-
// so operators can still debug delivery failures.
|
|
529
|
-
const clientErrors: string[] = [];
|
|
530
|
-
|
|
531
|
-
const totalDeadline = Date.now() + ARTIFACT_TOTAL_BUDGET_MS;
|
|
532
|
-
|
|
533
|
-
for (const url of urls) {
|
|
534
|
-
if (Date.now() >= totalDeadline) {
|
|
535
|
-
clientErrors.push("(total artifact download budget exhausted)");
|
|
536
|
-
break;
|
|
537
|
-
}
|
|
538
|
-
try {
|
|
539
|
-
return await fetchWithLimits(url, totalDeadline);
|
|
540
|
-
} catch (err) {
|
|
541
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
542
|
-
console.warn(`[registry-install] Artifact fetch failed from ${url}:`, message);
|
|
543
|
-
clientErrors.push(`${redactUrlForError(url)}: ${message}`);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
throw new Error(
|
|
548
|
-
`Failed to download artifact from any source. Tried:\n ${clientErrors.join("\n ")}`,
|
|
340
|
+
async function fetchArtifact(
|
|
341
|
+
artifactCaches: readonly unknown[],
|
|
342
|
+
artifact: ReleaseArtifactReference,
|
|
343
|
+
record: {
|
|
344
|
+
did: string;
|
|
345
|
+
collection: string;
|
|
346
|
+
rkey: string;
|
|
347
|
+
cid: string;
|
|
348
|
+
},
|
|
349
|
+
auth?: unknown,
|
|
350
|
+
): Promise<Uint8Array> {
|
|
351
|
+
const result = await fetchReleaseArtifact(
|
|
352
|
+
{
|
|
353
|
+
artifact,
|
|
354
|
+
record,
|
|
355
|
+
artifactCaches,
|
|
356
|
+
...(auth === undefined ? {} : { auth }),
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
fetch: async (url, init) => {
|
|
360
|
+
if (!init.signal) throw new Error("Registry artifact fetch requires an abort signal");
|
|
361
|
+
return fetchRegistryArtifactUrl(url.href, {
|
|
362
|
+
signal: init.signal,
|
|
363
|
+
maxResponseBytes: MAX_ARTIFACT_BYTES,
|
|
364
|
+
});
|
|
365
|
+
},
|
|
366
|
+
resolveHostname: async (hostname) =>
|
|
367
|
+
(await resolveAndValidateExternalUrlTarget(`https://${hostname}`)).addresses,
|
|
368
|
+
allowHttpLocalhost: import.meta.env.DEV,
|
|
369
|
+
headerTimeoutMs: ARTIFACT_FETCH_TIMEOUT_MS,
|
|
370
|
+
totalTimeoutMs: ARTIFACT_TOTAL_BUDGET_MS,
|
|
371
|
+
maxBytes: MAX_ARTIFACT_BYTES,
|
|
372
|
+
maxRedirects: MAX_REDIRECTS,
|
|
373
|
+
},
|
|
549
374
|
);
|
|
375
|
+
if (!result.success) {
|
|
376
|
+
throw new RegistryArtifactFetchError(result.error.code, result.error.message);
|
|
377
|
+
}
|
|
378
|
+
return result.value.bytes;
|
|
550
379
|
}
|
|
551
380
|
|
|
552
381
|
/**
|
|
@@ -603,7 +432,13 @@ export async function handleRegistryInstall(
|
|
|
603
432
|
sandboxRunner: SandboxRunner | null,
|
|
604
433
|
registryConfigInput: RegistryConfigInput | undefined,
|
|
605
434
|
input: RegistryInstallInput,
|
|
606
|
-
opts?: {
|
|
435
|
+
opts?: {
|
|
436
|
+
configuredPluginIds?: Set<string>;
|
|
437
|
+
hostEnv?: HostEnv;
|
|
438
|
+
authoritativeRecords?: AuthoritativeRecordReadOptions;
|
|
439
|
+
readAuthoritativeRecords?: AuthoritativeRecordReader;
|
|
440
|
+
verifyOnly?: boolean;
|
|
441
|
+
},
|
|
607
442
|
): Promise<ApiResult<RegistryInstallResult>> {
|
|
608
443
|
// Accept either the bare-string shorthand or the full
|
|
609
444
|
// `RegistryConfig` object (see `RegistryConfigInput`).
|
|
@@ -658,7 +493,8 @@ export async function handleRegistryInstall(
|
|
|
658
493
|
|
|
659
494
|
// Lazy-load the discovery client. Avoids pulling @atcute/client into
|
|
660
495
|
// every code path that imports core/api/handlers.
|
|
661
|
-
const { DiscoveryClient } =
|
|
496
|
+
const { DiscoveryClient, registryLabelerPolicy } =
|
|
497
|
+
await import("@emdash-cms/registry-client/discovery");
|
|
662
498
|
|
|
663
499
|
// Every aggregator XRPC call passes through `timedFetch`, which
|
|
664
500
|
// enforces a per-request timeout and shares a single total-budget
|
|
@@ -668,6 +504,7 @@ export async function handleRegistryInstall(
|
|
|
668
504
|
const discovery = new DiscoveryClient({
|
|
669
505
|
aggregatorUrl: registryConfig.aggregatorUrl,
|
|
670
506
|
acceptLabelers: registryConfig.acceptLabelers,
|
|
507
|
+
labelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),
|
|
671
508
|
fetch: timedFetch(aggregatorDeadline),
|
|
672
509
|
});
|
|
673
510
|
|
|
@@ -752,19 +589,9 @@ export async function handleRegistryInstall(
|
|
|
752
589
|
};
|
|
753
590
|
}
|
|
754
591
|
|
|
755
|
-
//
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
// different `(did, slug, version)` than we asked for; the
|
|
759
|
-
// handler would then fetch + checksum-verify + install bytes
|
|
760
|
-
// under the requested package's pluginId but for a different
|
|
761
|
-
// publisher's record. Checksum verification only proves the bytes
|
|
762
|
-
// match the *returned* record, not that the record belongs to
|
|
763
|
-
// the package we requested.
|
|
764
|
-
// `releaseView.release` is validated against the release lexicon by
|
|
765
|
-
// DiscoveryClient (or `null` if it didn't conform). A `null` here makes
|
|
766
|
-
// the identity checks below fail closed, which is the desired outcome.
|
|
767
|
-
const signedRelease = releaseView.release;
|
|
592
|
+
// The aggregator selects the package/version and supplies mirrors and
|
|
593
|
+
// moderation metadata. Its copies of the signed records are not
|
|
594
|
+
// verification inputs.
|
|
768
595
|
if (packageView.did !== publisherDid || packageView.slug !== slug) {
|
|
769
596
|
return {
|
|
770
597
|
success: false,
|
|
@@ -777,9 +604,7 @@ export async function handleRegistryInstall(
|
|
|
777
604
|
if (
|
|
778
605
|
releaseView.did !== publisherDid ||
|
|
779
606
|
releaseView.package !== slug ||
|
|
780
|
-
|
|
781
|
-
(requestedVersion !== undefined && releaseView.version !== requestedVersion) ||
|
|
782
|
-
signedRelease?.version !== releaseView.version
|
|
607
|
+
(requestedVersion !== undefined && releaseView.version !== requestedVersion)
|
|
783
608
|
) {
|
|
784
609
|
return {
|
|
785
610
|
success: false,
|
|
@@ -792,34 +617,74 @@ export async function handleRegistryInstall(
|
|
|
792
617
|
}
|
|
793
618
|
|
|
794
619
|
const version = releaseView.version;
|
|
620
|
+
if (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {
|
|
621
|
+
return {
|
|
622
|
+
success: false,
|
|
623
|
+
error: {
|
|
624
|
+
code: "RELEASE_YANKED",
|
|
625
|
+
message: "This release has been withdrawn",
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
}
|
|
795
629
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
);
|
|
802
|
-
const releaseYanked = (releaseView.labels ?? []).some(
|
|
803
|
-
(l: { val?: string }) => l.val === "security:yanked",
|
|
630
|
+
const authoritative = await (opts?.readAuthoritativeRecords ?? readAuthoritativePackageRelease)(
|
|
631
|
+
publisherDid,
|
|
632
|
+
slug,
|
|
633
|
+
version,
|
|
634
|
+
opts?.authoritativeRecords,
|
|
804
635
|
);
|
|
805
|
-
if (
|
|
636
|
+
if (!authoritative.success) {
|
|
637
|
+
return registryRecordError(authoritative.error.code, authoritative.error.message);
|
|
638
|
+
}
|
|
639
|
+
const records = authoritative.value;
|
|
640
|
+
const { profile, release } = records.inspection.value;
|
|
641
|
+
if (
|
|
642
|
+
packageView.uri !== records.profile.uri ||
|
|
643
|
+
packageView.cid !== records.profile.cid ||
|
|
644
|
+
releaseView.uri !== records.release.uri ||
|
|
645
|
+
releaseView.cid !== records.release.cid
|
|
646
|
+
) {
|
|
647
|
+
return {
|
|
648
|
+
success: false,
|
|
649
|
+
error: {
|
|
650
|
+
code: "AGGREGATOR_RECORD_MISMATCH",
|
|
651
|
+
message: "Aggregator metadata does not match the publisher's signed records.",
|
|
652
|
+
},
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
if (!opts?.verifyOnly) {
|
|
656
|
+
const consentError = recordConsentError(
|
|
657
|
+
{
|
|
658
|
+
profileCid: input.acknowledgedProfileCid,
|
|
659
|
+
releaseCid: input.acknowledgedReleaseCid,
|
|
660
|
+
},
|
|
661
|
+
records,
|
|
662
|
+
);
|
|
663
|
+
if (consentError) return consentError;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const packageYanked =
|
|
667
|
+
hasCurrentRecordLabel(packageView.labels ?? [], "security:yanked", records.profile) ||
|
|
668
|
+
hasCurrentRecordLabel(packageView.labels ?? [], "security-yanked", records.profile);
|
|
669
|
+
if (packageYanked) {
|
|
806
670
|
return {
|
|
807
671
|
success: false,
|
|
808
672
|
error: {
|
|
809
673
|
code: "RELEASE_YANKED",
|
|
810
|
-
message: "This release has been withdrawn
|
|
674
|
+
message: "This release has been withdrawn",
|
|
811
675
|
},
|
|
812
676
|
};
|
|
813
677
|
}
|
|
814
678
|
|
|
815
|
-
//
|
|
816
|
-
//
|
|
679
|
+
// Environment compatibility remains an install-safety gate. Listing
|
|
680
|
+
// approval says only that displayed metadata passed moderation. A release
|
|
681
|
+
// may carry a `requires` block (`env:emdash`, `env:astro`, ...). Refuse
|
|
817
682
|
// the install if the running host doesn't satisfy a constraint, so a
|
|
818
683
|
// stale browser tab or non-UI caller can't bypass the admin's
|
|
819
684
|
// disabled Install button. `requires` is lexicon-`unknown`; the
|
|
820
685
|
// helper guards its shape.
|
|
821
686
|
if (opts?.hostEnv) {
|
|
822
|
-
const envError = assertEnvCompatible(
|
|
687
|
+
const envError = assertEnvCompatible(release.requires, opts.hostEnv);
|
|
823
688
|
if (envError) return { success: false, error: envError };
|
|
824
689
|
}
|
|
825
690
|
|
|
@@ -829,13 +694,11 @@ export async function handleRegistryInstall(
|
|
|
829
694
|
// must still hit the holdback. The `minimumReleaseAgeExclude`
|
|
830
695
|
// allowlist short-circuits the check for trusted publisher DIDs.
|
|
831
696
|
//
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
//
|
|
835
|
-
//
|
|
836
|
-
//
|
|
837
|
-
// follow-up that adds full MST verification). If the timestamp
|
|
838
|
-
// is missing or malformed, we fail closed and reject the install.
|
|
697
|
+
// `releaseView.indexedAt` is aggregator operational data, not part
|
|
698
|
+
// of the signed release. The release schema has no publication
|
|
699
|
+
// timestamp, so minimum age remains a local discovery holdback
|
|
700
|
+
// rather than a cryptographic property. A missing or malformed
|
|
701
|
+
// timestamp fails closed.
|
|
839
702
|
// `registryConfig` is the user-supplied integration option, not
|
|
840
703
|
// the normalized manifest shape, so the duration parse runs once
|
|
841
704
|
// per install. Catch a malformed value here -- normally caught at
|
|
@@ -940,82 +803,69 @@ export async function handleRegistryInstall(
|
|
|
940
803
|
};
|
|
941
804
|
}
|
|
942
805
|
|
|
943
|
-
// Step
|
|
944
|
-
//
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
// not part of the signed record).
|
|
948
|
-
const release = releaseView.release;
|
|
949
|
-
const declaredUrl = release?.artifacts?.package?.url;
|
|
950
|
-
const declaredChecksum = release?.artifacts?.package?.checksum;
|
|
806
|
+
// Step 6: fetch bytes through an aggregator cache, the publisher PDS,
|
|
807
|
+
// or the URL in the authoritative signed release. Every source remains untrusted.
|
|
808
|
+
const packageArtifact = release.artifacts.package;
|
|
809
|
+
const declaredChecksum = release.artifacts.package.checksum;
|
|
951
810
|
|
|
952
|
-
if (!
|
|
811
|
+
if (!packageArtifact || !declaredChecksum) {
|
|
953
812
|
return {
|
|
954
813
|
success: false,
|
|
955
814
|
error: {
|
|
956
815
|
code: "INVALID_RELEASE",
|
|
957
|
-
message: "Release record is missing artifact
|
|
958
|
-
},
|
|
959
|
-
};
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
const mirrors = releaseView.mirrors ?? [];
|
|
963
|
-
const artifactBytes = await fetchArtifact(mirrors, declaredUrl);
|
|
964
|
-
|
|
965
|
-
// Step 5: verify the bytes against the signed record's checksum.
|
|
966
|
-
const checksumOk = await verifyChecksum(artifactBytes, declaredChecksum);
|
|
967
|
-
if (!checksumOk) {
|
|
968
|
-
return {
|
|
969
|
-
success: false,
|
|
970
|
-
error: {
|
|
971
|
-
code: "CHECKSUM_MISMATCH",
|
|
972
|
-
message:
|
|
973
|
-
"Artifact bytes do not match the release record's checksum, or the checksum encoding is unsupported.",
|
|
816
|
+
message: "Release record is missing its package artifact or checksum",
|
|
974
817
|
},
|
|
975
818
|
};
|
|
976
819
|
}
|
|
977
820
|
|
|
978
|
-
|
|
979
|
-
let bundle: PluginBundle;
|
|
821
|
+
let artifactBytes: Uint8Array;
|
|
980
822
|
try {
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
823
|
+
artifactBytes = await fetchArtifact(
|
|
824
|
+
releaseView.artifactCaches ?? [],
|
|
825
|
+
packageArtifact,
|
|
826
|
+
{
|
|
827
|
+
did: publisherDid,
|
|
828
|
+
collection: NSID.packageRelease,
|
|
829
|
+
rkey: `${slug}:${version}`,
|
|
830
|
+
cid: releaseView.cid,
|
|
988
831
|
},
|
|
989
|
-
|
|
832
|
+
release.auth,
|
|
833
|
+
);
|
|
834
|
+
} catch (error) {
|
|
835
|
+
if (error instanceof RegistryArtifactFetchError) {
|
|
836
|
+
return registryArtifactError(error.code, error.message, "install");
|
|
837
|
+
}
|
|
838
|
+
throw error;
|
|
990
839
|
}
|
|
991
840
|
|
|
992
|
-
//
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
841
|
+
// Steps 6-7: verify the signed checksum, archive, manifest, and
|
|
842
|
+
// expected package identity with the runtime-neutral verifier used
|
|
843
|
+
// by the release service.
|
|
844
|
+
const artifactReport = await validateRegistryArtifact(
|
|
845
|
+
artifactBytes,
|
|
846
|
+
declaredChecksum,
|
|
847
|
+
slug,
|
|
848
|
+
version,
|
|
849
|
+
);
|
|
850
|
+
if (!artifactReport.success) {
|
|
851
|
+
return registryArtifactError(
|
|
852
|
+
artifactReport.error.code,
|
|
853
|
+
artifactReport.error.message,
|
|
854
|
+
"install",
|
|
855
|
+
);
|
|
1001
856
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
success: false,
|
|
1013
|
-
error: {
|
|
1014
|
-
code: "MANIFEST_ID_MISMATCH",
|
|
1015
|
-
message: `Bundle manifest id (${bundle.manifest.id}) does not match registry slug (${slug})`,
|
|
1016
|
-
},
|
|
1017
|
-
};
|
|
857
|
+
const { bundle, artifactDigest, artifactDigests } = artifactReport.value;
|
|
858
|
+
const recordReport = await verifyAuthoritativePackageRelease(records, artifactDigest, {
|
|
859
|
+
...opts?.authoritativeRecords,
|
|
860
|
+
artifactDigests,
|
|
861
|
+
});
|
|
862
|
+
if (!recordReport.success) {
|
|
863
|
+
return registryRecordError(
|
|
864
|
+
recordReport.code,
|
|
865
|
+
recordReport.reasons[0]?.message ?? "The release provenance is invalid.",
|
|
866
|
+
);
|
|
1018
867
|
}
|
|
868
|
+
const verification = recordVerificationSummary(records, recordReport);
|
|
1019
869
|
|
|
1020
870
|
// Rewrite the manifest's id to the derived opaque pluginId before
|
|
1021
871
|
// it reaches R2 storage or the sandbox loader. The sandbox uses
|
|
@@ -1033,13 +883,8 @@ export async function handleRegistryInstall(
|
|
|
1033
883
|
// outside what the user reviewed. The capability-set consent gate below
|
|
1034
884
|
// is blind to constraint content (host scope), so compare the full
|
|
1035
885
|
// enforced access of record vs bundle here and refuse on any difference.
|
|
1036
|
-
const recordExt =
|
|
1037
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension
|
|
1038
|
-
(release?.extensions as Record<string, { declaredAccess?: DeclaredAccess }> | undefined)?.[
|
|
1039
|
-
RELEASE_EXTENSION_NSID
|
|
1040
|
-
];
|
|
1041
886
|
if (
|
|
1042
|
-
!
|
|
887
|
+
!verifiedAccessEqual(recordReport.value.declaredAccess, bundle.manifest.declaredAccess ?? {})
|
|
1043
888
|
) {
|
|
1044
889
|
return {
|
|
1045
890
|
success: false,
|
|
@@ -1053,13 +898,9 @@ export async function handleRegistryInstall(
|
|
|
1053
898
|
|
|
1054
899
|
// Capability consent gate: the admin MUST acknowledge the
|
|
1055
900
|
// capabilities the bundle's manifest actually declares before we
|
|
1056
|
-
// install it. The bundle manifest is the
|
|
1057
|
-
// the
|
|
1058
|
-
//
|
|
1059
|
-
// assertion that the publisher may or may not have included,
|
|
1060
|
-
// and trusting it would let a malicious publisher (or a
|
|
1061
|
-
// compromised aggregator) ship a bundle whose manifest
|
|
1062
|
-
// requests `content:*` etc. behind an empty consent dialog.
|
|
901
|
+
// install it. The bundle manifest is the runtime enforcement
|
|
902
|
+
// currency; the exact-equality check above binds it to the
|
|
903
|
+
// independently verified signed release.
|
|
1063
904
|
//
|
|
1064
905
|
// Two outcomes after normalization (filter to strings, dedupe,
|
|
1065
906
|
// sort):
|
|
@@ -1076,7 +917,7 @@ export async function handleRegistryInstall(
|
|
|
1076
917
|
// the runtime starts enforcing `declaredAccess` natively, this
|
|
1077
918
|
// comparison switches to that shape.
|
|
1078
919
|
const actualCapabilities = canonicalCapabilitiesForDriftCheck(bundle.manifest.capabilities);
|
|
1079
|
-
if (actualCapabilities.length > 0) {
|
|
920
|
+
if (!opts?.verifyOnly && actualCapabilities.length > 0) {
|
|
1080
921
|
if (input.acknowledgedDeclaredAccess === undefined) {
|
|
1081
922
|
return {
|
|
1082
923
|
success: false,
|
|
@@ -1106,19 +947,31 @@ export async function handleRegistryInstall(
|
|
|
1106
947
|
const actualMcpTools = (bundle.manifest.mcp?.tools ?? []).map(
|
|
1107
948
|
({ inputSchema: _, outputSchema: __, ...tool }) => tool,
|
|
1108
949
|
);
|
|
1109
|
-
if (actualMcpTools.length > 0) {
|
|
950
|
+
if (!opts?.verifyOnly && actualMcpTools.length > 0) {
|
|
1110
951
|
if (JSON.stringify(input.acknowledgedMcpTools) !== JSON.stringify(actualMcpTools)) {
|
|
1111
952
|
return {
|
|
1112
953
|
success: false,
|
|
1113
954
|
error: {
|
|
1114
955
|
code: "MCP_TOOL_CONSENT_REQUIRED",
|
|
1115
956
|
message: "Plugin MCP tools require explicit consent",
|
|
1116
|
-
details: { mcpTools: actualMcpTools },
|
|
957
|
+
details: { mcpTools: actualMcpTools, verification },
|
|
1117
958
|
},
|
|
1118
959
|
};
|
|
1119
960
|
}
|
|
1120
961
|
}
|
|
1121
962
|
|
|
963
|
+
const result: RegistryInstallResult = {
|
|
964
|
+
pluginId,
|
|
965
|
+
publisherDid,
|
|
966
|
+
slug,
|
|
967
|
+
version,
|
|
968
|
+
capabilities: bundle.manifest.capabilities,
|
|
969
|
+
declaredAccess: recordReport.value.releaseExtension.declaredAccess,
|
|
970
|
+
mcpTools: actualMcpTools,
|
|
971
|
+
verification,
|
|
972
|
+
};
|
|
973
|
+
if (opts?.verifyOnly) return { success: true, data: result };
|
|
974
|
+
|
|
1122
975
|
// Step 7: store in R2 under the registry prefix.
|
|
1123
976
|
await storeBundleInR2(storage, pluginId, version, bundle, "registry");
|
|
1124
977
|
|
|
@@ -1151,13 +1004,11 @@ export async function handleRegistryInstall(
|
|
|
1151
1004
|
// Cleanup is best-effort; if it also fails, the row failure
|
|
1152
1005
|
// still surfaces to the caller and the orphan R2 bundle costs
|
|
1153
1006
|
// only the storage of a single checksum-verified zip.
|
|
1154
|
-
// `packageView.profile` is lexicon-validated by DiscoveryClient (or null).
|
|
1155
|
-
const profile = packageView.profile;
|
|
1156
1007
|
try {
|
|
1157
1008
|
await stateRepo.upsert(pluginId, version, "active", {
|
|
1158
1009
|
source: "registry",
|
|
1159
|
-
displayName: profile
|
|
1160
|
-
description: profile
|
|
1010
|
+
displayName: profile.name ?? slug,
|
|
1011
|
+
description: profile.description ?? undefined,
|
|
1161
1012
|
registryPublisherDid: publisherDid,
|
|
1162
1013
|
registrySlug: slug,
|
|
1163
1014
|
});
|
|
@@ -1189,13 +1040,7 @@ export async function handleRegistryInstall(
|
|
|
1189
1040
|
|
|
1190
1041
|
return {
|
|
1191
1042
|
success: true,
|
|
1192
|
-
data:
|
|
1193
|
-
pluginId,
|
|
1194
|
-
publisherDid,
|
|
1195
|
-
slug,
|
|
1196
|
-
version,
|
|
1197
|
-
capabilities: bundle.manifest.capabilities,
|
|
1198
|
-
},
|
|
1043
|
+
data: result,
|
|
1199
1044
|
};
|
|
1200
1045
|
} catch (err) {
|
|
1201
1046
|
if (err instanceof ClientValidationError) {
|
|
@@ -1208,6 +1053,15 @@ export async function handleRegistryInstall(
|
|
|
1208
1053
|
};
|
|
1209
1054
|
}
|
|
1210
1055
|
if (err instanceof ClientResponseError) {
|
|
1056
|
+
if (err.error === "ListingUnavailable") {
|
|
1057
|
+
return {
|
|
1058
|
+
success: false,
|
|
1059
|
+
error: {
|
|
1060
|
+
code: "LISTING_UNAVAILABLE",
|
|
1061
|
+
message: "This plugin is unavailable under the active registry policy",
|
|
1062
|
+
},
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1211
1065
|
return {
|
|
1212
1066
|
success: false,
|
|
1213
1067
|
error: {
|
|
@@ -1324,6 +1178,7 @@ export interface RegistryUpdateResult {
|
|
|
1324
1178
|
capabilityChanges: { added: string[]; removed: string[] };
|
|
1325
1179
|
/** Set only when `newlyPublic` is non-empty, mirroring marketplace. */
|
|
1326
1180
|
routeVisibilityChanges?: { newlyPublic: string[] };
|
|
1181
|
+
verification: RegistryRecordVerificationSummary;
|
|
1327
1182
|
}
|
|
1328
1183
|
|
|
1329
1184
|
/**
|
|
@@ -1348,7 +1203,11 @@ export async function handleRegistryUpdate(
|
|
|
1348
1203
|
confirmCapabilityChanges?: boolean;
|
|
1349
1204
|
confirmRouteVisibilityChanges?: boolean;
|
|
1350
1205
|
confirmMcpTools?: boolean;
|
|
1206
|
+
acknowledgedProfileCid?: string;
|
|
1207
|
+
acknowledgedReleaseCid?: string;
|
|
1351
1208
|
hostEnv?: HostEnv;
|
|
1209
|
+
authoritativeRecords?: AuthoritativeRecordReadOptions;
|
|
1210
|
+
readAuthoritativeRecords?: AuthoritativeRecordReader;
|
|
1352
1211
|
},
|
|
1353
1212
|
): Promise<ApiResult<RegistryUpdateResult>> {
|
|
1354
1213
|
const registryConfig = coerceRegistryConfig(registryConfigInput);
|
|
@@ -1408,11 +1267,13 @@ export async function handleRegistryUpdate(
|
|
|
1408
1267
|
const publisherDid = existing.registryPublisherDid as Did;
|
|
1409
1268
|
const slug = existing.registrySlug;
|
|
1410
1269
|
|
|
1411
|
-
const { DiscoveryClient } =
|
|
1270
|
+
const { DiscoveryClient, registryLabelerPolicy } =
|
|
1271
|
+
await import("@emdash-cms/registry-client/discovery");
|
|
1412
1272
|
const aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;
|
|
1413
1273
|
const discovery = new DiscoveryClient({
|
|
1414
1274
|
aggregatorUrl: registryConfig.aggregatorUrl,
|
|
1415
1275
|
acceptLabelers: registryConfig.acceptLabelers,
|
|
1276
|
+
labelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),
|
|
1416
1277
|
fetch: timedFetch(aggregatorDeadline),
|
|
1417
1278
|
});
|
|
1418
1279
|
|
|
@@ -1457,16 +1318,12 @@ export async function handleRegistryUpdate(
|
|
|
1457
1318
|
};
|
|
1458
1319
|
}
|
|
1459
1320
|
|
|
1460
|
-
//
|
|
1461
|
-
//
|
|
1462
|
-
// (did, slug, version) under this plugin's pluginId.
|
|
1463
|
-
const signedRelease = releaseView.release;
|
|
1321
|
+
// The aggregator selects the target version and supplies mirrors and
|
|
1322
|
+
// moderation metadata. Its release-record copy is not trusted.
|
|
1464
1323
|
if (
|
|
1465
1324
|
releaseView.did !== publisherDid ||
|
|
1466
1325
|
releaseView.package !== slug ||
|
|
1467
|
-
|
|
1468
|
-
(opts?.version !== undefined && releaseView.version !== opts.version) ||
|
|
1469
|
-
signedRelease?.version !== releaseView.version
|
|
1326
|
+
(opts?.version !== undefined && releaseView.version !== opts.version)
|
|
1470
1327
|
) {
|
|
1471
1328
|
return {
|
|
1472
1329
|
success: false,
|
|
@@ -1477,90 +1334,147 @@ export async function handleRegistryUpdate(
|
|
|
1477
1334
|
},
|
|
1478
1335
|
};
|
|
1479
1336
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
if (newVersion === oldVersion) {
|
|
1337
|
+
const packageView = await discovery.getPackage({ did: publisherDid, slug });
|
|
1338
|
+
if (!packageView || packageView.did !== publisherDid || packageView.slug !== slug) {
|
|
1483
1339
|
return {
|
|
1484
1340
|
success: false,
|
|
1485
1341
|
error: {
|
|
1486
|
-
code: "
|
|
1487
|
-
message: "
|
|
1342
|
+
code: "AGGREGATOR_IDENTITY_MISMATCH",
|
|
1343
|
+
message: "Aggregator returned a package view that does not match the installed plugin.",
|
|
1488
1344
|
},
|
|
1489
1345
|
};
|
|
1490
1346
|
}
|
|
1491
1347
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
(l: { val?: string }) => l.val === "security:yanked",
|
|
1495
|
-
);
|
|
1496
|
-
if (releaseYanked) {
|
|
1348
|
+
const newVersion = releaseView.version;
|
|
1349
|
+
if (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {
|
|
1497
1350
|
return {
|
|
1498
1351
|
success: false,
|
|
1499
|
-
error: { code: "YANKED", message: "Release has been
|
|
1352
|
+
error: { code: "YANKED", message: "Release has been withdrawn" },
|
|
1500
1353
|
};
|
|
1501
1354
|
}
|
|
1502
|
-
|
|
1503
|
-
// Environment compatibility gate. An ungated update could otherwise
|
|
1504
|
-
// land a version whose `requires` the host doesn't satisfy. Same
|
|
1505
|
-
// guard as install; `requires` is lexicon-`unknown`.
|
|
1506
|
-
if (opts?.hostEnv) {
|
|
1507
|
-
const envError = assertEnvCompatible(signedRelease.requires, opts.hostEnv);
|
|
1508
|
-
if (envError) return { success: false, error: envError };
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
const declaredUrl = signedRelease.artifacts?.package?.url;
|
|
1512
|
-
const declaredChecksum = signedRelease.artifacts?.package?.checksum;
|
|
1513
|
-
if (!declaredUrl || !declaredChecksum) {
|
|
1355
|
+
if (newVersion === oldVersion) {
|
|
1514
1356
|
return {
|
|
1515
1357
|
success: false,
|
|
1516
1358
|
error: {
|
|
1517
|
-
code: "
|
|
1518
|
-
message: "
|
|
1359
|
+
code: "ALREADY_UP_TO_DATE",
|
|
1360
|
+
message: "Plugin is already at the requested version",
|
|
1519
1361
|
},
|
|
1520
1362
|
};
|
|
1521
1363
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1364
|
+
const authoritative = await (opts?.readAuthoritativeRecords ?? readAuthoritativePackageRelease)(
|
|
1365
|
+
publisherDid,
|
|
1366
|
+
slug,
|
|
1367
|
+
newVersion,
|
|
1368
|
+
opts?.authoritativeRecords,
|
|
1369
|
+
);
|
|
1370
|
+
if (!authoritative.success) {
|
|
1371
|
+
return registryRecordError(authoritative.error.code, authoritative.error.message);
|
|
1529
1372
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1373
|
+
const records = authoritative.value;
|
|
1374
|
+
const { profile, release } = records.inspection.value;
|
|
1375
|
+
if (
|
|
1376
|
+
packageView.uri !== records.profile.uri ||
|
|
1377
|
+
packageView.cid !== records.profile.cid ||
|
|
1378
|
+
releaseView.uri !== records.release.uri ||
|
|
1379
|
+
releaseView.cid !== records.release.cid
|
|
1380
|
+
) {
|
|
1534
1381
|
return {
|
|
1535
1382
|
success: false,
|
|
1536
1383
|
error: {
|
|
1537
|
-
code: "
|
|
1538
|
-
message: "
|
|
1384
|
+
code: "AGGREGATOR_RECORD_MISMATCH",
|
|
1385
|
+
message: "Aggregator metadata does not match the publisher's signed records.",
|
|
1539
1386
|
},
|
|
1540
1387
|
};
|
|
1541
1388
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
if (
|
|
1389
|
+
const packageYanked =
|
|
1390
|
+
hasCurrentRecordLabel(packageView.labels ?? [], "security:yanked", records.profile) ||
|
|
1391
|
+
hasCurrentRecordLabel(packageView.labels ?? [], "security-yanked", records.profile);
|
|
1392
|
+
if (packageYanked) {
|
|
1546
1393
|
return {
|
|
1547
1394
|
success: false,
|
|
1548
|
-
error: {
|
|
1549
|
-
code: "BUNDLE_VERSION_MISMATCH",
|
|
1550
|
-
message: `Bundle manifest version (${bundle.manifest.version}) does not match release version (${newVersion})`,
|
|
1551
|
-
},
|
|
1395
|
+
error: { code: "YANKED", message: "Package has been withdrawn" },
|
|
1552
1396
|
};
|
|
1553
1397
|
}
|
|
1554
|
-
if (
|
|
1398
|
+
if (
|
|
1399
|
+
opts?.confirmCapabilityChanges ||
|
|
1400
|
+
opts?.confirmRouteVisibilityChanges ||
|
|
1401
|
+
opts?.confirmMcpTools
|
|
1402
|
+
) {
|
|
1403
|
+
const consentError = recordConsentError(
|
|
1404
|
+
{
|
|
1405
|
+
profileCid: opts.acknowledgedProfileCid,
|
|
1406
|
+
releaseCid: opts.acknowledgedReleaseCid,
|
|
1407
|
+
},
|
|
1408
|
+
records,
|
|
1409
|
+
);
|
|
1410
|
+
if (consentError) return consentError;
|
|
1411
|
+
}
|
|
1412
|
+
// Environment compatibility remains independent from listing approval.
|
|
1413
|
+
// An ungated update could otherwise
|
|
1414
|
+
// land a version whose `requires` the host doesn't satisfy. Same
|
|
1415
|
+
// guard as install; `requires` is lexicon-`unknown`.
|
|
1416
|
+
if (opts?.hostEnv) {
|
|
1417
|
+
const envError = assertEnvCompatible(release.requires, opts.hostEnv);
|
|
1418
|
+
if (envError) return { success: false, error: envError };
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
const packageArtifact = release.artifacts.package;
|
|
1422
|
+
const declaredChecksum = packageArtifact?.checksum;
|
|
1423
|
+
if (!packageArtifact || !declaredChecksum) {
|
|
1555
1424
|
return {
|
|
1556
1425
|
success: false,
|
|
1557
1426
|
error: {
|
|
1558
|
-
code: "
|
|
1559
|
-
message:
|
|
1427
|
+
code: "INVALID_RELEASE",
|
|
1428
|
+
message: "Release record is missing its package artifact or checksum",
|
|
1560
1429
|
},
|
|
1561
1430
|
};
|
|
1562
1431
|
}
|
|
1563
1432
|
|
|
1433
|
+
let artifactBytes: Uint8Array;
|
|
1434
|
+
try {
|
|
1435
|
+
artifactBytes = await fetchArtifact(
|
|
1436
|
+
releaseView.artifactCaches ?? [],
|
|
1437
|
+
packageArtifact,
|
|
1438
|
+
{
|
|
1439
|
+
did: publisherDid,
|
|
1440
|
+
collection: NSID.packageRelease,
|
|
1441
|
+
rkey: `${slug}:${newVersion}`,
|
|
1442
|
+
cid: releaseView.cid,
|
|
1443
|
+
},
|
|
1444
|
+
release.auth,
|
|
1445
|
+
);
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
if (error instanceof RegistryArtifactFetchError) {
|
|
1448
|
+
return registryArtifactError(error.code, error.message, "update");
|
|
1449
|
+
}
|
|
1450
|
+
throw error;
|
|
1451
|
+
}
|
|
1452
|
+
const artifactReport = await validateRegistryArtifact(
|
|
1453
|
+
artifactBytes,
|
|
1454
|
+
declaredChecksum,
|
|
1455
|
+
slug,
|
|
1456
|
+
newVersion,
|
|
1457
|
+
);
|
|
1458
|
+
if (!artifactReport.success) {
|
|
1459
|
+
return registryArtifactError(
|
|
1460
|
+
artifactReport.error.code,
|
|
1461
|
+
artifactReport.error.message,
|
|
1462
|
+
"update",
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1465
|
+
const { bundle, artifactDigest, artifactDigests } = artifactReport.value;
|
|
1466
|
+
const recordReport = await verifyAuthoritativePackageRelease(records, artifactDigest, {
|
|
1467
|
+
...opts?.authoritativeRecords,
|
|
1468
|
+
artifactDigests,
|
|
1469
|
+
});
|
|
1470
|
+
if (!recordReport.success) {
|
|
1471
|
+
return registryRecordError(
|
|
1472
|
+
recordReport.code,
|
|
1473
|
+
recordReport.reasons[0]?.message ?? "The release provenance is invalid.",
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
const verification = recordVerificationSummary(records, recordReport);
|
|
1477
|
+
|
|
1564
1478
|
// Rewrite manifest.id to the opaque pluginId so the sandbox loader
|
|
1565
1479
|
// and R2 layout stay in sync across install and update.
|
|
1566
1480
|
bundle.manifest = { ...bundle.manifest, id: pluginId };
|
|
@@ -1570,13 +1484,8 @@ export async function handleRegistryUpdate(
|
|
|
1570
1484
|
// that changes only the host scope (e.g. api.good.com -> evil.com) keeps
|
|
1571
1485
|
// the capability set identical, sails through the escalation diff below,
|
|
1572
1486
|
// and installs a bundle enforcing a scope the record never showed.
|
|
1573
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension
|
|
1574
|
-
const updateRecordExtensions = signedRelease?.extensions as
|
|
1575
|
-
| Record<string, { declaredAccess?: DeclaredAccess }>
|
|
1576
|
-
| undefined;
|
|
1577
|
-
const recordExt = updateRecordExtensions?.[RELEASE_EXTENSION_NSID];
|
|
1578
1487
|
if (
|
|
1579
|
-
!
|
|
1488
|
+
!verifiedAccessEqual(recordReport.value.declaredAccess, bundle.manifest.declaredAccess ?? {})
|
|
1580
1489
|
) {
|
|
1581
1490
|
return {
|
|
1582
1491
|
success: false,
|
|
@@ -1602,7 +1511,7 @@ export async function handleRegistryUpdate(
|
|
|
1602
1511
|
error: {
|
|
1603
1512
|
code: "CAPABILITY_ESCALATION",
|
|
1604
1513
|
message: "Plugin update requires new capabilities",
|
|
1605
|
-
details: { capabilityChanges },
|
|
1514
|
+
details: { capabilityChanges, verification },
|
|
1606
1515
|
},
|
|
1607
1516
|
};
|
|
1608
1517
|
}
|
|
@@ -1615,7 +1524,7 @@ export async function handleRegistryUpdate(
|
|
|
1615
1524
|
error: {
|
|
1616
1525
|
code: "ROUTE_VISIBILITY_ESCALATION",
|
|
1617
1526
|
message: "Plugin update exposes new public (unauthenticated) routes",
|
|
1618
|
-
details: { routeVisibilityChanges, capabilityChanges },
|
|
1527
|
+
details: { routeVisibilityChanges, capabilityChanges, verification },
|
|
1619
1528
|
},
|
|
1620
1529
|
};
|
|
1621
1530
|
}
|
|
@@ -1634,6 +1543,7 @@ export async function handleRegistryUpdate(
|
|
|
1634
1543
|
message: "Plugin update changes its MCP tools",
|
|
1635
1544
|
details: {
|
|
1636
1545
|
mcpTools: newMcpTools.map(({ inputSchema: _, outputSchema: __, ...tool }) => tool),
|
|
1546
|
+
verification,
|
|
1637
1547
|
},
|
|
1638
1548
|
},
|
|
1639
1549
|
};
|
|
@@ -1643,17 +1553,14 @@ export async function handleRegistryUpdate(
|
|
|
1643
1553
|
// so a retry of the same update is idempotent.
|
|
1644
1554
|
await storeBundleInR2(storage, pluginId, newVersion, bundle, "registry");
|
|
1645
1555
|
|
|
1646
|
-
//
|
|
1647
|
-
//
|
|
1648
|
-
// re-fetch the profile here — that's a separate `getPackage` round
|
|
1649
|
-
// trip and the install-time values are still authoritative for
|
|
1650
|
-
// the same package identity).
|
|
1556
|
+
// Refresh display metadata from the same signed profile used for
|
|
1557
|
+
// release-policy verification.
|
|
1651
1558
|
await stateRepo.upsert(pluginId, newVersion, "active", {
|
|
1652
1559
|
source: "registry",
|
|
1653
1560
|
registryPublisherDid: publisherDid,
|
|
1654
1561
|
registrySlug: slug,
|
|
1655
|
-
displayName:
|
|
1656
|
-
description:
|
|
1562
|
+
displayName: profile.name ?? slug,
|
|
1563
|
+
description: profile.description ?? undefined,
|
|
1657
1564
|
mcpToolsEnabled: false,
|
|
1658
1565
|
mcpToolsConsent: null,
|
|
1659
1566
|
});
|
|
@@ -1673,6 +1580,7 @@ export async function handleRegistryUpdate(
|
|
|
1673
1580
|
newVersion,
|
|
1674
1581
|
capabilityChanges,
|
|
1675
1582
|
routeVisibilityChanges: hasNewPublicRoutes ? routeVisibilityChanges : undefined,
|
|
1583
|
+
verification,
|
|
1676
1584
|
},
|
|
1677
1585
|
};
|
|
1678
1586
|
} catch (err) {
|
|
@@ -1686,6 +1594,15 @@ export async function handleRegistryUpdate(
|
|
|
1686
1594
|
};
|
|
1687
1595
|
}
|
|
1688
1596
|
if (err instanceof ClientResponseError) {
|
|
1597
|
+
if (err.error === "ListingUnavailable") {
|
|
1598
|
+
return {
|
|
1599
|
+
success: false,
|
|
1600
|
+
error: {
|
|
1601
|
+
code: "LISTING_UNAVAILABLE",
|
|
1602
|
+
message: "This plugin is unavailable under the active registry policy",
|
|
1603
|
+
},
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1689
1606
|
return {
|
|
1690
1607
|
success: false,
|
|
1691
1608
|
error: {
|
|
@@ -1759,11 +1676,13 @@ export async function handleRegistryUpdateCheck(
|
|
|
1759
1676
|
return { success: true, data: { items: [] } };
|
|
1760
1677
|
}
|
|
1761
1678
|
|
|
1762
|
-
const { DiscoveryClient } =
|
|
1679
|
+
const { DiscoveryClient, registryLabelerPolicy } =
|
|
1680
|
+
await import("@emdash-cms/registry-client/discovery");
|
|
1763
1681
|
const aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;
|
|
1764
1682
|
const discovery = new DiscoveryClient({
|
|
1765
1683
|
aggregatorUrl: registryConfig.aggregatorUrl,
|
|
1766
1684
|
acceptLabelers: registryConfig.acceptLabelers,
|
|
1685
|
+
labelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),
|
|
1767
1686
|
fetch: timedFetch(aggregatorDeadline),
|
|
1768
1687
|
});
|
|
1769
1688
|
|
|
@@ -1776,6 +1695,9 @@ export async function handleRegistryUpdateCheck(
|
|
|
1776
1695
|
did: plugin.registryPublisherDid as Did,
|
|
1777
1696
|
package: plugin.registrySlug,
|
|
1778
1697
|
});
|
|
1698
|
+
if (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {
|
|
1699
|
+
continue;
|
|
1700
|
+
}
|
|
1779
1701
|
const latest = releaseView.version;
|
|
1780
1702
|
if (!latest) continue;
|
|
1781
1703
|
const installed = plugin.version;
|