emdash 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-34EN2eXO.d.mts → adapters-Bb1vG41D.d.mts} +8 -1
- package/dist/adapters-Bb1vG41D.d.mts.map +1 -0
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +9 -9
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +1 -1
- package/dist/{api-DdAsirBe.mjs → api-BeBglVxU.mjs} +10 -10
- package/dist/{api-DdAsirBe.mjs.map → api-BeBglVxU.mjs.map} +1 -1
- package/dist/{api-tokens-CFXu8F_2.mjs → api-tokens-KO24G1Fd.mjs} +2 -2
- package/dist/{api-tokens-CFXu8F_2.mjs.map → api-tokens-KO24G1Fd.mjs.map} +1 -1
- package/dist/{apply-D5zfeD09.mjs → apply-kUDt80-G.mjs} +16 -16
- package/dist/{apply-D5zfeD09.mjs.map → apply-kUDt80-G.mjs.map} +1 -1
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +71 -15
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.mjs +4 -4
- package/dist/astro/middleware/redirect.mjs +2 -2
- package/dist/astro/middleware/request-context.mjs +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +10 -10
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +143 -74
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +1 -1
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +2 -2
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +2 -2
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +3 -3
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +7 -7
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +7 -7
- package/dist/astro/routes/api/admin/bylines/index.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/comments/bulk.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/counts.mjs +2 -2
- package/dist/astro/routes/api/admin/comments/index.mjs +3 -3
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +1 -1
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +1 -1
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +7 -7
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +7 -7
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +4 -4
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs +7 -7
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +7 -7
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +1 -1
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +29 -29
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +29 -29
- package/dist/astro/routes/api/admin/plugins/updates.mjs +28 -28
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +28 -28
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +28 -28
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +1 -1
- package/dist/astro/routes/api/admin/users/index.mjs +1 -1
- package/dist/astro/routes/api/auth/dev-bypass.d.mts.map +1 -1
- package/dist/astro/routes/api/auth/dev-bypass.mjs +2 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/index.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
- package/dist/astro/routes/api/auth/logout.mjs +1 -1
- package/dist/astro/routes/api/auth/magic-link/send.mjs +3 -3
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +1 -1
- package/dist/astro/routes/api/auth/me.mjs +2 -2
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +2 -2
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/request.mjs +3 -3
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +5 -5
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/authors.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/index.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/trash.mjs +2 -2
- package/dist/astro/routes/api/dashboard.mjs +4 -4
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +1 -1
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +7 -7
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
- package/dist/astro/routes/api/import/wordpress/media.mjs +2 -2
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +11 -11
- package/dist/astro/routes/api/manifest.mjs +2 -2
- package/dist/astro/routes/api/mcp.mjs +38 -38
- package/dist/astro/routes/api/media/_id_/confirm.mjs +2 -2
- package/dist/astro/routes/api/media/_id_/upload.mjs +2 -2
- package/dist/astro/routes/api/media/_id_/usage.mjs +6 -6
- package/dist/astro/routes/api/media/_id_.mjs +5 -5
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +1 -1
- package/dist/astro/routes/api/media/providers/index.mjs +1 -1
- package/dist/astro/routes/api/media/upload-url.mjs +3 -3
- package/dist/astro/routes/api/media.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_/items.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_/translations.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_.mjs +3 -3
- package/dist/astro/routes/api/menus/index.mjs +3 -3
- package/dist/astro/routes/api/oauth/authorize.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
- package/dist/astro/routes/api/oauth/register.mjs +1 -1
- package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
- package/dist/astro/routes/api/oauth/token.mjs +4 -4
- package/dist/astro/routes/api/openapi.json.mjs +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/index.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/summary.mjs +3 -3
- package/dist/astro/routes/api/redirects/_id_.mjs +4 -4
- package/dist/astro/routes/api/redirects/index.mjs +4 -4
- package/dist/astro/routes/api/relations/_id_/index.mjs +9 -9
- package/dist/astro/routes/api/relations/_id_/translations.mjs +8 -8
- package/dist/astro/routes/api/relations/index.mjs +9 -9
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +1 -1
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/index.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/reorder.mjs +28 -28
- package/dist/astro/routes/api/schema/index.mjs +6 -6
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +28 -28
- package/dist/astro/routes/api/schema/orphans/index.mjs +28 -28
- package/dist/astro/routes/api/search/enable.mjs +5 -5
- package/dist/astro/routes/api/search/index.mjs +4 -4
- package/dist/astro/routes/api/search/rebuild.mjs +5 -5
- package/dist/astro/routes/api/search/stats.mjs +4 -4
- package/dist/astro/routes/api/search/suggest.mjs +4 -4
- package/dist/astro/routes/api/sections/_slug_.mjs +4 -4
- package/dist/astro/routes/api/sections/index.mjs +4 -4
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +3 -3
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +3 -3
- package/dist/astro/routes/api/settings/backups/export.mjs +3 -3
- package/dist/astro/routes/api/settings/backups/index.mjs +3 -3
- package/dist/astro/routes/api/settings/email.mjs +1 -1
- package/dist/astro/routes/api/settings.mjs +5 -5
- package/dist/astro/routes/api/setup/admin-verify.mjs +4 -4
- package/dist/astro/routes/api/setup/admin.mjs +4 -4
- package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +18 -21
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/index.mjs +15 -21
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +2 -2
- package/dist/astro/routes/api/snapshot.mjs +4 -4
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/index.mjs +6 -6
- package/dist/astro/routes/api/themes/preview.mjs +2 -2
- package/dist/astro/routes/api/typegen.mjs +3 -3
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +1 -1
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +4 -4
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +4 -4
- package/dist/astro/routes/api/widget-areas/_name_.mjs +3 -3
- package/dist/astro/routes/api/widget-areas/index.mjs +4 -4
- package/dist/astro/routes/robots.txt.mjs +2 -2
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +2 -2
- package/dist/astro/routes/sitemap.xml.mjs +2 -2
- package/dist/astro/types.d.mts +14 -14
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-1iFJzPaZ.mjs → authorize-DUElxgOQ.mjs} +1 -1
- package/dist/{authorize-1iFJzPaZ.mjs.map → authorize-DUElxgOQ.mjs.map} +1 -1
- package/dist/{backup-LHjIAVNP.mjs → backup-DWYpJlLj.mjs} +3 -3
- package/dist/{backup-LHjIAVNP.mjs.map → backup-DWYpJlLj.mjs.map} +1 -1
- package/dist/{bundle-CBEXrBnb.mjs → bundle-vn56sLos.mjs} +5 -5
- package/dist/{bundle-CBEXrBnb.mjs.map → bundle-vn56sLos.mjs.map} +1 -1
- package/dist/{byline-fields-C5S8wygO.mjs → byline-fields-txaLGoME.mjs} +1 -1
- package/dist/{byline-fields-C5S8wygO.mjs.map → byline-fields-txaLGoME.mjs.map} +1 -1
- package/dist/{byline-pEcoDps8.mjs → byline-yjvWq4ip.mjs} +4 -4
- package/dist/{byline-pEcoDps8.mjs.map → byline-yjvWq4ip.mjs.map} +1 -1
- package/dist/{bylines-DJfSfXCL.mjs → bylines-Bu7pchQ4.mjs} +3 -3
- package/dist/{bylines-DJfSfXCL.mjs.map → bylines-Bu7pchQ4.mjs.map} +1 -1
- package/dist/{bylines-BWemdX2k.mjs → bylines-DjXS5p0P.mjs} +5 -5
- package/dist/{bylines-BWemdX2k.mjs.map → bylines-DjXS5p0P.mjs.map} +1 -1
- package/dist/{cache-CBJaDy_E.mjs → cache-C46-3Irl.mjs} +2 -2
- package/dist/{cache-CBJaDy_E.mjs.map → cache-C46-3Irl.mjs.map} +1 -1
- package/dist/{challenge-store-CZyFVaad.mjs → challenge-store-SIL5DsF2.mjs} +1 -1
- package/dist/{challenge-store-CZyFVaad.mjs.map → challenge-store-SIL5DsF2.mjs.map} +1 -1
- package/dist/{chunks-DfPax1I0.mjs → chunks-fiZ8G9RJ.mjs} +2 -2
- package/dist/{chunks-DfPax1I0.mjs.map → chunks-fiZ8G9RJ.mjs.map} +1 -1
- package/dist/cli/index.mjs +551 -51
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.mjs +2 -2
- package/dist/{collection-deletion-C8BugSGz.mjs → collection-deletion-DQPvMD8R.mjs} +2 -2
- package/dist/{collection-deletion-C8BugSGz.mjs.map → collection-deletion-DQPvMD8R.mjs.map} +1 -1
- package/dist/{comment-reaction-D6qfHKBc.mjs → comment-reaction-D-2J84UE.mjs} +2 -2
- package/dist/{comment-reaction-D6qfHKBc.mjs.map → comment-reaction-D-2J84UE.mjs.map} +1 -1
- package/dist/{comments-CTOuhTSX.mjs → comments-CTfZtLmM.mjs} +1 -1
- package/dist/{comments-CTOuhTSX.mjs.map → comments-CTfZtLmM.mjs.map} +1 -1
- package/dist/config-COQzTe5Q.d.mts +34 -0
- package/dist/config-COQzTe5Q.d.mts.map +1 -0
- package/dist/{content-BNfwh7xX.mjs → content-ZHCc1ngN.mjs} +3 -3
- package/dist/{content-BNfwh7xX.mjs.map → content-ZHCc1ngN.mjs.map} +1 -1
- package/dist/{content-list-query-DhQ84eVV.d.mts → content-list-query-wJ2WN8Vp.d.mts} +1 -1
- package/dist/{content-list-query-DhQ84eVV.d.mts.map → content-list-query-wJ2WN8Vp.d.mts.map} +1 -1
- package/dist/{content-refresh-DNERP4-f.mjs → content-refresh-B_8SJzuU.mjs} +2 -2
- package/dist/{content-refresh-DNERP4-f.mjs.map → content-refresh-B_8SJzuU.mjs.map} +1 -1
- package/dist/{context-DRgOvSnW.mjs → context-ZldbZcle.mjs} +7 -7
- package/dist/{context-DRgOvSnW.mjs.map → context-ZldbZcle.mjs.map} +1 -1
- package/dist/{dashboard-CB8J89rJ.mjs → dashboard-DvHbk_cv.mjs} +3 -3
- package/dist/{dashboard-CB8J89rJ.mjs.map → dashboard-DvHbk_cv.mjs.map} +1 -1
- package/dist/db/index.d.mts +4 -4
- package/dist/db/index.mjs +30 -7
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql-migrations.d.mts +11 -0
- package/dist/db/libsql-migrations.d.mts.map +1 -0
- package/dist/db/libsql-migrations.mjs +57 -0
- package/dist/db/libsql-migrations.mjs.map +1 -0
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres-migrations.d.mts +10 -0
- package/dist/db/postgres-migrations.d.mts.map +1 -0
- package/dist/db/postgres-migrations.mjs +47 -0
- package/dist/db/postgres-migrations.mjs.map +1 -0
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite-migrations.d.mts +10 -0
- package/dist/db/sqlite-migrations.d.mts.map +1 -0
- package/dist/db/sqlite-migrations.mjs +28 -0
- package/dist/db/sqlite-migrations.mjs.map +1 -0
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{deferred-tasks-BMsa9F64.d.mts → deferred-tasks-SDWxrgzJ.d.mts} +1 -1
- package/dist/{deferred-tasks-BMsa9F64.d.mts.map → deferred-tasks-SDWxrgzJ.d.mts.map} +1 -1
- package/dist/{device-flow-CaioYGK3.mjs → device-flow-C0GdD_z8.mjs} +4 -4
- package/dist/{device-flow-CaioYGK3.mjs.map → device-flow-C0GdD_z8.mjs.map} +1 -1
- package/dist/direct-executor-CDXeJCSs.mjs +85 -0
- package/dist/direct-executor-CDXeJCSs.mjs.map +1 -0
- package/dist/{escape-C_aLKP3H.mjs → escape-DUY2r2Zs.mjs} +1 -1
- package/dist/{escape-C_aLKP3H.mjs.map → escape-DUY2r2Zs.mjs.map} +1 -1
- package/dist/{field-defs-cache-hg6QkjV9.mjs → field-defs-cache-BzwBlJrR.mjs} +2 -2
- package/dist/{field-defs-cache-hg6QkjV9.mjs.map → field-defs-cache-BzwBlJrR.mjs.map} +1 -1
- package/dist/{fts-manager-CZ-5dVwJ.mjs → fts-manager-DyZEohZC.mjs} +2 -2
- package/dist/{fts-manager-CZ-5dVwJ.mjs.map → fts-manager-DyZEohZC.mjs.map} +1 -1
- package/dist/{hreflang-DNswoYsi.mjs → hreflang-BBe83kKa.mjs} +5 -5
- package/dist/{hreflang-DNswoYsi.mjs.map → hreflang-BBe83kKa.mjs.map} +1 -1
- package/dist/{hreflang-D3Uehfv2.d.mts → hreflang-DwT2E0Sa.d.mts} +2 -2
- package/dist/{hreflang-D3Uehfv2.d.mts.map → hreflang-DwT2E0Sa.d.mts.map} +1 -1
- package/dist/identity-Ys6Dlc9C.mjs +35 -0
- package/dist/identity-Ys6Dlc9C.mjs.map +1 -0
- package/dist/{import-B41u8Rot.mjs → import-DPkLmeXO.mjs} +2 -2
- package/dist/{import-B41u8Rot.mjs.map → import-DPkLmeXO.mjs.map} +1 -1
- package/dist/{index-C3Y-rsyN.d.mts → index-BVBZj4-7.d.mts} +28 -49
- package/dist/index-BVBZj4-7.d.mts.map +1 -0
- package/dist/{index-Cof4-Fab.d.mts → index-C9KuQyO-.d.mts} +3 -3
- package/dist/{index-Cof4-Fab.d.mts.map → index-C9KuQyO-.d.mts.map} +1 -1
- package/dist/index.d.mts +21 -20
- package/dist/index.mjs +42 -42
- package/dist/{load-DmvJrtxS.mjs → load-YJ2xl3ra.mjs} +2 -2
- package/dist/{load-DmvJrtxS.mjs.map → load-YJ2xl3ra.mjs.map} +1 -1
- package/dist/{loader-DLBqRiGP.mjs → loader-CdG_Ci3H.mjs} +2 -2
- package/dist/{loader-DLBqRiGP.mjs.map → loader-CdG_Ci3H.mjs.map} +1 -1
- package/dist/manifest-builder-B252yQUf.mjs +189 -0
- package/dist/manifest-builder-B252yQUf.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/local-runtime.d.mts +12 -12
- package/dist/media/local-runtime.mjs +2 -2
- package/dist/{media-allowlist-BD6HhgXA.mjs → media-allowlist-D6x-Us02.mjs} +1 -1
- package/dist/{media-allowlist-BD6HhgXA.mjs.map → media-allowlist-D6x-Us02.mjs.map} +1 -1
- package/dist/{media-upload-9yas9zGH.mjs → media-upload-CHBVn6m-.mjs} +2 -2
- package/dist/{media-upload-9yas9zGH.mjs.map → media-upload-CHBVn6m-.mjs.map} +1 -1
- package/dist/{media-usage-De3qvwjc.mjs → media-usage-BZCydfvu.mjs} +4 -4
- package/dist/{media-usage-De3qvwjc.mjs.map → media-usage-BZCydfvu.mjs.map} +1 -1
- package/dist/{media-usage-work-B9Bksh_b.mjs → media-usage-work-CLo_6tct.mjs} +2 -2
- package/dist/{media-usage-work-B9Bksh_b.mjs.map → media-usage-work-CLo_6tct.mjs.map} +1 -1
- package/dist/{menus-CC-yn1F9.mjs → menus-DSo5QeW0.mjs} +6 -6
- package/dist/{menus-CC-yn1F9.mjs.map → menus-DSo5QeW0.mjs.map} +1 -1
- package/dist/{menus-fxvEDYE6.mjs → menus-TCNSd2vp.mjs} +2 -2
- package/dist/{menus-fxvEDYE6.mjs.map → menus-TCNSd2vp.mjs.map} +1 -1
- package/dist/migrations/index.d.mts +22 -0
- package/dist/migrations/index.d.mts.map +1 -0
- package/dist/migrations/index.mjs +7 -0
- package/dist/{mode-Bpe-tNNy.mjs → mode-BDoH_WcF.mjs} +1 -1
- package/dist/{mode-Bpe-tNNy.mjs.map → mode-BDoH_WcF.mjs.map} +1 -1
- package/dist/{oauth-authorization-BXO1MKej.mjs → oauth-authorization-iOikmrJd.mjs} +4 -4
- package/dist/{oauth-authorization-BXO1MKej.mjs.map → oauth-authorization-iOikmrJd.mjs.map} +1 -1
- package/dist/{oauth-clients-C_DNqtRu.mjs → oauth-clients-CvI0nHMJ.mjs} +1 -1
- package/dist/{oauth-clients-C_DNqtRu.mjs.map → oauth-clients-CvI0nHMJ.mjs.map} +1 -1
- package/dist/{oauth-state-store-DwAxMtk4.mjs → oauth-state-store-Ba8KZlwW.mjs} +1 -1
- package/dist/{oauth-state-store-DwAxMtk4.mjs.map → oauth-state-store-Ba8KZlwW.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-BxfZizdk.mjs → oauth-user-lookup-C5SQwGYC.mjs} +1 -1
- package/dist/{oauth-user-lookup-BxfZizdk.mjs.map → oauth-user-lookup-C5SQwGYC.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{options-DRe1pIBL.d.mts → options--hhz-jZ1.d.mts} +3 -3
- package/dist/{options-DRe1pIBL.d.mts.map → options--hhz-jZ1.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{passkey-config-_2GOwwK8.mjs → passkey-config-DFAmUyuE.mjs} +1 -1
- package/dist/{passkey-config-_2GOwwK8.mjs.map → passkey-config-DFAmUyuE.mjs.map} +1 -1
- package/dist/{placeholder-CzW7Lshs.d.mts → placeholder-Dp0K3RkD.d.mts} +1 -1
- package/dist/{placeholder-CzW7Lshs.d.mts.map → placeholder-Dp0K3RkD.d.mts.map} +1 -1
- 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/policy-TluUIxuu.mjs +45 -0
- package/dist/policy-TluUIxuu.mjs.map +1 -0
- package/dist/{portable-text-DhKbDAGr.mjs → portable-text-imBW5cHK.mjs} +1 -1
- package/dist/{portable-text-DhKbDAGr.mjs.map → portable-text-imBW5cHK.mjs.map} +1 -1
- package/dist/protocol-DjsJHTlK.d.mts +52 -0
- package/dist/protocol-DjsJHTlK.d.mts.map +1 -0
- package/dist/{query-vgZNwIhh.mjs → query-BWs7O5Il.mjs} +3 -3
- package/dist/{query-vgZNwIhh.mjs.map → query-BWs7O5Il.mjs.map} +1 -1
- package/dist/{query-BgjfiLUQ.mjs → query-zTVAR1Sl.mjs} +12 -12
- package/dist/{query-BgjfiLUQ.mjs.map → query-zTVAR1Sl.mjs.map} +1 -1
- package/dist/{rate-limit-L5anykgo.mjs → rate-limit-Bx4g9m3p.mjs} +1 -1
- package/dist/{rate-limit-L5anykgo.mjs.map → rate-limit-Bx4g9m3p.mjs.map} +1 -1
- package/dist/{redirect-KBNE3UEs.mjs → redirect-BgAOGHDq.mjs} +1 -1
- package/dist/{redirect-KBNE3UEs.mjs.map → redirect-BgAOGHDq.mjs.map} +1 -1
- package/dist/{redirects-cV9g7V-0.mjs → redirects-DTDHWH31.mjs} +2 -2
- package/dist/{redirects-cV9g7V-0.mjs.map → redirects-DTDHWH31.mjs.map} +1 -1
- package/dist/{redirects-CufRQt0Q.mjs → redirects-DyChwDkG.mjs} +1 -1
- package/dist/{redirects-CufRQt0Q.mjs.map → redirects-DyChwDkG.mjs.map} +1 -1
- package/dist/{registry-C581V4TP.mjs → registry-Cqa2I749.mjs} +6 -6
- package/dist/{registry-C581V4TP.mjs.map → registry-Cqa2I749.mjs.map} +1 -1
- package/dist/{relation-DtmAQH7s.mjs → relation-BnHB0T4J.mjs} +2 -2
- package/dist/{relation-DtmAQH7s.mjs.map → relation-BnHB0T4J.mjs.map} +1 -1
- package/dist/{relations-DItI3MN9.d.mts → relations-BHo5A8Zf.d.mts} +35 -35
- package/dist/{relations-DItI3MN9.d.mts.map → relations-BHo5A8Zf.d.mts.map} +1 -1
- package/dist/{relations-DICSyfsh.mjs → relations-D5sM5fW0.mjs} +4 -4
- package/dist/{relations-DICSyfsh.mjs.map → relations-D5sM5fW0.mjs.map} +1 -1
- package/dist/request-context.d.mts +1 -1
- package/dist/{runner-CgQbELYC.d.mts → runner-BYHZn3Jy.d.mts} +16 -3
- package/dist/runner-BYHZn3Jy.d.mts.map +1 -0
- package/dist/{runner-BiDvq5dv.mjs → runner-C1T11CfC.mjs} +34 -4
- package/dist/{runner-BiDvq5dv.mjs.map → runner-C1T11CfC.mjs.map} +1 -1
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-CeQSNwB3.mjs → schema-2AOsSn2v.mjs} +4 -4
- package/dist/{schema-CeQSNwB3.mjs.map → schema-2AOsSn2v.mjs.map} +1 -1
- package/dist/{schema-BFyTg38T.mjs → schema-DZ5YVVZj.mjs} +3 -3
- package/dist/{schema-BFyTg38T.mjs.map → schema-DZ5YVVZj.mjs.map} +1 -1
- package/dist/{scopes-BVeuhGBE.mjs → scopes-8ClPF2bC.mjs} +2 -2
- package/dist/{scopes-BVeuhGBE.mjs.map → scopes-8ClPF2bC.mjs.map} +1 -1
- package/dist/{search-BpuPKZb0.mjs → search-u1QYdOCG.mjs} +4 -4
- package/dist/{search-BpuPKZb0.mjs.map → search-u1QYdOCG.mjs.map} +1 -1
- package/dist/{secrets-dtYs2hDo.mjs → secrets-BEgsAQEU.mjs} +1 -1
- package/dist/{secrets-dtYs2hDo.mjs.map → secrets-BEgsAQEU.mjs.map} +1 -1
- package/dist/{sections-COctk6hf.mjs → sections-DmHviGyM.mjs} +2 -2
- package/dist/{sections-COctk6hf.mjs.map → sections-DmHviGyM.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +13 -13
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +9 -9
- package/dist/{seo-CvDxfvFb.mjs → seo-Dq4G-RrO.mjs} +2 -2
- package/dist/{seo-CvDxfvFb.mjs.map → seo-Dq4G-RrO.mjs.map} +1 -1
- package/dist/{service-xMiMSTsR.mjs → service-BpArayPr.mjs} +2 -2
- package/dist/{service-xMiMSTsR.mjs.map → service-BpArayPr.mjs.map} +1 -1
- package/dist/{session-user-BzVemLkw.mjs → session-user-BEorKVT-.mjs} +1 -1
- package/dist/{session-user-BzVemLkw.mjs.map → session-user-BEorKVT-.mjs.map} +1 -1
- package/dist/{settings-BE9aqn_9.mjs → settings-DE9xtzqd.mjs} +3 -3
- package/dist/{settings-BE9aqn_9.mjs.map → settings-DE9xtzqd.mjs.map} +1 -1
- package/dist/{settings-B372MV0p.mjs → settings-xRdoUZr0.mjs} +2 -2
- package/dist/{settings-B372MV0p.mjs.map → settings-xRdoUZr0.mjs.map} +1 -1
- package/dist/{setup-complete-DF7TAfD5.mjs → setup-complete-BnT8pq_C.mjs} +1 -1
- package/dist/{setup-complete-DF7TAfD5.mjs.map → setup-complete-BnT8pq_C.mjs.map} +1 -1
- package/dist/{setup-nonce-Do-U_cg9.mjs → setup-nonce-CBitf_oQ.mjs} +1 -1
- package/dist/{setup-nonce-Do-U_cg9.mjs.map → setup-nonce-CBitf_oQ.mjs.map} +1 -1
- package/dist/{site-url-Bowsi9T-.mjs → site-url-DjpIXMXJ.mjs} +1 -1
- package/dist/{site-url-Bowsi9T-.mjs.map → site-url-DjpIXMXJ.mjs.map} +1 -1
- package/dist/{snapshot-B5h6aPMh.mjs → snapshot-BBlGkb2q.mjs} +1 -1
- package/dist/{snapshot-B5h6aPMh.mjs.map → snapshot-BBlGkb2q.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +2 -2
- package/dist/storage/s3.d.mts +1 -1
- package/dist/target-DixqqV5i.mjs +27 -0
- package/dist/target-DixqqV5i.mjs.map +1 -0
- package/dist/{taxonomies-B0o9ExNV.mjs → taxonomies-BlrncHi9.mjs} +5 -5
- package/dist/{taxonomies-B0o9ExNV.mjs.map → taxonomies-BlrncHi9.mjs.map} +1 -1
- package/dist/{taxonomies-Dk3MAklL.mjs → taxonomies-OHUIiIpI.mjs} +4 -4
- package/dist/{taxonomies-Dk3MAklL.mjs.map → taxonomies-OHUIiIpI.mjs.map} +1 -1
- package/dist/{taxonomy-CPeM8d2o.mjs → taxonomy-BqCZ1TdU.mjs} +3 -3
- package/dist/{taxonomy-CPeM8d2o.mjs.map → taxonomy-BqCZ1TdU.mjs.map} +1 -1
- package/dist/{transport--CfOflrp.mjs → transport-DBJwa-Si.mjs} +1 -1
- package/dist/{transport--CfOflrp.mjs.map → transport-DBJwa-Si.mjs.map} +1 -1
- package/dist/{transport-B_rfeU-a.d.mts → transport-RQE36APG.d.mts} +1 -1
- package/dist/{transport-B_rfeU-a.d.mts.map → transport-RQE36APG.d.mts.map} +1 -1
- package/dist/{types-CFUfyt1s.d.mts → types-BcqdcT-r.d.mts} +1 -1
- package/dist/{types-CFUfyt1s.d.mts.map → types-BcqdcT-r.d.mts.map} +1 -1
- package/dist/{types-CPx2lIGo.d.mts → types-BntycPTF.d.mts} +1 -1
- package/dist/{types-CPx2lIGo.d.mts.map → types-BntycPTF.d.mts.map} +1 -1
- package/dist/{types-C3_AbkDP.d.mts → types-C2pMvO62.d.mts} +1 -1
- package/dist/{types-C3_AbkDP.d.mts.map → types-C2pMvO62.d.mts.map} +1 -1
- package/dist/{types-DefV3OiO.d.mts → types-CXnOzDe1.d.mts} +1 -1
- package/dist/{types-DefV3OiO.d.mts.map → types-CXnOzDe1.d.mts.map} +1 -1
- package/dist/{types-D4_1Ga4L.d.mts → types-DN64_zzZ.d.mts} +3 -3
- package/dist/{types-D4_1Ga4L.d.mts.map → types-DN64_zzZ.d.mts.map} +1 -1
- package/dist/{types-CAGdePf3.d.mts → types-DQIDRKF2.d.mts} +1 -1
- package/dist/{types-CAGdePf3.d.mts.map → types-DQIDRKF2.d.mts.map} +1 -1
- package/dist/{types-BPuloofb.d.mts → types-DWRhk9xZ.d.mts} +1 -1
- package/dist/{types-BPuloofb.d.mts.map → types-DWRhk9xZ.d.mts.map} +1 -1
- package/dist/{types-X0MThr7x.d.mts → types-Dfk8KI5R.d.mts} +1 -1
- package/dist/{types-X0MThr7x.d.mts.map → types-Dfk8KI5R.d.mts.map} +1 -1
- package/dist/{types-Cs-TFLNf.d.mts → types-DrJrVCT1.d.mts} +3 -3
- package/dist/{types-Cs-TFLNf.d.mts.map → types-DrJrVCT1.d.mts.map} +1 -1
- package/dist/{upload-attempts-hlne3iA1.mjs → upload-attempts-D4FS8LT6.mjs} +1 -1
- package/dist/{upload-attempts-hlne3iA1.mjs.map → upload-attempts-D4FS8LT6.mjs.map} +1 -1
- package/dist/{user-sHgJ62SP.mjs → user-DJZfUJVE.mjs} +2 -2
- package/dist/{user-sHgJ62SP.mjs.map → user-DJZfUJVE.mjs.map} +1 -1
- package/dist/{validate-BkqIG_Uj.mjs → validate-BMLBvxOC.mjs} +2 -2
- package/dist/{validate-BkqIG_Uj.mjs.map → validate-BMLBvxOC.mjs.map} +1 -1
- package/dist/{validate-DeGtBN84.d.mts → validate-CTqt5nuE.d.mts} +5 -5
- package/dist/{validate-DeGtBN84.d.mts.map → validate-CTqt5nuE.d.mts.map} +1 -1
- package/dist/{validation-CiJb_X5o.mjs → validation-npvENUCB.mjs} +7 -7
- package/dist/{validation-CiJb_X5o.mjs.map → validation-npvENUCB.mjs.map} +1 -1
- package/dist/version-DB_fqCSI.mjs +7 -0
- package/dist/{version-x0NjTScG.mjs.map → version-DB_fqCSI.mjs.map} +1 -1
- package/dist/{widgets-Cwzvww0W.mjs → widgets-BD3ho7Rq.mjs} +2 -2
- package/dist/{widgets-Cwzvww0W.mjs.map → widgets-BD3ho7Rq.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-QZjWxjyk.mjs → wxr-taxonomies-B1R3vBoD.mjs} +3 -3
- package/dist/{wxr-taxonomies-QZjWxjyk.mjs.map → wxr-taxonomies-B1R3vBoD.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-CmC0ZsDr.d.mts → wxr-taxonomies-Ca68766D.d.mts} +1 -1
- package/dist/{wxr-taxonomies-CmC0ZsDr.d.mts.map → wxr-taxonomies-Ca68766D.d.mts.map} +1 -1
- package/dist/{zod-generator-D11MhxG4.mjs → zod-generator-LC-on_Vb.mjs} +2 -2
- package/dist/{zod-generator-D11MhxG4.mjs.map → zod-generator-LC-on_Vb.mjs.map} +1 -1
- package/package.json +21 -5
- package/src/astro/integration/index.ts +46 -14
- package/src/astro/integration/runtime.ts +3 -0
- package/src/astro/middleware/scoped-db.ts +21 -0
- package/src/astro/middleware.ts +67 -10
- package/src/astro/routes/api/auth/dev-bypass.ts +0 -4
- package/src/astro/routes/api/setup/dev-bypass.ts +2 -7
- package/src/astro/routes/api/setup/index.ts +5 -13
- package/src/cli/commands/migrate.ts +593 -0
- package/src/cli/index.ts +2 -23
- package/src/database/index.ts +8 -2
- package/src/database/migrations/policy.ts +70 -0
- package/src/database/migrations/runner.ts +49 -3
- package/src/db/adapters.ts +50 -2
- package/src/db/index.ts +3 -1
- package/src/db/libsql-migrations.ts +72 -0
- package/src/db/postgres-migrations.ts +59 -0
- package/src/db/sqlite-migrations.ts +37 -0
- package/src/emdash-runtime.ts +44 -15
- package/src/i18n/normalize.ts +31 -0
- package/src/index.ts +7 -1
- package/src/migrations/config-loader.ts +205 -0
- package/src/migrations/direct-executor.ts +141 -0
- package/src/migrations/identity.ts +46 -0
- package/src/migrations/index.ts +19 -0
- package/src/migrations/integration-metadata.ts +67 -0
- package/src/migrations/manifest-builder.ts +55 -0
- package/src/migrations/manifest-writer.ts +63 -0
- package/src/migrations/manifest.ts +197 -0
- package/src/migrations/protocol.ts +59 -0
- package/src/migrations/target.ts +35 -0
- package/src/virtual-modules.d.ts +8 -0
- package/dist/adapters-34EN2eXO.d.mts.map +0 -1
- package/dist/index-C3Y-rsyN.d.mts.map +0 -1
- package/dist/runner-CgQbELYC.d.mts.map +0 -1
- package/dist/version-x0NjTScG.mjs +0 -7
- /package/dist/{api-tokens-CyTsLszZ.mjs → api-tokens-rIXC6LdE.mjs} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-builder-B252yQUf.mjs","names":[],"sources":["../src/i18n/normalize.ts","../src/migrations/manifest.ts","../src/migrations/integration-metadata.ts","../src/migrations/manifest-builder.ts"],"sourcesContent":["import type { I18nConfig } from \"./config.js\";\n\nexport interface AstroLocaleObject {\n\tpath: string;\n\tcodes: readonly string[];\n}\n\nexport interface AstroI18nInput {\n\tdefaultLocale: string;\n\tlocales: readonly (string | AstroLocaleObject)[];\n\tfallback?: Readonly<Record<string, string | undefined>>;\n\trouting?: string | { prefixDefaultLocale?: boolean };\n}\n\nexport function normalizeAstroI18n(config: AstroI18nInput | null | undefined): I18nConfig | null {\n\tif (!config) return null;\n\n\treturn {\n\t\tdefaultLocale: config.defaultLocale,\n\t\tlocales: config.locales.map((locale) => (typeof locale === \"string\" ? locale : locale.path)),\n\t\tfallback: config.fallback\n\t\t\t? Object.fromEntries(\n\t\t\t\t\tObject.entries(config.fallback).filter(\n\t\t\t\t\t\t(entry): entry is [string, string] => entry[1] !== undefined,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t: undefined,\n\t\tprefixDefaultLocale:\n\t\t\ttypeof config.routing === \"object\" ? (config.routing.prefixDefaultLocale ?? false) : false,\n\t};\n}\n","import { z } from \"zod\";\n\nimport type { CoreMigrationIdentity } from \"./identity.js\";\nimport { fingerprintMigrationSet } from \"./identity.js\";\n\nconst FINGERPRINT_PATTERN = /^[0-9a-f]{64}$/;\nconst MIGRATION_NAME_PATTERN = /^\\d{3}_[a-z0-9_]+$/;\nconst EXECUTOR_ENTRYPOINT_PATTERN =\n\t/^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*(?:\\/[A-Za-z0-9][A-Za-z0-9._-]*)*$/;\nconst ENVIRONMENT_VARIABLE_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;\nconst ENVIRONMENT_REFERENCE_KEY_PATTERN = /(?:env|envName|envVar)$/i;\nconst URL_SCHEME_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:/;\nconst SECRET_QUERY_KEY_PATTERN = /(?:auth|credential|key|password|secret|signature|token)/i;\nconst SECRET_CONFIG_KEY_PATTERN =\n\t/(?:accessToken|apiKey|authToken|certificate|connectionString|password|privateKey|secret)/i;\nconst SECRET_CONFIG_EXACT_KEY_PATTERN = /^(?:ca|cert|key)$/i;\nconst CONNECTION_URL_PROTOCOLS = new Set([\"mysql:\", \"postgres:\", \"postgresql:\"]);\n\ntype JsonPrimitive = boolean | number | string | null;\ntype JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };\n\nconst i18nConfigSchema = z\n\t.object({\n\t\tdefaultLocale: z.string().min(1),\n\t\tlocales: z.array(z.string().min(1)).min(1),\n\t\tfallback: z.record(z.string(), z.string()).optional(),\n\t\tprefixDefaultLocale: z.boolean().optional(),\n\t})\n\t.strict();\n\nexport const migrationManifestV1Schema = z\n\t.object({\n\t\tschemaVersion: z.literal(1),\n\t\temdashVersion: z.string().min(1),\n\t\tmigrationSet: z\n\t\t\t.object({\n\t\t\t\tnames: z\n\t\t\t\t\t.array(z.string().regex(MIGRATION_NAME_PATTERN))\n\t\t\t\t\t.min(1)\n\t\t\t\t\t.refine((names) => new Set(names).size === names.length),\n\t\t\t\tfingerprint: z.string().regex(FINGERPRINT_PATTERN),\n\t\t\t})\n\t\t\t.strict(),\n\t\ti18n: i18nConfigSchema.nullable(),\n\t\tdatabase: z\n\t\t\t.object({\n\t\t\t\ttype: z.enum([\"sqlite\", \"postgres\"]),\n\t\t\t\texecutorEntrypoint: z.string().regex(EXECUTOR_ENTRYPOINT_PATTERN),\n\t\t\t\texecutorConfig: z.unknown(),\n\t\t\t})\n\t\t\t.strict(),\n\t})\n\t.strict();\n\nexport type MigrationManifestV1 = z.infer<typeof migrationManifestV1Schema>;\n\nexport class MigrationManifestValidationError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(`Invalid migration manifest: ${message}`);\n\t\tthis.name = \"MigrationManifestValidationError\";\n\t}\n}\n\nfunction isEnvironmentReferenceKey(key: string): boolean {\n\treturn ENVIRONMENT_REFERENCE_KEY_PATTERN.test(key);\n}\n\nfunction assertSafeUrl(value: string, path: string): void {\n\tif (!URL_SCHEME_PATTERN.test(value)) return;\n\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(value);\n\t} catch {\n\t\tthrow new MigrationManifestValidationError(`${path} contains an invalid URL`);\n\t}\n\n\tif (CONNECTION_URL_PROTOCOLS.has(url.protocol)) {\n\t\tthrow new MigrationManifestValidationError(`${path} contains a database connection URL`);\n\t}\n\tif (url.username || url.password) {\n\t\tthrow new MigrationManifestValidationError(`${path} contains URL credentials`);\n\t}\n\tfor (const key of url.searchParams.keys()) {\n\t\tif (SECRET_QUERY_KEY_PATTERN.test(key)) {\n\t\t\tthrow new MigrationManifestValidationError(`${path} contains a credential query parameter`);\n\t\t}\n\t}\n}\n\nfunction cloneSecretFreeJson(value: unknown, path: string, ancestors: Set<object>): JsonValue {\n\tif (value === null || typeof value === \"boolean\") return value;\n\tif (typeof value === \"string\") {\n\t\tassertSafeUrl(value, path);\n\t\treturn value;\n\t}\n\tif (typeof value === \"number\") {\n\t\tif (Number.isFinite(value)) return value;\n\t\tthrow new MigrationManifestValidationError(`${path} contains a non-finite number`);\n\t}\n\tif (typeof value !== \"object\") {\n\t\tthrow new MigrationManifestValidationError(`${path} is not JSON-serializable`);\n\t}\n\tif (ancestors.has(value)) {\n\t\tthrow new MigrationManifestValidationError(`${path} contains a cycle`);\n\t}\n\n\tancestors.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value.map((item, index) => cloneSecretFreeJson(item, `${path}[${index}]`, ancestors));\n\t\t}\n\n\t\tconst prototype = Object.getPrototypeOf(value);\n\t\tif (prototype !== Object.prototype && prototype !== null) {\n\t\t\tthrow new MigrationManifestValidationError(`${path} contains a non-plain object`);\n\t\t}\n\n\t\tconst cloned: Record<string, JsonValue> = {};\n\t\tfor (const [key, item] of Object.entries(value)) {\n\t\t\tconst itemPath = `${path}.${key}`;\n\t\t\tconst environmentReference = isEnvironmentReferenceKey(key);\n\t\t\tif (\n\t\t\t\t!environmentReference &&\n\t\t\t\t(SECRET_CONFIG_KEY_PATTERN.test(key) || SECRET_CONFIG_EXACT_KEY_PATTERN.test(key))\n\t\t\t) {\n\t\t\t\tthrow new MigrationManifestValidationError(`${itemPath} is a credential-bearing field`);\n\t\t\t}\n\t\t\tif (environmentReference) {\n\t\t\t\tif (typeof item !== \"string\" || !ENVIRONMENT_VARIABLE_PATTERN.test(item)) {\n\t\t\t\t\tthrow new MigrationManifestValidationError(\n\t\t\t\t\t\t`${itemPath} must contain an environment-variable name`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcloned[key] = cloneSecretFreeJson(item, itemPath, ancestors);\n\t\t}\n\t\treturn cloned;\n\t} finally {\n\t\tancestors.delete(value);\n\t}\n}\n\nexport function validateSecretFreeExecutorConfig(\n\tvalue: unknown,\n\tpath = \"database.executorConfig\",\n): unknown {\n\treturn cloneSecretFreeJson(value, path, new Set());\n}\n\nfunction identitiesMatch(\n\tmanifest: MigrationManifestV1,\n\texpectedIdentity: CoreMigrationIdentity,\n): boolean {\n\treturn (\n\t\tmanifest.emdashVersion === expectedIdentity.emdashVersion &&\n\t\tmanifest.migrationSet.fingerprint === expectedIdentity.fingerprint &&\n\t\tmanifest.migrationSet.names.length === expectedIdentity.names.length &&\n\t\tmanifest.migrationSet.names.every((name, index) => name === expectedIdentity.names[index])\n\t);\n}\n\nexport async function validateMigrationManifest(\n\tvalue: unknown,\n\texpectedIdentity?: CoreMigrationIdentity,\n): Promise<MigrationManifestV1> {\n\tconst parsed = migrationManifestV1Schema.safeParse(value);\n\tif (!parsed.success) {\n\t\tthrow new MigrationManifestValidationError(\"schema validation failed\");\n\t}\n\n\tconst executorConfig = validateSecretFreeExecutorConfig(parsed.data.database.executorConfig);\n\tconst manifest: MigrationManifestV1 = {\n\t\t...parsed.data,\n\t\tdatabase: { ...parsed.data.database, executorConfig },\n\t};\n\tconst fingerprint = await fingerprintMigrationSet(\n\t\tmanifest.emdashVersion,\n\t\tmanifest.migrationSet.names,\n\t);\n\tif (fingerprint !== manifest.migrationSet.fingerprint) {\n\t\tthrow new MigrationManifestValidationError(\n\t\t\t\"migrationSet fingerprint does not match its version and ordered names\",\n\t\t);\n\t}\n\tif (expectedIdentity && !identitiesMatch(manifest, expectedIdentity)) {\n\t\tthrow new MigrationManifestValidationError(\n\t\t\t\"manifest does not match the loaded EmDash migration identity\",\n\t\t);\n\t}\n\n\treturn manifest;\n}\n\nexport function serializeMigrationManifest(manifest: MigrationManifestV1): string {\n\treturn `${JSON.stringify(manifest, null, \"\\t\")}\\n`;\n}\n","import type { DatabaseDescriptor } from \"../db/adapters.js\";\nimport { validateSecretFreeExecutorConfig } from \"./manifest.js\";\n\nexport const MIGRATION_CONFIG_SYMBOL = Symbol.for(\"emdash:migration-config\");\n\nexport interface MigrationIntegrationMetadata {\n\tdatabase?: Pick<DatabaseDescriptor, \"type\" | \"migrations\">;\n}\n\nfunction isRecord(value: unknown): value is Record<PropertyKey, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction isMigrationIntegrationMetadata(value: unknown): value is MigrationIntegrationMetadata {\n\tif (!isRecord(value)) return false;\n\tconst database = value.database;\n\tif (database === undefined) return true;\n\tif (!isRecord(database) || (database.type !== \"sqlite\" && database.type !== \"postgres\")) {\n\t\treturn false;\n\t}\n\n\tconst migrations = database.migrations;\n\treturn (\n\t\tmigrations === undefined ||\n\t\t(isRecord(migrations) &&\n\t\t\ttypeof migrations.entrypoint === \"string\" &&\n\t\t\tObject.hasOwn(migrations, \"manifestConfig\"))\n\t);\n}\n\nexport function createMigrationIntegrationMetadata(\n\tdatabase?: DatabaseDescriptor,\n): MigrationIntegrationMetadata {\n\tif (!database) return {};\n\n\treturn {\n\t\tdatabase: {\n\t\t\ttype: database.type,\n\t\t\tmigrations: database.migrations\n\t\t\t\t? {\n\t\t\t\t\t\tentrypoint: database.migrations.entrypoint,\n\t\t\t\t\t\tmanifestConfig: validateSecretFreeExecutorConfig(\n\t\t\t\t\t\t\tdatabase.migrations.manifestConfig,\n\t\t\t\t\t\t\t\"database.migrations.manifestConfig\",\n\t\t\t\t\t\t),\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t},\n\t};\n}\n\nexport function getMigrationIntegrationMetadata(\n\tintegration: unknown,\n): MigrationIntegrationMetadata | undefined {\n\tif (\n\t\t(typeof integration !== \"object\" || integration === null) &&\n\t\ttypeof integration !== \"function\"\n\t) {\n\t\treturn undefined;\n\t}\n\tif (!Object.hasOwn(integration, MIGRATION_CONFIG_SYMBOL)) {\n\t\treturn undefined;\n\t}\n\n\tconst metadata: unknown = Reflect.get(integration, MIGRATION_CONFIG_SYMBOL);\n\treturn isMigrationIntegrationMetadata(metadata) ? metadata : undefined;\n}\n","import type { DatabaseDescriptor } from \"../db/adapters.js\";\nimport type { I18nConfig } from \"../i18n/config.js\";\nimport type { CoreMigrationIdentity } from \"./identity.js\";\nimport { fingerprintMigrationSet } from \"./identity.js\";\nimport type { MigrationManifestV1 } from \"./manifest.js\";\nimport { MigrationManifestValidationError, validateMigrationManifest } from \"./manifest.js\";\n\nexport type ManifestDatabaseDescriptor = Pick<DatabaseDescriptor, \"type\" | \"migrations\">;\n\nexport interface BuildMigrationManifestOptions {\n\tidentity: CoreMigrationIdentity;\n\ti18n: I18nConfig | null;\n\tdatabase: ManifestDatabaseDescriptor;\n}\n\nexport class UnsupportedMigrationAdapterError extends Error {\n\tconstructor() {\n\t\tsuper(\"The configured database adapter does not provide a deployment migration executor\");\n\t\tthis.name = \"UnsupportedMigrationAdapterError\";\n\t}\n}\n\nexport async function buildMigrationManifest({\n\tidentity,\n\ti18n,\n\tdatabase,\n}: BuildMigrationManifestOptions): Promise<MigrationManifestV1> {\n\tconst identityFingerprint = await fingerprintMigrationSet(identity.emdashVersion, identity.names);\n\tif (identityFingerprint !== identity.fingerprint) {\n\t\tthrow new MigrationManifestValidationError(\n\t\t\t\"loaded identity fingerprint does not match its version and ordered names\",\n\t\t);\n\t}\n\tif (!database.migrations) {\n\t\tthrow new UnsupportedMigrationAdapterError();\n\t}\n\n\treturn validateMigrationManifest(\n\t\t{\n\t\t\tschemaVersion: 1,\n\t\t\temdashVersion: identity.emdashVersion,\n\t\t\tmigrationSet: {\n\t\t\t\tnames: [...identity.names],\n\t\t\t\tfingerprint: identity.fingerprint,\n\t\t\t},\n\t\t\ti18n,\n\t\t\tdatabase: {\n\t\t\t\ttype: database.type,\n\t\t\t\texecutorEntrypoint: database.migrations.entrypoint,\n\t\t\t\texecutorConfig: database.migrations.manifestConfig,\n\t\t\t},\n\t\t},\n\t\tidentity,\n\t);\n}\n"],"mappings":"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"}
|
package/dist/media/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as MediaValue, a as ComponentEmbed, b as mediaItemToValue, c as EmbedResult, d as MediaListResult, f as MediaProvider, g as MediaUploadInput, h as MediaProviderItem, i as AudioEmbed, l as ImageEmbed, m as MediaProviderDescriptor, n as generatePlaceholder, o as CreateMediaProviderFn, p as MediaProviderCapabilities, r as normalizeMediaValue, s as EmbedOptions, t as PlaceholderData, u as MediaListOptions, v as ThumbnailOptions, y as VideoEmbed } from "../placeholder-
|
|
1
|
+
import { _ as MediaValue, a as ComponentEmbed, b as mediaItemToValue, c as EmbedResult, d as MediaListResult, f as MediaProvider, g as MediaUploadInput, h as MediaProviderItem, i as AudioEmbed, l as ImageEmbed, m as MediaProviderDescriptor, n as generatePlaceholder, o as CreateMediaProviderFn, p as MediaProviderCapabilities, r as normalizeMediaValue, s as EmbedOptions, t as PlaceholderData, u as MediaListOptions, v as ThumbnailOptions, y as VideoEmbed } from "../placeholder-Dp0K3RkD.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/media/enrich.d.ts
|
|
4
4
|
/**
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import "../options-
|
|
2
|
-
import { t as Database } from "../types-
|
|
3
|
-
import "../types-
|
|
4
|
-
import "../relations-
|
|
5
|
-
import { h as MediaProviderItem, o as CreateMediaProviderFn } from "../placeholder-
|
|
6
|
-
import "../index-
|
|
7
|
-
import "../runner-
|
|
8
|
-
import "../index-
|
|
9
|
-
import { d as Storage } from "../types-
|
|
10
|
-
import "../types-
|
|
11
|
-
import "../hreflang-
|
|
12
|
-
import "../validate-
|
|
1
|
+
import "../options--hhz-jZ1.mjs";
|
|
2
|
+
import { t as Database } from "../types-DQIDRKF2.mjs";
|
|
3
|
+
import "../types-DrJrVCT1.mjs";
|
|
4
|
+
import "../relations-BHo5A8Zf.mjs";
|
|
5
|
+
import { h as MediaProviderItem, o as CreateMediaProviderFn } from "../placeholder-Dp0K3RkD.mjs";
|
|
6
|
+
import "../index-BVBZj4-7.mjs";
|
|
7
|
+
import "../runner-BYHZn3Jy.mjs";
|
|
8
|
+
import "../index-C9KuQyO-.mjs";
|
|
9
|
+
import { d as Storage } from "../types-BcqdcT-r.mjs";
|
|
10
|
+
import "../types-CXnOzDe1.mjs";
|
|
11
|
+
import "../hreflang-DwT2E0Sa.mjs";
|
|
12
|
+
import "../validate-CTqt5nuE.mjs";
|
|
13
13
|
import "../index.mjs";
|
|
14
14
|
import { Kysely } from "kysely";
|
|
15
15
|
|
|
@@ -7,8 +7,8 @@ import "../types-CQPWhPxE.mjs";
|
|
|
7
7
|
import { t as MediaRepository } from "../media-CzhAn2--.mjs";
|
|
8
8
|
import "../options-CBXIHUoT.mjs";
|
|
9
9
|
import "../request-cache-BSUptuJR.mjs";
|
|
10
|
-
import "../loader-
|
|
11
|
-
import { o as invalidateSiteSettingsCache } from "../settings-
|
|
10
|
+
import "../loader-CdG_Ci3H.mjs";
|
|
11
|
+
import { o as invalidateSiteSettingsCache } from "../settings-xRdoUZr0.mjs";
|
|
12
12
|
|
|
13
13
|
//#region src/media/local-runtime.ts
|
|
14
14
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-allowlist-
|
|
1
|
+
{"version":3,"file":"media-allowlist-D6x-Us02.mjs","names":[],"sources":["../src/api/handlers/media-allowlist.ts"],"sourcesContent":["import type { Kysely } from \"kysely\";\n\nimport type { Database } from \"../../database/types.js\";\nimport { parseAllowedMimeTypes } from \"../../media/mime.js\";\n\n/**\n * MIME types allowed for upload by default (when no field-specific list\n * overrides this). Entries ending with \"/\" are prefix-matched (e.g.\n * \"video/\" matches \"video/mp4\", \"video/webm\", etc.).\n *\n * Image types are enumerated explicitly rather than using a bare \"image/\"\n * prefix so that \"image/svg+xml\" is excluded by default: there is no\n * upload-time content validation for SVG, so an unvalidated bare-prefix\n * allowlist would let a Contributor+ upload an SVG with an embedded\n * `<script>`. Fields that genuinely need SVG uploads can still opt in via\n * their own `allowedMimeTypes` (see `resolveFieldAllowlist` below), which is\n * unaffected by this default.\n */\nexport const GLOBAL_UPLOAD_ALLOWLIST: readonly string[] = [\n\t\"image/png\",\n\t\"image/jpeg\",\n\t\"image/gif\",\n\t\"image/webp\",\n\t\"video/\",\n\t\"audio/\",\n\t\"application/pdf\",\n];\n\n/**\n * Resolve the MIME allowlist for a specific field.\n *\n * Returns the field's `allowedMimeTypes` list when the field exists, is of\n * type \"file\" or \"image\", and has a non-empty list configured. Returns null\n * in all other cases — callers should fall back to GLOBAL_UPLOAD_ALLOWLIST.\n *\n * Authentication is the caller's responsibility (the upload routes already\n * gate on `media:upload`).\n */\nexport async function resolveFieldAllowlist(\n\tdb: Kysely<Database>,\n\tfieldId: string,\n): Promise<string[] | null> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_fields\")\n\t\t.select([\"type\", \"validation\"])\n\t\t.where(\"id\", \"=\", fieldId)\n\t\t.where(\"type\", \"in\", [\"file\", \"image\"])\n\t\t.executeTakeFirst();\n\n\treturn row ? parseAllowedMimeTypes(row.validation) : null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,MAAa,0BAA6C;CACzD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;;;;;;;;;;AAYD,eAAsB,sBACrB,IACA,SAC2B;CAC3B,MAAM,MAAM,MAAM,GAChB,WAAW,iBAAiB,CAC5B,OAAO,CAAC,QAAQ,aAAa,CAAC,CAC9B,MAAM,MAAM,KAAK,QAAQ,CACzB,MAAM,QAAQ,MAAM,CAAC,QAAQ,QAAQ,CAAC,CACtC,kBAAkB;AAEpB,QAAO,MAAM,sBAAsB,IAAI,WAAW,GAAG"}
|
|
@@ -8,7 +8,7 @@ import { t as enrichImageMetadata } from "./enrich-_ySMEvEE.mjs";
|
|
|
8
8
|
import { n as computeContentHash } from "./hash-Cg0TXJPV.mjs";
|
|
9
9
|
import { r as ssrfSafeFetch, t as SsrfError } from "./ssrf-Cpvg_n3A.mjs";
|
|
10
10
|
import { n as DEFAULT_MAX_UPLOAD_SIZE, r as formatFileSize, t as CONTENT_TYPE_RE } from "./media-BWWWb8kg.mjs";
|
|
11
|
-
import { t as GLOBAL_UPLOAD_ALLOWLIST } from "./media-allowlist-
|
|
11
|
+
import { t as GLOBAL_UPLOAD_ALLOWLIST } from "./media-allowlist-D6x-Us02.mjs";
|
|
12
12
|
import { ulid } from "ulidx";
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
|
|
@@ -144,4 +144,4 @@ async function handleMediaUpload(db, storage, input) {
|
|
|
144
144
|
|
|
145
145
|
//#endregion
|
|
146
146
|
export { handleMediaUpload };
|
|
147
|
-
//# sourceMappingURL=media-upload-
|
|
147
|
+
//# sourceMappingURL=media-upload-CHBVn6m-.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-upload-9yas9zGH.mjs","names":[],"sources":["../src/api/handlers/media-upload.ts"],"sourcesContent":["/**\n * Programmatic media upload handler (MCP `media_upload` tool).\n *\n * Accepts file bytes as base64 or fetches them from an external URL\n * (SSRF-guarded), then runs the same pipeline as the multipart REST\n * upload route: allowlist + size validation, content-hash deduplication,\n * storage upload, image metadata enrichment, and record creation.\n */\n\nimport * as path from \"node:path\";\n\nimport type { Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { MediaRepository, type MediaItem } from \"../../database/repositories/media.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { enrichImageMetadata } from \"../../media/enrich.js\";\nimport { matchesMimeAllowlist, normalizeMime } from \"../../media/mime.js\";\nimport { SsrfError, ssrfSafeFetch } from \"../../security/ssrf.js\";\nimport type { Storage } from \"../../storage/types.js\";\nimport { decodeBase64Bytes } from \"../../utils/base64.js\";\nimport { computeContentHash } from \"../../utils/hash.js\";\nimport { CONTENT_TYPE_RE, DEFAULT_MAX_UPLOAD_SIZE, formatFileSize } from \"../schemas/media.js\";\nimport type { ApiResult } from \"../types.js\";\nimport { GLOBAL_UPLOAD_ALLOWLIST } from \"./media-allowlist.js\";\n\nexport interface MediaUploadInput {\n\t/** Original filename (e.g. 'logo.png'); the extension is kept on the storage key. */\n\tfilename: string;\n\t/** Base64-encoded file contents. Exactly one of `base64` / `url` must be set. */\n\tbase64?: string;\n\t/** External http(s) URL to fetch the file from. Exactly one of `base64` / `url` must be set. */\n\turl?: string;\n\t/**\n\t * MIME type. Required with `base64`; optional with `url` (falls back to\n\t * the response's Content-Type header).\n\t */\n\tcontentType?: string;\n\t/** Alt text stored on the media record. */\n\talt?: string;\n\tauthorId?: string;\n\t/** Upload size limit in bytes (defaults to DEFAULT_MAX_UPLOAD_SIZE). */\n\tmaxUploadSize?: number;\n}\n\nexport type MediaUploadResult = ApiResult<{\n\titem: MediaItem & { url: string };\n\tdeduplicated?: boolean;\n}>;\n\nfunction fail(code: string, message: string): MediaUploadResult {\n\treturn { success: false, error: { code, message } };\n}\n\n/** Same relative-URL shape the REST media routes return. */\nfunction withUrl(item: MediaItem): MediaItem & { url: string } {\n\treturn { ...item, url: `/_emdash/api/media/file/${item.storageKey}` };\n}\n\n/** Strip parameters from a Content-Type header value (e.g. '; charset=...'). */\nfunction bareMime(headerValue: string): string {\n\treturn (headerValue.split(\";\")[0] ?? \"\").trim();\n}\n\n/**\n * Acquire the file bytes and MIME type from either the base64 payload or\n * the external URL. Returns an error result on any validation failure.\n */\nasync function acquireBytes(\n\tinput: MediaUploadInput,\n\tmaxUploadSize: number,\n): Promise<{ bytes: Uint8Array; mimeType: string } | MediaUploadResult> {\n\tif (input.base64) {\n\t\tif (!input.contentType) {\n\t\t\treturn fail(\"VALIDATION_ERROR\", \"contentType is required when uploading base64 data\");\n\t\t}\n\t\t// Cheap size precheck on the encoded string (decoded size is ~3/4 of\n\t\t// the base64 length) before allocating the decoded buffer.\n\t\tif ((input.base64.length * 3) / 4 > maxUploadSize) {\n\t\t\treturn fail(\n\t\t\t\t\"PAYLOAD_TOO_LARGE\",\n\t\t\t\t`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\treturn { bytes: decodeBase64Bytes(input.base64), mimeType: input.contentType };\n\t\t} catch {\n\t\t\treturn fail(\"VALIDATION_ERROR\", \"Invalid base64 data\");\n\t\t}\n\t}\n\n\t// url mode — the caller guarantees exactly one source, so url is set here\n\tconst url = input.url;\n\tif (!url) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Provide exactly one of 'base64' or 'url'\");\n\t}\n\tlet response: Response;\n\ttry {\n\t\tresponse = await ssrfSafeFetch(url, { headers: { accept: \"*/*\" } });\n\t} catch (error) {\n\t\tif (error instanceof SsrfError) {\n\t\t\treturn fail(\"VALIDATION_ERROR\", `URL not allowed: ${error.message}`);\n\t\t}\n\t\treturn fail(\"FETCH_ERROR\", \"Failed to fetch file from URL\");\n\t}\n\tif (!response.ok) {\n\t\treturn fail(\"FETCH_ERROR\", `Failed to fetch file from URL (HTTP ${response.status})`);\n\t}\n\n\tconst contentLength = response.headers.get(\"Content-Length\");\n\tif (contentLength && parseInt(contentLength, 10) > maxUploadSize) {\n\t\treturn fail(\n\t\t\t\"PAYLOAD_TOO_LARGE\",\n\t\t\t`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,\n\t\t);\n\t}\n\n\tconst mimeType = input.contentType ?? bareMime(response.headers.get(\"Content-Type\") ?? \"\");\n\tif (!mimeType) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Could not determine MIME type — pass contentType explicitly\");\n\t}\n\n\tconst bytes = new Uint8Array(await response.arrayBuffer());\n\treturn { bytes, mimeType };\n}\n\n/**\n * Upload a media file from base64 data or an external URL.\n *\n * Mirrors the REST `POST /_emdash/api/media` route: global MIME allowlist,\n * size limit, content-hash dedupe (returns the existing item with\n * `deduplicated: true`), storage upload with cleanup on failure, and\n * image metadata enrichment (dimensions, blurhash, dominant color).\n */\nexport async function handleMediaUpload(\n\tdb: Kysely<Database>,\n\tstorage: Storage,\n\tinput: MediaUploadInput,\n): Promise<MediaUploadResult> {\n\tif (!input.base64 === !input.url) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Provide exactly one of 'base64' or 'url'\");\n\t}\n\n\tconst rawMax = input.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE;\n\tif (!Number.isFinite(rawMax) || rawMax <= 0) {\n\t\treturn fail(\"CONFIGURATION_ERROR\", \"Invalid maxUploadSize configuration\");\n\t}\n\n\tconst acquired = await acquireBytes(input, rawMax);\n\tif (\"success\" in acquired) return acquired;\n\tconst { bytes } = acquired;\n\n\t// Validate the raw MIME string before normalize/allowlist: normalizeMime\n\t// only strips parameters and matchesMimeAllowlist only checks startsWith,\n\t// so without this a crafted value like \"image/png\\r\\nX-Evil: 1\" would\n\t// reach the storage backend's ContentType header and be echoed by the\n\t// media file serving route.\n\tif (!CONTENT_TYPE_RE.test(acquired.mimeType)) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Invalid content type\");\n\t}\n\tconst mimeType = normalizeMime(acquired.mimeType);\n\n\tif (!matchesMimeAllowlist(mimeType, GLOBAL_UPLOAD_ALLOWLIST)) {\n\t\treturn fail(\"INVALID_TYPE\", \"File type not allowed\");\n\t}\n\tif (bytes.byteLength > rawMax) {\n\t\treturn fail(\"PAYLOAD_TOO_LARGE\", `File exceeds maximum size of ${formatFileSize(rawMax)}`);\n\t}\n\n\ttry {\n\t\tconst contentHash = await computeContentHash(bytes);\n\t\tconst repo = new MediaRepository(db);\n\n\t\tconst existing = await repo.findByContentHash(contentHash);\n\t\tif (existing) {\n\t\t\treturn { success: true, data: { item: withUrl(existing), deduplicated: true } };\n\t\t}\n\n\t\tconst storageKey = `${ulid()}${path.extname(input.filename)}`;\n\t\tawait storage.upload({ key: storageKey, body: bytes, contentType: mimeType });\n\n\t\ttry {\n\t\t\tconst enriched = await enrichImageMetadata(bytes, mimeType);\n\t\t\tconst item = await repo.create({\n\t\t\t\tfilename: input.filename,\n\t\t\t\tmimeType,\n\t\t\t\tsize: bytes.byteLength,\n\t\t\t\twidth: enriched.width,\n\t\t\t\theight: enriched.height,\n\t\t\t\talt: input.alt,\n\t\t\t\tstorageKey,\n\t\t\t\tcontentHash,\n\t\t\t\tblurhash: enriched.blurhash,\n\t\t\t\tdominantColor: enriched.dominantColor,\n\t\t\t\tauthorId: input.authorId,\n\t\t\t});\n\t\t\treturn { success: true, data: { item: withUrl(item) } };\n\t\t} catch (error) {\n\t\t\t// Don't leave an orphaned object in storage when record creation fails\n\t\t\ttry {\n\t\t\t\tawait storage.delete(storageKey);\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t} catch {\n\t\treturn fail(\"UPLOAD_ERROR\", \"Upload failed\");\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"media-upload-CHBVn6m-.mjs","names":[],"sources":["../src/api/handlers/media-upload.ts"],"sourcesContent":["/**\n * Programmatic media upload handler (MCP `media_upload` tool).\n *\n * Accepts file bytes as base64 or fetches them from an external URL\n * (SSRF-guarded), then runs the same pipeline as the multipart REST\n * upload route: allowlist + size validation, content-hash deduplication,\n * storage upload, image metadata enrichment, and record creation.\n */\n\nimport * as path from \"node:path\";\n\nimport type { Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { MediaRepository, type MediaItem } from \"../../database/repositories/media.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { enrichImageMetadata } from \"../../media/enrich.js\";\nimport { matchesMimeAllowlist, normalizeMime } from \"../../media/mime.js\";\nimport { SsrfError, ssrfSafeFetch } from \"../../security/ssrf.js\";\nimport type { Storage } from \"../../storage/types.js\";\nimport { decodeBase64Bytes } from \"../../utils/base64.js\";\nimport { computeContentHash } from \"../../utils/hash.js\";\nimport { CONTENT_TYPE_RE, DEFAULT_MAX_UPLOAD_SIZE, formatFileSize } from \"../schemas/media.js\";\nimport type { ApiResult } from \"../types.js\";\nimport { GLOBAL_UPLOAD_ALLOWLIST } from \"./media-allowlist.js\";\n\nexport interface MediaUploadInput {\n\t/** Original filename (e.g. 'logo.png'); the extension is kept on the storage key. */\n\tfilename: string;\n\t/** Base64-encoded file contents. Exactly one of `base64` / `url` must be set. */\n\tbase64?: string;\n\t/** External http(s) URL to fetch the file from. Exactly one of `base64` / `url` must be set. */\n\turl?: string;\n\t/**\n\t * MIME type. Required with `base64`; optional with `url` (falls back to\n\t * the response's Content-Type header).\n\t */\n\tcontentType?: string;\n\t/** Alt text stored on the media record. */\n\talt?: string;\n\tauthorId?: string;\n\t/** Upload size limit in bytes (defaults to DEFAULT_MAX_UPLOAD_SIZE). */\n\tmaxUploadSize?: number;\n}\n\nexport type MediaUploadResult = ApiResult<{\n\titem: MediaItem & { url: string };\n\tdeduplicated?: boolean;\n}>;\n\nfunction fail(code: string, message: string): MediaUploadResult {\n\treturn { success: false, error: { code, message } };\n}\n\n/** Same relative-URL shape the REST media routes return. */\nfunction withUrl(item: MediaItem): MediaItem & { url: string } {\n\treturn { ...item, url: `/_emdash/api/media/file/${item.storageKey}` };\n}\n\n/** Strip parameters from a Content-Type header value (e.g. '; charset=...'). */\nfunction bareMime(headerValue: string): string {\n\treturn (headerValue.split(\";\")[0] ?? \"\").trim();\n}\n\n/**\n * Acquire the file bytes and MIME type from either the base64 payload or\n * the external URL. Returns an error result on any validation failure.\n */\nasync function acquireBytes(\n\tinput: MediaUploadInput,\n\tmaxUploadSize: number,\n): Promise<{ bytes: Uint8Array; mimeType: string } | MediaUploadResult> {\n\tif (input.base64) {\n\t\tif (!input.contentType) {\n\t\t\treturn fail(\"VALIDATION_ERROR\", \"contentType is required when uploading base64 data\");\n\t\t}\n\t\t// Cheap size precheck on the encoded string (decoded size is ~3/4 of\n\t\t// the base64 length) before allocating the decoded buffer.\n\t\tif ((input.base64.length * 3) / 4 > maxUploadSize) {\n\t\t\treturn fail(\n\t\t\t\t\"PAYLOAD_TOO_LARGE\",\n\t\t\t\t`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\treturn { bytes: decodeBase64Bytes(input.base64), mimeType: input.contentType };\n\t\t} catch {\n\t\t\treturn fail(\"VALIDATION_ERROR\", \"Invalid base64 data\");\n\t\t}\n\t}\n\n\t// url mode — the caller guarantees exactly one source, so url is set here\n\tconst url = input.url;\n\tif (!url) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Provide exactly one of 'base64' or 'url'\");\n\t}\n\tlet response: Response;\n\ttry {\n\t\tresponse = await ssrfSafeFetch(url, { headers: { accept: \"*/*\" } });\n\t} catch (error) {\n\t\tif (error instanceof SsrfError) {\n\t\t\treturn fail(\"VALIDATION_ERROR\", `URL not allowed: ${error.message}`);\n\t\t}\n\t\treturn fail(\"FETCH_ERROR\", \"Failed to fetch file from URL\");\n\t}\n\tif (!response.ok) {\n\t\treturn fail(\"FETCH_ERROR\", `Failed to fetch file from URL (HTTP ${response.status})`);\n\t}\n\n\tconst contentLength = response.headers.get(\"Content-Length\");\n\tif (contentLength && parseInt(contentLength, 10) > maxUploadSize) {\n\t\treturn fail(\n\t\t\t\"PAYLOAD_TOO_LARGE\",\n\t\t\t`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,\n\t\t);\n\t}\n\n\tconst mimeType = input.contentType ?? bareMime(response.headers.get(\"Content-Type\") ?? \"\");\n\tif (!mimeType) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Could not determine MIME type — pass contentType explicitly\");\n\t}\n\n\tconst bytes = new Uint8Array(await response.arrayBuffer());\n\treturn { bytes, mimeType };\n}\n\n/**\n * Upload a media file from base64 data or an external URL.\n *\n * Mirrors the REST `POST /_emdash/api/media` route: global MIME allowlist,\n * size limit, content-hash dedupe (returns the existing item with\n * `deduplicated: true`), storage upload with cleanup on failure, and\n * image metadata enrichment (dimensions, blurhash, dominant color).\n */\nexport async function handleMediaUpload(\n\tdb: Kysely<Database>,\n\tstorage: Storage,\n\tinput: MediaUploadInput,\n): Promise<MediaUploadResult> {\n\tif (!input.base64 === !input.url) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Provide exactly one of 'base64' or 'url'\");\n\t}\n\n\tconst rawMax = input.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE;\n\tif (!Number.isFinite(rawMax) || rawMax <= 0) {\n\t\treturn fail(\"CONFIGURATION_ERROR\", \"Invalid maxUploadSize configuration\");\n\t}\n\n\tconst acquired = await acquireBytes(input, rawMax);\n\tif (\"success\" in acquired) return acquired;\n\tconst { bytes } = acquired;\n\n\t// Validate the raw MIME string before normalize/allowlist: normalizeMime\n\t// only strips parameters and matchesMimeAllowlist only checks startsWith,\n\t// so without this a crafted value like \"image/png\\r\\nX-Evil: 1\" would\n\t// reach the storage backend's ContentType header and be echoed by the\n\t// media file serving route.\n\tif (!CONTENT_TYPE_RE.test(acquired.mimeType)) {\n\t\treturn fail(\"VALIDATION_ERROR\", \"Invalid content type\");\n\t}\n\tconst mimeType = normalizeMime(acquired.mimeType);\n\n\tif (!matchesMimeAllowlist(mimeType, GLOBAL_UPLOAD_ALLOWLIST)) {\n\t\treturn fail(\"INVALID_TYPE\", \"File type not allowed\");\n\t}\n\tif (bytes.byteLength > rawMax) {\n\t\treturn fail(\"PAYLOAD_TOO_LARGE\", `File exceeds maximum size of ${formatFileSize(rawMax)}`);\n\t}\n\n\ttry {\n\t\tconst contentHash = await computeContentHash(bytes);\n\t\tconst repo = new MediaRepository(db);\n\n\t\tconst existing = await repo.findByContentHash(contentHash);\n\t\tif (existing) {\n\t\t\treturn { success: true, data: { item: withUrl(existing), deduplicated: true } };\n\t\t}\n\n\t\tconst storageKey = `${ulid()}${path.extname(input.filename)}`;\n\t\tawait storage.upload({ key: storageKey, body: bytes, contentType: mimeType });\n\n\t\ttry {\n\t\t\tconst enriched = await enrichImageMetadata(bytes, mimeType);\n\t\t\tconst item = await repo.create({\n\t\t\t\tfilename: input.filename,\n\t\t\t\tmimeType,\n\t\t\t\tsize: bytes.byteLength,\n\t\t\t\twidth: enriched.width,\n\t\t\t\theight: enriched.height,\n\t\t\t\talt: input.alt,\n\t\t\t\tstorageKey,\n\t\t\t\tcontentHash,\n\t\t\t\tblurhash: enriched.blurhash,\n\t\t\t\tdominantColor: enriched.dominantColor,\n\t\t\t\tauthorId: input.authorId,\n\t\t\t});\n\t\t\treturn { success: true, data: { item: withUrl(item) } };\n\t\t} catch (error) {\n\t\t\t// Don't leave an orphaned object in storage when record creation fails\n\t\t\ttry {\n\t\t\t\tawait storage.delete(storageKey);\n\t\t\t} catch {\n\t\t\t\t// Ignore cleanup errors\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t} catch {\n\t\treturn fail(\"UPLOAD_ERROR\", \"Upload failed\");\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as __exportAll } from "./runner-C1T11CfC.mjs";
|
|
2
2
|
import { t as validateIdentifier } from "./validate-BpwoDOgt.mjs";
|
|
3
|
-
import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-
|
|
3
|
+
import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-fiZ8G9RJ.mjs";
|
|
4
4
|
import { i as InvalidCursorError } from "./types-CQPWhPxE.mjs";
|
|
5
5
|
import { t as MediaRepository } from "./media-CzhAn2--.mjs";
|
|
6
|
-
import { _ as loadContentMediaUsageFields, d as withContentUsageCollectionLock, f as loadContentMediaUsageSnapshots, h as MediaUsageFieldDiscoveryError, m as buildContentMediaUsageSourceKey, n as CONTENT_MEDIA_USAGE_COLLECTION_SCOPE, p as MEDIA_USAGE_CONTENT_SOURCE_VARIANTS, t as CONTENT_MEDIA_USAGE_ADAPTER_ID, v as CONTENT_SOURCE_SCHEMA_VERSION, y as MediaUsageRepository } from "./content-refresh-
|
|
6
|
+
import { _ as loadContentMediaUsageFields, d as withContentUsageCollectionLock, f as loadContentMediaUsageSnapshots, h as MediaUsageFieldDiscoveryError, m as buildContentMediaUsageSourceKey, n as CONTENT_MEDIA_USAGE_COLLECTION_SCOPE, p as MEDIA_USAGE_CONTENT_SOURCE_VARIANTS, t as CONTENT_MEDIA_USAGE_ADAPTER_ID, v as CONTENT_SOURCE_SCHEMA_VERSION, y as MediaUsageRepository } from "./content-refresh-B_8SJzuU.mjs";
|
|
7
7
|
import { t as MediaUsageWorkRepository } from "./media-usage-work-DrCbqBcl.mjs";
|
|
8
8
|
import { t as ErrorCode } from "./errors-CLUPm7aD.mjs";
|
|
9
9
|
import { sql } from "kysely";
|
|
@@ -628,4 +628,4 @@ function normalizeMediaUsageReferenceType(referenceType) {
|
|
|
628
628
|
|
|
629
629
|
//#endregion
|
|
630
630
|
export { media_usage_exports as i, handleMediaUsageRepair as n, handleMediaUsageSummaries as r, handleMediaUsageDetails as t };
|
|
631
|
-
//# sourceMappingURL=media-usage-
|
|
631
|
+
//# sourceMappingURL=media-usage-BZCydfvu.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-usage-De3qvwjc.mjs","names":[],"sources":["../src/media/usage/content-repair.ts","../src/api/handlers/media-usage.ts"],"sourcesContent":["import { sql, type Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { MediaUsageWorkRepository } from \"../../database/repositories/media-usage-work.js\";\nimport {\n\tMediaUsageRepository,\n\ttype MediaUsageSource,\n} from \"../../database/repositories/media-usage.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { validateIdentifier } from \"../../database/validate.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport {\n\tloadContentMediaUsageFields,\n\tMediaUsageFieldDiscoveryError,\n\ttype ContentMediaUsageFieldDiscovery,\n} from \"./content-fields.js\";\nimport {\n\tCONTENT_MEDIA_USAGE_ADAPTER_ID,\n\tCONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\twithContentUsageCollectionLock,\n} from \"./content-refresh.js\";\nimport {\n\tCONTENT_SOURCE_SCHEMA_VERSION,\n\ttype ContentMediaUsageSnapshot,\n\tloadContentMediaUsageSnapshots,\n} from \"./content-snapshots.js\";\nimport {\n\tbuildContentMediaUsageSourceKey,\n\tMEDIA_USAGE_CONTENT_SOURCE_VARIANTS,\n} from \"./source-key.js\";\n\nexport type ContentMediaUsageRepairStatus = \"complete\" | \"partial\" | \"failed\" | \"stale\";\n\nexport const CONTENT_MEDIA_USAGE_REPAIR_ERROR = {\n\tCOLLECTION_NOT_FOUND: \"COLLECTION_NOT_FOUND\",\n\tCONTENT_NOT_FOUND: \"CONTENT_NOT_FOUND\",\n\tDRAFT_REVISION_NOT_FOUND: \"DRAFT_REVISION_NOT_FOUND\",\n\tDRAFT_REVISION_MISMATCH: \"DRAFT_REVISION_MISMATCH\",\n\tDRAFT_REVISION_INVALID: \"DRAFT_REVISION_INVALID\",\n\tCONTENT_USAGE_REPAIR_ERROR: \"CONTENT_USAGE_REPAIR_ERROR\",\n\tCONTENT_USAGE_REPAIR_CONFLICT: \"CONTENT_USAGE_REPAIR_CONFLICT\",\n\tINVALID_REPEATER_VALIDATION: \"INVALID_REPEATER_VALIDATION\",\n} as const;\n\nexport type ContentMediaUsageRepairErrorCode =\n\t(typeof CONTENT_MEDIA_USAGE_REPAIR_ERROR)[keyof typeof CONTENT_MEDIA_USAGE_REPAIR_ERROR];\n\nexport interface ContentMediaUsageRepairCollectionInput {\n\tcollectionSlug: string;\n}\n\nexport interface ContentMediaUsageRepairScope {\n\tadapterId: typeof CONTENT_MEDIA_USAGE_ADAPTER_ID;\n\tscopeType: typeof CONTENT_MEDIA_USAGE_COLLECTION_SCOPE;\n\tscopeKey: string;\n}\n\nexport interface ContentMediaUsageRepairCollectionResult {\n\tscope: ContentMediaUsageRepairScope;\n\tstatus: ContentMediaUsageRepairStatus;\n\tindexedSourceCount: number;\n\tfailedSourceCount: number;\n\tskippedSourceCount: number;\n\tdeletedSourceCount: number;\n\tlastErrorCode: string | null;\n\tstartedAt: string;\n\tcompletedAt: string | null;\n}\n\nexport interface ContentMediaUsageRepairAllResult {\n\tstatus: ContentMediaUsageRepairStatus;\n\tcollections: ContentMediaUsageRepairCollectionResult[];\n\tindexedSourceCount: number;\n\tfailedSourceCount: number;\n\tskippedSourceCount: number;\n\tdeletedSourceCount: number;\n}\n\nexport interface ContentMediaUsageCollectionScan {\n\tcollectionSlug: string;\n\tcontentIds: string[];\n}\n\ninterface ContentMediaUsageCollectionRecord {\n\tid: string;\n\tslug: string;\n}\n\ninterface ContentMediaUsageInitialCollectionResult {\n\tcollection: ContentMediaUsageCollectionRecord;\n\tresult: ContentMediaUsageRepairCollectionResult;\n}\n\ninterface ContentMediaUsageRepairExecution {\n\tcollectionId: string;\n\tstartingEpoch: number | string;\n}\n\nexport async function repairContentMediaUsageAll(\n\tdb: Kysely<Database>,\n): Promise<ContentMediaUsageRepairAllResult> {\n\tconst collections = await loadContentMediaUsageCollectionRecords(db);\n\tconst results: ContentMediaUsageInitialCollectionResult[] = [];\n\n\tfor (const collection of collections) {\n\t\tresults.push({\n\t\t\tcollection,\n\t\t\tresult: await repairContentMediaUsageCollectionSafely(db, collection),\n\t\t});\n\t}\n\n\treturn aggregateContentMediaUsageRepairAll(\n\t\tawait filterExistingContentMediaUsageCollectionResults(db, results),\n\t);\n}\n\nexport async function scanContentMediaUsageCollection(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\texpectedCollectionId?: string,\n): Promise<ContentMediaUsageCollectionScan | null> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\tlet collectionQuery = db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", collectionSlug);\n\tif (expectedCollectionId !== undefined) {\n\t\tcollectionQuery = collectionQuery.where(\"id\", \"=\", expectedCollectionId);\n\t}\n\tconst collection = await collectionQuery.executeTakeFirst();\n\tif (!collection) return null;\n\n\tconst tableName = getContentTableName(collectionSlug);\n\tconst rows = await sql<{ id: string }>`\n\t\tSELECT content.id\n\t\tFROM ${sql.ref(tableName)} AS content\n\t\tWHERE EXISTS (\n\t\t\tSELECT 1\n\t\t\tFROM _emdash_collections AS collection\n\t\t\tWHERE collection.id = ${collection.id}\n\t\t\t\tAND collection.slug = ${collectionSlug}\n\t\t)\n\t\tORDER BY content.id ASC\n\t`.execute(db);\n\n\treturn {\n\t\tcollectionSlug,\n\t\tcontentIds: rows.rows.map((row) => row.id),\n\t};\n}\n\nexport async function repairContentMediaUsageCollection(\n\tdb: Kysely<Database>,\n\tinput: ContentMediaUsageRepairCollectionInput,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\tvalidateIdentifier(input.collectionSlug, \"collection slug\");\n\treturn withContentUsageCollectionLock(input.collectionSlug, () =>\n\t\trepairContentMediaUsageCollectionUnlocked(db, input.collectionSlug),\n\t);\n}\n\nasync function loadContentMediaUsageCollectionRecords(\n\tdb: Kysely<Database>,\n): Promise<ContentMediaUsageCollectionRecord[]> {\n\treturn db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select([\"id\", \"slug\"])\n\t\t.orderBy(\"slug\", \"asc\")\n\t\t.execute();\n}\n\nasync function repairContentMediaUsageCollectionSafely(\n\tdb: Kysely<Database>,\n\tcollection: ContentMediaUsageCollectionRecord,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\ttry {\n\t\treturn await withContentUsageCollectionLock(collection.slug, () =>\n\t\t\trepairContentMediaUsageCollectionUnlocked(db, collection.slug, collection.id),\n\t\t);\n\t} catch (error) {\n\t\tconsole.error(`[media-usage] Failed to repair collection ${collection.slug}:`, error);\n\t\tconst now = new Date().toISOString();\n\t\treturn {\n\t\t\tscope: contentMediaUsageCollectionScope(collection.slug),\n\t\t\tstatus: \"failed\",\n\t\t\tindexedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\tskippedSourceCount: 0,\n\t\t\tdeletedSourceCount: 0,\n\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_ERROR,\n\t\t\tstartedAt: now,\n\t\t\tcompletedAt: now,\n\t\t};\n\t}\n}\n\nasync function filterExistingContentMediaUsageCollectionResults(\n\tdb: Kysely<Database>,\n\tresults: readonly ContentMediaUsageInitialCollectionResult[],\n): Promise<ContentMediaUsageRepairCollectionResult[]> {\n\tconst identityBound = await isIncrementalCaptureActive(db);\n\tconst currentCollections = await loadContentMediaUsageCollectionRecordsSafely(db);\n\tconst currentIdsBySlug = new Map(\n\t\tcurrentCollections.map((collection) => [collection.slug, collection.id]),\n\t);\n\tconst includedResults: ContentMediaUsageRepairCollectionResult[] = [];\n\tconst excludedResults: ContentMediaUsageInitialCollectionResult[] = [];\n\n\tfor (const { collection, result } of results) {\n\t\tif (currentIdsBySlug.get(collection.slug) === collection.id) {\n\t\t\tincludedResults.push(result);\n\t\t} else {\n\t\t\texcludedResults.push({ collection, result });\n\t\t}\n\t}\n\n\tif (excludedResults.length > 0) {\n\t\tconst repo = new MediaUsageRepository(db);\n\t\tfor (const excluded of excludedResults) {\n\t\t\tawait repo.deleteIndexStatus(\n\t\t\t\texcluded.result.scope,\n\t\t\t\tidentityBound ? excluded.collection.id : undefined,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn includedResults;\n}\n\nasync function loadContentMediaUsageCollectionRecordsSafely(\n\tdb: Kysely<Database>,\n): Promise<ContentMediaUsageCollectionRecord[]> {\n\ttry {\n\t\treturn await loadContentMediaUsageCollectionRecords(db);\n\t} catch {\n\t\t// Retry once before failing; returning unpruned results can over-report deleted collections.\n\t}\n\n\ttry {\n\t\treturn await loadContentMediaUsageCollectionRecords(db);\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage] Failed to reconcile all-content repair collections:\", error);\n\t\tthrow error;\n\t}\n}\n\nfunction aggregateContentMediaUsageRepairAll(\n\tcollections: readonly ContentMediaUsageRepairCollectionResult[],\n): ContentMediaUsageRepairAllResult {\n\treturn {\n\t\tstatus: determineRepairAllStatus(collections),\n\t\tcollections: [...collections],\n\t\tindexedSourceCount: sumCollectionRepairCount(collections, \"indexedSourceCount\"),\n\t\tfailedSourceCount: sumCollectionRepairCount(collections, \"failedSourceCount\"),\n\t\tskippedSourceCount: sumCollectionRepairCount(collections, \"skippedSourceCount\"),\n\t\tdeletedSourceCount: sumCollectionRepairCount(collections, \"deletedSourceCount\"),\n\t};\n}\n\nfunction determineRepairAllStatus(\n\tcollections: readonly ContentMediaUsageRepairCollectionResult[],\n): ContentMediaUsageRepairStatus {\n\tif (collections.length === 0) return \"complete\";\n\tif (collections.every((collection) => collection.status === \"complete\")) return \"complete\";\n\tif (collections.some((collection) => collection.status === \"stale\")) return \"stale\";\n\tif (collections.some((collection) => collection.status === \"partial\")) return \"partial\";\n\tif (collections.every((collection) => collection.status === \"failed\")) return \"failed\";\n\treturn \"partial\";\n}\n\nfunction sumCollectionRepairCount(\n\tcollections: readonly ContentMediaUsageRepairCollectionResult[],\n\tkey: \"indexedSourceCount\" | \"failedSourceCount\" | \"skippedSourceCount\" | \"deletedSourceCount\",\n): number {\n\treturn collections.reduce((sum, collection) => sum + collection[key], 0);\n}\n\nasync function repairContentMediaUsageCollectionUnlocked(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\texpectedCollectionId?: string,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\tconst requestedAt = new Date().toISOString();\n\tconst scope = contentMediaUsageCollectionScope(collectionSlug);\n\tconst identityBound = await isIncrementalCaptureActive(db);\n\tconst collection = await loadContentMediaUsageCollectionRecord(db, collectionSlug);\n\tif (\n\t\t!collection ||\n\t\t(identityBound && expectedCollectionId && collection.id !== expectedCollectionId)\n\t) {\n\t\treturn {\n\t\t\tscope,\n\t\t\tstatus: \"failed\",\n\t\t\tindexedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\tskippedSourceCount: 0,\n\t\t\tdeletedSourceCount: 0,\n\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.COLLECTION_NOT_FOUND,\n\t\t\tstartedAt: requestedAt,\n\t\t\tcompletedAt: requestedAt,\n\t\t};\n\t}\n\n\tconst repo = new MediaUsageRepository(db);\n\tconst runToken = ulid();\n\tlet startedAt = requestedAt;\n\tlet execution: ContentMediaUsageRepairExecution | undefined;\n\tif (identityBound) {\n\t\tconst run = await repo.beginIndexStatusRepairAtCurrentEpoch({\n\t\t\t...scope,\n\t\t\tcollectionId: collection.id,\n\t\t\trunToken,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t});\n\t\tif (!run) {\n\t\t\treturn {\n\t\t\t\tscope,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tindexedSourceCount: 0,\n\t\t\t\tfailedSourceCount: 0,\n\t\t\t\tskippedSourceCount: 0,\n\t\t\t\tdeletedSourceCount: 0,\n\t\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_ERROR,\n\t\t\t\tstartedAt,\n\t\t\t\tcompletedAt: requestedAt,\n\t\t\t};\n\t\t}\n\t\tstartedAt = run.startedAt;\n\t\texecution = { collectionId: collection.id, startingEpoch: run.changeEpoch };\n\t} else {\n\t\tawait repo.beginIndexStatusRepair({\n\t\t\t...scope,\n\t\t\trunToken,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t\tstartedAt,\n\t\t});\n\t}\n\n\ttry {\n\t\tconst scan = await scanContentMediaUsageCollection(db, collectionSlug, execution?.collectionId);\n\t\tif (!scan) {\n\t\t\tconst completedAt = new Date().toISOString();\n\t\t\treturn await finalizeRepairStatus(db, repo, {\n\t\t\t\t...scope,\n\t\t\t\trunToken,\n\t\t\t\tcounts: {\n\t\t\t\t\tindexedSourceCount: 0,\n\t\t\t\t\tfailedSourceCount: 0,\n\t\t\t\t\tskippedSourceCount: 0,\n\t\t\t\t\tdeletedSourceCount: 0,\n\t\t\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.COLLECTION_NOT_FOUND,\n\t\t\t\t\tmissingContentIds: new Set(),\n\t\t\t\t},\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tstartedAt,\n\t\t\t\tcompletedAt,\n\t\t\t\texecution,\n\t\t\t});\n\t\t}\n\t\tconst counts = await repairScannedContentSources(db, repo, scan, execution?.collectionId);\n\t\tconst finalScan = await scanContentMediaUsageCollection(\n\t\t\tdb,\n\t\t\tcollectionSlug,\n\t\t\texecution?.collectionId,\n\t\t);\n\t\tif (!finalScan) {\n\t\t\tcounts.failedSourceCount++;\n\t\t\tcounts.lastErrorCode = CONTENT_MEDIA_USAGE_REPAIR_ERROR.COLLECTION_NOT_FOUND;\n\t\t} else if (!sameContentIds(repairedContentIds(scan.contentIds, counts), finalScan.contentIds)) {\n\t\t\tmarkRepairConflict(counts);\n\t\t}\n\t\tconst completedAt = new Date().toISOString();\n\t\tconst status = determineRepairStatus(counts);\n\t\treturn await finalizeRepairStatus(db, repo, {\n\t\t\t...scope,\n\t\t\trunToken,\n\t\t\tcounts,\n\t\t\tstatus,\n\t\t\tstartedAt,\n\t\t\tcompletedAt,\n\t\t\texecution,\n\t\t});\n\t} catch (error) {\n\t\tif (!(error instanceof MediaUsageFieldDiscoveryError)) {\n\t\t\tconsole.error(`[media-usage] Failed to repair collection ${collectionSlug}:`, error);\n\t\t}\n\t\tconst completedAt = new Date().toISOString();\n\t\tconst lastErrorCode =\n\t\t\terror instanceof MediaUsageFieldDiscoveryError\n\t\t\t\t? error.code\n\t\t\t\t: CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_ERROR;\n\t\treturn finalizeRepairStatus(db, repo, {\n\t\t\t...scope,\n\t\t\trunToken,\n\t\t\tcounts: {\n\t\t\t\tindexedSourceCount: 0,\n\t\t\t\tfailedSourceCount: 0,\n\t\t\t\tskippedSourceCount: 0,\n\t\t\t\tdeletedSourceCount: 0,\n\t\t\t\tlastErrorCode,\n\t\t\t\tmissingContentIds: new Set(),\n\t\t\t},\n\t\t\tstatus: \"failed\",\n\t\t\tstartedAt,\n\t\t\tcompletedAt,\n\t\t\texecution,\n\t\t});\n\t}\n}\n\ninterface RepairCounts {\n\tindexedSourceCount: number;\n\tfailedSourceCount: number;\n\tskippedSourceCount: number;\n\tdeletedSourceCount: number;\n\tlastErrorCode: ContentMediaUsageRepairErrorCode | null;\n\tmissingContentIds: Set<string>;\n}\n\nasync function repairScannedContentSources(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tscan: ContentMediaUsageCollectionScan,\n\tcollectionId?: string,\n): Promise<RepairCounts> {\n\tconst counts: RepairCounts = {\n\t\tindexedSourceCount: 0,\n\t\tfailedSourceCount: 0,\n\t\tskippedSourceCount: 0,\n\t\tdeletedSourceCount: 0,\n\t\tlastErrorCode: null,\n\t\tmissingContentIds: new Set(),\n\t};\n\n\tconst fieldDiscovery = await loadContentMediaUsageFields(db, scan.collectionSlug, collectionId);\n\tconst observedSources = await repo.findSources(buildContentSourceKeysForScan(scan, collectionId));\n\n\tfor (const contentId of scan.contentIds) {\n\t\tawait repairContentSource(\n\t\t\tdb,\n\t\t\trepo,\n\t\t\tscan.collectionSlug,\n\t\t\tcontentId,\n\t\t\tfieldDiscovery,\n\t\t\tobservedSources,\n\t\t\tcounts,\n\t\t\tcollectionId,\n\t\t);\n\t}\n\n\tawait reconcileOrphanedContentSources(db, repo, scan.collectionSlug, counts, collectionId);\n\treturn counts;\n}\n\nasync function repairContentSource(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tcollectionSlug: string,\n\tcontentId: string,\n\tfieldDiscovery: ContentMediaUsageFieldDiscovery,\n\tobservedSources: Map<string, MediaUsageSource>,\n\tcounts: RepairCounts,\n\tcollectionId?: string,\n): Promise<void> {\n\tconst sourceKeys = buildContentSourceKeys(collectionSlug, contentId, collectionId);\n\tconst snapshotsResult = await loadContentMediaUsageSnapshots(\n\t\tdb,\n\t\tcollectionSlug,\n\t\tcontentId,\n\t\tfieldDiscovery,\n\t\tcollectionId ? { collectionId, identityVersion: 1 } : undefined,\n\t);\n\tif (!snapshotsResult.success) {\n\t\tif (snapshotsResult.error === CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_NOT_FOUND) {\n\t\t\tmarkRepairConflict(counts);\n\t\t\tcounts.missingContentIds.add(contentId);\n\t\t\treturn;\n\t\t}\n\t\tcounts.lastErrorCode = snapshotsResult.error;\n\t\tif (snapshotsResult.snapshots) {\n\t\t\t// Partial snapshot failures keep absent variants in place; the attempted source is marked below.\n\t\t\tawait repairSnapshotSources(repo, snapshotsResult.snapshots, observedSources, counts);\n\t\t}\n\t\tif (snapshotsResult.source) {\n\t\t\tconst result = await repo.markSourceAttemptedIfMatching(\n\t\t\t\t{\n\t\t\t\t\t...snapshotsResult.source,\n\t\t\t\t\tsourceCompleteness: \"failed\",\n\t\t\t\t\tlastErrorCode: snapshotsResult.error,\n\t\t\t\t},\n\t\t\t\tobservedSources.get(snapshotsResult.source.sourceKey) ?? null,\n\t\t\t);\n\t\t\tif (result.attempted) {\n\t\t\t\tcounts.failedSourceCount++;\n\t\t\t} else {\n\t\t\t\tmarkRepairConflict(counts);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tcounts.failedSourceCount++;\n\t\treturn;\n\t}\n\n\tconst expectedSourceKeys = await repairSnapshotSources(\n\t\trepo,\n\t\tsnapshotsResult.snapshots,\n\t\tobservedSources,\n\t\tcounts,\n\t);\n\n\tfor (const sourceKey of sourceKeys) {\n\t\tif (expectedSourceKeys.has(sourceKey)) continue;\n\t\tconst observedSource = observedSources.get(sourceKey);\n\t\tif (!observedSource) continue;\n\t\tawait deleteObservedSource(repo, sourceKey, observedSource, counts);\n\t}\n}\n\nasync function repairSnapshotSources(\n\trepo: MediaUsageRepository,\n\tsnapshots: readonly ContentMediaUsageSnapshot[],\n\tobservedSources: Map<string, MediaUsageSource>,\n\tcounts: RepairCounts,\n): Promise<Set<string>> {\n\tconst expectedSourceKeys = new Set<string>();\n\tfor (const snapshot of snapshots) {\n\t\texpectedSourceKeys.add(snapshot.source.sourceKey);\n\t\tconst result = await repo.replaceSourceIfMatching(\n\t\t\tsnapshot.source,\n\t\t\tsnapshot.occurrences,\n\t\t\tobservedSources.get(snapshot.source.sourceKey) ?? null,\n\t\t);\n\t\tif (result.replaced || result.unchanged) {\n\t\t\tcounts.indexedSourceCount++;\n\t\t} else {\n\t\t\tmarkRepairConflict(counts);\n\t\t}\n\t}\n\treturn expectedSourceKeys;\n}\n\nfunction markRepairConflict(counts: RepairCounts): void {\n\tcounts.skippedSourceCount++;\n\tcounts.lastErrorCode ??= CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_CONFLICT;\n}\n\nasync function reconcileOrphanedContentSources(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tcollectionSlug: string,\n\tcounts: RepairCounts,\n\tcollectionId?: string,\n): Promise<void> {\n\tconst sources = await repo.findCollectionContentSources(collectionSlug, collectionId);\n\tconst existingContentIds = await findExistingContentIds(\n\t\tdb,\n\t\tcollectionSlug,\n\t\tsources.flatMap((source) => (source.contentId ? [source.contentId] : [])),\n\t\tcollectionId,\n\t);\n\n\tfor (const source of sources) {\n\t\tif (!source.contentId) {\n\t\t\tawait deleteObservedSource(repo, source.sourceKey, source, counts);\n\t\t\tcontinue;\n\t\t}\n\t\tif (existingContentIds.has(source.contentId)) continue;\n\t\tawait deleteObservedSourceIfContentAbsent(repo, collectionSlug, source, counts);\n\t}\n}\n\nasync function findExistingContentIds(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentIds: readonly string[],\n\tcollectionId?: string,\n): Promise<Set<string>> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\tconst existingContentIds = new Set<string>();\n\tconst uniqueContentIds = [...new Set(contentIds)];\n\tif (uniqueContentIds.length === 0) return existingContentIds;\n\n\tconst tableName = getContentTableName(collectionSlug);\n\tfor (const contentIdBatch of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\tconst result = await sql<{ id: string }>`\n\t\t\tSELECT content.id\n\t\t\tFROM ${sql.ref(tableName)} AS content\n\t\t\tWHERE content.id IN (${sql.join(contentIdBatch)})\n\t\t\t${\n\t\t\t\tcollectionId\n\t\t\t\t\t? sql`AND EXISTS (\n\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\t\t\tWHERE collection.id = ${collectionId}\n\t\t\t\t\t\t\tAND collection.slug = ${collectionSlug}\n\t\t\t\t\t)`\n\t\t\t\t\t: sql``\n\t\t\t}\n\t\t`.execute(db);\n\t\tfor (const row of result.rows) {\n\t\t\texistingContentIds.add(row.id);\n\t\t}\n\t}\n\n\treturn existingContentIds;\n}\n\nasync function deleteObservedSourceIfContentAbsent(\n\trepo: MediaUsageRepository,\n\tcollectionSlug: string,\n\tobservedSource: MediaUsageSource,\n\tcounts: RepairCounts,\n): Promise<void> {\n\tif (!observedSource.contentId) return;\n\tconst result = await repo.deleteSourceIfMatchingContentAbsent(\n\t\tobservedSource.sourceKey,\n\t\tobservedSource,\n\t\tcollectionSlug,\n\t\tobservedSource.contentId,\n\t);\n\tif (result.deleted) {\n\t\tcounts.deletedSourceCount++;\n\t\treturn;\n\t}\n\tif (result.contentPresent) return;\n\tif (result.source) {\n\t\tmarkRepairConflict(counts);\n\t}\n}\n\nasync function deleteObservedSource(\n\trepo: MediaUsageRepository,\n\tsourceKey: string,\n\tobservedSource: MediaUsageSource,\n\tcounts: RepairCounts,\n): Promise<void> {\n\tconst result = await repo.deleteSourceIfMatching(sourceKey, observedSource);\n\tif (result.deleted) {\n\t\tcounts.deletedSourceCount++;\n\t\treturn;\n\t}\n\tif (result.source) {\n\t\tmarkRepairConflict(counts);\n\t}\n}\n\ninterface FinalizeInput extends ContentMediaUsageRepairScope {\n\trunToken: string;\n\tcounts: RepairCounts;\n\tstatus: Exclude<ContentMediaUsageRepairStatus, \"stale\">;\n\tstartedAt: string;\n\tcompletedAt: string;\n\texecution?: ContentMediaUsageRepairExecution;\n}\n\nasync function finalizeRepairStatus(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tinput: FinalizeInput,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\tlet result;\n\tif (input.execution) {\n\t\tif (input.status === \"complete\") {\n\t\t\tawait new MediaUsageWorkRepository(db).deleteWorkThroughEpoch(\n\t\t\t\tinput.execution.collectionId,\n\t\t\t\tinput.execution.startingEpoch,\n\t\t\t);\n\t\t}\n\t\tresult = await repo.finalizeIndexStatusRepairAtEpoch({\n\t\t\tadapterId: input.adapterId,\n\t\t\tscopeType: input.scopeType,\n\t\t\tscopeKey: input.scopeKey,\n\t\t\tcollectionId: input.execution.collectionId,\n\t\t\trunToken: input.runToken,\n\t\t\tstartingEpoch: input.execution.startingEpoch,\n\t\t\tstatus: input.status,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t\tindexedSourceCount: input.counts.indexedSourceCount,\n\t\t\tfailedSourceCount: input.counts.failedSourceCount,\n\t\t\tlastErrorCode: input.counts.lastErrorCode,\n\t\t});\n\t} else {\n\t\tresult = await repo.finalizeIndexStatusRepairIfRunning({\n\t\t\tadapterId: input.adapterId,\n\t\t\tscopeType: input.scopeType,\n\t\t\tscopeKey: input.scopeKey,\n\t\t\trunToken: input.runToken,\n\t\t\tstatus: input.status,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t\tcompletedAt: input.completedAt,\n\t\t\tindexedSourceCount: input.counts.indexedSourceCount,\n\t\t\tfailedSourceCount: input.counts.failedSourceCount,\n\t\t\tlastErrorCode: input.counts.lastErrorCode,\n\t\t});\n\t}\n\n\treturn {\n\t\tscope: {\n\t\t\tadapterId: input.adapterId,\n\t\t\tscopeType: input.scopeType,\n\t\t\tscopeKey: input.scopeKey,\n\t\t},\n\t\tstatus: result.finalized ? input.status : \"stale\",\n\t\tindexedSourceCount: input.counts.indexedSourceCount,\n\t\tfailedSourceCount: input.counts.failedSourceCount,\n\t\tskippedSourceCount: input.counts.skippedSourceCount,\n\t\tdeletedSourceCount: input.counts.deletedSourceCount,\n\t\tlastErrorCode: result.finalized\n\t\t\t? input.counts.lastErrorCode\n\t\t\t: (result.status?.lastErrorCode ??\n\t\t\t\tCONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_CONFLICT),\n\t\tstartedAt: input.startedAt,\n\t\tcompletedAt: result.finalized ? (result.status?.completedAt ?? input.completedAt) : null,\n\t};\n}\n\nfunction determineRepairStatus(\n\tcounts: RepairCounts,\n): Exclude<ContentMediaUsageRepairStatus, \"stale\"> {\n\tif (counts.failedSourceCount === 0 && counts.skippedSourceCount === 0) return \"complete\";\n\tconst trustedProgress = counts.indexedSourceCount + counts.deletedSourceCount;\n\tif (counts.failedSourceCount > 0 && trustedProgress === 0) {\n\t\treturn \"failed\";\n\t}\n\treturn \"partial\";\n}\n\nfunction buildContentSourceKeys(\n\tcollectionSlug: string,\n\tcontentId: string,\n\tcollectionId?: string,\n): string[] {\n\treturn MEDIA_USAGE_CONTENT_SOURCE_VARIANTS.map((sourceVariant) =>\n\t\tbuildContentMediaUsageSourceKey({ collectionId, collectionSlug, contentId, sourceVariant }),\n\t);\n}\n\nfunction buildContentSourceKeysForScan(\n\tscan: ContentMediaUsageCollectionScan,\n\tcollectionId?: string,\n): string[] {\n\treturn scan.contentIds.flatMap((contentId) =>\n\t\tbuildContentSourceKeys(scan.collectionSlug, contentId, collectionId),\n\t);\n}\n\nasync function loadContentMediaUsageCollectionRecord(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n): Promise<ContentMediaUsageCollectionRecord | null> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select([\"id\", \"slug\"])\n\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nasync function isIncrementalCaptureActive(db: Kysely<Database>): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_media_usage_activation\")\n\t\t.select(\"state\")\n\t\t.where(\"task_key\", \"=\", \"incremental_capture\")\n\t\t.executeTakeFirst();\n\treturn row?.state === \"active\";\n}\n\nfunction sameContentIds(left: readonly string[], right: readonly string[]): boolean {\n\tif (left.length !== right.length) return false;\n\tconst rightIds = new Set(right);\n\treturn left.every((id) => rightIds.has(id));\n}\n\nfunction repairedContentIds(\n\tcontentIds: readonly string[],\n\tcounts: Pick<RepairCounts, \"missingContentIds\">,\n): string[] {\n\tif (counts.missingContentIds.size === 0) return [...contentIds];\n\treturn contentIds.filter((contentId) => !counts.missingContentIds.has(contentId));\n}\n\nfunction contentMediaUsageCollectionScope(collectionSlug: string): ContentMediaUsageRepairScope {\n\treturn {\n\t\tadapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,\n\t\tscopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\t\tscopeKey: collectionSlug,\n\t};\n}\n\nfunction getContentTableName(collectionSlug: string): string {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\treturn `ec_${collectionSlug}`;\n}\n","import type { Kysely } from \"kysely\";\n\nimport {\n\tMediaUsageRepository,\n\ttype MediaUsageCollectionIndexStatusScope,\n\ttype MediaUsageEntryGroup,\n} from \"../../database/repositories/media-usage.js\";\nimport { MediaRepository } from \"../../database/repositories/media.js\";\nimport { InvalidCursorError } from \"../../database/repositories/types.js\";\nimport type { Database } from \"../../database/types.js\";\nimport {\n\tCONTENT_MEDIA_USAGE_ADAPTER_ID,\n\tCONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n} from \"../../media/usage/content-refresh.js\";\nimport {\n\trepairContentMediaUsageAll,\n\trepairContentMediaUsageCollection,\n\ttype ContentMediaUsageRepairAllResult,\n\ttype ContentMediaUsageRepairCollectionResult,\n} from \"../../media/usage/content-repair.js\";\nimport { CONTENT_SOURCE_SCHEMA_VERSION } from \"../../media/usage/content-snapshots.js\";\nimport { ErrorCode } from \"../errors.js\";\nimport type {\n\tMediaUsageCoverage,\n\tMediaUsageCoverageStatus,\n\tMediaUsageDetailsResponse,\n\tMediaUsageEntryDetail,\n\tMediaUsageOccurrenceDetail,\n\tMediaUsageRepairRequest,\n\tMediaUsageRepairResponse,\n\tMediaUsageSummary,\n} from \"../schemas/media-usage.js\";\nimport type { ApiResult } from \"../types.js\";\n\nexport type {\n\tMediaUsageCoverage,\n\tMediaUsageCoverageStatus,\n\tMediaUsageDetailsResponse,\n\tMediaUsageEntryDetail,\n\tMediaUsageOccurrenceDetail,\n\tMediaUsageSourceDetail,\n\tMediaUsageRepairRequest,\n\tMediaUsageRepairResponse,\n\tMediaUsageSummary,\n} from \"../schemas/media-usage.js\";\n\ntype ContentMediaUsageRepairResult =\n\t| ContentMediaUsageRepairCollectionResult\n\t| ContentMediaUsageRepairAllResult;\n\nexport function aggregateMediaUsageCoverageStatus(\n\tscopes: readonly MediaUsageCollectionIndexStatusScope[],\n): MediaUsageCoverageStatus {\n\tconst statuses = scopes.map(normalizeMediaUsageCoverageStatus);\n\tif (statuses.every((status) => status === \"complete\")) {\n\t\treturn \"complete\";\n\t}\n\tif (statuses.includes(\"unknown\")) return \"unknown\";\n\tif (statuses.includes(\"running\")) return \"running\";\n\tif (statuses.includes(\"stale\")) return \"stale\";\n\tif (statuses.includes(\"partial\")) return \"partial\";\n\tif (statuses.every((status) => status === \"never\")) return \"never\";\n\tif (statuses.every((status) => status === \"failed\")) return \"failed\";\n\treturn \"partial\";\n}\n\nexport async function handleMediaUsageSummaries(\n\tdb: Kysely<Database>,\n\tmediaIds: readonly string[],\n\toptions: { includeCount: boolean },\n): Promise<ApiResult<Record<string, MediaUsageSummary>>> {\n\tif (mediaIds.length === 0) return { success: true, data: {} };\n\n\ttry {\n\t\tconst repository = new MediaUsageRepository(db);\n\t\tconst coverage = await loadMediaUsageCoverage(repository);\n\t\tconst counts =\n\t\t\toptions.includeCount && coverage.status === \"complete\"\n\t\t\t\t? await repository.findActiveEntryCountsByMediaIds(mediaIds)\n\t\t\t\t: null;\n\t\tconst summaries: Record<string, MediaUsageSummary> = {};\n\n\t\tfor (const mediaId of new Set(mediaIds)) {\n\t\t\tsummaries[mediaId] = {\n\t\t\t\tcount: counts ? (counts.get(mediaId) ?? 0) : null,\n\t\t\t\tcoverage,\n\t\t\t};\n\t\t}\n\n\t\treturn { success: true, data: summaries };\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage] summary read failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_READ_ERROR,\n\t\t\t\tmessage: \"Failed to read media usage\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport async function handleMediaUsageDetails(\n\tdb: Kysely<Database>,\n\tmediaId: string,\n\toptions: { cursor?: string; limit?: number },\n): Promise<ApiResult<MediaUsageDetailsResponse>> {\n\ttry {\n\t\tconst media = await new MediaRepository(db).findById(mediaId);\n\t\tif (!media) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.NOT_FOUND,\n\t\t\t\t\tmessage: `Media item not found: ${mediaId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst repository = new MediaUsageRepository(db);\n\t\tconst coverage = await loadMediaUsageCoverage(repository);\n\t\tconst page = await repository.findCurrentEntryUsagePageByMediaId(mediaId, options);\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\titems: page.items.map(toMediaUsageEntryDetail),\n\t\t\t\t...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),\n\t\t\t\tcoverage,\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof InvalidCursorError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: ErrorCode.INVALID_CURSOR, message: error.message },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[media-usage] detail read failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_READ_ERROR,\n\t\t\t\tmessage: \"Failed to read media usage\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport async function handleMediaUsageRepair(\n\tdb: Kysely<Database>,\n\tinput: MediaUsageRepairRequest,\n): Promise<ApiResult<MediaUsageRepairResponse>> {\n\ttry {\n\t\tlet result: ContentMediaUsageRepairResult;\n\t\tif (input.scope === \"collection\") {\n\t\t\tresult = await repairContentMediaUsageCollection(db, { collectionSlug: input.collection });\n\t\t} else if (input.scope === \"all\") {\n\t\t\tresult = await repairContentMediaUsageAll(db);\n\t\t} else {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.VALIDATION_ERROR,\n\t\t\t\t\tmessage: \"Invalid media usage repair request\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn { success: true, data: toMediaUsageRepairResponse(result) };\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage] repair failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_REPAIR_ERROR,\n\t\t\t\tmessage: \"Failed to repair media usage\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport function toMediaUsageRepairResponse(\n\tresult: ContentMediaUsageRepairResult,\n): MediaUsageRepairResponse {\n\tconst collections = \"collections\" in result ? result.collections : [result];\n\n\treturn {\n\t\tstatus: result.status,\n\t\tindexedSourceCount: result.indexedSourceCount,\n\t\tfailedSourceCount: result.failedSourceCount,\n\t\tskippedSourceCount: result.skippedSourceCount,\n\t\tdeletedSourceCount: result.deletedSourceCount,\n\t\tcollections: collections.map(toMediaUsageRepairCollectionSummary),\n\t};\n}\n\nfunction toMediaUsageRepairCollectionSummary(result: ContentMediaUsageRepairCollectionResult) {\n\treturn {\n\t\tcollection: result.scope.scopeKey,\n\t\tstatus: result.status,\n\t\tindexedSourceCount: result.indexedSourceCount,\n\t\tfailedSourceCount: result.failedSourceCount,\n\t\tskippedSourceCount: result.skippedSourceCount,\n\t\tdeletedSourceCount: result.deletedSourceCount,\n\t\tlastErrorCode: result.lastErrorCode,\n\t\tstartedAt: result.startedAt,\n\t\tcompletedAt: result.completedAt,\n\t};\n}\n\nfunction normalizeMediaUsageCoverageStatus(\n\tscope: MediaUsageCollectionIndexStatusScope,\n): MediaUsageCoverageStatus {\n\tif (scope.status === null) return \"never\";\n\tif (scope.status === \"complete\") {\n\t\tif (scope.reconciliationRequired) return \"stale\";\n\t\treturn scope.schemaVersion === CONTENT_SOURCE_SCHEMA_VERSION ? \"complete\" : \"stale\";\n\t}\n\tif (\n\t\tscope.status === \"never\" ||\n\t\tscope.status === \"running\" ||\n\t\tscope.status === \"partial\" ||\n\t\tscope.status === \"failed\" ||\n\t\tscope.status === \"stale\"\n\t) {\n\t\treturn scope.status;\n\t}\n\treturn \"unknown\";\n}\n\nasync function loadMediaUsageCoverage(\n\trepository: MediaUsageRepository,\n): Promise<MediaUsageCoverage> {\n\tconst scopes = await repository.findCollectionIndexStatusScopes({\n\t\tadapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,\n\t\tscopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\t});\n\treturn {\n\t\tscope: \"all_content_collections\",\n\t\tstatus: aggregateMediaUsageCoverageStatus(scopes),\n\t};\n}\n\nfunction toMediaUsageEntryDetail(group: MediaUsageEntryGroup): MediaUsageEntryDetail {\n\tconst preferred =\n\t\tgroup.sources.find(({ source }) => source.sourceVariant === \"draft_overlay\") ??\n\t\tgroup.sources.find(({ source }) => source.sourceVariant === \"columns\");\n\tif (!preferred) {\n\t\tthrow new Error(\"Media usage entry has no supported source\");\n\t}\n\n\treturn {\n\t\tcollection: group.collectionSlug,\n\t\tcontentId: group.contentId,\n\t\ttitle: preferred.source.contentTitle,\n\t\tslug: preferred.source.contentSlug,\n\t\tlocale: preferred.source.locale,\n\t\tstatus: preferred.source.contentStatus,\n\t\tscheduledAt: preferred.source.contentScheduledAt,\n\t\tdeletedAt: group.contentDeletedAt,\n\t\tsources: group.sources.flatMap(({ source, occurrences }) => {\n\t\t\tif (source.sourceVariant !== \"columns\" && source.sourceVariant !== \"draft_overlay\") {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tvariant: source.sourceVariant,\n\t\t\t\t\toccurrences: occurrences.map((occurrence) => ({\n\t\t\t\t\t\tfieldSlug: occurrence.fieldSlug,\n\t\t\t\t\t\tfieldPath: occurrence.fieldPath,\n\t\t\t\t\t\toccurrenceIndex: occurrence.occurrenceIndex,\n\t\t\t\t\t\treferenceType: normalizeMediaUsageReferenceType(occurrence.referenceType),\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t];\n\t\t}),\n\t};\n}\n\nfunction normalizeMediaUsageReferenceType(\n\treferenceType: string,\n): MediaUsageOccurrenceDetail[\"referenceType\"] {\n\tif (\n\t\treferenceType === \"image_field\" ||\n\t\treferenceType === \"file_field\" ||\n\t\treferenceType === \"portable_text_image\"\n\t) {\n\t\treturn referenceType;\n\t}\n\treturn \"unknown\";\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"media-usage-BZCydfvu.mjs","names":[],"sources":["../src/media/usage/content-repair.ts","../src/api/handlers/media-usage.ts"],"sourcesContent":["import { sql, type Kysely } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { MediaUsageWorkRepository } from \"../../database/repositories/media-usage-work.js\";\nimport {\n\tMediaUsageRepository,\n\ttype MediaUsageSource,\n} from \"../../database/repositories/media-usage.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { validateIdentifier } from \"../../database/validate.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../../utils/chunks.js\";\nimport {\n\tloadContentMediaUsageFields,\n\tMediaUsageFieldDiscoveryError,\n\ttype ContentMediaUsageFieldDiscovery,\n} from \"./content-fields.js\";\nimport {\n\tCONTENT_MEDIA_USAGE_ADAPTER_ID,\n\tCONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\twithContentUsageCollectionLock,\n} from \"./content-refresh.js\";\nimport {\n\tCONTENT_SOURCE_SCHEMA_VERSION,\n\ttype ContentMediaUsageSnapshot,\n\tloadContentMediaUsageSnapshots,\n} from \"./content-snapshots.js\";\nimport {\n\tbuildContentMediaUsageSourceKey,\n\tMEDIA_USAGE_CONTENT_SOURCE_VARIANTS,\n} from \"./source-key.js\";\n\nexport type ContentMediaUsageRepairStatus = \"complete\" | \"partial\" | \"failed\" | \"stale\";\n\nexport const CONTENT_MEDIA_USAGE_REPAIR_ERROR = {\n\tCOLLECTION_NOT_FOUND: \"COLLECTION_NOT_FOUND\",\n\tCONTENT_NOT_FOUND: \"CONTENT_NOT_FOUND\",\n\tDRAFT_REVISION_NOT_FOUND: \"DRAFT_REVISION_NOT_FOUND\",\n\tDRAFT_REVISION_MISMATCH: \"DRAFT_REVISION_MISMATCH\",\n\tDRAFT_REVISION_INVALID: \"DRAFT_REVISION_INVALID\",\n\tCONTENT_USAGE_REPAIR_ERROR: \"CONTENT_USAGE_REPAIR_ERROR\",\n\tCONTENT_USAGE_REPAIR_CONFLICT: \"CONTENT_USAGE_REPAIR_CONFLICT\",\n\tINVALID_REPEATER_VALIDATION: \"INVALID_REPEATER_VALIDATION\",\n} as const;\n\nexport type ContentMediaUsageRepairErrorCode =\n\t(typeof CONTENT_MEDIA_USAGE_REPAIR_ERROR)[keyof typeof CONTENT_MEDIA_USAGE_REPAIR_ERROR];\n\nexport interface ContentMediaUsageRepairCollectionInput {\n\tcollectionSlug: string;\n}\n\nexport interface ContentMediaUsageRepairScope {\n\tadapterId: typeof CONTENT_MEDIA_USAGE_ADAPTER_ID;\n\tscopeType: typeof CONTENT_MEDIA_USAGE_COLLECTION_SCOPE;\n\tscopeKey: string;\n}\n\nexport interface ContentMediaUsageRepairCollectionResult {\n\tscope: ContentMediaUsageRepairScope;\n\tstatus: ContentMediaUsageRepairStatus;\n\tindexedSourceCount: number;\n\tfailedSourceCount: number;\n\tskippedSourceCount: number;\n\tdeletedSourceCount: number;\n\tlastErrorCode: string | null;\n\tstartedAt: string;\n\tcompletedAt: string | null;\n}\n\nexport interface ContentMediaUsageRepairAllResult {\n\tstatus: ContentMediaUsageRepairStatus;\n\tcollections: ContentMediaUsageRepairCollectionResult[];\n\tindexedSourceCount: number;\n\tfailedSourceCount: number;\n\tskippedSourceCount: number;\n\tdeletedSourceCount: number;\n}\n\nexport interface ContentMediaUsageCollectionScan {\n\tcollectionSlug: string;\n\tcontentIds: string[];\n}\n\ninterface ContentMediaUsageCollectionRecord {\n\tid: string;\n\tslug: string;\n}\n\ninterface ContentMediaUsageInitialCollectionResult {\n\tcollection: ContentMediaUsageCollectionRecord;\n\tresult: ContentMediaUsageRepairCollectionResult;\n}\n\ninterface ContentMediaUsageRepairExecution {\n\tcollectionId: string;\n\tstartingEpoch: number | string;\n}\n\nexport async function repairContentMediaUsageAll(\n\tdb: Kysely<Database>,\n): Promise<ContentMediaUsageRepairAllResult> {\n\tconst collections = await loadContentMediaUsageCollectionRecords(db);\n\tconst results: ContentMediaUsageInitialCollectionResult[] = [];\n\n\tfor (const collection of collections) {\n\t\tresults.push({\n\t\t\tcollection,\n\t\t\tresult: await repairContentMediaUsageCollectionSafely(db, collection),\n\t\t});\n\t}\n\n\treturn aggregateContentMediaUsageRepairAll(\n\t\tawait filterExistingContentMediaUsageCollectionResults(db, results),\n\t);\n}\n\nexport async function scanContentMediaUsageCollection(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\texpectedCollectionId?: string,\n): Promise<ContentMediaUsageCollectionScan | null> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\tlet collectionQuery = db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select(\"id\")\n\t\t.where(\"slug\", \"=\", collectionSlug);\n\tif (expectedCollectionId !== undefined) {\n\t\tcollectionQuery = collectionQuery.where(\"id\", \"=\", expectedCollectionId);\n\t}\n\tconst collection = await collectionQuery.executeTakeFirst();\n\tif (!collection) return null;\n\n\tconst tableName = getContentTableName(collectionSlug);\n\tconst rows = await sql<{ id: string }>`\n\t\tSELECT content.id\n\t\tFROM ${sql.ref(tableName)} AS content\n\t\tWHERE EXISTS (\n\t\t\tSELECT 1\n\t\t\tFROM _emdash_collections AS collection\n\t\t\tWHERE collection.id = ${collection.id}\n\t\t\t\tAND collection.slug = ${collectionSlug}\n\t\t)\n\t\tORDER BY content.id ASC\n\t`.execute(db);\n\n\treturn {\n\t\tcollectionSlug,\n\t\tcontentIds: rows.rows.map((row) => row.id),\n\t};\n}\n\nexport async function repairContentMediaUsageCollection(\n\tdb: Kysely<Database>,\n\tinput: ContentMediaUsageRepairCollectionInput,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\tvalidateIdentifier(input.collectionSlug, \"collection slug\");\n\treturn withContentUsageCollectionLock(input.collectionSlug, () =>\n\t\trepairContentMediaUsageCollectionUnlocked(db, input.collectionSlug),\n\t);\n}\n\nasync function loadContentMediaUsageCollectionRecords(\n\tdb: Kysely<Database>,\n): Promise<ContentMediaUsageCollectionRecord[]> {\n\treturn db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select([\"id\", \"slug\"])\n\t\t.orderBy(\"slug\", \"asc\")\n\t\t.execute();\n}\n\nasync function repairContentMediaUsageCollectionSafely(\n\tdb: Kysely<Database>,\n\tcollection: ContentMediaUsageCollectionRecord,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\ttry {\n\t\treturn await withContentUsageCollectionLock(collection.slug, () =>\n\t\t\trepairContentMediaUsageCollectionUnlocked(db, collection.slug, collection.id),\n\t\t);\n\t} catch (error) {\n\t\tconsole.error(`[media-usage] Failed to repair collection ${collection.slug}:`, error);\n\t\tconst now = new Date().toISOString();\n\t\treturn {\n\t\t\tscope: contentMediaUsageCollectionScope(collection.slug),\n\t\t\tstatus: \"failed\",\n\t\t\tindexedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\tskippedSourceCount: 0,\n\t\t\tdeletedSourceCount: 0,\n\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_ERROR,\n\t\t\tstartedAt: now,\n\t\t\tcompletedAt: now,\n\t\t};\n\t}\n}\n\nasync function filterExistingContentMediaUsageCollectionResults(\n\tdb: Kysely<Database>,\n\tresults: readonly ContentMediaUsageInitialCollectionResult[],\n): Promise<ContentMediaUsageRepairCollectionResult[]> {\n\tconst identityBound = await isIncrementalCaptureActive(db);\n\tconst currentCollections = await loadContentMediaUsageCollectionRecordsSafely(db);\n\tconst currentIdsBySlug = new Map(\n\t\tcurrentCollections.map((collection) => [collection.slug, collection.id]),\n\t);\n\tconst includedResults: ContentMediaUsageRepairCollectionResult[] = [];\n\tconst excludedResults: ContentMediaUsageInitialCollectionResult[] = [];\n\n\tfor (const { collection, result } of results) {\n\t\tif (currentIdsBySlug.get(collection.slug) === collection.id) {\n\t\t\tincludedResults.push(result);\n\t\t} else {\n\t\t\texcludedResults.push({ collection, result });\n\t\t}\n\t}\n\n\tif (excludedResults.length > 0) {\n\t\tconst repo = new MediaUsageRepository(db);\n\t\tfor (const excluded of excludedResults) {\n\t\t\tawait repo.deleteIndexStatus(\n\t\t\t\texcluded.result.scope,\n\t\t\t\tidentityBound ? excluded.collection.id : undefined,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn includedResults;\n}\n\nasync function loadContentMediaUsageCollectionRecordsSafely(\n\tdb: Kysely<Database>,\n): Promise<ContentMediaUsageCollectionRecord[]> {\n\ttry {\n\t\treturn await loadContentMediaUsageCollectionRecords(db);\n\t} catch {\n\t\t// Retry once before failing; returning unpruned results can over-report deleted collections.\n\t}\n\n\ttry {\n\t\treturn await loadContentMediaUsageCollectionRecords(db);\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage] Failed to reconcile all-content repair collections:\", error);\n\t\tthrow error;\n\t}\n}\n\nfunction aggregateContentMediaUsageRepairAll(\n\tcollections: readonly ContentMediaUsageRepairCollectionResult[],\n): ContentMediaUsageRepairAllResult {\n\treturn {\n\t\tstatus: determineRepairAllStatus(collections),\n\t\tcollections: [...collections],\n\t\tindexedSourceCount: sumCollectionRepairCount(collections, \"indexedSourceCount\"),\n\t\tfailedSourceCount: sumCollectionRepairCount(collections, \"failedSourceCount\"),\n\t\tskippedSourceCount: sumCollectionRepairCount(collections, \"skippedSourceCount\"),\n\t\tdeletedSourceCount: sumCollectionRepairCount(collections, \"deletedSourceCount\"),\n\t};\n}\n\nfunction determineRepairAllStatus(\n\tcollections: readonly ContentMediaUsageRepairCollectionResult[],\n): ContentMediaUsageRepairStatus {\n\tif (collections.length === 0) return \"complete\";\n\tif (collections.every((collection) => collection.status === \"complete\")) return \"complete\";\n\tif (collections.some((collection) => collection.status === \"stale\")) return \"stale\";\n\tif (collections.some((collection) => collection.status === \"partial\")) return \"partial\";\n\tif (collections.every((collection) => collection.status === \"failed\")) return \"failed\";\n\treturn \"partial\";\n}\n\nfunction sumCollectionRepairCount(\n\tcollections: readonly ContentMediaUsageRepairCollectionResult[],\n\tkey: \"indexedSourceCount\" | \"failedSourceCount\" | \"skippedSourceCount\" | \"deletedSourceCount\",\n): number {\n\treturn collections.reduce((sum, collection) => sum + collection[key], 0);\n}\n\nasync function repairContentMediaUsageCollectionUnlocked(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\texpectedCollectionId?: string,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\tconst requestedAt = new Date().toISOString();\n\tconst scope = contentMediaUsageCollectionScope(collectionSlug);\n\tconst identityBound = await isIncrementalCaptureActive(db);\n\tconst collection = await loadContentMediaUsageCollectionRecord(db, collectionSlug);\n\tif (\n\t\t!collection ||\n\t\t(identityBound && expectedCollectionId && collection.id !== expectedCollectionId)\n\t) {\n\t\treturn {\n\t\t\tscope,\n\t\t\tstatus: \"failed\",\n\t\t\tindexedSourceCount: 0,\n\t\t\tfailedSourceCount: 0,\n\t\t\tskippedSourceCount: 0,\n\t\t\tdeletedSourceCount: 0,\n\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.COLLECTION_NOT_FOUND,\n\t\t\tstartedAt: requestedAt,\n\t\t\tcompletedAt: requestedAt,\n\t\t};\n\t}\n\n\tconst repo = new MediaUsageRepository(db);\n\tconst runToken = ulid();\n\tlet startedAt = requestedAt;\n\tlet execution: ContentMediaUsageRepairExecution | undefined;\n\tif (identityBound) {\n\t\tconst run = await repo.beginIndexStatusRepairAtCurrentEpoch({\n\t\t\t...scope,\n\t\t\tcollectionId: collection.id,\n\t\t\trunToken,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t});\n\t\tif (!run) {\n\t\t\treturn {\n\t\t\t\tscope,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tindexedSourceCount: 0,\n\t\t\t\tfailedSourceCount: 0,\n\t\t\t\tskippedSourceCount: 0,\n\t\t\t\tdeletedSourceCount: 0,\n\t\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_ERROR,\n\t\t\t\tstartedAt,\n\t\t\t\tcompletedAt: requestedAt,\n\t\t\t};\n\t\t}\n\t\tstartedAt = run.startedAt;\n\t\texecution = { collectionId: collection.id, startingEpoch: run.changeEpoch };\n\t} else {\n\t\tawait repo.beginIndexStatusRepair({\n\t\t\t...scope,\n\t\t\trunToken,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t\tstartedAt,\n\t\t});\n\t}\n\n\ttry {\n\t\tconst scan = await scanContentMediaUsageCollection(db, collectionSlug, execution?.collectionId);\n\t\tif (!scan) {\n\t\t\tconst completedAt = new Date().toISOString();\n\t\t\treturn await finalizeRepairStatus(db, repo, {\n\t\t\t\t...scope,\n\t\t\t\trunToken,\n\t\t\t\tcounts: {\n\t\t\t\t\tindexedSourceCount: 0,\n\t\t\t\t\tfailedSourceCount: 0,\n\t\t\t\t\tskippedSourceCount: 0,\n\t\t\t\t\tdeletedSourceCount: 0,\n\t\t\t\t\tlastErrorCode: CONTENT_MEDIA_USAGE_REPAIR_ERROR.COLLECTION_NOT_FOUND,\n\t\t\t\t\tmissingContentIds: new Set(),\n\t\t\t\t},\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tstartedAt,\n\t\t\t\tcompletedAt,\n\t\t\t\texecution,\n\t\t\t});\n\t\t}\n\t\tconst counts = await repairScannedContentSources(db, repo, scan, execution?.collectionId);\n\t\tconst finalScan = await scanContentMediaUsageCollection(\n\t\t\tdb,\n\t\t\tcollectionSlug,\n\t\t\texecution?.collectionId,\n\t\t);\n\t\tif (!finalScan) {\n\t\t\tcounts.failedSourceCount++;\n\t\t\tcounts.lastErrorCode = CONTENT_MEDIA_USAGE_REPAIR_ERROR.COLLECTION_NOT_FOUND;\n\t\t} else if (!sameContentIds(repairedContentIds(scan.contentIds, counts), finalScan.contentIds)) {\n\t\t\tmarkRepairConflict(counts);\n\t\t}\n\t\tconst completedAt = new Date().toISOString();\n\t\tconst status = determineRepairStatus(counts);\n\t\treturn await finalizeRepairStatus(db, repo, {\n\t\t\t...scope,\n\t\t\trunToken,\n\t\t\tcounts,\n\t\t\tstatus,\n\t\t\tstartedAt,\n\t\t\tcompletedAt,\n\t\t\texecution,\n\t\t});\n\t} catch (error) {\n\t\tif (!(error instanceof MediaUsageFieldDiscoveryError)) {\n\t\t\tconsole.error(`[media-usage] Failed to repair collection ${collectionSlug}:`, error);\n\t\t}\n\t\tconst completedAt = new Date().toISOString();\n\t\tconst lastErrorCode =\n\t\t\terror instanceof MediaUsageFieldDiscoveryError\n\t\t\t\t? error.code\n\t\t\t\t: CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_ERROR;\n\t\treturn finalizeRepairStatus(db, repo, {\n\t\t\t...scope,\n\t\t\trunToken,\n\t\t\tcounts: {\n\t\t\t\tindexedSourceCount: 0,\n\t\t\t\tfailedSourceCount: 0,\n\t\t\t\tskippedSourceCount: 0,\n\t\t\t\tdeletedSourceCount: 0,\n\t\t\t\tlastErrorCode,\n\t\t\t\tmissingContentIds: new Set(),\n\t\t\t},\n\t\t\tstatus: \"failed\",\n\t\t\tstartedAt,\n\t\t\tcompletedAt,\n\t\t\texecution,\n\t\t});\n\t}\n}\n\ninterface RepairCounts {\n\tindexedSourceCount: number;\n\tfailedSourceCount: number;\n\tskippedSourceCount: number;\n\tdeletedSourceCount: number;\n\tlastErrorCode: ContentMediaUsageRepairErrorCode | null;\n\tmissingContentIds: Set<string>;\n}\n\nasync function repairScannedContentSources(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tscan: ContentMediaUsageCollectionScan,\n\tcollectionId?: string,\n): Promise<RepairCounts> {\n\tconst counts: RepairCounts = {\n\t\tindexedSourceCount: 0,\n\t\tfailedSourceCount: 0,\n\t\tskippedSourceCount: 0,\n\t\tdeletedSourceCount: 0,\n\t\tlastErrorCode: null,\n\t\tmissingContentIds: new Set(),\n\t};\n\n\tconst fieldDiscovery = await loadContentMediaUsageFields(db, scan.collectionSlug, collectionId);\n\tconst observedSources = await repo.findSources(buildContentSourceKeysForScan(scan, collectionId));\n\n\tfor (const contentId of scan.contentIds) {\n\t\tawait repairContentSource(\n\t\t\tdb,\n\t\t\trepo,\n\t\t\tscan.collectionSlug,\n\t\t\tcontentId,\n\t\t\tfieldDiscovery,\n\t\t\tobservedSources,\n\t\t\tcounts,\n\t\t\tcollectionId,\n\t\t);\n\t}\n\n\tawait reconcileOrphanedContentSources(db, repo, scan.collectionSlug, counts, collectionId);\n\treturn counts;\n}\n\nasync function repairContentSource(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tcollectionSlug: string,\n\tcontentId: string,\n\tfieldDiscovery: ContentMediaUsageFieldDiscovery,\n\tobservedSources: Map<string, MediaUsageSource>,\n\tcounts: RepairCounts,\n\tcollectionId?: string,\n): Promise<void> {\n\tconst sourceKeys = buildContentSourceKeys(collectionSlug, contentId, collectionId);\n\tconst snapshotsResult = await loadContentMediaUsageSnapshots(\n\t\tdb,\n\t\tcollectionSlug,\n\t\tcontentId,\n\t\tfieldDiscovery,\n\t\tcollectionId ? { collectionId, identityVersion: 1 } : undefined,\n\t);\n\tif (!snapshotsResult.success) {\n\t\tif (snapshotsResult.error === CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_NOT_FOUND) {\n\t\t\tmarkRepairConflict(counts);\n\t\t\tcounts.missingContentIds.add(contentId);\n\t\t\treturn;\n\t\t}\n\t\tcounts.lastErrorCode = snapshotsResult.error;\n\t\tif (snapshotsResult.snapshots) {\n\t\t\t// Partial snapshot failures keep absent variants in place; the attempted source is marked below.\n\t\t\tawait repairSnapshotSources(repo, snapshotsResult.snapshots, observedSources, counts);\n\t\t}\n\t\tif (snapshotsResult.source) {\n\t\t\tconst result = await repo.markSourceAttemptedIfMatching(\n\t\t\t\t{\n\t\t\t\t\t...snapshotsResult.source,\n\t\t\t\t\tsourceCompleteness: \"failed\",\n\t\t\t\t\tlastErrorCode: snapshotsResult.error,\n\t\t\t\t},\n\t\t\t\tobservedSources.get(snapshotsResult.source.sourceKey) ?? null,\n\t\t\t);\n\t\t\tif (result.attempted) {\n\t\t\t\tcounts.failedSourceCount++;\n\t\t\t} else {\n\t\t\t\tmarkRepairConflict(counts);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tcounts.failedSourceCount++;\n\t\treturn;\n\t}\n\n\tconst expectedSourceKeys = await repairSnapshotSources(\n\t\trepo,\n\t\tsnapshotsResult.snapshots,\n\t\tobservedSources,\n\t\tcounts,\n\t);\n\n\tfor (const sourceKey of sourceKeys) {\n\t\tif (expectedSourceKeys.has(sourceKey)) continue;\n\t\tconst observedSource = observedSources.get(sourceKey);\n\t\tif (!observedSource) continue;\n\t\tawait deleteObservedSource(repo, sourceKey, observedSource, counts);\n\t}\n}\n\nasync function repairSnapshotSources(\n\trepo: MediaUsageRepository,\n\tsnapshots: readonly ContentMediaUsageSnapshot[],\n\tobservedSources: Map<string, MediaUsageSource>,\n\tcounts: RepairCounts,\n): Promise<Set<string>> {\n\tconst expectedSourceKeys = new Set<string>();\n\tfor (const snapshot of snapshots) {\n\t\texpectedSourceKeys.add(snapshot.source.sourceKey);\n\t\tconst result = await repo.replaceSourceIfMatching(\n\t\t\tsnapshot.source,\n\t\t\tsnapshot.occurrences,\n\t\t\tobservedSources.get(snapshot.source.sourceKey) ?? null,\n\t\t);\n\t\tif (result.replaced || result.unchanged) {\n\t\t\tcounts.indexedSourceCount++;\n\t\t} else {\n\t\t\tmarkRepairConflict(counts);\n\t\t}\n\t}\n\treturn expectedSourceKeys;\n}\n\nfunction markRepairConflict(counts: RepairCounts): void {\n\tcounts.skippedSourceCount++;\n\tcounts.lastErrorCode ??= CONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_CONFLICT;\n}\n\nasync function reconcileOrphanedContentSources(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tcollectionSlug: string,\n\tcounts: RepairCounts,\n\tcollectionId?: string,\n): Promise<void> {\n\tconst sources = await repo.findCollectionContentSources(collectionSlug, collectionId);\n\tconst existingContentIds = await findExistingContentIds(\n\t\tdb,\n\t\tcollectionSlug,\n\t\tsources.flatMap((source) => (source.contentId ? [source.contentId] : [])),\n\t\tcollectionId,\n\t);\n\n\tfor (const source of sources) {\n\t\tif (!source.contentId) {\n\t\t\tawait deleteObservedSource(repo, source.sourceKey, source, counts);\n\t\t\tcontinue;\n\t\t}\n\t\tif (existingContentIds.has(source.contentId)) continue;\n\t\tawait deleteObservedSourceIfContentAbsent(repo, collectionSlug, source, counts);\n\t}\n}\n\nasync function findExistingContentIds(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n\tcontentIds: readonly string[],\n\tcollectionId?: string,\n): Promise<Set<string>> {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\tconst existingContentIds = new Set<string>();\n\tconst uniqueContentIds = [...new Set(contentIds)];\n\tif (uniqueContentIds.length === 0) return existingContentIds;\n\n\tconst tableName = getContentTableName(collectionSlug);\n\tfor (const contentIdBatch of chunks(uniqueContentIds, SQL_BATCH_SIZE)) {\n\t\tconst result = await sql<{ id: string }>`\n\t\t\tSELECT content.id\n\t\t\tFROM ${sql.ref(tableName)} AS content\n\t\t\tWHERE content.id IN (${sql.join(contentIdBatch)})\n\t\t\t${\n\t\t\t\tcollectionId\n\t\t\t\t\t? sql`AND EXISTS (\n\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\t\t\tWHERE collection.id = ${collectionId}\n\t\t\t\t\t\t\tAND collection.slug = ${collectionSlug}\n\t\t\t\t\t)`\n\t\t\t\t\t: sql``\n\t\t\t}\n\t\t`.execute(db);\n\t\tfor (const row of result.rows) {\n\t\t\texistingContentIds.add(row.id);\n\t\t}\n\t}\n\n\treturn existingContentIds;\n}\n\nasync function deleteObservedSourceIfContentAbsent(\n\trepo: MediaUsageRepository,\n\tcollectionSlug: string,\n\tobservedSource: MediaUsageSource,\n\tcounts: RepairCounts,\n): Promise<void> {\n\tif (!observedSource.contentId) return;\n\tconst result = await repo.deleteSourceIfMatchingContentAbsent(\n\t\tobservedSource.sourceKey,\n\t\tobservedSource,\n\t\tcollectionSlug,\n\t\tobservedSource.contentId,\n\t);\n\tif (result.deleted) {\n\t\tcounts.deletedSourceCount++;\n\t\treturn;\n\t}\n\tif (result.contentPresent) return;\n\tif (result.source) {\n\t\tmarkRepairConflict(counts);\n\t}\n}\n\nasync function deleteObservedSource(\n\trepo: MediaUsageRepository,\n\tsourceKey: string,\n\tobservedSource: MediaUsageSource,\n\tcounts: RepairCounts,\n): Promise<void> {\n\tconst result = await repo.deleteSourceIfMatching(sourceKey, observedSource);\n\tif (result.deleted) {\n\t\tcounts.deletedSourceCount++;\n\t\treturn;\n\t}\n\tif (result.source) {\n\t\tmarkRepairConflict(counts);\n\t}\n}\n\ninterface FinalizeInput extends ContentMediaUsageRepairScope {\n\trunToken: string;\n\tcounts: RepairCounts;\n\tstatus: Exclude<ContentMediaUsageRepairStatus, \"stale\">;\n\tstartedAt: string;\n\tcompletedAt: string;\n\texecution?: ContentMediaUsageRepairExecution;\n}\n\nasync function finalizeRepairStatus(\n\tdb: Kysely<Database>,\n\trepo: MediaUsageRepository,\n\tinput: FinalizeInput,\n): Promise<ContentMediaUsageRepairCollectionResult> {\n\tlet result;\n\tif (input.execution) {\n\t\tif (input.status === \"complete\") {\n\t\t\tawait new MediaUsageWorkRepository(db).deleteWorkThroughEpoch(\n\t\t\t\tinput.execution.collectionId,\n\t\t\t\tinput.execution.startingEpoch,\n\t\t\t);\n\t\t}\n\t\tresult = await repo.finalizeIndexStatusRepairAtEpoch({\n\t\t\tadapterId: input.adapterId,\n\t\t\tscopeType: input.scopeType,\n\t\t\tscopeKey: input.scopeKey,\n\t\t\tcollectionId: input.execution.collectionId,\n\t\t\trunToken: input.runToken,\n\t\t\tstartingEpoch: input.execution.startingEpoch,\n\t\t\tstatus: input.status,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t\tindexedSourceCount: input.counts.indexedSourceCount,\n\t\t\tfailedSourceCount: input.counts.failedSourceCount,\n\t\t\tlastErrorCode: input.counts.lastErrorCode,\n\t\t});\n\t} else {\n\t\tresult = await repo.finalizeIndexStatusRepairIfRunning({\n\t\t\tadapterId: input.adapterId,\n\t\t\tscopeType: input.scopeType,\n\t\t\tscopeKey: input.scopeKey,\n\t\t\trunToken: input.runToken,\n\t\t\tstatus: input.status,\n\t\t\tschemaVersion: CONTENT_SOURCE_SCHEMA_VERSION,\n\t\t\tcompletedAt: input.completedAt,\n\t\t\tindexedSourceCount: input.counts.indexedSourceCount,\n\t\t\tfailedSourceCount: input.counts.failedSourceCount,\n\t\t\tlastErrorCode: input.counts.lastErrorCode,\n\t\t});\n\t}\n\n\treturn {\n\t\tscope: {\n\t\t\tadapterId: input.adapterId,\n\t\t\tscopeType: input.scopeType,\n\t\t\tscopeKey: input.scopeKey,\n\t\t},\n\t\tstatus: result.finalized ? input.status : \"stale\",\n\t\tindexedSourceCount: input.counts.indexedSourceCount,\n\t\tfailedSourceCount: input.counts.failedSourceCount,\n\t\tskippedSourceCount: input.counts.skippedSourceCount,\n\t\tdeletedSourceCount: input.counts.deletedSourceCount,\n\t\tlastErrorCode: result.finalized\n\t\t\t? input.counts.lastErrorCode\n\t\t\t: (result.status?.lastErrorCode ??\n\t\t\t\tCONTENT_MEDIA_USAGE_REPAIR_ERROR.CONTENT_USAGE_REPAIR_CONFLICT),\n\t\tstartedAt: input.startedAt,\n\t\tcompletedAt: result.finalized ? (result.status?.completedAt ?? input.completedAt) : null,\n\t};\n}\n\nfunction determineRepairStatus(\n\tcounts: RepairCounts,\n): Exclude<ContentMediaUsageRepairStatus, \"stale\"> {\n\tif (counts.failedSourceCount === 0 && counts.skippedSourceCount === 0) return \"complete\";\n\tconst trustedProgress = counts.indexedSourceCount + counts.deletedSourceCount;\n\tif (counts.failedSourceCount > 0 && trustedProgress === 0) {\n\t\treturn \"failed\";\n\t}\n\treturn \"partial\";\n}\n\nfunction buildContentSourceKeys(\n\tcollectionSlug: string,\n\tcontentId: string,\n\tcollectionId?: string,\n): string[] {\n\treturn MEDIA_USAGE_CONTENT_SOURCE_VARIANTS.map((sourceVariant) =>\n\t\tbuildContentMediaUsageSourceKey({ collectionId, collectionSlug, contentId, sourceVariant }),\n\t);\n}\n\nfunction buildContentSourceKeysForScan(\n\tscan: ContentMediaUsageCollectionScan,\n\tcollectionId?: string,\n): string[] {\n\treturn scan.contentIds.flatMap((contentId) =>\n\t\tbuildContentSourceKeys(scan.collectionSlug, contentId, collectionId),\n\t);\n}\n\nasync function loadContentMediaUsageCollectionRecord(\n\tdb: Kysely<Database>,\n\tcollectionSlug: string,\n): Promise<ContentMediaUsageCollectionRecord | null> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections\")\n\t\t.select([\"id\", \"slug\"])\n\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nasync function isIncrementalCaptureActive(db: Kysely<Database>): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_media_usage_activation\")\n\t\t.select(\"state\")\n\t\t.where(\"task_key\", \"=\", \"incremental_capture\")\n\t\t.executeTakeFirst();\n\treturn row?.state === \"active\";\n}\n\nfunction sameContentIds(left: readonly string[], right: readonly string[]): boolean {\n\tif (left.length !== right.length) return false;\n\tconst rightIds = new Set(right);\n\treturn left.every((id) => rightIds.has(id));\n}\n\nfunction repairedContentIds(\n\tcontentIds: readonly string[],\n\tcounts: Pick<RepairCounts, \"missingContentIds\">,\n): string[] {\n\tif (counts.missingContentIds.size === 0) return [...contentIds];\n\treturn contentIds.filter((contentId) => !counts.missingContentIds.has(contentId));\n}\n\nfunction contentMediaUsageCollectionScope(collectionSlug: string): ContentMediaUsageRepairScope {\n\treturn {\n\t\tadapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,\n\t\tscopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\t\tscopeKey: collectionSlug,\n\t};\n}\n\nfunction getContentTableName(collectionSlug: string): string {\n\tvalidateIdentifier(collectionSlug, \"collection slug\");\n\treturn `ec_${collectionSlug}`;\n}\n","import type { Kysely } from \"kysely\";\n\nimport {\n\tMediaUsageRepository,\n\ttype MediaUsageCollectionIndexStatusScope,\n\ttype MediaUsageEntryGroup,\n} from \"../../database/repositories/media-usage.js\";\nimport { MediaRepository } from \"../../database/repositories/media.js\";\nimport { InvalidCursorError } from \"../../database/repositories/types.js\";\nimport type { Database } from \"../../database/types.js\";\nimport {\n\tCONTENT_MEDIA_USAGE_ADAPTER_ID,\n\tCONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n} from \"../../media/usage/content-refresh.js\";\nimport {\n\trepairContentMediaUsageAll,\n\trepairContentMediaUsageCollection,\n\ttype ContentMediaUsageRepairAllResult,\n\ttype ContentMediaUsageRepairCollectionResult,\n} from \"../../media/usage/content-repair.js\";\nimport { CONTENT_SOURCE_SCHEMA_VERSION } from \"../../media/usage/content-snapshots.js\";\nimport { ErrorCode } from \"../errors.js\";\nimport type {\n\tMediaUsageCoverage,\n\tMediaUsageCoverageStatus,\n\tMediaUsageDetailsResponse,\n\tMediaUsageEntryDetail,\n\tMediaUsageOccurrenceDetail,\n\tMediaUsageRepairRequest,\n\tMediaUsageRepairResponse,\n\tMediaUsageSummary,\n} from \"../schemas/media-usage.js\";\nimport type { ApiResult } from \"../types.js\";\n\nexport type {\n\tMediaUsageCoverage,\n\tMediaUsageCoverageStatus,\n\tMediaUsageDetailsResponse,\n\tMediaUsageEntryDetail,\n\tMediaUsageOccurrenceDetail,\n\tMediaUsageSourceDetail,\n\tMediaUsageRepairRequest,\n\tMediaUsageRepairResponse,\n\tMediaUsageSummary,\n} from \"../schemas/media-usage.js\";\n\ntype ContentMediaUsageRepairResult =\n\t| ContentMediaUsageRepairCollectionResult\n\t| ContentMediaUsageRepairAllResult;\n\nexport function aggregateMediaUsageCoverageStatus(\n\tscopes: readonly MediaUsageCollectionIndexStatusScope[],\n): MediaUsageCoverageStatus {\n\tconst statuses = scopes.map(normalizeMediaUsageCoverageStatus);\n\tif (statuses.every((status) => status === \"complete\")) {\n\t\treturn \"complete\";\n\t}\n\tif (statuses.includes(\"unknown\")) return \"unknown\";\n\tif (statuses.includes(\"running\")) return \"running\";\n\tif (statuses.includes(\"stale\")) return \"stale\";\n\tif (statuses.includes(\"partial\")) return \"partial\";\n\tif (statuses.every((status) => status === \"never\")) return \"never\";\n\tif (statuses.every((status) => status === \"failed\")) return \"failed\";\n\treturn \"partial\";\n}\n\nexport async function handleMediaUsageSummaries(\n\tdb: Kysely<Database>,\n\tmediaIds: readonly string[],\n\toptions: { includeCount: boolean },\n): Promise<ApiResult<Record<string, MediaUsageSummary>>> {\n\tif (mediaIds.length === 0) return { success: true, data: {} };\n\n\ttry {\n\t\tconst repository = new MediaUsageRepository(db);\n\t\tconst coverage = await loadMediaUsageCoverage(repository);\n\t\tconst counts =\n\t\t\toptions.includeCount && coverage.status === \"complete\"\n\t\t\t\t? await repository.findActiveEntryCountsByMediaIds(mediaIds)\n\t\t\t\t: null;\n\t\tconst summaries: Record<string, MediaUsageSummary> = {};\n\n\t\tfor (const mediaId of new Set(mediaIds)) {\n\t\t\tsummaries[mediaId] = {\n\t\t\t\tcount: counts ? (counts.get(mediaId) ?? 0) : null,\n\t\t\t\tcoverage,\n\t\t\t};\n\t\t}\n\n\t\treturn { success: true, data: summaries };\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage] summary read failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_READ_ERROR,\n\t\t\t\tmessage: \"Failed to read media usage\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport async function handleMediaUsageDetails(\n\tdb: Kysely<Database>,\n\tmediaId: string,\n\toptions: { cursor?: string; limit?: number },\n): Promise<ApiResult<MediaUsageDetailsResponse>> {\n\ttry {\n\t\tconst media = await new MediaRepository(db).findById(mediaId);\n\t\tif (!media) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.NOT_FOUND,\n\t\t\t\t\tmessage: `Media item not found: ${mediaId}`,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst repository = new MediaUsageRepository(db);\n\t\tconst coverage = await loadMediaUsageCoverage(repository);\n\t\tconst page = await repository.findCurrentEntryUsagePageByMediaId(mediaId, options);\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\titems: page.items.map(toMediaUsageEntryDetail),\n\t\t\t\t...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),\n\t\t\t\tcoverage,\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof InvalidCursorError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: ErrorCode.INVALID_CURSOR, message: error.message },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[media-usage] detail read failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_READ_ERROR,\n\t\t\t\tmessage: \"Failed to read media usage\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport async function handleMediaUsageRepair(\n\tdb: Kysely<Database>,\n\tinput: MediaUsageRepairRequest,\n): Promise<ApiResult<MediaUsageRepairResponse>> {\n\ttry {\n\t\tlet result: ContentMediaUsageRepairResult;\n\t\tif (input.scope === \"collection\") {\n\t\t\tresult = await repairContentMediaUsageCollection(db, { collectionSlug: input.collection });\n\t\t} else if (input.scope === \"all\") {\n\t\t\tresult = await repairContentMediaUsageAll(db);\n\t\t} else {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.VALIDATION_ERROR,\n\t\t\t\t\tmessage: \"Invalid media usage repair request\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn { success: true, data: toMediaUsageRepairResponse(result) };\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage] repair failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_REPAIR_ERROR,\n\t\t\t\tmessage: \"Failed to repair media usage\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport function toMediaUsageRepairResponse(\n\tresult: ContentMediaUsageRepairResult,\n): MediaUsageRepairResponse {\n\tconst collections = \"collections\" in result ? result.collections : [result];\n\n\treturn {\n\t\tstatus: result.status,\n\t\tindexedSourceCount: result.indexedSourceCount,\n\t\tfailedSourceCount: result.failedSourceCount,\n\t\tskippedSourceCount: result.skippedSourceCount,\n\t\tdeletedSourceCount: result.deletedSourceCount,\n\t\tcollections: collections.map(toMediaUsageRepairCollectionSummary),\n\t};\n}\n\nfunction toMediaUsageRepairCollectionSummary(result: ContentMediaUsageRepairCollectionResult) {\n\treturn {\n\t\tcollection: result.scope.scopeKey,\n\t\tstatus: result.status,\n\t\tindexedSourceCount: result.indexedSourceCount,\n\t\tfailedSourceCount: result.failedSourceCount,\n\t\tskippedSourceCount: result.skippedSourceCount,\n\t\tdeletedSourceCount: result.deletedSourceCount,\n\t\tlastErrorCode: result.lastErrorCode,\n\t\tstartedAt: result.startedAt,\n\t\tcompletedAt: result.completedAt,\n\t};\n}\n\nfunction normalizeMediaUsageCoverageStatus(\n\tscope: MediaUsageCollectionIndexStatusScope,\n): MediaUsageCoverageStatus {\n\tif (scope.status === null) return \"never\";\n\tif (scope.status === \"complete\") {\n\t\tif (scope.reconciliationRequired) return \"stale\";\n\t\treturn scope.schemaVersion === CONTENT_SOURCE_SCHEMA_VERSION ? \"complete\" : \"stale\";\n\t}\n\tif (\n\t\tscope.status === \"never\" ||\n\t\tscope.status === \"running\" ||\n\t\tscope.status === \"partial\" ||\n\t\tscope.status === \"failed\" ||\n\t\tscope.status === \"stale\"\n\t) {\n\t\treturn scope.status;\n\t}\n\treturn \"unknown\";\n}\n\nasync function loadMediaUsageCoverage(\n\trepository: MediaUsageRepository,\n): Promise<MediaUsageCoverage> {\n\tconst scopes = await repository.findCollectionIndexStatusScopes({\n\t\tadapterId: CONTENT_MEDIA_USAGE_ADAPTER_ID,\n\t\tscopeType: CONTENT_MEDIA_USAGE_COLLECTION_SCOPE,\n\t});\n\treturn {\n\t\tscope: \"all_content_collections\",\n\t\tstatus: aggregateMediaUsageCoverageStatus(scopes),\n\t};\n}\n\nfunction toMediaUsageEntryDetail(group: MediaUsageEntryGroup): MediaUsageEntryDetail {\n\tconst preferred =\n\t\tgroup.sources.find(({ source }) => source.sourceVariant === \"draft_overlay\") ??\n\t\tgroup.sources.find(({ source }) => source.sourceVariant === \"columns\");\n\tif (!preferred) {\n\t\tthrow new Error(\"Media usage entry has no supported source\");\n\t}\n\n\treturn {\n\t\tcollection: group.collectionSlug,\n\t\tcontentId: group.contentId,\n\t\ttitle: preferred.source.contentTitle,\n\t\tslug: preferred.source.contentSlug,\n\t\tlocale: preferred.source.locale,\n\t\tstatus: preferred.source.contentStatus,\n\t\tscheduledAt: preferred.source.contentScheduledAt,\n\t\tdeletedAt: group.contentDeletedAt,\n\t\tsources: group.sources.flatMap(({ source, occurrences }) => {\n\t\t\tif (source.sourceVariant !== \"columns\" && source.sourceVariant !== \"draft_overlay\") {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tvariant: source.sourceVariant,\n\t\t\t\t\toccurrences: occurrences.map((occurrence) => ({\n\t\t\t\t\t\tfieldSlug: occurrence.fieldSlug,\n\t\t\t\t\t\tfieldPath: occurrence.fieldPath,\n\t\t\t\t\t\toccurrenceIndex: occurrence.occurrenceIndex,\n\t\t\t\t\t\treferenceType: normalizeMediaUsageReferenceType(occurrence.referenceType),\n\t\t\t\t\t})),\n\t\t\t\t},\n\t\t\t];\n\t\t}),\n\t};\n}\n\nfunction normalizeMediaUsageReferenceType(\n\treferenceType: string,\n): MediaUsageOccurrenceDetail[\"referenceType\"] {\n\tif (\n\t\treferenceType === \"image_field\" ||\n\t\treferenceType === \"file_field\" ||\n\t\treferenceType === \"portable_text_image\"\n\t) {\n\t\treturn referenceType;\n\t}\n\treturn \"unknown\";\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as InvalidCursorError } from "./types-CQPWhPxE.mjs";
|
|
2
2
|
import { t as MediaUsageWorkRepository } from "./media-usage-work-DrCbqBcl.mjs";
|
|
3
3
|
import { t as ErrorCode } from "./errors-CLUPm7aD.mjs";
|
|
4
|
-
import { t as MediaUsageCollectionDeletionRepository } from "./collection-deletion-
|
|
4
|
+
import { t as MediaUsageCollectionDeletionRepository } from "./collection-deletion-DQPvMD8R.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/api/handlers/media-usage-work.ts
|
|
7
7
|
async function handleMediaUsageWorkList(db, query) {
|
|
@@ -149,4 +149,4 @@ async function handleMediaUsageWorkRetry(db, input) {
|
|
|
149
149
|
|
|
150
150
|
//#endregion
|
|
151
151
|
export { handleMediaUsageWorkRetry as i, handleMediaUsageCollectionDeletionRetry as n, handleMediaUsageWorkList as r, handleMediaUsageCollectionDeletionList as t };
|
|
152
|
-
//# sourceMappingURL=media-usage-work-
|
|
152
|
+
//# sourceMappingURL=media-usage-work-CLo_6tct.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-usage-work-
|
|
1
|
+
{"version":3,"file":"media-usage-work-CLo_6tct.mjs","names":[],"sources":["../src/api/handlers/media-usage-work.ts"],"sourcesContent":["import type { Kysely } from \"kysely\";\n\nimport { MediaUsageWorkRepository } from \"../../database/repositories/media-usage-work.js\";\nimport { InvalidCursorError } from \"../../database/repositories/types.js\";\nimport type { Database } from \"../../database/types.js\";\nimport { MediaUsageCollectionDeletionRepository } from \"../../media/usage/collection-deletion.js\";\nimport { ErrorCode } from \"../errors.js\";\nimport type {\n\tMediaUsageWorkListQuery,\n\tMediaUsageWorkListResponse,\n\tMediaUsageWorkRetryRequest,\n\tMediaUsageWorkRetryResponse,\n\tMediaUsageCollectionDeletionListQuery,\n\tMediaUsageCollectionDeletionListResponse,\n\tMediaUsageCollectionDeletionRetryRequest,\n\tMediaUsageCollectionDeletionRetryResponse,\n} from \"../schemas/media-usage.js\";\nimport type { ApiResult } from \"../types.js\";\n\nexport type {\n\tMediaUsageWorkItem,\n\tMediaUsageWorkListQuery,\n\tMediaUsageWorkListResponse,\n\tMediaUsageWorkRetryRequest,\n\tMediaUsageWorkRetryResponse,\n} from \"../schemas/media-usage.js\";\n\nexport async function handleMediaUsageWorkList(\n\tdb: Kysely<Database>,\n\tquery: MediaUsageWorkListQuery,\n): Promise<ApiResult<MediaUsageWorkListResponse>> {\n\ttry {\n\t\tconst page = await new MediaUsageWorkRepository(db).findOperatorPage({\n\t\t\tcollectionSlug: query.collection,\n\t\t\tstate: query.state,\n\t\t\tcursor: query.cursor,\n\t\t\tlimit: query.limit,\n\t\t});\n\t\tif (!page) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.COLLECTION_NOT_FOUND,\n\t\t\t\t\tmessage: \"Collection not found\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\treturn { success: true, data: page };\n\t} catch (error) {\n\t\tif (error instanceof InvalidCursorError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.INVALID_CURSOR,\n\t\t\t\t\tmessage: \"Invalid media usage work cursor\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[media-usage-work] list failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_WORK_LIST_ERROR,\n\t\t\t\tmessage: \"Failed to list media usage work\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport async function handleMediaUsageCollectionDeletionList(\n\tdb: Kysely<Database>,\n\tquery: MediaUsageCollectionDeletionListQuery,\n): Promise<ApiResult<MediaUsageCollectionDeletionListResponse>> {\n\ttry {\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tdata: await new MediaUsageCollectionDeletionRepository(db).findOperatorPage(query),\n\t\t};\n\t} catch (error) {\n\t\tif (error instanceof InvalidCursorError) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: { code: ErrorCode.INVALID_CURSOR, message: \"Invalid cursor\" },\n\t\t\t};\n\t\t}\n\t\tconsole.error(\"[media-usage:collection-deletion] list failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_COLLECTION_DELETION_LIST_ERROR,\n\t\t\t\tmessage: \"Failed to list collection deletions\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport async function handleMediaUsageCollectionDeletionRetry(\n\tdb: Kysely<Database>,\n\tinput: MediaUsageCollectionDeletionRetryRequest,\n): Promise<ApiResult<MediaUsageCollectionDeletionRetryResponse>> {\n\ttry {\n\t\tconst result = await new MediaUsageCollectionDeletionRepository(db).retryOperatorDeletion(\n\t\t\tinput,\n\t\t);\n\t\tif (result.outcome === \"pending\") {\n\t\t\treturn { success: true, data: { changed: result.changed, item: result.item } };\n\t\t}\n\t\tif (result.outcome === \"lease_active\") {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\terror: {\n\t\t\t\t\tcode: ErrorCode.WORK_LEASE_ACTIVE,\n\t\t\t\t\tmessage: \"Collection deletion is currently leased\",\n\t\t\t\t\tdetails: { leaseExpiresAt: result.leaseExpiresAt },\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: result.outcome === \"not_found\" ? ErrorCode.NOT_FOUND : ErrorCode.WORK_CHANGED,\n\t\t\t\tmessage:\n\t\t\t\t\tresult.outcome === \"not_found\"\n\t\t\t\t\t\t? \"Collection deletion not found\"\n\t\t\t\t\t\t: \"Collection deletion changed\",\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage:collection-deletion] retry failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_COLLECTION_DELETION_RETRY_ERROR,\n\t\t\t\tmessage: \"Failed to retry collection deletion\",\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport async function handleMediaUsageWorkRetry(\n\tdb: Kysely<Database>,\n\tinput: MediaUsageWorkRetryRequest,\n): Promise<ApiResult<MediaUsageWorkRetryResponse>> {\n\ttry {\n\t\tconst result = await new MediaUsageWorkRepository(db).retryOperatorWork(input);\n\t\tswitch (result.outcome) {\n\t\t\tcase \"pending\":\n\t\t\t\treturn {\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\tdata: { changed: result.changed, item: result.work },\n\t\t\t\t};\n\t\t\tcase \"lease_active\":\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: ErrorCode.WORK_LEASE_ACTIVE,\n\t\t\t\t\t\tmessage: \"Media usage work is currently leased\",\n\t\t\t\t\t\tdetails: { leaseExpiresAt: result.leaseExpiresAt },\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\tcase \"collection_not_found\":\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: ErrorCode.COLLECTION_NOT_FOUND,\n\t\t\t\t\t\tmessage: \"Collection not found\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\tcase \"conflict\":\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: ErrorCode.WORK_CHANGED,\n\t\t\t\t\t\tmessage: \"Media usage work changed; retry the request\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\"[media-usage-work] retry failed:\", error);\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: {\n\t\t\t\tcode: ErrorCode.MEDIA_USAGE_WORK_RETRY_ERROR,\n\t\t\t\tmessage: \"Failed to retry media usage work\",\n\t\t\t},\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;AA2BA,eAAsB,yBACrB,IACA,OACiD;AACjD,KAAI;EACH,MAAM,OAAO,MAAM,IAAI,yBAAyB,GAAG,CAAC,iBAAiB;GACpE,gBAAgB,MAAM;GACtB,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,OAAO,MAAM;GACb,CAAC;AACF,MAAI,CAAC,KACJ,QAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,UAAU;IAChB,SAAS;IACT;GACD;AAEF,SAAO;GAAE,SAAS;GAAM,MAAM;GAAM;UAC5B,OAAO;AACf,MAAI,iBAAiB,mBACpB,QAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,UAAU;IAChB,SAAS;IACT;GACD;AAEF,UAAQ,MAAM,mCAAmC,MAAM;AACvD,SAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,UAAU;IAChB,SAAS;IACT;GACD;;;AAIH,eAAsB,uCACrB,IACA,OAC+D;AAC/D,KAAI;AACH,SAAO;GACN,SAAS;GACT,MAAM,MAAM,IAAI,uCAAuC,GAAG,CAAC,iBAAiB,MAAM;GAClF;UACO,OAAO;AACf,MAAI,iBAAiB,mBACpB,QAAO;GACN,SAAS;GACT,OAAO;IAAE,MAAM,UAAU;IAAgB,SAAS;IAAkB;GACpE;AAEF,UAAQ,MAAM,kDAAkD,MAAM;AACtE,SAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,UAAU;IAChB,SAAS;IACT;GACD;;;AAIH,eAAsB,wCACrB,IACA,OACgE;AAChE,KAAI;EACH,MAAM,SAAS,MAAM,IAAI,uCAAuC,GAAG,CAAC,sBACnE,MACA;AACD,MAAI,OAAO,YAAY,UACtB,QAAO;GAAE,SAAS;GAAM,MAAM;IAAE,SAAS,OAAO;IAAS,MAAM,OAAO;IAAM;GAAE;AAE/E,MAAI,OAAO,YAAY,eACtB,QAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,UAAU;IAChB,SAAS;IACT,SAAS,EAAE,gBAAgB,OAAO,gBAAgB;IAClD;GACD;AAEF,SAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,OAAO,YAAY,cAAc,UAAU,YAAY,UAAU;IACvE,SACC,OAAO,YAAY,cAChB,kCACA;IACJ;GACD;UACO,OAAO;AACf,UAAQ,MAAM,mDAAmD,MAAM;AACvE,SAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,UAAU;IAChB,SAAS;IACT;GACD;;;AAIH,eAAsB,0BACrB,IACA,OACkD;AAClD,KAAI;EACH,MAAM,SAAS,MAAM,IAAI,yBAAyB,GAAG,CAAC,kBAAkB,MAAM;AAC9E,UAAQ,OAAO,SAAf;GACC,KAAK,UACJ,QAAO;IACN,SAAS;IACT,MAAM;KAAE,SAAS,OAAO;KAAS,MAAM,OAAO;KAAM;IACpD;GACF,KAAK,eACJ,QAAO;IACN,SAAS;IACT,OAAO;KACN,MAAM,UAAU;KAChB,SAAS;KACT,SAAS,EAAE,gBAAgB,OAAO,gBAAgB;KAClD;IACD;GACF,KAAK,uBACJ,QAAO;IACN,SAAS;IACT,OAAO;KACN,MAAM,UAAU;KAChB,SAAS;KACT;IACD;GACF,KAAK,WACJ,QAAO;IACN,SAAS;IACT,OAAO;KACN,MAAM,UAAU;KAChB,SAAS;KACT;IACD;;UAEK,OAAO;AACf,UAAQ,MAAM,oCAAoC,MAAM;AACxD,SAAO;GACN,SAAS;GACT,OAAO;IACN,MAAM,UAAU;IAChB,SAAS;IACT;GACD"}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { t as validateIdentifier } from "./validate-BpwoDOgt.mjs";
|
|
2
2
|
import { n as cachedQuery, t as CacheNamespace } from "./object-cache-71tPfV4A.mjs";
|
|
3
|
-
import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-
|
|
3
|
+
import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-fiZ8G9RJ.mjs";
|
|
4
4
|
import { o as decodeCursor, s as encodeCursor } from "./types-CQPWhPxE.mjs";
|
|
5
5
|
import { t as CommentRepository } from "./comment-Dxlep6cE.mjs";
|
|
6
6
|
import { t as OptionsRepository } from "./options-CBXIHUoT.mjs";
|
|
7
|
-
import { t as PluginContextFactory } from "./context-
|
|
7
|
+
import { t as PluginContextFactory } from "./context-ZldbZcle.mjs";
|
|
8
8
|
import { r as requestCached } from "./request-cache-BSUptuJR.mjs";
|
|
9
9
|
import { i as resolveLocaleChain, r as resolveLocale } from "./resolve-DOhVEvty.mjs";
|
|
10
|
-
import { s as getDb } from "./loader-
|
|
10
|
+
import { s as getDb } from "./loader-CdG_Ci3H.mjs";
|
|
11
11
|
import { a as normalizeCapabilities } from "./types-CP1CflWf.mjs";
|
|
12
|
-
import { gt as sanitizeHref } from "./redirects-
|
|
12
|
+
import { gt as sanitizeHref } from "./redirects-DTDHWH31.mjs";
|
|
13
13
|
import { t as extractRequestMeta } from "./request-meta-DgZJh3zG.mjs";
|
|
14
14
|
import { r as setCronTasksEnabled } from "./cron-Y9aLtgrA.mjs";
|
|
15
15
|
import { t as MediaUsageActivationWriteBlockedError } from "./media-usage-write-fence-aHEgN1zB.mjs";
|
|
16
|
-
import { t as CommentReactionRepository } from "./comment-reaction-
|
|
16
|
+
import { t as CommentReactionRepository } from "./comment-reaction-D-2J84UE.mjs";
|
|
17
17
|
import { sql } from "kysely";
|
|
18
18
|
import { ulid } from "ulidx";
|
|
19
19
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -3739,4 +3739,4 @@ function resolveTaxonomyUrl(referenceGroup, taxonomyLookup) {
|
|
|
3739
3739
|
|
|
3740
3740
|
//#endregion
|
|
3741
3741
|
export { file as A, createHookPipeline as C, prosemirrorToPortableText as D, portableTextToProsemirror as E, AuditRepository as M, portableText as O, HookPipeline as S, definePlugin as T, PluginRouteRegistry as _, SandboxUnavailableError as a, toRouteCallerInfo as b, getSandboxRouteErrorDetails as c, SandboxNotAvailableError as d, createNoopSandboxRunner as f, PluginRouteError as g, createPluginManager as h, getComments as i, image as j, reference as k, getSandboxRouteErrorEnvelope as l, PluginManager as m, getMenus as n, createSandboxRouteError as o, NodeCronScheduler as p, getCommentCount as r, createSandboxRouteErrorEnvelope as s, getMenu as t, NoopSandboxRunner as u, buildRouteMeta as v, resolveExclusiveHooks as w, EmailPipeline as x, parseRouteInput as y };
|
|
3742
|
-
//# sourceMappingURL=menus-
|
|
3742
|
+
//# sourceMappingURL=menus-DSo5QeW0.mjs.map
|