emdash 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activation-K0kGBWuJ.mjs +382 -0
- package/dist/activation-K0kGBWuJ.mjs.map +1 -0
- package/dist/{adapters-Bb1vG41D.d.mts → adapters-BDQwg236.d.mts} +6 -4
- package/dist/{adapters-Bb1vG41D.d.mts.map → adapters-BDQwg236.d.mts.map} +1 -1
- package/dist/{allowed-origins-DW7sYyqg.mjs → allowed-origins-BGglilcs.mjs} +2 -2
- package/dist/{allowed-origins-DW7sYyqg.mjs.map → allowed-origins-BGglilcs.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +23 -21
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +5 -5
- package/dist/api-Dz6rgoa3.mjs +10040 -0
- package/dist/api-Dz6rgoa3.mjs.map +1 -0
- package/dist/{api-tokens-KO24G1Fd.mjs → api-tokens-BSsQACNe.mjs} +2 -2
- package/dist/{api-tokens-KO24G1Fd.mjs.map → api-tokens-BSsQACNe.mjs.map} +1 -1
- package/dist/{apply-kUDt80-G.mjs → apply-B4SIOxR1.mjs} +34 -35
- package/dist/{apply-kUDt80-G.mjs.map → apply-B4SIOxR1.mjs.map} +1 -1
- package/dist/astro/image-endpoint.mjs +2 -2
- package/dist/astro/image-endpoint.mjs.map +1 -1
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +128 -44
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +10 -7
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/media-usage-write-fence.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware/request-context.mjs +3 -3
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +11 -12
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +237 -1412
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/api-tokens/index.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +16 -16
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +16 -16
- package/dist/astro/routes/api/admin/bylines/index.mjs +16 -16
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +13 -13
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/media-usage/activation.d.mts +9 -0
- package/dist/astro/routes/api/admin/media-usage/activation.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/activation.mjs +46 -0
- package/dist/astro/routes/api/admin/media-usage/activation.mjs.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/progress.d.mts +9 -0
- package/dist/astro/routes/api/admin/media-usage/progress.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/progress.mjs +44 -0
- package/dist/astro/routes/api/admin/media-usage/progress.mjs.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +16 -11
- package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +14 -13
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -1
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +55 -49
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +55 -49
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +54 -48
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +60 -50
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +179 -139
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +61 -51
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts +8 -0
- package/dist/astro/routes/api/admin/plugins/registry/verify.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +93 -0
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs.map +1 -0
- package/dist/astro/routes/api/admin/plugins/updates.mjs +53 -47
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +53 -47
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +53 -47
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +7 -7
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +3 -3
- package/dist/astro/routes/api/admin/users/index.mjs +6 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
- package/dist/astro/routes/api/auth/invite/complete.mjs +11 -11
- package/dist/astro/routes/api/auth/invite/index.mjs +8 -8
- package/dist/astro/routes/api/auth/invite/register-options.mjs +10 -10
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +10 -10
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.d.mts.map +1 -1
- package/dist/astro/routes/api/auth/me.mjs +19 -9
- package/dist/astro/routes/api/auth/me.mjs.map +1 -1
- package/dist/astro/routes/api/auth/mode.mjs +2 -2
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +12 -12
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -11
- package/dist/astro/routes/api/auth/signup/complete.mjs +11 -11
- package/dist/astro/routes/api/auth/signup/request.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +14 -14
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +13 -13
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +14 -5
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +13 -10
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +17 -15
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +17 -15
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +14 -14
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +14 -5
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/index.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
- package/dist/astro/routes/api/dashboard.mjs +8 -8
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +17 -17
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +13 -13
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
- package/dist/astro/routes/api/import/wordpress/media.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +12 -10
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +17 -17
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +27 -27
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/manifest.mjs.map +1 -1
- package/dist/astro/routes/api/mcp.mjs +188 -65
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +11 -11
- package/dist/astro/routes/api/media/_id_/replace.d.mts +8 -0
- package/dist/astro/routes/api/media/_id_/replace.d.mts.map +1 -0
- package/dist/astro/routes/api/media/_id_/replace.mjs +91 -0
- package/dist/astro/routes/api/media/_id_/replace.mjs.map +1 -0
- package/dist/astro/routes/api/media/_id_/upload.mjs +6 -6
- package/dist/astro/routes/api/media/_id_/usage.mjs +18 -13
- package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +21 -13
- package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/media/file/_...key_.d.mts.map +1 -1
- package/dist/astro/routes/api/media/file/_...key_.mjs +3 -2
- package/dist/astro/routes/api/media/file/_...key_.mjs.map +1 -1
- package/dist/astro/routes/api/media/folders/_id_.d.mts +10 -0
- package/dist/astro/routes/api/media/folders/_id_.d.mts.map +1 -0
- package/dist/astro/routes/api/media/folders/_id_.mjs +52 -0
- package/dist/astro/routes/api/media/folders/_id_.mjs.map +1 -0
- package/dist/astro/routes/api/media/folders/index.d.mts +9 -0
- package/dist/astro/routes/api/media/folders/index.d.mts.map +1 -0
- package/dist/astro/routes/api/media/folders/index.mjs +41 -0
- package/dist/astro/routes/api/media/folders/index.mjs.map +1 -0
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +16 -14
- package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
- package/dist/astro/routes/api/media.d.mts.map +1 -1
- package/dist/astro/routes/api/media.mjs +59 -27
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +20 -13
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items.mjs +15 -11
- package/dist/astro/routes/api/menus/_name_/items.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/reorder.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +15 -11
- package/dist/astro/routes/api/menus/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/translations.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_/translations.mjs +16 -12
- package/dist/astro/routes/api/menus/_name_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/_name_.mjs +21 -14
- package/dist/astro/routes/api/menus/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/menus/index.d.mts.map +1 -1
- package/dist/astro/routes/api/menus/index.mjs +15 -11
- package/dist/astro/routes/api/menus/index.mjs.map +1 -1
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
- package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +5 -5
- package/dist/astro/routes/api/oauth/token/revoke.mjs +5 -5
- package/dist/astro/routes/api/oauth/token.mjs +5 -5
- package/dist/astro/routes/api/openapi.json.mjs +253 -40
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
- package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
- package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
- package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
- package/dist/astro/routes/api/redirects/index.mjs +11 -11
- package/dist/astro/routes/api/relations/_id_/index.mjs +17 -15
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/translations.mjs +12 -10
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/relations/index.mjs +17 -15
- package/dist/astro/routes/api/relations/index.mjs.map +1 -1
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/reorder.mjs +53 -47
- package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +10 -8
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +53 -47
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +53 -47
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +10 -10
- package/dist/astro/routes/api/search/index.mjs +9 -9
- package/dist/astro/routes/api/search/rebuild.mjs +10 -10
- package/dist/astro/routes/api/search/stats.mjs +5 -5
- package/dist/astro/routes/api/search/suggest.mjs +9 -9
- package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
- package/dist/astro/routes/api/sections/index.mjs +9 -9
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +6 -6
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +5 -5
- package/dist/astro/routes/api/settings/backups/export.mjs +6 -6
- package/dist/astro/routes/api/settings/backups/index.mjs +7 -7
- package/dist/astro/routes/api/settings/email.mjs +5 -5
- package/dist/astro/routes/api/settings/email.mjs.map +1 -1
- package/dist/astro/routes/api/settings.d.mts.map +1 -1
- package/dist/astro/routes/api/settings.mjs +20 -16
- package/dist/astro/routes/api/settings.mjs.map +1 -1
- package/dist/astro/routes/api/setup/admin-verify.mjs +12 -12
- package/dist/astro/routes/api/setup/admin.mjs +11 -11
- package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +35 -28
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +32 -30
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +7 -7
- package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +20 -16
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +21 -17
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +25 -18
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +20 -16
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +14 -14
- package/dist/astro/routes/api/taxonomies/_name_.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_.mjs +26 -19
- package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/index.mjs +20 -16
- package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
- package/dist/astro/routes/api/themes/preview.mjs +5 -5
- package/dist/astro/routes/api/typegen.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +10 -8
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +17 -13
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +15 -12
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -6
- package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.d.mts.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.mjs +15 -12
- package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +7 -7
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +10 -10
- package/dist/astro/routes/sitemap.xml.mjs +8 -8
- package/dist/astro/types.d.mts +37 -17
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-DUElxgOQ.mjs → authorize-Bkf_UHhD.mjs} +2 -2
- package/dist/{authorize-DUElxgOQ.mjs.map → authorize-Bkf_UHhD.mjs.map} +1 -1
- package/dist/{backup-DWYpJlLj.mjs → backup-rwVAl9NI.mjs} +5 -5
- package/dist/{backup-DWYpJlLj.mjs.map → backup-rwVAl9NI.mjs.map} +1 -1
- package/dist/{bundle-vn56sLos.mjs → bundle-C8S2Zl6U.mjs} +4 -4
- package/dist/{bundle-vn56sLos.mjs.map → bundle-C8S2Zl6U.mjs.map} +1 -1
- package/dist/{byline-yjvWq4ip.mjs → byline-DuhF7NWl.mjs} +27 -14
- package/dist/byline-DuhF7NWl.mjs.map +1 -0
- package/dist/{byline-fields-txaLGoME.mjs → byline-fields-BovY4KYM.mjs} +2 -2
- package/dist/{byline-fields-txaLGoME.mjs.map → byline-fields-BovY4KYM.mjs.map} +1 -1
- package/dist/{byline-registry-DDun8sxh.mjs → byline-registry-Bpu9-MVs.mjs} +2 -2
- package/dist/{byline-registry-DDun8sxh.mjs.map → byline-registry-Bpu9-MVs.mjs.map} +1 -1
- package/dist/{bylines-DjXS5p0P.mjs → bylines-CJ5nd6Lw.mjs} +7 -7
- package/dist/{bylines-DjXS5p0P.mjs.map → bylines-CJ5nd6Lw.mjs.map} +1 -1
- package/dist/{bylines-Bu7pchQ4.mjs → bylines-RwBAeOF1.mjs} +3 -3
- package/dist/{bylines-Bu7pchQ4.mjs.map → bylines-RwBAeOF1.mjs.map} +1 -1
- package/dist/{cache-C46-3Irl.mjs → cache-BF5Kvn59.mjs} +4 -4
- package/dist/{cache-C46-3Irl.mjs.map → cache-BF5Kvn59.mjs.map} +1 -1
- package/dist/capture-triggers-CNW1q3HK.mjs +362 -0
- package/dist/capture-triggers-CNW1q3HK.mjs.map +1 -0
- package/dist/{challenge-store-SIL5DsF2.mjs → challenge-store-DQN_Yrck.mjs} +5 -3
- package/dist/challenge-store-DQN_Yrck.mjs.map +1 -0
- package/dist/chrome-tags-D8fiZG94.mjs +24 -0
- package/dist/chrome-tags-D8fiZG94.mjs.map +1 -0
- package/dist/{chunks-fiZ8G9RJ.mjs → chunks-C4szrD8o.mjs} +2 -2
- package/dist/{chunks-fiZ8G9RJ.mjs.map → chunks-C4szrD8o.mjs.map} +1 -1
- package/dist/cli/index.mjs +37 -53
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +74 -4
- package/dist/client/index.d.mts.map +1 -1
- package/dist/client/index.mjs +54 -2
- package/dist/client/index.mjs.map +1 -1
- package/dist/{collection-deletion-DQPvMD8R.mjs → collection-deletion-Bj07NHbj.mjs} +13 -356
- package/dist/collection-deletion-Bj07NHbj.mjs.map +1 -0
- package/dist/{comment-reaction-D-2J84UE.mjs → comment-reaction-BhrvSHml.mjs} +2 -2
- package/dist/{comment-reaction-D-2J84UE.mjs.map → comment-reaction-BhrvSHml.mjs.map} +1 -1
- package/dist/{comment-Dxlep6cE.mjs → comment-vxOzWbZN.mjs} +2 -2
- package/dist/{comment-Dxlep6cE.mjs.map → comment-vxOzWbZN.mjs.map} +1 -1
- package/dist/{comments-CTfZtLmM.mjs → comments-Ct4ensnf.mjs} +2 -2
- package/dist/{comments-CTfZtLmM.mjs.map → comments-Ct4ensnf.mjs.map} +1 -1
- package/dist/{components-CQy3lh9x.mjs → components-DJbo1z0S.mjs} +1 -1
- package/dist/{components-CQy3lh9x.mjs.map → components-DJbo1z0S.mjs.map} +1 -1
- package/dist/{config-COQzTe5Q.d.mts → config-B9eZ340f.d.mts} +1 -1
- package/dist/{config-COQzTe5Q.d.mts.map → config-B9eZ340f.d.mts.map} +1 -1
- package/dist/config-it29yDLA.mjs +1163 -0
- package/dist/config-it29yDLA.mjs.map +1 -0
- package/dist/{content-ZHCc1ngN.mjs → content-DjvM0T92.mjs} +86 -45
- package/dist/content-DjvM0T92.mjs.map +1 -0
- package/dist/{content-list-query-wJ2WN8Vp.d.mts → content-list-query-Btqd4uV2.d.mts} +1 -1
- package/dist/{content-list-query-wJ2WN8Vp.d.mts.map → content-list-query-Btqd4uV2.d.mts.map} +1 -1
- package/dist/{content-refresh-B_8SJzuU.mjs → content-refresh-Bwp-8yGy.mjs} +1139 -111
- package/dist/content-refresh-Bwp-8yGy.mjs.map +1 -0
- package/dist/{media-usage-BZCydfvu.mjs → content-repair-Czdp7jz5.mjs} +5 -196
- package/dist/content-repair-Czdp7jz5.mjs.map +1 -0
- package/dist/{context-ZldbZcle.mjs → context-CX6gxOLW.mjs} +55 -20
- package/dist/context-CX6gxOLW.mjs.map +1 -0
- package/dist/{cron-Y9aLtgrA.mjs → cron-C6o7VppJ.mjs} +1 -1
- package/dist/{cron-Y9aLtgrA.mjs.map → cron-C6o7VppJ.mjs.map} +1 -1
- package/dist/{dashboard-DvHbk_cv.mjs → dashboard-C2kTeeRI.mjs} +5 -5
- package/dist/{dashboard-DvHbk_cv.mjs.map → dashboard-C2kTeeRI.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +3 -3
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql-migrations.d.mts +1 -1
- package/dist/db/libsql-migrations.mjs +4 -4
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres-migrations.d.mts +1 -1
- package/dist/db/postgres-migrations.mjs +4 -4
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/postgres.d.mts.map +1 -1
- package/dist/db/postgres.mjs +19 -3
- package/dist/db/postgres.mjs.map +1 -1
- package/dist/db/sqlite-migrations.d.mts +1 -1
- package/dist/db/sqlite-migrations.mjs +4 -4
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.mjs +3 -3
- package/dist/db/sqlite.mjs.map +1 -1
- package/dist/{default-DdDl3fxv.mjs → default-Clra32mw.mjs} +1 -1
- package/dist/{default-DdDl3fxv.mjs.map → default-Clra32mw.mjs.map} +1 -1
- package/dist/{deferred-tasks-SDWxrgzJ.d.mts → deferred-tasks-CvDETTJO.d.mts} +1 -1
- package/dist/{deferred-tasks-SDWxrgzJ.d.mts.map → deferred-tasks-CvDETTJO.d.mts.map} +1 -1
- package/dist/dev-typegen-DzOPfsNj.mjs +29 -0
- package/dist/dev-typegen-DzOPfsNj.mjs.map +1 -0
- package/dist/{device-flow-C0GdD_z8.mjs → device-flow-D2oCWvLJ.mjs} +5 -5
- package/dist/{device-flow-C0GdD_z8.mjs.map → device-flow-D2oCWvLJ.mjs.map} +1 -1
- package/dist/{direct-executor-CDXeJCSs.mjs → direct-executor-Bfa5_ToE.mjs} +3 -3
- package/dist/{direct-executor-CDXeJCSs.mjs.map → direct-executor-Bfa5_ToE.mjs.map} +1 -1
- package/dist/{dist-D_6CKR8z.mjs → dist-89xCrGqi.mjs} +1 -1
- package/dist/{dist-D_6CKR8z.mjs.map → dist-89xCrGqi.mjs.map} +1 -1
- package/dist/{email-console-BHNjIwiB.mjs → email-console-Dzb6uGYa.mjs} +1 -1
- package/dist/{email-console-BHNjIwiB.mjs.map → email-console-Dzb6uGYa.mjs.map} +1 -1
- package/dist/{enrich-_ySMEvEE.mjs → enrich-BX0IDp4z.mjs} +3 -3
- package/dist/{enrich-_ySMEvEE.mjs.map → enrich-BX0IDp4z.mjs.map} +1 -1
- package/dist/{error-DupyJZE2.mjs → error-DFrQjppn.mjs} +2 -2
- package/dist/{error-DupyJZE2.mjs.map → error-DFrQjppn.mjs.map} +1 -1
- package/dist/{errors-CLUPm7aD.mjs → errors-DsVoDUjJ.mjs} +24 -2
- package/dist/errors-DsVoDUjJ.mjs.map +1 -0
- package/dist/{escape-DUY2r2Zs.mjs → escape-HmHTC1Is.mjs} +1 -1
- package/dist/{escape-DUY2r2Zs.mjs.map → escape-HmHTC1Is.mjs.map} +1 -1
- package/dist/{field-defs-cache-BzwBlJrR.mjs → field-defs-cache-BM4SIRqH.mjs} +5 -5
- package/dist/{field-defs-cache-BzwBlJrR.mjs.map → field-defs-cache-BM4SIRqH.mjs.map} +1 -1
- package/dist/focal-point-BeWFo6Xx.mjs +23 -0
- package/dist/focal-point-BeWFo6Xx.mjs.map +1 -0
- package/dist/{fts-manager-DyZEohZC.mjs → fts-manager-Do1BlW2r.mjs} +3 -3
- package/dist/{fts-manager-DyZEohZC.mjs.map → fts-manager-Do1BlW2r.mjs.map} +1 -1
- package/dist/{hash-Cg0TXJPV.mjs → hash-3JJPlHF2.mjs} +1 -1
- package/dist/{hash-Cg0TXJPV.mjs.map → hash-3JJPlHF2.mjs.map} +1 -1
- package/dist/hook-result-DCh2WUpe.d.mts +31 -0
- package/dist/hook-result-DCh2WUpe.d.mts.map +1 -0
- package/dist/{hreflang-DwT2E0Sa.d.mts → hreflang-B6hbAuuO.d.mts} +2 -2
- package/dist/{hreflang-DwT2E0Sa.d.mts.map → hreflang-B6hbAuuO.d.mts.map} +1 -1
- package/dist/{hreflang-BBe83kKa.mjs → hreflang-DQbPZliA.mjs} +7 -7
- package/dist/{hreflang-BBe83kKa.mjs.map → hreflang-DQbPZliA.mjs.map} +1 -1
- package/dist/{identity-Ys6Dlc9C.mjs → identity-DksvEazf.mjs} +3 -3
- package/dist/{identity-Ys6Dlc9C.mjs.map → identity-DksvEazf.mjs.map} +1 -1
- package/dist/{import-DPkLmeXO.mjs → import-CJpaBBBT.mjs} +6 -6
- package/dist/{import-DPkLmeXO.mjs.map → import-CJpaBBBT.mjs.map} +1 -1
- package/dist/{index-BVBZj4-7.d.mts → index-BJNMCWmu.d.mts} +267 -82
- package/dist/index-BJNMCWmu.d.mts.map +1 -0
- package/dist/{index-C9KuQyO-.d.mts → index-Du8K7bWa.d.mts} +3 -3
- package/dist/{index-C9KuQyO-.d.mts.map → index-Du8K7bWa.d.mts.map} +1 -1
- package/dist/index.d.mts +22 -21
- package/dist/index.mjs +79 -73
- package/dist/{init-lock-6b309ZrF.mjs → init-lock-DJkX6Hto.mjs} +1 -1
- package/dist/{init-lock-6b309ZrF.mjs.map → init-lock-DJkX6Hto.mjs.map} +1 -1
- package/dist/{load-YJ2xl3ra.mjs → load-CawHjTOH.mjs} +2 -2
- package/dist/{load-YJ2xl3ra.mjs.map → load-CawHjTOH.mjs.map} +1 -1
- package/dist/{loader-CdG_Ci3H.mjs → loader-Z8vl7zTq.mjs} +48 -13
- package/dist/loader-Z8vl7zTq.mjs.map +1 -0
- package/dist/{manifest-builder-B252yQUf.mjs → manifest-builder-COtzuca2.mjs} +2 -2
- package/dist/{manifest-builder-B252yQUf.mjs.map → manifest-builder-COtzuca2.mjs.map} +1 -1
- package/dist/{manifest-schema-BXbUAHod.mjs → manifest-schema-BNWwR8Vd.mjs} +5 -5
- package/dist/manifest-schema-BNWwR8Vd.mjs.map +1 -0
- package/dist/media/image-endpoint.d.mts +4 -2
- package/dist/media/image-endpoint.d.mts.map +1 -1
- package/dist/media/image-endpoint.mjs +6 -4
- package/dist/media/image-endpoint.mjs.map +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +13 -3
- package/dist/media/index.mjs.map +1 -1
- package/dist/media/local-runtime.d.mts +14 -12
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +12 -6
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-CzhAn2--.mjs → media-BBm8Wg3f.mjs} +69 -12
- package/dist/media-BBm8Wg3f.mjs.map +1 -0
- package/dist/{media-BWWWb8kg.mjs → media-BHjus86Y.mjs} +133 -9
- package/dist/media-BHjus86Y.mjs.map +1 -0
- package/dist/{media-allowlist-D6x-Us02.mjs → media-allowlist-CoTBWoDp.mjs} +3 -2
- package/dist/media-allowlist-CoTBWoDp.mjs.map +1 -0
- package/dist/media-folders-BGkATs8n.mjs +210 -0
- package/dist/media-folders-BGkATs8n.mjs.map +1 -0
- package/dist/{media-upload-CHBVn6m-.mjs → media-upload-BCidR-EY.mjs} +9 -9
- package/dist/{media-upload-CHBVn6m-.mjs.map → media-upload-BCidR-EY.mjs.map} +1 -1
- package/dist/{media-url-BvoX88FA.mjs → media-url-DHoPr0hq.mjs} +1 -1
- package/dist/{media-url-BvoX88FA.mjs.map → media-url-DHoPr0hq.mjs.map} +1 -1
- package/dist/media-usage-CdY5jhSR.mjs +1833 -0
- package/dist/media-usage-CdY5jhSR.mjs.map +1 -0
- package/dist/media-usage-activation-BgeMtK5C.mjs +78 -0
- package/dist/media-usage-activation-BgeMtK5C.mjs.map +1 -0
- package/dist/{media-usage-work-CLo_6tct.mjs → media-usage-work-CnGCdHxx.mjs} +4 -4
- package/dist/{media-usage-work-CLo_6tct.mjs.map → media-usage-work-CnGCdHxx.mjs.map} +1 -1
- package/dist/{media-usage-work-DrCbqBcl.mjs → media-usage-work-D6D9sMCA.mjs} +221 -59
- package/dist/media-usage-work-D6D9sMCA.mjs.map +1 -0
- package/dist/{media-usage-write-fence-aHEgN1zB.mjs → media-usage-write-fence-hjeD4u7s.mjs} +2 -2
- package/dist/{media-usage-write-fence-aHEgN1zB.mjs.map → media-usage-write-fence-hjeD4u7s.mjs.map} +1 -1
- package/dist/{menus-DSo5QeW0.mjs → menus-6qSwTNxG.mjs} +375 -107
- package/dist/menus-6qSwTNxG.mjs.map +1 -0
- package/dist/{menus-TCNSd2vp.mjs → menus-uvPLXMZf.mjs} +4 -4
- package/dist/{menus-TCNSd2vp.mjs.map → menus-uvPLXMZf.mjs.map} +1 -1
- package/dist/migrations/index.d.mts +1 -1
- package/dist/migrations/index.mjs +3 -3
- package/dist/{mime-DfIVjNkr.mjs → mime-CUIG6x84.mjs} +1 -1
- package/dist/mime-CUIG6x84.mjs.map +1 -0
- package/dist/{mode-BDoH_WcF.mjs → mode-DN_WneRx.mjs} +1 -1
- package/dist/{mode-BDoH_WcF.mjs.map → mode-DN_WneRx.mjs.map} +1 -1
- package/dist/node-sqlite-compat-BuJXIeMz.mjs +46 -0
- package/dist/node-sqlite-compat-BuJXIeMz.mjs.map +1 -0
- package/dist/{normalize-Bc-s_r-a.mjs → normalize-C2xNjTSG.mjs} +36 -5
- package/dist/normalize-C2xNjTSG.mjs.map +1 -0
- package/dist/{oauth-authorization-iOikmrJd.mjs → oauth-authorization-Dj6Kwc21.mjs} +5 -5
- package/dist/{oauth-authorization-iOikmrJd.mjs.map → oauth-authorization-Dj6Kwc21.mjs.map} +1 -1
- package/dist/{oauth-clients-CvI0nHMJ.mjs → oauth-clients-FD7I0J8m.mjs} +1 -1
- package/dist/{oauth-clients-CvI0nHMJ.mjs.map → oauth-clients-FD7I0J8m.mjs.map} +1 -1
- package/dist/{oauth-state-store-Ba8KZlwW.mjs → oauth-state-store-DkmDOmHs.mjs} +1 -1
- package/dist/{oauth-state-store-Ba8KZlwW.mjs.map → oauth-state-store-DkmDOmHs.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-C5SQwGYC.mjs → oauth-user-lookup-CvIwGpYd.mjs} +1 -1
- package/dist/{oauth-user-lookup-C5SQwGYC.mjs.map → oauth-user-lookup-CvIwGpYd.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{object-cache-71tPfV4A.mjs → object-cache-DVKnyOfy.mjs} +19 -3
- package/dist/object-cache-DVKnyOfy.mjs.map +1 -0
- package/dist/{options-CBXIHUoT.mjs → options-BHPeIbBd.mjs} +1 -1
- package/dist/{options-CBXIHUoT.mjs.map → options-BHPeIbBd.mjs.map} +1 -1
- package/dist/{options--hhz-jZ1.d.mts → options-CYb4y7hM.d.mts} +24 -11
- package/dist/options-CYb4y7hM.d.mts.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-DFbuc-Fh.mjs → parse-Ckpg-Qfp.mjs} +2 -2
- package/dist/{parse-DFbuc-Fh.mjs.map → parse-Ckpg-Qfp.mjs.map} +1 -1
- package/dist/{passkey-config-DFAmUyuE.mjs → passkey-config-O_lj3y0k.mjs} +1 -1
- package/dist/{passkey-config-DFAmUyuE.mjs.map → passkey-config-O_lj3y0k.mjs.map} +1 -1
- package/dist/{patterns-CiyXeDgr.mjs → patterns-D9iDO3j8.mjs} +1 -1
- package/dist/{patterns-CiyXeDgr.mjs.map → patterns-D9iDO3j8.mjs.map} +1 -1
- package/dist/{placeholder-Dp0K3RkD.d.mts → placeholder-4X9BrRrp.d.mts} +7 -1
- package/dist/placeholder-4X9BrRrp.d.mts.map +1 -0
- package/dist/plugin-types.d.mts +8 -3
- package/dist/plugin-types.d.mts.map +1 -1
- package/dist/plugin-utils.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{policy-TluUIxuu.mjs → policy-Dhtwntrt.mjs} +2 -2
- package/dist/{policy-TluUIxuu.mjs.map → policy-Dhtwntrt.mjs.map} +1 -1
- package/dist/{portable-text-imBW5cHK.mjs → portable-text-U85hOafW.mjs} +1 -1
- package/dist/{portable-text-imBW5cHK.mjs.map → portable-text-U85hOafW.mjs.map} +1 -1
- package/dist/{preview-PW-nOnq4.mjs → preview-B_vJLvf8.mjs} +2 -2
- package/dist/{preview-PW-nOnq4.mjs.map → preview-B_vJLvf8.mjs.map} +1 -1
- package/dist/{protocol-DjsJHTlK.d.mts → protocol-JdD2YhY9.d.mts} +2 -2
- package/dist/{protocol-DjsJHTlK.d.mts.map → protocol-JdD2YhY9.d.mts.map} +1 -1
- package/dist/{public-url-CWoFhQT8.mjs → public-url-CarhZJgf.mjs} +1 -1
- package/dist/{public-url-CWoFhQT8.mjs.map → public-url-CarhZJgf.mjs.map} +1 -1
- package/dist/{query-zTVAR1Sl.mjs → query-BZKZcG5b.mjs} +15 -15
- package/dist/{query-zTVAR1Sl.mjs.map → query-BZKZcG5b.mjs.map} +1 -1
- package/dist/{query-BWs7O5Il.mjs → query-BndaFxxp.mjs} +5 -5
- package/dist/{query-BWs7O5Il.mjs.map → query-BndaFxxp.mjs.map} +1 -1
- package/dist/{rate-limit-Bx4g9m3p.mjs → rate-limit-BZmATC6W.mjs} +2 -2
- package/dist/{rate-limit-Bx4g9m3p.mjs.map → rate-limit-BZmATC6W.mjs.map} +1 -1
- package/dist/{redirect-COsS6SWJ.mjs → redirect-CZkbozu9.mjs} +2 -2
- package/dist/{redirect-COsS6SWJ.mjs.map → redirect-CZkbozu9.mjs.map} +1 -1
- package/dist/{redirect-BgAOGHDq.mjs → redirect-CzqnLxK-.mjs} +1 -1
- package/dist/{redirect-BgAOGHDq.mjs.map → redirect-CzqnLxK-.mjs.map} +1 -1
- package/dist/{redirects-DTDHWH31.mjs → redirects-BLNOWQTn.mjs} +64 -29
- package/dist/redirects-BLNOWQTn.mjs.map +1 -0
- package/dist/{redirects-DyChwDkG.mjs → redirects-CqzNM0KM.mjs} +5 -5
- package/dist/{redirects-DyChwDkG.mjs.map → redirects-CqzNM0KM.mjs.map} +1 -1
- package/dist/{registry-Cqa2I749.mjs → registry-AZAsRsor.mjs} +30 -126
- package/dist/registry-AZAsRsor.mjs.map +1 -0
- package/dist/{relation-BnHB0T4J.mjs → relation-04DfKRTO.mjs} +2 -2
- package/dist/{relation-BnHB0T4J.mjs.map → relation-04DfKRTO.mjs.map} +1 -1
- package/dist/{relations-BBApI4e2.mjs → relations-C4htk5sb.mjs} +7 -7
- package/dist/relations-C4htk5sb.mjs.map +1 -0
- package/dist/{relations-D5sM5fW0.mjs → relations-CcFwDJdJ.mjs} +4 -4
- package/dist/{relations-D5sM5fW0.mjs.map → relations-CcFwDJdJ.mjs.map} +1 -1
- package/dist/{relations-BHo5A8Zf.d.mts → relations-DnRK4YIT.d.mts} +347 -54
- package/dist/relations-DnRK4YIT.d.mts.map +1 -0
- package/dist/{request-cache-BSUptuJR.mjs → request-cache-DH9zdCOD.mjs} +1 -1
- package/dist/{request-cache-BSUptuJR.mjs.map → request-cache-DH9zdCOD.mjs.map} +1 -1
- package/dist/request-context.d.mts +1 -1
- package/dist/{request-meta-DgZJh3zG.mjs → request-meta-DYxLlnmz.mjs} +2 -2
- package/dist/{request-meta-DgZJh3zG.mjs.map → request-meta-DYxLlnmz.mjs.map} +1 -1
- package/dist/{resolve-DOhVEvty.mjs → resolve-prZ3mP7l.mjs} +1 -1
- package/dist/{resolve-DOhVEvty.mjs.map → resolve-prZ3mP7l.mjs.map} +1 -1
- package/dist/{runner-C1T11CfC.mjs → runner-BLIDKpmA.mjs} +471 -290
- package/dist/runner-BLIDKpmA.mjs.map +1 -0
- package/dist/{runner-BYHZn3Jy.d.mts → runner-YKfIMnaF.d.mts} +3 -2
- package/dist/runner-YKfIMnaF.d.mts.map +1 -0
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-DZ5YVVZj.mjs → schema-CGnBeLiW.mjs} +3 -3
- package/dist/{schema-DZ5YVVZj.mjs.map → schema-CGnBeLiW.mjs.map} +1 -1
- package/dist/{schema-2AOsSn2v.mjs → schema-DYr-Vd-L.mjs} +36 -6
- package/dist/{schema-2AOsSn2v.mjs.map → schema-DYr-Vd-L.mjs.map} +1 -1
- package/dist/{scopes-8ClPF2bC.mjs → scopes-BhWvBavj.mjs} +3 -3
- package/dist/{scopes-8ClPF2bC.mjs.map → scopes-BhWvBavj.mjs.map} +1 -1
- package/dist/{search-u1QYdOCG.mjs → search-C-qDk4kx.mjs} +4 -4
- package/dist/{search-u1QYdOCG.mjs.map → search-C-qDk4kx.mjs.map} +1 -1
- package/dist/{secrets-BEgsAQEU.mjs → secrets-rEtHt-6z.mjs} +3 -3
- package/dist/{secrets-BEgsAQEU.mjs.map → secrets-rEtHt-6z.mjs.map} +1 -1
- package/dist/{sections-DmHviGyM.mjs → sections-B_nYvlwn.mjs} +2 -2
- package/dist/{sections-DmHviGyM.mjs.map → sections-B_nYvlwn.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +25 -23
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +17 -15
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-Dq4G-RrO.mjs → seo-926NRIgt.mjs} +3 -3
- package/dist/{seo-Dq4G-RrO.mjs.map → seo-926NRIgt.mjs.map} +1 -1
- package/dist/{seo-AFpVvTqr.mjs → seo-CVWkdv6x.mjs} +1 -1
- package/dist/{seo-AFpVvTqr.mjs.map → seo-CVWkdv6x.mjs.map} +1 -1
- package/dist/{service-BpArayPr.mjs → service-CJl3ZAYs.mjs} +3 -3
- package/dist/{service-BpArayPr.mjs.map → service-CJl3ZAYs.mjs.map} +1 -1
- package/dist/{session-user-BEorKVT-.mjs → session-user-BlMgqCHS.mjs} +1 -1
- package/dist/{session-user-BEorKVT-.mjs.map → session-user-BlMgqCHS.mjs.map} +1 -1
- package/dist/{settings-xRdoUZr0.mjs → settings--wobrkkC.mjs} +21 -8
- package/dist/settings--wobrkkC.mjs.map +1 -0
- package/dist/{settings-DE9xtzqd.mjs → settings-Cwm6o2jY.mjs} +3 -3
- package/dist/{settings-DE9xtzqd.mjs.map → settings-Cwm6o2jY.mjs.map} +1 -1
- package/dist/{setup-complete-BnT8pq_C.mjs → setup-complete-DQbClIN2.mjs} +2 -2
- package/dist/{setup-complete-BnT8pq_C.mjs.map → setup-complete-DQbClIN2.mjs.map} +1 -1
- package/dist/{setup-nonce-CBitf_oQ.mjs → setup-nonce-DZCFS2sn.mjs} +1 -1
- package/dist/{setup-nonce-CBitf_oQ.mjs.map → setup-nonce-DZCFS2sn.mjs.map} +1 -1
- package/dist/{single-flight-cache-B0eLKS6K.mjs → single-flight-cache-BPuGPdcN.mjs} +2 -2
- package/dist/{single-flight-cache-B0eLKS6K.mjs.map → single-flight-cache-BPuGPdcN.mjs.map} +1 -1
- package/dist/{site-url-DjpIXMXJ.mjs → site-url-BrxA_E9H.mjs} +3 -3
- package/dist/{site-url-DjpIXMXJ.mjs.map → site-url-BrxA_E9H.mjs.map} +1 -1
- package/dist/{snapshot-BBlGkb2q.mjs → snapshot--OCDTqdb.mjs} +1 -1
- package/dist/{snapshot-BBlGkb2q.mjs.map → snapshot--OCDTqdb.mjs.map} +1 -1
- package/dist/{ssrf-Cpvg_n3A.mjs → ssrf-5sIM5jzl.mjs} +23 -6
- package/dist/{ssrf-Cpvg_n3A.mjs.map → ssrf-5sIM5jzl.mjs.map} +1 -1
- package/dist/{state-DYwPue40.mjs → state-BpFVsJZB.mjs} +12 -4
- package/dist/state-BpFVsJZB.mjs.map +1 -0
- package/dist/{status-CWVYpbD8.mjs → status-B777-dXz.mjs} +1 -1
- package/dist/{status-CWVYpbD8.mjs.map → status-B777-dXz.mjs.map} +1 -1
- package/dist/storage/local.d.mts +2 -1
- package/dist/storage/local.d.mts.map +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/local.mjs.map +1 -1
- package/dist/storage/s3.d.mts +2 -1
- package/dist/storage/s3.d.mts.map +1 -1
- package/dist/storage/s3.mjs +3 -2
- package/dist/storage/s3.mjs.map +1 -1
- package/dist/{target-DixqqV5i.mjs → target-BmR0iTwz.mjs} +1 -1
- package/dist/{target-DixqqV5i.mjs.map → target-BmR0iTwz.mjs.map} +1 -1
- package/dist/{taxonomies-OHUIiIpI.mjs → taxonomies-DkAkMupr.mjs} +17 -14
- package/dist/taxonomies-DkAkMupr.mjs.map +1 -0
- package/dist/{taxonomies-BlrncHi9.mjs → taxonomies-Wfy_Lg12.mjs} +43 -17
- package/dist/taxonomies-Wfy_Lg12.mjs.map +1 -0
- package/dist/{taxonomy-BqCZ1TdU.mjs → taxonomy-tJepplLL.mjs} +5 -5
- package/dist/{taxonomy-BqCZ1TdU.mjs.map → taxonomy-tJepplLL.mjs.map} +1 -1
- package/dist/{tokens-C39klX8R.mjs → tokens-BCO1oruF.mjs} +1 -1
- package/dist/{tokens-C39klX8R.mjs.map → tokens-BCO1oruF.mjs.map} +1 -1
- package/dist/{transaction-NQj4VJ7Z.mjs → transaction-0KOpOyMu.mjs} +2 -1
- package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-0KOpOyMu.mjs.map} +1 -1
- package/dist/{transport-DBJwa-Si.mjs → transport-Dr3qtwXC.mjs} +4 -3
- package/dist/{transport-DBJwa-Si.mjs.map → transport-Dr3qtwXC.mjs.map} +1 -1
- package/dist/{transport-RQE36APG.d.mts → transport-xh6YKW2G.d.mts} +1 -1
- package/dist/{transport-RQE36APG.d.mts.map → transport-xh6YKW2G.d.mts.map} +1 -1
- package/dist/{trusted-proxy-CEVekpby.mjs → trusted-proxy-BR5F1Otp.mjs} +1 -1
- package/dist/{trusted-proxy-CEVekpby.mjs.map → trusted-proxy-BR5F1Otp.mjs.map} +1 -1
- package/dist/{types-BW1syrlw.mjs → types-3qNFPlci.mjs} +1 -1
- package/dist/{types-BW1syrlw.mjs.map → types-3qNFPlci.mjs.map} +1 -1
- package/dist/types-75KIqzL9.mjs.map +1 -1
- package/dist/{types-DrJrVCT1.d.mts → types-B6GrPic4.d.mts} +9 -3
- package/dist/{types-DrJrVCT1.d.mts.map → types-B6GrPic4.d.mts.map} +1 -1
- package/dist/{types-DN64_zzZ.d.mts → types-BuHW4xDE.d.mts} +11 -10
- package/dist/types-BuHW4xDE.d.mts.map +1 -0
- package/dist/{types-DWRhk9xZ.d.mts → types-C00oyCZp.d.mts} +1 -1
- package/dist/{types-DWRhk9xZ.d.mts.map → types-C00oyCZp.d.mts.map} +1 -1
- package/dist/{types-BntycPTF.d.mts → types-CDtkNSFQ.d.mts} +1 -1
- package/dist/{types-BntycPTF.d.mts.map → types-CDtkNSFQ.d.mts.map} +1 -1
- package/dist/{types-Dfk8KI5R.d.mts → types-CPeXo1wF.d.mts} +1 -1
- package/dist/{types-Dfk8KI5R.d.mts.map → types-CPeXo1wF.d.mts.map} +1 -1
- package/dist/types-CQPWhPxE.mjs.map +1 -1
- package/dist/{types-BcqdcT-r.d.mts → types-CXFkwEGC.d.mts} +3 -2
- package/dist/types-CXFkwEGC.d.mts.map +1 -0
- package/dist/{types-CtzytVXn.mjs → types-DM3vioBY.mjs} +1 -1
- package/dist/{types-CtzytVXn.mjs.map → types-DM3vioBY.mjs.map} +1 -1
- package/dist/{types-CXnOzDe1.d.mts → types-DczELMop.d.mts} +1 -1
- package/dist/{types-CXnOzDe1.d.mts.map → types-DczELMop.d.mts.map} +1 -1
- package/dist/types-DfuTR6dL.mjs +3 -0
- package/dist/{types-DQIDRKF2.d.mts → types-FV23hEoW.d.mts} +10 -1
- package/dist/types-FV23hEoW.d.mts.map +1 -0
- package/dist/{types-C2pMvO62.d.mts → types-xDMtUFnO.d.mts} +2 -1
- package/dist/types-xDMtUFnO.d.mts.map +1 -0
- package/dist/{upload-attempts-D4FS8LT6.mjs → upload-attempts-BsqoQ1fn.mjs} +1 -1
- package/dist/{upload-attempts-D4FS8LT6.mjs.map → upload-attempts-BsqoQ1fn.mjs.map} +1 -1
- package/dist/{url-pattern-BHS0V5Ng.mjs → url-pattern-D4c2czpJ.mjs} +1 -1
- package/dist/{url-pattern-BHS0V5Ng.mjs.map → url-pattern-D4c2czpJ.mjs.map} +1 -1
- package/dist/{user-DJZfUJVE.mjs → user-BTZ40aq6.mjs} +2 -2
- package/dist/{user-DJZfUJVE.mjs.map → user-BTZ40aq6.mjs.map} +1 -1
- package/dist/{utils-D4vD_Y98.mjs → utils-BKvNjjhY.mjs} +1 -1
- package/dist/{utils-D4vD_Y98.mjs.map → utils-BKvNjjhY.mjs.map} +1 -1
- package/dist/{validate-BMLBvxOC.mjs → validate-DE_LRwo9.mjs} +2 -2
- package/dist/{validate-BMLBvxOC.mjs.map → validate-DE_LRwo9.mjs.map} +1 -1
- package/dist/{validate-CTqt5nuE.d.mts → validate-DUP27OYD.d.mts} +5 -5
- package/dist/{validate-CTqt5nuE.d.mts.map → validate-DUP27OYD.d.mts.map} +1 -1
- package/dist/{validation-npvENUCB.mjs → validation-BMImFJ11.mjs} +10 -8
- package/dist/{validation-npvENUCB.mjs.map → validation-BMImFJ11.mjs.map} +1 -1
- package/dist/version-Bii3suaO.mjs +7 -0
- package/dist/{version-DB_fqCSI.mjs.map → version-Bii3suaO.mjs.map} +1 -1
- package/dist/{widgets-BD3ho7Rq.mjs → widgets-D3gJ10xU.mjs} +18 -5
- package/dist/widgets-D3gJ10xU.mjs.map +1 -0
- package/dist/{wxr-taxonomies-Ca68766D.d.mts → wxr-taxonomies-B2Og_6rU.d.mts} +1 -1
- package/dist/{wxr-taxonomies-Ca68766D.d.mts.map → wxr-taxonomies-B2Og_6rU.d.mts.map} +1 -1
- package/dist/{wxr-taxonomies-B1R3vBoD.mjs → wxr-taxonomies-Cxngsjwo.mjs} +4 -4
- package/dist/{wxr-taxonomies-B1R3vBoD.mjs.map → wxr-taxonomies-Cxngsjwo.mjs.map} +1 -1
- package/dist/{zod-generator-LC-on_Vb.mjs → zod-generator-BuYSQJB7.mjs} +32 -22
- package/dist/zod-generator-BuYSQJB7.mjs.map +1 -0
- package/package.json +20 -9
- package/src/api/errors.ts +22 -0
- package/src/api/handlers/content.ts +49 -11
- package/src/api/handlers/index.ts +18 -0
- package/src/api/handlers/manifest.ts +161 -12
- package/src/api/handlers/media-allowlist.ts +1 -0
- package/src/api/handlers/media-folders.ts +130 -0
- package/src/api/handlers/media-usage-activation.ts +102 -0
- package/src/api/handlers/media-usage.ts +108 -0
- package/src/api/handlers/media.ts +92 -2
- package/src/api/handlers/registry.ts +483 -561
- package/src/api/handlers/taxonomies.ts +16 -9
- package/src/api/openapi/document.ts +277 -7
- package/src/api/parse.ts +1 -1
- package/src/api/schemas/auth.ts +4 -4
- package/src/api/schemas/bylines.ts +3 -3
- package/src/api/schemas/comments.ts +1 -1
- package/src/api/schemas/common.ts +3 -2
- package/src/api/schemas/content.ts +78 -22
- package/src/api/schemas/media-usage.ts +77 -0
- package/src/api/schemas/media.ts +121 -0
- package/src/api/schemas/redirects.ts +6 -1
- package/src/api/schemas/schema.ts +4 -0
- package/src/api/schemas/setup.ts +1 -1
- package/src/api/schemas/taxonomies.ts +2 -2
- package/src/api/schemas/users.ts +1 -1
- package/src/api/types.ts +23 -10
- package/src/astro/dev-typegen.ts +35 -0
- package/src/astro/image-endpoint.ts +2 -2
- package/src/astro/integration/dev-typegen.ts +91 -0
- package/src/astro/integration/index.ts +9 -44
- package/src/astro/integration/routes.ts +23 -0
- package/src/astro/integration/vite-config.ts +23 -9
- package/src/astro/middleware/auth.ts +4 -0
- package/src/astro/middleware.ts +5 -7
- package/src/astro/routes/api/admin/api-tokens/index.ts +4 -1
- package/src/astro/routes/api/admin/media-usage/activation.ts +42 -0
- package/src/astro/routes/api/admin/media-usage/progress.ts +37 -0
- package/src/astro/routes/api/admin/oauth-clients/[id].ts +1 -1
- package/src/astro/routes/api/admin/oauth-clients/index.ts +1 -1
- package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/artifact.ts +247 -101
- package/src/astro/routes/api/admin/plugins/registry/install.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/verify.ts +60 -0
- package/src/astro/routes/api/auth/me.ts +14 -2
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +8 -2
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +2 -1
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +6 -2
- package/src/astro/routes/api/manifest.ts +1 -1
- package/src/astro/routes/api/media/[id]/replace.ts +114 -0
- package/src/astro/routes/api/media/[id].ts +3 -0
- package/src/astro/routes/api/media/file/[...key].ts +4 -1
- package/src/astro/routes/api/media/folders/[id].ts +55 -0
- package/src/astro/routes/api/media/folders/index.ts +37 -0
- package/src/astro/routes/api/media/upload-url.ts +8 -4
- package/src/astro/routes/api/media.ts +66 -10
- package/src/astro/routes/api/menus/[name]/items/[id].ts +7 -2
- package/src/astro/routes/api/menus/[name]/items.ts +4 -1
- package/src/astro/routes/api/menus/[name]/reorder.ts +4 -1
- package/src/astro/routes/api/menus/[name]/translations.ts +4 -1
- package/src/astro/routes/api/menus/[name].ts +7 -2
- package/src/astro/routes/api/menus/index.ts +4 -1
- package/src/astro/routes/api/settings/email.ts +1 -1
- package/src/astro/routes/api/settings.ts +4 -1
- package/src/astro/routes/api/setup/dev-bypass.ts +12 -0
- package/src/astro/routes/api/taxonomies/[name]/reorder.ts +4 -1
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug]/translations.ts +4 -1
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +7 -2
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +4 -1
- package/src/astro/routes/api/taxonomies/[name].ts +7 -2
- package/src/astro/routes/api/taxonomies/index.ts +4 -1
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +3 -1
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +5 -2
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +3 -1
- package/src/astro/routes/api/widget-areas/[name].ts +3 -1
- package/src/astro/routes/api/widget-areas/index.ts +3 -1
- package/src/astro/types.ts +34 -6
- package/src/auth/challenge-store.ts +3 -1
- package/src/aws-sdk.d.ts +1 -0
- package/src/cache/chrome-tags.ts +24 -0
- package/src/client/index.ts +140 -2
- package/src/client/transport.ts +6 -2
- package/src/components/EmDashImage.astro +156 -51
- package/src/components/EmDashMedia.astro +2 -1
- package/src/components/Gallery.astro +7 -0
- package/src/components/InlinePortableTextEditor.tsx +136 -2
- package/src/components/LiveSearch.astro +23 -0
- package/src/components/inline-code-block.tsx +180 -49
- package/src/components/widgets/Archives.astro +2 -37
- package/src/content/converters/gallery.ts +2 -0
- package/src/content/converters/index.ts +2 -0
- package/src/content/converters/mark-safety.ts +1 -0
- package/src/content/converters/portable-text-identity.ts +139 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +202 -69
- package/src/content/converters/prosemirror-to-portable-text.ts +96 -25
- package/src/content/converters/types.ts +3 -0
- package/src/database/connection.ts +2 -31
- package/src/database/index.ts +1 -1
- package/src/database/json-recordset.ts +16 -0
- package/src/database/migrations/017_authorization_codes.ts +14 -4
- package/src/database/migrations/019_i18n.ts +1 -1
- package/src/database/migrations/071_restore_content_bylines_table.ts +43 -0
- package/src/database/migrations/072_media_folders.ts +72 -0
- package/src/database/migrations/073_media_focal_point.ts +57 -0
- package/src/database/migrations/074_content_deleted_scheduled_index.ts +55 -0
- package/src/database/migrations/runner.ts +11 -3
- package/src/database/repositories/byline.ts +32 -16
- package/src/database/repositories/content.ts +119 -51
- package/src/database/repositories/media-folders.ts +115 -0
- package/src/database/repositories/media-usage-work.ts +395 -111
- package/src/database/repositories/media-usage.ts +1104 -47
- package/src/database/repositories/media.ts +156 -29
- package/src/database/repositories/plugin-storage.ts +74 -9
- package/src/database/repositories/types.ts +8 -7
- package/src/database/transaction.ts +1 -0
- package/src/database/types.ts +13 -0
- package/src/db/adapters.ts +10 -3
- package/src/db/node-sqlite-compat.ts +78 -0
- package/src/db/postgres.ts +23 -0
- package/src/db/sqlite.ts +3 -3
- package/src/emdash-runtime.ts +186 -251
- package/src/fields/boolean.ts +1 -1
- package/src/fields/datetime.ts +1 -1
- package/src/fields/file.ts +1 -1
- package/src/fields/image.ts +7 -1
- package/src/fields/integer.ts +1 -1
- package/src/fields/json.ts +1 -1
- package/src/fields/multiselect.ts +1 -1
- package/src/fields/number.ts +1 -1
- package/src/fields/portable-text.ts +1 -1
- package/src/fields/richtext.ts +1 -1
- package/src/fields/select.ts +1 -1
- package/src/fields/slug.ts +1 -1
- package/src/fields/text.ts +1 -1
- package/src/fields/textarea.ts +1 -1
- package/src/fields/types.ts +8 -4
- package/src/index.ts +30 -3
- package/src/loader.ts +63 -10
- package/src/mcp/server.ts +228 -24
- package/src/media/focal-point.ts +37 -0
- package/src/media/image-endpoint.ts +5 -2
- package/src/media/local-runtime.ts +8 -0
- package/src/media/mime.ts +1 -0
- package/src/media/normalize.ts +25 -4
- package/src/media/types.ts +10 -0
- package/src/media/usage/activation.ts +162 -35
- package/src/media/usage/capture-triggers.ts +19 -3
- package/src/media/usage/cleanup.ts +9 -8
- package/src/media/usage/collection-deletion-processor.ts +7 -3
- package/src/media/usage/collection-deletion.ts +10 -0
- package/src/media/usage/content-refresh.ts +258 -39
- package/src/media/usage/content-snapshots.ts +128 -31
- package/src/media/usage/extractor.ts +32 -13
- package/src/media/usage/maintenance-engine.ts +166 -0
- package/src/media/usage/reconciliation-processor.ts +106 -29
- package/src/media/usage/reconciliation.ts +90 -7
- package/src/media/usage/work-processor.ts +342 -101
- package/src/menus/index.ts +16 -0
- package/src/object-cache/index.ts +44 -2
- package/src/plugin-types.ts +18 -1
- package/src/plugins/hooks.ts +4 -1
- package/src/plugins/index.ts +7 -0
- package/src/plugins/manager.ts +2 -1
- package/src/plugins/manifest-schema.ts +1 -1
- package/src/plugins/routes.ts +3 -1
- package/src/plugins/sandbox/hook-result.ts +52 -0
- package/src/plugins/sandbox/index.ts +10 -0
- package/src/plugins/save-rejection.ts +20 -0
- package/src/plugins/scheduler/node.ts +1 -13
- package/src/plugins/scheduler/types.ts +0 -2
- package/src/plugins/state.ts +13 -3
- package/src/plugins/types.ts +6 -0
- package/src/registry/artifact-checksum.ts +15 -0
- package/src/registry/artifact-fetch.ts +530 -0
- package/src/registry/artifact-verification.ts +85 -0
- package/src/registry/authoritative-records.ts +180 -0
- package/src/registry/config.ts +24 -6
- package/src/registry/record-labels.ts +28 -0
- package/src/registry/types.ts +12 -15
- package/src/schema/registry.ts +26 -3
- package/src/schema/types.ts +1 -0
- package/src/schema/zod-generator.ts +28 -17
- package/src/security/ssrf.ts +26 -4
- package/src/seed/apply.ts +4 -4
- package/src/settings/index.ts +16 -0
- package/src/storage/local.ts +1 -0
- package/src/storage/s3.ts +2 -0
- package/src/storage/types.ts +2 -0
- package/src/taxonomies/index.ts +40 -20
- package/src/ui.ts +2 -0
- package/src/widgets/archives.ts +67 -0
- package/src/widgets/index.ts +16 -0
- package/dist/api-BeBglVxU.mjs +0 -4695
- package/dist/api-BeBglVxU.mjs.map +0 -1
- package/dist/byline-yjvWq4ip.mjs.map +0 -1
- package/dist/challenge-store-SIL5DsF2.mjs.map +0 -1
- package/dist/collection-deletion-DQPvMD8R.mjs.map +0 -1
- package/dist/content-ZHCc1ngN.mjs.map +0 -1
- package/dist/content-refresh-B_8SJzuU.mjs.map +0 -1
- package/dist/context-ZldbZcle.mjs.map +0 -1
- package/dist/errors-CLUPm7aD.mjs.map +0 -1
- package/dist/index-BVBZj4-7.d.mts.map +0 -1
- package/dist/loader-CdG_Ci3H.mjs.map +0 -1
- package/dist/manifest-schema-BXbUAHod.mjs.map +0 -1
- package/dist/media-BWWWb8kg.mjs.map +0 -1
- package/dist/media-CzhAn2--.mjs.map +0 -1
- package/dist/media-allowlist-D6x-Us02.mjs.map +0 -1
- package/dist/media-usage-BZCydfvu.mjs.map +0 -1
- package/dist/media-usage-work-DrCbqBcl.mjs.map +0 -1
- package/dist/menus-DSo5QeW0.mjs.map +0 -1
- package/dist/mime-DfIVjNkr.mjs.map +0 -1
- package/dist/normalize-Bc-s_r-a.mjs.map +0 -1
- package/dist/object-cache-71tPfV4A.mjs.map +0 -1
- package/dist/options--hhz-jZ1.d.mts.map +0 -1
- package/dist/placeholder-Dp0K3RkD.d.mts.map +0 -1
- package/dist/redirects-DTDHWH31.mjs.map +0 -1
- package/dist/registry-Cqa2I749.mjs.map +0 -1
- package/dist/relations-BBApI4e2.mjs.map +0 -1
- package/dist/relations-BHo5A8Zf.d.mts.map +0 -1
- package/dist/runner-BYHZn3Jy.d.mts.map +0 -1
- package/dist/runner-C1T11CfC.mjs.map +0 -1
- package/dist/settings-xRdoUZr0.mjs.map +0 -1
- package/dist/state-DYwPue40.mjs.map +0 -1
- package/dist/taxonomies-BlrncHi9.mjs.map +0 -1
- package/dist/taxonomies-OHUIiIpI.mjs.map +0 -1
- package/dist/types-BcqdcT-r.d.mts.map +0 -1
- package/dist/types-C2pMvO62.d.mts.map +0 -1
- package/dist/types-CP1CflWf.mjs +0 -3
- package/dist/types-DN64_zzZ.d.mts.map +0 -1
- package/dist/types-DQIDRKF2.d.mts.map +0 -1
- package/dist/version-DB_fqCSI.mjs +0 -7
- package/dist/widgets-BD3ho7Rq.mjs.map +0 -1
- package/dist/zod-generator-LC-on_Vb.mjs.map +0 -1
- /package/dist/{api-tokens-rIXC6LdE.mjs → api-tokens-BfYts-sq.mjs} +0 -0
- /package/dist/{ssrf-DpXgKsna.mjs → ssrf-Dg3cIT-b.mjs} +0 -0
|
@@ -9,11 +9,21 @@ import {
|
|
|
9
9
|
} from "kysely";
|
|
10
10
|
import { ulid } from "ulidx";
|
|
11
11
|
|
|
12
|
+
import {
|
|
13
|
+
MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION,
|
|
14
|
+
MediaUsageActivationVersionMismatchError,
|
|
15
|
+
} from "../../media/usage/activation.js";
|
|
12
16
|
import { isMediaUsageProjectionFingerprint } from "../../media/usage/projection-fingerprint.js";
|
|
13
17
|
import type { MediaUsageContentSourceVariant } from "../../media/usage/source-key.js";
|
|
14
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
CONTENT_SOURCE_SCHEMA_VERSION,
|
|
20
|
+
type MediaKind,
|
|
21
|
+
type MediaUsageReferenceType,
|
|
22
|
+
} from "../../media/usage/types.js";
|
|
23
|
+
import { getRequestContext } from "../../request-context.js";
|
|
15
24
|
import { chunks, SQL_BATCH_SIZE } from "../../utils/chunks.js";
|
|
16
25
|
import { isPostgres } from "../dialect-helpers.js";
|
|
26
|
+
import { jsonTextValues } from "../json-recordset.js";
|
|
17
27
|
import { withTransaction } from "../transaction.js";
|
|
18
28
|
import type {
|
|
19
29
|
Database,
|
|
@@ -33,11 +43,30 @@ type MediaUsageSourceNullableStringColumn =
|
|
|
33
43
|
| "updated_at"
|
|
34
44
|
| "last_attempted_at"
|
|
35
45
|
| "last_error_code";
|
|
46
|
+
type BatchSourceExpectedColumn =
|
|
47
|
+
| "source.collection_id"
|
|
48
|
+
| "source.source_fingerprint"
|
|
49
|
+
| "source.source_updated_at"
|
|
50
|
+
| "source.source_version"
|
|
51
|
+
| "source.identity_version"
|
|
52
|
+
| "source.revision_id"
|
|
53
|
+
| "source.last_attempted_at"
|
|
54
|
+
| "source.last_error_code";
|
|
55
|
+
type BatchInputExpectedColumn =
|
|
56
|
+
| "input.expected_collection_id"
|
|
57
|
+
| "input.expected_source_fingerprint"
|
|
58
|
+
| "input.expected_source_updated_at"
|
|
59
|
+
| "input.expected_source_version"
|
|
60
|
+
| "input.expected_identity_version"
|
|
61
|
+
| "input.expected_revision_id"
|
|
62
|
+
| "input.expected_last_attempted_at"
|
|
63
|
+
| "input.expected_last_error_code";
|
|
36
64
|
const OCCURRENCE_BIND_COLUMNS = 13;
|
|
65
|
+
const D1_MAX_BOUND_PARAMETERS = 100;
|
|
37
66
|
export const MEDIA_USAGE_GENERATION_WRITE_LEASE_MS = 60 * 60 * 1000;
|
|
38
67
|
const OCCURRENCE_INSERT_BATCH_SIZE = Math.max(
|
|
39
68
|
1,
|
|
40
|
-
Math.floor(
|
|
69
|
+
Math.floor(D1_MAX_BOUND_PARAMETERS / OCCURRENCE_BIND_COLUMNS),
|
|
41
70
|
);
|
|
42
71
|
|
|
43
72
|
function cleanupDeleteBatchSize(cleanupLease: MediaUsageCleanupLease | undefined): number {
|
|
@@ -48,6 +77,84 @@ function canIssueCleanupStatement(canIssueStatement: (() => boolean) | undefined
|
|
|
48
77
|
return canIssueStatement?.() ?? true;
|
|
49
78
|
}
|
|
50
79
|
|
|
80
|
+
function nullableRefMatch(
|
|
81
|
+
left: "content.live_revision_id" | "content.draft_revision_id",
|
|
82
|
+
right: "input.revision_id",
|
|
83
|
+
): RawBuilder<boolean> {
|
|
84
|
+
const leftRef = sql.ref(left);
|
|
85
|
+
const rightRef = sql.ref(right);
|
|
86
|
+
return sql<boolean>`(
|
|
87
|
+
(${leftRef} IS NULL AND ${rightRef} IS NULL)
|
|
88
|
+
OR ${leftRef} = ${rightRef}
|
|
89
|
+
)`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const MAX_JSON_BIND_BYTES = 1_900_000;
|
|
93
|
+
const MEDIA_USAGE_EVENT_QUERY_CEILING = 900;
|
|
94
|
+
const MEDIA_USAGE_QUERY_RESERVE = 150;
|
|
95
|
+
|
|
96
|
+
function chunkJsonRows<T>(rows: readonly T[]): T[][] {
|
|
97
|
+
const batches: T[][] = [];
|
|
98
|
+
let batch: T[] = [];
|
|
99
|
+
let batchBytes = 2;
|
|
100
|
+
for (const row of rows) {
|
|
101
|
+
const rowBytes =
|
|
102
|
+
new TextEncoder().encode(JSON.stringify(row)).byteLength + (batch.length > 0 ? 1 : 0);
|
|
103
|
+
if (batch.length > 0 && batchBytes + rowBytes > MAX_JSON_BIND_BYTES) {
|
|
104
|
+
batches.push(batch);
|
|
105
|
+
batch = [];
|
|
106
|
+
batchBytes = 2;
|
|
107
|
+
}
|
|
108
|
+
batch.push(row);
|
|
109
|
+
batchBytes += rowBytes;
|
|
110
|
+
}
|
|
111
|
+
if (batch.length > 0) batches.push(batch);
|
|
112
|
+
return batches;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function mediaUsageQueryBudgetAllows(queries: number): boolean {
|
|
116
|
+
const metrics = getRequestContext()?.metrics;
|
|
117
|
+
return (
|
|
118
|
+
!metrics ||
|
|
119
|
+
metrics.dbCount + queries + MEDIA_USAGE_QUERY_RESERVE <= MEDIA_USAGE_EVENT_QUERY_CEILING
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function mergeInto<T>(target: Set<T>, source: ReadonlySet<T>): void {
|
|
124
|
+
for (const value of source) target.add(value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function batchOccurrenceRows(
|
|
128
|
+
prepared: readonly {
|
|
129
|
+
projection: { occurrences: readonly MediaUsageOccurrenceInput[] };
|
|
130
|
+
generation: string;
|
|
131
|
+
leaseToken: string;
|
|
132
|
+
row: { source_key: string };
|
|
133
|
+
}[],
|
|
134
|
+
now: string,
|
|
135
|
+
) {
|
|
136
|
+
return prepared.flatMap((item) =>
|
|
137
|
+
item.projection.occurrences.map((occurrence) => ({
|
|
138
|
+
id: ulid(),
|
|
139
|
+
source_key: item.row.source_key,
|
|
140
|
+
generation: item.generation,
|
|
141
|
+
field_slug: occurrence.fieldSlug,
|
|
142
|
+
field_path: occurrence.fieldPath,
|
|
143
|
+
occurrence_index: occurrence.occurrenceIndex ?? 0,
|
|
144
|
+
reference_type: occurrence.referenceType,
|
|
145
|
+
media_id: occurrence.mediaId,
|
|
146
|
+
provider: occurrence.provider,
|
|
147
|
+
provider_asset_id: occurrence.providerAssetId,
|
|
148
|
+
media_kind: occurrence.mediaKind ?? null,
|
|
149
|
+
mime_type: occurrence.mimeType ?? null,
|
|
150
|
+
created_at: now,
|
|
151
|
+
lease_token: item.leaseToken,
|
|
152
|
+
})),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
type BatchOccurrenceRow = ReturnType<typeof batchOccurrenceRows>[number];
|
|
157
|
+
|
|
51
158
|
function cleanupDurationSeconds(value: number): number {
|
|
52
159
|
if (!Number.isSafeInteger(value) || value < 0) {
|
|
53
160
|
throw new Error("Media usage cleanup duration must be a non-negative whole number of seconds");
|
|
@@ -168,6 +275,15 @@ export interface MediaUsageGuardedReplaceResult {
|
|
|
168
275
|
source: MediaUsageSource | null;
|
|
169
276
|
}
|
|
170
277
|
|
|
278
|
+
export interface MediaUsageNewSourceProjection {
|
|
279
|
+
source: MediaUsageSourceInput;
|
|
280
|
+
occurrences: readonly MediaUsageOccurrenceInput[];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface MediaUsageExistingSourceProjection extends MediaUsageNewSourceProjection {
|
|
284
|
+
expectedSource: MediaUsageSource;
|
|
285
|
+
}
|
|
286
|
+
|
|
171
287
|
export interface MediaUsageGuardedDeleteResult {
|
|
172
288
|
deleted: boolean;
|
|
173
289
|
source: MediaUsageSource | null;
|
|
@@ -340,6 +456,12 @@ export interface MediaUsageCollectionIndexStatusScope {
|
|
|
340
456
|
reconciliationRequired: boolean;
|
|
341
457
|
}
|
|
342
458
|
|
|
459
|
+
export interface MediaUsageCollectionProgress {
|
|
460
|
+
status: "indexing" | "ready" | "needs_attention";
|
|
461
|
+
readyCollections: number;
|
|
462
|
+
totalCollections: number;
|
|
463
|
+
}
|
|
464
|
+
|
|
343
465
|
export interface MediaUsageEntrySource {
|
|
344
466
|
source: MediaUsageSource;
|
|
345
467
|
occurrences: MediaUsageOccurrence[];
|
|
@@ -537,14 +659,15 @@ export class MediaUsageRepository {
|
|
|
537
659
|
const uniqueSourceKeys = [...new Set(sourceKeys)];
|
|
538
660
|
const sources = new Map<string, MediaUsageSource>();
|
|
539
661
|
if (uniqueSourceKeys.length === 0) return sources;
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
.
|
|
547
|
-
|
|
662
|
+
for (const sourceKeyBatch of chunkJsonRows(uniqueSourceKeys)) {
|
|
663
|
+
const input = jsonTextValues(this.db, sourceKeyBatch);
|
|
664
|
+
const result = await sql<Selectable<MediaUsageSourceTable>>`
|
|
665
|
+
WITH requested AS (${input})
|
|
666
|
+
SELECT source.*
|
|
667
|
+
FROM _emdash_media_usage_sources AS source
|
|
668
|
+
INNER JOIN requested ON requested.value = source.source_key
|
|
669
|
+
`.execute(this.db);
|
|
670
|
+
for (const row of result.rows) {
|
|
548
671
|
const source = rowToSource(row);
|
|
549
672
|
sources.set(source.sourceKey, source);
|
|
550
673
|
}
|
|
@@ -619,6 +742,441 @@ export class MediaUsageRepository {
|
|
|
619
742
|
};
|
|
620
743
|
}
|
|
621
744
|
|
|
745
|
+
async replaceNewSourcesBatch(
|
|
746
|
+
projections: readonly MediaUsageNewSourceProjection[],
|
|
747
|
+
): Promise<Set<string>> {
|
|
748
|
+
const unique = [
|
|
749
|
+
...new Map(
|
|
750
|
+
projections.map((projection) => [projection.source.sourceKey, projection]),
|
|
751
|
+
).values(),
|
|
752
|
+
];
|
|
753
|
+
if (unique.length === 0) return new Set();
|
|
754
|
+
const collectionSlug = unique[0]?.source.collectionSlug;
|
|
755
|
+
const collectionId = unique[0]?.source.collectionId;
|
|
756
|
+
if (!collectionSlug || !collectionId) {
|
|
757
|
+
throw new Error("Canonical media usage batch requires collection identity");
|
|
758
|
+
}
|
|
759
|
+
validateIdentifier(collectionSlug, "collection slug");
|
|
760
|
+
if (
|
|
761
|
+
unique.some(
|
|
762
|
+
(projection) =>
|
|
763
|
+
projection.source.collectionSlug !== collectionSlug ||
|
|
764
|
+
projection.source.collectionId !== collectionId,
|
|
765
|
+
)
|
|
766
|
+
) {
|
|
767
|
+
throw new Error("Canonical media usage batch must contain one collection");
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const now = new Date().toISOString();
|
|
771
|
+
const expiresAt = new Date(Date.now() + MEDIA_USAGE_GENERATION_WRITE_LEASE_MS).toISOString();
|
|
772
|
+
const prepared = unique.map((projection) => {
|
|
773
|
+
const generation = ulid();
|
|
774
|
+
return {
|
|
775
|
+
projection,
|
|
776
|
+
generation,
|
|
777
|
+
leaseToken: ulid(),
|
|
778
|
+
row: this.buildSourceRow(projection.source, generation, now),
|
|
779
|
+
};
|
|
780
|
+
});
|
|
781
|
+
const sourceRows = prepared.map((item) => ({
|
|
782
|
+
...item.row,
|
|
783
|
+
lease_token: item.leaseToken,
|
|
784
|
+
}));
|
|
785
|
+
const occurrenceRows = batchOccurrenceRows(prepared, now);
|
|
786
|
+
const estimatedQueries =
|
|
787
|
+
2 + chunkJsonRows(occurrenceRows).length + chunkJsonRows(sourceRows).length;
|
|
788
|
+
if (!mediaUsageQueryBudgetAllows(estimatedQueries)) {
|
|
789
|
+
if (unique.length === 1) return new Set();
|
|
790
|
+
const midpoint = Math.ceil(unique.length / 2);
|
|
791
|
+
const inserted = new Set<string>();
|
|
792
|
+
mergeInto(inserted, await this.replaceNewSourcesBatch(unique.slice(0, midpoint)));
|
|
793
|
+
mergeInto(inserted, await this.replaceNewSourcesBatch(unique.slice(midpoint)));
|
|
794
|
+
return inserted;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const leasesPayload = JSON.stringify(
|
|
798
|
+
prepared.map((item) => ({
|
|
799
|
+
source_key: item.row.source_key,
|
|
800
|
+
collection_id: item.row.collection_id,
|
|
801
|
+
collection_slug: item.row.collection_slug,
|
|
802
|
+
generation: item.generation,
|
|
803
|
+
lease_token: item.leaseToken,
|
|
804
|
+
expires_at: expiresAt,
|
|
805
|
+
created_at: now,
|
|
806
|
+
})),
|
|
807
|
+
);
|
|
808
|
+
const leases = this.generationWriteBatchInput(leasesPayload);
|
|
809
|
+
await sql`
|
|
810
|
+
WITH input AS (${leases})
|
|
811
|
+
INSERT INTO _emdash_media_usage_generation_writes (
|
|
812
|
+
source_key, generation, lease_token, expires_at, created_at
|
|
813
|
+
)
|
|
814
|
+
SELECT source_key, generation, lease_token, expires_at, created_at
|
|
815
|
+
FROM input
|
|
816
|
+
WHERE EXISTS (
|
|
817
|
+
SELECT 1
|
|
818
|
+
FROM _emdash_collections AS collection
|
|
819
|
+
INNER JOIN _emdash_media_usage_index_status AS status
|
|
820
|
+
ON status.collection_id = collection.id
|
|
821
|
+
AND status.scope_key = collection.slug
|
|
822
|
+
WHERE collection.id = input.collection_id
|
|
823
|
+
AND collection.slug = input.collection_slug
|
|
824
|
+
AND status.adapter_id = 'content-media'
|
|
825
|
+
AND status.scope_type = 'collection'
|
|
826
|
+
AND status.capture_state = 'active'
|
|
827
|
+
AND NOT EXISTS (
|
|
828
|
+
SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
|
|
829
|
+
WHERE deletion.collection_id = input.collection_id
|
|
830
|
+
)
|
|
831
|
+
)
|
|
832
|
+
`.execute(this.db);
|
|
833
|
+
|
|
834
|
+
try {
|
|
835
|
+
await this.insertBatchOccurrences(occurrenceRows);
|
|
836
|
+
const tableName = `ec_${collectionSlug}`;
|
|
837
|
+
validateIdentifier(tableName, "content table");
|
|
838
|
+
const inserted = new Set<string>();
|
|
839
|
+
for (const sourceRowBatch of chunkJsonRows(sourceRows)) {
|
|
840
|
+
const input = this.sourceBatchInput(JSON.stringify(sourceRowBatch));
|
|
841
|
+
const result = await sql<{ source_key: string }>`
|
|
842
|
+
WITH input AS (${input})
|
|
843
|
+
INSERT INTO _emdash_media_usage_sources (
|
|
844
|
+
source_key, source_type, collection_id, collection_slug, content_id,
|
|
845
|
+
source_variant, locale, translation_group, content_slug, content_title,
|
|
846
|
+
content_status, content_scheduled_at, content_deleted_at, revision_id,
|
|
847
|
+
current_generation, schema_version, source_updated_at, source_version,
|
|
848
|
+
source_fingerprint, identity_version, source_completeness,
|
|
849
|
+
last_attempted_at, last_error_code, indexed_at, updated_at
|
|
850
|
+
)
|
|
851
|
+
SELECT
|
|
852
|
+
input.source_key, input.source_type, input.collection_id, input.collection_slug,
|
|
853
|
+
input.content_id, input.source_variant, input.locale, input.translation_group,
|
|
854
|
+
input.content_slug, input.content_title, input.content_status,
|
|
855
|
+
input.content_scheduled_at, input.content_deleted_at, input.revision_id,
|
|
856
|
+
input.current_generation, input.schema_version, input.source_updated_at,
|
|
857
|
+
input.source_version, input.source_fingerprint, input.identity_version,
|
|
858
|
+
input.source_completeness, input.last_attempted_at, input.last_error_code,
|
|
859
|
+
input.indexed_at, input.updated_at
|
|
860
|
+
FROM input
|
|
861
|
+
WHERE EXISTS (
|
|
862
|
+
SELECT 1
|
|
863
|
+
FROM _emdash_media_usage_generation_writes AS writer
|
|
864
|
+
WHERE writer.source_key = input.source_key
|
|
865
|
+
AND writer.generation = input.current_generation
|
|
866
|
+
AND writer.lease_token = input.lease_token
|
|
867
|
+
AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
|
|
868
|
+
)
|
|
869
|
+
AND EXISTS (
|
|
870
|
+
SELECT 1
|
|
871
|
+
FROM _emdash_collections AS collection
|
|
872
|
+
INNER JOIN _emdash_media_usage_index_status AS status
|
|
873
|
+
ON status.collection_id = collection.id
|
|
874
|
+
AND status.scope_key = collection.slug
|
|
875
|
+
WHERE collection.id = input.collection_id
|
|
876
|
+
AND collection.slug = input.collection_slug
|
|
877
|
+
AND status.adapter_id = 'content-media'
|
|
878
|
+
AND status.scope_type = 'collection'
|
|
879
|
+
AND status.capture_state = 'active'
|
|
880
|
+
AND NOT EXISTS (
|
|
881
|
+
SELECT 1
|
|
882
|
+
FROM _emdash_media_usage_collection_deletions AS deletion
|
|
883
|
+
WHERE deletion.collection_id = input.collection_id
|
|
884
|
+
)
|
|
885
|
+
)
|
|
886
|
+
AND EXISTS (
|
|
887
|
+
SELECT 1
|
|
888
|
+
FROM ${sql.ref(tableName)} AS content
|
|
889
|
+
WHERE content.id = input.content_id
|
|
890
|
+
AND content.version = input.source_version
|
|
891
|
+
AND content.updated_at = input.source_updated_at
|
|
892
|
+
AND (
|
|
893
|
+
(input.source_variant = 'columns' AND ${nullableRefMatch("content.live_revision_id", "input.revision_id")})
|
|
894
|
+
OR
|
|
895
|
+
(input.source_variant = 'draft_overlay' AND ${nullableRefMatch("content.draft_revision_id", "input.revision_id")})
|
|
896
|
+
)
|
|
897
|
+
)
|
|
898
|
+
ON CONFLICT (source_key) DO NOTHING
|
|
899
|
+
RETURNING source_key
|
|
900
|
+
`.execute(this.db);
|
|
901
|
+
for (const row of result.rows) inserted.add(row.source_key);
|
|
902
|
+
}
|
|
903
|
+
return inserted;
|
|
904
|
+
} finally {
|
|
905
|
+
await sql`
|
|
906
|
+
WITH input AS (${leases})
|
|
907
|
+
DELETE FROM _emdash_media_usage_generation_writes AS writer
|
|
908
|
+
WHERE EXISTS (
|
|
909
|
+
SELECT 1
|
|
910
|
+
FROM input
|
|
911
|
+
WHERE input.source_key = writer.source_key
|
|
912
|
+
AND input.generation = writer.generation
|
|
913
|
+
AND input.lease_token = writer.lease_token
|
|
914
|
+
)
|
|
915
|
+
`.execute(this.db);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
async replaceExistingSourcesBatch(
|
|
920
|
+
projections: readonly MediaUsageExistingSourceProjection[],
|
|
921
|
+
): Promise<Set<string>> {
|
|
922
|
+
const unique = [
|
|
923
|
+
...new Map(
|
|
924
|
+
projections.map((projection) => [projection.source.sourceKey, projection]),
|
|
925
|
+
).values(),
|
|
926
|
+
];
|
|
927
|
+
if (unique.length === 0) return new Set();
|
|
928
|
+
const collectionSlug = unique[0]?.source.collectionSlug;
|
|
929
|
+
const collectionId = unique[0]?.source.collectionId;
|
|
930
|
+
if (!collectionSlug || !collectionId) {
|
|
931
|
+
throw new Error("Canonical media usage batch requires collection identity");
|
|
932
|
+
}
|
|
933
|
+
validateIdentifier(collectionSlug, "collection slug");
|
|
934
|
+
if (
|
|
935
|
+
unique.some(
|
|
936
|
+
(projection) =>
|
|
937
|
+
projection.source.collectionSlug !== collectionSlug ||
|
|
938
|
+
projection.source.collectionId !== collectionId,
|
|
939
|
+
)
|
|
940
|
+
) {
|
|
941
|
+
throw new Error("Canonical media usage batch must contain one collection");
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const now = new Date().toISOString();
|
|
945
|
+
const expiresAt = new Date(Date.now() + MEDIA_USAGE_GENERATION_WRITE_LEASE_MS).toISOString();
|
|
946
|
+
const prepared = unique.map((projection) => {
|
|
947
|
+
const generation = ulid();
|
|
948
|
+
return {
|
|
949
|
+
projection,
|
|
950
|
+
generation,
|
|
951
|
+
leaseToken: ulid(),
|
|
952
|
+
row: this.buildSourceRow(projection.source, generation, now),
|
|
953
|
+
};
|
|
954
|
+
});
|
|
955
|
+
const sourceRows = prepared.map((item) => ({
|
|
956
|
+
...item.row,
|
|
957
|
+
lease_token: item.leaseToken,
|
|
958
|
+
expected_generation: item.projection.expectedSource.currentGeneration,
|
|
959
|
+
expected_collection_id: item.projection.expectedSource.collectionId,
|
|
960
|
+
expected_updated_at: item.projection.expectedSource.updatedAt,
|
|
961
|
+
expected_source_fingerprint: item.projection.expectedSource.sourceFingerprint,
|
|
962
|
+
expected_source_updated_at: item.projection.expectedSource.sourceUpdatedAt,
|
|
963
|
+
expected_source_version: item.projection.expectedSource.sourceVersion,
|
|
964
|
+
expected_identity_version: item.projection.expectedSource.identityVersion,
|
|
965
|
+
expected_revision_id: item.projection.expectedSource.revisionId,
|
|
966
|
+
expected_source_completeness: item.projection.expectedSource.sourceCompleteness,
|
|
967
|
+
expected_last_attempted_at: item.projection.expectedSource.lastAttemptedAt,
|
|
968
|
+
expected_last_error_code: item.projection.expectedSource.lastErrorCode,
|
|
969
|
+
}));
|
|
970
|
+
const occurrenceRows = batchOccurrenceRows(prepared, now);
|
|
971
|
+
const estimatedQueries =
|
|
972
|
+
2 + chunkJsonRows(occurrenceRows).length + chunkJsonRows(sourceRows).length;
|
|
973
|
+
if (!mediaUsageQueryBudgetAllows(estimatedQueries)) {
|
|
974
|
+
if (unique.length === 1) return new Set();
|
|
975
|
+
const midpoint = Math.ceil(unique.length / 2);
|
|
976
|
+
const replaced = new Set<string>();
|
|
977
|
+
mergeInto(replaced, await this.replaceExistingSourcesBatch(unique.slice(0, midpoint)));
|
|
978
|
+
mergeInto(replaced, await this.replaceExistingSourcesBatch(unique.slice(midpoint)));
|
|
979
|
+
return replaced;
|
|
980
|
+
}
|
|
981
|
+
const leasesPayload = JSON.stringify(
|
|
982
|
+
prepared.map((item) => ({
|
|
983
|
+
source_key: item.row.source_key,
|
|
984
|
+
collection_id: item.row.collection_id,
|
|
985
|
+
collection_slug: item.row.collection_slug,
|
|
986
|
+
generation: item.generation,
|
|
987
|
+
lease_token: item.leaseToken,
|
|
988
|
+
expires_at: expiresAt,
|
|
989
|
+
created_at: now,
|
|
990
|
+
})),
|
|
991
|
+
);
|
|
992
|
+
const leases = this.generationWriteBatchInput(leasesPayload);
|
|
993
|
+
await sql`
|
|
994
|
+
WITH input AS (${leases})
|
|
995
|
+
INSERT INTO _emdash_media_usage_generation_writes (
|
|
996
|
+
source_key, generation, lease_token, expires_at, created_at
|
|
997
|
+
)
|
|
998
|
+
SELECT source_key, generation, lease_token, expires_at, created_at
|
|
999
|
+
FROM input
|
|
1000
|
+
WHERE EXISTS (
|
|
1001
|
+
SELECT 1
|
|
1002
|
+
FROM _emdash_collections AS collection
|
|
1003
|
+
INNER JOIN _emdash_media_usage_index_status AS status
|
|
1004
|
+
ON status.collection_id = collection.id
|
|
1005
|
+
AND status.scope_key = collection.slug
|
|
1006
|
+
WHERE collection.id = input.collection_id
|
|
1007
|
+
AND collection.slug = input.collection_slug
|
|
1008
|
+
AND status.adapter_id = 'content-media'
|
|
1009
|
+
AND status.scope_type = 'collection'
|
|
1010
|
+
AND status.capture_state = 'active'
|
|
1011
|
+
AND NOT EXISTS (
|
|
1012
|
+
SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
|
|
1013
|
+
WHERE deletion.collection_id = input.collection_id
|
|
1014
|
+
)
|
|
1015
|
+
)
|
|
1016
|
+
`.execute(this.db);
|
|
1017
|
+
|
|
1018
|
+
try {
|
|
1019
|
+
await this.insertBatchOccurrences(occurrenceRows);
|
|
1020
|
+
const tableName = `ec_${collectionSlug}`;
|
|
1021
|
+
validateIdentifier(tableName, "content table");
|
|
1022
|
+
const replaced = new Set<string>();
|
|
1023
|
+
for (const sourceRowBatch of chunkJsonRows(sourceRows)) {
|
|
1024
|
+
const input = this.sourceBatchInput(JSON.stringify(sourceRowBatch));
|
|
1025
|
+
const result = await sql<{ source_key: string }>`
|
|
1026
|
+
WITH input AS (${input})
|
|
1027
|
+
UPDATE _emdash_media_usage_sources AS source
|
|
1028
|
+
SET source_type = input.source_type,
|
|
1029
|
+
collection_id = input.collection_id,
|
|
1030
|
+
collection_slug = input.collection_slug,
|
|
1031
|
+
content_id = input.content_id,
|
|
1032
|
+
source_variant = input.source_variant,
|
|
1033
|
+
locale = input.locale,
|
|
1034
|
+
translation_group = input.translation_group,
|
|
1035
|
+
content_slug = input.content_slug,
|
|
1036
|
+
content_title = input.content_title,
|
|
1037
|
+
content_status = input.content_status,
|
|
1038
|
+
content_scheduled_at = input.content_scheduled_at,
|
|
1039
|
+
content_deleted_at = input.content_deleted_at,
|
|
1040
|
+
revision_id = input.revision_id,
|
|
1041
|
+
current_generation = input.current_generation,
|
|
1042
|
+
schema_version = input.schema_version,
|
|
1043
|
+
source_updated_at = input.source_updated_at,
|
|
1044
|
+
source_version = input.source_version,
|
|
1045
|
+
source_fingerprint = input.source_fingerprint,
|
|
1046
|
+
identity_version = input.identity_version,
|
|
1047
|
+
source_completeness = input.source_completeness,
|
|
1048
|
+
last_attempted_at = input.last_attempted_at,
|
|
1049
|
+
last_error_code = input.last_error_code,
|
|
1050
|
+
indexed_at = input.indexed_at,
|
|
1051
|
+
updated_at = input.updated_at
|
|
1052
|
+
FROM input
|
|
1053
|
+
WHERE source.source_key = input.source_key
|
|
1054
|
+
AND source.current_generation = input.expected_generation
|
|
1055
|
+
AND ${this.batchRefsMatch("source.collection_id", "input.expected_collection_id")}
|
|
1056
|
+
AND source.updated_at = input.expected_updated_at
|
|
1057
|
+
AND ${this.batchRefsMatch("source.source_fingerprint", "input.expected_source_fingerprint")}
|
|
1058
|
+
AND ${this.batchRefsMatch("source.source_updated_at", "input.expected_source_updated_at")}
|
|
1059
|
+
AND ${this.batchRefsMatch("source.source_version", "input.expected_source_version")}
|
|
1060
|
+
AND ${this.batchRefsMatch("source.identity_version", "input.expected_identity_version")}
|
|
1061
|
+
AND ${this.batchRefsMatch("source.revision_id", "input.expected_revision_id")}
|
|
1062
|
+
AND source.source_completeness = input.expected_source_completeness
|
|
1063
|
+
AND ${this.batchRefsMatch("source.last_attempted_at", "input.expected_last_attempted_at")}
|
|
1064
|
+
AND ${this.batchRefsMatch("source.last_error_code", "input.expected_last_error_code")}
|
|
1065
|
+
AND EXISTS (
|
|
1066
|
+
SELECT 1
|
|
1067
|
+
FROM _emdash_media_usage_generation_writes AS writer
|
|
1068
|
+
WHERE writer.source_key = input.source_key
|
|
1069
|
+
AND writer.generation = input.current_generation
|
|
1070
|
+
AND writer.lease_token = input.lease_token
|
|
1071
|
+
AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
|
|
1072
|
+
)
|
|
1073
|
+
AND EXISTS (
|
|
1074
|
+
SELECT 1
|
|
1075
|
+
FROM _emdash_collections AS collection
|
|
1076
|
+
INNER JOIN _emdash_media_usage_index_status AS status
|
|
1077
|
+
ON status.collection_id = collection.id
|
|
1078
|
+
AND status.scope_key = collection.slug
|
|
1079
|
+
WHERE collection.id = input.collection_id
|
|
1080
|
+
AND collection.slug = input.collection_slug
|
|
1081
|
+
AND status.adapter_id = 'content-media'
|
|
1082
|
+
AND status.scope_type = 'collection'
|
|
1083
|
+
AND status.capture_state = 'active'
|
|
1084
|
+
AND NOT EXISTS (
|
|
1085
|
+
SELECT 1
|
|
1086
|
+
FROM _emdash_media_usage_collection_deletions AS deletion
|
|
1087
|
+
WHERE deletion.collection_id = input.collection_id
|
|
1088
|
+
)
|
|
1089
|
+
)
|
|
1090
|
+
AND EXISTS (
|
|
1091
|
+
SELECT 1
|
|
1092
|
+
FROM ${sql.ref(tableName)} AS content
|
|
1093
|
+
WHERE content.id = input.content_id
|
|
1094
|
+
AND content.version = input.source_version
|
|
1095
|
+
AND content.updated_at = input.source_updated_at
|
|
1096
|
+
AND (
|
|
1097
|
+
(input.source_variant = 'columns' AND ${nullableRefMatch("content.live_revision_id", "input.revision_id")})
|
|
1098
|
+
OR
|
|
1099
|
+
(input.source_variant = 'draft_overlay' AND ${nullableRefMatch("content.draft_revision_id", "input.revision_id")})
|
|
1100
|
+
)
|
|
1101
|
+
)
|
|
1102
|
+
RETURNING ${isPostgres(this.db) ? sql`source.source_key` : sql`source_key`} AS source_key
|
|
1103
|
+
`.execute(this.db);
|
|
1104
|
+
for (const row of result.rows) replaced.add(row.source_key);
|
|
1105
|
+
}
|
|
1106
|
+
return replaced;
|
|
1107
|
+
} finally {
|
|
1108
|
+
await sql`
|
|
1109
|
+
WITH input AS (${leases})
|
|
1110
|
+
DELETE FROM _emdash_media_usage_generation_writes AS writer
|
|
1111
|
+
WHERE EXISTS (
|
|
1112
|
+
SELECT 1
|
|
1113
|
+
FROM input
|
|
1114
|
+
WHERE input.source_key = writer.source_key
|
|
1115
|
+
AND input.generation = writer.generation
|
|
1116
|
+
AND input.lease_token = writer.lease_token
|
|
1117
|
+
)
|
|
1118
|
+
`.execute(this.db);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
async matchingExistingSourcesBatch(
|
|
1123
|
+
projections: readonly MediaUsageExistingSourceProjection[],
|
|
1124
|
+
): Promise<Set<string>> {
|
|
1125
|
+
if (projections.length === 0) return new Set();
|
|
1126
|
+
const now = new Date().toISOString();
|
|
1127
|
+
const rows = projections.map((projection) => ({
|
|
1128
|
+
...this.buildSourceRow(projection.source, projection.expectedSource.currentGeneration, now),
|
|
1129
|
+
expected_generation: projection.expectedSource.currentGeneration,
|
|
1130
|
+
expected_collection_id: projection.expectedSource.collectionId,
|
|
1131
|
+
expected_updated_at: projection.expectedSource.updatedAt,
|
|
1132
|
+
expected_source_fingerprint: projection.expectedSource.sourceFingerprint,
|
|
1133
|
+
expected_source_updated_at: projection.expectedSource.sourceUpdatedAt,
|
|
1134
|
+
expected_source_version: projection.expectedSource.sourceVersion,
|
|
1135
|
+
expected_identity_version: projection.expectedSource.identityVersion,
|
|
1136
|
+
expected_revision_id: projection.expectedSource.revisionId,
|
|
1137
|
+
expected_source_completeness: projection.expectedSource.sourceCompleteness,
|
|
1138
|
+
expected_last_attempted_at: projection.expectedSource.lastAttemptedAt,
|
|
1139
|
+
expected_last_error_code: projection.expectedSource.lastErrorCode,
|
|
1140
|
+
}));
|
|
1141
|
+
if (!mediaUsageQueryBudgetAllows(chunkJsonRows(rows).length)) {
|
|
1142
|
+
if (projections.length === 1) return new Set();
|
|
1143
|
+
const midpoint = Math.ceil(projections.length / 2);
|
|
1144
|
+
const matched = new Set<string>();
|
|
1145
|
+
mergeInto(matched, await this.matchingExistingSourcesBatch(projections.slice(0, midpoint)));
|
|
1146
|
+
mergeInto(matched, await this.matchingExistingSourcesBatch(projections.slice(midpoint)));
|
|
1147
|
+
return matched;
|
|
1148
|
+
}
|
|
1149
|
+
const matched = new Set<string>();
|
|
1150
|
+
for (const batch of chunkJsonRows(rows)) {
|
|
1151
|
+
const input = this.sourceBatchInput(JSON.stringify(batch));
|
|
1152
|
+
const result = await sql<{ source_key: string }>`
|
|
1153
|
+
WITH input AS (${input})
|
|
1154
|
+
SELECT source.source_key
|
|
1155
|
+
FROM _emdash_media_usage_sources AS source
|
|
1156
|
+
INNER JOIN input ON input.source_key = source.source_key
|
|
1157
|
+
WHERE source.current_generation = input.expected_generation
|
|
1158
|
+
AND ${this.batchRefsMatch("source.collection_id", "input.expected_collection_id")}
|
|
1159
|
+
AND source.updated_at = input.expected_updated_at
|
|
1160
|
+
AND ${this.batchRefsMatch("source.source_fingerprint", "input.expected_source_fingerprint")}
|
|
1161
|
+
AND ${this.batchRefsMatch("source.source_updated_at", "input.expected_source_updated_at")}
|
|
1162
|
+
AND ${this.batchRefsMatch("source.source_version", "input.expected_source_version")}
|
|
1163
|
+
AND ${this.batchRefsMatch("source.identity_version", "input.expected_identity_version")}
|
|
1164
|
+
AND ${this.batchRefsMatch("source.revision_id", "input.expected_revision_id")}
|
|
1165
|
+
AND source.source_completeness = input.expected_source_completeness
|
|
1166
|
+
AND ${this.batchRefsMatch("source.last_attempted_at", "input.expected_last_attempted_at")}
|
|
1167
|
+
AND ${this.batchRefsMatch("source.last_error_code", "input.expected_last_error_code")}
|
|
1168
|
+
AND EXISTS (
|
|
1169
|
+
SELECT 1
|
|
1170
|
+
FROM _emdash_collections AS collection
|
|
1171
|
+
WHERE collection.id = source.collection_id
|
|
1172
|
+
AND collection.slug = source.collection_slug
|
|
1173
|
+
)
|
|
1174
|
+
`.execute(this.db);
|
|
1175
|
+
for (const row of result.rows) matched.add(row.source_key);
|
|
1176
|
+
}
|
|
1177
|
+
return matched;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
622
1180
|
async markSourceAttempted(source: MediaUsageSourceInput): Promise<MediaUsageSource> {
|
|
623
1181
|
if (source.collectionId !== undefined && source.collectionId !== null) {
|
|
624
1182
|
const expectedSource = await this.findSource(source.sourceKey);
|
|
@@ -762,6 +1320,129 @@ export class MediaUsageRepository {
|
|
|
762
1320
|
}));
|
|
763
1321
|
}
|
|
764
1322
|
|
|
1323
|
+
async findCollectionProgress(): Promise<MediaUsageCollectionProgress | null> {
|
|
1324
|
+
const result = await sql<{
|
|
1325
|
+
activation_active: boolean | number;
|
|
1326
|
+
activation_generation: number | string | null;
|
|
1327
|
+
needs_attention: boolean | number;
|
|
1328
|
+
ready_collections: number | string;
|
|
1329
|
+
total_collections: number | string;
|
|
1330
|
+
cleanup_pending: boolean | number;
|
|
1331
|
+
}>`
|
|
1332
|
+
WITH collection_progress AS (
|
|
1333
|
+
SELECT
|
|
1334
|
+
CASE WHEN status.status = 'complete'
|
|
1335
|
+
AND status.schema_version = ${CONTENT_SOURCE_SCHEMA_VERSION}
|
|
1336
|
+
AND status.reconciliation_required = 0
|
|
1337
|
+
AND status.capture_state = 'active'
|
|
1338
|
+
AND NOT EXISTS (
|
|
1339
|
+
SELECT 1 FROM _emdash_media_usage_work AS work
|
|
1340
|
+
WHERE work.collection_id = collection.id
|
|
1341
|
+
AND work.collection_slug = collection.slug
|
|
1342
|
+
)
|
|
1343
|
+
THEN 1 ELSE 0 END AS is_ready,
|
|
1344
|
+
CASE WHEN status.collection_id IS NULL
|
|
1345
|
+
OR COALESCE(status.capture_state, '') <> 'active'
|
|
1346
|
+
OR status.status NOT IN ('complete', 'never', 'running', 'partial', 'failed', 'stale')
|
|
1347
|
+
OR status.status = 'failed'
|
|
1348
|
+
OR (
|
|
1349
|
+
status.reconciliation_required = 0
|
|
1350
|
+
AND (
|
|
1351
|
+
status.status <> 'complete'
|
|
1352
|
+
OR COALESCE(status.schema_version, -1) <> ${CONTENT_SOURCE_SCHEMA_VERSION}
|
|
1353
|
+
)
|
|
1354
|
+
AND NOT EXISTS (
|
|
1355
|
+
SELECT 1 FROM _emdash_media_usage_work AS work
|
|
1356
|
+
WHERE work.collection_id = collection.id
|
|
1357
|
+
AND work.collection_slug = collection.slug
|
|
1358
|
+
)
|
|
1359
|
+
)
|
|
1360
|
+
OR EXISTS (
|
|
1361
|
+
SELECT 1 FROM _emdash_media_usage_work AS work
|
|
1362
|
+
WHERE work.collection_id = collection.id
|
|
1363
|
+
AND work.collection_slug = collection.slug
|
|
1364
|
+
AND work.state = 'failed'
|
|
1365
|
+
)
|
|
1366
|
+
OR EXISTS (
|
|
1367
|
+
SELECT 1 FROM _emdash_media_usage_reconciliations AS reconciliation
|
|
1368
|
+
WHERE reconciliation.collection_id = collection.id
|
|
1369
|
+
AND reconciliation.collection_slug = collection.slug
|
|
1370
|
+
AND reconciliation.state = 'failed'
|
|
1371
|
+
AND status.reconciliation_required = 1
|
|
1372
|
+
AND (
|
|
1373
|
+
reconciliation.target_epoch IS NULL
|
|
1374
|
+
OR reconciliation.target_epoch >= status.change_epoch
|
|
1375
|
+
)
|
|
1376
|
+
)
|
|
1377
|
+
THEN 1 ELSE 0 END AS needs_attention
|
|
1378
|
+
FROM _emdash_collections AS collection
|
|
1379
|
+
LEFT JOIN _emdash_media_usage_index_status AS status
|
|
1380
|
+
ON status.adapter_id = 'content-media'
|
|
1381
|
+
AND status.scope_type = 'collection'
|
|
1382
|
+
AND status.collection_id = collection.id
|
|
1383
|
+
AND status.scope_key = collection.slug
|
|
1384
|
+
WHERE NOT EXISTS (
|
|
1385
|
+
SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
|
|
1386
|
+
WHERE deletion.collection_id = collection.id
|
|
1387
|
+
AND deletion.collection_slug = collection.slug
|
|
1388
|
+
)
|
|
1389
|
+
)
|
|
1390
|
+
SELECT
|
|
1391
|
+
(
|
|
1392
|
+
SELECT activation.runtime_generation
|
|
1393
|
+
FROM _emdash_media_usage_activation AS activation
|
|
1394
|
+
WHERE activation.task_key = 'incremental_capture'
|
|
1395
|
+
) AS activation_generation,
|
|
1396
|
+
EXISTS (
|
|
1397
|
+
SELECT 1 FROM _emdash_media_usage_activation AS activation
|
|
1398
|
+
WHERE activation.task_key = 'incremental_capture'
|
|
1399
|
+
AND activation.state = 'active'
|
|
1400
|
+
) AS activation_active,
|
|
1401
|
+
COUNT(*) AS total_collections,
|
|
1402
|
+
COALESCE(SUM(is_ready), 0) AS ready_collections,
|
|
1403
|
+
EXISTS (
|
|
1404
|
+
SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
|
|
1405
|
+
WHERE deletion.state IN ('pending', 'retry', 'leased')
|
|
1406
|
+
) AS cleanup_pending,
|
|
1407
|
+
CASE WHEN COALESCE(MAX(needs_attention), 0) = 1 OR EXISTS (
|
|
1408
|
+
SELECT 1 FROM _emdash_media_usage_collection_deletions AS deletion
|
|
1409
|
+
WHERE deletion.state = 'failed'
|
|
1410
|
+
) THEN 1 ELSE 0 END AS needs_attention
|
|
1411
|
+
FROM collection_progress
|
|
1412
|
+
`.execute(this.db);
|
|
1413
|
+
const row = result.rows[0];
|
|
1414
|
+
if (!row) throw new Error("Media usage progress query returned no result");
|
|
1415
|
+
if (!row.activation_active) return null;
|
|
1416
|
+
if (Number(row.activation_generation) !== MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION) {
|
|
1417
|
+
throw new MediaUsageActivationVersionMismatchError(
|
|
1418
|
+
"Media usage activation runtime generation is incompatible",
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
const readyCollections = Number(row.ready_collections);
|
|
1422
|
+
const totalCollections = Number(row.total_collections);
|
|
1423
|
+
const cleanupPending = Number(row.cleanup_pending);
|
|
1424
|
+
const needsAttention = Number(row.needs_attention);
|
|
1425
|
+
if (
|
|
1426
|
+
!Number.isSafeInteger(readyCollections) ||
|
|
1427
|
+
!Number.isSafeInteger(totalCollections) ||
|
|
1428
|
+
readyCollections < 0 ||
|
|
1429
|
+
totalCollections < readyCollections ||
|
|
1430
|
+
(cleanupPending !== 0 && cleanupPending !== 1) ||
|
|
1431
|
+
(needsAttention !== 0 && needsAttention !== 1)
|
|
1432
|
+
) {
|
|
1433
|
+
throw new Error("Media usage progress query returned invalid counts");
|
|
1434
|
+
}
|
|
1435
|
+
return {
|
|
1436
|
+
status: needsAttention
|
|
1437
|
+
? "needs_attention"
|
|
1438
|
+
: readyCollections === totalCollections && cleanupPending === 0
|
|
1439
|
+
? "ready"
|
|
1440
|
+
: "indexing",
|
|
1441
|
+
readyCollections,
|
|
1442
|
+
totalCollections,
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
|
|
765
1446
|
async findCurrentEntryUsagePageByMediaId(
|
|
766
1447
|
mediaId: string,
|
|
767
1448
|
options: FindMediaUsageOptions = {},
|
|
@@ -1120,12 +1801,21 @@ export class MediaUsageRepository {
|
|
|
1120
1801
|
};
|
|
1121
1802
|
}
|
|
1122
1803
|
|
|
1804
|
+
/**
|
|
1805
|
+
* Scans for reclaimable occurrences, or resolves to `null` when the sweep was
|
|
1806
|
+
* never issued because the lease is gone or the tick is out of budget. An
|
|
1807
|
+
* empty array means the scan ran and found nothing, which lets the caller
|
|
1808
|
+
* retire the sweep window; `null` must leave the cursor where it was.
|
|
1809
|
+
*/
|
|
1123
1810
|
async findMediaUsageCleanupCandidates(input: {
|
|
1124
1811
|
cutoff: string;
|
|
1125
1812
|
cursor: MediaUsageCleanupCursor | null;
|
|
1126
1813
|
limit: number;
|
|
1127
1814
|
cleanupLease?: MediaUsageCleanupLease;
|
|
1128
|
-
|
|
1815
|
+
canIssueStatement?: () => boolean;
|
|
1816
|
+
}): Promise<MediaUsageCleanupCandidate[] | null> {
|
|
1817
|
+
if (!(await this.admitCleanupScan(input.cleanupLease, input.canIssueStatement))) return null;
|
|
1818
|
+
|
|
1129
1819
|
let query = this.db
|
|
1130
1820
|
.selectFrom("_emdash_media_usage as u")
|
|
1131
1821
|
.leftJoin("_emdash_media_usage_sources as s", "s.source_key", "u.source_key")
|
|
@@ -1147,19 +1837,11 @@ export class MediaUsageRepository {
|
|
|
1147
1837
|
.orderBy("u.created_at", "asc")
|
|
1148
1838
|
.orderBy("u.id", "asc")
|
|
1149
1839
|
.limit(Math.max(0, Math.floor(input.limit)));
|
|
1150
|
-
if (input.cleanupLease) {
|
|
1151
|
-
query = query.where(this.activeCleanupLeaseExpression(input.cleanupLease));
|
|
1152
|
-
}
|
|
1153
1840
|
|
|
1154
1841
|
if (input.cursor) {
|
|
1842
|
+
const cursor = input.cursor;
|
|
1155
1843
|
query = query.where((eb) =>
|
|
1156
|
-
eb.
|
|
1157
|
-
eb("u.created_at", ">", input.cursor!.createdAt),
|
|
1158
|
-
eb.and([
|
|
1159
|
-
eb("u.created_at", "=", input.cursor!.createdAt),
|
|
1160
|
-
eb("u.id", ">", input.cursor!.id),
|
|
1161
|
-
]),
|
|
1162
|
-
]),
|
|
1844
|
+
eb(eb.refTuple("u.created_at", "u.id"), ">", eb.tuple(cursor.createdAt, cursor.id)),
|
|
1163
1845
|
);
|
|
1164
1846
|
}
|
|
1165
1847
|
|
|
@@ -1247,9 +1929,9 @@ export class MediaUsageRepository {
|
|
|
1247
1929
|
options.canIssueStatement,
|
|
1248
1930
|
);
|
|
1249
1931
|
}
|
|
1250
|
-
if (!
|
|
1932
|
+
if (!(await this.admitCleanupScan(options.cleanupLease, options.canIssueStatement))) return 0;
|
|
1251
1933
|
|
|
1252
|
-
|
|
1934
|
+
const rows = await this.db
|
|
1253
1935
|
.selectFrom("_emdash_media_usage as u")
|
|
1254
1936
|
.leftJoin("_emdash_media_usage_sources as s", (join) =>
|
|
1255
1937
|
join.onRef("s.source_key", "=", "u.source_key"),
|
|
@@ -1265,11 +1947,8 @@ export class MediaUsageRepository {
|
|
|
1265
1947
|
.where(this.noActiveGenerationWriteExpression("u"))
|
|
1266
1948
|
.orderBy("u.created_at", "asc")
|
|
1267
1949
|
.orderBy("u.id", "asc")
|
|
1268
|
-
.limit(batchLimit)
|
|
1269
|
-
|
|
1270
|
-
query = query.where(this.activeCleanupLeaseExpression(options.cleanupLease));
|
|
1271
|
-
}
|
|
1272
|
-
const rows = await query.execute();
|
|
1950
|
+
.limit(batchLimit)
|
|
1951
|
+
.execute();
|
|
1273
1952
|
|
|
1274
1953
|
return this.deleteOrphanCandidateIds(
|
|
1275
1954
|
rows.map((row) => row.id),
|
|
@@ -1294,9 +1973,9 @@ export class MediaUsageRepository {
|
|
|
1294
1973
|
options.canIssueStatement,
|
|
1295
1974
|
);
|
|
1296
1975
|
}
|
|
1297
|
-
if (!
|
|
1976
|
+
if (!(await this.admitCleanupScan(options.cleanupLease, options.canIssueStatement))) return 0;
|
|
1298
1977
|
|
|
1299
|
-
|
|
1978
|
+
const rows = await this.db
|
|
1300
1979
|
.selectFrom("_emdash_media_usage as u")
|
|
1301
1980
|
.innerJoin("_emdash_media_usage_sources as s", (join) =>
|
|
1302
1981
|
join.onRef("s.source_key", "=", "u.source_key"),
|
|
@@ -1313,11 +1992,8 @@ export class MediaUsageRepository {
|
|
|
1313
1992
|
.where(this.noActiveGenerationWriteExpression("u"))
|
|
1314
1993
|
.orderBy("u.created_at", "asc")
|
|
1315
1994
|
.orderBy("u.id", "asc")
|
|
1316
|
-
.limit(batchLimit)
|
|
1317
|
-
|
|
1318
|
-
query = query.where(this.activeCleanupLeaseExpression(options.cleanupLease));
|
|
1319
|
-
}
|
|
1320
|
-
const rows = await query.execute();
|
|
1995
|
+
.limit(batchLimit)
|
|
1996
|
+
.execute();
|
|
1321
1997
|
|
|
1322
1998
|
return this.deleteStaleCandidateIds(
|
|
1323
1999
|
rows.map((row) => row.id),
|
|
@@ -1342,9 +2018,9 @@ export class MediaUsageRepository {
|
|
|
1342
2018
|
options.canIssueStatement,
|
|
1343
2019
|
);
|
|
1344
2020
|
}
|
|
1345
|
-
if (!
|
|
2021
|
+
if (!(await this.admitCleanupScan(options.cleanupLease, options.canIssueStatement))) return 0;
|
|
1346
2022
|
|
|
1347
|
-
|
|
2023
|
+
const rows = await this.db
|
|
1348
2024
|
.selectFrom("_emdash_media_usage as u")
|
|
1349
2025
|
.innerJoin("_emdash_media_usage_sources as s", (join) =>
|
|
1350
2026
|
join.onRef("s.source_key", "=", "u.source_key"),
|
|
@@ -1361,11 +2037,8 @@ export class MediaUsageRepository {
|
|
|
1361
2037
|
.where(this.noActiveGenerationWriteExpression("u"))
|
|
1362
2038
|
.orderBy("u.created_at", "asc")
|
|
1363
2039
|
.orderBy("u.id", "asc")
|
|
1364
|
-
.limit(batchLimit)
|
|
1365
|
-
|
|
1366
|
-
query = query.where(this.activeCleanupLeaseExpression(options.cleanupLease));
|
|
1367
|
-
}
|
|
1368
|
-
const rows = await query.execute();
|
|
2040
|
+
.limit(batchLimit)
|
|
2041
|
+
.execute();
|
|
1369
2042
|
|
|
1370
2043
|
return this.deleteAbandonedCandidateIds(
|
|
1371
2044
|
rows.map((row) => row.id),
|
|
@@ -1381,16 +2054,16 @@ export class MediaUsageRepository {
|
|
|
1381
2054
|
canIssueStatement?: () => boolean,
|
|
1382
2055
|
): Promise<number> {
|
|
1383
2056
|
const batchLimit = Math.floor(limit);
|
|
1384
|
-
if (batchLimit <= 0
|
|
1385
|
-
|
|
2057
|
+
if (batchLimit <= 0) return 0;
|
|
2058
|
+
if (!(await this.admitCleanupScan(cleanupLease, canIssueStatement))) return 0;
|
|
2059
|
+
const rows = await this.db
|
|
1386
2060
|
.selectFrom("_emdash_media_usage_generation_writes")
|
|
1387
2061
|
.select("lease_token")
|
|
1388
2062
|
.where(this.generationWriteLeaseHasExpired("expires_at"))
|
|
1389
2063
|
.orderBy("expires_at", "asc")
|
|
1390
2064
|
.orderBy("lease_token", "asc")
|
|
1391
|
-
.limit(batchLimit)
|
|
1392
|
-
|
|
1393
|
-
const rows = await query.execute();
|
|
2065
|
+
.limit(batchLimit)
|
|
2066
|
+
.execute();
|
|
1394
2067
|
if (rows.length === 0 || !canIssueCleanupStatement(canIssueStatement)) return 0;
|
|
1395
2068
|
let deleteQuery = this.db
|
|
1396
2069
|
.deleteFrom("_emdash_media_usage_generation_writes")
|
|
@@ -1694,6 +2367,7 @@ export class MediaUsageRepository {
|
|
|
1694
2367
|
last_error_code: sql<
|
|
1695
2368
|
string | null
|
|
1696
2369
|
>`CASE WHEN ${canComplete} THEN NULL ELSE last_error_code END`,
|
|
2370
|
+
cursor: sql<string | null>`CASE WHEN ${canComplete} THEN NULL ELSE cursor END`,
|
|
1697
2371
|
last_incremental_success_at: now,
|
|
1698
2372
|
updated_at: now,
|
|
1699
2373
|
})
|
|
@@ -1715,6 +2389,94 @@ export class MediaUsageRepository {
|
|
|
1715
2389
|
return Number(result.numUpdatedRows ?? 0) > 0;
|
|
1716
2390
|
}
|
|
1717
2391
|
|
|
2392
|
+
async prepareIncrementalFinalization(
|
|
2393
|
+
input: MediaUsageIncrementalStatusIdentity,
|
|
2394
|
+
): Promise<
|
|
2395
|
+
{ outcome: "marked"; marker: string } | { outcome: "not_required" } | { outcome: "lost" }
|
|
2396
|
+
> {
|
|
2397
|
+
const observed = await this.db
|
|
2398
|
+
.selectFrom("_emdash_media_usage_index_status")
|
|
2399
|
+
.select(["change_epoch", "reconciliation_required"])
|
|
2400
|
+
.where("adapter_id", "=", "content-media")
|
|
2401
|
+
.where("scope_type", "=", "collection")
|
|
2402
|
+
.where("scope_key", "=", input.collectionSlug)
|
|
2403
|
+
.where("collection_id", "=", input.collectionId)
|
|
2404
|
+
.where("capture_state", "=", "active")
|
|
2405
|
+
.executeTakeFirst();
|
|
2406
|
+
if (!observed) return { outcome: "lost" };
|
|
2407
|
+
if (observed.reconciliation_required !== 0) return { outcome: "not_required" };
|
|
2408
|
+
const marker = `incremental-finalize:${String(observed.change_epoch)}:${ulid()}`;
|
|
2409
|
+
const result = await this.db
|
|
2410
|
+
.updateTable("_emdash_media_usage_index_status")
|
|
2411
|
+
.set({ cursor: marker, updated_at: this.sortableUtcTimestamp() })
|
|
2412
|
+
.where("adapter_id", "=", "content-media")
|
|
2413
|
+
.where("scope_type", "=", "collection")
|
|
2414
|
+
.where("scope_key", "=", input.collectionSlug)
|
|
2415
|
+
.where("collection_id", "=", input.collectionId)
|
|
2416
|
+
.where("capture_state", "=", "active")
|
|
2417
|
+
.where("reconciliation_required", "=", 0)
|
|
2418
|
+
.where("change_epoch", "=", observed.change_epoch)
|
|
2419
|
+
.where(
|
|
2420
|
+
sql<boolean>`EXISTS (
|
|
2421
|
+
SELECT 1
|
|
2422
|
+
FROM _emdash_collections AS collection
|
|
2423
|
+
WHERE collection.id = ${input.collectionId}
|
|
2424
|
+
AND collection.slug = ${input.collectionSlug}
|
|
2425
|
+
)`,
|
|
2426
|
+
)
|
|
2427
|
+
.where(
|
|
2428
|
+
sql<boolean>`NOT EXISTS (
|
|
2429
|
+
SELECT 1
|
|
2430
|
+
FROM _emdash_media_usage_collection_deletions AS deletion
|
|
2431
|
+
WHERE deletion.collection_id = ${input.collectionId}
|
|
2432
|
+
)`,
|
|
2433
|
+
)
|
|
2434
|
+
.executeTakeFirst();
|
|
2435
|
+
return Number(result.numUpdatedRows ?? 0) > 0
|
|
2436
|
+
? { outcome: "marked", marker }
|
|
2437
|
+
: { outcome: "lost" };
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
async recoverIncrementalFinalizations(): Promise<number> {
|
|
2441
|
+
const markerMatchesEpoch = isPostgres(this.db)
|
|
2442
|
+
? sql<boolean>`status.cursor LIKE ('incremental-finalize:' || status.change_epoch::text || ':%')`
|
|
2443
|
+
: sql<boolean>`status.cursor LIKE ('incremental-finalize:' || CAST(status.change_epoch AS text) || ':%')`;
|
|
2444
|
+
const now = new Date().toISOString();
|
|
2445
|
+
const result = await sql<{ collection_id: string }>`
|
|
2446
|
+
UPDATE _emdash_media_usage_index_status AS status
|
|
2447
|
+
SET status = 'complete',
|
|
2448
|
+
completed_at = ${now},
|
|
2449
|
+
cursor = NULL,
|
|
2450
|
+
last_error_code = NULL,
|
|
2451
|
+
last_incremental_success_at = ${now},
|
|
2452
|
+
updated_at = ${now}
|
|
2453
|
+
WHERE status.adapter_id = 'content-media'
|
|
2454
|
+
AND status.scope_type = 'collection'
|
|
2455
|
+
AND status.capture_state = 'active'
|
|
2456
|
+
AND status.reconciliation_required = 0
|
|
2457
|
+
AND status.cursor IS NOT NULL
|
|
2458
|
+
AND ${markerMatchesEpoch}
|
|
2459
|
+
AND EXISTS (
|
|
2460
|
+
SELECT 1
|
|
2461
|
+
FROM _emdash_collections AS collection
|
|
2462
|
+
WHERE collection.id = status.collection_id
|
|
2463
|
+
AND collection.slug = status.scope_key
|
|
2464
|
+
)
|
|
2465
|
+
AND NOT EXISTS (
|
|
2466
|
+
SELECT 1
|
|
2467
|
+
FROM _emdash_media_usage_collection_deletions AS deletion
|
|
2468
|
+
WHERE deletion.collection_id = status.collection_id
|
|
2469
|
+
)
|
|
2470
|
+
AND NOT EXISTS (
|
|
2471
|
+
SELECT 1
|
|
2472
|
+
FROM _emdash_media_usage_work AS work
|
|
2473
|
+
WHERE work.collection_id = status.collection_id
|
|
2474
|
+
)
|
|
2475
|
+
RETURNING collection_id
|
|
2476
|
+
`.execute(this.db);
|
|
2477
|
+
return result.rows.length;
|
|
2478
|
+
}
|
|
2479
|
+
|
|
1718
2480
|
async recordIncrementalFailure(
|
|
1719
2481
|
input: MediaUsageIncrementalStatusIdentity & {
|
|
1720
2482
|
contentId: string;
|
|
@@ -1778,6 +2540,71 @@ export class MediaUsageRepository {
|
|
|
1778
2540
|
return Number(result.numUpdatedRows ?? 0) > 0;
|
|
1779
2541
|
}
|
|
1780
2542
|
|
|
2543
|
+
async recordIncrementalFailuresByCollection(input: {
|
|
2544
|
+
collectionIds: readonly string[];
|
|
2545
|
+
errorCode: string;
|
|
2546
|
+
}): Promise<Set<string>> {
|
|
2547
|
+
const collectionIds = [...new Set(input.collectionIds)];
|
|
2548
|
+
if (collectionIds.length === 0) return new Set();
|
|
2549
|
+
const collections = jsonTextValues(this.db, collectionIds);
|
|
2550
|
+
const now = new Date().toISOString();
|
|
2551
|
+
const result = await sql<{ collection_id: string }>`
|
|
2552
|
+
WITH failed_collections AS (${collections})
|
|
2553
|
+
UPDATE _emdash_media_usage_index_status AS status
|
|
2554
|
+
SET status = CASE
|
|
2555
|
+
WHEN EXISTS (
|
|
2556
|
+
SELECT 1
|
|
2557
|
+
FROM _emdash_media_usage_reconciliations AS reconciliation
|
|
2558
|
+
WHERE reconciliation.collection_id = status.collection_id
|
|
2559
|
+
AND reconciliation.run_token = status.cursor
|
|
2560
|
+
) THEN status.status
|
|
2561
|
+
WHEN status.reconciliation_required = 0 THEN 'partial'
|
|
2562
|
+
WHEN status.status = 'running' THEN 'stale'
|
|
2563
|
+
ELSE status.status
|
|
2564
|
+
END,
|
|
2565
|
+
completed_at = CASE
|
|
2566
|
+
WHEN EXISTS (
|
|
2567
|
+
SELECT 1
|
|
2568
|
+
FROM _emdash_media_usage_reconciliations AS reconciliation
|
|
2569
|
+
WHERE reconciliation.collection_id = status.collection_id
|
|
2570
|
+
AND reconciliation.run_token = status.cursor
|
|
2571
|
+
) THEN status.completed_at
|
|
2572
|
+
WHEN status.reconciliation_required = 0 OR status.status = 'running' THEN NULL
|
|
2573
|
+
ELSE status.completed_at
|
|
2574
|
+
END,
|
|
2575
|
+
cursor = CASE
|
|
2576
|
+
WHEN EXISTS (
|
|
2577
|
+
SELECT 1
|
|
2578
|
+
FROM _emdash_media_usage_reconciliations AS reconciliation
|
|
2579
|
+
WHERE reconciliation.collection_id = status.collection_id
|
|
2580
|
+
AND reconciliation.run_token = status.cursor
|
|
2581
|
+
) THEN status.cursor
|
|
2582
|
+
WHEN status.status = 'running' THEN NULL
|
|
2583
|
+
ELSE status.cursor
|
|
2584
|
+
END,
|
|
2585
|
+
last_error_code = ${input.errorCode},
|
|
2586
|
+
updated_at = ${now}
|
|
2587
|
+
WHERE status.adapter_id = 'content-media'
|
|
2588
|
+
AND status.scope_type = 'collection'
|
|
2589
|
+
AND status.collection_id IN (SELECT value FROM failed_collections)
|
|
2590
|
+
AND EXISTS (
|
|
2591
|
+
SELECT 1
|
|
2592
|
+
FROM _emdash_media_usage_work AS work
|
|
2593
|
+
WHERE work.collection_id = status.collection_id
|
|
2594
|
+
AND work.state = 'failed'
|
|
2595
|
+
AND work.last_error_code = ${input.errorCode}
|
|
2596
|
+
)
|
|
2597
|
+
AND EXISTS (
|
|
2598
|
+
SELECT 1
|
|
2599
|
+
FROM _emdash_collections AS collection
|
|
2600
|
+
WHERE collection.id = status.collection_id
|
|
2601
|
+
AND collection.slug = status.scope_key
|
|
2602
|
+
)
|
|
2603
|
+
RETURNING collection_id
|
|
2604
|
+
`.execute(this.db);
|
|
2605
|
+
return new Set(result.rows.map((row) => row.collection_id));
|
|
2606
|
+
}
|
|
2607
|
+
|
|
1781
2608
|
async findIndexStatus(
|
|
1782
2609
|
identity: MediaUsageIndexStatusIdentity,
|
|
1783
2610
|
): Promise<MediaUsageIndexStatus | null> {
|
|
@@ -2071,6 +2898,43 @@ export class MediaUsageRepository {
|
|
|
2071
2898
|
)`;
|
|
2072
2899
|
}
|
|
2073
2900
|
|
|
2901
|
+
/**
|
|
2902
|
+
* Gates a bounded cleanup scan on the lease and the tick's statement budget.
|
|
2903
|
+
*
|
|
2904
|
+
* The budget is re-read after the lease row because that read is itself a
|
|
2905
|
+
* statement: an isolate suspended across it can resume with the budget spent.
|
|
2906
|
+
*/
|
|
2907
|
+
private async admitCleanupScan(
|
|
2908
|
+
cleanupLease: MediaUsageCleanupLease | undefined,
|
|
2909
|
+
canIssueStatement: (() => boolean) | undefined,
|
|
2910
|
+
): Promise<boolean> {
|
|
2911
|
+
if (!canIssueCleanupStatement(canIssueStatement)) return false;
|
|
2912
|
+
if (!cleanupLease) return true;
|
|
2913
|
+
return (
|
|
2914
|
+
(await this.holdsCleanupLease(cleanupLease)) && canIssueCleanupStatement(canIssueStatement)
|
|
2915
|
+
);
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
/**
|
|
2919
|
+
* Reads the lease row on its own so a lost lease can short-circuit a sweep.
|
|
2920
|
+
*
|
|
2921
|
+
* As a row-level `EXISTS` inside a `LIMIT`ed scan this predicate is constant
|
|
2922
|
+
* across every candidate row: when it is false the limit is unreachable and
|
|
2923
|
+
* the scan reads the whole `created_at` range before returning nothing.
|
|
2924
|
+
* Mutating statements still carry {@link activeCleanupLeaseExpression},
|
|
2925
|
+
* which is what actually fences a displaced owner.
|
|
2926
|
+
*/
|
|
2927
|
+
private async holdsCleanupLease(cleanupLease: MediaUsageCleanupLease): Promise<boolean> {
|
|
2928
|
+
let query = this.db
|
|
2929
|
+
.selectFrom("_emdash_media_usage_cleanup")
|
|
2930
|
+
.select("task_key")
|
|
2931
|
+
.where("task_key", "=", "projection_gc")
|
|
2932
|
+
.where("lease_token", "=", cleanupLease.leaseToken)
|
|
2933
|
+
.where(this.cleanupLeaseExpiryIsInFuture("_emdash_media_usage_cleanup.lease_expires_at"));
|
|
2934
|
+
if (isPostgres(this.db)) query = query.forUpdate();
|
|
2935
|
+
return (await query.executeTakeFirst()) !== undefined;
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2074
2938
|
private activeCleanupLeaseExpression(cleanupLease: MediaUsageCleanupLease) {
|
|
2075
2939
|
const rowLock = isPostgres(this.db) ? sql` FOR UPDATE` : sql``;
|
|
2076
2940
|
return sql<boolean>`EXISTS (
|
|
@@ -2204,6 +3068,199 @@ export class MediaUsageRepository {
|
|
|
2204
3068
|
}
|
|
2205
3069
|
}
|
|
2206
3070
|
|
|
3071
|
+
private async insertBatchOccurrences(rows: readonly BatchOccurrenceRow[]): Promise<void> {
|
|
3072
|
+
for (const rowBatch of chunkJsonRows(rows)) {
|
|
3073
|
+
const input = this.occurrenceBatchInput(JSON.stringify(rowBatch));
|
|
3074
|
+
await sql`
|
|
3075
|
+
WITH input AS (${input})
|
|
3076
|
+
INSERT INTO _emdash_media_usage (
|
|
3077
|
+
id, source_key, generation, field_slug, field_path, occurrence_index,
|
|
3078
|
+
reference_type, media_id, provider, provider_asset_id, media_kind,
|
|
3079
|
+
mime_type, created_at
|
|
3080
|
+
)
|
|
3081
|
+
SELECT
|
|
3082
|
+
id, source_key, generation, field_slug, field_path, occurrence_index,
|
|
3083
|
+
reference_type, media_id, provider, provider_asset_id, media_kind,
|
|
3084
|
+
mime_type, created_at
|
|
3085
|
+
FROM input
|
|
3086
|
+
WHERE EXISTS (
|
|
3087
|
+
SELECT 1
|
|
3088
|
+
FROM _emdash_media_usage_generation_writes AS writer
|
|
3089
|
+
WHERE writer.source_key = input.source_key
|
|
3090
|
+
AND writer.generation = input.generation
|
|
3091
|
+
AND writer.lease_token = input.lease_token
|
|
3092
|
+
AND ${this.generationWriteLeaseExpiryIsInFuture("writer.expires_at")}
|
|
3093
|
+
)
|
|
3094
|
+
`.execute(this.db);
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
private occurrenceBatchInput(payload: string): RawBuilder<unknown> {
|
|
3099
|
+
if (isPostgres(this.db)) {
|
|
3100
|
+
return sql`
|
|
3101
|
+
SELECT
|
|
3102
|
+
entry.value ->> 'id' AS id,
|
|
3103
|
+
entry.value ->> 'source_key' AS source_key,
|
|
3104
|
+
entry.value ->> 'generation' AS generation,
|
|
3105
|
+
entry.value ->> 'field_slug' AS field_slug,
|
|
3106
|
+
entry.value ->> 'field_path' AS field_path,
|
|
3107
|
+
CAST(entry.value ->> 'occurrence_index' AS integer) AS occurrence_index,
|
|
3108
|
+
entry.value ->> 'reference_type' AS reference_type,
|
|
3109
|
+
entry.value ->> 'media_id' AS media_id,
|
|
3110
|
+
entry.value ->> 'provider' AS provider,
|
|
3111
|
+
entry.value ->> 'provider_asset_id' AS provider_asset_id,
|
|
3112
|
+
entry.value ->> 'media_kind' AS media_kind,
|
|
3113
|
+
entry.value ->> 'mime_type' AS mime_type,
|
|
3114
|
+
entry.value ->> 'created_at' AS created_at,
|
|
3115
|
+
entry.value ->> 'lease_token' AS lease_token
|
|
3116
|
+
FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
|
|
3117
|
+
`;
|
|
3118
|
+
}
|
|
3119
|
+
return sql`
|
|
3120
|
+
SELECT
|
|
3121
|
+
json_extract(entry.value, '$.id') AS id,
|
|
3122
|
+
json_extract(entry.value, '$.source_key') AS source_key,
|
|
3123
|
+
json_extract(entry.value, '$.generation') AS generation,
|
|
3124
|
+
json_extract(entry.value, '$.field_slug') AS field_slug,
|
|
3125
|
+
json_extract(entry.value, '$.field_path') AS field_path,
|
|
3126
|
+
CAST(json_extract(entry.value, '$.occurrence_index') AS integer) AS occurrence_index,
|
|
3127
|
+
json_extract(entry.value, '$.reference_type') AS reference_type,
|
|
3128
|
+
json_extract(entry.value, '$.media_id') AS media_id,
|
|
3129
|
+
json_extract(entry.value, '$.provider') AS provider,
|
|
3130
|
+
json_extract(entry.value, '$.provider_asset_id') AS provider_asset_id,
|
|
3131
|
+
json_extract(entry.value, '$.media_kind') AS media_kind,
|
|
3132
|
+
json_extract(entry.value, '$.mime_type') AS mime_type,
|
|
3133
|
+
json_extract(entry.value, '$.created_at') AS created_at,
|
|
3134
|
+
json_extract(entry.value, '$.lease_token') AS lease_token
|
|
3135
|
+
FROM json_each(${payload}) AS entry
|
|
3136
|
+
`;
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
private sourceBatchInput(payload: string): RawBuilder<unknown> {
|
|
3140
|
+
if (isPostgres(this.db)) {
|
|
3141
|
+
return sql`
|
|
3142
|
+
SELECT
|
|
3143
|
+
entry.value ->> 'source_key' AS source_key,
|
|
3144
|
+
entry.value ->> 'source_type' AS source_type,
|
|
3145
|
+
entry.value ->> 'collection_id' AS collection_id,
|
|
3146
|
+
entry.value ->> 'collection_slug' AS collection_slug,
|
|
3147
|
+
entry.value ->> 'content_id' AS content_id,
|
|
3148
|
+
entry.value ->> 'source_variant' AS source_variant,
|
|
3149
|
+
entry.value ->> 'locale' AS locale,
|
|
3150
|
+
entry.value ->> 'translation_group' AS translation_group,
|
|
3151
|
+
entry.value ->> 'content_slug' AS content_slug,
|
|
3152
|
+
entry.value ->> 'content_title' AS content_title,
|
|
3153
|
+
entry.value ->> 'content_status' AS content_status,
|
|
3154
|
+
entry.value ->> 'content_scheduled_at' AS content_scheduled_at,
|
|
3155
|
+
entry.value ->> 'content_deleted_at' AS content_deleted_at,
|
|
3156
|
+
entry.value ->> 'revision_id' AS revision_id,
|
|
3157
|
+
entry.value ->> 'current_generation' AS current_generation,
|
|
3158
|
+
CAST(entry.value ->> 'schema_version' AS integer) AS schema_version,
|
|
3159
|
+
entry.value ->> 'source_updated_at' AS source_updated_at,
|
|
3160
|
+
CAST(entry.value ->> 'source_version' AS bigint) AS source_version,
|
|
3161
|
+
entry.value ->> 'source_fingerprint' AS source_fingerprint,
|
|
3162
|
+
CAST(entry.value ->> 'identity_version' AS integer) AS identity_version,
|
|
3163
|
+
entry.value ->> 'source_completeness' AS source_completeness,
|
|
3164
|
+
entry.value ->> 'last_attempted_at' AS last_attempted_at,
|
|
3165
|
+
entry.value ->> 'last_error_code' AS last_error_code,
|
|
3166
|
+
entry.value ->> 'indexed_at' AS indexed_at,
|
|
3167
|
+
entry.value ->> 'updated_at' AS updated_at,
|
|
3168
|
+
entry.value ->> 'lease_token' AS lease_token,
|
|
3169
|
+
entry.value ->> 'expected_generation' AS expected_generation,
|
|
3170
|
+
entry.value ->> 'expected_collection_id' AS expected_collection_id,
|
|
3171
|
+
entry.value ->> 'expected_updated_at' AS expected_updated_at,
|
|
3172
|
+
entry.value ->> 'expected_source_fingerprint' AS expected_source_fingerprint,
|
|
3173
|
+
entry.value ->> 'expected_source_updated_at' AS expected_source_updated_at,
|
|
3174
|
+
CAST(entry.value ->> 'expected_source_version' AS bigint) AS expected_source_version,
|
|
3175
|
+
CAST(entry.value ->> 'expected_identity_version' AS integer) AS expected_identity_version,
|
|
3176
|
+
entry.value ->> 'expected_revision_id' AS expected_revision_id,
|
|
3177
|
+
entry.value ->> 'expected_source_completeness' AS expected_source_completeness,
|
|
3178
|
+
entry.value ->> 'expected_last_attempted_at' AS expected_last_attempted_at,
|
|
3179
|
+
entry.value ->> 'expected_last_error_code' AS expected_last_error_code
|
|
3180
|
+
FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
|
|
3181
|
+
`;
|
|
3182
|
+
}
|
|
3183
|
+
return sql`
|
|
3184
|
+
SELECT
|
|
3185
|
+
json_extract(entry.value, '$.source_key') AS source_key,
|
|
3186
|
+
json_extract(entry.value, '$.source_type') AS source_type,
|
|
3187
|
+
json_extract(entry.value, '$.collection_id') AS collection_id,
|
|
3188
|
+
json_extract(entry.value, '$.collection_slug') AS collection_slug,
|
|
3189
|
+
json_extract(entry.value, '$.content_id') AS content_id,
|
|
3190
|
+
json_extract(entry.value, '$.source_variant') AS source_variant,
|
|
3191
|
+
json_extract(entry.value, '$.locale') AS locale,
|
|
3192
|
+
json_extract(entry.value, '$.translation_group') AS translation_group,
|
|
3193
|
+
json_extract(entry.value, '$.content_slug') AS content_slug,
|
|
3194
|
+
json_extract(entry.value, '$.content_title') AS content_title,
|
|
3195
|
+
json_extract(entry.value, '$.content_status') AS content_status,
|
|
3196
|
+
json_extract(entry.value, '$.content_scheduled_at') AS content_scheduled_at,
|
|
3197
|
+
json_extract(entry.value, '$.content_deleted_at') AS content_deleted_at,
|
|
3198
|
+
json_extract(entry.value, '$.revision_id') AS revision_id,
|
|
3199
|
+
json_extract(entry.value, '$.current_generation') AS current_generation,
|
|
3200
|
+
CAST(json_extract(entry.value, '$.schema_version') AS integer) AS schema_version,
|
|
3201
|
+
json_extract(entry.value, '$.source_updated_at') AS source_updated_at,
|
|
3202
|
+
CAST(json_extract(entry.value, '$.source_version') AS integer) AS source_version,
|
|
3203
|
+
json_extract(entry.value, '$.source_fingerprint') AS source_fingerprint,
|
|
3204
|
+
CAST(json_extract(entry.value, '$.identity_version') AS integer) AS identity_version,
|
|
3205
|
+
json_extract(entry.value, '$.source_completeness') AS source_completeness,
|
|
3206
|
+
json_extract(entry.value, '$.last_attempted_at') AS last_attempted_at,
|
|
3207
|
+
json_extract(entry.value, '$.last_error_code') AS last_error_code,
|
|
3208
|
+
json_extract(entry.value, '$.indexed_at') AS indexed_at,
|
|
3209
|
+
json_extract(entry.value, '$.updated_at') AS updated_at,
|
|
3210
|
+
json_extract(entry.value, '$.lease_token') AS lease_token,
|
|
3211
|
+
json_extract(entry.value, '$.expected_generation') AS expected_generation,
|
|
3212
|
+
json_extract(entry.value, '$.expected_collection_id') AS expected_collection_id,
|
|
3213
|
+
json_extract(entry.value, '$.expected_updated_at') AS expected_updated_at,
|
|
3214
|
+
json_extract(entry.value, '$.expected_source_fingerprint') AS expected_source_fingerprint,
|
|
3215
|
+
json_extract(entry.value, '$.expected_source_updated_at') AS expected_source_updated_at,
|
|
3216
|
+
CAST(json_extract(entry.value, '$.expected_source_version') AS integer) AS expected_source_version,
|
|
3217
|
+
CAST(json_extract(entry.value, '$.expected_identity_version') AS integer) AS expected_identity_version,
|
|
3218
|
+
json_extract(entry.value, '$.expected_revision_id') AS expected_revision_id,
|
|
3219
|
+
json_extract(entry.value, '$.expected_source_completeness') AS expected_source_completeness,
|
|
3220
|
+
json_extract(entry.value, '$.expected_last_attempted_at') AS expected_last_attempted_at,
|
|
3221
|
+
json_extract(entry.value, '$.expected_last_error_code') AS expected_last_error_code
|
|
3222
|
+
FROM json_each(${payload}) AS entry
|
|
3223
|
+
`;
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
private generationWriteBatchInput(payload: string): RawBuilder<unknown> {
|
|
3227
|
+
if (isPostgres(this.db)) {
|
|
3228
|
+
return sql`
|
|
3229
|
+
SELECT
|
|
3230
|
+
entry.value ->> 'source_key' AS source_key,
|
|
3231
|
+
entry.value ->> 'collection_id' AS collection_id,
|
|
3232
|
+
entry.value ->> 'collection_slug' AS collection_slug,
|
|
3233
|
+
entry.value ->> 'generation' AS generation,
|
|
3234
|
+
entry.value ->> 'lease_token' AS lease_token,
|
|
3235
|
+
entry.value ->> 'expires_at' AS expires_at,
|
|
3236
|
+
entry.value ->> 'created_at' AS created_at
|
|
3237
|
+
FROM jsonb_array_elements(${payload}::jsonb) AS entry(value)
|
|
3238
|
+
`;
|
|
3239
|
+
}
|
|
3240
|
+
return sql`
|
|
3241
|
+
SELECT
|
|
3242
|
+
json_extract(entry.value, '$.source_key') AS source_key,
|
|
3243
|
+
json_extract(entry.value, '$.collection_id') AS collection_id,
|
|
3244
|
+
json_extract(entry.value, '$.collection_slug') AS collection_slug,
|
|
3245
|
+
json_extract(entry.value, '$.generation') AS generation,
|
|
3246
|
+
json_extract(entry.value, '$.lease_token') AS lease_token,
|
|
3247
|
+
json_extract(entry.value, '$.expires_at') AS expires_at,
|
|
3248
|
+
json_extract(entry.value, '$.created_at') AS created_at
|
|
3249
|
+
FROM json_each(${payload}) AS entry
|
|
3250
|
+
`;
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
private batchRefsMatch(
|
|
3254
|
+
left: BatchSourceExpectedColumn,
|
|
3255
|
+
right: BatchInputExpectedColumn,
|
|
3256
|
+
): RawBuilder<boolean> {
|
|
3257
|
+
const leftRef = sql.ref(left);
|
|
3258
|
+
const rightRef = sql.ref(right);
|
|
3259
|
+
return isPostgres(this.db)
|
|
3260
|
+
? sql<boolean>`${leftRef} IS NOT DISTINCT FROM ${rightRef}`
|
|
3261
|
+
: sql<boolean>`${leftRef} IS ${rightRef}`;
|
|
3262
|
+
}
|
|
3263
|
+
|
|
2207
3264
|
private async lockCanonicalSourceCollection(
|
|
2208
3265
|
db: DatabaseExecutor,
|
|
2209
3266
|
source: MediaUsageSourceInput,
|