emdash 0.35.0 → 0.36.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-CzL3H752.d.mts} +3 -3
- package/dist/{adapters-Bb1vG41D.d.mts.map → adapters-CzL3H752.d.mts.map} +1 -1
- package/dist/{allowed-origins-DW7sYyqg.mjs → allowed-origins-maylGaHv.mjs} +2 -2
- package/dist/{allowed-origins-DW7sYyqg.mjs.map → allowed-origins-maylGaHv.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +22 -20
- 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-B0w3uBiG.mjs +2269 -0
- package/dist/api-B0w3uBiG.mjs.map +1 -0
- package/dist/{api-tokens-KO24G1Fd.mjs → api-tokens-9YrrCoE6.mjs} +2 -2
- package/dist/{api-tokens-KO24G1Fd.mjs.map → api-tokens-9YrrCoE6.mjs.map} +1 -1
- package/dist/{apply-kUDt80-G.mjs → apply-B_VMpJ2P.mjs} +34 -35
- package/dist/{apply-kUDt80-G.mjs.map → apply-B_VMpJ2P.mjs.map} +1 -1
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.mjs +26 -15
- 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 +168 -1305
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +15 -15
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +15 -15
- package/dist/astro/routes/api/admin/bylines/index.mjs +15 -15
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/comments/bulk.mjs +9 -9
- package/dist/astro/routes/api/admin/comments/counts.mjs +4 -4
- package/dist/astro/routes/api/admin/comments/index.mjs +9 -9
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/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 +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +52 -46
- 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 +52 -46
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +52 -46
- 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 +52 -46
- 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 +53 -47
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +54 -48
- 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 +172 -133
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +52 -46
- 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 +52 -46
- 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 +13 -13
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +12 -12
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +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 +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +16 -14
- 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 +16 -14
- 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 +13 -13
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- 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 +7 -7
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +16 -16
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +12 -12
- 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 +16 -16
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +26 -26
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/mcp.mjs +42 -42
- package/dist/astro/routes/api/media/_id_/confirm.mjs +11 -11
- 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_.mjs +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 +9 -9
- package/dist/astro/routes/api/media.d.mts.map +1 -1
- package/dist/astro/routes/api/media.mjs +28 -19
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/items.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/translations.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_.mjs +8 -8
- package/dist/astro/routes/api/menus/index.mjs +8 -8
- package/dist/astro/routes/api/oauth/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 +153 -6
- 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 +16 -14
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/translations.mjs +11 -9
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/relations/index.mjs +16 -14
- 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 +52 -46
- 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 +52 -46
- 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 +52 -46
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +52 -46
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +52 -46
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/reorder.mjs +52 -46
- 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 +52 -46
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +52 -46
- 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 +4 -4
- package/dist/astro/routes/api/settings.mjs +13 -13
- 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 +34 -27
- 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 +31 -29
- 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.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/index.mjs +13 -13
- package/dist/astro/routes/api/themes/preview.mjs +5 -5
- package/dist/astro/routes/api/typegen.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +10 -10
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +10 -10
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +10 -10
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +6 -6
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +9 -9
- package/dist/astro/routes/sitemap.xml.mjs +7 -7
- package/dist/astro/types.d.mts +19 -14
- 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-ByYs-Uxs.mjs} +2 -2
- package/dist/{authorize-DUElxgOQ.mjs.map → authorize-ByYs-Uxs.mjs.map} +1 -1
- package/dist/{backup-DWYpJlLj.mjs → backup-ChyHfIoX.mjs} +5 -5
- package/dist/{backup-DWYpJlLj.mjs.map → backup-ChyHfIoX.mjs.map} +1 -1
- package/dist/{bundle-vn56sLos.mjs → bundle-DZFhFurR.mjs} +4 -4
- package/dist/{bundle-vn56sLos.mjs.map → bundle-DZFhFurR.mjs.map} +1 -1
- package/dist/{byline-yjvWq4ip.mjs → byline-Cpnwrje1.mjs} +6 -6
- package/dist/{byline-yjvWq4ip.mjs.map → byline-Cpnwrje1.mjs.map} +1 -1
- package/dist/{byline-fields-txaLGoME.mjs → byline-fields-CAyA_Umd.mjs} +2 -2
- package/dist/{byline-fields-txaLGoME.mjs.map → byline-fields-CAyA_Umd.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-BSo9B9RX.mjs} +7 -7
- package/dist/{bylines-DjXS5p0P.mjs.map → bylines-BSo9B9RX.mjs.map} +1 -1
- package/dist/{bylines-Bu7pchQ4.mjs → bylines-CHjvehVX.mjs} +3 -3
- package/dist/{bylines-Bu7pchQ4.mjs.map → bylines-CHjvehVX.mjs.map} +1 -1
- package/dist/{cache-C46-3Irl.mjs → cache-5VirJ2Cn.mjs} +4 -4
- package/dist/{cache-C46-3Irl.mjs.map → cache-5VirJ2Cn.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-D5pSY2S1.mjs} +1 -1
- package/dist/{challenge-store-SIL5DsF2.mjs.map → challenge-store-D5pSY2S1.mjs.map} +1 -1
- package/dist/{chunks-fiZ8G9RJ.mjs → chunks-bNiX6_DR.mjs} +2 -2
- package/dist/{chunks-fiZ8G9RJ.mjs.map → chunks-bNiX6_DR.mjs.map} +1 -1
- package/dist/cli/index.mjs +36 -52
- 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-m_oi2c0N.mjs} +13 -356
- package/dist/collection-deletion-m_oi2c0N.mjs.map +1 -0
- package/dist/{comment-Dxlep6cE.mjs → comment-DhvLEdhQ.mjs} +1 -1
- package/dist/{comment-Dxlep6cE.mjs.map → comment-DhvLEdhQ.mjs.map} +1 -1
- package/dist/{comment-reaction-D-2J84UE.mjs → comment-reaction-CRuItEjt.mjs} +2 -2
- package/dist/{comment-reaction-D-2J84UE.mjs.map → comment-reaction-CRuItEjt.mjs.map} +1 -1
- package/dist/{comments-CTfZtLmM.mjs → comments-CLj4bi8I.mjs} +2 -2
- package/dist/{comments-CTfZtLmM.mjs.map → comments-CLj4bi8I.mjs.map} +1 -1
- package/dist/{components-CQy3lh9x.mjs → components-v4U4DT2y.mjs} +1 -1
- package/dist/{components-CQy3lh9x.mjs.map → components-v4U4DT2y.mjs.map} +1 -1
- package/dist/{config-COQzTe5Q.d.mts → config-DrfghTPe.d.mts} +1 -1
- package/dist/{config-COQzTe5Q.d.mts.map → config-DrfghTPe.d.mts.map} +1 -1
- package/dist/{content-ZHCc1ngN.mjs → content-CP-SeXNa.mjs} +5 -4
- package/dist/content-CP-SeXNa.mjs.map +1 -0
- package/dist/{content-list-query-wJ2WN8Vp.d.mts → content-list-query-BhejKVqb.d.mts} +1 -1
- package/dist/{content-list-query-wJ2WN8Vp.d.mts.map → content-list-query-BhejKVqb.d.mts.map} +1 -1
- package/dist/{content-refresh-B_8SJzuU.mjs → content-refresh-DLOfIhYc.mjs} +1094 -93
- package/dist/content-refresh-DLOfIhYc.mjs.map +1 -0
- package/dist/{media-usage-BZCydfvu.mjs → content-repair-BCxzy0r4.mjs} +5 -196
- package/dist/content-repair-BCxzy0r4.mjs.map +1 -0
- package/dist/{context-ZldbZcle.mjs → context-BR5hRWiK.mjs} +13 -13
- package/dist/{context-ZldbZcle.mjs.map → context-BR5hRWiK.mjs.map} +1 -1
- package/dist/{cron-Y9aLtgrA.mjs → cron-CWfWrWKG.mjs} +1 -1
- package/dist/{cron-Y9aLtgrA.mjs.map → cron-CWfWrWKG.mjs.map} +1 -1
- package/dist/{dashboard-DvHbk_cv.mjs → dashboard-CkIV2rnQ.mjs} +5 -5
- package/dist/{dashboard-DvHbk_cv.mjs.map → dashboard-CkIV2rnQ.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/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-YMEfcQUx.mjs} +1 -1
- package/dist/{default-DdDl3fxv.mjs.map → default-YMEfcQUx.mjs.map} +1 -1
- package/dist/{deferred-tasks-SDWxrgzJ.d.mts → deferred-tasks-DfqxH5Xf.d.mts} +1 -1
- package/dist/{deferred-tasks-SDWxrgzJ.d.mts.map → deferred-tasks-DfqxH5Xf.d.mts.map} +1 -1
- package/dist/{device-flow-C0GdD_z8.mjs → device-flow-CojjSBwb.mjs} +5 -5
- package/dist/{device-flow-C0GdD_z8.mjs.map → device-flow-CojjSBwb.mjs.map} +1 -1
- package/dist/{direct-executor-CDXeJCSs.mjs → direct-executor-BWeWiRYB.mjs} +3 -3
- package/dist/{direct-executor-CDXeJCSs.mjs.map → direct-executor-BWeWiRYB.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-CEJgR_LB.mjs} +1 -1
- package/dist/{email-console-BHNjIwiB.mjs.map → email-console-CEJgR_LB.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-D9w94fjV.mjs} +2 -2
- package/dist/{error-DupyJZE2.mjs.map → error-D9w94fjV.mjs.map} +1 -1
- package/dist/{errors-CLUPm7aD.mjs → errors-n2j49uNf.mjs} +22 -2
- package/dist/errors-n2j49uNf.mjs.map +1 -0
- package/dist/{escape-DUY2r2Zs.mjs → escape-DN4Qmjsl.mjs} +1 -1
- package/dist/{escape-DUY2r2Zs.mjs.map → escape-DN4Qmjsl.mjs.map} +1 -1
- package/dist/{field-defs-cache-BzwBlJrR.mjs → field-defs-cache-Bh4q6oar.mjs} +5 -5
- package/dist/{field-defs-cache-BzwBlJrR.mjs.map → field-defs-cache-Bh4q6oar.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-BhOdW0bi.mjs} +3 -3
- package/dist/{fts-manager-DyZEohZC.mjs.map → fts-manager-BhOdW0bi.mjs.map} +1 -1
- package/dist/{hash-Cg0TXJPV.mjs → hash-CTcngWvj.mjs} +1 -1
- package/dist/{hash-Cg0TXJPV.mjs.map → hash-CTcngWvj.mjs.map} +1 -1
- package/dist/{hreflang-DwT2E0Sa.d.mts → hreflang-7pTHsLZJ.d.mts} +2 -2
- package/dist/{hreflang-DwT2E0Sa.d.mts.map → hreflang-7pTHsLZJ.d.mts.map} +1 -1
- package/dist/{hreflang-BBe83kKa.mjs → hreflang-Huc6O5wa.mjs} +7 -7
- package/dist/{hreflang-BBe83kKa.mjs.map → hreflang-Huc6O5wa.mjs.map} +1 -1
- package/dist/{identity-Ys6Dlc9C.mjs → identity-DXmb4aVp.mjs} +3 -3
- package/dist/{identity-Ys6Dlc9C.mjs.map → identity-DXmb4aVp.mjs.map} +1 -1
- package/dist/{import-DPkLmeXO.mjs → import-C4_O3KXU.mjs} +5 -5
- package/dist/{import-DPkLmeXO.mjs.map → import-C4_O3KXU.mjs.map} +1 -1
- package/dist/{index-C9KuQyO-.d.mts → index-6bRee8zb.d.mts} +3 -3
- package/dist/{index-C9KuQyO-.d.mts.map → index-6bRee8zb.d.mts.map} +1 -1
- package/dist/{index-BVBZj4-7.d.mts → index-DWbJrsAg.d.mts} +128 -52
- package/dist/index-DWbJrsAg.d.mts.map +1 -0
- package/dist/index.d.mts +21 -21
- package/dist/index.mjs +78 -72
- 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-Bo7fZAbg.mjs} +2 -2
- package/dist/{load-YJ2xl3ra.mjs.map → load-Bo7fZAbg.mjs.map} +1 -1
- package/dist/{loader-CdG_Ci3H.mjs → loader-ZsjE4paL.mjs} +2 -2
- package/dist/{loader-CdG_Ci3H.mjs.map → loader-ZsjE4paL.mjs.map} +1 -1
- package/dist/{manifest-builder-B252yQUf.mjs → manifest-builder-QXuqvVMP.mjs} +2 -2
- package/dist/{manifest-builder-B252yQUf.mjs.map → manifest-builder-QXuqvVMP.mjs.map} +1 -1
- package/dist/{manifest-schema-BXbUAHod.mjs → manifest-schema-CUYPfCEH.mjs} +1 -1
- package/dist/{manifest-schema-BXbUAHod.mjs.map → manifest-schema-CUYPfCEH.mjs.map} +1 -1
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +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 +11 -5
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-BWWWb8kg.mjs → media-BjwNuLEu.mjs} +100 -6
- package/dist/media-BjwNuLEu.mjs.map +1 -0
- package/dist/{media-CzhAn2--.mjs → media-D6SeAfBl.mjs} +41 -12
- package/dist/media-D6SeAfBl.mjs.map +1 -0
- package/dist/{media-allowlist-D6x-Us02.mjs → media-allowlist-D35v7bmL.mjs} +3 -2
- package/dist/media-allowlist-D35v7bmL.mjs.map +1 -0
- package/dist/media-folders-D-0CmRZj.mjs +210 -0
- package/dist/media-folders-D-0CmRZj.mjs.map +1 -0
- package/dist/{media-upload-CHBVn6m-.mjs → media-upload-C7bLXGcI.mjs} +9 -9
- package/dist/{media-upload-CHBVn6m-.mjs.map → media-upload-C7bLXGcI.mjs.map} +1 -1
- package/dist/{media-url-BvoX88FA.mjs → media-url-DH7giMx_.mjs} +1 -1
- package/dist/{media-url-BvoX88FA.mjs.map → media-url-DH7giMx_.mjs.map} +1 -1
- package/dist/media-usage-BTRJvS6V.mjs +1833 -0
- package/dist/media-usage-BTRJvS6V.mjs.map +1 -0
- package/dist/media-usage-activation-Dx0kPV6b.mjs +78 -0
- package/dist/media-usage-activation-Dx0kPV6b.mjs.map +1 -0
- package/dist/{media-usage-work-DrCbqBcl.mjs → media-usage-work-C-fhM30c.mjs} +221 -59
- package/dist/media-usage-work-C-fhM30c.mjs.map +1 -0
- package/dist/{media-usage-work-CLo_6tct.mjs → media-usage-work-DWb2XPMI.mjs} +4 -4
- package/dist/{media-usage-work-CLo_6tct.mjs.map → media-usage-work-DWb2XPMI.mjs.map} +1 -1
- package/dist/{media-usage-write-fence-aHEgN1zB.mjs → media-usage-write-fence-BwoJVRDC.mjs} +2 -2
- package/dist/{media-usage-write-fence-aHEgN1zB.mjs.map → media-usage-write-fence-BwoJVRDC.mjs.map} +1 -1
- package/dist/{menus-TCNSd2vp.mjs → menus-CD7EFLwz.mjs} +3 -3
- package/dist/{menus-TCNSd2vp.mjs.map → menus-CD7EFLwz.mjs.map} +1 -1
- package/dist/{menus-DSo5QeW0.mjs → menus-CNg0QlDt.mjs} +42 -26
- package/dist/menus-CNg0QlDt.mjs.map +1 -0
- 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-DaUM_MB2.mjs} +1 -1
- package/dist/{mode-BDoH_WcF.mjs.map → mode-DaUM_MB2.mjs.map} +1 -1
- package/dist/node-sqlite-compat-Udb4Y-rs.mjs +46 -0
- package/dist/node-sqlite-compat-Udb4Y-rs.mjs.map +1 -0
- package/dist/{normalize-Bc-s_r-a.mjs → normalize-CSxdKBVa.mjs} +22 -1
- package/dist/normalize-CSxdKBVa.mjs.map +1 -0
- package/dist/{oauth-authorization-iOikmrJd.mjs → oauth-authorization-Dpsb-fMJ.mjs} +5 -5
- package/dist/{oauth-authorization-iOikmrJd.mjs.map → oauth-authorization-Dpsb-fMJ.mjs.map} +1 -1
- package/dist/{oauth-clients-CvI0nHMJ.mjs → oauth-clients-Bxnoe_67.mjs} +1 -1
- package/dist/{oauth-clients-CvI0nHMJ.mjs.map → oauth-clients-Bxnoe_67.mjs.map} +1 -1
- package/dist/{oauth-state-store-Ba8KZlwW.mjs → oauth-state-store-0C4s0K6w.mjs} +1 -1
- package/dist/{oauth-state-store-Ba8KZlwW.mjs.map → oauth-state-store-0C4s0K6w.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-C5SQwGYC.mjs → oauth-user-lookup-wlGYSeqX.mjs} +1 -1
- package/dist/{oauth-user-lookup-C5SQwGYC.mjs.map → oauth-user-lookup-wlGYSeqX.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- 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-BhZIeFY1.d.mts} +3 -3
- package/dist/{options--hhz-jZ1.d.mts.map → options-BhZIeFY1.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-DFbuc-Fh.mjs → parse-BRJg1kz8.mjs} +2 -2
- package/dist/{parse-DFbuc-Fh.mjs.map → parse-BRJg1kz8.mjs.map} +1 -1
- package/dist/{passkey-config-DFAmUyuE.mjs → passkey-config-B3P0nHRR.mjs} +1 -1
- package/dist/{passkey-config-DFAmUyuE.mjs.map → passkey-config-B3P0nHRR.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-BR4ZhVJl.d.mts} +7 -1
- package/dist/placeholder-BR4ZhVJl.d.mts.map +1 -0
- package/dist/plugin-types.d.mts +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-BVRganJQ.mjs} +2 -2
- package/dist/{policy-TluUIxuu.mjs.map → policy-BVRganJQ.mjs.map} +1 -1
- package/dist/{portable-text-imBW5cHK.mjs → portable-text-C8Tvp4_g.mjs} +1 -1
- package/dist/{portable-text-imBW5cHK.mjs.map → portable-text-C8Tvp4_g.mjs.map} +1 -1
- package/dist/{preview-PW-nOnq4.mjs → preview-dn2dUyM0.mjs} +2 -2
- package/dist/{preview-PW-nOnq4.mjs.map → preview-dn2dUyM0.mjs.map} +1 -1
- package/dist/{protocol-DjsJHTlK.d.mts → protocol-CuBEsqgE.d.mts} +2 -2
- package/dist/{protocol-DjsJHTlK.d.mts.map → protocol-CuBEsqgE.d.mts.map} +1 -1
- package/dist/{public-url-CWoFhQT8.mjs → public-url-DRz_AinV.mjs} +1 -1
- package/dist/{public-url-CWoFhQT8.mjs.map → public-url-DRz_AinV.mjs.map} +1 -1
- package/dist/{query-zTVAR1Sl.mjs → query-D18vTdha.mjs} +14 -14
- package/dist/{query-zTVAR1Sl.mjs.map → query-D18vTdha.mjs.map} +1 -1
- package/dist/{query-BWs7O5Il.mjs → query-tZDEdtCN.mjs} +4 -4
- package/dist/{query-BWs7O5Il.mjs.map → query-tZDEdtCN.mjs.map} +1 -1
- package/dist/{rate-limit-Bx4g9m3p.mjs → rate-limit-on7KPo-v.mjs} +2 -2
- package/dist/{rate-limit-Bx4g9m3p.mjs.map → rate-limit-on7KPo-v.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-DrbEmU9a.mjs} +1 -1
- package/dist/{redirect-BgAOGHDq.mjs.map → redirect-DrbEmU9a.mjs.map} +1 -1
- package/dist/{redirects-DyChwDkG.mjs → redirects-BahWiu8Z.mjs} +5 -5
- package/dist/{redirects-DyChwDkG.mjs.map → redirects-BahWiu8Z.mjs.map} +1 -1
- package/dist/{redirects-DTDHWH31.mjs → redirects-CRIM55KH.mjs} +6 -6
- package/dist/{redirects-DTDHWH31.mjs.map → redirects-CRIM55KH.mjs.map} +1 -1
- package/dist/{api-BeBglVxU.mjs → registry-BiO5WwPq.mjs} +483 -2462
- package/dist/registry-BiO5WwPq.mjs.map +1 -0
- package/dist/{registry-Cqa2I749.mjs → registry-Cx-jEJOS.mjs} +9 -123
- package/dist/registry-Cx-jEJOS.mjs.map +1 -0
- package/dist/{relation-BnHB0T4J.mjs → relation-A6wLF_DS.mjs} +2 -2
- package/dist/{relation-BnHB0T4J.mjs.map → relation-A6wLF_DS.mjs.map} +1 -1
- package/dist/{relations-BBApI4e2.mjs → relations-BTPM2saV.mjs} +2 -2
- package/dist/{relations-BBApI4e2.mjs.map → relations-BTPM2saV.mjs.map} +1 -1
- package/dist/{relations-BHo5A8Zf.d.mts → relations-C7B46bcU.d.mts} +192 -33
- package/dist/relations-C7B46bcU.d.mts.map +1 -0
- package/dist/{relations-D5sM5fW0.mjs → relations-Cj5hZ6Zg.mjs} +4 -4
- package/dist/{relations-D5sM5fW0.mjs.map → relations-Cj5hZ6Zg.mjs.map} +1 -1
- 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-DRp5ENd_.mjs} +2 -2
- package/dist/{request-meta-DgZJh3zG.mjs.map → request-meta-DRp5ENd_.mjs.map} +1 -1
- package/dist/{resolve-DOhVEvty.mjs → resolve-DxUpTMmE.mjs} +1 -1
- package/dist/{resolve-DOhVEvty.mjs.map → resolve-DxUpTMmE.mjs.map} +1 -1
- package/dist/{runner-C1T11CfC.mjs → runner-77GOZCnH.mjs} +410 -286
- package/dist/runner-77GOZCnH.mjs.map +1 -0
- package/dist/{runner-BYHZn3Jy.d.mts → runner-zgqA_CqP.d.mts} +2 -2
- package/dist/{runner-BYHZn3Jy.d.mts.map → runner-zgqA_CqP.d.mts.map} +1 -1
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-DZ5YVVZj.mjs → schema-BY-oVntF.mjs} +3 -3
- package/dist/{schema-DZ5YVVZj.mjs.map → schema-BY-oVntF.mjs.map} +1 -1
- package/dist/{schema-2AOsSn2v.mjs → schema-Cw9kVK0I.mjs} +35 -5
- package/dist/{schema-2AOsSn2v.mjs.map → schema-Cw9kVK0I.mjs.map} +1 -1
- package/dist/{scopes-8ClPF2bC.mjs → scopes-vMR1uIbt.mjs} +3 -3
- package/dist/{scopes-8ClPF2bC.mjs.map → scopes-vMR1uIbt.mjs.map} +1 -1
- package/dist/{search-u1QYdOCG.mjs → search-CfT3KxO5.mjs} +4 -4
- package/dist/{search-u1QYdOCG.mjs.map → search-CfT3KxO5.mjs.map} +1 -1
- package/dist/{secrets-BEgsAQEU.mjs → secrets-BH2Qow1-.mjs} +3 -3
- package/dist/{secrets-BEgsAQEU.mjs.map → secrets-BH2Qow1-.mjs.map} +1 -1
- package/dist/{sections-DmHviGyM.mjs → sections-DTfjyf1z.mjs} +2 -2
- package/dist/{sections-DmHviGyM.mjs.map → sections-DTfjyf1z.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +24 -22
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +16 -14
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-AFpVvTqr.mjs → seo-BSiTIXzd.mjs} +1 -1
- package/dist/{seo-AFpVvTqr.mjs.map → seo-BSiTIXzd.mjs.map} +1 -1
- package/dist/{seo-Dq4G-RrO.mjs → seo-Cm-dtvgk.mjs} +2 -2
- package/dist/{seo-Dq4G-RrO.mjs.map → seo-Cm-dtvgk.mjs.map} +1 -1
- package/dist/{service-BpArayPr.mjs → service-DYsDHEKe.mjs} +3 -3
- package/dist/{service-BpArayPr.mjs.map → service-DYsDHEKe.mjs.map} +1 -1
- package/dist/{session-user-BEorKVT-.mjs → session-user-CNAfvsdw.mjs} +1 -1
- package/dist/{session-user-BEorKVT-.mjs.map → session-user-CNAfvsdw.mjs.map} +1 -1
- package/dist/{settings-xRdoUZr0.mjs → settings-BgN-0DT-.mjs} +6 -6
- package/dist/{settings-xRdoUZr0.mjs.map → settings-BgN-0DT-.mjs.map} +1 -1
- package/dist/{settings-DE9xtzqd.mjs → settings-DjtkUo__.mjs} +3 -3
- package/dist/{settings-DE9xtzqd.mjs.map → settings-DjtkUo__.mjs.map} +1 -1
- package/dist/{setup-complete-BnT8pq_C.mjs → setup-complete-CcpXaPU7.mjs} +2 -2
- package/dist/{setup-complete-BnT8pq_C.mjs.map → setup-complete-CcpXaPU7.mjs.map} +1 -1
- package/dist/{setup-nonce-CBitf_oQ.mjs → setup-nonce-Reyw30jg.mjs} +1 -1
- package/dist/{setup-nonce-CBitf_oQ.mjs.map → setup-nonce-Reyw30jg.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-Cs_V9n3V.mjs} +3 -3
- package/dist/{site-url-DjpIXMXJ.mjs.map → site-url-Cs_V9n3V.mjs.map} +1 -1
- package/dist/{snapshot-BBlGkb2q.mjs → snapshot-DSB8P7Lm.mjs} +1 -1
- package/dist/{snapshot-BBlGkb2q.mjs.map → snapshot-DSB8P7Lm.mjs.map} +1 -1
- package/dist/{ssrf-Cpvg_n3A.mjs → ssrf-DqebVE34.mjs} +21 -5
- package/dist/{ssrf-Cpvg_n3A.mjs.map → ssrf-DqebVE34.mjs.map} +1 -1
- package/dist/{state-DYwPue40.mjs → state-BJfU9Uzw.mjs} +12 -4
- package/dist/state-BJfU9Uzw.mjs.map +1 -0
- package/dist/{status-CWVYpbD8.mjs → status-DDu8HIQl.mjs} +1 -1
- package/dist/{status-CWVYpbD8.mjs.map → status-DDu8HIQl.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{target-DixqqV5i.mjs → target-D0o26Azo.mjs} +1 -1
- package/dist/{target-DixqqV5i.mjs.map → target-D0o26Azo.mjs.map} +1 -1
- package/dist/{taxonomies-OHUIiIpI.mjs → taxonomies-CPGxo-bn.mjs} +5 -5
- package/dist/{taxonomies-OHUIiIpI.mjs.map → taxonomies-CPGxo-bn.mjs.map} +1 -1
- package/dist/{taxonomies-BlrncHi9.mjs → taxonomies-kmyudNGP.mjs} +7 -7
- package/dist/{taxonomies-BlrncHi9.mjs.map → taxonomies-kmyudNGP.mjs.map} +1 -1
- package/dist/{taxonomy-BqCZ1TdU.mjs → taxonomy-8X09dLN5.mjs} +4 -4
- package/dist/{taxonomy-BqCZ1TdU.mjs.map → taxonomy-8X09dLN5.mjs.map} +1 -1
- package/dist/{tokens-C39klX8R.mjs → tokens-gTS-IVUp.mjs} +1 -1
- package/dist/{tokens-C39klX8R.mjs.map → tokens-gTS-IVUp.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-RQE36APG.d.mts → transport-_6R1ub7t.d.mts} +1 -1
- package/dist/{transport-RQE36APG.d.mts.map → transport-_6R1ub7t.d.mts.map} +1 -1
- package/dist/{transport-DBJwa-Si.mjs → transport-fX59xaJz.mjs} +4 -3
- package/dist/{transport-DBJwa-Si.mjs.map → transport-fX59xaJz.mjs.map} +1 -1
- package/dist/{trusted-proxy-CEVekpby.mjs → trusted-proxy-fe6nEgR4.mjs} +1 -1
- package/dist/{trusted-proxy-CEVekpby.mjs.map → trusted-proxy-fe6nEgR4.mjs.map} +1 -1
- package/dist/types-75KIqzL9.mjs.map +1 -1
- package/dist/{types-BW1syrlw.mjs → types-BXUmsINL.mjs} +1 -1
- package/dist/{types-BW1syrlw.mjs.map → types-BXUmsINL.mjs.map} +1 -1
- package/dist/{types-BntycPTF.d.mts → types-Blb-MRC3.d.mts} +1 -1
- package/dist/{types-BntycPTF.d.mts.map → types-Blb-MRC3.d.mts.map} +1 -1
- package/dist/{types-C2pMvO62.d.mts → types-Br-ERaxh.d.mts} +2 -1
- package/dist/types-Br-ERaxh.d.mts.map +1 -0
- package/dist/{types-DWRhk9xZ.d.mts → types-BzBd3PeX.d.mts} +1 -1
- package/dist/{types-DWRhk9xZ.d.mts.map → types-BzBd3PeX.d.mts.map} +1 -1
- package/dist/{types-CXnOzDe1.d.mts → types-CLqvnd05.d.mts} +1 -1
- package/dist/{types-CXnOzDe1.d.mts.map → types-CLqvnd05.d.mts.map} +1 -1
- package/dist/{types-Dfk8KI5R.d.mts → types-D5VVLr7J.d.mts} +1 -1
- package/dist/{types-Dfk8KI5R.d.mts.map → types-D5VVLr7J.d.mts.map} +1 -1
- package/dist/{types-DQIDRKF2.d.mts → types-DHxzBjbD.d.mts} +10 -1
- package/dist/types-DHxzBjbD.d.mts.map +1 -0
- package/dist/{types-DN64_zzZ.d.mts → types-DJQxln7q.d.mts} +3 -3
- package/dist/{types-DN64_zzZ.d.mts.map → types-DJQxln7q.d.mts.map} +1 -1
- 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-BcqdcT-r.d.mts → types-D_DWe7k9.d.mts} +1 -1
- package/dist/{types-BcqdcT-r.d.mts.map → types-D_DWe7k9.d.mts.map} +1 -1
- package/dist/{types-DrJrVCT1.d.mts → types-Vg6f5i3s.d.mts} +3 -3
- package/dist/{types-DrJrVCT1.d.mts.map → types-Vg6f5i3s.d.mts.map} +1 -1
- package/dist/{upload-attempts-D4FS8LT6.mjs → upload-attempts-CNyGSepg.mjs} +1 -1
- package/dist/{upload-attempts-D4FS8LT6.mjs.map → upload-attempts-CNyGSepg.mjs.map} +1 -1
- package/dist/{url-pattern-BHS0V5Ng.mjs → url-pattern-K-KdXcSe.mjs} +1 -1
- package/dist/{url-pattern-BHS0V5Ng.mjs.map → url-pattern-K-KdXcSe.mjs.map} +1 -1
- package/dist/{user-DJZfUJVE.mjs → user-CtYY0tp2.mjs} +2 -2
- package/dist/{user-DJZfUJVE.mjs.map → user-CtYY0tp2.mjs.map} +1 -1
- package/dist/{utils-D4vD_Y98.mjs → utils-HztC73hq.mjs} +1 -1
- package/dist/{utils-D4vD_Y98.mjs.map → utils-HztC73hq.mjs.map} +1 -1
- package/dist/{validate-BMLBvxOC.mjs → validate-Dd-IUjnZ.mjs} +2 -2
- package/dist/{validate-BMLBvxOC.mjs.map → validate-Dd-IUjnZ.mjs.map} +1 -1
- package/dist/{validate-CTqt5nuE.d.mts → validate-DlqdOZXB.d.mts} +5 -5
- package/dist/{validate-CTqt5nuE.d.mts.map → validate-DlqdOZXB.d.mts.map} +1 -1
- package/dist/{validation-npvENUCB.mjs → validation-D83n7Npi.mjs} +10 -8
- package/dist/{validation-npvENUCB.mjs.map → validation-D83n7Npi.mjs.map} +1 -1
- package/dist/version-ri-sCyAp.mjs +7 -0
- package/dist/{version-DB_fqCSI.mjs.map → version-ri-sCyAp.mjs.map} +1 -1
- package/dist/{widgets-BD3ho7Rq.mjs → widgets-CpmZd9Q-.mjs} +4 -4
- package/dist/{widgets-BD3ho7Rq.mjs.map → widgets-CpmZd9Q-.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-Ca68766D.d.mts → wxr-taxonomies-BFd0y72_.d.mts} +1 -1
- package/dist/{wxr-taxonomies-Ca68766D.d.mts.map → wxr-taxonomies-BFd0y72_.d.mts.map} +1 -1
- package/dist/{wxr-taxonomies-B1R3vBoD.mjs → wxr-taxonomies-Df4vxWcU.mjs} +4 -4
- package/dist/{wxr-taxonomies-B1R3vBoD.mjs.map → wxr-taxonomies-Df4vxWcU.mjs.map} +1 -1
- package/dist/{zod-generator-LC-on_Vb.mjs → zod-generator-DylzusHh.mjs} +24 -18
- package/dist/zod-generator-DylzusHh.mjs.map +1 -0
- package/package.json +16 -8
- package/src/api/errors.ts +20 -0
- package/src/api/handlers/index.ts +17 -0
- 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 +62 -2
- package/src/api/handlers/registry.ts +134 -316
- package/src/api/openapi/document.ts +192 -2
- package/src/api/schemas/media-usage.ts +77 -0
- package/src/api/schemas/media.ts +69 -0
- package/src/astro/integration/routes.ts +18 -0
- package/src/astro/integration/vite-config.ts +2 -7
- package/src/astro/middleware/auth.ts +4 -0
- package/src/astro/middleware.ts +0 -7
- 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/plugins/registry/artifact.ts +246 -96
- package/src/astro/routes/api/auth/me.ts +14 -2
- package/src/astro/routes/api/media/[id].ts +3 -0
- 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.ts +12 -2
- package/src/astro/routes/api/setup/dev-bypass.ts +12 -0
- package/src/astro/types.ts +11 -1
- package/src/client/index.ts +140 -2
- package/src/client/transport.ts +6 -2
- package/src/components/EmDashImage.astro +154 -50
- package/src/components/Gallery.astro +7 -0
- package/src/components/InlinePortableTextEditor.tsx +130 -1
- package/src/components/LiveSearch.astro +23 -0
- package/src/components/inline-code-block.tsx +180 -49
- package/src/content/converters/gallery.ts +2 -0
- package/src/content/converters/types.ts +2 -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/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/runner.ts +8 -2
- package/src/database/repositories/content.ts +5 -4
- 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 +1038 -10
- package/src/database/repositories/media.ts +97 -29
- package/src/database/transaction.ts +1 -0
- package/src/database/types.ts +13 -0
- package/src/db/adapters.ts +2 -2
- package/src/db/node-sqlite-compat.ts +78 -0
- package/src/db/sqlite.ts +3 -3
- package/src/emdash-runtime.ts +110 -105
- package/src/fields/image.ts +7 -1
- package/src/fields/types.ts +6 -2
- package/src/index.ts +5 -0
- package/src/media/focal-point.ts +37 -0
- package/src/media/local-runtime.ts +8 -0
- package/src/media/mime.ts +1 -0
- package/src/media/normalize.ts +10 -0
- 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/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/plugins/index.ts +1 -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/registry/artifact-fetch.ts +530 -0
- package/src/registry/config.ts +24 -6
- package/src/registry/types.ts +12 -15
- package/src/schema/types.ts +1 -0
- package/src/schema/zod-generator.ts +12 -4
- package/src/security/ssrf.ts +21 -2
- package/src/seed/apply.ts +4 -4
- package/src/ui.ts +2 -0
- package/dist/api-BeBglVxU.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/errors-CLUPm7aD.mjs.map +0 -1
- package/dist/index-BVBZj4-7.d.mts.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/placeholder-Dp0K3RkD.d.mts.map +0 -1
- package/dist/registry-Cqa2I749.mjs.map +0 -1
- package/dist/relations-BHo5A8Zf.d.mts.map +0 -1
- package/dist/runner-C1T11CfC.mjs.map +0 -1
- package/dist/state-DYwPue40.mjs.map +0 -1
- package/dist/types-C2pMvO62.d.mts.map +0 -1
- package/dist/types-DQIDRKF2.d.mts.map +0 -1
- package/dist/version-DB_fqCSI.mjs +0 -7
- package/dist/zod-generator-LC-on_Vb.mjs.map +0 -1
- /package/dist/{api-tokens-rIXC6LdE.mjs → api-tokens-B5NHLNUN.mjs} +0 -0
- /package/dist/{ssrf-DpXgKsna.mjs → ssrf-BPKzkm6O.mjs} +0 -0
- /package/dist/{types-CP1CflWf.mjs → types-D6OcIp2P.mjs} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-BiO5WwPq.mjs","names":["TRAILING_SLASHES","TRAILING_DOT"],"sources":["../src/plugins/marketplace.ts","../src/plugins/storage-indexes.ts","../src/api/handlers/marketplace.ts","../src/registry/artifact-fetch.ts","../src/registry/config.ts","../src/registry/plugin-id.ts","../src/api/handlers/registry.ts"],"sourcesContent":["/**\n * MarketplaceClient — HTTP client for the EmDash Plugin Marketplace\n *\n * Used by the install/update/proxy endpoints in EmDash core to communicate\n * with the marketplace Worker. The marketplace is a distribution channel,\n * not a runtime dependency — bundles are copied to site-local R2 at install time.\n */\n\nimport { createGzipDecoder, unpackTar } from \"modern-tar\";\n\nimport { pluginManifestSchema, reconcileManifestAccess } from \"./manifest-schema.js\";\nimport type { PluginManifest } from \"./types.js\";\n\n// ── Module-level regex patterns ───────────────────────────────────\n\nconst TRAILING_SLASHES = /\\/+$/;\nconst LEADING_DOT_SLASH = /^\\.\\//;\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface MarketplacePluginSummary {\n\tid: string;\n\tname: string;\n\tdescription: string | null;\n\tauthor: {\n\t\tname: string;\n\t\tverified: boolean;\n\t\tavatarUrl: string | null;\n\t};\n\tcapabilities: string[];\n\tkeywords: string[];\n\tinstallCount: number;\n\thasIcon: boolean;\n\ticonUrl: string;\n\tlatestVersion?: {\n\t\tversion: string;\n\t\taudit?: {\n\t\t\tverdict: string;\n\t\t\triskScore: number;\n\t\t};\n\t\timageAudit?: {\n\t\t\tverdict: string;\n\t\t};\n\t};\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface MarketplaceVersionSummary {\n\tversion: string;\n\tminEmDashVersion: string | null;\n\tbundleSize: number;\n\tchecksum: string;\n\tchangelog: string | null;\n\tcapabilities: string[];\n\tstatus: string;\n\tauditVerdict: string | null;\n\timageAuditVerdict: string | null;\n\tpublishedAt: string;\n}\n\nexport interface MarketplacePluginDetail extends MarketplacePluginSummary {\n\trepositoryUrl: string | null;\n\thomepageUrl: string | null;\n\tlicense: string | null;\n\tlatestVersion?: {\n\t\tversion: string;\n\t\tminEmDashVersion: string | null;\n\t\tbundleSize: number;\n\t\tchecksum: string;\n\t\tchangelog: string | null;\n\t\treadme: string | null;\n\t\thasIcon: boolean;\n\t\tscreenshotCount: number;\n\t\tscreenshotUrls: string[];\n\t\tcapabilities: string[];\n\t\tstatus: string;\n\t\taudit?: {\n\t\t\tverdict: string;\n\t\t\triskScore: number;\n\t\t};\n\t\timageAudit?: {\n\t\t\tverdict: string;\n\t\t};\n\t\tpublishedAt: string;\n\t};\n}\n\nexport interface MarketplaceSearchOpts {\n\tcategory?: string;\n\tcapability?: string;\n\tsort?: \"installs\" | \"updated\" | \"created\" | \"name\";\n\tcursor?: string;\n\tlimit?: number;\n}\n\nexport interface MarketplaceSearchResult {\n\titems: MarketplacePluginSummary[];\n\tnextCursor?: string;\n}\n\n// ── Theme types ───────────────────────────────────────────────────\n\nexport interface MarketplaceThemeSummary {\n\tid: string;\n\tname: string;\n\tdescription: string | null;\n\tauthor: {\n\t\tname: string;\n\t\tverified: boolean;\n\t\tavatarUrl: string | null;\n\t};\n\tkeywords: string[];\n\tpreviewUrl: string;\n\tdemoUrl: string | null;\n\thasThumbnail: boolean;\n\tthumbnailUrl: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface MarketplaceThemeDetail extends MarketplaceThemeSummary {\n\tauthor: {\n\t\tid: string;\n\t\tname: string;\n\t\tverified: boolean;\n\t\tavatarUrl: string | null;\n\t};\n\trepositoryUrl: string | null;\n\thomepageUrl: string | null;\n\tlicense: string | null;\n\tscreenshotCount: number;\n\tscreenshotUrls: string[];\n}\n\nexport interface MarketplaceThemeSearchOpts {\n\tkeyword?: string;\n\tsort?: \"name\" | \"created\" | \"updated\";\n\tcursor?: string;\n\tlimit?: number;\n}\n\nexport interface MarketplaceThemeSearchResult {\n\titems: MarketplaceThemeSummary[];\n\tnextCursor?: string;\n}\n\nexport interface PluginBundle {\n\tmanifest: PluginManifest;\n\tbackendCode: string;\n\tadminCode?: string;\n\tchecksum: string;\n}\n\n// ── Interface ──────────────────────────────────────────────────────\n\nexport interface MarketplaceClient {\n\t/** Search the marketplace catalog */\n\tsearch(query?: string, opts?: MarketplaceSearchOpts): Promise<MarketplaceSearchResult>;\n\n\t/** Get full plugin detail */\n\tgetPlugin(id: string): Promise<MarketplacePluginDetail>;\n\n\t/** Get version history for a plugin */\n\tgetVersions(id: string): Promise<MarketplaceVersionSummary[]>;\n\n\t/** Download and extract a plugin bundle */\n\tdownloadBundle(id: string, version: string): Promise<PluginBundle>;\n\n\t/** Fire-and-forget install stat (never throws) */\n\treportInstall(id: string, version: string): Promise<void>;\n\n\t/** Search theme listings */\n\tsearchThemes(\n\t\tquery?: string,\n\t\topts?: MarketplaceThemeSearchOpts,\n\t): Promise<MarketplaceThemeSearchResult>;\n\n\t/** Get full theme detail */\n\tgetTheme(id: string): Promise<MarketplaceThemeDetail>;\n}\n\n// ── Errors ─────────────────────────────────────────────────────────\n\nexport class MarketplaceError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly status?: number,\n\t\tpublic readonly code?: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"MarketplaceError\";\n\t}\n}\n\nexport class MarketplaceUnavailableError extends MarketplaceError {\n\tconstructor(cause?: unknown) {\n\t\tsuper(\"Plugin marketplace is unavailable\", undefined, \"MARKETPLACE_UNAVAILABLE\");\n\t\tif (cause) this.cause = cause;\n\t}\n}\n\n// ── Implementation ─────────────────────────────────────────────────\n\nclass MarketplaceClientImpl implements MarketplaceClient {\n\tprivate readonly baseUrl: string;\n\tprivate readonly siteOrigin: string | undefined;\n\n\tconstructor(baseUrl: string, siteOrigin?: string) {\n\t\t// Strip trailing slash\n\t\tthis.baseUrl = baseUrl.replace(TRAILING_SLASHES, \"\");\n\t\tthis.siteOrigin = siteOrigin;\n\t}\n\n\tasync search(query?: string, opts?: MarketplaceSearchOpts): Promise<MarketplaceSearchResult> {\n\t\tconst params = new URLSearchParams();\n\t\tif (query) params.set(\"q\", query);\n\t\tif (opts?.category) params.set(\"category\", opts.category);\n\t\tif (opts?.capability) params.set(\"capability\", opts.capability);\n\t\tif (opts?.sort) params.set(\"sort\", opts.sort);\n\t\tif (opts?.cursor) params.set(\"cursor\", opts.cursor);\n\t\tif (opts?.limit) params.set(\"limit\", String(opts.limit));\n\n\t\tconst qs = params.toString();\n\t\tconst url = `${this.baseUrl}/api/v1/plugins${qs ? `?${qs}` : \"\"}`;\n\t\tconst data = await this.fetchJson<MarketplaceSearchResult>(url);\n\t\treturn data;\n\t}\n\n\tasync getPlugin(id: string): Promise<MarketplacePluginDetail> {\n\t\tconst url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}`;\n\t\treturn this.fetchJson<MarketplacePluginDetail>(url);\n\t}\n\n\tasync getVersions(id: string): Promise<MarketplaceVersionSummary[]> {\n\t\tconst url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/versions`;\n\t\tconst data = await this.fetchJson<{ items: MarketplaceVersionSummary[] }>(url);\n\t\treturn data.items;\n\t}\n\n\tasync downloadBundle(id: string, version: string): Promise<PluginBundle> {\n\t\tconst bundleUrl = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/versions/${encodeURIComponent(version)}/bundle`;\n\n\t\tconst marketplaceOrigin = new URL(this.baseUrl).origin;\n\t\tconst MAX_REDIRECTS = 5;\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tlet currentUrl = bundleUrl;\n\t\t\tresponse = await fetch(currentUrl, { redirect: \"manual\" });\n\n\t\t\t// Follow redirects manually, validating each target stays on the marketplace host\n\t\t\tfor (let i = 0; i < MAX_REDIRECTS; i++) {\n\t\t\t\tif (response.status < 300 || response.status >= 400) break;\n\n\t\t\t\tconst location = response.headers.get(\"location\");\n\t\t\t\tif (!location) break;\n\n\t\t\t\tconst target = new URL(location, currentUrl);\n\t\t\t\tif (target.origin !== marketplaceOrigin) {\n\t\t\t\t\tthrow new MarketplaceError(\n\t\t\t\t\t\t`Bundle download redirected to untrusted host: ${target.origin}`,\n\t\t\t\t\t\tresponse.status,\n\t\t\t\t\t\t\"BUNDLE_REDIRECT_UNTRUSTED\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcurrentUrl = target.href;\n\t\t\t\tresponse = await fetch(currentUrl, { redirect: \"manual\" });\n\t\t\t}\n\n\t\t\t// If still a redirect after MAX_REDIRECTS, fail explicitly\n\t\t\tif (response.status >= 300 && response.status < 400) {\n\t\t\t\tthrow new MarketplaceError(\n\t\t\t\t\t`Bundle download exceeded maximum redirects (${MAX_REDIRECTS})`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t\t\"BUNDLE_TOO_MANY_REDIRECTS\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (err instanceof MarketplaceError) throw err;\n\t\t\tthrow new MarketplaceUnavailableError(err);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t`Failed to download bundle: ${response.status} ${response.statusText}`,\n\t\t\t\tresponse.status,\n\t\t\t\t\"BUNDLE_DOWNLOAD_FAILED\",\n\t\t\t);\n\t\t}\n\n\t\tconst tarballBytes = new Uint8Array(await response.arrayBuffer());\n\t\ttry {\n\t\t\treturn await extractBundle(tarballBytes);\n\t\t} catch (err) {\n\t\t\tif (err instanceof MarketplaceError) throw err;\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t\"Failed to extract plugin bundle\",\n\t\t\t\tundefined,\n\t\t\t\t\"BUNDLE_EXTRACT_FAILED\",\n\t\t\t);\n\t\t}\n\t}\n\n\tasync reportInstall(id: string, version: string): Promise<void> {\n\t\t// Generate a stable site hash from the site origin (best-effort, non-identifying)\n\t\tconst siteHash = await generateSiteHash(this.siteOrigin);\n\t\tconst url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/installs`;\n\n\t\ttry {\n\t\t\tawait fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ siteHash, version }),\n\t\t\t});\n\t\t} catch {\n\t\t\t// Fire-and-forget — never throw\n\t\t}\n\t}\n\n\tasync searchThemes(\n\t\tquery?: string,\n\t\topts?: MarketplaceThemeSearchOpts,\n\t): Promise<MarketplaceThemeSearchResult> {\n\t\tconst params = new URLSearchParams();\n\t\tif (query) params.set(\"q\", query);\n\t\tif (opts?.keyword) params.set(\"keyword\", opts.keyword);\n\t\tif (opts?.sort) params.set(\"sort\", opts.sort);\n\t\tif (opts?.cursor) params.set(\"cursor\", opts.cursor);\n\t\tif (opts?.limit) params.set(\"limit\", String(opts.limit));\n\n\t\tconst qs = params.toString();\n\t\tconst url = `${this.baseUrl}/api/v1/themes${qs ? `?${qs}` : \"\"}`;\n\t\treturn this.fetchJson<MarketplaceThemeSearchResult>(url);\n\t}\n\n\tasync getTheme(id: string): Promise<MarketplaceThemeDetail> {\n\t\tconst url = `${this.baseUrl}/api/v1/themes/${encodeURIComponent(id)}`;\n\t\treturn this.fetchJson<MarketplaceThemeDetail>(url);\n\t}\n\n\tprivate async fetchJson<T>(url: string): Promise<T> {\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\theaders: { Accept: \"application/json\" },\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tthrow new MarketplaceUnavailableError(err);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Marketplace request failed: ${response.status}`;\n\t\t\ttry {\n\t\t\t\tconst body: { error?: string } = await response.json();\n\t\t\t\tif (body.error) errorMessage = body.error;\n\t\t\t} catch {\n\t\t\t\t// use default message\n\t\t\t}\n\t\t\tthrow new MarketplaceError(errorMessage, response.status);\n\t\t}\n\n\t\tconst data: T = await response.json();\n\t\treturn data;\n\t}\n}\n\n// ── Bundle extraction ──────────────────────────────────────────────\n\n/**\n * Extract manifest + code files from a tarball.\n *\n * The tarball is a gzipped tar archive containing:\n * - manifest.json\n * - backend.js\n * - admin.js (optional)\n *\n * We use a minimal tar parser since we only need to read a few small files.\n */\n/**\n * Exported so the experimental registry install handler can reuse the\n * same parse / validate / hash primitive. Despite the file name, this\n * function predates the marketplace-vs-registry split and is generic\n * over plugin bundle tarballs regardless of distribution channel.\n */\n// Aligns with RFC 0001 §\"Bundle size limits\" (256 KiB decompressed,\n// 20 files). Matches `MAX_BUNDLE_SIZE` in cli/commands/bundle-utils.ts\n// (the publish-side cap). We don't import that constant to keep this\n// runtime module independent of the CLI; the two values are\n// load-bearing identical and must stay in sync.\n//\n// Tar adds per-file headers (~512 bytes each) plus directory entries,\n// so the entry count cap is set comfortably above RFC's 20-file limit.\n// Going over either is a strong signal the bundle isn't a legitimate\n// sandboxed plugin.\nconst MAX_DECOMPRESSED_BUNDLE_BYTES = 256 * 1024;\nconst MAX_BUNDLE_TAR_ENTRIES = 32;\n\nexport async function extractBundle(tarballBytes: Uint8Array): Promise<PluginBundle> {\n\t// Decompress fully into memory first, then parse the tar.\n\t// Passing a pipeThrough() stream directly to unpackTar causes a backpressure\n\t// deadlock in workerd: the tar decoder's body-stream pull() needs more\n\t// decompressed data, but the upstream pipe is stalled waiting for the\n\t// decoder's writable side to drain — a circular dependency.\n\tconst decompressedStream = new ReadableStream<Uint8Array>({\n\t\tstart(controller) {\n\t\t\tcontroller.enqueue(tarballBytes);\n\t\t\tcontroller.close();\n\t\t},\n\t}).pipeThrough(createGzipDecoder());\n\n\t// Collect decompressed bytes with a hard cap. A gzip-bomb -- a small\n\t// tarball that decompresses to gigabytes -- otherwise exhausts\n\t// worker / Node memory before we know to reject it. The cap matches\n\t// RFC 0001's publish-time bundle size limit (MAX_DECOMPRESSED_BUNDLE_BYTES);\n\t// anything past that isn't a legitimate sandboxed plugin.\n\tconst reader = decompressedStream.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\twhile (true) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (done) break;\n\t\tif (!value) continue;\n\t\ttotal += value.byteLength;\n\t\tif (total > MAX_DECOMPRESSED_BUNDLE_BYTES) {\n\t\t\ttry {\n\t\t\t\tawait reader.cancel();\n\t\t\t} catch {\n\t\t\t\t// nothing to do\n\t\t\t}\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t`Bundle decompressed size exceeds limit (${MAX_DECOMPRESSED_BUNDLE_BYTES} bytes)`,\n\t\t\t\tundefined,\n\t\t\t\t\"INVALID_BUNDLE\",\n\t\t\t);\n\t\t}\n\t\tchunks.push(value);\n\t}\n\tconst decompressedBytes = new Uint8Array(total);\n\t{\n\t\tlet offset = 0;\n\t\tfor (const chunk of chunks) {\n\t\t\tdecompressedBytes.set(chunk, offset);\n\t\t\toffset += chunk.byteLength;\n\t\t}\n\t}\n\n\tconst decompressed = new ReadableStream<Uint8Array>({\n\t\tstart(controller) {\n\t\t\tcontroller.enqueue(decompressedBytes);\n\t\t\tcontroller.close();\n\t\t},\n\t});\n\n\tconst entries = await unpackTar(decompressed);\n\tif (entries.length > MAX_BUNDLE_TAR_ENTRIES) {\n\t\tthrow new MarketplaceError(\n\t\t\t`Bundle has too many tar entries (${entries.length} > ${MAX_BUNDLE_TAR_ENTRIES})`,\n\t\t\tundefined,\n\t\t\t\"INVALID_BUNDLE\",\n\t\t);\n\t}\n\n\tconst decoder = new TextDecoder();\n\tconst files = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tif (entry.data && entry.header.type === \"file\") {\n\t\t\t// Strip leading ./ prefix that tar tools commonly add\n\t\t\tconst name = entry.header.name.replace(LEADING_DOT_SLASH, \"\");\n\t\t\tfiles.set(name, decoder.decode(entry.data));\n\t\t}\n\t}\n\n\tconst manifestJson = files.get(\"manifest.json\");\n\tconst backendCode = files.get(\"backend.js\");\n\n\tif (!manifestJson) {\n\t\tthrow new MarketplaceError(\n\t\t\t\"Invalid bundle: missing manifest.json\",\n\t\t\tundefined,\n\t\t\t\"INVALID_BUNDLE\",\n\t\t);\n\t}\n\tif (!backendCode) {\n\t\tthrow new MarketplaceError(\"Invalid bundle: missing backend.js\", undefined, \"INVALID_BUNDLE\");\n\t}\n\n\tlet manifest: PluginManifest;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(manifestJson);\n\t\tconst result = pluginManifestSchema.safeParse(parsed);\n\t\tif (!result.success) {\n\t\t\tthrow new MarketplaceError(\n\t\t\t\t\"Invalid bundle: manifest.json failed validation\",\n\t\t\t\tundefined,\n\t\t\t\t\"INVALID_BUNDLE\",\n\t\t\t);\n\t\t}\n\t\tmanifest = reconcileManifestAccess(result.data);\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceError) throw err;\n\t\tthrow new MarketplaceError(\n\t\t\t\"Invalid bundle: malformed manifest.json\",\n\t\t\tundefined,\n\t\t\t\"INVALID_BUNDLE\",\n\t\t);\n\t}\n\n\t// Compute SHA-256 checksum of the tarball for verification\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Uint8Array is a valid BufferSource at runtime; TS lib mismatch\n\tconst hashBuffer = await crypto.subtle.digest(\"SHA-256\", tarballBytes as unknown as BufferSource);\n\tconst hashArray = new Uint8Array(hashBuffer);\n\tconst checksum = Array.from(hashArray, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n\n\treturn {\n\t\tmanifest,\n\t\tbackendCode,\n\t\tadminCode: files.get(\"admin.js\"),\n\t\tchecksum,\n\t};\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/**\n * Generate a stable non-identifying site hash from the site origin.\n * The same origin always produces the same hash, so the marketplace\n * installs table deduplicates correctly per (plugin_id, site_hash).\n */\nasync function generateSiteHash(siteOrigin?: string): Promise<string> {\n\tconst seed = siteOrigin ? `emdash-site:${siteOrigin}` : `emdash-anonymous`;\n\ttry {\n\t\tconst hash = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(seed));\n\t\tconst arr = new Uint8Array(hash);\n\t\treturn Array.from(arr.slice(0, 8), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n\t} catch {\n\t\t// Fallback for environments without crypto.subtle: FNV-1a hash encoded as hex.\n\t\t// Deterministic, uniform distribution, no origin leakage.\n\t\tlet h = 0x811c9dc5;\n\t\tfor (let i = 0; i < seed.length; i++) {\n\t\t\th ^= seed.charCodeAt(i);\n\t\t\th = Math.imul(h, 0x01000193);\n\t\t}\n\t\tconst h2 = h ^ (h >>> 16);\n\t\treturn (h >>> 0).toString(16).padStart(8, \"0\") + (h2 >>> 0).toString(16).padStart(8, \"0\");\n\t}\n}\n\n// ── Factory ────────────────────────────────────────────────────────\n\n/**\n * Create a MarketplaceClient for the given marketplace URL.\n *\n * @param baseUrl - The marketplace API base URL (e.g. \"https://marketplace.emdashcms.com\")\n * @param siteOrigin - The origin of the EmDash site (e.g. \"https://myblog.example.com\").\n * Used to generate a stable, non-identifying site hash for install deduplication.\n */\nexport function createMarketplaceClient(baseUrl: string, siteOrigin?: string): MarketplaceClient {\n\treturn new MarketplaceClientImpl(baseUrl, siteOrigin);\n}\n","/**\n * Plugin Storage Index Management\n *\n * Manages expression indexes on the _plugin_storage table for efficient queries.\n *\n * @see PLUGIN-SYSTEM.md § Plugin Storage > Index Management\n */\n\nimport type { Kysely, RawBuilder } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { jsonExtractExpr, isPostgres } from \"../database/dialect-helpers.js\";\nimport type { Database } from \"../database/types.js\";\nimport {\n\tvalidateJsonFieldName,\n\tvalidatePluginIdentifier,\n\tvalidateStorageCollectionName,\n} from \"../database/validate.js\";\n\n/**\n * Generate a deterministic index name.\n * Unique indexes use a `uidx_` prefix to avoid collisions with regular indexes on the same fields.\n */\nexport function generateIndexName(\n\tpluginId: string,\n\tcollection: string,\n\tfields: string[],\n\toptions?: { unique?: boolean },\n): string {\n\tconst prefix = options?.unique ? \"uidx\" : \"idx\";\n\tconst fieldPart = fields.join(\"_\");\n\t// SQLite index names have no length limit, but keep it reasonable\n\treturn `${prefix}_plugin_${pluginId}_${collection}_${fieldPart}`.substring(0, 128);\n}\n\n/**\n * Generate a Kysely sql expression for creating an expression index.\n *\n * Validates all inputs before interpolation. The collection uses the\n * permissive manifest-key rules rather than SQL-identifier rules — it is\n * stored as opaque text and only reaches SQL inside the generated index\n * name — so kebab-case collections index like any other.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Kysely instance\nexport function generateCreateIndexSql(\n\tdb: Kysely<any>,\n\tpluginId: string,\n\tcollection: string,\n\tfields: string[],\n\toptions?: { unique?: boolean },\n): RawBuilder<unknown> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateStorageCollectionName(collection, \"collection name\");\n\tfor (const field of fields) {\n\t\tvalidateJsonFieldName(field, \"index field name\");\n\t}\n\n\tconst indexName = generateIndexName(pluginId, collection, fields, options);\n\n\t// Build the indexed expressions\n\t// Fields are validated above, safe to interpolate into json path\n\tconst expressions = fields\n\t\t.map((field) => {\n\t\t\tif (isPostgres(db)) {\n\t\t\t\t// Postgres expression indexes need parens around the expression\n\t\t\t\treturn `(${jsonExtractExpr(db, \"data\", field)})`;\n\t\t\t}\n\t\t\treturn jsonExtractExpr(db, \"data\", field);\n\t\t})\n\t\t.join(\", \");\n\n\t// Composite non-partial index: the leading (plugin_id, collection) columns\n\t// scope it per plugin/collection — including unique-index semantics — and\n\t// let it serve the repository's bound-parameter WHERE plus the JSON\n\t// expression ORDER BY. A partial index (WHERE plugin_id = 'x' AND\n\t// collection = 'y') is never chosen by SQLite under bound parameters\n\t// unless ANALYZE has run, and D1 never runs ANALYZE.\n\tconst createKeyword = options?.unique ? \"CREATE UNIQUE INDEX\" : \"CREATE INDEX\";\n\treturn sql`${sql.raw(createKeyword)} IF NOT EXISTS ${sql.ref(indexName)}\n\t\tON _plugin_storage(plugin_id, collection, ${sql.raw(expressions)})\n\t`;\n}\n\n/**\n * Generate a Kysely sql expression for dropping an index.\n *\n * Uses sql.ref() for safe identifier quoting.\n */\nexport function generateDropIndexSql(indexName: string): RawBuilder<unknown> {\n\treturn sql`DROP INDEX IF EXISTS ${sql.ref(indexName)}`;\n}\n\n/**\n * Normalize index declarations to arrays of field arrays\n */\nexport function normalizeIndexes(indexes: Array<string | string[]>): string[][] {\n\treturn indexes.map((index) => (Array.isArray(index) ? index : [index]));\n}\n\n/**\n * Create all declared indexes for a plugin collection\n */\nexport async function createStorageIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollection: string,\n\tindexes: Array<string | string[]>,\n\toptions?: { uniqueIndexes?: Array<string | string[]> },\n): Promise<{\n\tcreated: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst normalized = normalizeIndexes(indexes);\n\tconst uniqueNormalized = options?.uniqueIndexes ? normalizeIndexes(options.uniqueIndexes) : [];\n\tconst uniqueSet = new Set(uniqueNormalized.map((f) => f.join(\",\")));\n\n\t// Deduplicate: if fields appear in both indexes and uniqueIndexes, only create the unique version\n\tconst deduped = normalized.filter((f) => !uniqueSet.has(f.join(\",\")));\n\tconst allEntries: Array<{ fields: string[]; unique: boolean }> = [\n\t\t...deduped.map((fields) => ({ fields, unique: false })),\n\t\t...uniqueNormalized.map((fields) => ({ fields, unique: true })),\n\t];\n\n\tconst created: string[] = [];\n\tconst errors: Array<{ index: string; error: string }> = [];\n\n\tfor (const entry of allEntries) {\n\t\tconst { fields } = entry;\n\t\tconst indexName = generateIndexName(pluginId, collection, fields, { unique: entry.unique });\n\n\t\ttry {\n\t\t\t// Create the index\n\t\t\tconst createSql = generateCreateIndexSql(db, pluginId, collection, fields, {\n\t\t\t\tunique: entry.unique,\n\t\t\t});\n\t\t\tawait createSql.execute(db);\n\n\t\t\t// Track in _plugin_indexes table\n\t\t\tawait db\n\t\t\t\t.insertInto(\"_plugin_indexes\")\n\t\t\t\t.values({\n\t\t\t\t\tplugin_id: pluginId,\n\t\t\t\t\tcollection,\n\t\t\t\t\tindex_name: indexName,\n\t\t\t\t\tfields: JSON.stringify(fields),\n\t\t\t\t})\n\t\t\t\t.onConflict((oc) =>\n\t\t\t\t\toc\n\t\t\t\t\t\t.columns([\"plugin_id\", \"collection\", \"index_name\"])\n\t\t\t\t\t\t.doUpdateSet({ fields: JSON.stringify(fields) }),\n\t\t\t\t)\n\t\t\t\t.execute();\n\n\t\t\tcreated.push(indexName);\n\t\t} catch (error) {\n\t\t\terrors.push({\n\t\t\t\tindex: indexName,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { created, errors };\n}\n\n/**\n * Remove indexes that are no longer declared\n */\nexport async function removeOrphanedIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollection: string,\n\tcurrentIndexes: Array<string | string[]>,\n\toptions?: { uniqueIndexes?: Array<string | string[]> },\n): Promise<{\n\tremoved: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst normalized = normalizeIndexes(currentIndexes);\n\tconst uniqueNormalized = options?.uniqueIndexes ? normalizeIndexes(options.uniqueIndexes) : [];\n\tconst uniqueSet = new Set(uniqueNormalized.map((f) => f.join(\",\")));\n\n\t// Build the set of expected index names (regular + unique with correct prefix)\n\tconst currentIndexNames = new Set<string>();\n\tfor (const fields of normalized) {\n\t\t// If this field set is in both, only the unique version exists (deduplication in create)\n\t\tif (!uniqueSet.has(fields.join(\",\"))) {\n\t\t\tcurrentIndexNames.add(generateIndexName(pluginId, collection, fields));\n\t\t}\n\t}\n\tfor (const fields of uniqueNormalized) {\n\t\tcurrentIndexNames.add(generateIndexName(pluginId, collection, fields, { unique: true }));\n\t}\n\n\t// Get existing indexes from tracking table\n\tconst existingIndexes = await db\n\t\t.selectFrom(\"_plugin_indexes\")\n\t\t.select([\"index_name\"])\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.where(\"collection\", \"=\", collection)\n\t\t.execute();\n\n\tconst removed: string[] = [];\n\tconst errors: Array<{ index: string; error: string }> = [];\n\n\tfor (const { index_name } of existingIndexes) {\n\t\tif (!currentIndexNames.has(index_name)) {\n\t\t\ttry {\n\t\t\t\t// Drop the index\n\t\t\t\tawait generateDropIndexSql(index_name).execute(db);\n\n\t\t\t\t// Remove from tracking table\n\t\t\t\tawait db\n\t\t\t\t\t.deleteFrom(\"_plugin_indexes\")\n\t\t\t\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t\t\t\t.where(\"collection\", \"=\", collection)\n\t\t\t\t\t.where(\"index_name\", \"=\", index_name)\n\t\t\t\t\t.execute();\n\n\t\t\t\tremoved.push(index_name);\n\t\t\t} catch (error) {\n\t\t\t\terrors.push({\n\t\t\t\t\tindex: index_name,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { removed, errors };\n}\n\n/**\n * Sync indexes for a plugin collection (create new, remove old)\n */\nexport async function syncStorageIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n\tcollection: string,\n\tindexes: Array<string | string[]>,\n\toptions?: { uniqueIndexes?: Array<string | string[]> },\n): Promise<{\n\tcreated: string[];\n\tremoved: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst [createResult, removeResult] = await Promise.all([\n\t\tcreateStorageIndexes(db, pluginId, collection, indexes, options),\n\t\tremoveOrphanedIndexes(db, pluginId, collection, indexes, options),\n\t]);\n\n\treturn {\n\t\tcreated: createResult.created,\n\t\tremoved: removeResult.removed,\n\t\terrors: [...createResult.errors, ...removeResult.errors],\n\t};\n}\n\n/**\n * Materialize the storage indexes a set of plugins declare in their\n * manifests. Failures are logged per collection and never thrown — a missing\n * index affects query performance, not correctness, so it must not fail an\n * install or a scheduler tick.\n */\nexport async function syncDeclaredStorageIndexes(\n\tdb: Kysely<Database>,\n\tplugins: Array<{\n\t\tid: string;\n\t\tstorage?: Record<\n\t\t\tstring,\n\t\t\t{ indexes: Array<string | string[]>; uniqueIndexes?: Array<string | string[]> }\n\t\t>;\n\t}>,\n): Promise<void> {\n\tfor (const plugin of plugins) {\n\t\tfor (const [collection, config] of Object.entries(plugin.storage ?? {})) {\n\t\t\ttry {\n\t\t\t\tconst result = await syncStorageIndexes(db, plugin.id, collection, config.indexes, {\n\t\t\t\t\tuniqueIndexes: config.uniqueIndexes,\n\t\t\t\t});\n\t\t\t\tfor (const failure of result.errors) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`[plugins] Failed to sync storage index ${failure.index} for ${plugin.id}/${collection}: ${failure.error}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[plugins] Failed to sync storage indexes for ${plugin.id}/${collection}:`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Remove all indexes for a plugin\n */\nexport async function removeAllPluginIndexes(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n): Promise<{\n\tremoved: string[];\n\terrors: Array<{ index: string; error: string }>;\n}> {\n\tconst existingIndexes = await db\n\t\t.selectFrom(\"_plugin_indexes\")\n\t\t.select([\"index_name\", \"collection\"])\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.execute();\n\n\tconst removed: string[] = [];\n\tconst errors: Array<{ index: string; error: string }> = [];\n\n\tfor (const { index_name } of existingIndexes) {\n\t\ttry {\n\t\t\tawait generateDropIndexSql(index_name).execute(db);\n\t\t\tremoved.push(index_name);\n\t\t} catch (error) {\n\t\t\terrors.push({\n\t\t\t\tindex: index_name,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\n\t// Clean up tracking table\n\tawait db.deleteFrom(\"_plugin_indexes\").where(\"plugin_id\", \"=\", pluginId).execute();\n\n\treturn { removed, errors };\n}\n\n/**\n * Get current index status for a plugin\n */\nexport async function getPluginIndexStatus(\n\tdb: Kysely<Database>,\n\tpluginId: string,\n): Promise<\n\tArray<{\n\t\tcollection: string;\n\t\tindexName: string;\n\t\tfields: string[];\n\t\tcreatedAt: string;\n\t}>\n> {\n\tconst rows = await db\n\t\t.selectFrom(\"_plugin_indexes\")\n\t\t.select([\"collection\", \"index_name\", \"fields\", \"created_at\"])\n\t\t.where(\"plugin_id\", \"=\", pluginId)\n\t\t.execute();\n\n\treturn rows.map((row) => {\n\t\tconst parsed: unknown = JSON.parse(row.fields);\n\t\tconst fields = Array.isArray(parsed)\n\t\t\t? parsed.filter((f): f is string => typeof f === \"string\")\n\t\t\t: [];\n\t\treturn {\n\t\t\tcollection: row.collection,\n\t\t\tindexName: row.index_name,\n\t\t\tfields,\n\t\t\tcreatedAt: row.created_at,\n\t\t};\n\t});\n}\n","/**\n * Marketplace plugin handlers\n *\n * Business logic for installing, updating, uninstalling, and checking\n * updates for marketplace plugins. Routes are thin wrappers around these.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { validatePluginIdentifier } from \"../../database/validate.js\";\nimport { pluginManifestSchema, reconcileManifestAccess } from \"../../plugins/manifest-schema.js\";\nimport { normalizeManifestRoute } from \"../../plugins/manifest-schema.js\";\nimport {\n\tcreateMarketplaceClient,\n\tMarketplaceError,\n\tMarketplaceUnavailableError,\n\ttype MarketplaceClient,\n\ttype MarketplacePluginDetail,\n\ttype MarketplaceSearchOpts,\n\ttype MarketplaceThemeSearchOpts,\n\ttype MarketplaceVersionSummary,\n\ttype PluginBundle,\n} from \"../../plugins/marketplace.js\";\nimport type { SandboxRunner } from \"../../plugins/sandbox/types.js\";\nimport { PluginStateRepository } from \"../../plugins/state.js\";\nimport {\n\tremoveAllPluginIndexes,\n\tsyncDeclaredStorageIndexes,\n} from \"../../plugins/storage-indexes.js\";\nimport { normalizeCapabilities } from \"../../plugins/types.js\";\nimport type { PluginManifest } from \"../../plugins/types.js\";\nimport { EmDashStorageError } from \"../../storage/types.js\";\nimport type { Storage } from \"../../storage/types.js\";\nimport type { ApiResult } from \"../types.js\";\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface MarketplaceInstallResult {\n\tpluginId: string;\n\tversion: string;\n\tcapabilities: string[];\n}\n\nexport interface MarketplaceUpdateResult {\n\tpluginId: string;\n\toldVersion: string;\n\tnewVersion: string;\n\tcapabilityChanges: {\n\t\tadded: string[];\n\t\tremoved: string[];\n\t};\n\trouteVisibilityChanges?: {\n\t\tnewlyPublic: string[];\n\t};\n}\n\nexport interface MarketplaceUpdateCheck {\n\tpluginId: string;\n\tinstalled: string;\n\tlatest: string;\n\thasUpdate: boolean;\n\thasCapabilityChanges: boolean;\n\tcapabilityChanges?: {\n\t\tadded: string[];\n\t\tremoved: string[];\n\t};\n\thasRouteVisibilityChanges: boolean;\n\trouteVisibilityChanges?: {\n\t\tnewlyPublic: string[];\n\t};\n}\n\nexport interface MarketplaceUninstallResult {\n\tpluginId: string;\n\tdataDeleted: boolean;\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/** Semver-like pattern: digits, dots, hyphens, plus signs (e.g. 1.0.0, 1.0.0-beta.1) */\nconst VERSION_PATTERN = /^[a-z0-9][a-z0-9._+-]*$/i;\n\nfunction validateVersion(version: string): void {\n\tif (version.includes(\"..\")) throw new Error(\"Invalid version format\");\n\tif (!VERSION_PATTERN.test(version)) {\n\t\tthrow new Error(\"Invalid version format\");\n\t}\n}\n\nfunction getClient(\n\tmarketplaceUrl: string | undefined,\n\tsiteOrigin?: string,\n): MarketplaceClient | null {\n\tif (!marketplaceUrl) return null;\n\treturn createMarketplaceClient(marketplaceUrl, siteOrigin);\n}\n\nexport function diffCapabilities(\n\toldCaps: string[],\n\tnewCaps: string[],\n): { added: string[]; removed: string[] } {\n\t// Normalize both sides before diffing so that an installed v1 manifest\n\t// declaring `read:content` and an upgrade v2 manifest declaring\n\t// `content:read` produces an empty diff — users should not see a\n\t// spurious \"capability changed\" prompt for a pure rename.\n\tconst oldNorm = normalizeCapabilities(oldCaps);\n\tconst newNorm = normalizeCapabilities(newCaps);\n\tconst oldSet = new Set(oldNorm);\n\tconst newSet = new Set(newNorm);\n\treturn {\n\t\tadded: newNorm.filter((c) => !oldSet.has(c)),\n\t\tremoved: oldNorm.filter((c) => !newSet.has(c)),\n\t};\n}\n\n/**\n * Diff route visibility between two manifests.\n * Returns routes that changed from private to public (newly exposed).\n */\nexport function diffRouteVisibility(\n\toldManifest: PluginManifest | undefined,\n\tnewManifest: PluginManifest,\n): { newlyPublic: string[] } {\n\tconst oldPublicRoutes = new Set<string>();\n\tif (oldManifest) {\n\t\tfor (const entry of oldManifest.routes) {\n\t\t\tconst normalized = normalizeManifestRoute(entry);\n\t\t\tif (normalized.public === true) {\n\t\t\t\toldPublicRoutes.add(normalized.name);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst newlyPublic: string[] = [];\n\tfor (const entry of newManifest.routes) {\n\t\tconst normalized = normalizeManifestRoute(entry);\n\t\tif (normalized.public === true && !oldPublicRoutes.has(normalized.name)) {\n\t\t\tnewlyPublic.push(normalized.name);\n\t\t}\n\t}\n\n\treturn { newlyPublic };\n}\n\nasync function resolveVersionMetadata(\n\tclient: MarketplaceClient,\n\tpluginId: string,\n\tpluginDetail: MarketplacePluginDetail,\n\tversion: string,\n): Promise<MarketplaceVersionSummary | null> {\n\tif (pluginDetail.latestVersion?.version === version) {\n\t\treturn {\n\t\t\tversion: pluginDetail.latestVersion.version,\n\t\t\tminEmDashVersion: pluginDetail.latestVersion.minEmDashVersion,\n\t\t\tbundleSize: pluginDetail.latestVersion.bundleSize,\n\t\t\tchecksum: pluginDetail.latestVersion.checksum,\n\t\t\tchangelog: pluginDetail.latestVersion.changelog,\n\t\t\tcapabilities: pluginDetail.latestVersion.capabilities,\n\t\t\tstatus: pluginDetail.latestVersion.status,\n\t\t\tauditVerdict: pluginDetail.latestVersion.audit?.verdict ?? null,\n\t\t\timageAuditVerdict: pluginDetail.latestVersion.imageAudit?.verdict ?? null,\n\t\t\tpublishedAt: pluginDetail.latestVersion.publishedAt,\n\t\t};\n\t}\n\n\tconst versions = await client.getVersions(pluginId);\n\treturn versions.find((v) => v.version === version) ?? null;\n}\n\nfunction validateBundleIdentity(\n\tbundle: PluginBundle,\n\tpluginId: string,\n\tversion: string,\n): ApiResult<never> | null {\n\tif (bundle.manifest.id !== pluginId) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"MANIFEST_MISMATCH\",\n\t\t\t\tmessage: `Bundle manifest ID (${bundle.manifest.id}) does not match requested plugin (${pluginId})`,\n\t\t\t},\n\t\t};\n\t}\n\n\tif (bundle.manifest.version !== version) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"MANIFEST_VERSION_MISMATCH\",\n\t\t\t\tmessage: `Bundle manifest version (${bundle.manifest.version}) does not match requested version (${version})`,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn null;\n}\n\n/** Store a plugin bundle's files in site-local R2 storage */\n/**\n * Storage source for an installed plugin bundle. Determines the R2\n * key prefix and is used to keep marketplace and registry installs\n * cleanly separated in object listings.\n */\nexport type PluginBundleSource = \"marketplace\" | \"registry\";\n\nfunction bundlePrefix(source: PluginBundleSource, pluginId: string, version: string): string {\n\treturn `${source}/${pluginId}/${version}`;\n}\n\nexport async function storeBundleInR2(\n\tstorage: Storage,\n\tpluginId: string,\n\tversion: string,\n\tbundle: PluginBundle,\n\tsource: PluginBundleSource = \"marketplace\",\n): Promise<void> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateVersion(version);\n\tconst prefix = bundlePrefix(source, pluginId, version);\n\n\t// Store manifest\n\tawait storage.upload({\n\t\tkey: `${prefix}/manifest.json`,\n\t\tbody: new TextEncoder().encode(JSON.stringify(bundle.manifest)),\n\t\tcontentType: \"application/json\",\n\t});\n\n\t// Store backend code\n\tawait storage.upload({\n\t\tkey: `${prefix}/backend.js`,\n\t\tbody: new TextEncoder().encode(bundle.backendCode),\n\t\tcontentType: \"application/javascript\",\n\t});\n\n\t// Store admin code if present\n\tif (bundle.adminCode) {\n\t\tawait storage.upload({\n\t\t\tkey: `${prefix}/admin.js`,\n\t\t\tbody: new TextEncoder().encode(bundle.adminCode),\n\t\t\tcontentType: \"application/javascript\",\n\t\t});\n\t}\n}\n\n/** Read a ReadableStream to string */\nasync function streamToText(stream: ReadableStream<Uint8Array>): Promise<string> {\n\treturn new Response(stream).text();\n}\n\n/**\n * Load a plugin bundle from site-local R2 storage.\n *\n * `source` selects the R2 key prefix: marketplace plugins are stored\n * under `marketplace/<id>/<version>/`, registry plugins under\n * `registry/<id>/<version>/`. Defaults to `\"marketplace\"` for\n * backwards compatibility with pre-registry call sites.\n */\nexport async function loadBundleFromR2(\n\tstorage: Storage,\n\tpluginId: string,\n\tversion: string,\n\tsource: PluginBundleSource = \"marketplace\",\n): Promise<{ manifest: PluginManifest; backendCode: string; adminCode?: string } | null> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateVersion(version);\n\tconst prefix = bundlePrefix(source, pluginId, version);\n\n\ttry {\n\t\tconst manifestResult = await storage.download(`${prefix}/manifest.json`);\n\t\tconst backendResult = await storage.download(`${prefix}/backend.js`);\n\n\t\tconst manifestText = await streamToText(manifestResult.body);\n\t\tconst backendCode = await streamToText(backendResult.body);\n\t\tconst parsed: unknown = JSON.parse(manifestText);\n\t\tconst result = pluginManifestSchema.safeParse(parsed);\n\t\tif (!result.success) return null;\n\t\tconst manifest = reconcileManifestAccess(result.data);\n\n\t\t// Try to load admin code (optional)\n\t\tlet adminCode: string | undefined;\n\t\ttry {\n\t\t\tconst adminResult = await storage.download(`${prefix}/admin.js`);\n\t\t\tadminCode = await streamToText(adminResult.body);\n\t\t} catch {\n\t\t\t// admin.js is optional\n\t\t}\n\n\t\treturn { manifest, backendCode, adminCode };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Delete a plugin bundle from site-local R2 storage */\nexport async function deleteBundleFromR2(\n\tstorage: Storage,\n\tpluginId: string,\n\tversion: string,\n\tsource: PluginBundleSource = \"marketplace\",\n): Promise<void> {\n\tvalidatePluginIdentifier(pluginId, \"plugin ID\");\n\tvalidateVersion(version);\n\tconst prefix = bundlePrefix(source, pluginId, version);\n\tconst files = [\"manifest.json\", \"backend.js\", \"admin.js\"];\n\n\tfor (const file of files) {\n\t\ttry {\n\t\t\tawait storage.delete(`${prefix}/${file}`);\n\t\t} catch {\n\t\t\t// Ignore missing files\n\t\t}\n\t}\n}\n\n// ── Install ────────────────────────────────────────────────────────\n\nexport async function handleMarketplaceInstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tmarketplaceUrl: string | undefined,\n\tpluginId: string,\n\topts?: {\n\t\tversion?: string;\n\t\tconfiguredPluginIds?: Set<string>;\n\t\tsiteOrigin?: string;\n\t\t/**\n\t\t * When true, sandbox: false bypass mode is active. The sandbox runner\n\t\t * is the noop runner (isAvailable() === false) but the runtime will\n\t\t * load the marketplace plugin in-process via syncMarketplacePlugins().\n\t\t * Skip the SANDBOX_NOT_AVAILABLE gate so the install can proceed.\n\t\t */\n\t\tsandboxBypassed?: boolean;\n\t\tconfirmMcpTools?: boolean;\n\t},\n): Promise<ApiResult<MarketplaceInstallResult>> {\n\tconst client = getClient(marketplaceUrl, opts?.siteOrigin);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"MARKETPLACE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Marketplace is not configured\",\n\t\t\t},\n\t\t};\n\t}\n\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"STORAGE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Storage is required for marketplace plugin installation\",\n\t\t\t},\n\t\t};\n\t}\n\n\t// Sandbox availability check: skip when sandbox: false bypass is active.\n\t// The runtime's syncMarketplacePlugins() will load the plugin in-process.\n\tif (!opts?.sandboxBypassed && (!sandboxRunner || !sandboxRunner.isAvailable())) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"SANDBOX_NOT_AVAILABLE\",\n\t\t\t\tmessage: \"Sandbox runner is required for marketplace plugins\",\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\t// Check if already installed\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (existing && existing.source === \"marketplace\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ALREADY_INSTALLED\",\n\t\t\t\t\tmessage: `Plugin ${pluginId} is already installed`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Block installation if a configured (trusted) plugin with the same ID exists.\n\t\t// Without this check, the sandboxed plugin could shadow the trusted plugin's\n\t\t// route handlers while auth decisions are made against the trusted plugin's metadata.\n\t\tif (opts?.configuredPluginIds?.has(pluginId)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"PLUGIN_ID_CONFLICT\",\n\t\t\t\t\tmessage: `Cannot install marketplace plugin \"${pluginId}\" — a configured plugin with the same ID already exists`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fetch plugin detail from marketplace\n\t\tconst pluginDetail = await client.getPlugin(pluginId);\n\t\tconst version = opts?.version ?? pluginDetail.latestVersion?.version;\n\t\tif (!version) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: `No published versions found for plugin ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst versionMetadata = await resolveVersionMetadata(client, pluginId, pluginDetail, version);\n\t\tif (!versionMetadata) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: `Version ${version} was not found for plugin ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Block installation of plugins that haven't passed audit.\n\t\t// Both \"fail\" (explicitly malicious) and \"warn\" (audit error or\n\t\t// inconclusive) are non-installable — only \"pass\" or null (no audit\n\t\t// ran) are allowed through.\n\t\tif (versionMetadata.auditVerdict === \"fail\" || versionMetadata.auditVerdict === \"warn\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AUDIT_FAILED\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\tversionMetadata.auditVerdict === \"fail\"\n\t\t\t\t\t\t\t? \"Plugin failed security audit and cannot be installed\"\n\t\t\t\t\t\t\t: \"Plugin audit was inconclusive and cannot be installed until reviewed\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Download and extract bundle\n\t\tconst bundle = await client.downloadBundle(pluginId, version);\n\n\t\t// Verify checksum matches marketplace-published checksum\n\t\tif (versionMetadata.checksum && bundle.checksum !== versionMetadata.checksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CHECKSUM_MISMATCH\",\n\t\t\t\t\tmessage: \"Bundle checksum does not match marketplace record. Download may be corrupted.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst bundleIdentityError = validateBundleIdentity(bundle, pluginId, version);\n\t\tif (bundleIdentityError) return bundleIdentityError;\n\n\t\tif ((bundle.manifest.mcp?.tools.length ?? 0) > 0 && !opts?.confirmMcpTools) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\tmessage: \"Plugin MCP tools require explicit consent\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tmcpTools: bundle.manifest.mcp?.tools.map(\n\t\t\t\t\t\t\t({ inputSchema: _, outputSchema: __, ...tool }) => tool,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Store bundle in site-local R2\n\t\tawait storeBundleInR2(storage, pluginId, version, bundle);\n\n\t\t// Write plugin state\n\t\tawait stateRepo.upsert(pluginId, version, \"active\", {\n\t\t\tsource: \"marketplace\",\n\t\t\tmarketplaceVersion: version,\n\t\t\tdisplayName: pluginDetail.name,\n\t\t\tdescription: pluginDetail.description ?? undefined,\n\t\t});\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\t// Fire-and-forget install stat\n\t\tclient.reportInstall(pluginId, version).catch(() => {\n\t\t\t// Intentional: never fails the install\n\t\t});\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tpluginId,\n\t\t\t\tversion,\n\t\t\t\tcapabilities: bundle.manifest.capabilities,\n\t\t\t},\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MARKETPLACE_UNAVAILABLE\",\n\t\t\t\t\tmessage: \"Plugin marketplace is currently unavailable\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"MARKETPLACE_ERROR\",\n\t\t\t\t\tmessage: err.message,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof EmDashStorageError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"STORAGE_ERROR\",\n\t\t\t\t\tmessage: \"Storage error while installing plugin\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err && typeof err === \"object\" && \"code\" in err) {\n\t\t\tconst code = (err as { code?: unknown }).code;\n\t\t\tif (typeof code === \"string\" && code.trim()) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode,\n\t\t\t\t\t\tmessage: \"Failed to install plugin from marketplace\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tconsole.error(\"Failed to install marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"INSTALL_FAILED\",\n\t\t\t\tmessage: \"Failed to install plugin from marketplace\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update ─────────────────────────────────────────────────────────\n\nexport async function handleMarketplaceUpdate(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tmarketplaceUrl: string | undefined,\n\tpluginId: string,\n\topts?: {\n\t\tversion?: string;\n\t\tconfirmCapabilityChanges?: boolean;\n\t\tconfirmRouteVisibilityChanges?: boolean;\n\t\tconfirmMcpTools?: boolean;\n\t\t/**\n\t\t * When true, sandbox: false bypass mode is active. The sandbox runner\n\t\t * is the noop runner (isAvailable() === false) but the runtime will\n\t\t * load the marketplace plugin in-process via syncMarketplacePlugins().\n\t\t * Skip the SANDBOX_NOT_AVAILABLE gate so the update can proceed.\n\t\t */\n\t\tsandboxBypassed?: boolean;\n\t},\n): Promise<ApiResult<MarketplaceUpdateResult>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"STORAGE_NOT_CONFIGURED\", message: \"Storage is required\" },\n\t\t};\n\t}\n\t// Sandbox availability check: skip when sandbox: false bypass is active.\n\t// The runtime's syncMarketplacePlugins() will load the plugin in-process.\n\tif (!opts?.sandboxBypassed && (!sandboxRunner || !sandboxRunner.isAvailable())) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"SANDBOX_NOT_AVAILABLE\", message: \"Sandbox runner is required\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"marketplace\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `No marketplace plugin found: ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst oldVersion = existing.marketplaceVersion ?? existing.version;\n\n\t\t// Get target version\n\t\tconst pluginDetail = await client.getPlugin(pluginId);\n\t\tconst newVersion = opts?.version ?? pluginDetail.latestVersion?.version;\n\t\tif (!newVersion) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NO_VERSION\", message: \"No newer version available\" },\n\t\t\t};\n\t\t}\n\n\t\tif (newVersion === oldVersion) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"ALREADY_UP_TO_DATE\", message: \"Plugin is already up to date\" },\n\t\t\t};\n\t\t}\n\n\t\tconst versionMetadata = await resolveVersionMetadata(\n\t\t\tclient,\n\t\t\tpluginId,\n\t\t\tpluginDetail,\n\t\t\tnewVersion,\n\t\t);\n\t\tif (!versionMetadata) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: `Version ${newVersion} was not found for plugin ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Download new bundle\n\t\tconst bundle = await client.downloadBundle(pluginId, newVersion);\n\n\t\t// Verify checksum matches marketplace-published checksum for this version\n\t\tif (versionMetadata.checksum && bundle.checksum !== versionMetadata.checksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CHECKSUM_MISMATCH\",\n\t\t\t\t\tmessage: \"Bundle checksum does not match marketplace record. Download may be corrupted.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst bundleIdentityError = validateBundleIdentity(bundle, pluginId, newVersion);\n\t\tif (bundleIdentityError) return bundleIdentityError;\n\n\t\t// Diff capabilities and route visibility against old version\n\t\tconst oldBundle = await loadBundleFromR2(storage, pluginId, oldVersion);\n\t\tconst oldCaps = oldBundle?.manifest.capabilities ?? [];\n\t\tconst capabilityChanges = diffCapabilities(oldCaps, bundle.manifest.capabilities);\n\t\tconst hasEscalation = capabilityChanges.added.length > 0;\n\n\t\t// If capabilities escalated, require explicit confirmation\n\t\tif (hasEscalation && !opts?.confirmCapabilityChanges) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CAPABILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update requires new capabilities\",\n\t\t\t\t\tdetails: { capabilityChanges },\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Diff route visibility — routes going from private to public are a\n\t\t// security-sensitive change that exposes unauthenticated endpoints.\n\t\tconst routeVisibilityChanges = diffRouteVisibility(oldBundle?.manifest, bundle.manifest);\n\t\tconst hasNewPublicRoutes = routeVisibilityChanges.newlyPublic.length > 0;\n\n\t\tif (hasNewPublicRoutes && !opts?.confirmRouteVisibilityChanges) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ROUTE_VISIBILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update exposes new public (unauthenticated) routes\",\n\t\t\t\t\tdetails: { routeVisibilityChanges, capabilityChanges },\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst oldMcpTools = [...(oldBundle?.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tconst newMcpTools = [...(bundle.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tif (JSON.stringify(oldMcpTools) !== JSON.stringify(newMcpTools) && !opts?.confirmMcpTools) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\tmessage: \"Plugin update changes its MCP tools\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tmcpTools: newMcpTools.map(({ inputSchema: _, outputSchema: __, ...tool }) => tool),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Store new bundle\n\t\tawait storeBundleInR2(storage, pluginId, newVersion, bundle);\n\n\t\t// Update state\n\t\tawait stateRepo.upsert(pluginId, newVersion, \"active\", {\n\t\t\tsource: \"marketplace\",\n\t\t\tmarketplaceVersion: newVersion,\n\t\t\tdisplayName: pluginDetail.name,\n\t\t\tdescription: pluginDetail.description ?? undefined,\n\t\t\tmcpToolsEnabled: false,\n\t\t\tmcpToolsConsent: null,\n\t\t});\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\t// Clean up old bundle from R2 (best-effort)\n\t\tdeleteBundleFromR2(storage, pluginId, oldVersion).catch(() => {});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tpluginId,\n\t\t\t\toldVersion,\n\t\t\t\tnewVersion,\n\t\t\t\tcapabilityChanges,\n\t\t\t\trouteVisibilityChanges: hasNewPublicRoutes ? routeVisibilityChanges : undefined,\n\t\t\t},\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: err.code ?? \"MARKETPLACE_ERROR\", message: err.message },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to update marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_FAILED\", message: \"Failed to update plugin\" },\n\t\t};\n\t}\n}\n\n// ── Uninstall ──────────────────────────────────────────────────────\n\nexport async function handleMarketplaceUninstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tpluginId: string,\n\topts?: { deleteData?: boolean },\n): Promise<ApiResult<MarketplaceUninstallResult>> {\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"marketplace\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `No marketplace plugin found: ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst version = existing.marketplaceVersion ?? existing.version;\n\n\t\t// Delete bundle from site R2\n\t\tif (storage) {\n\t\t\tawait deleteBundleFromR2(storage, pluginId, version);\n\t\t}\n\n\t\t// Optionally delete plugin storage data\n\t\tlet dataDeleted = false;\n\t\tif (opts?.deleteData) {\n\t\t\ttry {\n\t\t\t\tawait db.deleteFrom(\"_plugin_storage\").where(\"plugin_id\", \"=\", pluginId).execute();\n\t\t\t\tdataDeleted = true;\n\t\t\t} catch {\n\t\t\t\t// Plugin storage table may not have data for this plugin\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tawait removeAllPluginIndexes(db, pluginId);\n\t\t} catch {\n\t\t\t// Nothing to drop, or tracking table predates the feature\n\t\t}\n\n\t\t// Delete state row\n\t\tawait stateRepo.delete(pluginId);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: { pluginId, dataDeleted },\n\t\t};\n\t} catch (err) {\n\t\tconsole.error(\"Failed to uninstall marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"UNINSTALL_FAILED\",\n\t\t\t\tmessage: \"Failed to uninstall plugin\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update check ───────────────────────────────────────────────────\n\nexport async function handleMarketplaceUpdateCheck(\n\tdb: Kysely<Database>,\n\tmarketplaceUrl: string | undefined,\n): Promise<ApiResult<{ items: MarketplaceUpdateCheck[] }>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst marketplacePlugins = await stateRepo.getMarketplacePlugins();\n\n\t\tconst items: MarketplaceUpdateCheck[] = [];\n\n\t\tfor (const plugin of marketplacePlugins) {\n\t\t\ttry {\n\t\t\t\tconst detail = await client.getPlugin(plugin.pluginId);\n\t\t\t\tconst latest = detail.latestVersion?.version;\n\t\t\t\tconst installed = plugin.marketplaceVersion ?? plugin.version;\n\n\t\t\t\tif (!latest) continue;\n\n\t\t\t\tconst hasUpdate = latest !== installed;\n\t\t\t\tlet capabilityChanges: { added: string[]; removed: string[] } | undefined;\n\t\t\t\tlet hasCapabilityChanges = false;\n\n\t\t\t\tif (hasUpdate && detail.latestVersion) {\n\t\t\t\t\tconst oldCaps = detail.capabilities ?? [];\n\t\t\t\t\tconst newCaps = detail.latestVersion.capabilities ?? [];\n\t\t\t\t\tcapabilityChanges = diffCapabilities(oldCaps, newCaps);\n\t\t\t\t\thasCapabilityChanges =\n\t\t\t\t\t\tcapabilityChanges.added.length > 0 || capabilityChanges.removed.length > 0;\n\t\t\t\t}\n\n\t\t\t\titems.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tinstalled,\n\t\t\t\t\tlatest: latest ?? installed,\n\t\t\t\t\thasUpdate,\n\t\t\t\t\thasCapabilityChanges,\n\t\t\t\t\tcapabilityChanges: hasCapabilityChanges ? capabilityChanges : undefined,\n\t\t\t\t\t// Route visibility changes require downloading both bundles to compare\n\t\t\t\t\t// manifests, which is too expensive for a preview check. The actual\n\t\t\t\t\t// enforcement happens at update time in handleMarketplaceUpdate.\n\t\t\t\t\thasRouteVisibilityChanges: false,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\t// Skip plugins that can't be checked (marketplace down, plugin delisted)\n\t\t\t\tconsole.warn(`Failed to check updates for ${plugin.pluginId}:`, err);\n\t\t\t}\n\t\t}\n\n\t\treturn { success: true, data: { items } };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to check marketplace updates:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_CHECK_FAILED\", message: \"Failed to check for updates\" },\n\t\t};\n\t}\n}\n\n// ── Proxy ──────────────────────────────────────────────────────────\n\nexport async function handleMarketplaceSearch(\n\tmarketplaceUrl: string | undefined,\n\tquery?: string,\n\topts?: MarketplaceSearchOpts,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.search(query, opts);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to search marketplace:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"SEARCH_FAILED\", message: \"Failed to search marketplace\" },\n\t\t};\n\t}\n}\n\nexport async function handleMarketplaceGetPlugin(\n\tmarketplaceUrl: string | undefined,\n\tpluginId: string,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.getPlugin(pluginId);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceError && err.status === 404) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `Plugin not found: ${pluginId}` },\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to get marketplace plugin:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"GET_PLUGIN_FAILED\", message: \"Failed to get plugin details\" },\n\t\t};\n\t}\n}\n\n// ── Theme proxy handlers ──────────────────────────────────────────\n\nexport async function handleThemeSearch(\n\tmarketplaceUrl: string | undefined,\n\tquery?: string,\n\topts?: MarketplaceThemeSearchOpts,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.searchThemes(query, opts);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to search themes:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"THEME_SEARCH_FAILED\", message: \"Failed to search themes\" },\n\t\t};\n\t}\n}\n\nexport async function handleThemeGetDetail(\n\tmarketplaceUrl: string | undefined,\n\tthemeId: string,\n): Promise<ApiResult<unknown>> {\n\tconst client = getClient(marketplaceUrl);\n\tif (!client) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst result = await client.getTheme(themeId);\n\t\treturn { success: true, data: result };\n\t} catch (err) {\n\t\tif (err instanceof MarketplaceError && err.status === 404) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `Theme not found: ${themeId}` },\n\t\t\t};\n\t\t}\n\t\tif (err instanceof MarketplaceUnavailableError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"MARKETPLACE_UNAVAILABLE\", message: \"Marketplace is unavailable\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"Failed to get marketplace theme:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"GET_THEME_FAILED\", message: \"Failed to get theme details\" },\n\t\t};\n\t}\n}\n","import { resolveAndValidateExternalUrlTarget, SsrfError } from \"../security/ssrf.js\";\n\nconst TRAILING_DOT = /\\.+$/;\nconst HEADER_END = new Uint8Array([13, 10, 13, 10]);\nconst CRLF = new Uint8Array([13, 10]);\nconst STATUS_LINE_RE = /^HTTP\\/1\\.[01] ([2-5][0-9]{2})(?: .*)?$/;\nconst HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;\nconst FOLDED_HEADER_RE = /^[ \\t]/;\nconst CONTENT_LENGTH_RE = /^(0|[1-9][0-9]*)$/;\nconst CHUNK_SIZE_RE = /^[0-9A-Fa-f]+$/;\nconst MAX_HEADER_BYTES = 32 * 1024;\nconst LOCALHOST_HOSTNAMES = new Set([\n\t\"localhost\",\n\t\"localhost.localdomain\",\n\t\"ip6-localhost\",\n\t\"ip6-loopback\",\n]);\n\nexport interface RegistryArtifactTransportInput {\n\turl: URL;\n\tallowedAddresses: readonly string[];\n\tsignal: AbortSignal;\n\tmaxResponseBytes: number;\n}\n\nexport interface RegistryArtifactTransport {\n\tfetch(input: RegistryArtifactTransportInput): Promise<{\n\t\tresponse: Response;\n\t\tconnectedAddress: string;\n\t}>;\n}\n\nexport interface RegistryArtifactFetchOptions {\n\tsignal: AbortSignal;\n\tmaxResponseBytes: number;\n}\n\nlet defaultTransport: RegistryArtifactTransport | null = null;\n\nexport function setDefaultRegistryArtifactTransport(\n\ttransport: RegistryArtifactTransport | null,\n): RegistryArtifactTransport | null {\n\tconst previous = defaultTransport;\n\tdefaultTransport = transport;\n\treturn previous;\n}\n\nfunction isLocalhostHostname(hostname: string): boolean {\n\tconst normalized = hostname.toLowerCase().replace(TRAILING_DOT, \"\");\n\treturn (\n\t\tLOCALHOST_HOSTNAMES.has(normalized) ||\n\t\tnormalized.endsWith(\".localhost\") ||\n\t\tnormalized === \"127.0.0.1\" ||\n\t\tnormalized === \"::1\" ||\n\t\tnormalized === \"[::1]\" ||\n\t\tnormalized.startsWith(\"::ffff:127.\") ||\n\t\tnormalized.startsWith(\"::ffff:7f00:\")\n\t);\n}\n\nasync function resolveSafeArtifactTarget(urlString: string): Promise<{\n\turl: URL;\n\taddresses: readonly string[];\n}> {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(urlString);\n\t} catch {\n\t\tthrow new Error(`Invalid artifact URL: ${urlString}`);\n\t}\n\tif (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n\t\tthrow new Error(`Artifact URL protocol not allowed: ${url.protocol}`);\n\t}\n\tif (url.username || url.password) {\n\t\tthrow new Error(\"Artifact URL must not contain embedded credentials\");\n\t}\n\n\tconst rawHostname = url.hostname.toLowerCase().replace(TRAILING_DOT, \"\");\n\tconst hostname = stripIpv6Brackets(rawHostname);\n\tconst localhost = isLocalhostHostname(hostname);\n\n\tif (!import.meta.env.DEV) {\n\t\tif (url.protocol === \"http:\") {\n\t\t\tthrow new Error(\"Artifact URL must use https\");\n\t\t}\n\t\tif (localhost) {\n\t\t\tthrow new Error(`Artifact URL points to localhost: ${hostname}`);\n\t\t}\n\t} else if (url.protocol === \"http:\" && !localhost) {\n\t\tthrow new Error(\"Artifact URL must use https (http allowed only for localhost in dev)\");\n\t}\n\n\tif (localhost) {\n\t\treturn { url, addresses: [] };\n\t}\n\n\ttry {\n\t\treturn await resolveAndValidateExternalUrlTarget(url.href);\n\t} catch (error) {\n\t\tif (error instanceof SsrfError) {\n\t\t\tthrow new Error(`Artifact URL rejected: ${error.message}`, { cause: error });\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nexport async function assertSafeArtifactUrl(urlString: string): Promise<URL> {\n\treturn (await resolveSafeArtifactTarget(urlString)).url;\n}\n\nexport async function fetchRegistryArtifactUrl(\n\turlString: string,\n\toptions: RegistryArtifactFetchOptions,\n): Promise<Response> {\n\tif (!Number.isSafeInteger(options.maxResponseBytes) || options.maxResponseBytes < 0) {\n\t\tthrow new TypeError(\"Registry artifact response limit is invalid\");\n\t}\n\tconst target = await resolveSafeArtifactTarget(urlString);\n\tif (target.addresses.length === 0) {\n\t\treturn globalThis.fetch(target.url, { redirect: \"manual\", signal: options.signal });\n\t}\n\n\tconst transport = defaultTransport ?? (await createRuntimeRegistryArtifactTransport());\n\tconst result = await transport.fetch({\n\t\turl: target.url,\n\t\tallowedAddresses: target.addresses,\n\t\tsignal: options.signal,\n\t\tmaxResponseBytes: options.maxResponseBytes,\n\t});\n\tif (!target.addresses.includes(result.connectedAddress)) {\n\t\tawait result.response.body?.cancel().catch(() => undefined);\n\t\tthrow new Error(\"Registry artifact transport connected outside the validated address set\");\n\t}\n\treturn result.response;\n}\n\nasync function createRuntimeRegistryArtifactTransport(): Promise<RegistryArtifactTransport> {\n\ttry {\n\t\t// @ts-ignore - virtual module\n\t\tconst sockets: unknown = await import(\"cloudflare:sockets\");\n\t\tconst connect = objectProperty(sockets, \"connect\");\n\t\tif (!isWorkerSocketConnect(connect)) throw new TypeError(\"Workers socket binding is invalid\");\n\t\treturn createWorkersRegistryArtifactTransport(connect);\n\t} catch {\n\t\treturn createNodeRegistryArtifactTransport();\n\t}\n}\n\nexport interface RegistryArtifactWorkerSocket {\n\treadonly opened: Promise<unknown>;\n\treadonly readable: ReadableStream<Uint8Array>;\n\treadonly writable: WritableStream<Uint8Array>;\n\tstartTls(options: { expectedServerHostname: string }): RegistryArtifactWorkerSocket;\n\tclose(): Promise<void>;\n}\n\nexport type RegistryArtifactWorkerSocketConnect = (\n\taddress: { hostname: string; port: number },\n\toptions: { secureTransport: \"starttls\"; allowHalfOpen: false },\n) => RegistryArtifactWorkerSocket;\n\nexport function createWorkersRegistryArtifactTransport(\n\tconnect: RegistryArtifactWorkerSocketConnect,\n): RegistryArtifactTransport {\n\treturn {\n\t\tasync fetch(input) {\n\t\t\tlet lastError: unknown;\n\t\t\tfor (const address of input.allowedAddresses) {\n\t\t\t\tif (input.signal.aborted) throw new Error(\"Registry artifact request was aborted\");\n\t\t\t\tlet socket: RegistryArtifactWorkerSocket | undefined;\n\t\t\t\tlet tls: RegistryArtifactWorkerSocket | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tsocket = connect(\n\t\t\t\t\t\t{ hostname: address, port: parseHttpsPort(input.url) },\n\t\t\t\t\t\t{ secureTransport: \"starttls\", allowHalfOpen: false },\n\t\t\t\t\t);\n\t\t\t\t\tawait abortable(socket.opened, input.signal, () => socket?.close());\n\t\t\t\t\ttls = socket.startTls({\n\t\t\t\t\t\texpectedServerHostname: stripIpv6Brackets(input.url.hostname),\n\t\t\t\t\t});\n\t\t\t\t\tawait abortable(tls.opened, input.signal, () => tls?.close());\n\t\t\t\t\tconst writer = tls.writable.getWriter();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait abortable(writer.write(buildHttpRequest(input.url)), input.signal, () =>\n\t\t\t\t\t\t\ttls?.close(),\n\t\t\t\t\t\t);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\twriter.releaseLock();\n\t\t\t\t\t}\n\t\t\t\t\tconst bytes = await readWorkerSocketResponse(\n\t\t\t\t\t\ttls,\n\t\t\t\t\t\tinput.maxResponseBytes + MAX_HEADER_BYTES,\n\t\t\t\t\t\tinput.signal,\n\t\t\t\t\t);\n\t\t\t\t\tawait tls.close().catch(() => undefined);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tresponse: parsedResponseToWebResponse(\n\t\t\t\t\t\t\tparsePinnedHttpResponse(bytes, input.maxResponseBytes),\n\t\t\t\t\t\t),\n\t\t\t\t\t\tconnectedAddress: address,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\ttls?.close().catch(() => undefined),\n\t\t\t\t\t\tsocket?.close().catch(() => undefined),\n\t\t\t\t\t]);\n\t\t\t\t\tif (input.signal.aborted) throw error;\n\t\t\t\t\tlastError = error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(\"Registry artifact transport could not connect to an approved address\", {\n\t\t\t\tcause: lastError,\n\t\t\t});\n\t\t},\n\t};\n}\n\nasync function createNodeRegistryArtifactTransport(): Promise<RegistryArtifactTransport> {\n\tconst { request } = await import(\"node:https\");\n\treturn {\n\t\tasync fetch(input) {\n\t\t\tlet lastError: unknown;\n\t\t\tfor (const address of input.allowedAddresses) {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await new Promise<Response>((resolve, reject) => {\n\t\t\t\t\t\tconst chunks: Uint8Array[] = [];\n\t\t\t\t\t\tlet total = 0;\n\t\t\t\t\t\tconst req = request(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tprotocol: \"https:\",\n\t\t\t\t\t\t\t\thostname: stripIpv6Brackets(input.url.hostname),\n\t\t\t\t\t\t\t\tport: parseHttpsPort(input.url),\n\t\t\t\t\t\t\t\tpath: `${input.url.pathname}${input.url.search}`,\n\t\t\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\t\t\t// A pooled socket may have been opened for the same hostname\n\t\t\t\t\t\t\t\t// before this address set was resolved.\n\t\t\t\t\t\t\t\tagent: false,\n\t\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t\tHost: input.url.host,\n\t\t\t\t\t\t\t\t\tConnection: \"close\",\n\t\t\t\t\t\t\t\t\t\"Accept-Encoding\": \"identity\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tlookup: (_hostname, _options, callback) => {\n\t\t\t\t\t\t\t\t\tcallback(null, address, address.includes(\":\") ? 6 : 4);\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tsignal: input.signal,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t(upstream) => {\n\t\t\t\t\t\t\t\tconst contentEncoding = upstream.headers[\"content-encoding\"];\n\t\t\t\t\t\t\t\tif (contentEncoding && contentEncoding.toLowerCase() !== \"identity\") {\n\t\t\t\t\t\t\t\t\tupstream.destroy(\n\t\t\t\t\t\t\t\t\t\tnew Error(\"Registry artifact response content encoding is unsupported\"),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tupstream.on(\"data\", (chunk: Uint8Array) => {\n\t\t\t\t\t\t\t\t\ttotal += chunk.byteLength;\n\t\t\t\t\t\t\t\t\tif (total > input.maxResponseBytes) {\n\t\t\t\t\t\t\t\t\t\tupstream.destroy(\n\t\t\t\t\t\t\t\t\t\t\tnew RangeError(\"Registry artifact response exceeds its byte limit\"),\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tchunks.push(new Uint8Array(chunk));\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tupstream.once(\"error\", reject);\n\t\t\t\t\t\t\t\tupstream.once(\"end\", () => {\n\t\t\t\t\t\t\t\t\tconst bytes = concatBytes(chunks, total);\n\t\t\t\t\t\t\t\t\tconst headers = new Headers();\n\t\t\t\t\t\t\t\t\tfor (let index = 0; index < upstream.rawHeaders.length; index += 2) {\n\t\t\t\t\t\t\t\t\t\theaders.append(upstream.rawHeaders[index], upstream.rawHeaders[index + 1]);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t\t\tparsedResponseToWebResponse({\n\t\t\t\t\t\t\t\t\t\t\tstatus: upstream.statusCode ?? 502,\n\t\t\t\t\t\t\t\t\t\t\theaders,\n\t\t\t\t\t\t\t\t\t\t\tbody: bytes,\n\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\treq.once(\"error\", reject);\n\t\t\t\t\t\treq.end();\n\t\t\t\t\t});\n\t\t\t\t\treturn { response, connectedAddress: address };\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (input.signal.aborted) throw error;\n\t\t\t\t\tlastError = error;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(\"Registry artifact transport could not connect to an approved address\", {\n\t\t\t\tcause: lastError,\n\t\t\t});\n\t\t},\n\t};\n}\n\ninterface ParsedHttpResponse {\n\tstatus: number;\n\theaders: Headers;\n\tbody: Uint8Array;\n}\n\nfunction parsePinnedHttpResponse(bytes: Uint8Array, maxResponseBytes: number): ParsedHttpResponse {\n\tconst headerEnd = findSequence(bytes, HEADER_END, 0);\n\tif (headerEnd === -1 || headerEnd > MAX_HEADER_BYTES) {\n\t\tthrow new Error(\"Registry artifact response headers are invalid or too large\");\n\t}\n\tconst headerText = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes.subarray(0, headerEnd));\n\tconst lines = headerText.split(\"\\r\\n\");\n\tconst statusMatch = STATUS_LINE_RE.exec(lines.shift() ?? \"\");\n\tif (!statusMatch) throw new Error(\"Registry artifact response status line is invalid\");\n\tconst headers = new Headers();\n\tconst rawHeaders = new Map<string, string[]>();\n\tfor (const line of lines) {\n\t\tif (FOLDED_HEADER_RE.test(line)) {\n\t\t\tthrow new Error(\"Registry artifact response uses folded headers\");\n\t\t}\n\t\tconst separator = line.indexOf(\":\");\n\t\tif (separator < 1) throw new Error(\"Registry artifact response header is invalid\");\n\t\tconst name = line.slice(0, separator);\n\t\tconst value = line.slice(separator + 1).trim();\n\t\tif (!HEADER_NAME_RE.test(name) || containsInvalidHeaderValue(value)) {\n\t\t\tthrow new Error(\"Registry artifact response header is invalid\");\n\t\t}\n\t\tconst normalized = name.toLowerCase();\n\t\tconst values = rawHeaders.get(normalized) ?? [];\n\t\tvalues.push(value);\n\t\trawHeaders.set(normalized, values);\n\t\theaders.append(name, value);\n\t}\n\tconst contentEncoding = rawHeaders.get(\"content-encoding\");\n\tif (\n\t\tcontentEncoding &&\n\t\t(contentEncoding.length !== 1 || contentEncoding[0]?.toLowerCase() !== \"identity\")\n\t) {\n\t\tthrow new Error(\"Registry artifact response content encoding is unsupported\");\n\t}\n\tconst contentLength = rawHeaders.get(\"content-length\");\n\tconst transferEncoding = rawHeaders.get(\"transfer-encoding\");\n\tif (contentLength && transferEncoding) {\n\t\tthrow new Error(\"Registry artifact response framing is ambiguous\");\n\t}\n\tconst bodyBytes = bytes.subarray(headerEnd + HEADER_END.length);\n\tlet body: Uint8Array;\n\tif (transferEncoding) {\n\t\tif (transferEncoding.length !== 1 || transferEncoding[0]?.toLowerCase() !== \"chunked\") {\n\t\t\tthrow new Error(\"Registry artifact response transfer encoding is unsupported\");\n\t\t}\n\t\tbody = decodeChunkedBody(bodyBytes, maxResponseBytes);\n\t} else if (contentLength) {\n\t\tif (contentLength.length !== 1 || !CONTENT_LENGTH_RE.test(contentLength[0])) {\n\t\t\tthrow new Error(\"Registry artifact response content length is invalid\");\n\t\t}\n\t\tconst length = Number(contentLength[0]);\n\t\tif (!Number.isSafeInteger(length) || length !== bodyBytes.byteLength) {\n\t\t\tthrow new Error(\"Registry artifact response body length does not match its framing\");\n\t\t}\n\t\tbody = new Uint8Array(bodyBytes);\n\t} else {\n\t\tbody = new Uint8Array(bodyBytes);\n\t}\n\tif (body.byteLength > maxResponseBytes) {\n\t\tthrow new RangeError(\"Registry artifact response exceeds its byte limit\");\n\t}\n\treturn { status: Number(statusMatch[1]), headers, body };\n}\n\nfunction parsedResponseToWebResponse(parsed: ParsedHttpResponse): Response {\n\tconst bodyAllowed = ![204, 205, 304].includes(parsed.status);\n\tlet body: ArrayBuffer | null = null;\n\tif (bodyAllowed) {\n\t\tbody = new ArrayBuffer(parsed.body.byteLength);\n\t\tnew Uint8Array(body).set(parsed.body);\n\t}\n\treturn new Response(body, {\n\t\tstatus: parsed.status,\n\t\theaders: parsed.headers,\n\t});\n}\n\nfunction buildHttpRequest(url: URL): Uint8Array {\n\treturn new TextEncoder().encode(\n\t\t[\n\t\t\t`GET ${url.pathname}${url.search} HTTP/1.1`,\n\t\t\t`Host: ${url.host}`,\n\t\t\t\"Connection: close\",\n\t\t\t\"Accept-Encoding: identity\",\n\t\t\t\"\",\n\t\t\t\"\",\n\t\t].join(\"\\r\\n\"),\n\t);\n}\n\nasync function readWorkerSocketResponse(\n\tsocket: RegistryArtifactWorkerSocket,\n\tmaximumBytes: number,\n\tsignal: AbortSignal,\n): Promise<Uint8Array> {\n\tconst reader = socket.readable.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet total = 0;\n\ttry {\n\t\tfor (;;) {\n\t\t\tconst next = await abortable(reader.read(), signal, () => socket.close());\n\t\t\tif (next.done) break;\n\t\t\ttotal += next.value.byteLength;\n\t\t\tif (total > maximumBytes) {\n\t\t\t\tthrow new RangeError(\"Registry artifact response exceeds its byte limit\");\n\t\t\t}\n\t\t\tchunks.push(next.value);\n\t\t}\n\t} finally {\n\t\treader.releaseLock();\n\t}\n\treturn concatBytes(chunks, total);\n}\n\nfunction decodeChunkedBody(bytes: Uint8Array, maximumBytes: number): Uint8Array {\n\tconst chunks: Uint8Array[] = [];\n\tlet offset = 0;\n\tlet total = 0;\n\tfor (;;) {\n\t\tconst lineEnd = findSequence(bytes, CRLF, offset);\n\t\tif (lineEnd === -1 || lineEnd - offset > 128) {\n\t\t\tthrow new Error(\"Registry artifact chunk size line is invalid\");\n\t\t}\n\t\tconst sizeText = new TextDecoder().decode(bytes.subarray(offset, lineEnd)).split(\";\", 1)[0];\n\t\tif (!sizeText || !CHUNK_SIZE_RE.test(sizeText)) {\n\t\t\tthrow new Error(\"Registry artifact chunk size is invalid\");\n\t\t}\n\t\tconst size = Number.parseInt(sizeText, 16);\n\t\tif (!Number.isSafeInteger(size)) throw new Error(\"Registry artifact chunk size is invalid\");\n\t\toffset = lineEnd + CRLF.length;\n\t\tif (size === 0) {\n\t\t\tif (\n\t\t\t\toffset + CRLF.length !== bytes.length ||\n\t\t\t\tbytes[offset] !== 13 ||\n\t\t\t\tbytes[offset + 1] !== 10\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Registry artifact chunk trailer is invalid\");\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tif (offset + size + CRLF.length > bytes.length) {\n\t\t\tthrow new Error(\"Registry artifact chunk body is truncated\");\n\t\t}\n\t\tif (bytes[offset + size] !== 13 || bytes[offset + size + 1] !== 10) {\n\t\t\tthrow new Error(\"Registry artifact chunk delimiter is invalid\");\n\t\t}\n\t\ttotal += size;\n\t\tif (total > maximumBytes) {\n\t\t\tthrow new RangeError(\"Registry artifact response exceeds its byte limit\");\n\t\t}\n\t\tchunks.push(new Uint8Array(bytes.subarray(offset, offset + size)));\n\t\toffset += size + CRLF.length;\n\t}\n\treturn concatBytes(chunks, total);\n}\n\nfunction findSequence(bytes: Uint8Array, sequence: Uint8Array, start: number): number {\n\touter: for (let offset = start; offset <= bytes.length - sequence.length; offset += 1) {\n\t\tfor (let index = 0; index < sequence.length; index += 1) {\n\t\t\tif (bytes[offset + index] !== sequence[index]) continue outer;\n\t\t}\n\t\treturn offset;\n\t}\n\treturn -1;\n}\n\nfunction concatBytes(chunks: readonly Uint8Array[], total: number): Uint8Array {\n\tconst bytes = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tbytes.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn bytes;\n}\n\nfunction containsInvalidHeaderValue(value: string): boolean {\n\tfor (const character of value) {\n\t\tconst code = character.codePointAt(0)!;\n\t\tif (code === 0 || code === 10 || code === 13) return true;\n\t}\n\treturn false;\n}\n\nfunction parseHttpsPort(url: URL): number {\n\tconst port = url.port === \"\" ? 443 : Number(url.port);\n\tif (!Number.isSafeInteger(port) || port < 1 || port > 65_535) {\n\t\tthrow new TypeError(\"Registry artifact HTTPS port is invalid\");\n\t}\n\treturn port;\n}\n\nfunction stripIpv6Brackets(hostname: string): string {\n\treturn hostname.startsWith(\"[\") && hostname.endsWith(\"]\") ? hostname.slice(1, -1) : hostname;\n}\n\nfunction objectProperty(value: unknown, key: string): unknown {\n\tif (typeof value !== \"object\" || value === null) return undefined;\n\treturn Object.getOwnPropertyDescriptor(value, key)?.value;\n}\n\nfunction isWorkerSocketConnect(value: unknown): value is RegistryArtifactWorkerSocketConnect {\n\treturn typeof value === \"function\";\n}\n\nasync function abortable<T>(\n\toperation: Promise<T>,\n\tsignal: AbortSignal,\n\tonAbort: () => void | Promise<void>,\n): Promise<T> {\n\tif (signal.aborted) throw new Error(\"Registry artifact request was aborted\");\n\tlet abort: (() => void) | undefined;\n\tconst aborted = new Promise<never>((_resolve, reject) => {\n\t\tabort = () => {\n\t\t\tvoid onAbort();\n\t\t\treject(new Error(\"Registry artifact request was aborted\"));\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t});\n\ttry {\n\t\treturn await Promise.race([operation, aborted]);\n\t} finally {\n\t\tif (abort) signal.removeEventListener(\"abort\", abort);\n\t}\n}\n","/**\n * Helpers for normalizing the experimental registry integration option\n * (`config.experimental.registry` in `astro.config.mjs`) into the shape\n * exposed on the admin manifest.\n *\n * The integration option accepts a human-friendly duration string for\n * `policy.minimumReleaseAge` (`\"48h\"`, `\"7d\"`); the manifest exposes\n * seconds so the browser doesn't need a duration parser.\n */\n\nimport { isDid } from \"@atcute/lexicons/syntax\";\n\nimport type { RegistryConfig, RegistryConfigInput } from \"./types.js\";\n\n/**\n * Shape returned in the admin manifest's `registry` field. The browser\n * consumes this directly -- all duration normalization and aggregator URL\n * validation has already happened by the time it gets here.\n */\nexport interface ManifestRegistryConfig {\n\taggregatorUrl: string;\n\tacceptLabelers?: string;\n\tpolicy?: {\n\t\tminimumReleaseAgeSeconds?: number;\n\t\t/**\n\t\t * Allowlist of publishers / packages exempt from the\n\t\t * {@link minimumReleaseAgeSeconds} holdback. Each entry is either:\n\t\t *\n\t\t * - A bare publisher DID: `\"did:plc:abc123\"`. Every package from\n\t\t * that publisher is exempt.\n\t\t * - A `<did>/<slug>` pair: only that specific package is exempt.\n\t\t *\n\t\t * Handles are not accepted because they are mutable\n\t\t * aggregator-supplied envelope data.\n\t\t *\n\t\t * Normalized to lowercase strings at config load time so the\n\t\t * browser does case-insensitive comparison. See\n\t\t * {@link releaseExemptFromMinimumAge}.\n\t\t */\n\t\tminimumReleaseAgeExclude?: string[];\n\t};\n}\n\n/**\n * Canonicalize a capabilities list for set-style comparison.\n *\n * Capabilities (the legacy declared-access shape used by the current\n * sandbox enforcer) are conceptually a *set*: order, duplicates, and\n * non-string entries don't carry meaning. The install handler's drift\n * check compares the admin's acknowledged set against the bundle\n * manifest's set; both sides pass through this canonicalizer first so\n * an aggregator-supplied array with unstable order or junk entries\n * can't cause a spurious drift rejection.\n *\n * Filters non-strings, deduplicates, and sorts lexically. Named to\n * avoid shadowing `@emdash-cms/plugin-types`'s existing\n * `normalizeCapabilities` (which dedupes + applies the deprecated →\n * current alias map but does not filter junk or sort).\n *\n * Exported so the same shape is produced by the browser before sending\n * the `acknowledgedDeclaredAccess` payload and by the server before\n * comparing against the bundle.\n */\nexport function canonicalCapabilitiesForDriftCheck(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\tconst seen = new Set<string>();\n\tfor (const entry of value) {\n\t\tif (typeof entry === \"string\" && entry.length > 0) {\n\t\t\tseen.add(entry);\n\t\t}\n\t}\n\treturn [...seen].toSorted();\n}\n\n/**\n * Returns whether a `(publisher_did, slug)` pair is on the\n * minimum-release-age exemption list. Exported so the same matcher is\n * used by the browser policy filter and the server-side install\n * enforcement.\n *\n * Matching is DID-only. Handles are aggregator-supplied envelope data\n * (mutable, controlled by an attacker who compromises the aggregator)\n * and cannot be used as a trust input -- a compromised aggregator\n * could claim any handle for any package and bypass the holdback. DIDs\n * are part of the AT URI of the package record and are independently\n * resolvable, so even a compromised aggregator can't lie about the\n * publisher DID without also breaking checksum verification downstream.\n *\n * Entries from config are already lowercased at manifest-build time.\n * Runtime values are lowercased here at compare time.\n */\nexport function releaseExemptFromMinimumAge(\n\texclude: readonly string[] | undefined,\n\tpublisherDid: string,\n\tslug: string,\n): boolean {\n\tif (!exclude || exclude.length === 0) return false;\n\tconst didLower = publisherDid.toLowerCase();\n\tconst slugLower = slug.toLowerCase();\n\tconst fullDid = `${didLower}/${slugLower}`;\n\n\tfor (const entry of exclude) {\n\t\tif (entry === didLower) return true;\n\t\tif (entry === fullDid) return true;\n\t}\n\treturn false;\n}\n\nconst DURATION_PATTERN = /^(\\d+)(s|m|h|d|w)$/;\n\n/** Trailing slashes on the aggregator URL, stripped during normalization. */\nconst TRAILING_SLASHES = /\\/+$/;\n\n/** Trailing dot on a hostname, stripped before URL host comparisons. */\nconst TRAILING_DOT = /\\.$/;\n\nconst REGISTRY_PACKAGE_SLUG_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;\n\n/**\n * Parse a duration string or raw second count into a non-negative\n * integer count of seconds. Throws on unrecognised input so config\n * mistakes fail at startup rather than silently disabling the policy.\n */\nexport function parseDurationSeconds(duration: string | number): number {\n\tif (typeof duration === \"number\") {\n\t\tif (!Number.isFinite(duration) || duration < 0) {\n\t\t\tthrow new Error(`Invalid duration: ${duration} (must be a non-negative finite number)`);\n\t\t}\n\t\treturn Math.floor(duration);\n\t}\n\n\tconst match = duration.match(DURATION_PATTERN);\n\tif (!match) {\n\t\tthrow new Error(\n\t\t\t`Invalid duration format: \"${duration}\". Use a duration string like \"48h\", \"7d\", \"30m\", or a number of seconds.`,\n\t\t);\n\t}\n\n\tconst value = parseInt(match[1], 10);\n\tconst unit = match[2];\n\n\tswitch (unit) {\n\t\tcase \"s\":\n\t\t\treturn value;\n\t\tcase \"m\":\n\t\t\treturn value * 60;\n\t\tcase \"h\":\n\t\t\treturn value * 60 * 60;\n\t\tcase \"d\":\n\t\t\treturn value * 24 * 60 * 60;\n\t\tcase \"w\":\n\t\t\treturn value * 7 * 24 * 60 * 60;\n\t\tdefault:\n\t\t\t// Unreachable given the regex, but keep the exhaustive arm for\n\t\t\t// future maintainers who add a unit to the pattern.\n\t\t\tthrow new Error(`Unknown duration unit: ${unit}`);\n\t}\n}\n\n/**\n * Validate that `aggregatorUrl` is a safe outbound target for the\n * registry's XRPC calls. Same posture as artifact downloads: HTTPS\n * required in production; `http://localhost` allowed only in dev.\n *\n * The aggregator's responses are the trust source for release records,\n * checksums, labels, artifact cache descriptors, and `indexedAt` (until full MST\n * verification lands). Allowing plain HTTP here would let a network\n * attacker swap a release record and point the artifact URL at their\n * own HTTPS bundle, defeating the checksum trust chain because the\n * attacker controls the unsigned transport that supplied the checksum.\n */\nexport function validateAggregatorUrl(aggregatorUrl: string): URL {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(aggregatorUrl);\n\t} catch {\n\t\tthrow new Error(`registry.aggregatorUrl is not a valid URL: ${aggregatorUrl}`);\n\t}\n\tif (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n\t\tthrow new Error(`registry.aggregatorUrl must use http or https: ${aggregatorUrl}`);\n\t}\n\t// Reject embedded credentials. The normalized aggregator URL ends\n\t// up in the admin manifest and is shipped to every admin browser;\n\t// browser `fetch()` also outright rejects URLs with `user:pass@`,\n\t// so leaving them in would both leak the credentials and break the\n\t// registry UI at runtime.\n\tif (parsed.username || parsed.password) {\n\t\tthrow new Error(\"registry.aggregatorUrl must not contain embedded credentials (user:pass@)\");\n\t}\n\n\t// WHATWG URL preserves the brackets on IPv6 hostnames -- strip them\n\t// before any comparison so `https://[::1]/` is recognised as localhost\n\t// and not treated as a generic domain string.\n\tconst rawHostname = parsed.hostname.toLowerCase().replace(TRAILING_DOT, \"\");\n\tconst hostname =\n\t\trawHostname.startsWith(\"[\") && rawHostname.endsWith(\"]\")\n\t\t\t? rawHostname.slice(1, -1)\n\t\t\t: rawHostname;\n\tconst isLocalhost =\n\t\thostname === \"localhost\" ||\n\t\thostname.endsWith(\".localhost\") ||\n\t\thostname === \"127.0.0.1\" ||\n\t\thostname === \"::1\" ||\n\t\t// IPv4-mapped IPv6 forms of loopback, e.g. `::ffff:127.0.0.1` and `::ffff:7f00:1`.\n\t\thostname.startsWith(\"::ffff:127.\") ||\n\t\thostname.startsWith(\"::ffff:7f00:\");\n\n\tif (!import.meta.env.DEV) {\n\t\tif (parsed.protocol === \"http:\") {\n\t\t\tthrow new Error(`registry.aggregatorUrl must use https in production: ${aggregatorUrl}`);\n\t\t}\n\t\tif (isLocalhost) {\n\t\t\tthrow new Error(\n\t\t\t\t`registry.aggregatorUrl points at localhost; allowed only in dev: ${aggregatorUrl}`,\n\t\t\t);\n\t\t}\n\t} else if (parsed.protocol === \"http:\" && !isLocalhost) {\n\t\tthrow new Error(\n\t\t\t`registry.aggregatorUrl must use https (http allowed only for localhost in dev): ${aggregatorUrl}`,\n\t\t);\n\t}\n\n\treturn parsed;\n}\n\n/**\n * Expand the `RegistryConfigInput` shorthand into the full\n * `RegistryConfig` object shape.\n *\n * Users can pass a bare aggregator URL string for the common case\n * (`experimental.registry: \"https://registry.emdashcms.com\"`); the\n * normalizer handles either form transparently.\n *\n * Returns `undefined` for `undefined` input so callers can chain with\n * optional chaining.\n */\nexport function coerceRegistryConfig(\n\tinput: RegistryConfigInput | undefined,\n): RegistryConfig | undefined {\n\tif (input === undefined) return undefined;\n\tif (typeof input === \"string\") return { aggregatorUrl: input };\n\treturn input;\n}\n\n/**\n * Normalize the user-supplied `RegistryConfigInput` into the shape that\n * ships to the admin browser via the manifest endpoint.\n *\n * Accepts either the shorthand string form\n * (`\"https://registry.emdashcms.com\"`) or the full `RegistryConfig`\n * object. Returns `null` when `input` is undefined so callers can\n * spread the result directly into the manifest object.\n *\n * Throws if the aggregator URL is malformed, points at a forbidden host,\n * or `policy.minimumReleaseAge` is unparseable. These surface at\n * runtime startup as 500s from the manifest endpoint -- intended,\n * because the alternative is silently disabling the registry on\n * misconfigured sites.\n *\n * TODO: switch to a Zod schema for richer per-field error messages and\n * to surface misconfigurations to the admin UI as a banner instead of\n * a manifest 500.\n */\nexport function normalizeRegistryConfig(\n\tinput: RegistryConfigInput | undefined,\n): ManifestRegistryConfig | null {\n\tconst config = coerceRegistryConfig(input);\n\tif (!config) return null;\n\n\tconst aggregatorUrl = config.aggregatorUrl?.trim();\n\tif (!aggregatorUrl) {\n\t\tthrow new Error(\"registry.aggregatorUrl is required when registry is configured\");\n\t}\n\n\tvalidateAggregatorUrl(aggregatorUrl);\n\n\tconst out: ManifestRegistryConfig = {\n\t\t// Strip any trailing slash so `${aggregatorUrl}/xrpc/...` works\n\t\t// regardless of how the user wrote it.\n\t\taggregatorUrl: aggregatorUrl.replace(TRAILING_SLASHES, \"\"),\n\t};\n\n\tif (config.acceptLabelers) {\n\t\tout.acceptLabelers = config.acceptLabelers;\n\t}\n\n\tconst policy: ManifestRegistryConfig[\"policy\"] = {};\n\tlet hasPolicy = false;\n\n\tif (config.policy?.minimumReleaseAge !== undefined) {\n\t\tpolicy.minimumReleaseAgeSeconds = parseDurationSeconds(config.policy.minimumReleaseAge);\n\t\thasPolicy = true;\n\t}\n\n\tif (config.policy?.minimumReleaseAgeExclude !== undefined) {\n\t\t// Normalize at load time so callers (browser and server) can do\n\t\t// plain string compares without each one re-implementing the\n\t\t// case-folding rule.\n\t\tconst list = config.policy.minimumReleaseAgeExclude.map((entry) => {\n\t\t\tconst trimmed = entry.trim();\n\t\t\tif (!trimmed) {\n\t\t\t\tthrow new Error(\"registry.policy.minimumReleaseAgeExclude entries cannot be empty\");\n\t\t\t}\n\t\t\tconst lower = trimmed.toLowerCase();\n\t\t\tconst [did, slug, ...extra] = lower.split(\"/\");\n\t\t\tif (\n\t\t\t\t!did ||\n\t\t\t\t!isDid(did) ||\n\t\t\t\textra.length > 0 ||\n\t\t\t\t(slug !== undefined && !REGISTRY_PACKAGE_SLUG_PATTERN.test(slug))\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`registry.policy.minimumReleaseAgeExclude entry must be a DID or <did>/<slug>: ${trimmed}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn lower;\n\t\t});\n\t\tif (list.length > 0) {\n\t\t\tpolicy.minimumReleaseAgeExclude = list;\n\t\t\thasPolicy = true;\n\t\t}\n\t}\n\n\tif (hasPolicy) {\n\t\tout.policy = policy;\n\t}\n\n\treturn out;\n}\n","/**\n * Plugin identifier helpers for the experimental decentralized plugin\n * registry.\n *\n * Registry plugins are addressed by `(publisher_did, slug)`, but the\n * EmDash runtime threads a single `pluginId: string` through every\n * install primitive (R2 storage keys, `PluginStateRepository`,\n * `syncMarketplacePlugins`, sandbox cache keys). Rather than refactor\n * everything to carry a composite identifier, we normalize the registry\n * tuple to an opaque content-addressed id that satisfies the existing\n * `validatePluginIdentifier` shape (`/^[a-z][a-z0-9_-]*$/`).\n *\n * The normalized id is:\n *\n * `r_` + base32-encoded SHA-256(publisher_did + \"\\n\" + slug), truncated.\n *\n * Properties:\n *\n * - Deterministic. The same `(publisher, slug)` always produces the\n * same id, so re-resolving an installed plugin's metadata against\n * the aggregator is a straightforward lookup keyed by the columns\n * stored alongside `plugin_id` in `plugin_states`.\n * - Collision-resistant. 80 bits of truncated hash; a 50% birthday\n * collision happens around 2^40 distinct plugins, well beyond what\n * this registry will ever index.\n * - R2-safe. Lowercase alphanumerics + underscore (no hyphens), no\n * `:` or `/`. Existing sandbox cache keys (`${pluginId}:${version}`)\n * keep working because the id contains no `:`.\n * - Syntactically distinct from typical marketplace plugin ids: the\n * `r_` prefix plus exactly 16 base32 characters is unlikely to be\n * chosen as a marketplace id. Not formally guaranteed by the\n * validator -- marketplace ids may begin with `r_` and contain\n * hyphens -- so the install handler also performs an explicit\n * pre-existing-row check at the derived id and rejects any cross-\n * source collision (`PLUGIN_ID_COLLISION`).\n *\n * Reverse lookup (id → publisher + slug) requires the `plugin_states`\n * row -- the hash is one-way. That's intentional: any code path that\n * needs the human-meaningful pair already has the state row in hand.\n */\n\n/** Length (in base32 characters) of the truncated hash portion of the id. */\nconst HASH_LENGTH = 16;\n\n/** Total expected length of a registry plugin id. */\nexport const REGISTRY_PLUGIN_ID_LENGTH = 2 /* \"r_\" */ + HASH_LENGTH;\n\n/**\n * Regex matching a well-formed registry plugin id. Used by call sites\n * that need to distinguish registry installs from marketplace installs\n * without consulting the `source` column on `plugin_states`.\n *\n * The base32 alphabet here uses RFC 4648 lowercase without padding,\n * matching {@link base32Encode}'s output.\n */\nexport const REGISTRY_PLUGIN_ID_PATTERN = /^r_[a-z2-7]{16}$/;\n\nconst BASE32_ALPHABET = \"abcdefghijklmnopqrstuvwxyz234567\";\n\n/**\n * RFC 4648 base32 encoding without padding, lowercase. Implemented inline\n * rather than depending on a multibase library because (a) we only need\n * lowercase base32 here, (b) we need it to run identically in workerd,\n * Node, and the browser, and (c) the implementation is fewer lines than\n * the import statement would be.\n */\nfunction base32Encode(bytes: Uint8Array): string {\n\tlet bits = 0;\n\tlet value = 0;\n\tlet out = \"\";\n\tfor (const byte of bytes) {\n\t\tvalue = (value << 8) | byte;\n\t\tbits += 8;\n\t\twhile (bits >= 5) {\n\t\t\tbits -= 5;\n\t\t\tout += BASE32_ALPHABET[(value >>> bits) & 0x1f];\n\t\t}\n\t}\n\tif (bits > 0) {\n\t\tout += BASE32_ALPHABET[(value << (5 - bits)) & 0x1f];\n\t}\n\treturn out;\n}\n\n/**\n * Derive the normalized plugin id for a registry-published plugin.\n *\n * Throws if either input is empty or whitespace-only -- a missing DID\n * or slug is always a programming error in the install path, not a\n * recoverable runtime condition.\n */\nexport async function makeRegistryPluginId(publisherDid: string, slug: string): Promise<string> {\n\tconst did = publisherDid.trim();\n\tconst s = slug.trim();\n\tif (!did) throw new Error(\"makeRegistryPluginId: publisherDid is required\");\n\tif (!s) throw new Error(\"makeRegistryPluginId: slug is required\");\n\n\t// `\\n` separator avoids ambiguity: no canonical did:plc / did:web form\n\t// contains a literal newline, so `(\"a\", \"b\\nc\")` cannot hash to the\n\t// same bytes as `(\"a\\nb\", \"c\")`.\n\tconst input = `${did}\\n${s}`;\n\tconst hashBuffer = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(input));\n\tconst encoded = base32Encode(new Uint8Array(hashBuffer));\n\treturn `r_${encoded.slice(0, HASH_LENGTH)}`;\n}\n\n/**\n * Return whether `pluginId` is a well-formed registry plugin id.\n *\n * This is a syntactic check, not a database lookup -- it answers\n * \"could this id have come from `makeRegistryPluginId`?\", not \"is this\n * plugin installed?\".\n */\nexport function isRegistryPluginId(pluginId: string): boolean {\n\treturn REGISTRY_PLUGIN_ID_PATTERN.test(pluginId);\n}\n","/**\n * Registry plugin install handler.\n *\n * Installs a plugin published to the experimental decentralized plugin\n * registry described in RFC 0001. The install flow:\n *\n * 1. Resolve `(handle, slug)` to a publisher DID via the configured\n * aggregator's `resolvePackage` XRPC.\n * 2. Look up the requested release (or the policy-filtered latest one)\n * via `getLatestRelease` / `listReleases`.\n * 3. Require the aggregator's approved listing projection, then apply the\n * independent release-age and environment policies.\n * 4. Fetch the bundle artifact through an advertised record-scoped cache,\n * the publisher PDS, or the publisher-declared URL.\n * 5. Verify the artifact's multibase checksum against the signed\n * release record's `artifacts.package.checksum`.\n * 6. Extract `manifest.json` + `backend.js` + optional `admin.js` from\n * the gzipped tar bundle.\n * 7. Store the extracted files in site-local R2 under the\n * `registry/<plugin-id>/<version>/` prefix.\n * 8. Write a `plugin_states` row with `source = \"registry\"` and the\n * `(publisher_did, slug)` pair so updates can be resolved later.\n * 9. Sync the runtime so the plugin becomes active immediately.\n *\n * Known gaps (tracked separately):\n *\n * - The aggregator-supplied records are not yet cryptographically\n * verified against the publisher's MST signature. The signed bytes\n * and CIDs are passed through verbatim per the lexicon, but full\n * PDS-direct verification with proof traversal is follow-up work.\n * The artifact checksum is verified end-to-end against the value\n * in the (aggregator-relayed) release record, which is the actual\n * trust boundary for the bytes that end up in the sandbox.\n * - Listing approval controls whether metadata is eligible for discovery.\n * It is never treated as approval of plugin code: checksum, bundle,\n * manifest, access, consent, sandbox, and environment gates remain\n * independent below.\n */\n\nimport { ClientResponseError, ClientValidationError } from \"@atcute/client\";\nimport type { Did } from \"@atcute/lexicons\";\nimport { checkEnvCompatibility, findSkippedEnvConstraints } from \"@emdash-cms/registry-client/env\";\nimport type { HostEnv } from \"@emdash-cms/registry-client/env\";\nimport { evaluateRegistryReleaseWithdrawal } from \"@emdash-cms/registry-client/withdrawal\";\nimport { NSID } from \"@emdash-cms/registry-lexicons\";\nimport {\n\tfetchReleaseArtifact,\n\ttype ReleaseArtifactReference,\n} from \"@emdash-cms/registry-verification/artifact\";\nimport type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { extractBundle } from \"../../plugins/marketplace.js\";\nimport type { PluginBundle } from \"../../plugins/marketplace.js\";\nimport type { SandboxRunner } from \"../../plugins/sandbox/types.js\";\nimport { PluginStateRepository } from \"../../plugins/state.js\";\nimport {\n\tremoveAllPluginIndexes,\n\tsyncDeclaredStorageIndexes,\n} from \"../../plugins/storage-indexes.js\";\nimport { declaredAccessToCapabilities } from \"../../plugins/types.js\";\nimport type { DeclaredAccess } from \"../../plugins/types.js\";\nimport { fetchRegistryArtifactUrl } from \"../../registry/artifact-fetch.js\";\nimport {\n\tcanonicalCapabilitiesForDriftCheck,\n\tcoerceRegistryConfig,\n\tparseDurationSeconds,\n\treleaseExemptFromMinimumAge,\n\tvalidateAggregatorUrl,\n} from \"../../registry/config.js\";\nimport { makeRegistryPluginId } from \"../../registry/plugin-id.js\";\nimport type { RegistryConfigInput } from \"../../registry/types.js\";\nimport { resolveAndValidateExternalUrlTarget } from \"../../security/ssrf.js\";\nimport { EmDashStorageError } from \"../../storage/types.js\";\nimport type { Storage } from \"../../storage/types.js\";\nimport type { ApiResult } from \"../types.js\";\nimport {\n\tdeleteBundleFromR2,\n\tdiffCapabilities,\n\tdiffRouteVisibility,\n\tloadBundleFromR2,\n\tstoreBundleInR2,\n} from \"./marketplace.js\";\n\nexport { assertSafeArtifactUrl } from \"../../registry/artifact-fetch.js\";\n\nconst RELEASE_EXTENSION_NSID = \"com.emdashcms.experimental.package.releaseExtension\";\n\n/**\n * Whether two `declaredAccess` blocks grant exactly the same enforced access --\n * the same capabilities AND the same host allow-list. Both are lowered through\n * the canonical converter so that constraint content (`allowedHosts`), not just\n * the capability set, is part of the comparison. The capability-set consent\n * gate is blind to host scope; this is what keeps a bundle from being installed\n * with a wider (or simply different) host allow-list than its published record\n * advertised and the user consented to.\n */\nexport function enforcedAccessEqual(a: DeclaredAccess, b: DeclaredAccess): boolean {\n\tconst aa = declaredAccessToCapabilities(a);\n\tconst bb = declaredAccessToCapabilities(b);\n\treturn (\n\t\tJSON.stringify(aa.capabilities.toSorted()) === JSON.stringify(bb.capabilities.toSorted()) &&\n\t\tJSON.stringify(aa.allowedHosts.toSorted()) === JSON.stringify(bb.allowedHosts.toSorted())\n\t);\n}\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface RegistryInstallInput {\n\t/**\n\t * Publisher DID. Required. The browser is expected to resolve\n\t * `(handle, slug) → (did, slug)` via the aggregator's\n\t * `resolvePackage` XRPC before posting -- the server then skips that\n\t * round-trip and looks up the package directly.\n\t *\n\t * Passing DID rather than handle here means installs work for\n\t * publishers whose handle the aggregator couldn't resolve at view\n\t * time (handle is \"best-effort\" per the lexicon -- absent for any\n\t * publisher whose DID document didn't resolve cleanly at ingest).\n\t */\n\tdid: string;\n\t/** Package slug (rkey of the publisher's profile record). */\n\tslug: string;\n\t/** Optional explicit version. When omitted, the aggregator's latest. */\n\tversion?: string;\n\t/**\n\t * Capabilities the admin acknowledged in the consent dialog, lifted\n\t * from the release record's `declaredAccess` block. Compared against\n\t * the bundle's `manifest.declaredAccess` to detect drift between\n\t * what the admin agreed to and what the bundle actually requests.\n\t *\n\t * When omitted, drift detection is skipped -- callers that don't\n\t * surface a consent UI before posting (e.g. CI scripts) opt out.\n\t */\n\tacknowledgedDeclaredAccess?: unknown;\n\tacknowledgedMcpTools?: unknown;\n}\n\nexport interface RegistryInstallResult {\n\t/** Hashed, opaque plugin id used everywhere in the runtime. */\n\tpluginId: string;\n\t/** Publisher DID resolved from the handle. */\n\tpublisherDid: string;\n\t/** Publisher slug (== the registry slug). */\n\tslug: string;\n\t/** Installed version. */\n\tversion: string;\n\t/** Capabilities surfaced from the bundle's manifest. */\n\tcapabilities: string[];\n}\n\n// ── Helpers ────────────────────────────────────────────────────────\n\n/** Matches a bare 64-character lowercase/uppercase hex SHA-256 digest. */\nconst SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/i;\n\n/** Compute the SHA-256 of `bytes` as a lowercase hex string. */\nasync function sha256Hex(bytes: Uint8Array): Promise<string> {\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Uint8Array is a valid BufferSource at runtime\n\tconst buf = await crypto.subtle.digest(\"SHA-256\", bytes as unknown as BufferSource);\n\tconst arr = new Uint8Array(buf);\n\treturn Array.from(arr, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/** multihash code for sha2-256 (single-byte varint). */\nconst MULTIHASH_SHA256_CODE = 0x12;\n/** sha2-256 digest length in bytes (single-byte varint). */\nconst MULTIHASH_SHA256_LENGTH = 0x20;\n\n/**\n * Compute the multibase-multihash sha2-256 checksum of `bytes`, in the\n * same `b<base32>` shape the registry CLI publishes\n * (`packages/plugin-cli/src/multihash.ts`). Returns a 56-character\n * string starting with `b`.\n *\n * The trust contract is: if both sides produce the same string for\n * the same bytes, the bytes are unchanged. We don't decode the\n * publisher-supplied checksum -- we just re-encode our own and compare,\n * which is equivalent and avoids needing a base32 decoder.\n */\nasync function sha256MultibaseMultihash(bytes: Uint8Array): Promise<string> {\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Uint8Array is a valid BufferSource at runtime\n\tconst digestBuf = await crypto.subtle.digest(\"SHA-256\", bytes as unknown as BufferSource);\n\tconst digest = new Uint8Array(digestBuf);\n\tconst multihash = new Uint8Array(2 + digest.length);\n\tmultihash[0] = MULTIHASH_SHA256_CODE;\n\tmultihash[1] = MULTIHASH_SHA256_LENGTH;\n\tmultihash.set(digest, 2);\n\tconst { toBase32 } = await import(\"@atcute/multibase\");\n\treturn `b${toBase32(multihash)}`;\n}\n\n/**\n * Verify that a checksum string from a release record's\n * `artifact.checksum` field corresponds to the SHA-256 of the given\n * bytes.\n *\n * Accepts two formats:\n *\n * - Bare lowercase/uppercase hex SHA-256 (64 chars). Convenience for\n * publishers / tools that emit hex rather than multibase.\n * - Multibase-multihash with the `b` (base32) prefix and sha2-256.\n * This is the format RFC 0001 mandates and the registry CLI emits\n * (see `packages/plugin-cli/src/multihash.ts`).\n *\n * Hash functions other than sha2-256 are out of scope for this\n * initial release; the install fails closed.\n */\nexport async function verifyChecksum(bytes: Uint8Array, checksum: string): Promise<boolean> {\n\tif (SHA256_HEX_PATTERN.test(checksum)) {\n\t\tconst actual = await sha256Hex(bytes);\n\t\treturn checksum.toLowerCase() === actual;\n\t}\n\n\t// Multibase-base32 multihash with sha2-256. We re-encode our own\n\t// digest in the same shape and compare strings -- equivalent to\n\t// decoding and comparing bytes, but doesn't need a base32 decoder.\n\t// 56 chars = 'b' + base32(34 bytes) = 'b' + 55 chars.\n\tif (checksum.length === 56 && checksum.startsWith(\"b\")) {\n\t\tconst actual = await sha256MultibaseMultihash(bytes);\n\t\t// Case-insensitive: multibase 'b' is lowercase by convention but\n\t\t// some emitters use uppercase. RFC 4648 base32 alphabets are\n\t\t// case-insensitive.\n\t\treturn actual.toLowerCase() === checksum.toLowerCase();\n\t}\n\n\treturn false;\n}\n\n/**\n * Bytes-per-artifact cap on the gzipped tarball we'll download before\n * decompression. RFC 0001 caps a sandboxed plugin bundle at 256 KiB\n * decompressed (see `MAX_BUNDLE_SIZE` in cli/commands/bundle-utils.ts);\n * gzip on a mix of JSON manifest + JS code typically gives 0.3-0.6\n * ratio, so compressed bundles are well under 200 KiB in practice.\n * 512 KiB leaves margin for unusual file mixes that compress poorly\n * while still rejecting anything that's obviously not a legitimate\n * plugin bundle.\n */\nconst MAX_ARTIFACT_BYTES = 512 * 1024;\n\n/**\n * Maximum number of HTTP redirects followed during artifact download.\n * Each hop is independently URL-validated, so a malicious server cannot\n * redirect through a series of allowed-looking origins to reach a\n * forbidden one.\n */\nconst MAX_REDIRECTS = 5;\n\n/**\n * Wall-clock cap on any single artifact fetch attempt (per URL).\n * Defends against slow-loris artifact sources that accept the connection but\n * never finish sending headers or body.\n */\nconst ARTIFACT_FETCH_TIMEOUT_MS = 15_000;\n\n/**\n * Total wall-clock budget for the artifact-download phase across all\n * advertised caches, the publisher PDS, and the declared URL. Even with the\n * per-URL timeout, unavailable sources could otherwise tie up the install request for\n * minutes; this caps total time at a budget interactive admins can\n * tolerate. Tuned so a fast happy path takes <1s of budget per\n * attempt and a worst case still completes in under a minute.\n */\nconst ARTIFACT_TOTAL_BUDGET_MS = 45_000;\n\n/**\n * Per-request timeout applied to every aggregator XRPC call\n * (`resolvePackage`, `getLatestRelease`, `listReleases`). Matches the\n * per-URL artifact-fetch cap. Without this, a slow-loris aggregator\n * can stall the install before the artifact phase even starts.\n */\nconst AGGREGATOR_REQUEST_TIMEOUT_MS = 15_000;\n\n/**\n * Total wall-clock budget for the aggregator-discovery phase\n * (resolve + selected-release lookup). Mirrors the artifact-download\n * budget. Worst case with the pinned-version path's 20-page cap is\n * 20 + 1 calls; capping the total ensures any one stalled call\n * still bounds the whole phase.\n */\nconst AGGREGATOR_TOTAL_BUDGET_MS = 30_000;\n\n/** Build a fetch function that enforces a per-request and per-budget timeout. */\nfunction timedFetch(totalDeadline: number): typeof fetch {\n\treturn (input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]) => {\n\t\tconst now = Date.now();\n\t\tconst remaining = Math.max(0, totalDeadline - now);\n\t\tif (remaining === 0) {\n\t\t\treturn Promise.reject(new Error(\"Aggregator request budget exhausted\"));\n\t\t}\n\t\tconst timeout = Math.min(AGGREGATOR_REQUEST_TIMEOUT_MS, remaining);\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), timeout);\n\t\tconst callerSignal = init?.signal;\n\t\tif (callerSignal) {\n\t\t\tif (callerSignal.aborted) controller.abort(callerSignal.reason);\n\t\t\telse callerSignal.addEventListener(\"abort\", () => controller.abort(callerSignal.reason));\n\t\t}\n\t\treturn fetch(input, { ...init, signal: controller.signal }).finally(() => {\n\t\t\tclearTimeout(timer);\n\t\t});\n\t};\n}\n\nasync function fetchArtifact(\n\tartifactCaches: readonly unknown[],\n\tartifact: ReleaseArtifactReference,\n\trecord: {\n\t\tdid: string;\n\t\tcollection: string;\n\t\trkey: string;\n\t\tcid: string;\n\t},\n\tauth?: unknown,\n): Promise<Uint8Array> {\n\tconst result = await fetchReleaseArtifact(\n\t\t{\n\t\t\tartifact,\n\t\t\trecord,\n\t\t\tartifactCaches,\n\t\t\t...(auth === undefined ? {} : { auth }),\n\t\t},\n\t\t{\n\t\t\tfetch: async (url, init) => {\n\t\t\t\tif (!init.signal) throw new Error(\"Registry artifact fetch requires an abort signal\");\n\t\t\t\treturn fetchRegistryArtifactUrl(url.href, {\n\t\t\t\t\tsignal: init.signal,\n\t\t\t\t\tmaxResponseBytes: MAX_ARTIFACT_BYTES,\n\t\t\t\t});\n\t\t\t},\n\t\t\tresolveHostname: async (hostname) =>\n\t\t\t\t(await resolveAndValidateExternalUrlTarget(`https://${hostname}`)).addresses,\n\t\t\tallowHttpLocalhost: import.meta.env.DEV,\n\t\t\theaderTimeoutMs: ARTIFACT_FETCH_TIMEOUT_MS,\n\t\t\ttotalTimeoutMs: ARTIFACT_TOTAL_BUDGET_MS,\n\t\t\tmaxBytes: MAX_ARTIFACT_BYTES,\n\t\t\tmaxRedirects: MAX_REDIRECTS,\n\t\t},\n\t);\n\tif (!result.success) {\n\t\tthrow new Error(`Artifact retrieval failed (${result.error.code}): ${result.error.message}`);\n\t}\n\treturn result.value.bytes;\n}\n\n/**\n * The shape of a single env-compatibility failure returned to the admin in\n * the `ENV_INCOMPATIBLE` error's `details`.\n */\ninterface EnvIncompatibleError {\n\tcode: \"ENV_INCOMPATIBLE\";\n\tmessage: string;\n\tdetails: { requires: Record<string, string>; host: HostEnv };\n}\n\n/**\n * Gate a release's `requires` constraints against the running host\n * environment. `requires` is the lexicon-`unknown` value off the signed\n * release record — never trust its shape; `checkEnvCompatibility` guards it.\n *\n * Returns `null` when every advertised constraint is satisfied (or there are\n * none), or a structured `ENV_INCOMPATIBLE` error naming the unsatisfied\n * constraints and the host versions. The error carries the guarded `requires`\n * and `host` maps so the admin can render the same mismatch the UI gate shows.\n */\nexport function assertEnvCompatible(\n\trequires: unknown,\n\thostEnv: HostEnv,\n): EnvIncompatibleError | null {\n\t// A constraint the host can't evaluate (unknown or unparseable host\n\t// version) downgrades the gate to a no-op for that env. Log it so a\n\t// silent bypass is observable rather than invisible.\n\tfor (const skipped of findSkippedEnvConstraints(requires, hostEnv)) {\n\t\tconsole.warn(\n\t\t\t`[registry] env compatibility constraint skipped: ${skipped.key} requires ${skipped.required} but host version is ${skipped.reason}`,\n\t\t);\n\t}\n\tconst mismatches = checkEnvCompatibility(requires, hostEnv);\n\tif (mismatches.length === 0) return null;\n\tconst guarded: Record<string, string> = {};\n\tfor (const m of mismatches) guarded[m.key] = m.required;\n\tconst summary = mismatches\n\t\t.map((m) => `${m.key} requires ${m.required} but host is ${m.host}`)\n\t\t.join(\"; \");\n\treturn {\n\t\tcode: \"ENV_INCOMPATIBLE\",\n\t\tmessage: `This release is not compatible with the current environment: ${summary}.`,\n\t\tdetails: { requires: guarded, host: hostEnv },\n\t};\n}\n\n// ── Install ────────────────────────────────────────────────────────\n\nexport async function handleRegistryInstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tregistryConfigInput: RegistryConfigInput | undefined,\n\tinput: RegistryInstallInput,\n\topts?: { configuredPluginIds?: Set<string>; hostEnv?: HostEnv },\n): Promise<ApiResult<RegistryInstallResult>> {\n\t// Accept either the bare-string shorthand or the full\n\t// `RegistryConfig` object (see `RegistryConfigInput`).\n\tconst registryConfig = coerceRegistryConfig(registryConfigInput);\n\tif (!registryConfig) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REGISTRY_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Registry is not configured\",\n\t\t\t},\n\t\t};\n\t}\n\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"STORAGE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Storage is required for registry plugin installation\",\n\t\t\t},\n\t\t};\n\t}\n\n\tif (!sandboxRunner || !sandboxRunner.isAvailable()) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"SANDBOX_NOT_AVAILABLE\",\n\t\t\t\tmessage: \"Sandbox runner is required for registry plugins\",\n\t\t\t},\n\t\t};\n\t}\n\n\t// Defense in depth: validate the aggregator URL even though the same\n\t// check runs at config-normalize time. Keeps every entrypoint into\n\t// `handleRegistryInstall` safe regardless of how the caller obtained\n\t// the config.\n\ttry {\n\t\tvalidateAggregatorUrl(registryConfig.aggregatorUrl);\n\t} catch (err) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REGISTRY_NOT_CONFIGURED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Invalid aggregator URL\",\n\t\t\t},\n\t\t};\n\t}\n\n\tconst { did, slug, version: requestedVersion } = input;\n\n\t// Lazy-load the discovery client. Avoids pulling @atcute/client into\n\t// every code path that imports core/api/handlers.\n\tconst { DiscoveryClient, registryLabelerPolicy } =\n\t\tawait import(\"@emdash-cms/registry-client/discovery\");\n\n\t// Every aggregator XRPC call passes through `timedFetch`, which\n\t// enforces a per-request timeout and shares a single total-budget\n\t// deadline. Defends against a slow-loris aggregator stalling the\n\t// install before the artifact phase begins.\n\tconst aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;\n\tconst discovery = new DiscoveryClient({\n\t\taggregatorUrl: registryConfig.aggregatorUrl,\n\t\tacceptLabelers: registryConfig.acceptLabelers,\n\t\tlabelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),\n\t\tfetch: timedFetch(aggregatorDeadline),\n\t});\n\n\t// Basic shape check on the DID. The browser is expected to send a\n\t// DID resolved via the aggregator's `resolvePackage`; reject obvious\n\t// malformations here rather than letting the XRPC call fail\n\t// opaquely. The lexicon's `did:${string}:${string}` template is the\n\t// authoritative check.\n\tif (!did.startsWith(\"did:\") || did.split(\":\").length < 3) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"INVALID_DID\",\n\t\t\t\tmessage: \"DID must be a valid atproto DID (e.g. did:plc:abc123)\",\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\t// Step 1: look up the package by DID + slug. The browser already\n\t\t// resolved any handle to a DID via `resolvePackage`; we skip that\n\t\t// round-trip and go straight to `getPackage`.\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- validated above\n\t\tconst publisherDid = did as Did;\n\t\tconst packageView = await discovery.getPackage({\n\t\t\tdid: publisherDid,\n\t\t\tslug,\n\t\t});\n\n\t\t// Step 2: select the target release.\n\t\t// For an explicit version, page through listReleases until we find\n\t\t// the matching record; the aggregator returns releases ordered by\n\t\t// semver descending. For \"latest\", use the dedicated convenience\n\t\t// endpoint which applies the aggregator's policy filter (yanked\n\t\t// exclusion etc.) server-side.\n\t\t//\n\t\t// Pagination is bounded both by total pages and by repeated-cursor\n\t\t// detection: a buggy or compromised aggregator could otherwise\n\t\t// return endless distinct cursors that never include the\n\t\t// requested version, hanging the install for the platform's\n\t\t// request-time budget.\n\t\tconst MAX_LIST_PAGES = 20; // 20 * 50 limit = 1000 releases worth\n\t\tconst latestRelease = await (async () => {\n\t\t\tif (!requestedVersion) {\n\t\t\t\treturn discovery.getLatestRelease({\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tpackage: slug,\n\t\t\t\t});\n\t\t\t}\n\t\t\tlet cursor: string | undefined;\n\t\t\tconst seenCursors = new Set<string>();\n\t\t\tfor (let page = 0; page < MAX_LIST_PAGES; page++) {\n\t\t\t\tif (cursor !== undefined) {\n\t\t\t\t\tif (seenCursors.has(cursor)) break;\n\t\t\t\t\tseenCursors.add(cursor);\n\t\t\t\t}\n\t\t\t\tconst result = await discovery.listReleases({\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tpackage: slug,\n\t\t\t\t\tcursor,\n\t\t\t\t\tlimit: 50,\n\t\t\t\t});\n\t\t\t\tfor (const r of result.releases) {\n\t\t\t\t\tif (r.version === requestedVersion) return r;\n\t\t\t\t}\n\t\t\t\tif (!result.cursor) break;\n\t\t\t\tcursor = result.cursor;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t})();\n\t\tconst releaseView = latestRelease;\n\n\t\tif (!releaseView) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_RELEASE\",\n\t\t\t\t\tmessage: requestedVersion\n\t\t\t\t\t\t? `Version ${requestedVersion} not found for ${publisherDid}/${slug}`\n\t\t\t\t\t\t: `No installable release found for ${publisherDid}/${slug}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Identity cross-check on every field the aggregator denormalises\n\t\t// onto the package and release views. A buggy or compromised\n\t\t// aggregator could otherwise return a release view for a\n\t\t// different `(did, slug, version)` than we asked for; the\n\t\t// handler would then fetch + checksum-verify + install bytes\n\t\t// under the requested package's pluginId but for a different\n\t\t// publisher's record. Checksum verification only proves the bytes\n\t\t// match the *returned* record, not that the record belongs to\n\t\t// the package we requested.\n\t\t// `releaseView.release` is validated against the release lexicon by\n\t\t// DiscoveryClient (or `null` if it didn't conform). A `null` here makes\n\t\t// the identity checks below fail closed, which is the desired outcome.\n\t\tconst signedRelease = releaseView.release;\n\t\tif (packageView.did !== publisherDid || packageView.slug !== slug) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage: \"Aggregator returned a package view for a different publisher or slug.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (\n\t\t\treleaseView.did !== publisherDid ||\n\t\t\treleaseView.package !== slug ||\n\t\t\tsignedRelease?.package !== slug ||\n\t\t\t(requestedVersion !== undefined && releaseView.version !== requestedVersion) ||\n\t\t\tsignedRelease?.version !== releaseView.version\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Aggregator returned a release view that does not match the requested package or version.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst version = releaseView.version;\n\t\tif (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"RELEASE_YANKED\",\n\t\t\t\t\tmessage: \"This release has been withdrawn\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Environment compatibility remains an install-safety gate. Listing\n\t\t// approval says only that displayed metadata passed moderation. A release\n\t\t// may carry a `requires` block (`env:emdash`, `env:astro`, ...). Refuse\n\t\t// the install if the running host doesn't satisfy a constraint, so a\n\t\t// stale browser tab or non-UI caller can't bypass the admin's\n\t\t// disabled Install button. `requires` is lexicon-`unknown`; the\n\t\t// helper guards its shape.\n\t\tif (opts?.hostEnv) {\n\t\t\tconst envError = assertEnvCompatible(releaseView.release?.requires, opts.hostEnv);\n\t\t\tif (envError) return { success: false, error: envError };\n\t\t}\n\n\t\t// Step 3a: enforce the configured minimum release age. The browser\n\t\t// applies the same check up front for UX, but the gate lives here\n\t\t// -- a stale browser tab, a deep link, or a non-admin-UI caller\n\t\t// must still hit the holdback. The `minimumReleaseAgeExclude`\n\t\t// allowlist short-circuits the check for trusted publisher DIDs.\n\t\t//\n\t\t// Caveat: `releaseView.indexedAt` is aggregator-supplied envelope\n\t\t// data, not a signed timestamp. A compromised aggregator can\n\t\t// claim an arbitrary indexed-at date and bypass the holdback;\n\t\t// closing this gap requires fetching the release record's\n\t\t// signed createdAt from the publisher's PDS (deferred to the\n\t\t// follow-up that adds full MST verification). If the timestamp\n\t\t// is missing or malformed, we fail closed and reject the install.\n\t\t// `registryConfig` is the user-supplied integration option, not\n\t\t// the normalized manifest shape, so the duration parse runs once\n\t\t// per install. Catch a malformed value here -- normally caught at\n\t\t// `normalizeRegistryConfig` time, but a future config-mutation\n\t\t// path could re-enter with a bad value -- and surface it as a\n\t\t// structured error rather than letting it bubble out as a generic\n\t\t// 500.\n\t\tconst minimumReleaseAge = registryConfig.policy?.minimumReleaseAge;\n\t\tlet minimumReleaseAgeSeconds = 0;\n\t\tif (minimumReleaseAge !== undefined) {\n\t\t\ttry {\n\t\t\t\tminimumReleaseAgeSeconds = parseDurationSeconds(minimumReleaseAge);\n\t\t\t} catch (err) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"REGISTRY_POLICY_INVALID\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\terr instanceof Error\n\t\t\t\t\t\t\t\t? err.message\n\t\t\t\t\t\t\t\t: \"Invalid minimumReleaseAge value in registry config\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tif (minimumReleaseAgeSeconds > 0) {\n\t\t\tconst exclude = registryConfig.policy?.minimumReleaseAgeExclude?.map((e) =>\n\t\t\t\te.trim().toLowerCase(),\n\t\t\t);\n\t\t\tconst exempt = releaseExemptFromMinimumAge(exclude, publisherDid, slug);\n\t\t\tif (!exempt) {\n\t\t\t\tconst indexedAt = Date.parse(releaseView.indexedAt);\n\t\t\t\tif (!Number.isFinite(indexedAt)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcode: \"RELEASE_TIMESTAMP_INVALID\",\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t\"Release record is missing a valid indexed-at timestamp; cannot evaluate minimum release age policy.\",\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tconst ageSeconds = (Date.now() - indexedAt) / 1000;\n\t\t\t\tif (ageSeconds < minimumReleaseAgeSeconds) {\n\t\t\t\t\tconst remaining = Math.ceil(minimumReleaseAgeSeconds - ageSeconds);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: {\n\t\t\t\t\t\t\tcode: \"RELEASE_TOO_NEW\",\n\t\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\t`This release does not meet the configured minimum release age of ` +\n\t\t\t\t\t\t\t\t`${minimumReleaseAgeSeconds}s. It will be installable in ~${remaining}s.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Derive the normalized opaque plugin id we'll use as the\n\t\t// runtime-wide identifier from here on. The publisher_did + slug\n\t\t// stay in the state row for update resolution and admin display.\n\t\tconst pluginId = await makeRegistryPluginId(publisherDid, slug);\n\n\t\t// Block installation if a configured (trusted) plugin shares this\n\t\t// id. Mirrors the marketplace install's PLUGIN_ID_CONFLICT check.\n\t\tif (opts?.configuredPluginIds?.has(pluginId)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"PLUGIN_ID_CONFLICT\",\n\t\t\t\t\tmessage: \"A configured plugin with the same derived id already exists\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Check for an existing install (any source) under the derived id.\n\t\t// We reject all pre-existing rows -- if the row is from a registry\n\t\t// install of this same package, the caller should go through the\n\t\t// (future) update flow; if it's from any other source, the\n\t\t// pluginId collision means installing would silently mutate an\n\t\t// unrelated plugin's lifecycle row.\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (existing) {\n\t\t\tif (existing.source === \"registry\") {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"ALREADY_INSTALLED\",\n\t\t\t\t\t\tmessage: `Plugin ${publisherDid}/${slug} is already installed`,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"PLUGIN_ID_COLLISION\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t`A non-registry plugin already exists at the derived id ${pluginId}. ` +\n\t\t\t\t\t\t\"Uninstall it before installing this registry plugin.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Step 4: fetch the artifact bytes.\n\t\t// `releaseView.release` is lexicon-validated by DiscoveryClient (or\n\t\t// `null`); a missing source/checksum (including the `null` case) fails\n\t\t// closed below. Cache descriptors are unsigned aggregator operational data.\n\t\tconst release = releaseView.release;\n\t\tconst packageArtifact = release?.artifacts?.package;\n\t\tconst declaredChecksum = packageArtifact?.checksum;\n\n\t\tif (!packageArtifact || !declaredChecksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INVALID_RELEASE\",\n\t\t\t\t\tmessage: \"Release record is missing its package artifact or checksum\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst artifactBytes = await fetchArtifact(\n\t\t\treleaseView.artifactCaches ?? [],\n\t\t\tpackageArtifact,\n\t\t\t{\n\t\t\t\tdid: publisherDid,\n\t\t\t\tcollection: NSID.packageRelease,\n\t\t\t\trkey: `${slug}:${version}`,\n\t\t\t\tcid: releaseView.cid,\n\t\t\t},\n\t\t\trelease.auth,\n\t\t);\n\n\t\t// Step 5: verify the bytes against the signed record's checksum.\n\t\tconst checksumOk = await verifyChecksum(artifactBytes, declaredChecksum);\n\t\tif (!checksumOk) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CHECKSUM_MISMATCH\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Artifact bytes do not match the release record's checksum, or the checksum encoding is unsupported.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Step 6: extract the bundle.\n\t\tlet bundle: PluginBundle;\n\t\ttry {\n\t\t\tbundle = await extractBundle(artifactBytes);\n\t\t} catch (err) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INVALID_BUNDLE\",\n\t\t\t\t\tmessage: err instanceof Error ? err.message : \"Failed to extract plugin bundle\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Manifest sanity: declared version must match the release's version.\n\t\tif (bundle.manifest.version !== version) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MANIFEST_VERSION_MISMATCH\",\n\t\t\t\t\tmessage: `Bundle manifest version (${bundle.manifest.version}) does not match release version (${version})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Manifest identity: the bundle's `manifest.id` is the publisher's\n\t\t// natural plugin id (their slug). It MUST equal the slug the\n\t\t// install was requested for; otherwise a malicious registry bundle\n\t\t// could declare `manifest.id: \"audit-log\"` and confuse the sandbox\n\t\t// bridge, which uses `manifest.id` as the trust key for\n\t\t// per-plugin storage, cron schedules, and bridge-scoped\n\t\t// operations.\n\t\tif (bundle.manifest.id !== slug) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MANIFEST_ID_MISMATCH\",\n\t\t\t\t\tmessage: `Bundle manifest id (${bundle.manifest.id}) does not match registry slug (${slug})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Rewrite the manifest's id to the derived opaque pluginId before\n\t\t// it reaches R2 storage or the sandbox loader. The sandbox uses\n\t\t// `manifest.id` as its identity for per-plugin storage and bridge\n\t\t// calls; addressing it by the same pluginId we use in the runtime\n\t\t// cache, R2 prefix, and `_plugin_state` row keeps every layer\n\t\t// in sync and prevents registry installs from colliding with\n\t\t// marketplace plugins that happen to share the publisher's slug.\n\t\tbundle.manifest = { ...bundle.manifest, id: pluginId };\n\n\t\t// Integrity: the bundle that will run MUST declare exactly the access\n\t\t// the signed release record advertises. The consent dialog is driven\n\t\t// from the record's `declaredAccess`, so a bundle enforcing something\n\t\t// different -- a wider host allow-list, an extra capability -- would run\n\t\t// outside what the user reviewed. The capability-set consent gate below\n\t\t// is blind to constraint content (host scope), so compare the full\n\t\t// enforced access of record vs bundle here and refuse on any difference.\n\t\tconst recordExt =\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension\n\t\t\t(release?.extensions as Record<string, { declaredAccess?: DeclaredAccess }> | undefined)?.[\n\t\t\t\tRELEASE_EXTENSION_NSID\n\t\t\t];\n\t\tif (\n\t\t\t!enforcedAccessEqual(recordExt?.declaredAccess ?? {}, bundle.manifest.declaredAccess ?? {})\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"DECLARED_ACCESS_DRIFT\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"The plugin bundle declares different permissions than its published record. Installation refused.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Capability consent gate: the admin MUST acknowledge the\n\t\t// capabilities the bundle's manifest actually declares before we\n\t\t// install it. The bundle manifest is the only source of truth\n\t\t// the runtime sandbox enforces -- the release record's\n\t\t// `declaredAccess` extension is an aggregator-supplied\n\t\t// assertion that the publisher may or may not have included,\n\t\t// and trusting it would let a malicious publisher (or a\n\t\t// compromised aggregator) ship a bundle whose manifest\n\t\t// requests `content:*` etc. behind an empty consent dialog.\n\t\t//\n\t\t// Two outcomes after normalization (filter to strings, dedupe,\n\t\t// sort):\n\t\t//\n\t\t// 1. The bundle declares no capabilities: install is allowed\n\t\t// without any acknowledgement (nothing to consent to).\n\t\t// 2. The bundle declares capabilities: install requires the\n\t\t// caller to send `acknowledgedDeclaredAccess`, and the\n\t\t// sorted lists must match exactly.\n\t\t//\n\t\t// We compare against the bundle's *capabilities* (the legacy\n\t\t// shape) for v1 because EmDash's existing sandbox enforces\n\t\t// capabilities, not the RFC's structured `declaredAccess`. Once\n\t\t// the runtime starts enforcing `declaredAccess` natively, this\n\t\t// comparison switches to that shape.\n\t\tconst actualCapabilities = canonicalCapabilitiesForDriftCheck(bundle.manifest.capabilities);\n\t\tif (actualCapabilities.length > 0) {\n\t\t\tif (input.acknowledgedDeclaredAccess === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"DECLARED_ACCESS_REQUIRED\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"This plugin declares capabilities that require consent. Re-open the install dialog to review and acknowledge them.\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst acknowledged = canonicalCapabilitiesForDriftCheck(input.acknowledgedDeclaredAccess);\n\t\t\tif (\n\t\t\t\tacknowledged.length !== actualCapabilities.length ||\n\t\t\t\tacknowledged.some((cap, i) => cap !== actualCapabilities[i])\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"DECLARED_ACCESS_DRIFT\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"Plugin manifest has changed since you consented. Re-open the install dialog to review the new permissions.\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconst actualMcpTools = (bundle.manifest.mcp?.tools ?? []).map(\n\t\t\t({ inputSchema: _, outputSchema: __, ...tool }) => tool,\n\t\t);\n\t\tif (actualMcpTools.length > 0) {\n\t\t\tif (JSON.stringify(input.acknowledgedMcpTools) !== JSON.stringify(actualMcpTools)) {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\t\tmessage: \"Plugin MCP tools require explicit consent\",\n\t\t\t\t\t\tdetails: { mcpTools: actualMcpTools },\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Step 7: store in R2 under the registry prefix.\n\t\tawait storeBundleInR2(storage, pluginId, version, bundle, \"registry\");\n\n\t\t// Step 8: write plugin state.\n\t\t// Display name and description come from the *package profile*\n\t\t// (the signed record from the publisher's repo), not from the\n\t\t// bundle manifest -- the manifest carries the trust contract,\n\t\t// the profile carries the marketing copy.\n\t\t//\n\t\t// On failure, we may need to clean up the R2 bundle we just\n\t\t// wrote. But two parallel installs of the same (did, slug,\n\t\t// version) both pass the earlier `existing` check at line 822\n\t\t// (the read is not transactional with the insert), both upload\n\t\t// to the same deterministic R2 prefix (overwrites are\n\t\t// content-identical because R2 keys include the version and\n\t\t// the bundle is checksum-verified upstream), and then one wins\n\t\t// the insert while the other fails with a PK constraint\n\t\t// violation.\n\t\t//\n\t\t// If we blindly clean up R2 on every state-write failure, the\n\t\t// loser of that race would delete the winner's bundle and the\n\t\t// runtime would fail to load the plugin on the next sync.\n\t\t//\n\t\t// Instead: on state-write failure, re-query the state row. If\n\t\t// a row now exists for this pluginId, we lost the race -- the\n\t\t// winner owns the R2 bundle and we must not touch it. If the\n\t\t// row doesn't exist, the failure was a real DB error and the\n\t\t// R2 bytes are orphans; clean them up.\n\t\t//\n\t\t// Cleanup is best-effort; if it also fails, the row failure\n\t\t// still surfaces to the caller and the orphan R2 bundle costs\n\t\t// only the storage of a single checksum-verified zip.\n\t\t// `packageView.profile` is lexicon-validated by DiscoveryClient (or null).\n\t\tconst profile = packageView.profile;\n\t\ttry {\n\t\t\tawait stateRepo.upsert(pluginId, version, \"active\", {\n\t\t\t\tsource: \"registry\",\n\t\t\t\tdisplayName: profile?.name ?? slug,\n\t\t\t\tdescription: profile?.description ?? undefined,\n\t\t\t\tregistryPublisherDid: publisherDid,\n\t\t\t\tregistrySlug: slug,\n\t\t\t});\n\t\t} catch (stateErr) {\n\t\t\tlet lostRace = false;\n\t\t\ttry {\n\t\t\t\tconst winner = await stateRepo.get(pluginId);\n\t\t\t\tlostRace = winner !== undefined && winner !== null;\n\t\t\t} catch (probeErr) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[registry-install] Failed to probe state row for ${pluginId} after state-write failure; treating as orphan:`,\n\t\t\t\t\tprobeErr,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!lostRace) {\n\t\t\t\ttry {\n\t\t\t\t\tawait deleteBundleFromR2(storage, pluginId, version, \"registry\");\n\t\t\t\t} catch (cleanupErr) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[registry-install] Failed to clean up R2 bundle for ${pluginId}@${version} after state-row write failure:`,\n\t\t\t\t\t\tcleanupErr,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow stateErr;\n\t\t}\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tpluginId,\n\t\t\t\tpublisherDid,\n\t\t\t\tslug,\n\t\t\t\tversion,\n\t\t\t\tcapabilities: bundle.manifest.capabilities,\n\t\t\t},\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof ClientValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RESPONSE_INVALID\",\n\t\t\t\t\tmessage: `Aggregator returned a response that does not conform to its lexicon (${err.target})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof ClientResponseError) {\n\t\t\tif (err.error === \"ListingUnavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"LISTING_UNAVAILABLE\",\n\t\t\t\t\t\tmessage: \"This plugin is unavailable under the active registry policy\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.status === 404 ? \"AGGREGATOR_NOT_FOUND\" : \"AGGREGATOR_HTTP_ERROR\",\n\t\t\t\t\tmessage: `Aggregator returned ${err.status}: ${err.error}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof EmDashStorageError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"STORAGE_ERROR\",\n\t\t\t\t\tmessage: \"Storage error while installing plugin\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[registry-install] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"INSTALL_FAILED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Failed to install plugin from registry\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Uninstall ──────────────────────────────────────────────────────\n\nexport interface RegistryUninstallResult {\n\tpluginId: string;\n\t/** True when `_plugin_storage` rows were also deleted (opts.deleteData). */\n\tdataDeleted: boolean;\n}\n\n/**\n * Uninstall a registry-source plugin. Deletes the R2 bundle under\n * `registry/<pluginId>/<version>/`, optionally drops the plugin's\n * `_plugin_storage` rows, and removes the `_plugin_state` row. The\n * sandbox runtime is reconciled by the route's `syncRegistryPlugins`\n * call after this returns.\n *\n * Refuses to uninstall plugins whose `source` is not `\"registry\"` to\n * avoid trashing a marketplace/config plugin that happens to share the\n * pluginId namespace.\n */\nexport async function handleRegistryUninstall(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tpluginId: string,\n\topts?: { deleteData?: boolean },\n): Promise<ApiResult<RegistryUninstallResult>> {\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"registry\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NOT_FOUND\",\n\t\t\t\t\tmessage: `No registry plugin found: ${pluginId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// `_plugin_state.version` carries the installed version directly for\n\t\t// registry-source rows (there's no shadow column like marketplace's\n\t\t// `marketplaceVersion`). Use it verbatim for the R2 prefix.\n\t\tconst version = existing.version;\n\n\t\t// Order: optional storage cleanup → bundle delete → state row delete.\n\t\t// The most failure-prone step runs first so a transient DB error\n\t\t// (deadlock, contention) cascades to the outer catch with the state\n\t\t// row and bundle intact — admin retries safely. Bundle delete is\n\t\t// idempotent on misses.\n\t\tlet dataDeleted = false;\n\t\tif (opts?.deleteData) {\n\t\t\tawait db.deleteFrom(\"_plugin_storage\").where(\"plugin_id\", \"=\", pluginId).execute();\n\t\t\tdataDeleted = true;\n\t\t}\n\n\t\tif (storage) {\n\t\t\tawait deleteBundleFromR2(storage, pluginId, version, \"registry\");\n\t\t}\n\n\t\ttry {\n\t\t\tawait removeAllPluginIndexes(db, pluginId);\n\t\t} catch {\n\t\t\t// Nothing to drop, or tracking table predates the feature\n\t\t}\n\n\t\tawait stateRepo.delete(pluginId);\n\n\t\treturn { success: true, data: { pluginId, dataDeleted } };\n\t} catch (err) {\n\t\tconsole.error(\"[registry-uninstall] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"UNINSTALL_FAILED\",\n\t\t\t\tmessage: \"Failed to uninstall plugin\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update ─────────────────────────────────────────────────────────\n\nexport interface RegistryUpdateResult {\n\tpluginId: string;\n\toldVersion: string;\n\tnewVersion: string;\n\tcapabilityChanges: { added: string[]; removed: string[] };\n\t/** Set only when `newlyPublic` is non-empty, mirroring marketplace. */\n\trouteVisibilityChanges?: { newlyPublic: string[] };\n}\n\n/**\n * Update a registry-source plugin to a newer release. Mirrors\n * `handleMarketplaceUpdate`: resolves the target version via the aggregator,\n * re-runs the artifact fetch / checksum / extract pipeline, diffs capabilities\n * and route visibility against the currently installed bundle, and gates\n * escalations behind `confirmCapabilityChanges` / `confirmRouteVisibilityChanges`\n * so the admin re-consents to widened permissions.\n *\n * Refuses non-registry sources. Refuses when the stored state row is missing\n * the `(publisherDid, slug)` it needs to resolve against the aggregator.\n */\nexport async function handleRegistryUpdate(\n\tdb: Kysely<Database>,\n\tstorage: Storage | null,\n\tsandboxRunner: SandboxRunner | null,\n\tregistryConfigInput: RegistryConfigInput | undefined,\n\tpluginId: string,\n\topts?: {\n\t\tversion?: string;\n\t\tconfirmCapabilityChanges?: boolean;\n\t\tconfirmRouteVisibilityChanges?: boolean;\n\t\tconfirmMcpTools?: boolean;\n\t\thostEnv?: HostEnv;\n\t},\n): Promise<ApiResult<RegistryUpdateResult>> {\n\tconst registryConfig = coerceRegistryConfig(registryConfigInput);\n\tif (!registryConfig) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"REGISTRY_NOT_CONFIGURED\", message: \"Registry is not configured\" },\n\t\t};\n\t}\n\tif (!storage) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"STORAGE_NOT_CONFIGURED\",\n\t\t\t\tmessage: \"Storage is required for registry plugin updates\",\n\t\t\t},\n\t\t};\n\t}\n\tif (!sandboxRunner || !sandboxRunner.isAvailable()) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"SANDBOX_NOT_AVAILABLE\", message: \"Sandbox runner is required\" },\n\t\t};\n\t}\n\ttry {\n\t\tvalidateAggregatorUrl(registryConfig.aggregatorUrl);\n\t} catch (err) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"REGISTRY_NOT_CONFIGURED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Invalid aggregator URL\",\n\t\t\t},\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst existing = await stateRepo.get(pluginId);\n\t\tif (!existing || existing.source !== \"registry\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"NOT_FOUND\", message: `No registry plugin found: ${pluginId}` },\n\t\t\t};\n\t\t}\n\t\tif (!existing.registryPublisherDid || !existing.registrySlug) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INVALID_STATE\",\n\t\t\t\t\tmessage: `Registry plugin ${pluginId} is missing publisher DID or slug in state`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconst oldVersion = existing.version;\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- existing.registryPublisherDid is a DID string written by the install handler\n\t\tconst publisherDid = existing.registryPublisherDid as Did;\n\t\tconst slug = existing.registrySlug;\n\n\t\tconst { DiscoveryClient, registryLabelerPolicy } =\n\t\t\tawait import(\"@emdash-cms/registry-client/discovery\");\n\t\tconst aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;\n\t\tconst discovery = new DiscoveryClient({\n\t\t\taggregatorUrl: registryConfig.aggregatorUrl,\n\t\t\tacceptLabelers: registryConfig.acceptLabelers,\n\t\t\tlabelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),\n\t\t\tfetch: timedFetch(aggregatorDeadline),\n\t\t});\n\n\t\t// Resolve target release. Explicit version → paginate listReleases;\n\t\t// otherwise getLatestRelease (aggregator applies its own filters).\n\t\tconst MAX_LIST_PAGES = 20;\n\t\tconst releaseView = await (async () => {\n\t\t\tif (!opts?.version) {\n\t\t\t\treturn discovery.getLatestRelease({ did: publisherDid, package: slug });\n\t\t\t}\n\t\t\tlet cursor: string | undefined;\n\t\t\tconst seenCursors = new Set<string>();\n\t\t\tfor (let page = 0; page < MAX_LIST_PAGES; page++) {\n\t\t\t\tif (cursor !== undefined) {\n\t\t\t\t\tif (seenCursors.has(cursor)) break;\n\t\t\t\t\tseenCursors.add(cursor);\n\t\t\t\t}\n\t\t\t\tconst result = await discovery.listReleases({\n\t\t\t\t\tdid: publisherDid,\n\t\t\t\t\tpackage: slug,\n\t\t\t\t\tcursor,\n\t\t\t\t\tlimit: 50,\n\t\t\t\t});\n\t\t\t\tfor (const r of result.releases) {\n\t\t\t\t\tif (r.version === opts.version) return r;\n\t\t\t\t}\n\t\t\t\tif (!result.cursor) break;\n\t\t\t\tcursor = result.cursor;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t})();\n\n\t\tif (!releaseView) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"NO_VERSION\",\n\t\t\t\t\tmessage: opts?.version\n\t\t\t\t\t\t? `Version ${opts.version} not found for ${publisherDid}/${slug}`\n\t\t\t\t\t\t: `No installable release found for ${publisherDid}/${slug}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Identity cross-check. A buggy/compromised aggregator must not\n\t\t// trick us into installing a record signed for a different\n\t\t// (did, slug, version) under this plugin's pluginId.\n\t\tconst signedRelease = releaseView.release;\n\t\tif (\n\t\t\treleaseView.did !== publisherDid ||\n\t\t\treleaseView.package !== slug ||\n\t\t\tsignedRelease?.package !== slug ||\n\t\t\t(opts?.version !== undefined && releaseView.version !== opts.version) ||\n\t\t\tsignedRelease?.version !== releaseView.version\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Aggregator returned a release view that does not match the requested package or version.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst newVersion = releaseView.version;\n\t\tif (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: \"YANKED\", message: \"Release has been withdrawn\" },\n\t\t\t};\n\t\t}\n\t\tif (newVersion === oldVersion) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ALREADY_UP_TO_DATE\",\n\t\t\t\t\tmessage: \"Plugin is already at the requested version\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Environment compatibility remains independent from listing approval.\n\t\t// An ungated update could otherwise\n\t\t// land a version whose `requires` the host doesn't satisfy. Same\n\t\t// guard as install; `requires` is lexicon-`unknown`.\n\t\tif (opts?.hostEnv) {\n\t\t\tconst envError = assertEnvCompatible(signedRelease.requires, opts.hostEnv);\n\t\t\tif (envError) return { success: false, error: envError };\n\t\t}\n\n\t\tconst packageArtifact = signedRelease.artifacts?.package;\n\t\tconst declaredChecksum = packageArtifact?.checksum;\n\t\tif (!packageArtifact || !declaredChecksum) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"INVALID_RELEASE\",\n\t\t\t\t\tmessage: \"Release record is missing its package artifact or checksum\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst artifactBytes = await fetchArtifact(\n\t\t\treleaseView.artifactCaches ?? [],\n\t\t\tpackageArtifact,\n\t\t\t{\n\t\t\t\tdid: publisherDid,\n\t\t\t\tcollection: NSID.packageRelease,\n\t\t\t\trkey: `${slug}:${newVersion}`,\n\t\t\t\tcid: releaseView.cid,\n\t\t\t},\n\t\t\tsignedRelease.auth,\n\t\t);\n\t\tif (!(await verifyChecksum(artifactBytes, declaredChecksum))) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CHECKSUM_MISMATCH\",\n\t\t\t\t\tmessage: \"Artifact bytes do not match the release's published checksum\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst bundle: PluginBundle = await extractBundle(artifactBytes);\n\n\t\tif (bundle.manifest.version !== newVersion) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"BUNDLE_VERSION_MISMATCH\",\n\t\t\t\t\tmessage: `Bundle manifest version (${bundle.manifest.version}) does not match release version (${newVersion})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (bundle.manifest.id !== slug) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"BUNDLE_IDENTITY_MISMATCH\",\n\t\t\t\t\tmessage: `Bundle manifest id (${bundle.manifest.id}) does not match registry slug (${slug})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Rewrite manifest.id to the opaque pluginId so the sandbox loader\n\t\t// and R2 layout stay in sync across install and update.\n\t\tbundle.manifest = { ...bundle.manifest, id: pluginId };\n\n\t\t// Integrity: same gate as install. The new bundle must declare exactly\n\t\t// the access its signed release record advertises. Without it, an update\n\t\t// that changes only the host scope (e.g. api.good.com -> evil.com) keeps\n\t\t// the capability set identical, sails through the escalation diff below,\n\t\t// and installs a bundle enforcing a scope the record never showed.\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension\n\t\tconst updateRecordExtensions = signedRelease?.extensions as\n\t\t\t| Record<string, { declaredAccess?: DeclaredAccess }>\n\t\t\t| undefined;\n\t\tconst recordExt = updateRecordExtensions?.[RELEASE_EXTENSION_NSID];\n\t\tif (\n\t\t\t!enforcedAccessEqual(recordExt?.declaredAccess ?? {}, bundle.manifest.declaredAccess ?? {})\n\t\t) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"DECLARED_ACCESS_DRIFT\",\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"The plugin bundle declares different permissions than its published record. Update refused.\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Diff capabilities + route visibility against the currently\n\t\t// installed bundle. Loading from R2 keeps us honest: the diff is\n\t\t// against the bytes the sandbox is actually running, not whatever\n\t\t// the state row claims.\n\t\tconst oldBundle = await loadBundleFromR2(storage, pluginId, oldVersion, \"registry\");\n\t\tconst oldCaps = oldBundle?.manifest.capabilities ?? [];\n\t\tconst capabilityChanges = diffCapabilities(oldCaps, bundle.manifest.capabilities);\n\t\tconst hasEscalation = capabilityChanges.added.length > 0;\n\t\tif (hasEscalation && !opts?.confirmCapabilityChanges) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"CAPABILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update requires new capabilities\",\n\t\t\t\t\tdetails: { capabilityChanges },\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst routeVisibilityChanges = diffRouteVisibility(oldBundle?.manifest, bundle.manifest);\n\t\tconst hasNewPublicRoutes = routeVisibilityChanges.newlyPublic.length > 0;\n\t\tif (hasNewPublicRoutes && !opts?.confirmRouteVisibilityChanges) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"ROUTE_VISIBILITY_ESCALATION\",\n\t\t\t\t\tmessage: \"Plugin update exposes new public (unauthenticated) routes\",\n\t\t\t\t\tdetails: { routeVisibilityChanges, capabilityChanges },\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst oldMcpTools = [...(oldBundle?.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tconst newMcpTools = [...(bundle.manifest.mcp?.tools ?? [])].toSorted((a, b) =>\n\t\t\ta.name.localeCompare(b.name),\n\t\t);\n\t\tif (JSON.stringify(oldMcpTools) !== JSON.stringify(newMcpTools) && !opts?.confirmMcpTools) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"MCP_TOOL_CONSENT_REQUIRED\",\n\t\t\t\t\tmessage: \"Plugin update changes its MCP tools\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tmcpTools: newMcpTools.map(({ inputSchema: _, outputSchema: __, ...tool }) => tool),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Store new bundle. R2 prefix is deterministic per (pluginId, version),\n\t\t// so a retry of the same update is idempotent.\n\t\tawait storeBundleInR2(storage, pluginId, newVersion, bundle, \"registry\");\n\n\t\t// Update state. Preserve publisher/slug; refresh displayName /\n\t\t// description from the install handler's seeded values (we don't\n\t\t// re-fetch the profile here — that's a separate `getPackage` round\n\t\t// trip and the install-time values are still authoritative for\n\t\t// the same package identity).\n\t\tawait stateRepo.upsert(pluginId, newVersion, \"active\", {\n\t\t\tsource: \"registry\",\n\t\t\tregistryPublisherDid: publisherDid,\n\t\t\tregistrySlug: slug,\n\t\t\tdisplayName: existing.displayName ?? slug,\n\t\t\tdescription: existing.description ?? undefined,\n\t\t\tmcpToolsEnabled: false,\n\t\t\tmcpToolsConsent: null,\n\t\t});\n\n\t\tawait syncDeclaredStorageIndexes(db, [bundle.manifest]);\n\n\t\t// Best-effort cleanup of the old bundle. Failures here don't roll\n\t\t// back the upgrade (the new bundle is already stored and committed\n\t\t// in the state row); the orphan is just storage we'll pay for.\n\t\tdeleteBundleFromR2(storage, pluginId, oldVersion, \"registry\").catch(() => {});\n\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\tpluginId,\n\t\t\t\toldVersion,\n\t\t\t\tnewVersion,\n\t\t\t\tcapabilityChanges,\n\t\t\t\trouteVisibilityChanges: hasNewPublicRoutes ? routeVisibilityChanges : undefined,\n\t\t\t},\n\t\t};\n\t} catch (err) {\n\t\tif (err instanceof ClientValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RESPONSE_INVALID\",\n\t\t\t\t\tmessage: `Aggregator returned a response that does not conform to its lexicon (${err.target})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof ClientResponseError) {\n\t\t\tif (err.error === \"ListingUnavailable\") {\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: false,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tcode: \"LISTING_UNAVAILABLE\",\n\t\t\t\t\t\tmessage: \"This plugin is unavailable under the active registry policy\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.status === 404 ? \"AGGREGATOR_NOT_FOUND\" : \"AGGREGATOR_HTTP_ERROR\",\n\t\t\t\t\tmessage: `Aggregator returned ${err.status}: ${err.error}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof EmDashStorageError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.code ?? \"STORAGE_ERROR\",\n\t\t\t\t\tmessage: \"Storage error while updating plugin\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[registry-update] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: \"UPDATE_FAILED\",\n\t\t\t\tmessage: err instanceof Error ? err.message : \"Failed to update plugin\",\n\t\t\t},\n\t\t};\n\t}\n}\n\n// ── Update check ───────────────────────────────────────────────────\n\nexport interface RegistryUpdateCheck {\n\tpluginId: string;\n\tinstalled: string;\n\tlatest: string;\n\thasUpdate: boolean;\n\t/**\n\t * Both diff fields are `false` here by design: computing them at\n\t * update-check time would require downloading both bundles (or\n\t * extracting from the signed release extension and the installed\n\t * R2 bundle), which is too expensive for a bulk preview. The actual\n\t * escalation gate runs at update time in `handleRegistryUpdate`.\n\t * Mirrors marketplace's `hasRouteVisibilityChanges: false`.\n\t */\n\thasCapabilityChanges: boolean;\n\thasRouteVisibilityChanges: boolean;\n}\n\n/**\n * Bulk update check across every installed registry plugin. Queries the\n * aggregator for each plugin's latest release and reports `hasUpdate`\n * based on the version comparison. Plugins whose aggregator lookup fails\n * (unreachable, delisted, malformed) are skipped silently — one bad\n * publisher must not blank the whole admin Updates list.\n */\nexport async function handleRegistryUpdateCheck(\n\tdb: Kysely<Database>,\n\tregistryConfigInput: RegistryConfigInput | undefined,\n): Promise<ApiResult<{ items: RegistryUpdateCheck[] }>> {\n\tconst registryConfig = coerceRegistryConfig(registryConfigInput);\n\tif (!registryConfig) {\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"REGISTRY_NOT_CONFIGURED\", message: \"Registry is not configured\" },\n\t\t};\n\t}\n\n\ttry {\n\t\tconst stateRepo = new PluginStateRepository(db);\n\t\tconst registryPlugins = await stateRepo.getRegistryPlugins();\n\t\tif (registryPlugins.length === 0) {\n\t\t\treturn { success: true, data: { items: [] } };\n\t\t}\n\n\t\tconst { DiscoveryClient, registryLabelerPolicy } =\n\t\t\tawait import(\"@emdash-cms/registry-client/discovery\");\n\t\tconst aggregatorDeadline = Date.now() + AGGREGATOR_TOTAL_BUDGET_MS;\n\t\tconst discovery = new DiscoveryClient({\n\t\t\taggregatorUrl: registryConfig.aggregatorUrl,\n\t\t\tacceptLabelers: registryConfig.acceptLabelers,\n\t\t\tlabelerPolicy: registryLabelerPolicy(registryConfig.acceptLabelers),\n\t\t\tfetch: timedFetch(aggregatorDeadline),\n\t\t});\n\n\t\tconst items: RegistryUpdateCheck[] = [];\n\t\tfor (const plugin of registryPlugins) {\n\t\t\tif (!plugin.registryPublisherDid || !plugin.registrySlug) continue;\n\t\t\ttry {\n\t\t\t\tconst releaseView = await discovery.getLatestRelease({\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- DID string was validated by the install handler\n\t\t\t\t\tdid: plugin.registryPublisherDid as Did,\n\t\t\t\t\tpackage: plugin.registrySlug,\n\t\t\t\t});\n\t\t\t\tif (evaluateRegistryReleaseWithdrawal(releaseView, discovery.labelerPolicy).withdrawn) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst latest = releaseView.version;\n\t\t\t\tif (!latest) continue;\n\t\t\t\tconst installed = plugin.version;\n\t\t\t\titems.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tinstalled,\n\t\t\t\t\tlatest,\n\t\t\t\t\thasUpdate: latest !== installed,\n\t\t\t\t\thasCapabilityChanges: false,\n\t\t\t\t\thasRouteVisibilityChanges: false,\n\t\t\t\t});\n\t\t\t} catch (err) {\n\t\t\t\t// Skip plugins that can't be checked. Don't fail the whole\n\t\t\t\t// list because one aggregator query went wrong.\n\t\t\t\tconsole.warn(`[registry-update-check] Skipped ${plugin.pluginId}:`, err);\n\t\t\t}\n\t\t}\n\n\t\treturn { success: true, data: { items } };\n\t} catch (err) {\n\t\tif (err instanceof ClientValidationError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: \"AGGREGATOR_RESPONSE_INVALID\",\n\t\t\t\t\tmessage: `Aggregator returned a response that does not conform to its lexicon (${err.target})`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (err instanceof ClientResponseError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: err.status === 404 ? \"AGGREGATOR_NOT_FOUND\" : \"AGGREGATOR_HTTP_ERROR\",\n\t\t\t\t\tmessage: `Aggregator returned ${err.status}: ${err.error}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[registry-update-check] Failed:\", err);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: { code: \"UPDATE_CHECK_FAILED\", message: \"Failed to check for registry updates\" },\n\t\t};\n\t}\n}\n"],"mappings":"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"}
|