emdash 0.27.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-u037EnTR.d.mts → adapters-hUy8vOB-.d.mts} +1 -1
- package/dist/{adapters-u037EnTR.d.mts.map → adapters-hUy8vOB-.d.mts.map} +1 -1
- package/dist/{allowed-origins-D5FxMUo8.mjs → allowed-origins-CRsJ6BPM.mjs} +2 -2
- package/dist/{allowed-origins-D5FxMUo8.mjs.map → allowed-origins-CRsJ6BPM.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +17 -16
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-80kycR9o.mjs → api-DxFSorgk.mjs} +33 -23
- package/dist/api-DxFSorgk.mjs.map +1 -0
- package/dist/{api-tokens-D6ppjIVi.mjs → api-tokens-DvqeZmVq.mjs} +2 -2
- package/dist/{api-tokens-D6ppjIVi.mjs.map → api-tokens-DvqeZmVq.mjs.map} +1 -1
- package/dist/{apply-DSypk6Iu.mjs → apply-CDJqIjPU.mjs} +111 -74
- package/dist/apply-CDJqIjPU.mjs.map +1 -0
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.mjs +3 -3
- 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 +50 -11
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.mjs +5 -5
- package/dist/astro/middleware/request-context.mjs +2 -2
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +2 -2
- package/dist/astro/middleware.mjs +124 -71
- 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 +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +8 -8
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +14 -14
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +14 -14
- package/dist/astro/routes/api/admin/bylines/index.mjs +14 -14
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +6 -6
- 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/oauth-clients/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +37 -36
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +37 -36
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +36 -35
- 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 +36 -35
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +37 -36
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +37 -36
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +36 -35
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +36 -35
- 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 +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +36 -35
- 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 +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
- 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 +5 -5
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
- package/dist/astro/routes/api/auth/invite/register-options.mjs +9 -9
- package/dist/astro/routes/api/auth/logout.mjs +3 -3
- package/dist/astro/routes/api/auth/magic-link/send.mjs +9 -9
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +7 -7
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +9 -9
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/request.mjs +9 -9
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +13 -13
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +12 -12
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +10 -10
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +13 -13
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/index.mjs +7 -7
- 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 +3 -3
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +19 -11
- package/dist/astro/routes/api/import/probe.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -41
- package/dist/astro/routes/api/import/wordpress/execute.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/execute.mjs +12 -407
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
- package/dist/astro/routes/api/import/wordpress/media.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +19 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +97 -79
- 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 +19 -11
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +40 -2
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +519 -39
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/mcp.mjs +39 -39
- package/dist/astro/routes/api/media/_id_/confirm.mjs +7 -7
- package/dist/astro/routes/api/media/_id_.mjs +7 -7
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.mjs +8 -8
- package/dist/astro/routes/api/media.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/items.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_/translations.mjs +9 -9
- package/dist/astro/routes/api/menus/_name_.mjs +9 -9
- package/dist/astro/routes/api/menus/index.mjs +9 -9
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
- package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
- package/dist/astro/routes/api/oauth/register.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +4 -4
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +6 -14
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/404s/index.mjs +9 -9
- package/dist/astro/routes/api/redirects/404s/summary.mjs +9 -9
- package/dist/astro/routes/api/redirects/_id_.mjs +10 -10
- package/dist/astro/routes/api/redirects/index.mjs +10 -10
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +36 -35
- 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 +36 -35
- 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 +36 -35
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +36 -35
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +36 -35
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +7 -6
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +36 -35
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +36 -35
- 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 +6 -6
- 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/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +12 -12
- package/dist/astro/routes/api/setup/admin-verify.mjs +11 -11
- package/dist/astro/routes/api/setup/admin.mjs +10 -10
- package/dist/astro/routes/api/setup/dev-bypass.mjs +25 -24
- 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 +26 -25
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +4 -4
- package/dist/astro/routes/api/snapshot.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/index.mjs +13 -13
- package/dist/astro/routes/api/themes/preview.mjs +5 -5
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +9 -9
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +9 -9
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +9 -9
- package/dist/astro/routes/api/widget-components.mjs +3 -3
- package/dist/astro/routes/robots.txt.mjs +6 -6
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +9 -9
- package/dist/astro/routes/sitemap.xml.mjs +7 -7
- package/dist/astro/types.d.mts +13 -13
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-C3a_zGsM.mjs → authorize-Dz88F_Fw.mjs} +2 -2
- package/dist/{authorize-C3a_zGsM.mjs.map → authorize-Dz88F_Fw.mjs.map} +1 -1
- package/dist/{byline-DEKSVPTy.mjs → byline-Bm8ZgUP9.mjs} +6 -6
- package/dist/{byline-DEKSVPTy.mjs.map → byline-Bm8ZgUP9.mjs.map} +1 -1
- package/dist/{byline-fields-eyJMblSr.mjs → byline-fields-2Lp5Hbgf.mjs} +18 -3
- package/dist/byline-fields-2Lp5Hbgf.mjs.map +1 -0
- package/dist/{byline-fields-CtQ6blXd.d.mts → byline-fields-CHE8UStY.d.mts} +47 -32
- package/dist/{byline-fields-CtQ6blXd.d.mts.map → byline-fields-CHE8UStY.d.mts.map} +1 -1
- package/dist/{byline-fields-DaMKzkhO.mjs → byline-fields-CIx_GuN4.mjs} +1 -1
- package/dist/{byline-fields-DaMKzkhO.mjs.map → byline-fields-CIx_GuN4.mjs.map} +1 -1
- package/dist/{bylines-Ds4-otXl.mjs → bylines-6tm9mgQb.mjs} +4 -4
- package/dist/{bylines-Ds4-otXl.mjs.map → bylines-6tm9mgQb.mjs.map} +1 -1
- package/dist/{bylines-CiqgTHWF.mjs → bylines-CAZxGSrm.mjs} +6 -6
- package/dist/{bylines-CiqgTHWF.mjs.map → bylines-CAZxGSrm.mjs.map} +1 -1
- package/dist/{cache-Ciu3e3AF.mjs → cache-CEBAZjir.mjs} +2 -2
- package/dist/{cache-Ciu3e3AF.mjs.map → cache-CEBAZjir.mjs.map} +1 -1
- package/dist/{challenge-store-LhiqMccz.mjs → challenge-store-anhwVER0.mjs} +1 -1
- package/dist/{challenge-store-LhiqMccz.mjs.map → challenge-store-anhwVER0.mjs.map} +1 -1
- package/dist/{chunks-CZFiauig.mjs → chunks-B2isWQhr.mjs} +2 -2
- package/dist/{chunks-CZFiauig.mjs.map → chunks-B2isWQhr.mjs.map} +1 -1
- package/dist/cli/index.mjs +22 -21
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -2
- package/dist/{comment-CH1IrFFs.mjs → comment-CzeatwBV.mjs} +3 -3
- package/dist/{comment-CH1IrFFs.mjs.map → comment-CzeatwBV.mjs.map} +1 -1
- package/dist/{comment-reaction-kruNY-kP.mjs → comment-reaction-Cj9QJY7I.mjs} +2 -2
- package/dist/{comment-reaction-kruNY-kP.mjs.map → comment-reaction-Cj9QJY7I.mjs.map} +1 -1
- package/dist/{comments-BUJyXbgq.mjs → comments-BLE-jv5k.mjs} +3 -3
- package/dist/{comments-BUJyXbgq.mjs.map → comments-BLE-jv5k.mjs.map} +1 -1
- package/dist/{components-CYt4uVK9.mjs → components-B_sATNUc.mjs} +1 -1
- package/dist/{components-CYt4uVK9.mjs.map → components-B_sATNUc.mjs.map} +1 -1
- package/dist/{content-RmfHoWqK.mjs → content-DhgEth4k.mjs} +4 -4
- package/dist/{content-RmfHoWqK.mjs.map → content-DhgEth4k.mjs.map} +1 -1
- package/dist/content-refresh-Br4gMzh8.mjs +1537 -0
- package/dist/content-refresh-Br4gMzh8.mjs.map +1 -0
- package/dist/{context-CzUU_WzL.mjs → context-BRIpHARC.mjs} +76 -54
- package/dist/context-BRIpHARC.mjs.map +1 -0
- package/dist/{cron-C5LVoNmP.mjs → cron-v4FTEFR_.mjs} +1 -1
- package/dist/{cron-C5LVoNmP.mjs.map → cron-v4FTEFR_.mjs.map} +1 -1
- package/dist/{dashboard-CVyGhift.mjs → dashboard-EvEKF5en.mjs} +4 -4
- package/dist/{dashboard-CVyGhift.mjs.map → dashboard-EvEKF5en.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{default-CeRG-Ot4.mjs → default-CM_D83Mg.mjs} +1 -1
- package/dist/{default-CeRG-Ot4.mjs.map → default-CM_D83Mg.mjs.map} +1 -1
- package/dist/{device-flow-DENDCQ9F.mjs → device-flow-CfGP18Uw.mjs} +4 -4
- package/dist/{device-flow-DENDCQ9F.mjs.map → device-flow-CfGP18Uw.mjs.map} +1 -1
- package/dist/{email-console-DJP32ucW.mjs → email-console-CIf4Lfvv.mjs} +1 -1
- package/dist/{email-console-DJP32ucW.mjs.map → email-console-CIf4Lfvv.mjs.map} +1 -1
- package/dist/{error-BHkLZk-w.mjs → error-CkAseSqZ.mjs} +2 -2
- package/dist/{error-BHkLZk-w.mjs.map → error-CkAseSqZ.mjs.map} +1 -1
- package/dist/{escape-Bjio4ZsM.mjs → escape-DqD0aCKu.mjs} +1 -1
- package/dist/{escape-Bjio4ZsM.mjs.map → escape-DqD0aCKu.mjs.map} +1 -1
- package/dist/{field-defs-cache-BA-8ZPiT.mjs → field-defs-cache-WUI88LX5.mjs} +2 -2
- package/dist/{field-defs-cache-BA-8ZPiT.mjs.map → field-defs-cache-WUI88LX5.mjs.map} +1 -1
- package/dist/{fts-manager-Db9jGavf.mjs → fts-manager-BbFaF_V8.mjs} +2 -2
- package/dist/{fts-manager-Db9jGavf.mjs.map → fts-manager-BbFaF_V8.mjs.map} +1 -1
- package/dist/{import-Bb1T9WJS.mjs → import-fYLf14Co.mjs} +514 -25
- package/dist/import-fYLf14Co.mjs.map +1 -0
- package/dist/{index-CDZZjrLT.d.mts → index-B9sIxwe4.d.mts} +21 -22
- package/dist/{index-CDZZjrLT.d.mts.map → index-B9sIxwe4.d.mts.map} +1 -1
- package/dist/{index-DGIjmUXQ.d.mts → index-Bm-Gy8Mh.d.mts} +25 -4
- package/dist/index-Bm-Gy8Mh.d.mts.map +1 -0
- package/dist/index.d.mts +18 -18
- package/dist/index.mjs +54 -53
- package/dist/{load-DpHICVPh.mjs → load-Djn3HWQS.mjs} +2 -2
- package/dist/{load-DpHICVPh.mjs.map → load-Djn3HWQS.mjs.map} +1 -1
- package/dist/{loader-4K37qA-y.mjs → loader-CB6YHeul.mjs} +6 -5
- package/dist/loader-CB6YHeul.mjs.map +1 -0
- package/dist/{manifest-schema-kPGX7VS-.mjs → manifest-schema-BHlgZM1W.mjs} +1 -1
- package/dist/{manifest-schema-kPGX7VS-.mjs.map → manifest-schema-BHlgZM1W.mjs.map} +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/local-runtime.d.mts +12 -12
- package/dist/media/local-runtime.mjs +5 -5
- package/dist/{media-C0nRRfs1.mjs → media-BrXIaMFN.mjs} +2 -2
- package/dist/{media-C0nRRfs1.mjs.map → media-BrXIaMFN.mjs.map} +1 -1
- package/dist/{media-allowlist-Du8t1u6a.mjs → media-allowlist-CRHEeJ_i.mjs} +1 -1
- package/dist/{media-allowlist-Du8t1u6a.mjs.map → media-allowlist-CRHEeJ_i.mjs.map} +1 -1
- package/dist/{media-url-O4rm9-SQ.mjs → media-url-CKi5XSw4.mjs} +1 -1
- package/dist/{media-url-O4rm9-SQ.mjs.map → media-url-CKi5XSw4.mjs.map} +1 -1
- package/dist/{menus-SL2K0xq2.mjs → menus-Cfivc2Ad.mjs} +3 -3
- package/dist/{menus-SL2K0xq2.mjs.map → menus-Cfivc2Ad.mjs.map} +1 -1
- package/dist/{menus-DzRyC1ID.mjs → menus-b2T4clbO.mjs} +16 -12
- package/dist/{menus-DzRyC1ID.mjs.map → menus-b2T4clbO.mjs.map} +1 -1
- package/dist/{mode-BB0F8xTC.mjs → mode-BM9QgEmt.mjs} +1 -1
- package/dist/{mode-BB0F8xTC.mjs.map → mode-BM9QgEmt.mjs.map} +1 -1
- package/dist/{oauth-authorization-CsvzIp_F.mjs → oauth-authorization-CXWkIGT4.mjs} +4 -4
- package/dist/{oauth-authorization-CsvzIp_F.mjs.map → oauth-authorization-CXWkIGT4.mjs.map} +1 -1
- package/dist/{oauth-clients-C9SYwEbZ.mjs → oauth-clients-BmTI5rsl.mjs} +1 -1
- package/dist/{oauth-clients-C9SYwEbZ.mjs.map → oauth-clients-BmTI5rsl.mjs.map} +1 -1
- package/dist/{oauth-state-store---zrApfB.mjs → oauth-state-store-YLFLcH5F.mjs} +1 -1
- package/dist/{oauth-state-store---zrApfB.mjs.map → oauth-state-store-YLFLcH5F.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-SHsWRlG9.mjs → oauth-user-lookup-Bsc_GsX-.mjs} +1 -1
- package/dist/{oauth-user-lookup-SHsWRlG9.mjs.map → oauth-user-lookup-Bsc_GsX-.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{object-cache-CHbHv83-.mjs → object-cache-Bok5j2ae.mjs} +43 -3
- package/dist/object-cache-Bok5j2ae.mjs.map +1 -0
- package/dist/{options-41nCWqi9.d.mts → options-iMpRRxIe.d.mts} +3 -3
- package/dist/{options-41nCWqi9.d.mts.map → options-iMpRRxIe.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-CqyZj3Hp.mjs → parse-GmjecGct.mjs} +2 -2
- package/dist/{parse-CqyZj3Hp.mjs.map → parse-GmjecGct.mjs.map} +1 -1
- package/dist/{passkey-config-C0YfSBko.mjs → passkey-config-De0ahnRg.mjs} +1 -1
- package/dist/{passkey-config-C0YfSBko.mjs.map → passkey-config-De0ahnRg.mjs.map} +1 -1
- package/dist/{placeholder-Cuce9U-m.d.mts → placeholder-BSKwzRe4.d.mts} +1 -1
- package/dist/{placeholder-Cuce9U-m.d.mts.map → placeholder-BSKwzRe4.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{portable-text-BICg8bTk.mjs → portable-text-cCa0cGV6.mjs} +1 -1
- package/dist/{portable-text-BICg8bTk.mjs.map → portable-text-cCa0cGV6.mjs.map} +1 -1
- package/dist/{preview-DKGCt2_p.mjs → preview-SSXNVPAC.mjs} +2 -2
- package/dist/{preview-DKGCt2_p.mjs.map → preview-SSXNVPAC.mjs.map} +1 -1
- package/dist/{public-url-CTVqgMmg.mjs → public-url-Danv47cS.mjs} +1 -1
- package/dist/{public-url-CTVqgMmg.mjs.map → public-url-Danv47cS.mjs.map} +1 -1
- package/dist/{query-B0M-9LYR.mjs → query-1Cp6U9oo.mjs} +14 -14
- package/dist/{query-B0M-9LYR.mjs.map → query-1Cp6U9oo.mjs.map} +1 -1
- package/dist/{rate-limit-BwoUdIpC.mjs → rate-limit-C_Lf63Dd.mjs} +2 -2
- package/dist/{rate-limit-BwoUdIpC.mjs.map → rate-limit-C_Lf63Dd.mjs.map} +1 -1
- package/dist/{redirect-CS-PHtNh.mjs → redirect-CoW3G-0U.mjs} +1 -1
- package/dist/{redirect-CS-PHtNh.mjs.map → redirect-CoW3G-0U.mjs.map} +1 -1
- package/dist/{redirect-lMOzG04S.mjs → redirect-IyfNirgS.mjs} +2 -2
- package/dist/{redirect-lMOzG04S.mjs.map → redirect-IyfNirgS.mjs.map} +1 -1
- package/dist/{redirects-9218MmYl.mjs → redirects-Bs1g42Ri.mjs} +4 -4
- package/dist/{redirects-9218MmYl.mjs.map → redirects-Bs1g42Ri.mjs.map} +1 -1
- package/dist/{redirects-Dcbuisoj.mjs → redirects-DXEXBAII.mjs} +2 -2
- package/dist/{redirects-Dcbuisoj.mjs.map → redirects-DXEXBAII.mjs.map} +1 -1
- package/dist/{registry-DP-iEtdR.mjs → registry-B57vp_X9.mjs} +122 -79
- package/dist/registry-B57vp_X9.mjs.map +1 -0
- package/dist/{request-meta-CmS1tDFf.mjs → request-meta-DbvXaAvU.mjs} +2 -2
- package/dist/{request-meta-CmS1tDFf.mjs.map → request-meta-DbvXaAvU.mjs.map} +1 -1
- package/dist/{resolve-C7I0qiR0.mjs → resolve-DR3wqMTy.mjs} +1 -1
- package/dist/{resolve-C7I0qiR0.mjs.map → resolve-DR3wqMTy.mjs.map} +1 -1
- package/dist/{runner-BbR3DfrL.d.mts → runner-DvFWoz8-.d.mts} +2 -2
- package/dist/{runner-BbR3DfrL.d.mts.map → runner-DvFWoz8-.d.mts.map} +1 -1
- package/dist/{runner-CFngB4WZ.mjs → runner-K8XK101B.mjs} +309 -190
- package/dist/runner-K8XK101B.mjs.map +1 -0
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +2 -2
- package/dist/{scheduled-publish-BMljtY5G.d.mts → scheduled-publish-Drffrf2a.d.mts} +1 -1
- package/dist/scheduled-publish-Drffrf2a.d.mts.map +1 -0
- package/dist/{schema-BfJ7bIXH.mjs → schema-CX9cfNkZ.mjs} +5 -5
- package/dist/{schema-BfJ7bIXH.mjs.map → schema-CX9cfNkZ.mjs.map} +1 -1
- package/dist/{search-C7t3PH1J.mjs → search-DZN2HZ-V.mjs} +4 -4
- package/dist/{search-C7t3PH1J.mjs.map → search-DZN2HZ-V.mjs.map} +1 -1
- package/dist/{secrets-BSf9pRRY.mjs → secrets-kp760LlD.mjs} +2 -2
- package/dist/{secrets-BSf9pRRY.mjs.map → secrets-kp760LlD.mjs.map} +1 -1
- package/dist/{sections-Dm0CVX23.mjs → sections-CpF5SaGr.mjs} +3 -3
- package/dist/{sections-Dm0CVX23.mjs.map → sections-CpF5SaGr.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +19 -18
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/{seo-BkhuuaaE.mjs → seo-89-53DDs.mjs} +1 -1
- package/dist/{seo-BkhuuaaE.mjs.map → seo-89-53DDs.mjs.map} +1 -1
- package/dist/{seo-Dd3tbY_k.mjs → seo-DXu2UQnC.mjs} +3 -3
- package/dist/{seo-Dd3tbY_k.mjs.map → seo-DXu2UQnC.mjs.map} +1 -1
- package/dist/{service-CTrqp_Jy.mjs → service-DQmiBMzt.mjs} +3 -3
- package/dist/{service-CTrqp_Jy.mjs.map → service-DQmiBMzt.mjs.map} +1 -1
- package/dist/{session-user-B8aLtKAH.mjs → session-user-Cascbax7.mjs} +1 -1
- package/dist/{session-user-B8aLtKAH.mjs.map → session-user-Cascbax7.mjs.map} +1 -1
- package/dist/{settings-CZ7ZP9cW.mjs → settings-rZyHEcNT.mjs} +3 -3
- package/dist/{settings-CZ7ZP9cW.mjs.map → settings-rZyHEcNT.mjs.map} +1 -1
- package/dist/{settings-BX8cWBus.mjs → settings-zhmdKyJQ.mjs} +5 -5
- package/dist/{settings-BX8cWBus.mjs.map → settings-zhmdKyJQ.mjs.map} +1 -1
- package/dist/{setup-complete-gEiySUc-.mjs → setup-complete-Cb3LRyzy.mjs} +1 -1
- package/dist/{setup-complete-gEiySUc-.mjs.map → setup-complete-Cb3LRyzy.mjs.map} +1 -1
- package/dist/{setup-nonce-DzS50zme.mjs → setup-nonce-Z8x0tHf8.mjs} +1 -1
- package/dist/{setup-nonce-DzS50zme.mjs.map → setup-nonce-Z8x0tHf8.mjs.map} +1 -1
- package/dist/{single-flight-cache-Cdfkic3t.mjs → single-flight-cache-BG3B_GhN.mjs} +1 -1
- package/dist/{single-flight-cache-Cdfkic3t.mjs.map → single-flight-cache-BG3B_GhN.mjs.map} +1 -1
- package/dist/{site-url-BLebyON8.mjs → site-url-BBfDTgZB.mjs} +1 -1
- package/dist/{site-url-BLebyON8.mjs.map → site-url-BBfDTgZB.mjs.map} +1 -1
- package/dist/{ssrf-BRKb343l.mjs → ssrf-1g7usGBg.mjs} +1 -1
- package/dist/{ssrf-BRKb343l.mjs.map → ssrf-1g7usGBg.mjs.map} +1 -1
- package/dist/{status-vUK0SA17.mjs → status-CqJQX1QN.mjs} +1 -1
- package/dist/{status-vUK0SA17.mjs.map → status-CqJQX1QN.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-DChKcVxf.mjs → taxonomies-Bs13SN-F.mjs} +4 -4
- package/dist/{taxonomies-DChKcVxf.mjs.map → taxonomies-Bs13SN-F.mjs.map} +1 -1
- package/dist/{taxonomies-ckPjz-6d.mjs → taxonomies-kH9ABDUF.mjs} +7 -7
- package/dist/{taxonomies-ckPjz-6d.mjs.map → taxonomies-kH9ABDUF.mjs.map} +1 -1
- package/dist/{taxonomy-Cru-he6s.mjs → taxonomy-BxGxZZdu.mjs} +4 -4
- package/dist/{taxonomy-Cru-he6s.mjs.map → taxonomy-BxGxZZdu.mjs.map} +1 -1
- package/dist/{tokens-LeuXF9gG.mjs → tokens-BGT0TUUo.mjs} +1 -1
- package/dist/{tokens-LeuXF9gG.mjs.map → tokens-BGT0TUUo.mjs.map} +1 -1
- package/dist/{transport-Blrl2k_o.d.mts → transport-BMDWHSgZ.d.mts} +1 -1
- package/dist/{transport-Blrl2k_o.d.mts.map → transport-BMDWHSgZ.d.mts.map} +1 -1
- package/dist/{transport-_2nBz7e9.mjs → transport-DPefPMcz.mjs} +1 -1
- package/dist/{transport-_2nBz7e9.mjs.map → transport-DPefPMcz.mjs.map} +1 -1
- package/dist/{trusted-proxy-DZY5WCn2.mjs → trusted-proxy-VTPRiX2s.mjs} +1 -1
- package/dist/{trusted-proxy-DZY5WCn2.mjs.map → trusted-proxy-VTPRiX2s.mjs.map} +1 -1
- package/dist/{types-ETmO_jQr.d.mts → types-BcLUUbbR.d.mts} +2 -2
- package/dist/{types-ETmO_jQr.d.mts.map → types-BcLUUbbR.d.mts.map} +1 -1
- package/dist/{types-BYnDBVwe.d.mts → types-CGbYowGQ.d.mts} +2 -2
- package/dist/{types-BYnDBVwe.d.mts.map → types-CGbYowGQ.d.mts.map} +1 -1
- package/dist/{types-CvuKO5Pn.d.mts → types-CgvhU-hF.d.mts} +1 -1
- package/dist/{types-CvuKO5Pn.d.mts.map → types-CgvhU-hF.d.mts.map} +1 -1
- package/dist/{types-Y09-wtyU.d.mts → types-ClUkdbWb.d.mts} +1 -1
- package/dist/{types-Y09-wtyU.d.mts.map → types-ClUkdbWb.d.mts.map} +1 -1
- package/dist/{types-DU4TSFS5.mjs → types-CuW0zWDA.mjs} +2 -2
- package/dist/{types-DU4TSFS5.mjs.map → types-CuW0zWDA.mjs.map} +1 -1
- package/dist/{types-BkZ8DUEI.d.mts → types-DmHk7vAt.d.mts} +1 -1
- package/dist/{types-BkZ8DUEI.d.mts.map → types-DmHk7vAt.d.mts.map} +1 -1
- package/dist/{types-CQAugunJ.mjs → types-Jo3UYd6b.mjs} +1 -1
- package/dist/{types-CQAugunJ.mjs.map → types-Jo3UYd6b.mjs.map} +1 -1
- package/dist/{types-xIfVRNLp.d.mts → types-_HFLRzTn.d.mts} +22 -1
- package/dist/types-_HFLRzTn.d.mts.map +1 -0
- package/dist/{types-ByChcBgE.d.mts → types-ea9sAPck.d.mts} +1 -1
- package/dist/{types-ByChcBgE.d.mts.map → types-ea9sAPck.d.mts.map} +1 -1
- package/dist/{types-CNlaBFzx.d.mts → types-g_aFIdW0.d.mts} +1 -1
- package/dist/{types-CNlaBFzx.d.mts.map → types-g_aFIdW0.d.mts.map} +1 -1
- package/dist/{types-Dbqff978.d.mts → types-vgEWF18A.d.mts} +19 -1
- package/dist/{types-Dbqff978.d.mts.map → types-vgEWF18A.d.mts.map} +1 -1
- package/dist/{user-DgGbKL9s.mjs → user-BjAOvU3Z.mjs} +3 -3
- package/dist/{user-DgGbKL9s.mjs.map → user-BjAOvU3Z.mjs.map} +1 -1
- package/dist/{utils-B7A57fm9.mjs → utils-BJXElxdw.mjs} +156 -7
- package/dist/utils-BJXElxdw.mjs.map +1 -0
- package/dist/{validate-CRkcYZAR.mjs → validate-CWSnKVIJ.mjs} +2 -2
- package/dist/{validate-CRkcYZAR.mjs.map → validate-CWSnKVIJ.mjs.map} +1 -1
- package/dist/{validate-9ECmtEpJ.d.mts → validate-Z1b86_3B.d.mts} +5 -5
- package/dist/{validate-9ECmtEpJ.d.mts.map → validate-Z1b86_3B.d.mts.map} +1 -1
- package/dist/{validation-sR26SOwD.mjs → validation-0l8-RYj4.mjs} +8 -5
- package/dist/{validation-sR26SOwD.mjs.map → validation-0l8-RYj4.mjs.map} +1 -1
- package/dist/version-qmeO1XZY.mjs +7 -0
- package/dist/{version-CrqhKGWn.mjs.map → version-qmeO1XZY.mjs.map} +1 -1
- package/dist/{widgets-DRgtn7Cl.mjs → widgets-hWcIicX8.mjs} +3 -3
- package/dist/{widgets-DRgtn7Cl.mjs.map → widgets-hWcIicX8.mjs.map} +1 -1
- package/dist/wxr-taxonomies-0_uxSrmf.d.mts +34 -0
- package/dist/wxr-taxonomies-0_uxSrmf.d.mts.map +1 -0
- package/dist/wxr-taxonomies-CNFcsNCr.mjs +425 -0
- package/dist/wxr-taxonomies-CNFcsNCr.mjs.map +1 -0
- package/dist/{zod-generator-C5JaV--i.mjs → zod-generator-CacrR-i8.mjs} +2 -2
- package/dist/{zod-generator-C5JaV--i.mjs.map → zod-generator-CacrR-i8.mjs.map} +1 -1
- package/package.json +7 -7
- package/src/api/handlers/content.ts +10 -7
- package/src/api/schemas/import.ts +19 -0
- package/src/astro/integration/runtime.ts +3 -6
- package/src/astro/middleware/auth.ts +9 -3
- package/src/astro/middleware/csp.ts +54 -3
- package/src/astro/routes/admin.astro +9 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +122 -98
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +794 -44
- package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +17 -15
- package/src/components/EmDashImage.astro +59 -23
- package/src/components/Image.astro +36 -35
- package/src/database/migrations/049_taxonomies_name_locale_index.ts +42 -0
- package/src/database/migrations/050_media_usage_index_status.ts +142 -0
- package/src/database/migrations/runner.ts +4 -0
- package/src/database/repositories/media-usage.ts +955 -59
- package/src/database/types.ts +22 -0
- package/src/emdash-runtime.ts +123 -3
- package/src/import/comments.ts +159 -0
- package/src/import/index.ts +8 -1
- package/src/import/settings.ts +65 -189
- package/src/import/sources/wordpress-plugin.ts +338 -42
- package/src/import/sources/wxr.ts +4 -1
- package/src/import/types.ts +12 -0
- package/src/import/utils.ts +207 -0
- package/src/import/wxr-taxonomies.ts +39 -0
- package/src/loader.ts +13 -2
- package/src/media/usage/content-fields.ts +119 -0
- package/src/media/usage/content-refresh.ts +524 -0
- package/src/media/usage/content-repair.ts +675 -0
- package/src/media/usage/content-snapshots.ts +439 -0
- package/src/media/usage/extractor.ts +4 -4
- package/src/media/usage/source-key.ts +22 -0
- package/src/menus/index.ts +17 -1
- package/src/object-cache/index.ts +82 -7
- package/src/plugins/context.ts +100 -71
- package/src/schema/registry.ts +240 -154
- package/src/seed/apply.ts +156 -99
- package/dist/api-80kycR9o.mjs.map +0 -1
- package/dist/apply-DSypk6Iu.mjs.map +0 -1
- package/dist/byline-fields-eyJMblSr.mjs.map +0 -1
- package/dist/context-CzUU_WzL.mjs.map +0 -1
- package/dist/import-Bb1T9WJS.mjs.map +0 -1
- package/dist/index-DGIjmUXQ.d.mts.map +0 -1
- package/dist/loader-4K37qA-y.mjs.map +0 -1
- package/dist/object-cache-CHbHv83-.mjs.map +0 -1
- package/dist/registry-DP-iEtdR.mjs.map +0 -1
- package/dist/runner-CFngB4WZ.mjs.map +0 -1
- package/dist/scheduled-publish-BMljtY5G.d.mts.map +0 -1
- package/dist/types-xIfVRNLp.d.mts.map +0 -1
- package/dist/utils-B7A57fm9.mjs.map +0 -1
- package/dist/version-CrqhKGWn.mjs +0 -7
- /package/dist/{api-tokens-B4BQybOp.mjs → api-tokens-DsvvCtdh.mjs} +0 -0
- /package/dist/{ssrf-CcX9zvMK.mjs → ssrf-CmuGgCq3.mjs} +0 -0
- /package/dist/{types-BvbeGEtr.mjs → types-DROWRxYV.mjs} +0 -0
|
@@ -1,14 +1,37 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
sql,
|
|
3
|
+
type ExpressionBuilder,
|
|
4
|
+
type Insertable,
|
|
5
|
+
type Kysely,
|
|
6
|
+
type Selectable,
|
|
7
|
+
type Transaction,
|
|
8
|
+
type Updateable,
|
|
9
|
+
} from "kysely";
|
|
2
10
|
import { ulid } from "ulidx";
|
|
3
11
|
|
|
12
|
+
import type { MediaUsageContentSourceVariant } from "../../media/usage/source-key.js";
|
|
4
13
|
import type { MediaKind, MediaUsageReferenceType } from "../../media/usage/types.js";
|
|
5
14
|
import { chunks, SQL_BATCH_SIZE } from "../../utils/chunks.js";
|
|
6
15
|
import { withTransaction } from "../transaction.js";
|
|
7
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
Database,
|
|
18
|
+
MediaUsageIndexStatusTable,
|
|
19
|
+
MediaUsageSourceTable,
|
|
20
|
+
MediaUsageTable,
|
|
21
|
+
} from "../types.js";
|
|
22
|
+
import { validateIdentifier } from "../validate.js";
|
|
23
|
+
import { decodeCursor, encodeCursor, type FindManyResult } from "./types.js";
|
|
8
24
|
|
|
9
25
|
type DatabaseExecutor = Kysely<Database> | Transaction<Database>;
|
|
26
|
+
type MediaUsageSourceNullableStringColumn =
|
|
27
|
+
| "source_fingerprint"
|
|
28
|
+
| "source_updated_at"
|
|
29
|
+
| "revision_id"
|
|
30
|
+
| "updated_at"
|
|
31
|
+
| "last_attempted_at"
|
|
32
|
+
| "last_error_code";
|
|
10
33
|
|
|
11
|
-
const OCCURRENCE_BIND_COLUMNS =
|
|
34
|
+
const OCCURRENCE_BIND_COLUMNS = 13;
|
|
12
35
|
const OCCURRENCE_INSERT_BATCH_SIZE = Math.max(
|
|
13
36
|
1,
|
|
14
37
|
Math.floor(SQL_BATCH_SIZE / OCCURRENCE_BIND_COLUMNS),
|
|
@@ -19,7 +42,7 @@ export interface MediaUsageSourceInput {
|
|
|
19
42
|
sourceType: string;
|
|
20
43
|
collectionSlug?: string | null;
|
|
21
44
|
contentId?: string | null;
|
|
22
|
-
sourceVariant:
|
|
45
|
+
sourceVariant: MediaUsageContentSourceVariant;
|
|
23
46
|
locale?: string | null;
|
|
24
47
|
translationGroup?: string | null;
|
|
25
48
|
contentSlug?: string | null;
|
|
@@ -29,6 +52,12 @@ export interface MediaUsageSourceInput {
|
|
|
29
52
|
contentDeletedAt?: string | null;
|
|
30
53
|
revisionId?: string | null;
|
|
31
54
|
schemaVersion?: number;
|
|
55
|
+
sourceUpdatedAt?: string | null;
|
|
56
|
+
sourceVersion?: number | null;
|
|
57
|
+
sourceFingerprint?: string | null;
|
|
58
|
+
sourceCompleteness?: MediaUsageSourceCompleteness;
|
|
59
|
+
lastAttemptedAt?: string | null;
|
|
60
|
+
lastErrorCode?: string | null;
|
|
32
61
|
}
|
|
33
62
|
|
|
34
63
|
export interface MediaUsageOccurrenceInput {
|
|
@@ -59,11 +88,138 @@ export interface MediaUsageSource {
|
|
|
59
88
|
revisionId: string | null;
|
|
60
89
|
currentGeneration: string;
|
|
61
90
|
schemaVersion: number;
|
|
91
|
+
sourceUpdatedAt: string | null;
|
|
92
|
+
sourceVersion: number | null;
|
|
93
|
+
sourceFingerprint: string | null;
|
|
94
|
+
sourceCompleteness: string;
|
|
95
|
+
lastAttemptedAt: string | null;
|
|
96
|
+
lastErrorCode: string | null;
|
|
62
97
|
indexedAt: string;
|
|
63
98
|
createdAt: string;
|
|
64
99
|
updatedAt: string;
|
|
65
100
|
}
|
|
66
101
|
|
|
102
|
+
export interface MediaUsageGuardedReplaceResult {
|
|
103
|
+
replaced: boolean;
|
|
104
|
+
/** Populated only when a guarded replacement did not win the current source row. */
|
|
105
|
+
source: MediaUsageSource | null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface MediaUsageGuardedDeleteResult {
|
|
109
|
+
deleted: boolean;
|
|
110
|
+
source: MediaUsageSource | null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface MediaUsageGuardedAbsentDeleteResult extends MediaUsageGuardedDeleteResult {
|
|
114
|
+
contentPresent: boolean;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface MediaUsageGuardedAttemptResult {
|
|
118
|
+
attempted: boolean;
|
|
119
|
+
/** Populated only when a guarded attempted mark did not win the current source row. */
|
|
120
|
+
source: MediaUsageSource | null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface MediaUsageIndexStatusRepairInput extends MediaUsageIndexStatusIdentity {
|
|
124
|
+
runToken: string;
|
|
125
|
+
schemaVersion?: number;
|
|
126
|
+
startedAt: string;
|
|
127
|
+
updatedAt?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface MediaUsageIndexStatusFinalizeInput extends MediaUsageIndexStatusIdentity {
|
|
131
|
+
runToken: string;
|
|
132
|
+
status: Exclude<MediaUsageIndexStatusValue, "never" | "running" | "stale">;
|
|
133
|
+
schemaVersion?: number;
|
|
134
|
+
completedAt: string;
|
|
135
|
+
indexedSourceCount?: number;
|
|
136
|
+
failedSourceCount?: number;
|
|
137
|
+
lastErrorCode?: string | null;
|
|
138
|
+
updatedAt?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface MediaUsageGuardedIndexStatusResult {
|
|
142
|
+
finalized: boolean;
|
|
143
|
+
status: MediaUsageIndexStatus | null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type MediaUsageSourceCompleteness =
|
|
147
|
+
| "unknown"
|
|
148
|
+
| "complete"
|
|
149
|
+
| "partial"
|
|
150
|
+
| "failed"
|
|
151
|
+
| "unsupported";
|
|
152
|
+
|
|
153
|
+
export type MediaUsageIndexStatusValue =
|
|
154
|
+
| "never"
|
|
155
|
+
| "running"
|
|
156
|
+
| "complete"
|
|
157
|
+
| "partial"
|
|
158
|
+
| "failed"
|
|
159
|
+
| "stale";
|
|
160
|
+
|
|
161
|
+
export interface MediaUsageIndexStatusIdentity {
|
|
162
|
+
adapterId: string;
|
|
163
|
+
scopeType: string;
|
|
164
|
+
scopeKey: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface MediaUsageIndexStatusInput extends MediaUsageIndexStatusIdentity {
|
|
168
|
+
status: MediaUsageIndexStatusValue;
|
|
169
|
+
schemaVersion?: number;
|
|
170
|
+
startedAt?: string | null;
|
|
171
|
+
completedAt?: string | null;
|
|
172
|
+
cursor?: string | null;
|
|
173
|
+
indexedSourceCount?: number;
|
|
174
|
+
failedSourceCount?: number;
|
|
175
|
+
lastErrorCode?: string | null;
|
|
176
|
+
updatedAt?: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface MediaUsageIndexStatus extends MediaUsageIndexStatusIdentity {
|
|
180
|
+
status: string;
|
|
181
|
+
schemaVersion: number;
|
|
182
|
+
startedAt: string | null;
|
|
183
|
+
completedAt: string | null;
|
|
184
|
+
cursor: string | null;
|
|
185
|
+
indexedSourceCount: number;
|
|
186
|
+
failedSourceCount: number;
|
|
187
|
+
lastErrorCode: string | null;
|
|
188
|
+
updatedAt: string;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface FindMediaUsageOptions {
|
|
192
|
+
limit?: number;
|
|
193
|
+
cursor?: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface MediaUsageSourceRow {
|
|
197
|
+
source_key: string;
|
|
198
|
+
source_type: string;
|
|
199
|
+
collection_slug: string | null;
|
|
200
|
+
content_id: string | null;
|
|
201
|
+
source_variant: string;
|
|
202
|
+
locale: string | null;
|
|
203
|
+
translation_group: string | null;
|
|
204
|
+
content_slug: string | null;
|
|
205
|
+
content_title: string | null;
|
|
206
|
+
content_status: string | null;
|
|
207
|
+
content_scheduled_at: string | null;
|
|
208
|
+
content_deleted_at: string | null;
|
|
209
|
+
revision_id: string | null;
|
|
210
|
+
current_generation: string;
|
|
211
|
+
schema_version: number;
|
|
212
|
+
source_updated_at: string | null;
|
|
213
|
+
source_version: number | null;
|
|
214
|
+
source_fingerprint: string | null;
|
|
215
|
+
source_completeness: string;
|
|
216
|
+
last_attempted_at: string | null;
|
|
217
|
+
last_error_code: string | null;
|
|
218
|
+
indexed_at: string;
|
|
219
|
+
created_at: string;
|
|
220
|
+
updated_at: string;
|
|
221
|
+
}
|
|
222
|
+
|
|
67
223
|
export interface MediaUsageOccurrence {
|
|
68
224
|
id: string;
|
|
69
225
|
sourceKey: string;
|
|
@@ -101,9 +257,15 @@ interface JoinedUsageRow {
|
|
|
101
257
|
revision_id: string | null;
|
|
102
258
|
current_generation: string;
|
|
103
259
|
schema_version: number;
|
|
260
|
+
source_updated_at: string | null;
|
|
261
|
+
source_version: number | null;
|
|
262
|
+
source_fingerprint: string | null;
|
|
263
|
+
source_completeness: string;
|
|
264
|
+
last_attempted_at: string | null;
|
|
265
|
+
last_error_code: string | null;
|
|
104
266
|
indexed_at: string;
|
|
105
267
|
source_created_at: string;
|
|
106
|
-
|
|
268
|
+
source_row_updated_at: string;
|
|
107
269
|
occurrence_id: string;
|
|
108
270
|
generation: string;
|
|
109
271
|
field_slug: string;
|
|
@@ -130,14 +292,8 @@ export class MediaUsageRepository {
|
|
|
130
292
|
const now = new Date().toISOString();
|
|
131
293
|
|
|
132
294
|
await withTransaction(this.db, async (trx) => {
|
|
133
|
-
await this.insertOccurrences(trx, source.sourceKey, generation, occurrences);
|
|
295
|
+
await this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);
|
|
134
296
|
await this.upsertSource(trx, source, generation, now);
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
await this.deleteStaleGenerations(trx, source.sourceKey, generation);
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error("[media-usage] failed to delete stale generations:", error);
|
|
140
|
-
}
|
|
141
297
|
});
|
|
142
298
|
|
|
143
299
|
const replaced = await this.findSource(source.sourceKey);
|
|
@@ -146,6 +302,30 @@ export class MediaUsageRepository {
|
|
|
146
302
|
}
|
|
147
303
|
return replaced;
|
|
148
304
|
}
|
|
305
|
+
async replaceSourceIfCurrent(
|
|
306
|
+
source: MediaUsageSourceInput,
|
|
307
|
+
occurrences: readonly MediaUsageOccurrenceInput[],
|
|
308
|
+
expectedCurrentGeneration: string | null,
|
|
309
|
+
): Promise<MediaUsageGuardedReplaceResult> {
|
|
310
|
+
const generation = ulid();
|
|
311
|
+
const now = new Date().toISOString();
|
|
312
|
+
const row = this.buildSourceRow(source, generation, now);
|
|
313
|
+
let replaced = false;
|
|
314
|
+
|
|
315
|
+
await withTransaction(this.db, async (trx) => {
|
|
316
|
+
await this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);
|
|
317
|
+
if (expectedCurrentGeneration === null) {
|
|
318
|
+
replaced = await this.insertSourceIfAbsent(trx, row);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
replaced = await this.updateSourceIfGeneration(trx, row, expectedCurrentGeneration);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
replaced,
|
|
326
|
+
source: replaced ? null : await this.findSource(source.sourceKey),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
149
329
|
|
|
150
330
|
async findSource(sourceKey: string): Promise<MediaUsageSource | null> {
|
|
151
331
|
const row = await this.db
|
|
@@ -157,6 +337,89 @@ export class MediaUsageRepository {
|
|
|
157
337
|
return row ? rowToSource(row) : null;
|
|
158
338
|
}
|
|
159
339
|
|
|
340
|
+
async findSources(sourceKeys: readonly string[]): Promise<Map<string, MediaUsageSource>> {
|
|
341
|
+
const uniqueSourceKeys = [...new Set(sourceKeys)];
|
|
342
|
+
const sources = new Map<string, MediaUsageSource>();
|
|
343
|
+
if (uniqueSourceKeys.length === 0) return sources;
|
|
344
|
+
|
|
345
|
+
for (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {
|
|
346
|
+
const rows = await this.db
|
|
347
|
+
.selectFrom("_emdash_media_usage_sources")
|
|
348
|
+
.selectAll()
|
|
349
|
+
.where("source_key", "in", sourceKeyBatch)
|
|
350
|
+
.execute();
|
|
351
|
+
for (const row of rows) {
|
|
352
|
+
const source = rowToSource(row);
|
|
353
|
+
sources.set(source.sourceKey, source);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return sources;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async replaceSourceIfMatching(
|
|
361
|
+
source: MediaUsageSourceInput,
|
|
362
|
+
occurrences: readonly MediaUsageOccurrenceInput[],
|
|
363
|
+
expectedSource: MediaUsageSource | null,
|
|
364
|
+
): Promise<MediaUsageGuardedReplaceResult> {
|
|
365
|
+
const generation = ulid();
|
|
366
|
+
const now = new Date().toISOString();
|
|
367
|
+
const row = this.buildSourceRow(source, generation, now);
|
|
368
|
+
let replaced = false;
|
|
369
|
+
|
|
370
|
+
await withTransaction(this.db, async (trx) => {
|
|
371
|
+
await this.insertOccurrences(trx, source.sourceKey, generation, occurrences, now);
|
|
372
|
+
if (expectedSource === null) {
|
|
373
|
+
replaced = await this.insertSourceIfAbsent(trx, row);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
replaced = await this.updateSourceIfMatching(trx, row, expectedSource);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
replaced,
|
|
381
|
+
source: replaced ? null : await this.findSource(source.sourceKey),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async markSourceAttempted(source: MediaUsageSourceInput): Promise<MediaUsageSource> {
|
|
386
|
+
const now = new Date().toISOString();
|
|
387
|
+
const row = this.buildAttemptedSourceRow(source, now);
|
|
388
|
+
const updates = this.attemptedSourceUpdateSet(source, row);
|
|
389
|
+
|
|
390
|
+
await this.db
|
|
391
|
+
.insertInto("_emdash_media_usage_sources")
|
|
392
|
+
.values(row)
|
|
393
|
+
.onConflict((oc) => oc.column("source_key").doUpdateSet(updates))
|
|
394
|
+
.execute();
|
|
395
|
+
|
|
396
|
+
const attempted = await this.findSource(source.sourceKey);
|
|
397
|
+
if (!attempted) {
|
|
398
|
+
throw new Error(`Media usage source ${source.sourceKey} was not persisted`);
|
|
399
|
+
}
|
|
400
|
+
return attempted;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async markSourceAttemptedIfMatching(
|
|
404
|
+
source: MediaUsageSourceInput,
|
|
405
|
+
expectedSource: MediaUsageSource | null,
|
|
406
|
+
): Promise<MediaUsageGuardedAttemptResult> {
|
|
407
|
+
const now = new Date().toISOString();
|
|
408
|
+
const row = this.buildAttemptedSourceRow(source, now);
|
|
409
|
+
let attempted = false;
|
|
410
|
+
|
|
411
|
+
if (expectedSource === null) {
|
|
412
|
+
attempted = await this.insertSourceIfAbsent(this.db, row);
|
|
413
|
+
} else {
|
|
414
|
+
attempted = await this.updateAttemptedSourceIfMatching(this.db, source, row, expectedSource);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
attempted,
|
|
419
|
+
source: attempted ? null : await this.findSource(source.sourceKey),
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
160
423
|
async findCurrentUsageByMediaId(mediaId: string): Promise<MediaUsageRecord[]> {
|
|
161
424
|
const rows = await this.db
|
|
162
425
|
.selectFrom("_emdash_media_usage_sources as s")
|
|
@@ -197,15 +460,114 @@ export class MediaUsageRepository {
|
|
|
197
460
|
return rows.map(rowToUsageRecord);
|
|
198
461
|
}
|
|
199
462
|
|
|
463
|
+
async findCurrentUsagePageByMediaId(
|
|
464
|
+
mediaId: string,
|
|
465
|
+
options: FindMediaUsageOptions = {},
|
|
466
|
+
): Promise<FindManyResult<MediaUsageRecord>> {
|
|
467
|
+
return this.findCurrentUsagePage((query) => query.where("u.media_id", "=", mediaId), options);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async findCurrentUsagePageByProviderAsset(
|
|
471
|
+
provider: string,
|
|
472
|
+
providerAssetId: string,
|
|
473
|
+
options: FindMediaUsageOptions = {},
|
|
474
|
+
): Promise<FindManyResult<MediaUsageRecord>> {
|
|
475
|
+
return this.findCurrentUsagePage(
|
|
476
|
+
(query) =>
|
|
477
|
+
query.where("u.provider", "=", provider).where("u.provider_asset_id", "=", providerAssetId),
|
|
478
|
+
options,
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
200
482
|
async deleteSource(sourceKey: string): Promise<number> {
|
|
201
|
-
return
|
|
202
|
-
|
|
483
|
+
return this.deleteSources([sourceKey]);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async deleteSourceIfCurrent(
|
|
487
|
+
sourceKey: string,
|
|
488
|
+
expectedCurrentGeneration: string,
|
|
489
|
+
): Promise<MediaUsageGuardedDeleteResult> {
|
|
490
|
+
let deleted = false;
|
|
491
|
+
await withTransaction(this.db, async (trx) => {
|
|
492
|
+
const result = await trx
|
|
493
|
+
.deleteFrom("_emdash_media_usage_sources")
|
|
494
|
+
.where("source_key", "=", sourceKey)
|
|
495
|
+
.where("current_generation", "=", expectedCurrentGeneration)
|
|
496
|
+
.executeTakeFirst();
|
|
497
|
+
deleted = Number(result.numDeletedRows ?? 0) > 0;
|
|
498
|
+
if (!deleted) return;
|
|
499
|
+
await this.deleteSourceGenerationOccurrences(trx, sourceKey, expectedCurrentGeneration);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
deleted,
|
|
504
|
+
source: await this.findSource(sourceKey),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
async deleteSourceIfMatching(
|
|
509
|
+
sourceKey: string,
|
|
510
|
+
expectedSource: MediaUsageSource,
|
|
511
|
+
): Promise<MediaUsageGuardedDeleteResult> {
|
|
512
|
+
let deleted = false;
|
|
513
|
+
await withTransaction(this.db, async (trx) => {
|
|
203
514
|
const result = await trx
|
|
204
515
|
.deleteFrom("_emdash_media_usage_sources")
|
|
205
516
|
.where("source_key", "=", sourceKey)
|
|
517
|
+
.where(this.sourceMatchExpression(expectedSource))
|
|
206
518
|
.executeTakeFirst();
|
|
207
|
-
|
|
519
|
+
deleted = Number(result.numDeletedRows ?? 0) > 0;
|
|
520
|
+
if (!deleted) return;
|
|
521
|
+
await this.deleteSourceGenerationOccurrences(
|
|
522
|
+
trx,
|
|
523
|
+
sourceKey,
|
|
524
|
+
expectedSource.currentGeneration,
|
|
525
|
+
);
|
|
208
526
|
});
|
|
527
|
+
|
|
528
|
+
return {
|
|
529
|
+
deleted,
|
|
530
|
+
source: await this.findSource(sourceKey),
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
async deleteSourceIfMatchingContentAbsent(
|
|
535
|
+
sourceKey: string,
|
|
536
|
+
expectedSource: MediaUsageSource,
|
|
537
|
+
collectionSlug: string,
|
|
538
|
+
contentId: string,
|
|
539
|
+
): Promise<MediaUsageGuardedAbsentDeleteResult> {
|
|
540
|
+
validateIdentifier(collectionSlug, "collection slug");
|
|
541
|
+
const tableName = `ec_${collectionSlug}`;
|
|
542
|
+
let deleted = false;
|
|
543
|
+
await withTransaction(this.db, async (trx) => {
|
|
544
|
+
const result = await trx
|
|
545
|
+
.deleteFrom("_emdash_media_usage_sources")
|
|
546
|
+
.where("source_key", "=", sourceKey)
|
|
547
|
+
.where(this.sourceMatchExpression(expectedSource))
|
|
548
|
+
.where(
|
|
549
|
+
sql<boolean>`NOT EXISTS (SELECT 1 FROM ${sql.ref(tableName)} WHERE id = ${contentId})`,
|
|
550
|
+
)
|
|
551
|
+
.executeTakeFirst();
|
|
552
|
+
deleted = Number(result.numDeletedRows ?? 0) > 0;
|
|
553
|
+
if (!deleted) return;
|
|
554
|
+
await this.deleteSourceGenerationOccurrences(
|
|
555
|
+
trx,
|
|
556
|
+
sourceKey,
|
|
557
|
+
expectedSource.currentGeneration,
|
|
558
|
+
);
|
|
559
|
+
});
|
|
560
|
+
const contentPresent = deleted ? false : await this.contentRowExists(tableName, contentId);
|
|
561
|
+
|
|
562
|
+
return {
|
|
563
|
+
deleted,
|
|
564
|
+
contentPresent,
|
|
565
|
+
source: deleted || contentPresent ? null : await this.findSource(sourceKey),
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async deleteSources(sourceKeys: readonly string[]): Promise<number> {
|
|
570
|
+
return this.deleteSourceKeys(sourceKeys);
|
|
209
571
|
}
|
|
210
572
|
|
|
211
573
|
async deleteContentSources(collectionSlug: string, contentId: string): Promise<number> {
|
|
@@ -217,30 +579,358 @@ export class MediaUsageRepository {
|
|
|
217
579
|
.where("content_id", "=", contentId)
|
|
218
580
|
.execute();
|
|
219
581
|
const sourceKeys = sourceRows.map((row) => row.source_key);
|
|
220
|
-
|
|
582
|
+
return this.deleteSourceKeys(sourceKeys);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
async deleteCollectionSources(collectionSlug: string): Promise<number> {
|
|
586
|
+
let deleted = 0;
|
|
587
|
+
while (true) {
|
|
588
|
+
const sourceRows = await this.db
|
|
589
|
+
.selectFrom("_emdash_media_usage_sources")
|
|
590
|
+
.select("source_key")
|
|
591
|
+
.where("source_type", "=", "content")
|
|
592
|
+
.where("collection_slug", "=", collectionSlug)
|
|
593
|
+
.orderBy("source_key", "asc")
|
|
594
|
+
.limit(SQL_BATCH_SIZE)
|
|
595
|
+
.execute();
|
|
596
|
+
if (sourceRows.length === 0) break;
|
|
597
|
+
|
|
598
|
+
deleted += await this.deleteSourceKeys(sourceRows.map((row) => row.source_key));
|
|
599
|
+
}
|
|
600
|
+
return deleted;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async findCollectionContentSources(collectionSlug: string): Promise<MediaUsageSource[]> {
|
|
604
|
+
const rows = await this.db
|
|
605
|
+
.selectFrom("_emdash_media_usage_sources")
|
|
606
|
+
.selectAll()
|
|
607
|
+
.where("source_type", "=", "content")
|
|
608
|
+
.where("collection_slug", "=", collectionSlug)
|
|
609
|
+
.orderBy("source_key", "asc")
|
|
610
|
+
.execute();
|
|
611
|
+
return rows.map((row) => rowToSource(row));
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
async deleteOrphanOccurrencesOlderThan(cutoff: string, limit: number): Promise<number> {
|
|
615
|
+
const batchLimit = Math.floor(limit);
|
|
616
|
+
if (batchLimit <= 0) return 0;
|
|
617
|
+
|
|
618
|
+
const rows = await this.db
|
|
619
|
+
.selectFrom("_emdash_media_usage as u")
|
|
620
|
+
.leftJoin("_emdash_media_usage_sources as s", (join) =>
|
|
621
|
+
join.onRef("s.source_key", "=", "u.source_key"),
|
|
622
|
+
)
|
|
623
|
+
.select("u.id")
|
|
624
|
+
.where("s.source_key", "is", null)
|
|
625
|
+
.where("u.created_at", "<", cutoff)
|
|
626
|
+
.orderBy("u.created_at", "asc")
|
|
627
|
+
.orderBy("u.id", "asc")
|
|
628
|
+
.limit(batchLimit)
|
|
629
|
+
.execute();
|
|
630
|
+
|
|
631
|
+
let deleted = 0;
|
|
632
|
+
for (const idBatch of chunks(
|
|
633
|
+
rows.map((row) => row.id),
|
|
634
|
+
SQL_BATCH_SIZE,
|
|
635
|
+
)) {
|
|
636
|
+
const result = await this.db
|
|
637
|
+
.deleteFrom("_emdash_media_usage")
|
|
638
|
+
.where("id", "in", idBatch)
|
|
639
|
+
.where("created_at", "<", cutoff)
|
|
640
|
+
.where(
|
|
641
|
+
sql<boolean>`NOT EXISTS (SELECT 1 FROM _emdash_media_usage_sources s WHERE s.source_key = _emdash_media_usage.source_key)`,
|
|
642
|
+
)
|
|
643
|
+
.executeTakeFirst();
|
|
644
|
+
deleted += Number(result.numDeletedRows ?? 0);
|
|
645
|
+
}
|
|
646
|
+
return deleted;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
async deleteStaleGenerationsOlderThan(cutoff: string, limit: number): Promise<number> {
|
|
650
|
+
const batchLimit = Math.floor(limit);
|
|
651
|
+
if (batchLimit <= 0) return 0;
|
|
652
|
+
|
|
653
|
+
const rows = await this.db
|
|
654
|
+
.selectFrom("_emdash_media_usage as u")
|
|
655
|
+
.innerJoin("_emdash_media_usage_sources as s", (join) =>
|
|
656
|
+
join.onRef("s.source_key", "=", "u.source_key"),
|
|
657
|
+
)
|
|
658
|
+
.select("u.id")
|
|
659
|
+
.where("u.created_at", "<", cutoff)
|
|
660
|
+
.whereRef("u.generation", "!=", "s.current_generation")
|
|
661
|
+
.whereRef("u.created_at", "<", "s.indexed_at")
|
|
662
|
+
.orderBy("u.created_at", "asc")
|
|
663
|
+
.orderBy("u.id", "asc")
|
|
664
|
+
.limit(batchLimit)
|
|
665
|
+
.execute();
|
|
666
|
+
|
|
667
|
+
const ids = rows.map((row) => row.id);
|
|
668
|
+
if (ids.length === 0) return 0;
|
|
669
|
+
|
|
670
|
+
let deleted = 0;
|
|
671
|
+
for (const idBatch of chunks(ids, SQL_BATCH_SIZE)) {
|
|
672
|
+
const result = await this.db
|
|
673
|
+
.deleteFrom("_emdash_media_usage")
|
|
674
|
+
.where("id", "in", idBatch)
|
|
675
|
+
.where("created_at", "<", cutoff)
|
|
676
|
+
.where((eb) =>
|
|
677
|
+
eb.exists(
|
|
678
|
+
eb
|
|
679
|
+
.selectFrom("_emdash_media_usage_sources as s")
|
|
680
|
+
.select("s.source_key")
|
|
681
|
+
.whereRef("s.source_key", "=", "_emdash_media_usage.source_key")
|
|
682
|
+
.whereRef("s.current_generation", "!=", "_emdash_media_usage.generation")
|
|
683
|
+
.whereRef("_emdash_media_usage.created_at", "<", "s.indexed_at"),
|
|
684
|
+
),
|
|
685
|
+
)
|
|
686
|
+
.executeTakeFirst();
|
|
687
|
+
deleted += Number(result.numDeletedRows ?? 0);
|
|
688
|
+
}
|
|
689
|
+
return deleted;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
async deleteAbandonedGenerationsOlderThan(cutoff: string, limit: number): Promise<number> {
|
|
693
|
+
const batchLimit = Math.floor(limit);
|
|
694
|
+
if (batchLimit <= 0) return 0;
|
|
695
|
+
|
|
696
|
+
const rows = await this.db
|
|
697
|
+
.selectFrom("_emdash_media_usage as u")
|
|
698
|
+
.innerJoin("_emdash_media_usage_sources as s", (join) =>
|
|
699
|
+
join.onRef("s.source_key", "=", "u.source_key"),
|
|
700
|
+
)
|
|
701
|
+
.select("u.id")
|
|
702
|
+
.where("u.created_at", "<", cutoff)
|
|
703
|
+
.whereRef("u.generation", "!=", "s.current_generation")
|
|
704
|
+
.whereRef("u.created_at", ">=", "s.indexed_at")
|
|
705
|
+
.orderBy("u.created_at", "asc")
|
|
706
|
+
.orderBy("u.id", "asc")
|
|
707
|
+
.limit(batchLimit)
|
|
708
|
+
.execute();
|
|
709
|
+
|
|
710
|
+
let deleted = 0;
|
|
711
|
+
for (const idBatch of chunks(
|
|
712
|
+
rows.map((row) => row.id),
|
|
713
|
+
SQL_BATCH_SIZE,
|
|
714
|
+
)) {
|
|
715
|
+
const result = await this.db
|
|
716
|
+
.deleteFrom("_emdash_media_usage")
|
|
717
|
+
.where("id", "in", idBatch)
|
|
718
|
+
.where("created_at", "<", cutoff)
|
|
719
|
+
.where((eb) =>
|
|
720
|
+
eb.exists(
|
|
721
|
+
eb
|
|
722
|
+
.selectFrom("_emdash_media_usage_sources as s")
|
|
723
|
+
.select("s.source_key")
|
|
724
|
+
.whereRef("s.source_key", "=", "_emdash_media_usage.source_key")
|
|
725
|
+
.whereRef("s.current_generation", "!=", "_emdash_media_usage.generation")
|
|
726
|
+
.whereRef("_emdash_media_usage.created_at", ">=", "s.indexed_at"),
|
|
727
|
+
),
|
|
728
|
+
)
|
|
729
|
+
.executeTakeFirst();
|
|
730
|
+
deleted += Number(result.numDeletedRows ?? 0);
|
|
731
|
+
}
|
|
732
|
+
return deleted;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
async upsertIndexStatus(input: MediaUsageIndexStatusInput): Promise<MediaUsageIndexStatus> {
|
|
736
|
+
const now = input.updatedAt ?? new Date().toISOString();
|
|
737
|
+
const row = {
|
|
738
|
+
adapter_id: input.adapterId,
|
|
739
|
+
scope_type: input.scopeType,
|
|
740
|
+
scope_key: input.scopeKey,
|
|
741
|
+
status: input.status,
|
|
742
|
+
schema_version: input.schemaVersion ?? 1,
|
|
743
|
+
started_at: input.startedAt ?? null,
|
|
744
|
+
completed_at: input.completedAt ?? null,
|
|
745
|
+
cursor: input.cursor ?? null,
|
|
746
|
+
indexed_source_count: input.indexedSourceCount ?? 0,
|
|
747
|
+
failed_source_count: input.failedSourceCount ?? 0,
|
|
748
|
+
last_error_code: input.lastErrorCode ?? null,
|
|
749
|
+
updated_at: now,
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
await this.db
|
|
753
|
+
.insertInto("_emdash_media_usage_index_status")
|
|
754
|
+
.values(row)
|
|
755
|
+
.onConflict((oc) =>
|
|
756
|
+
oc.columns(["adapter_id", "scope_type", "scope_key"]).doUpdateSet({
|
|
757
|
+
status: row.status,
|
|
758
|
+
schema_version: row.schema_version,
|
|
759
|
+
started_at: row.started_at,
|
|
760
|
+
completed_at: row.completed_at,
|
|
761
|
+
cursor: row.cursor,
|
|
762
|
+
indexed_source_count: row.indexed_source_count,
|
|
763
|
+
failed_source_count: row.failed_source_count,
|
|
764
|
+
last_error_code: row.last_error_code,
|
|
765
|
+
updated_at: row.updated_at,
|
|
766
|
+
}),
|
|
767
|
+
)
|
|
768
|
+
.execute();
|
|
769
|
+
|
|
770
|
+
const status = await this.findIndexStatus(input);
|
|
771
|
+
if (!status) {
|
|
772
|
+
throw new Error(
|
|
773
|
+
`Media usage index status ${input.adapterId}:${input.scopeType}:${input.scopeKey} was not persisted`,
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
return status;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
async beginIndexStatusRepair(
|
|
780
|
+
input: MediaUsageIndexStatusRepairInput,
|
|
781
|
+
): Promise<MediaUsageIndexStatus> {
|
|
782
|
+
return this.upsertIndexStatus({
|
|
783
|
+
adapterId: input.adapterId,
|
|
784
|
+
scopeType: input.scopeType,
|
|
785
|
+
scopeKey: input.scopeKey,
|
|
786
|
+
status: "running",
|
|
787
|
+
schemaVersion: input.schemaVersion,
|
|
788
|
+
startedAt: input.startedAt,
|
|
789
|
+
completedAt: null,
|
|
790
|
+
cursor: input.runToken,
|
|
791
|
+
indexedSourceCount: 0,
|
|
792
|
+
failedSourceCount: 0,
|
|
793
|
+
lastErrorCode: null,
|
|
794
|
+
updatedAt: input.updatedAt,
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
async finalizeIndexStatusRepairIfRunning(
|
|
799
|
+
input: MediaUsageIndexStatusFinalizeInput,
|
|
800
|
+
): Promise<MediaUsageGuardedIndexStatusResult> {
|
|
801
|
+
const updates: Updateable<MediaUsageIndexStatusTable> = {
|
|
802
|
+
status: input.status,
|
|
803
|
+
completed_at: input.completedAt,
|
|
804
|
+
cursor: null,
|
|
805
|
+
indexed_source_count: input.indexedSourceCount ?? 0,
|
|
806
|
+
failed_source_count: input.failedSourceCount ?? 0,
|
|
807
|
+
last_error_code: input.lastErrorCode ?? null,
|
|
808
|
+
updated_at: input.updatedAt ?? new Date().toISOString(),
|
|
809
|
+
};
|
|
810
|
+
if (input.schemaVersion !== undefined) updates.schema_version = input.schemaVersion;
|
|
811
|
+
|
|
812
|
+
const result = await this.db
|
|
813
|
+
.updateTable("_emdash_media_usage_index_status")
|
|
814
|
+
.set(updates)
|
|
815
|
+
.where("adapter_id", "=", input.adapterId)
|
|
816
|
+
.where("scope_type", "=", input.scopeType)
|
|
817
|
+
.where("scope_key", "=", input.scopeKey)
|
|
818
|
+
.where("status", "=", "running")
|
|
819
|
+
.where("cursor", "=", input.runToken)
|
|
820
|
+
.executeTakeFirst();
|
|
821
|
+
const finalized = Number(result.numUpdatedRows ?? 0) > 0;
|
|
822
|
+
|
|
823
|
+
return {
|
|
824
|
+
finalized,
|
|
825
|
+
status: await this.findIndexStatus(input),
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
async findIndexStatus(
|
|
830
|
+
identity: MediaUsageIndexStatusIdentity,
|
|
831
|
+
): Promise<MediaUsageIndexStatus | null> {
|
|
832
|
+
const row = await this.db
|
|
833
|
+
.selectFrom("_emdash_media_usage_index_status")
|
|
834
|
+
.selectAll()
|
|
835
|
+
.where("adapter_id", "=", identity.adapterId)
|
|
836
|
+
.where("scope_type", "=", identity.scopeType)
|
|
837
|
+
.where("scope_key", "=", identity.scopeKey)
|
|
838
|
+
.executeTakeFirst();
|
|
839
|
+
|
|
840
|
+
return row ? rowToIndexStatus(row) : null;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
async deleteIndexStatus(identity: MediaUsageIndexStatusIdentity): Promise<number> {
|
|
844
|
+
const result = await this.db
|
|
845
|
+
.deleteFrom("_emdash_media_usage_index_status")
|
|
846
|
+
.where("adapter_id", "=", identity.adapterId)
|
|
847
|
+
.where("scope_type", "=", identity.scopeType)
|
|
848
|
+
.where("scope_key", "=", identity.scopeKey)
|
|
849
|
+
.executeTakeFirst();
|
|
850
|
+
return Number(result.numDeletedRows ?? 0);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
private async findCurrentUsagePage(
|
|
854
|
+
applyFilter: (
|
|
855
|
+
query: ReturnType<MediaUsageRepository["currentUsageBaseQuery"]>,
|
|
856
|
+
) => ReturnType<MediaUsageRepository["currentUsageBaseQuery"]>,
|
|
857
|
+
options: FindMediaUsageOptions,
|
|
858
|
+
): Promise<FindManyResult<MediaUsageRecord>> {
|
|
859
|
+
const limit = Math.min(Math.max(1, options.limit ?? 50), 100);
|
|
860
|
+
let query = applyFilter(this.currentUsageBaseQuery())
|
|
861
|
+
.orderBy("u.id", "asc")
|
|
862
|
+
.limit(limit + 1);
|
|
863
|
+
|
|
864
|
+
if (options.cursor) {
|
|
865
|
+
const { id } = decodeCursor(options.cursor);
|
|
866
|
+
query = query.where("u.id", ">", id);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const rows = await query.execute();
|
|
870
|
+
const items = rows.slice(0, limit).map(rowToUsageRecord);
|
|
871
|
+
const result: FindManyResult<MediaUsageRecord> = { items };
|
|
872
|
+
|
|
873
|
+
if (rows.length > limit && items.length > 0) {
|
|
874
|
+
const last = items.at(-1)!;
|
|
875
|
+
result.nextCursor = encodeCursor(last.occurrence.id, last.occurrence.id);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return result;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
private currentUsageBaseQuery() {
|
|
882
|
+
return this.db
|
|
883
|
+
.selectFrom("_emdash_media_usage_sources as s")
|
|
884
|
+
.innerJoin("_emdash_media_usage as u", (join) =>
|
|
885
|
+
join
|
|
886
|
+
.onRef("u.source_key", "=", "s.source_key")
|
|
887
|
+
.onRef("u.generation", "=", "s.current_generation"),
|
|
888
|
+
)
|
|
889
|
+
.select(currentUsageSelect);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
private async deleteSourceKeys(sourceKeys: readonly string[]): Promise<number> {
|
|
893
|
+
const uniqueSourceKeys = [...new Set(sourceKeys)];
|
|
894
|
+
if (uniqueSourceKeys.length === 0) return 0;
|
|
221
895
|
|
|
222
896
|
return withTransaction(this.db, async (trx) => {
|
|
223
897
|
let deleted = 0;
|
|
224
|
-
for (const sourceKeyBatch of chunks(
|
|
225
|
-
await trx
|
|
226
|
-
.deleteFrom("_emdash_media_usage")
|
|
227
|
-
.where("source_key", "in", sourceKeyBatch)
|
|
228
|
-
.execute();
|
|
898
|
+
for (const sourceKeyBatch of chunks(uniqueSourceKeys, SQL_BATCH_SIZE)) {
|
|
229
899
|
const result = await trx
|
|
230
900
|
.deleteFrom("_emdash_media_usage_sources")
|
|
231
901
|
.where("source_key", "in", sourceKeyBatch)
|
|
232
902
|
.executeTakeFirst();
|
|
233
903
|
deleted += Number(result.numDeletedRows ?? 0);
|
|
904
|
+
|
|
905
|
+
await trx
|
|
906
|
+
.deleteFrom("_emdash_media_usage")
|
|
907
|
+
.where("source_key", "in", sourceKeyBatch)
|
|
908
|
+
.execute();
|
|
234
909
|
}
|
|
235
910
|
return deleted;
|
|
236
911
|
});
|
|
237
912
|
}
|
|
238
913
|
|
|
914
|
+
private async deleteSourceGenerationOccurrences(
|
|
915
|
+
db: DatabaseExecutor,
|
|
916
|
+
sourceKey: string,
|
|
917
|
+
generation: string,
|
|
918
|
+
): Promise<void> {
|
|
919
|
+
// Guarded source deletes remove only the generation that won the source CAS;
|
|
920
|
+
// unmatched generations become invisible orphans reclaimed by age-gated cleanup.
|
|
921
|
+
await db
|
|
922
|
+
.deleteFrom("_emdash_media_usage")
|
|
923
|
+
.where("source_key", "=", sourceKey)
|
|
924
|
+
.where("generation", "=", generation)
|
|
925
|
+
.execute();
|
|
926
|
+
}
|
|
927
|
+
|
|
239
928
|
private async insertOccurrences(
|
|
240
929
|
db: DatabaseExecutor,
|
|
241
930
|
sourceKey: string,
|
|
242
931
|
generation: string,
|
|
243
932
|
occurrences: readonly MediaUsageOccurrenceInput[],
|
|
933
|
+
now: string,
|
|
244
934
|
): Promise<void> {
|
|
245
935
|
if (occurrences.length === 0) return;
|
|
246
936
|
|
|
@@ -257,6 +947,7 @@ export class MediaUsageRepository {
|
|
|
257
947
|
provider_asset_id: occurrence.providerAssetId,
|
|
258
948
|
media_kind: occurrence.mediaKind ?? null,
|
|
259
949
|
mime_type: occurrence.mimeType ?? null,
|
|
950
|
+
created_at: now,
|
|
260
951
|
}));
|
|
261
952
|
|
|
262
953
|
for (const rowBatch of chunks(rows, OCCURRENCE_INSERT_BATCH_SIZE)) {
|
|
@@ -270,7 +961,113 @@ export class MediaUsageRepository {
|
|
|
270
961
|
generation: string,
|
|
271
962
|
now: string,
|
|
272
963
|
): Promise<void> {
|
|
273
|
-
const row =
|
|
964
|
+
const row = this.buildSourceRow(source, generation, now);
|
|
965
|
+
|
|
966
|
+
await db
|
|
967
|
+
.insertInto("_emdash_media_usage_sources")
|
|
968
|
+
.values(row)
|
|
969
|
+
.onConflict((oc) => oc.column("source_key").doUpdateSet(this.sourceUpdateSet(row)))
|
|
970
|
+
.execute();
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
private async insertSourceIfAbsent(
|
|
974
|
+
db: DatabaseExecutor,
|
|
975
|
+
row: Insertable<MediaUsageSourceTable>,
|
|
976
|
+
): Promise<boolean> {
|
|
977
|
+
const result = await db
|
|
978
|
+
.insertInto("_emdash_media_usage_sources")
|
|
979
|
+
.values(row)
|
|
980
|
+
.onConflict((oc) => oc.column("source_key").doNothing())
|
|
981
|
+
.executeTakeFirst();
|
|
982
|
+
return (result.numInsertedOrUpdatedRows ?? 0n) > 0n;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
private async updateSourceIfGeneration(
|
|
986
|
+
db: DatabaseExecutor,
|
|
987
|
+
row: ReturnType<MediaUsageRepository["buildSourceRow"]>,
|
|
988
|
+
expectedCurrentGeneration: string,
|
|
989
|
+
): Promise<boolean> {
|
|
990
|
+
const result = await db
|
|
991
|
+
.updateTable("_emdash_media_usage_sources")
|
|
992
|
+
.set(this.sourceUpdateSet(row))
|
|
993
|
+
.where("source_key", "=", row.source_key)
|
|
994
|
+
.where("current_generation", "=", expectedCurrentGeneration)
|
|
995
|
+
.executeTakeFirst();
|
|
996
|
+
return Number(result.numUpdatedRows ?? 0) > 0;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
private async updateSourceIfMatching(
|
|
1000
|
+
db: DatabaseExecutor,
|
|
1001
|
+
row: ReturnType<MediaUsageRepository["buildSourceRow"]>,
|
|
1002
|
+
expectedSource: MediaUsageSource,
|
|
1003
|
+
): Promise<boolean> {
|
|
1004
|
+
const result = await db
|
|
1005
|
+
.updateTable("_emdash_media_usage_sources")
|
|
1006
|
+
.set(this.sourceUpdateSet(row))
|
|
1007
|
+
.where("source_key", "=", row.source_key)
|
|
1008
|
+
.where(this.sourceMatchExpression(expectedSource))
|
|
1009
|
+
.executeTakeFirst();
|
|
1010
|
+
return Number(result.numUpdatedRows ?? 0) > 0;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
private async updateAttemptedSourceIfMatching(
|
|
1014
|
+
db: DatabaseExecutor,
|
|
1015
|
+
source: MediaUsageSourceInput,
|
|
1016
|
+
row: ReturnType<MediaUsageRepository["buildAttemptedSourceRow"]>,
|
|
1017
|
+
expectedSource: MediaUsageSource,
|
|
1018
|
+
): Promise<boolean> {
|
|
1019
|
+
const result = await db
|
|
1020
|
+
.updateTable("_emdash_media_usage_sources")
|
|
1021
|
+
.set(this.attemptedSourceUpdateSet(source, row))
|
|
1022
|
+
.where("source_key", "=", row.source_key)
|
|
1023
|
+
.where(this.sourceMatchExpression(expectedSource))
|
|
1024
|
+
.executeTakeFirst();
|
|
1025
|
+
return Number(result.numUpdatedRows ?? 0) > 0;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
private sourceMatchExpression(expectedSource: MediaUsageSource) {
|
|
1029
|
+
return (eb: ExpressionBuilder<Database, "_emdash_media_usage_sources">) =>
|
|
1030
|
+
eb.and([
|
|
1031
|
+
eb("current_generation", "=", expectedSource.currentGeneration),
|
|
1032
|
+
eb("source_completeness", "=", expectedSource.sourceCompleteness),
|
|
1033
|
+
this.nullableStringExpression(eb, "updated_at", expectedSource.updatedAt),
|
|
1034
|
+
this.nullableStringExpression(eb, "source_fingerprint", expectedSource.sourceFingerprint),
|
|
1035
|
+
this.nullableStringExpression(eb, "source_updated_at", expectedSource.sourceUpdatedAt),
|
|
1036
|
+
this.nullableNumberExpression(eb, "source_version", expectedSource.sourceVersion),
|
|
1037
|
+
this.nullableStringExpression(eb, "revision_id", expectedSource.revisionId),
|
|
1038
|
+
this.nullableStringExpression(eb, "last_attempted_at", expectedSource.lastAttemptedAt),
|
|
1039
|
+
this.nullableStringExpression(eb, "last_error_code", expectedSource.lastErrorCode),
|
|
1040
|
+
]);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
private nullableStringExpression(
|
|
1044
|
+
eb: ExpressionBuilder<Database, "_emdash_media_usage_sources">,
|
|
1045
|
+
column: MediaUsageSourceNullableStringColumn,
|
|
1046
|
+
value: string | null,
|
|
1047
|
+
) {
|
|
1048
|
+
return value === null ? eb(column, "is", null) : eb(column, "=", value);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
private nullableNumberExpression(
|
|
1052
|
+
eb: ExpressionBuilder<Database, "_emdash_media_usage_sources">,
|
|
1053
|
+
column: "source_version",
|
|
1054
|
+
value: number | null,
|
|
1055
|
+
) {
|
|
1056
|
+
return value === null ? eb(column, "is", null) : eb(column, "=", value);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
private async contentRowExists(tableName: string, contentId: string): Promise<boolean> {
|
|
1060
|
+
const result = await sql<{ id: string }>`
|
|
1061
|
+
SELECT id
|
|
1062
|
+
FROM ${sql.ref(tableName)}
|
|
1063
|
+
WHERE id = ${contentId}
|
|
1064
|
+
LIMIT 1
|
|
1065
|
+
`.execute(this.db);
|
|
1066
|
+
return result.rows.length > 0;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
private buildSourceRow(source: MediaUsageSourceInput, generation: string, now: string) {
|
|
1070
|
+
return {
|
|
274
1071
|
source_key: source.sourceKey,
|
|
275
1072
|
source_type: source.sourceType,
|
|
276
1073
|
collection_slug: source.collectionSlug ?? null,
|
|
@@ -286,46 +1083,110 @@ export class MediaUsageRepository {
|
|
|
286
1083
|
revision_id: source.revisionId ?? null,
|
|
287
1084
|
current_generation: generation,
|
|
288
1085
|
schema_version: source.schemaVersion ?? 1,
|
|
1086
|
+
source_updated_at: source.sourceUpdatedAt ?? null,
|
|
1087
|
+
source_version: source.sourceVersion ?? null,
|
|
1088
|
+
source_fingerprint: source.sourceFingerprint ?? null,
|
|
1089
|
+
// Complete means this source was fully refreshed for the extractor's current
|
|
1090
|
+
// schema/version coverage, not that every possible reference shape is known.
|
|
1091
|
+
source_completeness: source.sourceCompleteness ?? "complete",
|
|
1092
|
+
last_attempted_at: source.lastAttemptedAt ?? now,
|
|
1093
|
+
last_error_code: null,
|
|
289
1094
|
indexed_at: now,
|
|
290
1095
|
updated_at: now,
|
|
291
1096
|
};
|
|
1097
|
+
}
|
|
292
1098
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
.
|
|
296
|
-
.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
.
|
|
1099
|
+
private buildAttemptedSourceRow(source: MediaUsageSourceInput, now: string) {
|
|
1100
|
+
return {
|
|
1101
|
+
source_key: source.sourceKey,
|
|
1102
|
+
source_type: source.sourceType,
|
|
1103
|
+
collection_slug: source.collectionSlug ?? null,
|
|
1104
|
+
content_id: source.contentId ?? null,
|
|
1105
|
+
source_variant: source.sourceVariant,
|
|
1106
|
+
locale: source.locale ?? null,
|
|
1107
|
+
translation_group: source.translationGroup ?? null,
|
|
1108
|
+
content_slug: source.contentSlug ?? null,
|
|
1109
|
+
content_title: source.contentTitle ?? null,
|
|
1110
|
+
content_status: source.contentStatus ?? null,
|
|
1111
|
+
content_scheduled_at: source.contentScheduledAt ?? null,
|
|
1112
|
+
content_deleted_at: source.contentDeletedAt ?? null,
|
|
1113
|
+
revision_id: source.revisionId ?? null,
|
|
1114
|
+
current_generation: ulid(),
|
|
1115
|
+
schema_version: source.schemaVersion ?? 1,
|
|
1116
|
+
source_updated_at: source.sourceUpdatedAt ?? null,
|
|
1117
|
+
source_version: source.sourceVersion ?? null,
|
|
1118
|
+
source_fingerprint: source.sourceFingerprint ?? null,
|
|
1119
|
+
source_completeness:
|
|
1120
|
+
source.sourceCompleteness ?? (source.lastErrorCode ? "failed" : "unknown"),
|
|
1121
|
+
last_attempted_at: source.lastAttemptedAt ?? now,
|
|
1122
|
+
last_error_code: source.lastErrorCode ?? null,
|
|
1123
|
+
indexed_at: now,
|
|
1124
|
+
updated_at: now,
|
|
1125
|
+
};
|
|
317
1126
|
}
|
|
318
1127
|
|
|
319
|
-
private
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
.
|
|
326
|
-
.
|
|
327
|
-
.
|
|
328
|
-
.
|
|
1128
|
+
private attemptedSourceUpdateSet(
|
|
1129
|
+
source: MediaUsageSourceInput,
|
|
1130
|
+
row: ReturnType<MediaUsageRepository["buildAttemptedSourceRow"]>,
|
|
1131
|
+
): Updateable<MediaUsageSourceTable> {
|
|
1132
|
+
const updates: Updateable<MediaUsageSourceTable> = {
|
|
1133
|
+
source_type: row.source_type,
|
|
1134
|
+
source_variant: row.source_variant,
|
|
1135
|
+
source_completeness: row.source_completeness,
|
|
1136
|
+
last_attempted_at: row.last_attempted_at,
|
|
1137
|
+
last_error_code: row.last_error_code,
|
|
1138
|
+
updated_at: row.updated_at,
|
|
1139
|
+
};
|
|
1140
|
+
|
|
1141
|
+
if (source.collectionSlug !== undefined) updates.collection_slug = row.collection_slug;
|
|
1142
|
+
if (source.contentId !== undefined) updates.content_id = row.content_id;
|
|
1143
|
+
if (source.locale !== undefined) updates.locale = row.locale;
|
|
1144
|
+
if (source.translationGroup !== undefined) updates.translation_group = row.translation_group;
|
|
1145
|
+
if (source.contentSlug !== undefined) updates.content_slug = row.content_slug;
|
|
1146
|
+
if (source.contentTitle !== undefined) updates.content_title = row.content_title;
|
|
1147
|
+
if (source.contentStatus !== undefined) updates.content_status = row.content_status;
|
|
1148
|
+
if (source.contentScheduledAt !== undefined) {
|
|
1149
|
+
updates.content_scheduled_at = row.content_scheduled_at;
|
|
1150
|
+
}
|
|
1151
|
+
if (source.contentDeletedAt !== undefined) updates.content_deleted_at = row.content_deleted_at;
|
|
1152
|
+
if (source.revisionId !== undefined) updates.revision_id = row.revision_id;
|
|
1153
|
+
if (source.schemaVersion !== undefined) updates.schema_version = row.schema_version;
|
|
1154
|
+
if (source.sourceUpdatedAt !== undefined) updates.source_updated_at = row.source_updated_at;
|
|
1155
|
+
if (source.sourceVersion !== undefined) updates.source_version = row.source_version;
|
|
1156
|
+
if (source.sourceFingerprint !== undefined) {
|
|
1157
|
+
updates.source_fingerprint = row.source_fingerprint;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return updates;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
private sourceUpdateSet(
|
|
1164
|
+
row: ReturnType<MediaUsageRepository["buildSourceRow"]>,
|
|
1165
|
+
): Updateable<MediaUsageSourceTable> {
|
|
1166
|
+
return {
|
|
1167
|
+
source_type: row.source_type,
|
|
1168
|
+
collection_slug: row.collection_slug,
|
|
1169
|
+
content_id: row.content_id,
|
|
1170
|
+
source_variant: row.source_variant,
|
|
1171
|
+
locale: row.locale,
|
|
1172
|
+
translation_group: row.translation_group,
|
|
1173
|
+
content_slug: row.content_slug,
|
|
1174
|
+
content_title: row.content_title,
|
|
1175
|
+
content_status: row.content_status,
|
|
1176
|
+
content_scheduled_at: row.content_scheduled_at,
|
|
1177
|
+
content_deleted_at: row.content_deleted_at,
|
|
1178
|
+
revision_id: row.revision_id,
|
|
1179
|
+
current_generation: row.current_generation,
|
|
1180
|
+
schema_version: row.schema_version,
|
|
1181
|
+
source_updated_at: row.source_updated_at,
|
|
1182
|
+
source_version: row.source_version,
|
|
1183
|
+
source_fingerprint: row.source_fingerprint,
|
|
1184
|
+
source_completeness: row.source_completeness,
|
|
1185
|
+
last_attempted_at: row.last_attempted_at,
|
|
1186
|
+
last_error_code: row.last_error_code,
|
|
1187
|
+
indexed_at: row.indexed_at,
|
|
1188
|
+
updated_at: row.updated_at,
|
|
1189
|
+
};
|
|
329
1190
|
}
|
|
330
1191
|
}
|
|
331
1192
|
|
|
@@ -345,9 +1206,15 @@ const currentUsageSelect = [
|
|
|
345
1206
|
"s.revision_id as revision_id",
|
|
346
1207
|
"s.current_generation as current_generation",
|
|
347
1208
|
"s.schema_version as schema_version",
|
|
1209
|
+
"s.source_updated_at as source_updated_at",
|
|
1210
|
+
"s.source_version as source_version",
|
|
1211
|
+
"s.source_fingerprint as source_fingerprint",
|
|
1212
|
+
"s.source_completeness as source_completeness",
|
|
1213
|
+
"s.last_attempted_at as last_attempted_at",
|
|
1214
|
+
"s.last_error_code as last_error_code",
|
|
348
1215
|
"s.indexed_at as indexed_at",
|
|
349
1216
|
"s.created_at as source_created_at",
|
|
350
|
-
"s.updated_at as
|
|
1217
|
+
"s.updated_at as source_row_updated_at",
|
|
351
1218
|
"u.id as occurrence_id",
|
|
352
1219
|
"u.generation as generation",
|
|
353
1220
|
"u.field_slug as field_slug",
|
|
@@ -362,7 +1229,7 @@ const currentUsageSelect = [
|
|
|
362
1229
|
"u.created_at as occurrence_created_at",
|
|
363
1230
|
] as const;
|
|
364
1231
|
|
|
365
|
-
function rowToSource(row:
|
|
1232
|
+
function rowToSource(row: MediaUsageSourceRow): MediaUsageSource {
|
|
366
1233
|
return {
|
|
367
1234
|
sourceKey: row.source_key,
|
|
368
1235
|
sourceType: row.source_type,
|
|
@@ -379,6 +1246,12 @@ function rowToSource(row: Selectable<MediaUsageSourceTable>): MediaUsageSource {
|
|
|
379
1246
|
revisionId: row.revision_id,
|
|
380
1247
|
currentGeneration: row.current_generation,
|
|
381
1248
|
schemaVersion: Number(row.schema_version),
|
|
1249
|
+
sourceUpdatedAt: row.source_updated_at,
|
|
1250
|
+
sourceVersion: row.source_version === null ? null : Number(row.source_version),
|
|
1251
|
+
sourceFingerprint: row.source_fingerprint,
|
|
1252
|
+
sourceCompleteness: row.source_completeness,
|
|
1253
|
+
lastAttemptedAt: row.last_attempted_at,
|
|
1254
|
+
lastErrorCode: row.last_error_code,
|
|
382
1255
|
indexedAt: row.indexed_at,
|
|
383
1256
|
createdAt: row.created_at,
|
|
384
1257
|
updatedAt: row.updated_at,
|
|
@@ -421,9 +1294,15 @@ function rowToUsageRecord(row: JoinedUsageRow): MediaUsageRecord {
|
|
|
421
1294
|
revision_id: row.revision_id,
|
|
422
1295
|
current_generation: row.current_generation,
|
|
423
1296
|
schema_version: row.schema_version,
|
|
1297
|
+
source_updated_at: row.source_updated_at,
|
|
1298
|
+
source_version: row.source_version,
|
|
1299
|
+
source_fingerprint: row.source_fingerprint,
|
|
1300
|
+
source_completeness: row.source_completeness,
|
|
1301
|
+
last_attempted_at: row.last_attempted_at,
|
|
1302
|
+
last_error_code: row.last_error_code,
|
|
424
1303
|
indexed_at: row.indexed_at,
|
|
425
1304
|
created_at: row.source_created_at,
|
|
426
|
-
updated_at: row.
|
|
1305
|
+
updated_at: row.source_row_updated_at,
|
|
427
1306
|
}),
|
|
428
1307
|
occurrence: rowToOccurrence({
|
|
429
1308
|
id: row.occurrence_id,
|
|
@@ -442,3 +1321,20 @@ function rowToUsageRecord(row: JoinedUsageRow): MediaUsageRecord {
|
|
|
442
1321
|
}),
|
|
443
1322
|
};
|
|
444
1323
|
}
|
|
1324
|
+
|
|
1325
|
+
function rowToIndexStatus(row: Selectable<MediaUsageIndexStatusTable>): MediaUsageIndexStatus {
|
|
1326
|
+
return {
|
|
1327
|
+
adapterId: row.adapter_id,
|
|
1328
|
+
scopeType: row.scope_type,
|
|
1329
|
+
scopeKey: row.scope_key,
|
|
1330
|
+
status: row.status,
|
|
1331
|
+
schemaVersion: Number(row.schema_version),
|
|
1332
|
+
startedAt: row.started_at,
|
|
1333
|
+
completedAt: row.completed_at,
|
|
1334
|
+
cursor: row.cursor,
|
|
1335
|
+
indexedSourceCount: Number(row.indexed_source_count),
|
|
1336
|
+
failedSourceCount: Number(row.failed_source_count),
|
|
1337
|
+
lastErrorCode: row.last_error_code,
|
|
1338
|
+
updatedAt: row.updated_at,
|
|
1339
|
+
};
|
|
1340
|
+
}
|