emdash 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-34EN2eXO.d.mts → adapters-Bb1vG41D.d.mts} +8 -1
- package/dist/adapters-Bb1vG41D.d.mts.map +1 -0
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +9 -9
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +1 -1
- package/dist/{api-DdAsirBe.mjs → api-BeBglVxU.mjs} +10 -10
- package/dist/{api-DdAsirBe.mjs.map → api-BeBglVxU.mjs.map} +1 -1
- package/dist/{api-tokens-CFXu8F_2.mjs → api-tokens-KO24G1Fd.mjs} +2 -2
- package/dist/{api-tokens-CFXu8F_2.mjs.map → api-tokens-KO24G1Fd.mjs.map} +1 -1
- package/dist/{apply-D5zfeD09.mjs → apply-kUDt80-G.mjs} +16 -16
- package/dist/{apply-D5zfeD09.mjs.map → apply-kUDt80-G.mjs.map} +1 -1
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +71 -15
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.mjs +4 -4
- package/dist/astro/middleware/redirect.mjs +2 -2
- package/dist/astro/middleware/request-context.mjs +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +10 -10
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +143 -74
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +1 -1
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +2 -2
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +2 -2
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +3 -3
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +7 -7
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +7 -7
- package/dist/astro/routes/api/admin/bylines/index.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/comments/bulk.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/counts.mjs +2 -2
- package/dist/astro/routes/api/admin/comments/index.mjs +3 -3
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +1 -1
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +1 -1
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +7 -7
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +7 -7
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +4 -4
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs +7 -7
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +7 -7
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +1 -1
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +29 -29
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +28 -28
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +29 -29
- package/dist/astro/routes/api/admin/plugins/updates.mjs +28 -28
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +28 -28
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +28 -28
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +1 -1
- package/dist/astro/routes/api/admin/users/index.mjs +1 -1
- package/dist/astro/routes/api/auth/dev-bypass.d.mts.map +1 -1
- package/dist/astro/routes/api/auth/dev-bypass.mjs +2 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/index.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
- package/dist/astro/routes/api/auth/logout.mjs +1 -1
- package/dist/astro/routes/api/auth/magic-link/send.mjs +3 -3
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +1 -1
- package/dist/astro/routes/api/auth/me.mjs +2 -2
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +2 -2
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/request.mjs +3 -3
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +5 -5
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/authors.mjs +1 -1
- package/dist/astro/routes/api/content/_collection_/index.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/trash.mjs +2 -2
- package/dist/astro/routes/api/dashboard.mjs +4 -4
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +1 -1
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +7 -7
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
- package/dist/astro/routes/api/import/wordpress/media.mjs +2 -2
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +11 -11
- package/dist/astro/routes/api/manifest.mjs +2 -2
- package/dist/astro/routes/api/mcp.mjs +38 -38
- package/dist/astro/routes/api/media/_id_/confirm.mjs +2 -2
- package/dist/astro/routes/api/media/_id_/upload.mjs +2 -2
- package/dist/astro/routes/api/media/_id_/usage.mjs +6 -6
- package/dist/astro/routes/api/media/_id_.mjs +5 -5
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +1 -1
- package/dist/astro/routes/api/media/providers/index.mjs +1 -1
- package/dist/astro/routes/api/media/upload-url.mjs +3 -3
- package/dist/astro/routes/api/media.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_/items.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_/translations.mjs +3 -3
- package/dist/astro/routes/api/menus/_name_.mjs +3 -3
- package/dist/astro/routes/api/menus/index.mjs +3 -3
- package/dist/astro/routes/api/oauth/authorize.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
- package/dist/astro/routes/api/oauth/register.mjs +1 -1
- package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
- package/dist/astro/routes/api/oauth/token.mjs +4 -4
- package/dist/astro/routes/api/openapi.json.mjs +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/index.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/summary.mjs +3 -3
- package/dist/astro/routes/api/redirects/_id_.mjs +4 -4
- package/dist/astro/routes/api/redirects/index.mjs +4 -4
- package/dist/astro/routes/api/relations/_id_/index.mjs +9 -9
- package/dist/astro/routes/api/relations/_id_/translations.mjs +8 -8
- package/dist/astro/routes/api/relations/index.mjs +9 -9
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +1 -1
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/index.mjs +28 -28
- package/dist/astro/routes/api/schema/collections/reorder.mjs +28 -28
- package/dist/astro/routes/api/schema/index.mjs +6 -6
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +28 -28
- package/dist/astro/routes/api/schema/orphans/index.mjs +28 -28
- package/dist/astro/routes/api/search/enable.mjs +5 -5
- package/dist/astro/routes/api/search/index.mjs +4 -4
- package/dist/astro/routes/api/search/rebuild.mjs +5 -5
- package/dist/astro/routes/api/search/stats.mjs +4 -4
- package/dist/astro/routes/api/search/suggest.mjs +4 -4
- package/dist/astro/routes/api/sections/_slug_.mjs +4 -4
- package/dist/astro/routes/api/sections/index.mjs +4 -4
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +3 -3
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +3 -3
- package/dist/astro/routes/api/settings/backups/export.mjs +3 -3
- package/dist/astro/routes/api/settings/backups/index.mjs +3 -3
- package/dist/astro/routes/api/settings/email.mjs +1 -1
- package/dist/astro/routes/api/settings.mjs +5 -5
- package/dist/astro/routes/api/setup/admin-verify.mjs +4 -4
- package/dist/astro/routes/api/setup/admin.mjs +4 -4
- package/dist/astro/routes/api/setup/dev-bypass.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +18 -21
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/index.mjs +15 -21
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +2 -2
- package/dist/astro/routes/api/snapshot.mjs +4 -4
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/index.mjs +6 -6
- package/dist/astro/routes/api/themes/preview.mjs +2 -2
- package/dist/astro/routes/api/typegen.mjs +3 -3
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +1 -1
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +4 -4
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +4 -4
- package/dist/astro/routes/api/widget-areas/_name_.mjs +3 -3
- package/dist/astro/routes/api/widget-areas/index.mjs +4 -4
- package/dist/astro/routes/robots.txt.mjs +2 -2
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +2 -2
- package/dist/astro/routes/sitemap.xml.mjs +2 -2
- package/dist/astro/types.d.mts +14 -14
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-1iFJzPaZ.mjs → authorize-DUElxgOQ.mjs} +1 -1
- package/dist/{authorize-1iFJzPaZ.mjs.map → authorize-DUElxgOQ.mjs.map} +1 -1
- package/dist/{backup-LHjIAVNP.mjs → backup-DWYpJlLj.mjs} +3 -3
- package/dist/{backup-LHjIAVNP.mjs.map → backup-DWYpJlLj.mjs.map} +1 -1
- package/dist/{bundle-CBEXrBnb.mjs → bundle-vn56sLos.mjs} +5 -5
- package/dist/{bundle-CBEXrBnb.mjs.map → bundle-vn56sLos.mjs.map} +1 -1
- package/dist/{byline-fields-C5S8wygO.mjs → byline-fields-txaLGoME.mjs} +1 -1
- package/dist/{byline-fields-C5S8wygO.mjs.map → byline-fields-txaLGoME.mjs.map} +1 -1
- package/dist/{byline-pEcoDps8.mjs → byline-yjvWq4ip.mjs} +4 -4
- package/dist/{byline-pEcoDps8.mjs.map → byline-yjvWq4ip.mjs.map} +1 -1
- package/dist/{bylines-DJfSfXCL.mjs → bylines-Bu7pchQ4.mjs} +3 -3
- package/dist/{bylines-DJfSfXCL.mjs.map → bylines-Bu7pchQ4.mjs.map} +1 -1
- package/dist/{bylines-BWemdX2k.mjs → bylines-DjXS5p0P.mjs} +5 -5
- package/dist/{bylines-BWemdX2k.mjs.map → bylines-DjXS5p0P.mjs.map} +1 -1
- package/dist/{cache-CBJaDy_E.mjs → cache-C46-3Irl.mjs} +2 -2
- package/dist/{cache-CBJaDy_E.mjs.map → cache-C46-3Irl.mjs.map} +1 -1
- package/dist/{challenge-store-CZyFVaad.mjs → challenge-store-SIL5DsF2.mjs} +1 -1
- package/dist/{challenge-store-CZyFVaad.mjs.map → challenge-store-SIL5DsF2.mjs.map} +1 -1
- package/dist/{chunks-DfPax1I0.mjs → chunks-fiZ8G9RJ.mjs} +2 -2
- package/dist/{chunks-DfPax1I0.mjs.map → chunks-fiZ8G9RJ.mjs.map} +1 -1
- package/dist/cli/index.mjs +551 -51
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.mjs +2 -2
- package/dist/{collection-deletion-C8BugSGz.mjs → collection-deletion-DQPvMD8R.mjs} +2 -2
- package/dist/{collection-deletion-C8BugSGz.mjs.map → collection-deletion-DQPvMD8R.mjs.map} +1 -1
- package/dist/{comment-reaction-D6qfHKBc.mjs → comment-reaction-D-2J84UE.mjs} +2 -2
- package/dist/{comment-reaction-D6qfHKBc.mjs.map → comment-reaction-D-2J84UE.mjs.map} +1 -1
- package/dist/{comments-CTOuhTSX.mjs → comments-CTfZtLmM.mjs} +1 -1
- package/dist/{comments-CTOuhTSX.mjs.map → comments-CTfZtLmM.mjs.map} +1 -1
- package/dist/config-COQzTe5Q.d.mts +34 -0
- package/dist/config-COQzTe5Q.d.mts.map +1 -0
- package/dist/{content-BNfwh7xX.mjs → content-ZHCc1ngN.mjs} +3 -3
- package/dist/{content-BNfwh7xX.mjs.map → content-ZHCc1ngN.mjs.map} +1 -1
- package/dist/{content-list-query-DhQ84eVV.d.mts → content-list-query-wJ2WN8Vp.d.mts} +1 -1
- package/dist/{content-list-query-DhQ84eVV.d.mts.map → content-list-query-wJ2WN8Vp.d.mts.map} +1 -1
- package/dist/{content-refresh-DNERP4-f.mjs → content-refresh-B_8SJzuU.mjs} +2 -2
- package/dist/{content-refresh-DNERP4-f.mjs.map → content-refresh-B_8SJzuU.mjs.map} +1 -1
- package/dist/{context-DRgOvSnW.mjs → context-ZldbZcle.mjs} +7 -7
- package/dist/{context-DRgOvSnW.mjs.map → context-ZldbZcle.mjs.map} +1 -1
- package/dist/{dashboard-CB8J89rJ.mjs → dashboard-DvHbk_cv.mjs} +3 -3
- package/dist/{dashboard-CB8J89rJ.mjs.map → dashboard-DvHbk_cv.mjs.map} +1 -1
- package/dist/db/index.d.mts +4 -4
- package/dist/db/index.mjs +30 -7
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql-migrations.d.mts +11 -0
- package/dist/db/libsql-migrations.d.mts.map +1 -0
- package/dist/db/libsql-migrations.mjs +57 -0
- package/dist/db/libsql-migrations.mjs.map +1 -0
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres-migrations.d.mts +10 -0
- package/dist/db/postgres-migrations.d.mts.map +1 -0
- package/dist/db/postgres-migrations.mjs +47 -0
- package/dist/db/postgres-migrations.mjs.map +1 -0
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite-migrations.d.mts +10 -0
- package/dist/db/sqlite-migrations.d.mts.map +1 -0
- package/dist/db/sqlite-migrations.mjs +28 -0
- package/dist/db/sqlite-migrations.mjs.map +1 -0
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{deferred-tasks-BMsa9F64.d.mts → deferred-tasks-SDWxrgzJ.d.mts} +1 -1
- package/dist/{deferred-tasks-BMsa9F64.d.mts.map → deferred-tasks-SDWxrgzJ.d.mts.map} +1 -1
- package/dist/{device-flow-CaioYGK3.mjs → device-flow-C0GdD_z8.mjs} +4 -4
- package/dist/{device-flow-CaioYGK3.mjs.map → device-flow-C0GdD_z8.mjs.map} +1 -1
- package/dist/direct-executor-CDXeJCSs.mjs +85 -0
- package/dist/direct-executor-CDXeJCSs.mjs.map +1 -0
- package/dist/{escape-C_aLKP3H.mjs → escape-DUY2r2Zs.mjs} +1 -1
- package/dist/{escape-C_aLKP3H.mjs.map → escape-DUY2r2Zs.mjs.map} +1 -1
- package/dist/{field-defs-cache-hg6QkjV9.mjs → field-defs-cache-BzwBlJrR.mjs} +2 -2
- package/dist/{field-defs-cache-hg6QkjV9.mjs.map → field-defs-cache-BzwBlJrR.mjs.map} +1 -1
- package/dist/{fts-manager-CZ-5dVwJ.mjs → fts-manager-DyZEohZC.mjs} +2 -2
- package/dist/{fts-manager-CZ-5dVwJ.mjs.map → fts-manager-DyZEohZC.mjs.map} +1 -1
- package/dist/{hreflang-DNswoYsi.mjs → hreflang-BBe83kKa.mjs} +5 -5
- package/dist/{hreflang-DNswoYsi.mjs.map → hreflang-BBe83kKa.mjs.map} +1 -1
- package/dist/{hreflang-D3Uehfv2.d.mts → hreflang-DwT2E0Sa.d.mts} +2 -2
- package/dist/{hreflang-D3Uehfv2.d.mts.map → hreflang-DwT2E0Sa.d.mts.map} +1 -1
- package/dist/identity-Ys6Dlc9C.mjs +35 -0
- package/dist/identity-Ys6Dlc9C.mjs.map +1 -0
- package/dist/{import-B41u8Rot.mjs → import-DPkLmeXO.mjs} +2 -2
- package/dist/{import-B41u8Rot.mjs.map → import-DPkLmeXO.mjs.map} +1 -1
- package/dist/{index-C3Y-rsyN.d.mts → index-BVBZj4-7.d.mts} +28 -49
- package/dist/index-BVBZj4-7.d.mts.map +1 -0
- package/dist/{index-Cof4-Fab.d.mts → index-C9KuQyO-.d.mts} +3 -3
- package/dist/{index-Cof4-Fab.d.mts.map → index-C9KuQyO-.d.mts.map} +1 -1
- package/dist/index.d.mts +21 -20
- package/dist/index.mjs +42 -42
- package/dist/{load-DmvJrtxS.mjs → load-YJ2xl3ra.mjs} +2 -2
- package/dist/{load-DmvJrtxS.mjs.map → load-YJ2xl3ra.mjs.map} +1 -1
- package/dist/{loader-DLBqRiGP.mjs → loader-CdG_Ci3H.mjs} +2 -2
- package/dist/{loader-DLBqRiGP.mjs.map → loader-CdG_Ci3H.mjs.map} +1 -1
- package/dist/manifest-builder-B252yQUf.mjs +189 -0
- package/dist/manifest-builder-B252yQUf.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/local-runtime.d.mts +12 -12
- package/dist/media/local-runtime.mjs +2 -2
- package/dist/{media-allowlist-BD6HhgXA.mjs → media-allowlist-D6x-Us02.mjs} +1 -1
- package/dist/{media-allowlist-BD6HhgXA.mjs.map → media-allowlist-D6x-Us02.mjs.map} +1 -1
- package/dist/{media-upload-9yas9zGH.mjs → media-upload-CHBVn6m-.mjs} +2 -2
- package/dist/{media-upload-9yas9zGH.mjs.map → media-upload-CHBVn6m-.mjs.map} +1 -1
- package/dist/{media-usage-De3qvwjc.mjs → media-usage-BZCydfvu.mjs} +4 -4
- package/dist/{media-usage-De3qvwjc.mjs.map → media-usage-BZCydfvu.mjs.map} +1 -1
- package/dist/{media-usage-work-B9Bksh_b.mjs → media-usage-work-CLo_6tct.mjs} +2 -2
- package/dist/{media-usage-work-B9Bksh_b.mjs.map → media-usage-work-CLo_6tct.mjs.map} +1 -1
- package/dist/{menus-CC-yn1F9.mjs → menus-DSo5QeW0.mjs} +6 -6
- package/dist/{menus-CC-yn1F9.mjs.map → menus-DSo5QeW0.mjs.map} +1 -1
- package/dist/{menus-fxvEDYE6.mjs → menus-TCNSd2vp.mjs} +2 -2
- package/dist/{menus-fxvEDYE6.mjs.map → menus-TCNSd2vp.mjs.map} +1 -1
- package/dist/migrations/index.d.mts +22 -0
- package/dist/migrations/index.d.mts.map +1 -0
- package/dist/migrations/index.mjs +7 -0
- package/dist/{mode-Bpe-tNNy.mjs → mode-BDoH_WcF.mjs} +1 -1
- package/dist/{mode-Bpe-tNNy.mjs.map → mode-BDoH_WcF.mjs.map} +1 -1
- package/dist/{oauth-authorization-BXO1MKej.mjs → oauth-authorization-iOikmrJd.mjs} +4 -4
- package/dist/{oauth-authorization-BXO1MKej.mjs.map → oauth-authorization-iOikmrJd.mjs.map} +1 -1
- package/dist/{oauth-clients-C_DNqtRu.mjs → oauth-clients-CvI0nHMJ.mjs} +1 -1
- package/dist/{oauth-clients-C_DNqtRu.mjs.map → oauth-clients-CvI0nHMJ.mjs.map} +1 -1
- package/dist/{oauth-state-store-DwAxMtk4.mjs → oauth-state-store-Ba8KZlwW.mjs} +1 -1
- package/dist/{oauth-state-store-DwAxMtk4.mjs.map → oauth-state-store-Ba8KZlwW.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-BxfZizdk.mjs → oauth-user-lookup-C5SQwGYC.mjs} +1 -1
- package/dist/{oauth-user-lookup-BxfZizdk.mjs.map → oauth-user-lookup-C5SQwGYC.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{options-DRe1pIBL.d.mts → options--hhz-jZ1.d.mts} +3 -3
- package/dist/{options-DRe1pIBL.d.mts.map → options--hhz-jZ1.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{passkey-config-_2GOwwK8.mjs → passkey-config-DFAmUyuE.mjs} +1 -1
- package/dist/{passkey-config-_2GOwwK8.mjs.map → passkey-config-DFAmUyuE.mjs.map} +1 -1
- package/dist/{placeholder-CzW7Lshs.d.mts → placeholder-Dp0K3RkD.d.mts} +1 -1
- package/dist/{placeholder-CzW7Lshs.d.mts.map → placeholder-Dp0K3RkD.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
- package/dist/policy-TluUIxuu.mjs +45 -0
- package/dist/policy-TluUIxuu.mjs.map +1 -0
- package/dist/{portable-text-DhKbDAGr.mjs → portable-text-imBW5cHK.mjs} +1 -1
- package/dist/{portable-text-DhKbDAGr.mjs.map → portable-text-imBW5cHK.mjs.map} +1 -1
- package/dist/protocol-DjsJHTlK.d.mts +52 -0
- package/dist/protocol-DjsJHTlK.d.mts.map +1 -0
- package/dist/{query-vgZNwIhh.mjs → query-BWs7O5Il.mjs} +3 -3
- package/dist/{query-vgZNwIhh.mjs.map → query-BWs7O5Il.mjs.map} +1 -1
- package/dist/{query-BgjfiLUQ.mjs → query-zTVAR1Sl.mjs} +12 -12
- package/dist/{query-BgjfiLUQ.mjs.map → query-zTVAR1Sl.mjs.map} +1 -1
- package/dist/{rate-limit-L5anykgo.mjs → rate-limit-Bx4g9m3p.mjs} +1 -1
- package/dist/{rate-limit-L5anykgo.mjs.map → rate-limit-Bx4g9m3p.mjs.map} +1 -1
- package/dist/{redirect-KBNE3UEs.mjs → redirect-BgAOGHDq.mjs} +1 -1
- package/dist/{redirect-KBNE3UEs.mjs.map → redirect-BgAOGHDq.mjs.map} +1 -1
- package/dist/{redirects-cV9g7V-0.mjs → redirects-DTDHWH31.mjs} +2 -2
- package/dist/{redirects-cV9g7V-0.mjs.map → redirects-DTDHWH31.mjs.map} +1 -1
- package/dist/{redirects-CufRQt0Q.mjs → redirects-DyChwDkG.mjs} +1 -1
- package/dist/{redirects-CufRQt0Q.mjs.map → redirects-DyChwDkG.mjs.map} +1 -1
- package/dist/{registry-C581V4TP.mjs → registry-Cqa2I749.mjs} +6 -6
- package/dist/{registry-C581V4TP.mjs.map → registry-Cqa2I749.mjs.map} +1 -1
- package/dist/{relation-DtmAQH7s.mjs → relation-BnHB0T4J.mjs} +2 -2
- package/dist/{relation-DtmAQH7s.mjs.map → relation-BnHB0T4J.mjs.map} +1 -1
- package/dist/{relations-DItI3MN9.d.mts → relations-BHo5A8Zf.d.mts} +35 -35
- package/dist/{relations-DItI3MN9.d.mts.map → relations-BHo5A8Zf.d.mts.map} +1 -1
- package/dist/{relations-DICSyfsh.mjs → relations-D5sM5fW0.mjs} +4 -4
- package/dist/{relations-DICSyfsh.mjs.map → relations-D5sM5fW0.mjs.map} +1 -1
- package/dist/request-context.d.mts +1 -1
- package/dist/{runner-CgQbELYC.d.mts → runner-BYHZn3Jy.d.mts} +16 -3
- package/dist/runner-BYHZn3Jy.d.mts.map +1 -0
- package/dist/{runner-BiDvq5dv.mjs → runner-C1T11CfC.mjs} +34 -4
- package/dist/{runner-BiDvq5dv.mjs.map → runner-C1T11CfC.mjs.map} +1 -1
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-CeQSNwB3.mjs → schema-2AOsSn2v.mjs} +4 -4
- package/dist/{schema-CeQSNwB3.mjs.map → schema-2AOsSn2v.mjs.map} +1 -1
- package/dist/{schema-BFyTg38T.mjs → schema-DZ5YVVZj.mjs} +3 -3
- package/dist/{schema-BFyTg38T.mjs.map → schema-DZ5YVVZj.mjs.map} +1 -1
- package/dist/{scopes-BVeuhGBE.mjs → scopes-8ClPF2bC.mjs} +2 -2
- package/dist/{scopes-BVeuhGBE.mjs.map → scopes-8ClPF2bC.mjs.map} +1 -1
- package/dist/{search-BpuPKZb0.mjs → search-u1QYdOCG.mjs} +4 -4
- package/dist/{search-BpuPKZb0.mjs.map → search-u1QYdOCG.mjs.map} +1 -1
- package/dist/{secrets-dtYs2hDo.mjs → secrets-BEgsAQEU.mjs} +1 -1
- package/dist/{secrets-dtYs2hDo.mjs.map → secrets-BEgsAQEU.mjs.map} +1 -1
- package/dist/{sections-COctk6hf.mjs → sections-DmHviGyM.mjs} +2 -2
- package/dist/{sections-COctk6hf.mjs.map → sections-DmHviGyM.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +13 -13
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +9 -9
- package/dist/{seo-CvDxfvFb.mjs → seo-Dq4G-RrO.mjs} +2 -2
- package/dist/{seo-CvDxfvFb.mjs.map → seo-Dq4G-RrO.mjs.map} +1 -1
- package/dist/{service-xMiMSTsR.mjs → service-BpArayPr.mjs} +2 -2
- package/dist/{service-xMiMSTsR.mjs.map → service-BpArayPr.mjs.map} +1 -1
- package/dist/{session-user-BzVemLkw.mjs → session-user-BEorKVT-.mjs} +1 -1
- package/dist/{session-user-BzVemLkw.mjs.map → session-user-BEorKVT-.mjs.map} +1 -1
- package/dist/{settings-BE9aqn_9.mjs → settings-DE9xtzqd.mjs} +3 -3
- package/dist/{settings-BE9aqn_9.mjs.map → settings-DE9xtzqd.mjs.map} +1 -1
- package/dist/{settings-B372MV0p.mjs → settings-xRdoUZr0.mjs} +2 -2
- package/dist/{settings-B372MV0p.mjs.map → settings-xRdoUZr0.mjs.map} +1 -1
- package/dist/{setup-complete-DF7TAfD5.mjs → setup-complete-BnT8pq_C.mjs} +1 -1
- package/dist/{setup-complete-DF7TAfD5.mjs.map → setup-complete-BnT8pq_C.mjs.map} +1 -1
- package/dist/{setup-nonce-Do-U_cg9.mjs → setup-nonce-CBitf_oQ.mjs} +1 -1
- package/dist/{setup-nonce-Do-U_cg9.mjs.map → setup-nonce-CBitf_oQ.mjs.map} +1 -1
- package/dist/{site-url-Bowsi9T-.mjs → site-url-DjpIXMXJ.mjs} +1 -1
- package/dist/{site-url-Bowsi9T-.mjs.map → site-url-DjpIXMXJ.mjs.map} +1 -1
- package/dist/{snapshot-B5h6aPMh.mjs → snapshot-BBlGkb2q.mjs} +1 -1
- package/dist/{snapshot-B5h6aPMh.mjs.map → snapshot-BBlGkb2q.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +2 -2
- package/dist/storage/s3.d.mts +1 -1
- package/dist/target-DixqqV5i.mjs +27 -0
- package/dist/target-DixqqV5i.mjs.map +1 -0
- package/dist/{taxonomies-B0o9ExNV.mjs → taxonomies-BlrncHi9.mjs} +5 -5
- package/dist/{taxonomies-B0o9ExNV.mjs.map → taxonomies-BlrncHi9.mjs.map} +1 -1
- package/dist/{taxonomies-Dk3MAklL.mjs → taxonomies-OHUIiIpI.mjs} +4 -4
- package/dist/{taxonomies-Dk3MAklL.mjs.map → taxonomies-OHUIiIpI.mjs.map} +1 -1
- package/dist/{taxonomy-CPeM8d2o.mjs → taxonomy-BqCZ1TdU.mjs} +3 -3
- package/dist/{taxonomy-CPeM8d2o.mjs.map → taxonomy-BqCZ1TdU.mjs.map} +1 -1
- package/dist/{transport--CfOflrp.mjs → transport-DBJwa-Si.mjs} +1 -1
- package/dist/{transport--CfOflrp.mjs.map → transport-DBJwa-Si.mjs.map} +1 -1
- package/dist/{transport-B_rfeU-a.d.mts → transport-RQE36APG.d.mts} +1 -1
- package/dist/{transport-B_rfeU-a.d.mts.map → transport-RQE36APG.d.mts.map} +1 -1
- package/dist/{types-CFUfyt1s.d.mts → types-BcqdcT-r.d.mts} +1 -1
- package/dist/{types-CFUfyt1s.d.mts.map → types-BcqdcT-r.d.mts.map} +1 -1
- package/dist/{types-CPx2lIGo.d.mts → types-BntycPTF.d.mts} +1 -1
- package/dist/{types-CPx2lIGo.d.mts.map → types-BntycPTF.d.mts.map} +1 -1
- package/dist/{types-C3_AbkDP.d.mts → types-C2pMvO62.d.mts} +1 -1
- package/dist/{types-C3_AbkDP.d.mts.map → types-C2pMvO62.d.mts.map} +1 -1
- package/dist/{types-DefV3OiO.d.mts → types-CXnOzDe1.d.mts} +1 -1
- package/dist/{types-DefV3OiO.d.mts.map → types-CXnOzDe1.d.mts.map} +1 -1
- package/dist/{types-D4_1Ga4L.d.mts → types-DN64_zzZ.d.mts} +3 -3
- package/dist/{types-D4_1Ga4L.d.mts.map → types-DN64_zzZ.d.mts.map} +1 -1
- package/dist/{types-CAGdePf3.d.mts → types-DQIDRKF2.d.mts} +1 -1
- package/dist/{types-CAGdePf3.d.mts.map → types-DQIDRKF2.d.mts.map} +1 -1
- package/dist/{types-BPuloofb.d.mts → types-DWRhk9xZ.d.mts} +1 -1
- package/dist/{types-BPuloofb.d.mts.map → types-DWRhk9xZ.d.mts.map} +1 -1
- package/dist/{types-X0MThr7x.d.mts → types-Dfk8KI5R.d.mts} +1 -1
- package/dist/{types-X0MThr7x.d.mts.map → types-Dfk8KI5R.d.mts.map} +1 -1
- package/dist/{types-Cs-TFLNf.d.mts → types-DrJrVCT1.d.mts} +3 -3
- package/dist/{types-Cs-TFLNf.d.mts.map → types-DrJrVCT1.d.mts.map} +1 -1
- package/dist/{upload-attempts-hlne3iA1.mjs → upload-attempts-D4FS8LT6.mjs} +1 -1
- package/dist/{upload-attempts-hlne3iA1.mjs.map → upload-attempts-D4FS8LT6.mjs.map} +1 -1
- package/dist/{user-sHgJ62SP.mjs → user-DJZfUJVE.mjs} +2 -2
- package/dist/{user-sHgJ62SP.mjs.map → user-DJZfUJVE.mjs.map} +1 -1
- package/dist/{validate-BkqIG_Uj.mjs → validate-BMLBvxOC.mjs} +2 -2
- package/dist/{validate-BkqIG_Uj.mjs.map → validate-BMLBvxOC.mjs.map} +1 -1
- package/dist/{validate-DeGtBN84.d.mts → validate-CTqt5nuE.d.mts} +5 -5
- package/dist/{validate-DeGtBN84.d.mts.map → validate-CTqt5nuE.d.mts.map} +1 -1
- package/dist/{validation-CiJb_X5o.mjs → validation-npvENUCB.mjs} +7 -7
- package/dist/{validation-CiJb_X5o.mjs.map → validation-npvENUCB.mjs.map} +1 -1
- package/dist/version-DB_fqCSI.mjs +7 -0
- package/dist/{version-x0NjTScG.mjs.map → version-DB_fqCSI.mjs.map} +1 -1
- package/dist/{widgets-Cwzvww0W.mjs → widgets-BD3ho7Rq.mjs} +2 -2
- package/dist/{widgets-Cwzvww0W.mjs.map → widgets-BD3ho7Rq.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-QZjWxjyk.mjs → wxr-taxonomies-B1R3vBoD.mjs} +3 -3
- package/dist/{wxr-taxonomies-QZjWxjyk.mjs.map → wxr-taxonomies-B1R3vBoD.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-CmC0ZsDr.d.mts → wxr-taxonomies-Ca68766D.d.mts} +1 -1
- package/dist/{wxr-taxonomies-CmC0ZsDr.d.mts.map → wxr-taxonomies-Ca68766D.d.mts.map} +1 -1
- package/dist/{zod-generator-D11MhxG4.mjs → zod-generator-LC-on_Vb.mjs} +2 -2
- package/dist/{zod-generator-D11MhxG4.mjs.map → zod-generator-LC-on_Vb.mjs.map} +1 -1
- package/package.json +21 -5
- package/src/astro/integration/index.ts +46 -14
- package/src/astro/integration/runtime.ts +3 -0
- package/src/astro/middleware/scoped-db.ts +21 -0
- package/src/astro/middleware.ts +67 -10
- package/src/astro/routes/api/auth/dev-bypass.ts +0 -4
- package/src/astro/routes/api/setup/dev-bypass.ts +2 -7
- package/src/astro/routes/api/setup/index.ts +5 -13
- package/src/cli/commands/migrate.ts +593 -0
- package/src/cli/index.ts +2 -23
- package/src/database/index.ts +8 -2
- package/src/database/migrations/policy.ts +70 -0
- package/src/database/migrations/runner.ts +49 -3
- package/src/db/adapters.ts +50 -2
- package/src/db/index.ts +3 -1
- package/src/db/libsql-migrations.ts +72 -0
- package/src/db/postgres-migrations.ts +59 -0
- package/src/db/sqlite-migrations.ts +37 -0
- package/src/emdash-runtime.ts +44 -15
- package/src/i18n/normalize.ts +31 -0
- package/src/index.ts +7 -1
- package/src/migrations/config-loader.ts +205 -0
- package/src/migrations/direct-executor.ts +141 -0
- package/src/migrations/identity.ts +46 -0
- package/src/migrations/index.ts +19 -0
- package/src/migrations/integration-metadata.ts +67 -0
- package/src/migrations/manifest-builder.ts +55 -0
- package/src/migrations/manifest-writer.ts +63 -0
- package/src/migrations/manifest.ts +197 -0
- package/src/migrations/protocol.ts +59 -0
- package/src/migrations/target.ts +35 -0
- package/src/virtual-modules.d.ts +8 -0
- package/dist/adapters-34EN2eXO.d.mts.map +0 -1
- package/dist/index-C3Y-rsyN.d.mts.map +0 -1
- package/dist/runner-CgQbELYC.d.mts.map +0 -1
- package/dist/version-x0NjTScG.mjs +0 -7
- /package/dist/{api-tokens-CyTsLszZ.mjs → api-tokens-rIXC6LdE.mjs} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-C581V4TP.mjs","names":[],"sources":["../src/media/usage/activation.ts","../src/schema/registry.ts"],"sourcesContent":["import { sql, type Kysely, type RawBuilder, type Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { isPostgres, tableExists } from \"../../database/dialect-helpers.js\";\nimport type { Database, MediaUsageActivationTable } from \"../../database/types.js\";\nimport {\n\tinstallMediaUsageCaptureTriggers,\n\tverifyMediaUsageCaptureTriggers,\n} from \"./capture-triggers.js\";\n\nconst ACTIVATION_KEY = \"incremental_capture\";\nconst ACTIVATION_ERROR_CODE = \"MEDIA_USAGE_ACTIVATION_FAILED\";\nexport const MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION = 1;\n\nexport const MEDIA_USAGE_ACTIVATION_LIMITS = Object.freeze({\n\tcollectionsPerCall: 1,\n\tleaseDurationSeconds: 5 * 60,\n});\n\nexport type MediaUsageActivationResult =\n\t| { outcome: \"active\"; processedCollections: number }\n\t| {\n\t\t\toutcome: \"activating\";\n\t\t\tprocessedCollections: number;\n\t\t\tcollectionCursor: string | null;\n\t }\n\t| { outcome: \"lease_active\"; leaseExpiresAt: string }\n\t| { outcome: \"conflict\"; processedCollections: number };\n\nexport interface MediaUsageCollectionCapturePreparation {\n\tcaptureRequired: boolean;\n\tcollectionId: string;\n\tregistrationExists: boolean;\n\tresuming: boolean;\n}\n\nexport async function canResumeMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string; creationFingerprint?: string },\n): Promise<boolean> {\n\tconst activation = await findActivationIfAvailable(db);\n\tif (!activation) return false;\n\tassertRuntimeGeneration(activation);\n\tif (activation.state !== \"active\") return false;\n\n\tconst lifecycle = await db\n\t\t.selectFrom(\"_emdash_media_usage_index_status\")\n\t\t.select([\"collection_id\", \"cursor\"])\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", identity.collectionSlug)\n\t\t.where(\"collection_id\", \"=\", identity.collectionId)\n\t\t.where(\"capture_state\", \"in\", [\"installing\", \"ready\"])\n\t\t.executeTakeFirst();\n\treturn (\n\t\tlifecycle?.collection_id === identity.collectionId &&\n\t\t(identity.creationFingerprint\n\t\t\t? lifecycle.cursor === identity.creationFingerprint\n\t\t\t: lifecycle.cursor === null)\n\t);\n}\n\nexport async function prepareMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tinput: {\n\t\tcollectionId: string;\n\t\tcollectionSlug: string;\n\t\tcreationFingerprint?: string;\n\t\tregisteredCollectionId?: string;\n\t},\n): Promise<MediaUsageCollectionCapturePreparation> {\n\tconst activation = await findActivationIfAvailable(db);\n\tif (!activation) {\n\t\treturn {\n\t\t\tcaptureRequired: false,\n\t\t\tcollectionId: input.collectionId,\n\t\t\tregistrationExists: input.registeredCollectionId !== undefined,\n\t\t\tresuming: false,\n\t\t};\n\t}\n\tassertRuntimeGeneration(activation);\n\tif (activation.state !== \"active\") {\n\t\treturn {\n\t\t\tcaptureRequired: false,\n\t\t\tcollectionId: input.collectionId,\n\t\t\tregistrationExists: input.registeredCollectionId !== undefined,\n\t\t\tresuming: false,\n\t\t};\n\t}\n\n\tconst existing = await db\n\t\t.selectFrom(\"_emdash_media_usage_index_status\")\n\t\t.select([\"collection_id\", \"capture_state\", \"cursor\"])\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", input.collectionSlug)\n\t\t.executeTakeFirst();\n\tif (existing) {\n\t\tif (\n\t\t\texisting.collection_id &&\n\t\t\t(existing.capture_state === \"installing\" || existing.capture_state === \"ready\") &&\n\t\t\t(input.creationFingerprint\n\t\t\t\t? existing.cursor === input.creationFingerprint\n\t\t\t\t: existing.cursor === null) &&\n\t\t\t(input.registeredCollectionId === undefined ||\n\t\t\t\tinput.registeredCollectionId === existing.collection_id)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tcaptureRequired: true,\n\t\t\t\tcollectionId: existing.collection_id,\n\t\t\t\tregistrationExists: input.registeredCollectionId !== undefined,\n\t\t\t\tresuming: true,\n\t\t\t};\n\t\t}\n\t\tthrow new Error(\"Media usage collection lifecycle identity conflict\");\n\t}\n\tif (input.registeredCollectionId !== undefined) {\n\t\tthrow new Error(\"Media usage collection lifecycle is missing\");\n\t}\n\n\tawait db\n\t\t.insertInto(\"_emdash_media_usage_index_status\")\n\t\t.values({\n\t\t\tadapter_id: \"content-media\",\n\t\t\tscope_type: \"collection\",\n\t\t\tscope_key: input.collectionSlug,\n\t\t\tstatus: \"never\",\n\t\t\tcollection_id: input.collectionId,\n\t\t\treconciliation_required: 1,\n\t\t\tcapture_state: \"installing\",\n\t\t\tcursor: input.creationFingerprint ?? null,\n\t\t\tupdated_at: timestampOffset(db, 0),\n\t\t})\n\t\t.execute();\n\treturn {\n\t\tcaptureRequired: true,\n\t\tcollectionId: input.collectionId,\n\t\tregistrationExists: false,\n\t\tresuming: false,\n\t};\n}\n\nexport async function installPreparedMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string },\n): Promise<void> {\n\tawait installMediaUsageCaptureTriggers(db, identity, { replaceExisting: false });\n\tif (!(await verifyMediaUsageCaptureTriggers(db, identity))) {\n\t\tthrow new Error(\"Media usage capture trigger verification failed\");\n\t}\n}\n\nexport async function markMediaUsageCollectionCaptureReady(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string },\n): Promise<void> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_index_status\")\n\t\t.set({ capture_state: \"ready\", updated_at: timestampOffset(db, 0) })\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", identity.collectionSlug)\n\t\t.where(\"collection_id\", \"=\", identity.collectionId)\n\t\t.where(\"capture_state\", \"in\", [\"installing\", \"ready\"])\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_media_usage_activation AS activation\n\t\t\t\tWHERE activation.task_key = ${ACTIVATION_KEY}\n\t\t\t\t\tAND activation.state = 'active'\n\t\t\t\t\tAND activation.runtime_generation = ${MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION}\n\t\t\t)`,\n\t\t)\n\t\t.executeTakeFirst();\n\tif (Number(result.numUpdatedRows ?? 0) === 0) {\n\t\tthrow new Error(\"Media usage collection readiness lost its fence\");\n\t}\n}\n\nexport async function finalizeMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string },\n): Promise<void> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_index_status\")\n\t\t.set({ capture_state: \"active\", cursor: null, updated_at: timestampOffset(db, 0) })\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", identity.collectionSlug)\n\t\t.where(\"collection_id\", \"=\", identity.collectionId)\n\t\t.where(\"capture_state\", \"=\", \"ready\")\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_collections AS collection\n\t\t\t\tWHERE collection.id = ${identity.collectionId}\n\t\t\t\t\tAND collection.slug = ${identity.collectionSlug}\n\t\t\t)`,\n\t\t)\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_media_usage_activation AS activation\n\t\t\t\tWHERE activation.task_key = ${ACTIVATION_KEY}\n\t\t\t\t\tAND activation.state = 'active'\n\t\t\t\t\tAND activation.runtime_generation = ${MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION}\n\t\t\t)`,\n\t\t)\n\t\t.executeTakeFirst();\n\tif (Number(result.numUpdatedRows ?? 0) === 0) {\n\t\tthrow new Error(\"Media usage collection activation lost its fence\");\n\t}\n}\n\nexport async function activateMediaUsageCapture(\n\tdb: Kysely<Database>,\n\tinput: { writersDrained: true },\n): Promise<MediaUsageActivationResult> {\n\tif (input.writersDrained !== true) {\n\t\tthrow new Error(\"Media usage activation requires confirmation that writers are drained\");\n\t}\n\n\tconst before = await findActivation(db);\n\tassertRuntimeGeneration(before);\n\tif (before.state === \"active\") {\n\t\treturn { outcome: \"active\", processedCollections: 0 };\n\t}\n\n\tconst leaseToken = ulid();\n\tconst lease = await claimActivation(db, leaseToken);\n\tif (!lease) return activationClaimLoss(db);\n\n\tlet processedCollections = 0;\n\ttry {\n\t\tconst candidates = await findActivationCandidates(db, lease.collection_cursor);\n\t\tfor (const collection of candidates.slice(\n\t\t\t0,\n\t\t\tMEDIA_USAGE_ACTIVATION_LIMITS.collectionsPerCall,\n\t\t)) {\n\t\t\tawait requireActivationLease(db, leaseToken);\n\t\t\tawait prepareCollectionForActivation(db, collection, leaseToken);\n\t\t\tawait requireActivationLease(db, leaseToken);\n\t\t\tawait installMediaUsageCaptureTriggers(\n\t\t\t\tdb,\n\t\t\t\t{\n\t\t\t\t\tcollectionId: collection.id,\n\t\t\t\t\tcollectionSlug: collection.slug,\n\t\t\t\t},\n\t\t\t\t{ replaceExisting: false },\n\t\t\t);\n\t\t\tawait requireActivationLease(db, leaseToken);\n\t\t\tif (\n\t\t\t\t!(await verifyMediaUsageCaptureTriggers(db, {\n\t\t\t\t\tcollectionId: collection.id,\n\t\t\t\t\tcollectionSlug: collection.slug,\n\t\t\t\t}))\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Media usage capture trigger verification failed\");\n\t\t\t}\n\t\t\tif (!(await activateCollectionLifecycle(db, collection, leaseToken))) {\n\t\t\t\tthrow new Error(\"Media usage collection activation lost its fence\");\n\t\t\t}\n\t\t\tprocessedCollections++;\n\t\t}\n\n\t\tconst collectionCursor =\n\t\t\tcandidates[Math.min(processedCollections, candidates.length) - 1]?.slug ??\n\t\t\tlease.collection_cursor;\n\t\tif (candidates.length > MEDIA_USAGE_ACTIVATION_LIMITS.collectionsPerCall) {\n\t\t\tif (!(await releaseActivationBatch(db, leaseToken, collectionCursor))) {\n\t\t\t\tthrow new Error(\"Media usage activation cursor lost its fence\");\n\t\t\t}\n\t\t\treturn { outcome: \"activating\", processedCollections, collectionCursor };\n\t\t}\n\n\t\tconst incomplete = await findIncompleteCollection(db);\n\t\tif (incomplete) {\n\t\t\tif (!(await releaseActivationBatch(db, leaseToken, null))) {\n\t\t\t\tthrow new Error(\"Media usage activation restart lost its fence\");\n\t\t\t}\n\t\t\treturn { outcome: \"activating\", processedCollections, collectionCursor: null };\n\t\t}\n\n\t\tif (!(await finalizeActivation(db, leaseToken, collectionCursor))) {\n\t\t\tthrow new Error(\"Media usage activation finalization lost its fence\");\n\t\t}\n\t\treturn { outcome: \"active\", processedCollections };\n\t} catch (error) {\n\t\tif (!(await activationLeaseIsLive(db, leaseToken))) {\n\t\t\treturn { outcome: \"conflict\", processedCollections };\n\t\t}\n\t\tawait recordActivationFailure(db, leaseToken);\n\t\tthrow new Error(\"Media usage activation failed\", { cause: error });\n\t}\n}\n\nasync function findActivation(\n\tdb: Kysely<Database>,\n): Promise<Selectable<MediaUsageActivationTable>> {\n\treturn db\n\t\t.selectFrom(\"_emdash_media_usage_activation\")\n\t\t.selectAll()\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.executeTakeFirstOrThrow();\n}\n\nasync function findActivationIfAvailable(\n\tdb: Kysely<Database>,\n): Promise<Selectable<MediaUsageActivationTable> | null> {\n\tif (!(await tableExists(db, \"_emdash_media_usage_activation\"))) return null;\n\treturn findActivation(db);\n}\n\nfunction assertRuntimeGeneration(activation: Selectable<MediaUsageActivationTable>): void {\n\tif (activation.runtime_generation !== MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION) {\n\t\tthrow new Error(\"Media usage activation runtime generation mismatch\");\n\t}\n}\n\nasync function claimActivation(\n\tdb: Kysely<Database>,\n\tleaseToken: string,\n): Promise<Selectable<MediaUsageActivationTable> | null> {\n\tconst now = timestampOffset(db, 0);\n\treturn (\n\t\t(await db\n\t\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t\t.set({\n\t\t\t\tstate: \"activating\",\n\t\t\t\tdrain_confirmed_at: now,\n\t\t\t\tlease_token: leaseToken,\n\t\t\t\tlease_expires_at: timestampOffset(db, MEDIA_USAGE_ACTIVATION_LIMITS.leaseDurationSeconds),\n\t\t\t\tattempt_count: sql<number>`attempt_count + 1`,\n\t\t\t\tlast_attempted_at: now,\n\t\t\t\tlast_error_code: null,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t\t.where((eb) =>\n\t\t\t\teb.or([\n\t\t\t\t\teb(\"state\", \"=\", \"expanded\"),\n\t\t\t\t\teb.and([\n\t\t\t\t\t\teb(\"state\", \"=\", \"activating\"),\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(\"lease_token\", \"is\", null),\n\t\t\t\t\t\t\teb.and([\n\t\t\t\t\t\t\t\teb(\"lease_expires_at\", \"is not\", null),\n\t\t\t\t\t\t\t\ttimestampIsDue(db, \"lease_expires_at\"),\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t]),\n\t\t\t\t\t]),\n\t\t\t\t]),\n\t\t\t)\n\t\t\t.returningAll()\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nasync function activationClaimLoss(db: Kysely<Database>): Promise<MediaUsageActivationResult> {\n\tconst current = await findActivation(db);\n\tassertRuntimeGeneration(current);\n\tif (current.state === \"active\") return { outcome: \"active\", processedCollections: 0 };\n\tif (\n\t\tcurrent.state === \"activating\" &&\n\t\tcurrent.lease_token &&\n\t\tcurrent.lease_expires_at &&\n\t\t(await activationLeaseIsLive(db, current.lease_token))\n\t) {\n\t\treturn { outcome: \"lease_active\", leaseExpiresAt: current.lease_expires_at };\n\t}\n\tthrow new Error(\"Media usage activation state is not claimable\");\n}\n\nasync function activationLeaseIsLive(db: Kysely<Database>, leaseToken: string): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_media_usage_activation\")\n\t\t.select(\"task_key\")\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.where(\"lease_expires_at\", \"is not\", null)\n\t\t.where(timestampIsLive(db, \"lease_expires_at\"))\n\t\t.executeTakeFirst();\n\treturn row !== undefined;\n}\n\nasync function findActivationCandidates(\n\tdb: Kysely<Database>,\n\tcollectionCursor: string | null,\n): Promise<Array<{ id: string; slug: string }>> {\n\tlet query = db.selectFrom(\"_emdash_collections\").select([\"id\", \"slug\"]);\n\tif (collectionCursor) query = query.where(\"slug\", \">\", collectionCursor);\n\treturn query\n\t\t.orderBy(\"slug\", \"asc\")\n\t\t.limit(MEDIA_USAGE_ACTIVATION_LIMITS.collectionsPerCall + 1)\n\t\t.execute();\n}\n\nasync function prepareCollectionForActivation(\n\tdb: Kysely<Database>,\n\tcollection: { id: string; slug: string },\n\tleaseToken: string,\n): Promise<void> {\n\tconst now = timestampOffset(db, 0);\n\tconst existingStatus = sql.ref(\"_emdash_media_usage_index_status.status\");\n\tconst existingCompletedAt = sql.ref(\"_emdash_media_usage_index_status.completed_at\");\n\tconst existingCollectionId = sql.ref(\"_emdash_media_usage_index_status.collection_id\");\n\tconst existingCaptureState = sql.ref(\"_emdash_media_usage_index_status.capture_state\");\n\tconst row = await db\n\t\t.insertInto(\"_emdash_media_usage_index_status\")\n\t\t.values({\n\t\t\tadapter_id: \"content-media\",\n\t\t\tscope_type: \"collection\",\n\t\t\tscope_key: collection.slug,\n\t\t\tstatus: \"never\",\n\t\t\tcollection_id: collection.id,\n\t\t\treconciliation_required: 1,\n\t\t\tcapture_state: \"installing\",\n\t\t\tupdated_at: now,\n\t\t})\n\t\t.onConflict((conflict) =>\n\t\t\tconflict\n\t\t\t\t.columns([\"adapter_id\", \"scope_type\", \"scope_key\"])\n\t\t\t\t.doUpdateSet({\n\t\t\t\t\tstatus: sql<string>`CASE WHEN ${existingStatus} IN ('complete', 'running') THEN 'stale' ELSE ${existingStatus} END`,\n\t\t\t\t\tcompleted_at: sql<\n\t\t\t\t\t\tstring | null\n\t\t\t\t\t>`CASE WHEN ${existingStatus} IN ('complete', 'running') THEN NULL ELSE ${existingCompletedAt} END`,\n\t\t\t\t\tcursor: null,\n\t\t\t\t\tcollection_id: collection.id,\n\t\t\t\t\treconciliation_required: 1,\n\t\t\t\t\tcapture_state: \"installing\",\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t})\n\t\t\t\t.where((eb) =>\n\t\t\t\t\teb.and([\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(existingCollectionId, \"is\", null),\n\t\t\t\t\t\t\teb(existingCollectionId, \"=\", collection.id),\n\t\t\t\t\t\t]),\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(existingCaptureState, \"is\", null),\n\t\t\t\t\t\t\teb(existingCaptureState, \"!=\", \"deleting\"),\n\t\t\t\t\t\t]),\n\t\t\t\t\t\tactiveActivationLease(db, leaseToken),\n\t\t\t\t\t]),\n\t\t\t\t),\n\t\t)\n\t\t.returning(\"collection_id\")\n\t\t.executeTakeFirst();\n\tif (row?.collection_id !== collection.id) {\n\t\tthrow new Error(\"Media usage collection lifecycle identity conflict\");\n\t}\n}\n\nasync function requireActivationLease(db: Kysely<Database>, leaseToken: string): Promise<void> {\n\tif (!(await activationLeaseIsLive(db, leaseToken))) {\n\t\tthrow new Error(\"Media usage activation lease is no longer live\");\n\t}\n}\n\nasync function activateCollectionLifecycle(\n\tdb: Kysely<Database>,\n\tcollection: { id: string; slug: string },\n\tleaseToken: string,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_index_status\")\n\t\t.set({ capture_state: \"active\", updated_at: timestampOffset(db, 0) })\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", collection.slug)\n\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t.where(\"capture_state\", \"=\", \"installing\")\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_collections AS collection\n\t\t\t\tWHERE collection.id = ${collection.id}\n\t\t\t\t\tAND collection.slug = ${collection.slug}\n\t\t\t)`,\n\t\t)\n\t\t.where(activeActivationLease(db, leaseToken))\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0) > 0;\n}\n\nasync function releaseActivationBatch(\n\tdb: Kysely<Database>,\n\tleaseToken: string,\n\tcollectionCursor: string | null,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t.set({\n\t\t\tcollection_cursor: collectionCursor,\n\t\t\tlease_token: null,\n\t\t\tlease_expires_at: null,\n\t\t\tupdated_at: timestampOffset(db, 0),\n\t\t})\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.where(\"lease_expires_at\", \"is not\", null)\n\t\t.where(timestampIsLive(db, \"lease_expires_at\"))\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0) > 0;\n}\n\nasync function findIncompleteCollection(\n\tdb: Kysely<Database>,\n): Promise<{ id: string; slug: string } | null> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections as collection\")\n\t\t.select([\"collection.id\", \"collection.slug\"])\n\t\t.where((eb) =>\n\t\t\teb.not(\n\t\t\t\teb.exists(\n\t\t\t\t\teb\n\t\t\t\t\t\t.selectFrom(\"_emdash_media_usage_index_status as status\")\n\t\t\t\t\t\t.select(\"status.scope_key\")\n\t\t\t\t\t\t.where(\"status.adapter_id\", \"=\", \"content-media\")\n\t\t\t\t\t\t.where(\"status.scope_type\", \"=\", \"collection\")\n\t\t\t\t\t\t.whereRef(\"status.scope_key\", \"=\", \"collection.slug\")\n\t\t\t\t\t\t.whereRef(\"status.collection_id\", \"=\", \"collection.id\")\n\t\t\t\t\t\t.where(\"status.capture_state\", \"=\", \"active\"),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\t.orderBy(\"collection.slug\", \"asc\")\n\t\t.limit(1)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nasync function finalizeActivation(\n\tdb: Kysely<Database>,\n\tleaseToken: string,\n\tcollectionCursor: string | null,\n): Promise<boolean> {\n\tconst now = timestampOffset(db, 0);\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t.set({\n\t\t\tstate: \"active\",\n\t\t\tcollection_cursor: collectionCursor,\n\t\t\tlease_token: null,\n\t\t\tlease_expires_at: null,\n\t\t\tlast_error_code: null,\n\t\t\tactivated_at: now,\n\t\t\tupdated_at: now,\n\t\t})\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.where(\"lease_expires_at\", \"is not\", null)\n\t\t.where(timestampIsLive(db, \"lease_expires_at\"))\n\t\t.where(\n\t\t\tsql<boolean>`NOT EXISTS (\n\t\t\t\tSELECT 1\n\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\tWHERE NOT EXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM _emdash_media_usage_index_status AS status\n\t\t\t\t\tWHERE status.adapter_id = 'content-media'\n\t\t\t\t\t\tAND status.scope_type = 'collection'\n\t\t\t\t\t\tAND status.scope_key = collection.slug\n\t\t\t\t\t\tAND status.collection_id = collection.id\n\t\t\t\t\t\tAND status.capture_state = 'active'\n\t\t\t\t)\n\t\t\t)`,\n\t\t)\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0) > 0;\n}\n\nasync function recordActivationFailure(db: Kysely<Database>, leaseToken: string): Promise<void> {\n\tawait db\n\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t.set({\n\t\t\tlease_token: null,\n\t\t\tlease_expires_at: null,\n\t\t\tlast_error_code: ACTIVATION_ERROR_CODE,\n\t\t\tupdated_at: timestampOffset(db, 0),\n\t\t})\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.execute();\n}\n\nfunction activeActivationLease(db: Kysely<Database>, leaseToken: string): RawBuilder<boolean> {\n\treturn sql<boolean>`EXISTS (\n\t\tSELECT 1\n\t\tFROM _emdash_media_usage_activation AS activation\n\t\tWHERE activation.task_key = ${ACTIVATION_KEY}\n\t\t\tAND activation.state = 'activating'\n\t\t\tAND activation.runtime_generation = ${MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION}\n\t\t\tAND activation.lease_token = ${leaseToken}\n\t\t\tAND activation.lease_expires_at IS NOT NULL\n\t\t\tAND ${timestampIsLive(db, \"activation.lease_expires_at\")}\n\t)`;\n}\n\nfunction timestampIsDue(db: Kysely<Database>, column: string): RawBuilder<boolean> {\n\treturn isPostgres(db)\n\t\t? sql<boolean>`${sql.ref(column)}::timestamptz <= clock_timestamp()`\n\t\t: sql<boolean>`${sql.ref(column)} <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;\n}\n\nfunction timestampIsLive(db: Kysely<Database>, column: string): RawBuilder<boolean> {\n\treturn isPostgres(db)\n\t\t? sql<boolean>`${sql.ref(column)}::timestamptz > clock_timestamp()`\n\t\t: sql<boolean>`${sql.ref(column)} > strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;\n}\n\nfunction timestampOffset(db: Kysely<Database>, seconds: number): RawBuilder<string> {\n\tif (isPostgres(db)) {\n\t\treturn sql<string>`to_char(\n\t\t\tclock_timestamp() AT TIME ZONE 'UTC' + (${seconds} * interval '1 second'),\n\t\t\t'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"'\n\t\t)`;\n\t}\n\treturn sql<string>`strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ${`+${seconds} seconds`})`;\n}\n","import type {\n\tColumnDataType,\n\tCreateTableBuilder,\n\tInsertable,\n\tKysely,\n\tSelectable,\n\tUpdateable,\n} from \"kysely\";\nimport { sql } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { currentTimestamp, listTablesLike, tableExists } from \"../database/dialect-helpers.js\";\nimport { withTransaction } from \"../database/transaction.js\";\nimport type { CollectionTable, Database, FieldTable } from \"../database/types.js\";\nimport { validateIdentifier } from \"../database/validate.js\";\nimport {\n\tcanResumeMediaUsageCollectionCapture,\n\tfinalizeMediaUsageCollectionCapture,\n\tinstallPreparedMediaUsageCollectionCapture,\n\tmarkMediaUsageCollectionCaptureReady,\n\tprepareMediaUsageCollectionCapture,\n} from \"../media/usage/activation.js\";\nimport {\n\tdeleteActivatedMediaUsageCollection,\n\tisMediaUsageCollectionSlugDeleting,\n} from \"../media/usage/collection-deletion.js\";\nimport {\n\tdeleteContentMediaUsageCollection,\n\tinvalidateContentMediaUsageSchemaChange,\n\tmarkContentMediaUsageCollectionStaleSafely,\n} from \"../media/usage/content-refresh.js\";\nimport { FTSManager } from \"../search/fts-manager.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../utils/chunks.js\";\nimport {\n\ttype Collection,\n\ttype CollectionAdminConfig,\n\ttype CollectionSource,\n\ttype CollectionSupport,\n\ttype ColumnType,\n\ttype Field,\n\ttype CreateCollectionInput,\n\ttype UpdateCollectionInput,\n\ttype CreateFieldInput,\n\ttype UpdateFieldInput,\n\ttype CollectionWithFields,\n\ttype FieldType,\n\tFIELD_TYPE_TO_COLUMN,\n\tisIndexableFieldType,\n\tRESERVED_FIELD_SLUGS,\n\tRESERVED_COLLECTION_SLUGS,\n} from \"./types.js\";\n\n// Regex patterns for schema registry\nconst SLUG_VALIDATION_PATTERN = /^[a-z][a-z0-9_]*$/;\nconst EC_PREFIX_PATTERN = /^ec_/;\nconst SINGLE_QUOTE_PATTERN = /'/g;\nconst UNDERSCORE_PATTERN = /_/g;\nconst WORD_BOUNDARY_PATTERN = /\\b\\w/g;\nconst FIELD_ID_PATTERN = /^[0-9A-Z]{26}$/;\n\n/** Valid column types for runtime validation */\nconst COLUMN_TYPES: ReadonlySet<string> = new Set([\"TEXT\", \"REAL\", \"INTEGER\", \"JSON\"]);\nconst COLUMN_TYPE_TO_DATA_TYPE = {\n\tTEXT: \"text\",\n\tREAL: \"real\",\n\tINTEGER: \"integer\",\n\tJSON: \"json\",\n} satisfies Record<ColumnType, ColumnDataType>;\nconst TEXT_ALIAS_FIELD_TYPES: ReadonlySet<FieldType> = new Set([\"string\", \"text\", \"slug\"]);\n\n/** Field types usable as a `titleField` — plain text that reads well as a title. */\nconst TITLE_FIELD_TYPES: ReadonlySet<string> = new Set([\"string\", \"text\", \"slug\"]);\n\n/** Valid collection source prefixes/values */\nconst VALID_SOURCES: ReadonlySet<string> = new Set([\"manual\", \"discovered\", \"seed\"]);\n\nfunction isCollectionSource(value: string): value is CollectionSource {\n\treturn VALID_SOURCES.has(value) || value.startsWith(\"template:\") || value.startsWith(\"import:\");\n}\n\nfunction isFieldType(value: string): value is FieldType {\n\treturn value in FIELD_TYPE_TO_COLUMN;\n}\n\nfunction isColumnType(value: string): value is ColumnType {\n\treturn COLUMN_TYPES.has(value);\n}\n\nconst VALID_COLLECTION_SUPPORTS: ReadonlySet<string> = new Set<CollectionSupport>([\n\t\"drafts\",\n\t\"revisions\",\n\t\"preview\",\n\t\"scheduling\",\n\t\"search\",\n\t\"seo\",\n]);\n\n// Each _emdash_fields row uses 16 bound parameters. Six rows keep every\n// multi-row INSERT below D1's 100-parameter statement limit.\nconst SEED_FIELD_INSERT_BATCH_SIZE = 6;\n\n/**\n * Rank given to collections without an explicit `sort_order`. SQLite sorts\n * NULL first on ASC while Postgres sorts it last, so the fallback is\n * materialised with COALESCE rather than left to the dialect.\n */\nconst UNORDERED_COLLECTION_RANK = 2147483647;\n\n/**\n * Collection ordering shared by every list read: explicit `sort_order`\n * first (ascending), then alphabetically by slug.\n */\nconst collectionOrder = sql<number>`coalesce(sort_order, ${sql.lit(UNORDERED_COLLECTION_RANK)})`;\n\nfunction assertIndexableField(type: FieldType, indexed: boolean | undefined, slug: string): void {\n\tif (indexed && !isIndexableFieldType(type)) {\n\t\tthrow new SchemaError(\n\t\t\t`Field \"${slug}\" cannot be indexed because type \"${type}\" is not a scalar query type`,\n\t\t\t\"FIELD_NOT_INDEXABLE\",\n\t\t);\n\t}\n}\n\nfunction isCollectionSupport(value: unknown): value is CollectionSupport {\n\treturn typeof value === \"string\" && VALID_COLLECTION_SUPPORTS.has(value);\n}\n\n/**\n * Parse a collection's `supports` column (stored as a JSON array of\n * CollectionSupport keys). Unknown/invalid entries are filtered out so the\n * runtime value matches the declared `CollectionSupport[]` type.\n *\n * Throws on malformed JSON so corruption surfaces loudly; returns an empty\n * array only for explicitly null/empty values or non-array JSON.\n */\nfunction parseSupports(raw: string | null | undefined): CollectionSupport[] {\n\tif (!raw) return [];\n\tconst parsed: unknown = JSON.parse(raw);\n\tif (!Array.isArray(parsed)) return [];\n\treturn parsed.filter(isCollectionSupport);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseCollectionAdmin(raw: string | null | undefined): CollectionAdminConfig | undefined {\n\tif (!raw) return undefined;\n\tconst parsed: unknown = JSON.parse(raw);\n\tif (!isRecord(parsed)) return undefined;\n\tconst listColumns = parsed.listColumns;\n\treturn {\n\t\tlistColumns: Array.isArray(listColumns)\n\t\t\t? listColumns.filter((value): value is string => typeof value === \"string\")\n\t\t\t: undefined,\n\t};\n}\n\nexport async function buildSeedCollectionCaptureFingerprint(\n\tinput: Omit<CreateCollectionInput, \"source\">,\n\tfields: readonly CreateFieldInput[],\n): Promise<string> {\n\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\tlet maxSortOrder = -1;\n\tconst definitions = fields.map((field) => {\n\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\t\treturn {\n\t\t\tslug: field.slug,\n\t\t\tlabel: field.label,\n\t\t\ttype: field.type,\n\t\t\trequired: field.required ?? false,\n\t\t\tunique: field.unique ?? false,\n\t\t\tdefaultValue: field.defaultValue === undefined ? null : JSON.stringify(field.defaultValue),\n\t\t\tvalidation: field.validation ? JSON.stringify(field.validation) : null,\n\t\t\twidget: field.widget ?? null,\n\t\t\toptions: field.options ? JSON.stringify(field.options) : null,\n\t\t\tsortOrder,\n\t\t\tsearchable: field.searchable ?? false,\n\t\t\ttranslatable: field.translatable ?? true,\n\t\t};\n\t});\n\tconst payload = JSON.stringify(\n\t\tcanonicalizeFingerprintValue({\n\t\t\tversion: 1,\n\t\t\tcollection: {\n\t\t\t\tslug: input.slug,\n\t\t\t\tlabel: input.label,\n\t\t\t\tlabelSingular: input.labelSingular ?? null,\n\t\t\t\tdescription: input.description ?? null,\n\t\t\t\ticon: input.icon ?? null,\n\t\t\t\tadmin: input.admin ?? null,\n\t\t\t\tsupports,\n\t\t\t\thasSeo,\n\t\t\t\thidden: input.hidden ?? false,\n\t\t\t\tsortOrder: input.sortOrder ?? null,\n\t\t\t\tcommentsEnabled: input.commentsEnabled ?? false,\n\t\t\t\turlPattern: input.urlPattern ?? null,\n\t\t\t\troutable: input.routable ?? true,\n\t\t\t},\n\t\t\tfields: definitions,\n\t\t}),\n\t);\n\tconst digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(payload));\n\tconst hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\")).join(\n\t\t\"\",\n\t);\n\treturn `media-usage-seed:v1:sha256:${hex}`;\n}\n\nfunction canonicalizeFingerprintValue(value: unknown): unknown {\n\tif (value === undefined) return { __emdashUndefined: true };\n\tif (Array.isArray(value)) return value.map(canonicalizeFingerprintValue);\n\tif (typeof value !== \"object\" || value === null) return value;\n\n\tconst canonical: Record<string, unknown> = {};\n\tfor (const [key, entry] of Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b))) {\n\t\tcanonical[key] = canonicalizeFingerprintValue(entry);\n\t}\n\treturn canonical;\n}\n\n/**\n * Error thrown when a schema operation fails\n */\nexport class SchemaError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic code: string,\n\t\tpublic details?: Record<string, unknown>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SchemaError\";\n\t}\n}\n\n/**\n * Schema Registry\n *\n * Manages collection and field definitions stored in D1.\n * Handles runtime DDL operations (CREATE TABLE, ALTER TABLE).\n */\nexport class SchemaRegistry {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t// ============================================\n\t// Collection Operations\n\t// ============================================\n\n\t/**\n\t * List all collections\n\t */\n\tasync listCollections(): Promise<Collection[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(collectionOrder, \"asc\")\n\t\t\t.orderBy(\"slug\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(this.mapCollectionRow);\n\t}\n\n\t/**\n\t * Get a collection by slug\n\t */\n\tasync getCollection(slug: string): Promise<Collection | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t.selectAll()\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.mapCollectionRow(row) : null;\n\t}\n\n\t/**\n\t * Get a collection with all its fields\n\t */\n\tasync getCollectionWithFields(slug: string): Promise<CollectionWithFields | null> {\n\t\tconst collection = await this.getCollection(slug);\n\t\tif (!collection) return null;\n\n\t\tconst fields = await this.listFields(collection.id);\n\n\t\treturn { ...collection, fields };\n\t}\n\n\t/**\n\t * List every collection together with its fields in O(1) query shapes\n\t * — one for collections, then one batched query for the fields of every\n\t * returned collection — instead of the N+1 pattern of `listCollections`\n\t * + per-collection `listFields`. The fields query is chunked at\n\t * `SQL_BATCH_SIZE` to stay under D1's bound-parameter limit, so on\n\t * sites with more than `SQL_BATCH_SIZE` collections the field fetch\n\t * becomes `ceil(collectionCount / SQL_BATCH_SIZE)` queries — still\n\t * a constant factor, not N+1. Typical sites have well under\n\t * `SQL_BATCH_SIZE` collections, so this is two queries in practice.\n\t *\n\t * Used by the manifest build, which previously paid N+1 round-trips on\n\t * every admin request. Each round-trip costs ~80–150ms against the D1\n\t * primary on a busy link, so a 10-collection site spent ~1 s rebuilding\n\t * a manifest that is now built fresh per admin request (no cache).\n\t */\n\tasync listCollectionsWithFields(): Promise<CollectionWithFields[]> {\n\t\tconst collectionRows = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(collectionOrder, \"asc\")\n\t\t\t.orderBy(\"slug\", \"asc\")\n\t\t\t.execute();\n\n\t\tif (collectionRows.length === 0) return [];\n\n\t\tconst fieldsByCollection = new Map<string, Field[]>();\n\t\t// Chunk to stay under D1's bound-parameter limit. Typical sites have\n\t\t// well under SQL_BATCH_SIZE collections, so this is a single query\n\t\t// in practice; on larger sites it becomes a small constant number\n\t\t// of queries, never N+1.\n\t\tfor (const idChunk of chunks(\n\t\t\tcollectionRows.map((c) => c.id),\n\t\t\tSQL_BATCH_SIZE,\n\t\t)) {\n\t\t\tconst fieldRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t.where(\"collection_id\", \"in\", idChunk)\n\t\t\t\t.selectAll()\n\t\t\t\t.orderBy(\"collection_id\", \"asc\")\n\t\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t\t.execute();\n\t\t\tfor (const row of fieldRows) {\n\t\t\t\tconst list = fieldsByCollection.get(row.collection_id) ?? [];\n\t\t\t\tlist.push(this.mapFieldRow(row));\n\t\t\t\tfieldsByCollection.set(row.collection_id, list);\n\t\t\t}\n\t\t}\n\n\t\treturn collectionRows.map((c) => ({\n\t\t\t...this.mapCollectionRow(c),\n\t\t\tfields: fieldsByCollection.get(c.id) ?? [],\n\t\t}));\n\t}\n\n\t/**\n\t * Validate `titleField`/`dateField` against the collection's fields:\n\t * `titleField` must be a text-like field; `dateField` must be a `datetime` field.\n\t * Only truthy values are checked (undefined = unchanged, null/\"\" = cleared).\n\t */\n\tprivate async validateTitleDateFields(\n\t\tcollectionId: string,\n\t\tcollectionSlug: string,\n\t\tinput: { titleField?: string | null; dateField?: string | null },\n\t\tdb: Kysely<Database> = this.db,\n\t): Promise<void> {\n\t\tconst slugs = [input.titleField, input.dateField].filter((slug): slug is string => !!slug);\n\t\tif (slugs.length === 0) return;\n\n\t\tconst rows = await db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.where(\"slug\", \"in\", slugs)\n\t\t\t.select([\"slug\", \"type\"])\n\t\t\t.execute();\n\t\tconst typeBySlug = new Map(rows.map((row) => [row.slug, row.type]));\n\n\t\tif (input.titleField) {\n\t\t\tconst type = typeBySlug.get(input.titleField);\n\t\t\tif (type === undefined) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`titleField \"${input.titleField}\" is not a field on \"${collectionSlug}\"`,\n\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!TITLE_FIELD_TYPES.has(type)) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`titleField \"${input.titleField}\" must be a text field (got \"${type}\")`,\n\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (input.dateField) {\n\t\t\tconst type = typeBySlug.get(input.dateField);\n\t\t\tif (type === undefined) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`dateField \"${input.dateField}\" is not a field on \"${collectionSlug}\"`,\n\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (type !== \"datetime\") {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`dateField \"${input.dateField}\" must be a datetime field (got \"${type}\")`,\n\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a new collection\n\t */\n\tasync createCollection(input: CreateCollectionInput): Promise<Collection> {\n\t\t// Validate slug\n\t\tthis.validateSlug(input.slug, \"collection\");\n\t\tif (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Collection slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\t// Check if collection already exists\n\t\tconst existing = await this.getCollection(input.slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst proposedId = existing?.id ?? ulid();\n\n\t\t// Default `supports` to drafts + revisions when the caller didn't\n\t\t// specify it. Explicit empty array (`[]`) is preserved as an opt-out\n\t\t// — only `undefined` triggers the default. This is the canonical\n\t\t// default for new collections; the MCP and admin UI layers used to\n\t\t// duplicate this default but now defer to the registry.\n\t\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\n\t\t// Insert collection record and create content table in a transaction\n\t\t// so a failure in table creation doesn't leave an orphaned row.\n\t\t// Uses withTransaction for D1 compatibility (no transaction support).\n\t\t// Derive hasSeo from supports array if not explicitly set\n\t\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tconst capture = await prepareMediaUsageCollectionCapture(trx, {\n\t\t\t\tcollectionId: proposedId,\n\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t});\n\t\t\tconst values: Insertable<CollectionTable> = {\n\t\t\t\tid: capture.collectionId,\n\t\t\t\tslug: input.slug,\n\t\t\t\tlabel: input.label,\n\t\t\t\tlabel_singular: input.labelSingular ?? null,\n\t\t\t\tdescription: input.description ?? null,\n\t\t\t\ticon: input.icon ?? null,\n\t\t\t\tadmin_config: input.admin ? JSON.stringify(input.admin) : null,\n\t\t\t\tsupports: JSON.stringify(supports),\n\t\t\t\tsource: input.source ?? \"manual\",\n\t\t\t\thas_seo: hasSeo ? 1 : 0,\n\t\t\t\troutable: input.routable === false ? 0 : 1,\n\t\t\t\thidden: input.hidden ? 1 : 0,\n\t\t\t\tsort_order: input.sortOrder ?? null,\n\t\t\t\tcomments_enabled: input.commentsEnabled ? 1 : 0,\n\t\t\t\turl_pattern: input.urlPattern ?? null,\n\t\t\t};\n\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait this.createContentTable(input.slug, trx, [], {\n\t\t\t\t\tifNotExists: capture.resuming,\n\t\t\t\t});\n\t\t\t\tawait installPreparedMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\tawait markMediaUsageCollectionCaptureReady(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\tif (!capture.registrationExists) {\n\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(values).execute();\n\t\t\t\t}\n\t\t\t\tawait finalizeMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait trx.insertInto(\"_emdash_collections\").values(values).execute();\n\t\t\tawait this.createContentTable(input.slug, trx);\n\t\t});\n\n\t\tconst collection = await this.getCollection(input.slug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(\"Failed to create collection\", \"CREATE_FAILED\");\n\t\t}\n\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Create a seed-owned collection and all of its fields in bulk.\n\t *\n\t * Fresh seeds can define dozens of fields. Creating them through\n\t * `createField` performs multiple reads, one ALTER TABLE, and one media\n\t * usage invalidation per field, which can exhaust D1's per-request query\n\t * budget. This path validates the full schema before mutating it, creates\n\t * the complete content table in one statement, and inserts field metadata\n\t * in parameter-safe batches.\n\t */\n\tasync createSeedCollection(\n\t\tinput: Omit<CreateCollectionInput, \"source\">,\n\t\tfields: readonly CreateFieldInput[],\n\t): Promise<void> {\n\t\tthis.validateSlug(input.slug, \"collection\");\n\t\tif (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Collection slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst fieldSlugs = new Set<string>();\n\t\tfor (const field of fields) {\n\t\t\tthis.validateSlug(field.slug, \"field\");\n\t\t\tassertIndexableField(field.type, field.indexed, field.slug);\n\t\t\tif (RESERVED_FIELD_SLUGS.includes(field.slug)) {\n\t\t\t\tthrow new SchemaError(`Field slug \"${field.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t\t}\n\t\t\tif (fieldSlugs.has(field.slug)) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`Field \"${field.slug}\" already exists in collection \"${input.slug}\"`,\n\t\t\t\t\t\"FIELD_EXISTS\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tfieldSlugs.add(field.slug);\n\t\t}\n\n\t\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\t\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\t\tconst creationFingerprint = await buildSeedCollectionCaptureFingerprint(input, fields);\n\t\tconst existing = await this.getCollection(input.slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t\tcreationFingerprint,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst proposedCollectionId = existing?.id ?? ulid();\n\t\tlet maxSortOrder = -1;\n\t\tconst fieldRows: Insertable<FieldTable>[] = fields.map((field) => {\n\t\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\n\t\t\treturn {\n\t\t\t\tid: ulid(),\n\t\t\t\tcollection_id: proposedCollectionId,\n\t\t\t\tslug: field.slug,\n\t\t\t\tlabel: field.label,\n\t\t\t\ttype: field.type,\n\t\t\t\tcolumn_type: FIELD_TYPE_TO_COLUMN[field.type],\n\t\t\t\trequired: field.required ? 1 : 0,\n\t\t\t\tunique: field.unique ? 1 : 0,\n\t\t\t\tdefault_value: field.defaultValue !== undefined ? JSON.stringify(field.defaultValue) : null,\n\t\t\t\tvalidation: field.validation ? JSON.stringify(field.validation) : null,\n\t\t\t\twidget: field.widget ?? null,\n\t\t\t\toptions: field.options ? JSON.stringify(field.options) : null,\n\t\t\t\tsort_order: sortOrder,\n\t\t\t\tsearchable: field.searchable ? 1 : 0,\n\t\t\t\tindexed: field.indexed ? 1 : 0,\n\t\t\t\ttranslatable: field.translatable === false ? 0 : 1,\n\t\t\t};\n\t\t});\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\tconst capture = await prepareMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: proposedCollectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\tcreationFingerprint,\n\t\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t\t});\n\t\t\t\tconst collectionValues: Insertable<CollectionTable> = {\n\t\t\t\t\tid: capture.collectionId,\n\t\t\t\t\tslug: input.slug,\n\t\t\t\t\tlabel: input.label,\n\t\t\t\t\tlabel_singular: input.labelSingular ?? null,\n\t\t\t\t\tdescription: input.description ?? null,\n\t\t\t\t\ticon: input.icon ?? null,\n\t\t\t\t\tadmin_config: input.admin ? JSON.stringify(input.admin) : null,\n\t\t\t\t\tsupports: JSON.stringify(supports),\n\t\t\t\t\tsource: \"seed\",\n\t\t\t\t\thas_seo: hasSeo ? 1 : 0,\n\t\t\t\t\troutable: input.routable === false ? 0 : 1,\n\t\t\t\t\thidden: input.hidden ? 1 : 0,\n\t\t\t\t\tsort_order: input.sortOrder ?? null,\n\t\t\t\t\tcomments_enabled: input.commentsEnabled ? 1 : 0,\n\t\t\t\t\turl_pattern: input.urlPattern ?? null,\n\t\t\t\t};\n\t\t\t\tconst rows = fieldRows.map((row) => ({\n\t\t\t\t\t...row,\n\t\t\t\t\tcollection_id: capture.collectionId,\n\t\t\t\t}));\n\n\t\t\t\tif (capture.captureRequired) {\n\t\t\t\t\tawait this.createContentTable(input.slug, trx, fields, {\n\t\t\t\t\t\tifNotExists: capture.resuming,\n\t\t\t\t\t});\n\t\t\t\t\tawait installPreparedMediaUsageCollectionCapture(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t\tawait markMediaUsageCollectionCaptureReady(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t\tif (!capture.registrationExists) {\n\t\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(collectionValues).execute();\n\t\t\t\t\t\tschemaMutated = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(collectionValues).execute();\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t\tawait this.createContentTable(input.slug, trx, fields);\n\t\t\t\t}\n\n\t\t\t\tfor (const fieldBatch of chunks(rows, SEED_FIELD_INSERT_BATCH_SIZE)) {\n\t\t\t\t\tlet insert = trx.insertInto(\"_emdash_fields\").values(fieldBatch);\n\t\t\t\t\tif (capture.resuming) {\n\t\t\t\t\t\tinsert = insert.onConflict((conflict) =>\n\t\t\t\t\t\t\tconflict.columns([\"collection_id\", \"slug\"]).doNothing(),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tawait insert.execute();\n\t\t\t\t}\n\t\t\t\tlet indexedRows: readonly { id: string; slug: string; indexed?: number }[] = rows;\n\t\t\t\tif (capture.resuming) {\n\t\t\t\t\tindexedRows = await this.assertSeedFieldDefinitions(capture.collectionId, fields, trx);\n\t\t\t\t}\n\t\t\t\tfor (const field of indexedRows) {\n\t\t\t\t\tif (field.indexed === 1) {\n\t\t\t\t\t\tawait this.createFieldIndex(input.slug, field.id, field.slug, trx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (capture.captureRequired) {\n\t\t\t\t\tawait finalizeMediaUsageCollectionCapture(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, input.slug, \"CONTENT_USAGE_STALE\");\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tinput.slug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async assertSeedFieldDefinitions(\n\t\tcollectionId: string,\n\t\tfields: readonly CreateFieldInput[],\n\t\tdb: Kysely<Database>,\n\t): Promise<Selectable<FieldTable>[]> {\n\t\tconst stored = await db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.selectAll()\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.execute();\n\t\tif (stored.length !== fields.length) {\n\t\t\tthrow new SchemaError(\"Interrupted seed collection fields do not match\", \"CREATE_FAILED\");\n\t\t}\n\n\t\tlet maxSortOrder = -1;\n\t\tfor (const field of fields) {\n\t\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\t\t\tconst row = stored.find((candidate) => candidate.slug === field.slug);\n\t\t\tif (\n\t\t\t\t!row ||\n\t\t\t\trow.label !== field.label ||\n\t\t\t\trow.type !== field.type ||\n\t\t\t\trow.column_type !== FIELD_TYPE_TO_COLUMN[field.type] ||\n\t\t\t\trow.required !== (field.required ? 1 : 0) ||\n\t\t\t\trow.unique !== (field.unique ? 1 : 0) ||\n\t\t\t\trow.default_value !==\n\t\t\t\t\t(field.defaultValue !== undefined ? JSON.stringify(field.defaultValue) : null) ||\n\t\t\t\trow.validation !== (field.validation ? JSON.stringify(field.validation) : null) ||\n\t\t\t\trow.widget !== (field.widget ?? null) ||\n\t\t\t\trow.options !== (field.options ? JSON.stringify(field.options) : null) ||\n\t\t\t\trow.sort_order !== sortOrder ||\n\t\t\t\trow.searchable !== (field.searchable ? 1 : 0) ||\n\t\t\t\trow.indexed !== (field.indexed ? 1 : 0) ||\n\t\t\t\trow.translatable !== (field.translatable === false ? 0 : 1)\n\t\t\t) {\n\t\t\t\tthrow new SchemaError(\"Interrupted seed collection fields do not match\", \"CREATE_FAILED\");\n\t\t\t}\n\t\t}\n\t\treturn stored;\n\t}\n\n\t/**\n\t * Update a collection\n\t */\n\tasync updateCollection(slug: string, input: UpdateCollectionInput): Promise<Collection> {\n\t\treturn withTransaction(this.db, async (trx) => {\n\t\t\tconst existingRow = await trx\n\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t.selectAll()\n\t\t\t\t.executeTakeFirst();\n\t\t\tif (!existingRow) {\n\t\t\t\tthrow new SchemaError(`Collection \"${slug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t\t}\n\t\t\tconst existing = this.mapCollectionRow(existingRow);\n\t\t\tawait this.validateTitleDateFields(\n\t\t\t\texisting.id,\n\t\t\t\tslug,\n\t\t\t\t{\n\t\t\t\t\ttitleField: input.titleField,\n\t\t\t\t\tdateField: input.dateField,\n\t\t\t\t},\n\t\t\t\ttrx,\n\t\t\t);\n\t\t\tconst updates: Updateable<CollectionTable> = {};\n\n\t\t\tif (input.label !== undefined) updates.label = input.label;\n\t\t\tif (input.labelSingular !== undefined) updates.label_singular = input.labelSingular;\n\t\t\tif (input.description !== undefined) updates.description = input.description;\n\t\t\tif (input.icon !== undefined) updates.icon = input.icon;\n\t\t\tif (input.admin !== undefined) updates.admin_config = JSON.stringify(input.admin);\n\t\t\tif (input.supports !== undefined) updates.supports = JSON.stringify(input.supports);\n\t\t\tif (input.urlPattern !== undefined) updates.url_pattern = input.urlPattern;\n\t\t\tif (input.routable !== undefined) updates.routable = input.routable ? 1 : 0;\n\t\t\tif (input.hasSeo !== undefined) {\n\t\t\t\tupdates.has_seo = input.hasSeo ? 1 : 0;\n\t\t\t} else if (input.supports !== undefined) {\n\t\t\t\tupdates.has_seo = input.supports.includes(\"seo\") ? 1 : 0;\n\t\t\t}\n\t\t\tif (input.hidden !== undefined) updates.hidden = input.hidden ? 1 : 0;\n\t\t\tif (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;\n\t\t\tif (input.titleField !== undefined) updates.title_field = input.titleField || null;\n\t\t\tif (input.dateField !== undefined) updates.date_field = input.dateField || null;\n\t\t\tif (input.commentsEnabled !== undefined) {\n\t\t\t\tupdates.comments_enabled = input.commentsEnabled ? 1 : 0;\n\t\t\t}\n\t\t\tif (input.commentsModeration !== undefined) {\n\t\t\t\tupdates.comments_moderation = input.commentsModeration;\n\t\t\t}\n\t\t\tif (input.commentsClosedAfterDays !== undefined) {\n\t\t\t\tupdates.comments_closed_after_days = input.commentsClosedAfterDays;\n\t\t\t}\n\t\t\tif (input.commentsAutoApproveUsers !== undefined) {\n\t\t\t\tupdates.comments_auto_approve_users = input.commentsAutoApproveUsers ? 1 : 0;\n\t\t\t}\n\n\t\t\tupdates.updated_at = new Date().toISOString();\n\t\t\tawait trx\n\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t.set(updates)\n\t\t\t\t.where(\"id\", \"=\", existing.id)\n\t\t\t\t.execute();\n\n\t\t\tconst row = await trx\n\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t.selectAll()\n\t\t\t\t.executeTakeFirst();\n\n\t\t\tif (!row) {\n\t\t\t\tthrow new SchemaError(\"Failed to update collection\", \"UPDATE_FAILED\");\n\t\t\t}\n\n\t\t\t// Sync FTS state when the supports array changes (e.g. search toggled on/off)\n\t\t\tif (input.supports !== undefined) {\n\t\t\t\tconst hadSearch = existing.supports.includes(\"search\");\n\t\t\t\tconst hasSearch = parseSupports(row.supports).includes(\"search\");\n\t\t\t\tif (hadSearch !== hasSearch) {\n\t\t\t\t\tawait this.syncSearchState(slug, trx);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.mapCollectionRow(row);\n\t\t});\n\t}\n\n\t/**\n\t * Delete a collection\n\t */\n\tasync deleteCollection(slug: string, options?: { force?: boolean }): Promise<void> {\n\t\tconst existing = await this.getCollection(slug);\n\t\tif (existing && !options?.force && (await this.collectionHasContent(slug))) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Collection \"${slug}\" has content. Use force: true to delete.`,\n\t\t\t\t\"COLLECTION_HAS_CONTENT\",\n\t\t\t);\n\t\t}\n\t\tconst activated = await deleteActivatedMediaUsageCollection(this.db, {\n\t\t\tcollectionId: existing?.id,\n\t\t\tcollectionSlug: slug,\n\t\t\tforceDelete: options?.force === true,\n\t\t});\n\t\tif (activated === \"has_content\") {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Collection \"${slug}\" has content. Use force: true to delete.`,\n\t\t\t\t\"COLLECTION_HAS_CONTENT\",\n\t\t\t);\n\t\t}\n\t\tif (activated === \"in_progress\") {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" deletion is already in progress`, \"CONFLICT\");\n\t\t}\n\t\tif (activated === \"deleted\") return;\n\t\tif (!existing) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\tlet contentTableDropped = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\t// Drop FTS table and triggers before dropping the content table\n\t\t\t\tconst ftsManager = new FTSManager(trx);\n\t\t\t\tawait ftsManager.dropFtsTable(slug);\n\n\t\t\t\t// Drop the content table\n\t\t\t\tconst tableName = this.getTableName(slug);\n\t\t\t\tawait sql`DROP TABLE IF EXISTS ${sql.ref(tableName)}`.execute(trx);\n\t\t\t\tcontentTableDropped = true;\n\n\t\t\t\t// Delete the collection record (fields will cascade)\n\t\t\t\tawait trx.deleteFrom(\"_emdash_collections\").where(\"id\", \"=\", existing.id).execute();\n\t\t\t});\n\t\t\tawait deleteContentMediaUsageCollection(this.db, slug);\n\t\t} catch (error) {\n\t\t\tif (contentTableDropped && !(await tableExists(this.db, this.getTableName(slug)))) {\n\t\t\t\tawait deleteContentMediaUsageCollection(this.db, slug);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ============================================\n\t// Field Operations\n\t// ============================================\n\n\t/**\n\t * List fields for a collection\n\t */\n\tasync listFields(collectionId: string): Promise<Field[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(this.mapFieldRow);\n\t}\n\n\t/**\n\t * Get a field by slug within a collection\n\t */\n\tasync getField(collectionSlug: string, fieldSlug: string): Promise<Field | null> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) return null;\n\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t.selectAll()\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.mapFieldRow(row) : null;\n\t}\n\n\t/**\n\t * Create a new field\n\t */\n\tasync createField(collectionSlug: string, input: CreateFieldInput): Promise<Field> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(`Collection \"${collectionSlug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\t// Validate slug\n\t\tthis.validateSlug(input.slug, \"field\");\n\t\tif (RESERVED_FIELD_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Field slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\n\t\t// Check if field already exists\n\t\tconst existing = await this.getField(collectionSlug, input.slug);\n\t\tif (existing) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Field \"${input.slug}\" already exists in collection \"${collectionSlug}\"`,\n\t\t\t\t\"FIELD_EXISTS\",\n\t\t\t);\n\t\t}\n\n\t\tconst id = ulid();\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[input.type];\n\t\tassertIndexableField(input.type, input.indexed, input.slug);\n\n\t\t// Get max sort order\n\t\tconst maxSort = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t.select((eb) => eb.fn.max<number>(\"sort_order\").as(\"max\"))\n\t\t\t.executeTakeFirst();\n\n\t\tconst sortOrder = input.sortOrder ?? (maxSort?.max ?? -1) + 1;\n\t\tconst activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\tthis.db,\n\t\t\tcollectionSlug,\n\t\t);\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tconst created = await withTransaction(this.db, async (trx) => {\n\t\t\t\t// Insert field record\n\t\t\t\tawait trx\n\t\t\t\t\t.insertInto(\"_emdash_fields\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcollection_id: collection.id,\n\t\t\t\t\t\tslug: input.slug,\n\t\t\t\t\t\tlabel: input.label,\n\t\t\t\t\t\ttype: input.type,\n\t\t\t\t\t\tcolumn_type: columnType,\n\t\t\t\t\t\trequired: input.required ? 1 : 0,\n\t\t\t\t\t\tunique: input.unique ? 1 : 0,\n\t\t\t\t\t\tdefault_value:\n\t\t\t\t\t\t\tinput.defaultValue !== undefined ? JSON.stringify(input.defaultValue) : null,\n\t\t\t\t\t\tvalidation: input.validation ? JSON.stringify(input.validation) : null,\n\t\t\t\t\t\twidget: input.widget ?? null,\n\t\t\t\t\t\toptions: input.options ? JSON.stringify(input.options) : null,\n\t\t\t\t\t\tsort_order: sortOrder,\n\t\t\t\t\t\tsearchable: input.searchable ? 1 : 0,\n\t\t\t\t\t\tindexed: input.indexed ? 1 : 0,\n\t\t\t\t\t\ttranslatable: input.translatable === false ? 0 : 1,\n\t\t\t\t\t})\n\t\t\t\t\t.execute();\n\t\t\t\tschemaMutated = true;\n\n\t\t\t\t// Add column to content table — pass trx to stay on the same connection\n\t\t\t\tawait this.addColumn(\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\tinput.slug,\n\t\t\t\t\tinput.type,\n\t\t\t\t\t{\n\t\t\t\t\t\trequired: input.required,\n\t\t\t\t\t\tdefaultValue: input.defaultValue,\n\t\t\t\t\t},\n\t\t\t\t\ttrx,\n\t\t\t\t);\n\n\t\t\t\tif (input.indexed) {\n\t\t\t\t\tawait this.createFieldIndex(collectionSlug, id, input.slug, trx);\n\t\t\t\t}\n\n\t\t\t\t// Read the created field via trx (not this.db) to avoid connection mutex deadlock\n\t\t\t\tconst fieldRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t\t\t.where(\"slug\", \"=\", input.slug)\n\t\t\t\t\t.selectAll()\n\t\t\t\t\t.executeTakeFirst();\n\n\t\t\t\tif (!fieldRow) {\n\t\t\t\t\tthrow new SchemaError(\"Failed to create field\", \"CREATE_FAILED\");\n\t\t\t\t}\n\n\t\t\t\tconst field = this.mapFieldRow(fieldRow);\n\n\t\t\t\t// Sync search state if this field is searchable; support checks are handled by syncSearchState()\n\t\t\t\tif (input.searchable) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\treturn field;\n\t\t\t});\n\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t} else {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn created;\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Update a field\n\t */\n\tasync updateField(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tinput: UpdateFieldInput,\n\t): Promise<Field> {\n\t\tlet activeCoverageInvalidated = false;\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tconst updatedField = await withTransaction(this.db, async (trx) => {\n\t\t\t\tconst collectionRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t\t\t.select([\"id\", \"title_field\", \"date_field\"])\n\t\t\t\t\t.executeTakeFirst();\n\t\t\t\tconst fieldRow = collectionRow\n\t\t\t\t\t? await trx\n\t\t\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t\t\t.where(\"collection_id\", \"=\", collectionRow.id)\n\t\t\t\t\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t\t\t\t\t.selectAll()\n\t\t\t\t\t\t\t.executeTakeFirst()\n\t\t\t\t\t: undefined;\n\t\t\t\tif (!fieldRow) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Field \"${fieldSlug}\" not found in collection \"${collectionSlug}\"`,\n\t\t\t\t\t\t\"FIELD_NOT_FOUND\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst field = this.mapFieldRow(fieldRow);\n\t\t\t\tconst updates: Updateable<FieldTable> = {};\n\t\t\t\tlet nextType = field.type;\n\n\t\t\t\tif (input.type !== undefined && input.type !== field.type) {\n\t\t\t\t\tconst newColumnType = FIELD_TYPE_TO_COLUMN[input.type];\n\t\t\t\t\tif (newColumnType !== field.columnType) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`Cannot change field \"${fieldSlug}\" in collection \"${collectionSlug}\" from type ` +\n\t\t\t\t\t\t\t\t`\"${field.type}\" to \"${input.type}\": the underlying column type would change from ` +\n\t\t\t\t\t\t\t\t`${field.columnType} to ${newColumnType}, which requires a manual content migration.`,\n\t\t\t\t\t\t\t\"FIELD_TYPE_COLUMN_CHANGE\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (!TEXT_ALIAS_FIELD_TYPES.has(field.type) || !TEXT_ALIAS_FIELD_TYPES.has(input.type)) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`Cannot change field \"${fieldSlug}\" in collection \"${collectionSlug}\" from type ` +\n\t\t\t\t\t\t\t\t`\"${field.type}\" to \"${input.type}\" without a manual content migration.`,\n\t\t\t\t\t\t\t\"FIELD_TYPE_CHANGE_REQUIRES_MIGRATION\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (collectionRow?.title_field === fieldSlug && !TITLE_FIELD_TYPES.has(input.type)) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`titleField \"${fieldSlug}\" must stay a text field, not \"${input.type}\"`,\n\t\t\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (collectionRow?.date_field === fieldSlug && input.type !== \"datetime\") {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`dateField \"${fieldSlug}\" must stay a datetime field, not \"${input.type}\"`,\n\t\t\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tnextType = input.type;\n\t\t\t\t\tupdates.type = input.type;\n\t\t\t\t\tupdates.column_type = newColumnType;\n\t\t\t\t}\n\n\t\t\t\tif (input.required !== undefined && input.required !== field.required) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing required for field \"${fieldSlug}\" requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (input.unique !== undefined && input.unique !== field.unique) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing unique for field \"${fieldSlug}\" requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (input.translatable === false && field.translatable) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing field \"${fieldSlug}\" to non-translatable requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (input.label !== undefined) updates.label = input.label;\n\t\t\t\tif (input.required !== undefined) updates.required = input.required ? 1 : 0;\n\t\t\t\tif (input.unique !== undefined) updates.unique = input.unique ? 1 : 0;\n\t\t\t\tif (input.searchable !== undefined) updates.searchable = input.searchable ? 1 : 0;\n\t\t\t\tconst indexedChanged = input.indexed !== undefined && input.indexed !== field.indexed;\n\t\t\t\tif (indexedChanged) updates.indexed = input.indexed ? 1 : 0;\n\t\t\t\tif (input.translatable !== undefined) {\n\t\t\t\t\tupdates.translatable = input.translatable ? 1 : 0;\n\t\t\t\t}\n\t\t\t\tif (input.defaultValue !== undefined) {\n\t\t\t\t\tupdates.default_value = JSON.stringify(input.defaultValue);\n\t\t\t\t}\n\t\t\t\tif (input.validation !== undefined) {\n\t\t\t\t\tupdates.validation = input.validation ? JSON.stringify(input.validation) : null;\n\t\t\t\t}\n\t\t\t\tif (input.widget !== undefined) updates.widget = input.widget;\n\t\t\t\tif (input.options !== undefined) updates.options = JSON.stringify(input.options);\n\t\t\t\tif (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;\n\n\t\t\t\tassertIndexableField(nextType, input.indexed ?? field.indexed, fieldSlug);\n\t\t\t\tif (Object.keys(updates).length === 0) return field;\n\n\t\t\t\tactiveCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\t\t\ttrx,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t);\n\n\t\t\t\tif (Object.keys(updates).length > 0) {\n\t\t\t\t\tawait trx.updateTable(\"_emdash_fields\").set(updates).where(\"id\", \"=\", field.id).execute();\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t}\n\n\t\t\t\tif (indexedChanged) {\n\t\t\t\t\tif (input.indexed) {\n\t\t\t\t\t\tawait this.createFieldIndex(collectionSlug, field.id, fieldSlug, trx);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.dropFieldIndex(field.id, trx);\n\t\t\t\t\t}\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t}\n\n\t\t\t\t// Read the updated field via trx (not this.db) to avoid connection mutex deadlock\n\t\t\t\tconst updatedRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t.where(\"collection_id\", \"=\", field.collectionId)\n\t\t\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t\t\t.selectAll()\n\t\t\t\t\t.executeTakeFirst();\n\n\t\t\t\tif (!updatedRow) {\n\t\t\t\t\tthrow new SchemaError(\"Failed to update field\", \"UPDATE_FAILED\");\n\t\t\t\t}\n\n\t\t\t\tconst updated = this.mapFieldRow(updatedRow);\n\n\t\t\t\t// If searchable changed, sync FTS state for this collection\n\t\t\t\tconst searchableChanged =\n\t\t\t\t\tschemaMutated && input.searchable !== undefined && input.searchable !== field.searchable;\n\t\t\t\tif (searchableChanged) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\treturn updated;\n\t\t\t});\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn updatedField;\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Synchronize an existing FTS index with the collection's current state.\n\t *\n\t * Only rebuilds or disables — never first-time enables. First-time FTS\n\t * enablement is handled by the seed's explicit enableSearch call (which\n\t * is try-caught) or the admin UI toggle.\n\t *\n\t * - FTS active + still has search support and searchable fields → rebuild\n\t * - FTS active + lost search support or no searchable fields → disable\n\t * - FTS not active → no-op\n\t *\n\t * Pass `db` when calling from within a transaction so FTS operations\n\t * participate in the same transaction and are rolled back on failure.\n\t */\n\tprivate async syncSearchState(collectionSlug: string, db?: Kysely<Database>): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst ftsManager = new FTSManager(conn);\n\n\t\t// Query via conn (not this.db) to avoid connection mutex deadlock when called inside a transaction\n\t\tconst row = await conn\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t.select(\"supports\")\n\t\t\t.executeTakeFirst();\n\t\tif (!row) return;\n\n\t\tconst wantsSearch = parseSupports(row.supports).includes(\"search\");\n\t\tconst searchableFields = await ftsManager.getSearchableFields(collectionSlug);\n\t\tconst config = await ftsManager.getSearchConfig(collectionSlug);\n\t\tconst ftsActive = config?.enabled === true;\n\n\t\tif (wantsSearch && searchableFields.length > 0 && ftsActive) {\n\t\t\tawait ftsManager.rebuildIndex(\n\t\t\t\tcollectionSlug,\n\t\t\t\tsearchableFields,\n\t\t\t\tconfig?.weights,\n\t\t\t\tconfig?.tokenize,\n\t\t\t);\n\t\t} else if (ftsActive && (!wantsSearch || searchableFields.length === 0)) {\n\t\t\tawait ftsManager.disableSearch(collectionSlug);\n\t\t}\n\t}\n\n\t/**\n\t * Delete a field\n\t */\n\tasync deleteField(collectionSlug: string, fieldSlug: string): Promise<void> {\n\t\tconst field = await this.getField(collectionSlug, fieldSlug);\n\t\tif (!field) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Field \"${fieldSlug}\" not found in collection \"${collectionSlug}\"`,\n\t\t\t\t\"FIELD_NOT_FOUND\",\n\t\t\t);\n\t\t}\n\t\tconst activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\tthis.db,\n\t\t\tcollectionSlug,\n\t\t);\n\n\t\t// If this field powers the collection's titleField/dateField,\n\t\t// clear that reference in the same transaction — otherwise the metadata\n\t\t// would point at a dropped column and later crash the content list sort.\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tconst clearTitle = collection?.titleField === fieldSlug;\n\t\tconst clearDate = collection?.dateField === fieldSlug;\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\t// Delete the field record first so syncSearchState sees the updated field list.\n\t\t\t\t// This ordering matters for searchable fields: SQLite prevents dropping a column\n\t\t\t\t// that is still referenced by a trigger. syncSearchState drops and recreates the\n\t\t\t\t// FTS triggers based on the remaining searchable fields, clearing the dependency\n\t\t\t\t// before we attempt the ALTER TABLE DROP COLUMN below.\n\t\t\t\tawait trx.deleteFrom(\"_emdash_fields\").where(\"id\", \"=\", field.id).execute();\n\t\t\t\tschemaMutated = true;\n\n\t\t\t\tif (clearTitle || clearDate) {\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t...(clearTitle ? { title_field: null } : {}),\n\t\t\t\t\t\t\t...(clearDate ? { date_field: null } : {}),\n\t\t\t\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\n\t\t\t\t// If the deleted field was searchable, sync FTS state (removes old triggers)\n\t\t\t\tif (field.searchable) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\tif (field.indexed) {\n\t\t\t\t\tawait this.dropFieldIndex(field.id, trx);\n\t\t\t\t}\n\n\t\t\t\t// Drop column from content table — safe now because FTS triggers are gone\n\t\t\t\tawait this.dropColumn(collectionSlug, fieldSlug, trx);\n\t\t\t});\n\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t} else {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Reorder collections in the admin sidebar.\n\t *\n\t * `slugs` is the full desired order: every listed collection gets its\n\t * index as `sort_order`, and any collection left out has its explicit\n\t * position cleared, dropping it back to the alphabetical tail. Unknown or\n\t * duplicate slugs throw before anything is written.\n\t */\n\tasync reorderCollections(slugs: string[]): Promise<void> {\n\t\tconst known = new Set((await this.listCollections()).map((collection) => collection.slug));\n\n\t\tconst unknown = slugs.filter((slug) => !known.has(slug));\n\t\tif (unknown.length > 0) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Unknown collection${unknown.length > 1 ? \"s\" : \"\"}: ${unknown.join(\", \")}`,\n\t\t\t\t\"COLLECTION_NOT_FOUND\",\n\t\t\t\t{ slugs: unknown },\n\t\t\t);\n\t\t}\n\n\t\tconst duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);\n\t\tif (duplicates.length > 0) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Duplicate collection${duplicates.length > 1 ? \"s\" : \"\"}: ${[...new Set(duplicates)].join(\", \")}`,\n\t\t\t\t\"DUPLICATE_SLUG\",\n\t\t\t\t{ slugs: [...new Set(duplicates)] },\n\t\t\t);\n\t\t}\n\n\t\tconst now = new Date().toISOString();\n\t\tconst ordered = new Set(slugs);\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tfor (const [index, slug] of slugs.entries()) {\n\t\t\t\tawait trx\n\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t.set({ sort_order: index, updated_at: now })\n\t\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t\t.execute();\n\t\t\t}\n\n\t\t\tconst cleared = [...known].filter((slug) => !ordered.has(slug));\n\t\t\t// Chunked to stay under D1's bound-parameter limit; typical sites\n\t\t\t// clear far fewer than one chunk.\n\t\t\tfor (const slugChunk of chunks(cleared, SQL_BATCH_SIZE)) {\n\t\t\t\tawait trx\n\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t.set({ sort_order: null, updated_at: now })\n\t\t\t\t\t.where(\"slug\", \"in\", slugChunk)\n\t\t\t\t\t.execute();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Reorder fields\n\t */\n\tasync reorderFields(collectionSlug: string, fieldSlugs: string[]): Promise<void> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(`Collection \"${collectionSlug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\t// Update sort_order for each field\n\t\tfor (let i = 0; i < fieldSlugs.length; i++) {\n\t\t\tawait this.db\n\t\t\t\t.updateTable(\"_emdash_fields\")\n\t\t\t\t.set({ sort_order: i })\n\t\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t\t.where(\"slug\", \"=\", fieldSlugs[i])\n\t\t\t\t.execute();\n\t\t}\n\t}\n\n\t// ============================================\n\t// DDL Operations\n\t// ============================================\n\n\t/**\n\t * Create a content table for a collection\n\t */\n\tprivate async createContentTable(\n\t\tslug: string,\n\t\tdb?: Kysely<Database>,\n\t\tfields: readonly CreateFieldInput[] = [],\n\t\toptions: { ifNotExists?: boolean } = {},\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(slug);\n\n\t\tlet table: CreateTableBuilder<string, string> = conn.schema\n\t\t\t.createTable(tableName)\n\t\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t\t.addColumn(\"slug\", \"text\")\n\t\t\t.addColumn(\"status\", \"text\", (col) => col.defaultTo(\"draft\"))\n\t\t\t.addColumn(\"author_id\", \"text\")\n\t\t\t.addColumn(\"primary_byline_id\", \"text\")\n\t\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(conn)))\n\t\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(conn)))\n\t\t\t.addColumn(\"published_at\", \"text\")\n\t\t\t.addColumn(\"scheduled_at\", \"text\")\n\t\t\t.addColumn(\"deleted_at\", \"text\")\n\t\t\t.addColumn(\"version\", \"integer\", (col) => col.defaultTo(1))\n\t\t\t.addColumn(\"live_revision_id\", \"text\", (col) => col.references(\"revisions.id\"))\n\t\t\t.addColumn(\"draft_revision_id\", \"text\", (col) => col.references(\"revisions.id\"))\n\t\t\t.addColumn(\"locale\", \"text\", (col) => col.notNull().defaultTo(\"en\"))\n\t\t\t.addColumn(\"translation_group\", \"text\");\n\t\tif (options.ifNotExists) table = table.ifNotExists();\n\n\t\tfor (const field of fields) {\n\t\t\tconst columnName = this.getColumnName(field.slug);\n\t\t\tconst columnType = COLUMN_TYPE_TO_DATA_TYPE[FIELD_TYPE_TO_COLUMN[field.type]];\n\t\t\ttable = table.addColumn(columnName, columnType, (column) => {\n\t\t\t\tif (!field.required) return column;\n\n\t\t\t\tconst defaultValue =\n\t\t\t\t\tfield.defaultValue !== undefined\n\t\t\t\t\t\t? this.formatDefaultValue(field.defaultValue, field.type)\n\t\t\t\t\t\t: this.getEmptyDefault(field.type);\n\t\t\t\treturn column.notNull().defaultTo(sql.raw(defaultValue));\n\t\t\t});\n\t\t}\n\n\t\tawait table\n\t\t\t.addUniqueConstraint(`${tableName}_slug_locale_unique`, [\"slug\", \"locale\"])\n\t\t\t.execute();\n\n\t\tconst createIndex = options.ifNotExists ? sql`CREATE INDEX IF NOT EXISTS` : sql`CREATE INDEX`;\n\n\t\t// Create standard indexes\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_slug`)}\n\t\t\tON ${sql.ref(tableName)} (slug)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_scheduled`)}\n\t\t\tON ${sql.ref(tableName)} (scheduled_at)\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_live_revision`)}\n\t\t\tON ${sql.ref(tableName)} (live_revision_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_draft_revision`)}\n\t\t\tON ${sql.ref(tableName)} (draft_revision_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_author`)}\n\t\t\tON ${sql.ref(tableName)} (author_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_primary_byline`)}\n\t\t\tON ${sql.ref(tableName)} (primary_byline_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_locale`)}\n\t\t\tON ${sql.ref(tableName)} (locale)\n\t\t`.execute(conn);\n\n\t\t// Names must stay identical to migration 055, which creates these indexes\n\t\t// on tables that already exist. Lookups that don't constrain `deleted_at`\n\t\t// (menu and reference resolution) need the first; reads that do need the\n\t\t// second.\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (translation_group, locale)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_del_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, translation_group, locale)\n\t\t`.execute(conn);\n\n\t\t// Composite indexes for optimized query performance (see migration 033)\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_updated_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, updated_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_status`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, status)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_created_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, created_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_published_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, published_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\t// Locale-aware composite indexes for i18n content lists (see migration 041).\n\t\t// Short `loc_upd`/`loc_crt` suffix keeps the updated/created discriminator\n\t\t// inside Postgres's 63-byte identifier limit for long slugs; keep these\n\t\t// names identical to migration 041.\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_loc_upd`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, updated_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_loc_crt`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, created_at DESC, id DESC)\n\t\t`.execute(conn);\n\t}\n\n\tprivate getFieldIndexName(fieldId: string): string {\n\t\tif (!FIELD_ID_PATTERN.test(fieldId)) {\n\t\t\tthrow new SchemaError(`Invalid field id \"${fieldId}\"`, \"INVALID_FIELD_ID\");\n\t\t}\n\t\treturn `idx_cf_${fieldId.toLowerCase()}`;\n\t}\n\n\tprivate getLocaleFieldIndexName(fieldId: string): string {\n\t\treturn `${this.getFieldIndexName(fieldId)}_loc`;\n\t}\n\n\tprivate async createFieldIndex(\n\t\tcollectionSlug: string,\n\t\tfieldId: string,\n\t\tfieldSlug: string,\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\t\tconst indexName = this.getFieldIndexName(fieldId);\n\t\tconst localeIndexName = this.getLocaleFieldIndexName(fieldId);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(indexName)}\n\t\t\tON ${sql.ref(tableName)} (\n\t\t\t\t(${sql.ref(columnName)} IS NOT NULL),\n\t\t\t\t${sql.ref(columnName)},\n\t\t\t\tid\n\t\t\t)\n\t\t\tWHERE deleted_at IS NULL\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(localeIndexName)}\n\t\t\tON ${sql.ref(tableName)} (\n\t\t\t\tlocale,\n\t\t\t\t(${sql.ref(columnName)} IS NOT NULL),\n\t\t\t\t${sql.ref(columnName)},\n\t\t\t\tid\n\t\t\t)\n\t\t\tWHERE deleted_at IS NULL\n\t\t`.execute(conn);\n\t}\n\n\tprivate async dropFieldIndex(fieldId: string, db?: Kysely<Database>): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(this.getFieldIndexName(fieldId))}`.execute(conn);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(this.getLocaleFieldIndexName(fieldId))}`.execute(conn);\n\t}\n\n\t/**\n\t * Add a column to a content table\n\t */\n\tprivate async addColumn(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tfieldType: FieldType,\n\t\toptions?: { required?: boolean; defaultValue?: unknown },\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\n\t\t// Build ALTER TABLE statement\n\t\t// Note: SQLite requires DEFAULT for NOT NULL columns in ALTER TABLE\n\t\tif (options?.required && options?.defaultValue !== undefined) {\n\t\t\tconst defaultVal = this.formatDefaultValue(options.defaultValue, fieldType);\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}\n\t\t\t`.execute(conn);\n\t\t} else if (options?.required) {\n\t\t\t// For required fields without default, use empty string/0 as default\n\t\t\tconst defaultVal = this.getEmptyDefault(fieldType);\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}\n\t\t\t`.execute(conn);\n\t\t} else {\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)}\n\t\t\t`.execute(conn);\n\t\t}\n\t}\n\n\t/**\n\t * Drop a column from a content table\n\t */\n\tprivate async dropColumn(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tDROP COLUMN ${sql.ref(columnName)}\n\t\t`.execute(db ?? this.db);\n\t}\n\n\t// ============================================\n\t// Helpers\n\t// ============================================\n\n\t/**\n\t * Check if a collection has any content\n\t */\n\tprivate async collectionHasContent(slug: string): Promise<boolean> {\n\t\tconst tableName = this.getTableName(slug);\n\t\ttry {\n\t\t\tconst result = await sql<{ count: number }>`\n\t\t\t\tSELECT COUNT(*) as count FROM ${sql.ref(tableName)}\n\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t`.execute(this.db);\n\t\t\treturn (result.rows[0]?.count ?? 0) > 0;\n\t\t} catch {\n\t\t\t// Table might not exist\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get table name for a collection\n\t */\n\tprivate getTableName(slug: string): string {\n\t\tvalidateIdentifier(slug, \"collection slug\");\n\t\treturn `ec_${slug}`;\n\t}\n\n\t/**\n\t * Get column name for a field\n\t */\n\tprivate getColumnName(slug: string): string {\n\t\tvalidateIdentifier(slug, \"field slug\");\n\t\treturn slug;\n\t}\n\n\t/**\n\t * Validate a slug\n\t */\n\tprivate validateSlug(slug: string, type: \"collection\" | \"field\"): void {\n\t\tif (!slug || typeof slug !== \"string\") {\n\t\t\tthrow new SchemaError(`${type} slug is required`, \"INVALID_SLUG\");\n\t\t}\n\n\t\tif (!SLUG_VALIDATION_PATTERN.test(slug)) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`${type} slug must start with a letter and contain only lowercase letters, numbers, and underscores`,\n\t\t\t\t\"INVALID_SLUG\",\n\t\t\t);\n\t\t}\n\n\t\tif (slug.length > 63) {\n\t\t\tthrow new SchemaError(`${type} slug must be 63 characters or less`, \"INVALID_SLUG\");\n\t\t}\n\t}\n\n\t/**\n\t * Format a default value for SQL.\n\t *\n\t * SQLite `ALTER TABLE ADD COLUMN ... DEFAULT` requires a literal constant\n\t * expression — parameterized values cannot be used here. We manually escape\n\t * single quotes and coerce types to ensure the output is safe.\n\t *\n\t * INTEGER/REAL values are coerced through `Number()` which can only produce\n\t * digits, `.`, `-`, `e`, `Infinity`, or `NaN` — all safe in SQL.\n\t * TEXT/JSON values have single quotes escaped via SQL standard doubling (`''`).\n\t */\n\tprivate formatDefaultValue(value: unknown, fieldType: FieldType): string {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn \"NULL\";\n\t\t}\n\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\n\t\tif (columnType === \"JSON\") {\n\t\t\t// JSON.stringify produces valid JSON; escape single quotes for SQL literal\n\t\t\tconst json = JSON.stringify(value);\n\t\t\treturn `'${json.replace(SINGLE_QUOTE_PATTERN, \"''\")}'`;\n\t\t}\n\n\t\tif (columnType === \"INTEGER\") {\n\t\t\tif (typeof value === \"boolean\") {\n\t\t\t\treturn value ? \"1\" : \"0\";\n\t\t\t}\n\t\t\tconst num = Number(value);\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn \"0\";\n\t\t\t}\n\t\t\treturn String(Math.trunc(num));\n\t\t}\n\n\t\tif (columnType === \"REAL\") {\n\t\t\tconst num = Number(value);\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn \"0\";\n\t\t\t}\n\t\t\treturn String(num);\n\t\t}\n\n\t\t// TEXT — escape single quotes via SQL standard doubling\n\t\tlet text: string;\n\t\tif (typeof value === \"string\") {\n\t\t\ttext = value;\n\t\t} else if (typeof value === \"number\" || typeof value === \"boolean\") {\n\t\t\ttext = String(value);\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\ttext = JSON.stringify(value);\n\t\t} else {\n\t\t\ttext = \"\";\n\t\t}\n\t\treturn `'${text.replace(SINGLE_QUOTE_PATTERN, \"''\")}'`;\n\t}\n\n\t/**\n\t * Get empty default for a field type\n\t */\n\tprivate getEmptyDefault(fieldType: FieldType): string {\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\n\t\tswitch (columnType) {\n\t\t\tcase \"INTEGER\":\n\t\t\t\treturn \"0\";\n\t\t\tcase \"REAL\":\n\t\t\t\treturn \"0.0\";\n\t\t\tcase \"JSON\":\n\t\t\t\treturn \"'null'\";\n\t\t\tdefault:\n\t\t\t\treturn \"''\";\n\t\t}\n\t}\n\n\t/**\n\t * Map a collection row to a Collection object\n\t */\n\tprivate mapCollectionRow = (row: Selectable<CollectionTable>): Collection => {\n\t\tconst moderation = row.comments_moderation;\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tslug: row.slug,\n\t\t\tlabel: row.label,\n\t\t\tlabelSingular: row.label_singular ?? undefined,\n\t\t\tdescription: row.description ?? undefined,\n\t\t\ticon: row.icon ?? undefined,\n\t\t\tadmin: parseCollectionAdmin(row.admin_config),\n\t\t\tsupports: parseSupports(row.supports),\n\t\t\tsource: row.source && isCollectionSource(row.source) ? row.source : undefined,\n\t\t\thasSeo: row.has_seo === 1,\n\t\t\t// Raw value; undefined when unset. The admin list resolves the\n\t\t\t// default (title fallback chain / updatedAt) at the point of use.\n\t\t\ttitleField: row.title_field ?? undefined,\n\t\t\tdateField: row.date_field ?? undefined,\n\t\t\turlPattern: row.url_pattern ?? undefined,\n\t\t\troutable: row.routable !== 0,\n\t\t\thidden: row.hidden === 1,\n\t\t\tsortOrder: row.sort_order ?? undefined,\n\t\t\tcommentsEnabled: row.comments_enabled === 1,\n\t\t\tcommentsModeration:\n\t\t\t\tmoderation === \"all\" || moderation === \"first_time\" || moderation === \"none\"\n\t\t\t\t\t? moderation\n\t\t\t\t\t: \"first_time\",\n\t\t\tcommentsClosedAfterDays: row.comments_closed_after_days ?? 90,\n\t\t\tcommentsAutoApproveUsers: row.comments_auto_approve_users === 1,\n\t\t\tcreatedAt: row.created_at,\n\t\t\tupdatedAt: row.updated_at,\n\t\t};\n\t};\n\n\t/**\n\t * Map a field row to a Field object\n\t */\n\tprivate mapFieldRow = (row: Selectable<FieldTable>): Field => {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tcollectionId: row.collection_id,\n\t\t\tslug: row.slug,\n\t\t\tlabel: row.label,\n\t\t\ttype: isFieldType(row.type) ? row.type : \"string\",\n\t\t\tcolumnType: isColumnType(row.column_type) ? row.column_type : \"TEXT\",\n\t\t\trequired: row.required === 1,\n\t\t\tunique: row.unique === 1,\n\t\t\tdefaultValue: row.default_value ? JSON.parse(row.default_value) : undefined,\n\t\t\tvalidation: row.validation ? JSON.parse(row.validation) : undefined,\n\t\t\twidget: row.widget ?? undefined,\n\t\t\toptions: row.options ? JSON.parse(row.options) : undefined,\n\t\t\tsortOrder: row.sort_order,\n\t\t\tsearchable: row.searchable === 1,\n\t\t\tindexed: row.indexed === 1,\n\t\t\ttranslatable: row.translatable !== 0,\n\t\t\tcreatedAt: row.created_at,\n\t\t};\n\t};\n\n\t// ============================================\n\t// Discovery\n\t// ============================================\n\n\t/**\n\t * Discover orphaned content tables\n\t *\n\t * Finds ec_* tables that exist in the database but don't have a\n\t * corresponding entry in _emdash_collections.\n\t */\n\tasync discoverOrphanedTables(): Promise<\n\t\tArray<{ slug: string; tableName: string; rowCount: number }>\n\t> {\n\t\t// Get all ec_* tables\n\t\t// Content tables are ec_* (e.g., ec_posts, ec_pages)\n\t\t// Internal tables are _emdash_* (e.g., _emdash_collections, _emdash_fts_posts)\n\t\tconst allTables = await listTablesLike(this.db, \"ec_%\");\n\n\t\t// Get registered collections\n\t\tconst registered = await this.listCollections();\n\t\tconst registeredSlugs = new Set(registered.map((c) => c.slug));\n\n\t\t// Find orphans\n\t\tconst orphans: Array<{\n\t\t\tslug: string;\n\t\t\ttableName: string;\n\t\t\trowCount: number;\n\t\t}> = [];\n\n\t\tfor (const tableName of allTables) {\n\t\t\tconst slug = tableName.replace(EC_PREFIX_PATTERN, \"\");\n\n\t\t\tif (!registeredSlugs.has(slug)) {\n\t\t\t\t// Count rows in the orphaned table\n\t\t\t\ttry {\n\t\t\t\t\tconst countResult = await sql<{ count: number }>`\n\t\t\t\t\t\tSELECT COUNT(*) as count FROM ${sql.ref(tableName)}\n\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t`.execute(this.db);\n\n\t\t\t\t\torphans.push({\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\trowCount: countResult.rows[0]?.count ?? 0,\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// Table might have unexpected schema, still report it\n\t\t\t\t\torphans.push({\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\trowCount: 0,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn orphans;\n\t}\n\n\t/**\n\t * Register an orphaned table as a collection\n\t *\n\t * Creates a _emdash_collections entry for an existing ec_* table.\n\t */\n\tasync registerOrphanedTable(\n\t\tslug: string,\n\t\toptions?: {\n\t\t\tlabel?: string;\n\t\t\tlabelSingular?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<Collection> {\n\t\t// Verify table exists\n\t\tconst tableName = this.getTableName(slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tconst exists = await tableExists(this.db, tableName);\n\n\t\tif (!exists) {\n\t\t\tthrow new SchemaError(`Table \"${tableName}\" does not exist`, \"TABLE_NOT_FOUND\");\n\t\t}\n\n\t\t// Check if already registered\n\t\tconst existing = await this.getCollection(slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\t// Create collection entry\n\t\tconst proposedId = existing?.id ?? ulid();\n\t\tconst label = options?.label || this.slugToLabel(slug);\n\n\t\tlet collectionRegistered = false;\n\t\ttry {\n\t\t\tconst capture = await prepareMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: proposedId,\n\t\t\t\tcollectionSlug: slug,\n\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t});\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait installPreparedMediaUsageCollectionCapture(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t\tawait markMediaUsageCollectionCaptureReady(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!capture.registrationExists) {\n\t\t\t\tawait this.db\n\t\t\t\t\t.insertInto(\"_emdash_collections\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tid: capture.collectionId,\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tlabel_singular: options?.labelSingular ?? null,\n\t\t\t\t\t\tdescription: options?.description ?? null,\n\t\t\t\t\t\ticon: null,\n\t\t\t\t\t\tsupports: JSON.stringify([]),\n\t\t\t\t\t\tsource: \"discovered\",\n\t\t\t\t\t\thas_seo: 0,\n\t\t\t\t\t\turl_pattern: null,\n\t\t\t\t\t})\n\t\t\t\t\t.execute();\n\t\t\t\tcollectionRegistered = true;\n\t\t\t}\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait finalizeMediaUsageCollectionCapture(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection(slug);\n\t\t\tif (!collection) {\n\t\t\t\tthrow new SchemaError(\"Failed to register orphaned table\", \"REGISTER_FAILED\");\n\t\t\t}\n\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, slug, \"CONTENT_USAGE_STALE\");\n\n\t\t\treturn collection;\n\t\t} catch (error) {\n\t\t\tif (collectionRegistered) {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, slug, \"CONTENT_USAGE_STALE\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Convert slug to human-readable label\n\t */\n\tprivate slugToLabel(slug: string): string {\n\t\treturn slug\n\t\t\t.replace(UNDERSCORE_PATTERN, \" \")\n\t\t\t.replace(WORD_BOUNDARY_PATTERN, (c) => c.toUpperCase());\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registry-Cqa2I749.mjs","names":[],"sources":["../src/media/usage/activation.ts","../src/schema/registry.ts"],"sourcesContent":["import { sql, type Kysely, type RawBuilder, type Selectable } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { isPostgres, tableExists } from \"../../database/dialect-helpers.js\";\nimport type { Database, MediaUsageActivationTable } from \"../../database/types.js\";\nimport {\n\tinstallMediaUsageCaptureTriggers,\n\tverifyMediaUsageCaptureTriggers,\n} from \"./capture-triggers.js\";\n\nconst ACTIVATION_KEY = \"incremental_capture\";\nconst ACTIVATION_ERROR_CODE = \"MEDIA_USAGE_ACTIVATION_FAILED\";\nexport const MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION = 1;\n\nexport const MEDIA_USAGE_ACTIVATION_LIMITS = Object.freeze({\n\tcollectionsPerCall: 1,\n\tleaseDurationSeconds: 5 * 60,\n});\n\nexport type MediaUsageActivationResult =\n\t| { outcome: \"active\"; processedCollections: number }\n\t| {\n\t\t\toutcome: \"activating\";\n\t\t\tprocessedCollections: number;\n\t\t\tcollectionCursor: string | null;\n\t }\n\t| { outcome: \"lease_active\"; leaseExpiresAt: string }\n\t| { outcome: \"conflict\"; processedCollections: number };\n\nexport interface MediaUsageCollectionCapturePreparation {\n\tcaptureRequired: boolean;\n\tcollectionId: string;\n\tregistrationExists: boolean;\n\tresuming: boolean;\n}\n\nexport async function canResumeMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string; creationFingerprint?: string },\n): Promise<boolean> {\n\tconst activation = await findActivationIfAvailable(db);\n\tif (!activation) return false;\n\tassertRuntimeGeneration(activation);\n\tif (activation.state !== \"active\") return false;\n\n\tconst lifecycle = await db\n\t\t.selectFrom(\"_emdash_media_usage_index_status\")\n\t\t.select([\"collection_id\", \"cursor\"])\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", identity.collectionSlug)\n\t\t.where(\"collection_id\", \"=\", identity.collectionId)\n\t\t.where(\"capture_state\", \"in\", [\"installing\", \"ready\"])\n\t\t.executeTakeFirst();\n\treturn (\n\t\tlifecycle?.collection_id === identity.collectionId &&\n\t\t(identity.creationFingerprint\n\t\t\t? lifecycle.cursor === identity.creationFingerprint\n\t\t\t: lifecycle.cursor === null)\n\t);\n}\n\nexport async function prepareMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tinput: {\n\t\tcollectionId: string;\n\t\tcollectionSlug: string;\n\t\tcreationFingerprint?: string;\n\t\tregisteredCollectionId?: string;\n\t},\n): Promise<MediaUsageCollectionCapturePreparation> {\n\tconst activation = await findActivationIfAvailable(db);\n\tif (!activation) {\n\t\treturn {\n\t\t\tcaptureRequired: false,\n\t\t\tcollectionId: input.collectionId,\n\t\t\tregistrationExists: input.registeredCollectionId !== undefined,\n\t\t\tresuming: false,\n\t\t};\n\t}\n\tassertRuntimeGeneration(activation);\n\tif (activation.state !== \"active\") {\n\t\treturn {\n\t\t\tcaptureRequired: false,\n\t\t\tcollectionId: input.collectionId,\n\t\t\tregistrationExists: input.registeredCollectionId !== undefined,\n\t\t\tresuming: false,\n\t\t};\n\t}\n\n\tconst existing = await db\n\t\t.selectFrom(\"_emdash_media_usage_index_status\")\n\t\t.select([\"collection_id\", \"capture_state\", \"cursor\"])\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", input.collectionSlug)\n\t\t.executeTakeFirst();\n\tif (existing) {\n\t\tif (\n\t\t\texisting.collection_id &&\n\t\t\t(existing.capture_state === \"installing\" || existing.capture_state === \"ready\") &&\n\t\t\t(input.creationFingerprint\n\t\t\t\t? existing.cursor === input.creationFingerprint\n\t\t\t\t: existing.cursor === null) &&\n\t\t\t(input.registeredCollectionId === undefined ||\n\t\t\t\tinput.registeredCollectionId === existing.collection_id)\n\t\t) {\n\t\t\treturn {\n\t\t\t\tcaptureRequired: true,\n\t\t\t\tcollectionId: existing.collection_id,\n\t\t\t\tregistrationExists: input.registeredCollectionId !== undefined,\n\t\t\t\tresuming: true,\n\t\t\t};\n\t\t}\n\t\tthrow new Error(\"Media usage collection lifecycle identity conflict\");\n\t}\n\tif (input.registeredCollectionId !== undefined) {\n\t\tthrow new Error(\"Media usage collection lifecycle is missing\");\n\t}\n\n\tawait db\n\t\t.insertInto(\"_emdash_media_usage_index_status\")\n\t\t.values({\n\t\t\tadapter_id: \"content-media\",\n\t\t\tscope_type: \"collection\",\n\t\t\tscope_key: input.collectionSlug,\n\t\t\tstatus: \"never\",\n\t\t\tcollection_id: input.collectionId,\n\t\t\treconciliation_required: 1,\n\t\t\tcapture_state: \"installing\",\n\t\t\tcursor: input.creationFingerprint ?? null,\n\t\t\tupdated_at: timestampOffset(db, 0),\n\t\t})\n\t\t.execute();\n\treturn {\n\t\tcaptureRequired: true,\n\t\tcollectionId: input.collectionId,\n\t\tregistrationExists: false,\n\t\tresuming: false,\n\t};\n}\n\nexport async function installPreparedMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string },\n): Promise<void> {\n\tawait installMediaUsageCaptureTriggers(db, identity, { replaceExisting: false });\n\tif (!(await verifyMediaUsageCaptureTriggers(db, identity))) {\n\t\tthrow new Error(\"Media usage capture trigger verification failed\");\n\t}\n}\n\nexport async function markMediaUsageCollectionCaptureReady(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string },\n): Promise<void> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_index_status\")\n\t\t.set({ capture_state: \"ready\", updated_at: timestampOffset(db, 0) })\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", identity.collectionSlug)\n\t\t.where(\"collection_id\", \"=\", identity.collectionId)\n\t\t.where(\"capture_state\", \"in\", [\"installing\", \"ready\"])\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_media_usage_activation AS activation\n\t\t\t\tWHERE activation.task_key = ${ACTIVATION_KEY}\n\t\t\t\t\tAND activation.state = 'active'\n\t\t\t\t\tAND activation.runtime_generation = ${MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION}\n\t\t\t)`,\n\t\t)\n\t\t.executeTakeFirst();\n\tif (Number(result.numUpdatedRows ?? 0) === 0) {\n\t\tthrow new Error(\"Media usage collection readiness lost its fence\");\n\t}\n}\n\nexport async function finalizeMediaUsageCollectionCapture(\n\tdb: Kysely<Database>,\n\tidentity: { collectionId: string; collectionSlug: string },\n): Promise<void> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_index_status\")\n\t\t.set({ capture_state: \"active\", cursor: null, updated_at: timestampOffset(db, 0) })\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", identity.collectionSlug)\n\t\t.where(\"collection_id\", \"=\", identity.collectionId)\n\t\t.where(\"capture_state\", \"=\", \"ready\")\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_collections AS collection\n\t\t\t\tWHERE collection.id = ${identity.collectionId}\n\t\t\t\t\tAND collection.slug = ${identity.collectionSlug}\n\t\t\t)`,\n\t\t)\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_media_usage_activation AS activation\n\t\t\t\tWHERE activation.task_key = ${ACTIVATION_KEY}\n\t\t\t\t\tAND activation.state = 'active'\n\t\t\t\t\tAND activation.runtime_generation = ${MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION}\n\t\t\t)`,\n\t\t)\n\t\t.executeTakeFirst();\n\tif (Number(result.numUpdatedRows ?? 0) === 0) {\n\t\tthrow new Error(\"Media usage collection activation lost its fence\");\n\t}\n}\n\nexport async function activateMediaUsageCapture(\n\tdb: Kysely<Database>,\n\tinput: { writersDrained: true },\n): Promise<MediaUsageActivationResult> {\n\tif (input.writersDrained !== true) {\n\t\tthrow new Error(\"Media usage activation requires confirmation that writers are drained\");\n\t}\n\n\tconst before = await findActivation(db);\n\tassertRuntimeGeneration(before);\n\tif (before.state === \"active\") {\n\t\treturn { outcome: \"active\", processedCollections: 0 };\n\t}\n\n\tconst leaseToken = ulid();\n\tconst lease = await claimActivation(db, leaseToken);\n\tif (!lease) return activationClaimLoss(db);\n\n\tlet processedCollections = 0;\n\ttry {\n\t\tconst candidates = await findActivationCandidates(db, lease.collection_cursor);\n\t\tfor (const collection of candidates.slice(\n\t\t\t0,\n\t\t\tMEDIA_USAGE_ACTIVATION_LIMITS.collectionsPerCall,\n\t\t)) {\n\t\t\tawait requireActivationLease(db, leaseToken);\n\t\t\tawait prepareCollectionForActivation(db, collection, leaseToken);\n\t\t\tawait requireActivationLease(db, leaseToken);\n\t\t\tawait installMediaUsageCaptureTriggers(\n\t\t\t\tdb,\n\t\t\t\t{\n\t\t\t\t\tcollectionId: collection.id,\n\t\t\t\t\tcollectionSlug: collection.slug,\n\t\t\t\t},\n\t\t\t\t{ replaceExisting: false },\n\t\t\t);\n\t\t\tawait requireActivationLease(db, leaseToken);\n\t\t\tif (\n\t\t\t\t!(await verifyMediaUsageCaptureTriggers(db, {\n\t\t\t\t\tcollectionId: collection.id,\n\t\t\t\t\tcollectionSlug: collection.slug,\n\t\t\t\t}))\n\t\t\t) {\n\t\t\t\tthrow new Error(\"Media usage capture trigger verification failed\");\n\t\t\t}\n\t\t\tif (!(await activateCollectionLifecycle(db, collection, leaseToken))) {\n\t\t\t\tthrow new Error(\"Media usage collection activation lost its fence\");\n\t\t\t}\n\t\t\tprocessedCollections++;\n\t\t}\n\n\t\tconst collectionCursor =\n\t\t\tcandidates[Math.min(processedCollections, candidates.length) - 1]?.slug ??\n\t\t\tlease.collection_cursor;\n\t\tif (candidates.length > MEDIA_USAGE_ACTIVATION_LIMITS.collectionsPerCall) {\n\t\t\tif (!(await releaseActivationBatch(db, leaseToken, collectionCursor))) {\n\t\t\t\tthrow new Error(\"Media usage activation cursor lost its fence\");\n\t\t\t}\n\t\t\treturn { outcome: \"activating\", processedCollections, collectionCursor };\n\t\t}\n\n\t\tconst incomplete = await findIncompleteCollection(db);\n\t\tif (incomplete) {\n\t\t\tif (!(await releaseActivationBatch(db, leaseToken, null))) {\n\t\t\t\tthrow new Error(\"Media usage activation restart lost its fence\");\n\t\t\t}\n\t\t\treturn { outcome: \"activating\", processedCollections, collectionCursor: null };\n\t\t}\n\n\t\tif (!(await finalizeActivation(db, leaseToken, collectionCursor))) {\n\t\t\tthrow new Error(\"Media usage activation finalization lost its fence\");\n\t\t}\n\t\treturn { outcome: \"active\", processedCollections };\n\t} catch (error) {\n\t\tif (!(await activationLeaseIsLive(db, leaseToken))) {\n\t\t\treturn { outcome: \"conflict\", processedCollections };\n\t\t}\n\t\tawait recordActivationFailure(db, leaseToken);\n\t\tthrow new Error(\"Media usage activation failed\", { cause: error });\n\t}\n}\n\nasync function findActivation(\n\tdb: Kysely<Database>,\n): Promise<Selectable<MediaUsageActivationTable>> {\n\treturn db\n\t\t.selectFrom(\"_emdash_media_usage_activation\")\n\t\t.selectAll()\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.executeTakeFirstOrThrow();\n}\n\nasync function findActivationIfAvailable(\n\tdb: Kysely<Database>,\n): Promise<Selectable<MediaUsageActivationTable> | null> {\n\tif (!(await tableExists(db, \"_emdash_media_usage_activation\"))) return null;\n\treturn findActivation(db);\n}\n\nfunction assertRuntimeGeneration(activation: Selectable<MediaUsageActivationTable>): void {\n\tif (activation.runtime_generation !== MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION) {\n\t\tthrow new Error(\"Media usage activation runtime generation mismatch\");\n\t}\n}\n\nasync function claimActivation(\n\tdb: Kysely<Database>,\n\tleaseToken: string,\n): Promise<Selectable<MediaUsageActivationTable> | null> {\n\tconst now = timestampOffset(db, 0);\n\treturn (\n\t\t(await db\n\t\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t\t.set({\n\t\t\t\tstate: \"activating\",\n\t\t\t\tdrain_confirmed_at: now,\n\t\t\t\tlease_token: leaseToken,\n\t\t\t\tlease_expires_at: timestampOffset(db, MEDIA_USAGE_ACTIVATION_LIMITS.leaseDurationSeconds),\n\t\t\t\tattempt_count: sql<number>`attempt_count + 1`,\n\t\t\t\tlast_attempted_at: now,\n\t\t\t\tlast_error_code: null,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t\t.where((eb) =>\n\t\t\t\teb.or([\n\t\t\t\t\teb(\"state\", \"=\", \"expanded\"),\n\t\t\t\t\teb.and([\n\t\t\t\t\t\teb(\"state\", \"=\", \"activating\"),\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(\"lease_token\", \"is\", null),\n\t\t\t\t\t\t\teb.and([\n\t\t\t\t\t\t\t\teb(\"lease_expires_at\", \"is not\", null),\n\t\t\t\t\t\t\t\ttimestampIsDue(db, \"lease_expires_at\"),\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t]),\n\t\t\t\t\t]),\n\t\t\t\t]),\n\t\t\t)\n\t\t\t.returningAll()\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nasync function activationClaimLoss(db: Kysely<Database>): Promise<MediaUsageActivationResult> {\n\tconst current = await findActivation(db);\n\tassertRuntimeGeneration(current);\n\tif (current.state === \"active\") return { outcome: \"active\", processedCollections: 0 };\n\tif (\n\t\tcurrent.state === \"activating\" &&\n\t\tcurrent.lease_token &&\n\t\tcurrent.lease_expires_at &&\n\t\t(await activationLeaseIsLive(db, current.lease_token))\n\t) {\n\t\treturn { outcome: \"lease_active\", leaseExpiresAt: current.lease_expires_at };\n\t}\n\tthrow new Error(\"Media usage activation state is not claimable\");\n}\n\nasync function activationLeaseIsLive(db: Kysely<Database>, leaseToken: string): Promise<boolean> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_media_usage_activation\")\n\t\t.select(\"task_key\")\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.where(\"lease_expires_at\", \"is not\", null)\n\t\t.where(timestampIsLive(db, \"lease_expires_at\"))\n\t\t.executeTakeFirst();\n\treturn row !== undefined;\n}\n\nasync function findActivationCandidates(\n\tdb: Kysely<Database>,\n\tcollectionCursor: string | null,\n): Promise<Array<{ id: string; slug: string }>> {\n\tlet query = db.selectFrom(\"_emdash_collections\").select([\"id\", \"slug\"]);\n\tif (collectionCursor) query = query.where(\"slug\", \">\", collectionCursor);\n\treturn query\n\t\t.orderBy(\"slug\", \"asc\")\n\t\t.limit(MEDIA_USAGE_ACTIVATION_LIMITS.collectionsPerCall + 1)\n\t\t.execute();\n}\n\nasync function prepareCollectionForActivation(\n\tdb: Kysely<Database>,\n\tcollection: { id: string; slug: string },\n\tleaseToken: string,\n): Promise<void> {\n\tconst now = timestampOffset(db, 0);\n\tconst existingStatus = sql.ref(\"_emdash_media_usage_index_status.status\");\n\tconst existingCompletedAt = sql.ref(\"_emdash_media_usage_index_status.completed_at\");\n\tconst existingCollectionId = sql.ref(\"_emdash_media_usage_index_status.collection_id\");\n\tconst existingCaptureState = sql.ref(\"_emdash_media_usage_index_status.capture_state\");\n\tconst row = await db\n\t\t.insertInto(\"_emdash_media_usage_index_status\")\n\t\t.values({\n\t\t\tadapter_id: \"content-media\",\n\t\t\tscope_type: \"collection\",\n\t\t\tscope_key: collection.slug,\n\t\t\tstatus: \"never\",\n\t\t\tcollection_id: collection.id,\n\t\t\treconciliation_required: 1,\n\t\t\tcapture_state: \"installing\",\n\t\t\tupdated_at: now,\n\t\t})\n\t\t.onConflict((conflict) =>\n\t\t\tconflict\n\t\t\t\t.columns([\"adapter_id\", \"scope_type\", \"scope_key\"])\n\t\t\t\t.doUpdateSet({\n\t\t\t\t\tstatus: sql<string>`CASE WHEN ${existingStatus} IN ('complete', 'running') THEN 'stale' ELSE ${existingStatus} END`,\n\t\t\t\t\tcompleted_at: sql<\n\t\t\t\t\t\tstring | null\n\t\t\t\t\t>`CASE WHEN ${existingStatus} IN ('complete', 'running') THEN NULL ELSE ${existingCompletedAt} END`,\n\t\t\t\t\tcursor: null,\n\t\t\t\t\tcollection_id: collection.id,\n\t\t\t\t\treconciliation_required: 1,\n\t\t\t\t\tcapture_state: \"installing\",\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t})\n\t\t\t\t.where((eb) =>\n\t\t\t\t\teb.and([\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(existingCollectionId, \"is\", null),\n\t\t\t\t\t\t\teb(existingCollectionId, \"=\", collection.id),\n\t\t\t\t\t\t]),\n\t\t\t\t\t\teb.or([\n\t\t\t\t\t\t\teb(existingCaptureState, \"is\", null),\n\t\t\t\t\t\t\teb(existingCaptureState, \"!=\", \"deleting\"),\n\t\t\t\t\t\t]),\n\t\t\t\t\t\tactiveActivationLease(db, leaseToken),\n\t\t\t\t\t]),\n\t\t\t\t),\n\t\t)\n\t\t.returning(\"collection_id\")\n\t\t.executeTakeFirst();\n\tif (row?.collection_id !== collection.id) {\n\t\tthrow new Error(\"Media usage collection lifecycle identity conflict\");\n\t}\n}\n\nasync function requireActivationLease(db: Kysely<Database>, leaseToken: string): Promise<void> {\n\tif (!(await activationLeaseIsLive(db, leaseToken))) {\n\t\tthrow new Error(\"Media usage activation lease is no longer live\");\n\t}\n}\n\nasync function activateCollectionLifecycle(\n\tdb: Kysely<Database>,\n\tcollection: { id: string; slug: string },\n\tleaseToken: string,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_index_status\")\n\t\t.set({ capture_state: \"active\", updated_at: timestampOffset(db, 0) })\n\t\t.where(\"adapter_id\", \"=\", \"content-media\")\n\t\t.where(\"scope_type\", \"=\", \"collection\")\n\t\t.where(\"scope_key\", \"=\", collection.slug)\n\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t.where(\"capture_state\", \"=\", \"installing\")\n\t\t.where(\n\t\t\tsql<boolean>`EXISTS (\n\t\t\t\tSELECT 1 FROM _emdash_collections AS collection\n\t\t\t\tWHERE collection.id = ${collection.id}\n\t\t\t\t\tAND collection.slug = ${collection.slug}\n\t\t\t)`,\n\t\t)\n\t\t.where(activeActivationLease(db, leaseToken))\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0) > 0;\n}\n\nasync function releaseActivationBatch(\n\tdb: Kysely<Database>,\n\tleaseToken: string,\n\tcollectionCursor: string | null,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t.set({\n\t\t\tcollection_cursor: collectionCursor,\n\t\t\tlease_token: null,\n\t\t\tlease_expires_at: null,\n\t\t\tupdated_at: timestampOffset(db, 0),\n\t\t})\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.where(\"lease_expires_at\", \"is not\", null)\n\t\t.where(timestampIsLive(db, \"lease_expires_at\"))\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0) > 0;\n}\n\nasync function findIncompleteCollection(\n\tdb: Kysely<Database>,\n): Promise<{ id: string; slug: string } | null> {\n\tconst row = await db\n\t\t.selectFrom(\"_emdash_collections as collection\")\n\t\t.select([\"collection.id\", \"collection.slug\"])\n\t\t.where((eb) =>\n\t\t\teb.not(\n\t\t\t\teb.exists(\n\t\t\t\t\teb\n\t\t\t\t\t\t.selectFrom(\"_emdash_media_usage_index_status as status\")\n\t\t\t\t\t\t.select(\"status.scope_key\")\n\t\t\t\t\t\t.where(\"status.adapter_id\", \"=\", \"content-media\")\n\t\t\t\t\t\t.where(\"status.scope_type\", \"=\", \"collection\")\n\t\t\t\t\t\t.whereRef(\"status.scope_key\", \"=\", \"collection.slug\")\n\t\t\t\t\t\t.whereRef(\"status.collection_id\", \"=\", \"collection.id\")\n\t\t\t\t\t\t.where(\"status.capture_state\", \"=\", \"active\"),\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\t.orderBy(\"collection.slug\", \"asc\")\n\t\t.limit(1)\n\t\t.executeTakeFirst();\n\treturn row ?? null;\n}\n\nasync function finalizeActivation(\n\tdb: Kysely<Database>,\n\tleaseToken: string,\n\tcollectionCursor: string | null,\n): Promise<boolean> {\n\tconst now = timestampOffset(db, 0);\n\tconst result = await db\n\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t.set({\n\t\t\tstate: \"active\",\n\t\t\tcollection_cursor: collectionCursor,\n\t\t\tlease_token: null,\n\t\t\tlease_expires_at: null,\n\t\t\tlast_error_code: null,\n\t\t\tactivated_at: now,\n\t\t\tupdated_at: now,\n\t\t})\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.where(\"lease_expires_at\", \"is not\", null)\n\t\t.where(timestampIsLive(db, \"lease_expires_at\"))\n\t\t.where(\n\t\t\tsql<boolean>`NOT EXISTS (\n\t\t\t\tSELECT 1\n\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\tWHERE NOT EXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM _emdash_media_usage_index_status AS status\n\t\t\t\t\tWHERE status.adapter_id = 'content-media'\n\t\t\t\t\t\tAND status.scope_type = 'collection'\n\t\t\t\t\t\tAND status.scope_key = collection.slug\n\t\t\t\t\t\tAND status.collection_id = collection.id\n\t\t\t\t\t\tAND status.capture_state = 'active'\n\t\t\t\t)\n\t\t\t)`,\n\t\t)\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0) > 0;\n}\n\nasync function recordActivationFailure(db: Kysely<Database>, leaseToken: string): Promise<void> {\n\tawait db\n\t\t.updateTable(\"_emdash_media_usage_activation\")\n\t\t.set({\n\t\t\tlease_token: null,\n\t\t\tlease_expires_at: null,\n\t\t\tlast_error_code: ACTIVATION_ERROR_CODE,\n\t\t\tupdated_at: timestampOffset(db, 0),\n\t\t})\n\t\t.where(\"task_key\", \"=\", ACTIVATION_KEY)\n\t\t.where(\"state\", \"=\", \"activating\")\n\t\t.where(\"runtime_generation\", \"=\", MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION)\n\t\t.where(\"lease_token\", \"=\", leaseToken)\n\t\t.execute();\n}\n\nfunction activeActivationLease(db: Kysely<Database>, leaseToken: string): RawBuilder<boolean> {\n\treturn sql<boolean>`EXISTS (\n\t\tSELECT 1\n\t\tFROM _emdash_media_usage_activation AS activation\n\t\tWHERE activation.task_key = ${ACTIVATION_KEY}\n\t\t\tAND activation.state = 'activating'\n\t\t\tAND activation.runtime_generation = ${MEDIA_USAGE_ACTIVATION_RUNTIME_GENERATION}\n\t\t\tAND activation.lease_token = ${leaseToken}\n\t\t\tAND activation.lease_expires_at IS NOT NULL\n\t\t\tAND ${timestampIsLive(db, \"activation.lease_expires_at\")}\n\t)`;\n}\n\nfunction timestampIsDue(db: Kysely<Database>, column: string): RawBuilder<boolean> {\n\treturn isPostgres(db)\n\t\t? sql<boolean>`${sql.ref(column)}::timestamptz <= clock_timestamp()`\n\t\t: sql<boolean>`${sql.ref(column)} <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;\n}\n\nfunction timestampIsLive(db: Kysely<Database>, column: string): RawBuilder<boolean> {\n\treturn isPostgres(db)\n\t\t? sql<boolean>`${sql.ref(column)}::timestamptz > clock_timestamp()`\n\t\t: sql<boolean>`${sql.ref(column)} > strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;\n}\n\nfunction timestampOffset(db: Kysely<Database>, seconds: number): RawBuilder<string> {\n\tif (isPostgres(db)) {\n\t\treturn sql<string>`to_char(\n\t\t\tclock_timestamp() AT TIME ZONE 'UTC' + (${seconds} * interval '1 second'),\n\t\t\t'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"'\n\t\t)`;\n\t}\n\treturn sql<string>`strftime('%Y-%m-%dT%H:%M:%fZ', 'now', ${`+${seconds} seconds`})`;\n}\n","import type {\n\tColumnDataType,\n\tCreateTableBuilder,\n\tInsertable,\n\tKysely,\n\tSelectable,\n\tUpdateable,\n} from \"kysely\";\nimport { sql } from \"kysely\";\nimport { ulid } from \"ulidx\";\n\nimport { currentTimestamp, listTablesLike, tableExists } from \"../database/dialect-helpers.js\";\nimport { withTransaction } from \"../database/transaction.js\";\nimport type { CollectionTable, Database, FieldTable } from \"../database/types.js\";\nimport { validateIdentifier } from \"../database/validate.js\";\nimport {\n\tcanResumeMediaUsageCollectionCapture,\n\tfinalizeMediaUsageCollectionCapture,\n\tinstallPreparedMediaUsageCollectionCapture,\n\tmarkMediaUsageCollectionCaptureReady,\n\tprepareMediaUsageCollectionCapture,\n} from \"../media/usage/activation.js\";\nimport {\n\tdeleteActivatedMediaUsageCollection,\n\tisMediaUsageCollectionSlugDeleting,\n} from \"../media/usage/collection-deletion.js\";\nimport {\n\tdeleteContentMediaUsageCollection,\n\tinvalidateContentMediaUsageSchemaChange,\n\tmarkContentMediaUsageCollectionStaleSafely,\n} from \"../media/usage/content-refresh.js\";\nimport { FTSManager } from \"../search/fts-manager.js\";\nimport { chunks, SQL_BATCH_SIZE } from \"../utils/chunks.js\";\nimport {\n\ttype Collection,\n\ttype CollectionAdminConfig,\n\ttype CollectionSource,\n\ttype CollectionSupport,\n\ttype ColumnType,\n\ttype Field,\n\ttype CreateCollectionInput,\n\ttype UpdateCollectionInput,\n\ttype CreateFieldInput,\n\ttype UpdateFieldInput,\n\ttype CollectionWithFields,\n\ttype FieldType,\n\tFIELD_TYPE_TO_COLUMN,\n\tisIndexableFieldType,\n\tRESERVED_FIELD_SLUGS,\n\tRESERVED_COLLECTION_SLUGS,\n} from \"./types.js\";\n\n// Regex patterns for schema registry\nconst SLUG_VALIDATION_PATTERN = /^[a-z][a-z0-9_]*$/;\nconst EC_PREFIX_PATTERN = /^ec_/;\nconst SINGLE_QUOTE_PATTERN = /'/g;\nconst UNDERSCORE_PATTERN = /_/g;\nconst WORD_BOUNDARY_PATTERN = /\\b\\w/g;\nconst FIELD_ID_PATTERN = /^[0-9A-Z]{26}$/;\n\n/** Valid column types for runtime validation */\nconst COLUMN_TYPES: ReadonlySet<string> = new Set([\"TEXT\", \"REAL\", \"INTEGER\", \"JSON\"]);\nconst COLUMN_TYPE_TO_DATA_TYPE = {\n\tTEXT: \"text\",\n\tREAL: \"real\",\n\tINTEGER: \"integer\",\n\tJSON: \"json\",\n} satisfies Record<ColumnType, ColumnDataType>;\nconst TEXT_ALIAS_FIELD_TYPES: ReadonlySet<FieldType> = new Set([\"string\", \"text\", \"slug\"]);\n\n/** Field types usable as a `titleField` — plain text that reads well as a title. */\nconst TITLE_FIELD_TYPES: ReadonlySet<string> = new Set([\"string\", \"text\", \"slug\"]);\n\n/** Valid collection source prefixes/values */\nconst VALID_SOURCES: ReadonlySet<string> = new Set([\"manual\", \"discovered\", \"seed\"]);\n\nfunction isCollectionSource(value: string): value is CollectionSource {\n\treturn VALID_SOURCES.has(value) || value.startsWith(\"template:\") || value.startsWith(\"import:\");\n}\n\nfunction isFieldType(value: string): value is FieldType {\n\treturn value in FIELD_TYPE_TO_COLUMN;\n}\n\nfunction isColumnType(value: string): value is ColumnType {\n\treturn COLUMN_TYPES.has(value);\n}\n\nconst VALID_COLLECTION_SUPPORTS: ReadonlySet<string> = new Set<CollectionSupport>([\n\t\"drafts\",\n\t\"revisions\",\n\t\"preview\",\n\t\"scheduling\",\n\t\"search\",\n\t\"seo\",\n]);\n\n// Each _emdash_fields row uses 16 bound parameters. Six rows keep every\n// multi-row INSERT below D1's 100-parameter statement limit.\nconst SEED_FIELD_INSERT_BATCH_SIZE = 6;\n\n/**\n * Rank given to collections without an explicit `sort_order`. SQLite sorts\n * NULL first on ASC while Postgres sorts it last, so the fallback is\n * materialised with COALESCE rather than left to the dialect.\n */\nconst UNORDERED_COLLECTION_RANK = 2147483647;\n\n/**\n * Collection ordering shared by every list read: explicit `sort_order`\n * first (ascending), then alphabetically by slug.\n */\nconst collectionOrder = sql<number>`coalesce(sort_order, ${sql.lit(UNORDERED_COLLECTION_RANK)})`;\n\nfunction assertIndexableField(type: FieldType, indexed: boolean | undefined, slug: string): void {\n\tif (indexed && !isIndexableFieldType(type)) {\n\t\tthrow new SchemaError(\n\t\t\t`Field \"${slug}\" cannot be indexed because type \"${type}\" is not a scalar query type`,\n\t\t\t\"FIELD_NOT_INDEXABLE\",\n\t\t);\n\t}\n}\n\nfunction isCollectionSupport(value: unknown): value is CollectionSupport {\n\treturn typeof value === \"string\" && VALID_COLLECTION_SUPPORTS.has(value);\n}\n\n/**\n * Parse a collection's `supports` column (stored as a JSON array of\n * CollectionSupport keys). Unknown/invalid entries are filtered out so the\n * runtime value matches the declared `CollectionSupport[]` type.\n *\n * Throws on malformed JSON so corruption surfaces loudly; returns an empty\n * array only for explicitly null/empty values or non-array JSON.\n */\nfunction parseSupports(raw: string | null | undefined): CollectionSupport[] {\n\tif (!raw) return [];\n\tconst parsed: unknown = JSON.parse(raw);\n\tif (!Array.isArray(parsed)) return [];\n\treturn parsed.filter(isCollectionSupport);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction parseCollectionAdmin(raw: string | null | undefined): CollectionAdminConfig | undefined {\n\tif (!raw) return undefined;\n\tconst parsed: unknown = JSON.parse(raw);\n\tif (!isRecord(parsed)) return undefined;\n\tconst listColumns = parsed.listColumns;\n\treturn {\n\t\tlistColumns: Array.isArray(listColumns)\n\t\t\t? listColumns.filter((value): value is string => typeof value === \"string\")\n\t\t\t: undefined,\n\t};\n}\n\nexport async function buildSeedCollectionCaptureFingerprint(\n\tinput: Omit<CreateCollectionInput, \"source\">,\n\tfields: readonly CreateFieldInput[],\n): Promise<string> {\n\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\tlet maxSortOrder = -1;\n\tconst definitions = fields.map((field) => {\n\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\t\treturn {\n\t\t\tslug: field.slug,\n\t\t\tlabel: field.label,\n\t\t\ttype: field.type,\n\t\t\trequired: field.required ?? false,\n\t\t\tunique: field.unique ?? false,\n\t\t\tdefaultValue: field.defaultValue === undefined ? null : JSON.stringify(field.defaultValue),\n\t\t\tvalidation: field.validation ? JSON.stringify(field.validation) : null,\n\t\t\twidget: field.widget ?? null,\n\t\t\toptions: field.options ? JSON.stringify(field.options) : null,\n\t\t\tsortOrder,\n\t\t\tsearchable: field.searchable ?? false,\n\t\t\ttranslatable: field.translatable ?? true,\n\t\t};\n\t});\n\tconst payload = JSON.stringify(\n\t\tcanonicalizeFingerprintValue({\n\t\t\tversion: 1,\n\t\t\tcollection: {\n\t\t\t\tslug: input.slug,\n\t\t\t\tlabel: input.label,\n\t\t\t\tlabelSingular: input.labelSingular ?? null,\n\t\t\t\tdescription: input.description ?? null,\n\t\t\t\ticon: input.icon ?? null,\n\t\t\t\tadmin: input.admin ?? null,\n\t\t\t\tsupports,\n\t\t\t\thasSeo,\n\t\t\t\thidden: input.hidden ?? false,\n\t\t\t\tsortOrder: input.sortOrder ?? null,\n\t\t\t\tcommentsEnabled: input.commentsEnabled ?? false,\n\t\t\t\turlPattern: input.urlPattern ?? null,\n\t\t\t\troutable: input.routable ?? true,\n\t\t\t},\n\t\t\tfields: definitions,\n\t\t}),\n\t);\n\tconst digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(payload));\n\tconst hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\")).join(\n\t\t\"\",\n\t);\n\treturn `media-usage-seed:v1:sha256:${hex}`;\n}\n\nfunction canonicalizeFingerprintValue(value: unknown): unknown {\n\tif (value === undefined) return { __emdashUndefined: true };\n\tif (Array.isArray(value)) return value.map(canonicalizeFingerprintValue);\n\tif (typeof value !== \"object\" || value === null) return value;\n\n\tconst canonical: Record<string, unknown> = {};\n\tfor (const [key, entry] of Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b))) {\n\t\tcanonical[key] = canonicalizeFingerprintValue(entry);\n\t}\n\treturn canonical;\n}\n\n/**\n * Error thrown when a schema operation fails\n */\nexport class SchemaError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic code: string,\n\t\tpublic details?: Record<string, unknown>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SchemaError\";\n\t}\n}\n\n/**\n * Schema Registry\n *\n * Manages collection and field definitions stored in D1.\n * Handles runtime DDL operations (CREATE TABLE, ALTER TABLE).\n */\nexport class SchemaRegistry {\n\tconstructor(private db: Kysely<Database>) {}\n\n\t// ============================================\n\t// Collection Operations\n\t// ============================================\n\n\t/**\n\t * List all collections\n\t */\n\tasync listCollections(): Promise<Collection[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(collectionOrder, \"asc\")\n\t\t\t.orderBy(\"slug\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(this.mapCollectionRow);\n\t}\n\n\t/**\n\t * Get a collection by slug\n\t */\n\tasync getCollection(slug: string): Promise<Collection | null> {\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t.selectAll()\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.mapCollectionRow(row) : null;\n\t}\n\n\t/**\n\t * Get a collection with all its fields\n\t */\n\tasync getCollectionWithFields(slug: string): Promise<CollectionWithFields | null> {\n\t\tconst collection = await this.getCollection(slug);\n\t\tif (!collection) return null;\n\n\t\tconst fields = await this.listFields(collection.id);\n\n\t\treturn { ...collection, fields };\n\t}\n\n\t/**\n\t * List every collection together with its fields in O(1) query shapes\n\t * — one for collections, then one batched query for the fields of every\n\t * returned collection — instead of the N+1 pattern of `listCollections`\n\t * + per-collection `listFields`. The fields query is chunked at\n\t * `SQL_BATCH_SIZE` to stay under D1's bound-parameter limit, so on\n\t * sites with more than `SQL_BATCH_SIZE` collections the field fetch\n\t * becomes `ceil(collectionCount / SQL_BATCH_SIZE)` queries — still\n\t * a constant factor, not N+1. Typical sites have well under\n\t * `SQL_BATCH_SIZE` collections, so this is two queries in practice.\n\t *\n\t * Used by the manifest build, which previously paid N+1 round-trips on\n\t * every admin request. Each round-trip costs ~80–150ms against the D1\n\t * primary on a busy link, so a 10-collection site spent ~1 s rebuilding\n\t * a manifest that is now built fresh per admin request (no cache).\n\t */\n\tasync listCollectionsWithFields(): Promise<CollectionWithFields[]> {\n\t\tconst collectionRows = await this.db\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.selectAll()\n\t\t\t.orderBy(collectionOrder, \"asc\")\n\t\t\t.orderBy(\"slug\", \"asc\")\n\t\t\t.execute();\n\n\t\tif (collectionRows.length === 0) return [];\n\n\t\tconst fieldsByCollection = new Map<string, Field[]>();\n\t\t// Chunk to stay under D1's bound-parameter limit. Typical sites have\n\t\t// well under SQL_BATCH_SIZE collections, so this is a single query\n\t\t// in practice; on larger sites it becomes a small constant number\n\t\t// of queries, never N+1.\n\t\tfor (const idChunk of chunks(\n\t\t\tcollectionRows.map((c) => c.id),\n\t\t\tSQL_BATCH_SIZE,\n\t\t)) {\n\t\t\tconst fieldRows = await this.db\n\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t.where(\"collection_id\", \"in\", idChunk)\n\t\t\t\t.selectAll()\n\t\t\t\t.orderBy(\"collection_id\", \"asc\")\n\t\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t\t.execute();\n\t\t\tfor (const row of fieldRows) {\n\t\t\t\tconst list = fieldsByCollection.get(row.collection_id) ?? [];\n\t\t\t\tlist.push(this.mapFieldRow(row));\n\t\t\t\tfieldsByCollection.set(row.collection_id, list);\n\t\t\t}\n\t\t}\n\n\t\treturn collectionRows.map((c) => ({\n\t\t\t...this.mapCollectionRow(c),\n\t\t\tfields: fieldsByCollection.get(c.id) ?? [],\n\t\t}));\n\t}\n\n\t/**\n\t * Validate `titleField`/`dateField` against the collection's fields:\n\t * `titleField` must be a text-like field; `dateField` must be a `datetime` field.\n\t * Only truthy values are checked (undefined = unchanged, null/\"\" = cleared).\n\t */\n\tprivate async validateTitleDateFields(\n\t\tcollectionId: string,\n\t\tcollectionSlug: string,\n\t\tinput: { titleField?: string | null; dateField?: string | null },\n\t\tdb: Kysely<Database> = this.db,\n\t): Promise<void> {\n\t\tconst slugs = [input.titleField, input.dateField].filter((slug): slug is string => !!slug);\n\t\tif (slugs.length === 0) return;\n\n\t\tconst rows = await db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.where(\"slug\", \"in\", slugs)\n\t\t\t.select([\"slug\", \"type\"])\n\t\t\t.execute();\n\t\tconst typeBySlug = new Map(rows.map((row) => [row.slug, row.type]));\n\n\t\tif (input.titleField) {\n\t\t\tconst type = typeBySlug.get(input.titleField);\n\t\t\tif (type === undefined) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`titleField \"${input.titleField}\" is not a field on \"${collectionSlug}\"`,\n\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!TITLE_FIELD_TYPES.has(type)) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`titleField \"${input.titleField}\" must be a text field (got \"${type}\")`,\n\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (input.dateField) {\n\t\t\tconst type = typeBySlug.get(input.dateField);\n\t\t\tif (type === undefined) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`dateField \"${input.dateField}\" is not a field on \"${collectionSlug}\"`,\n\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (type !== \"datetime\") {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`dateField \"${input.dateField}\" must be a datetime field (got \"${type}\")`,\n\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Create a new collection\n\t */\n\tasync createCollection(input: CreateCollectionInput): Promise<Collection> {\n\t\t// Validate slug\n\t\tthis.validateSlug(input.slug, \"collection\");\n\t\tif (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Collection slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\t// Check if collection already exists\n\t\tconst existing = await this.getCollection(input.slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst proposedId = existing?.id ?? ulid();\n\n\t\t// Default `supports` to drafts + revisions when the caller didn't\n\t\t// specify it. Explicit empty array (`[]`) is preserved as an opt-out\n\t\t// — only `undefined` triggers the default. This is the canonical\n\t\t// default for new collections; the MCP and admin UI layers used to\n\t\t// duplicate this default but now defer to the registry.\n\t\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\n\t\t// Insert collection record and create content table in a transaction\n\t\t// so a failure in table creation doesn't leave an orphaned row.\n\t\t// Uses withTransaction for D1 compatibility (no transaction support).\n\t\t// Derive hasSeo from supports array if not explicitly set\n\t\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tconst capture = await prepareMediaUsageCollectionCapture(trx, {\n\t\t\t\tcollectionId: proposedId,\n\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t});\n\t\t\tconst values: Insertable<CollectionTable> = {\n\t\t\t\tid: capture.collectionId,\n\t\t\t\tslug: input.slug,\n\t\t\t\tlabel: input.label,\n\t\t\t\tlabel_singular: input.labelSingular ?? null,\n\t\t\t\tdescription: input.description ?? null,\n\t\t\t\ticon: input.icon ?? null,\n\t\t\t\tadmin_config: input.admin ? JSON.stringify(input.admin) : null,\n\t\t\t\tsupports: JSON.stringify(supports),\n\t\t\t\tsource: input.source ?? \"manual\",\n\t\t\t\thas_seo: hasSeo ? 1 : 0,\n\t\t\t\troutable: input.routable === false ? 0 : 1,\n\t\t\t\thidden: input.hidden ? 1 : 0,\n\t\t\t\tsort_order: input.sortOrder ?? null,\n\t\t\t\tcomments_enabled: input.commentsEnabled ? 1 : 0,\n\t\t\t\turl_pattern: input.urlPattern ?? null,\n\t\t\t};\n\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait this.createContentTable(input.slug, trx, [], {\n\t\t\t\t\tifNotExists: capture.resuming,\n\t\t\t\t});\n\t\t\t\tawait installPreparedMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\tawait markMediaUsageCollectionCaptureReady(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\tif (!capture.registrationExists) {\n\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(values).execute();\n\t\t\t\t}\n\t\t\t\tawait finalizeMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait trx.insertInto(\"_emdash_collections\").values(values).execute();\n\t\t\tawait this.createContentTable(input.slug, trx);\n\t\t});\n\n\t\tconst collection = await this.getCollection(input.slug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(\"Failed to create collection\", \"CREATE_FAILED\");\n\t\t}\n\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Create a seed-owned collection and all of its fields in bulk.\n\t *\n\t * Fresh seeds can define dozens of fields. Creating them through\n\t * `createField` performs multiple reads, one ALTER TABLE, and one media\n\t * usage invalidation per field, which can exhaust D1's per-request query\n\t * budget. This path validates the full schema before mutating it, creates\n\t * the complete content table in one statement, and inserts field metadata\n\t * in parameter-safe batches.\n\t */\n\tasync createSeedCollection(\n\t\tinput: Omit<CreateCollectionInput, \"source\">,\n\t\tfields: readonly CreateFieldInput[],\n\t): Promise<void> {\n\t\tthis.validateSlug(input.slug, \"collection\");\n\t\tif (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Collection slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst fieldSlugs = new Set<string>();\n\t\tfor (const field of fields) {\n\t\t\tthis.validateSlug(field.slug, \"field\");\n\t\t\tassertIndexableField(field.type, field.indexed, field.slug);\n\t\t\tif (RESERVED_FIELD_SLUGS.includes(field.slug)) {\n\t\t\t\tthrow new SchemaError(`Field slug \"${field.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t\t}\n\t\t\tif (fieldSlugs.has(field.slug)) {\n\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t`Field \"${field.slug}\" already exists in collection \"${input.slug}\"`,\n\t\t\t\t\t\"FIELD_EXISTS\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tfieldSlugs.add(field.slug);\n\t\t}\n\n\t\tconst supports = input.supports ?? [\"drafts\", \"revisions\"];\n\t\tconst hasSeo = input.hasSeo ?? supports.includes(\"seo\") ?? false;\n\t\tconst creationFingerprint = await buildSeedCollectionCaptureFingerprint(input, fields);\n\t\tconst existing = await this.getCollection(input.slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t\tcreationFingerprint,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${input.slug}\" already exists`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\tconst proposedCollectionId = existing?.id ?? ulid();\n\t\tlet maxSortOrder = -1;\n\t\tconst fieldRows: Insertable<FieldTable>[] = fields.map((field) => {\n\t\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\n\t\t\treturn {\n\t\t\t\tid: ulid(),\n\t\t\t\tcollection_id: proposedCollectionId,\n\t\t\t\tslug: field.slug,\n\t\t\t\tlabel: field.label,\n\t\t\t\ttype: field.type,\n\t\t\t\tcolumn_type: FIELD_TYPE_TO_COLUMN[field.type],\n\t\t\t\trequired: field.required ? 1 : 0,\n\t\t\t\tunique: field.unique ? 1 : 0,\n\t\t\t\tdefault_value: field.defaultValue !== undefined ? JSON.stringify(field.defaultValue) : null,\n\t\t\t\tvalidation: field.validation ? JSON.stringify(field.validation) : null,\n\t\t\t\twidget: field.widget ?? null,\n\t\t\t\toptions: field.options ? JSON.stringify(field.options) : null,\n\t\t\t\tsort_order: sortOrder,\n\t\t\t\tsearchable: field.searchable ? 1 : 0,\n\t\t\t\tindexed: field.indexed ? 1 : 0,\n\t\t\t\ttranslatable: field.translatable === false ? 0 : 1,\n\t\t\t};\n\t\t});\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\tconst capture = await prepareMediaUsageCollectionCapture(trx, {\n\t\t\t\t\tcollectionId: proposedCollectionId,\n\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\tcreationFingerprint,\n\t\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t\t});\n\t\t\t\tconst collectionValues: Insertable<CollectionTable> = {\n\t\t\t\t\tid: capture.collectionId,\n\t\t\t\t\tslug: input.slug,\n\t\t\t\t\tlabel: input.label,\n\t\t\t\t\tlabel_singular: input.labelSingular ?? null,\n\t\t\t\t\tdescription: input.description ?? null,\n\t\t\t\t\ticon: input.icon ?? null,\n\t\t\t\t\tadmin_config: input.admin ? JSON.stringify(input.admin) : null,\n\t\t\t\t\tsupports: JSON.stringify(supports),\n\t\t\t\t\tsource: \"seed\",\n\t\t\t\t\thas_seo: hasSeo ? 1 : 0,\n\t\t\t\t\troutable: input.routable === false ? 0 : 1,\n\t\t\t\t\thidden: input.hidden ? 1 : 0,\n\t\t\t\t\tsort_order: input.sortOrder ?? null,\n\t\t\t\t\tcomments_enabled: input.commentsEnabled ? 1 : 0,\n\t\t\t\t\turl_pattern: input.urlPattern ?? null,\n\t\t\t\t};\n\t\t\t\tconst rows = fieldRows.map((row) => ({\n\t\t\t\t\t...row,\n\t\t\t\t\tcollection_id: capture.collectionId,\n\t\t\t\t}));\n\n\t\t\t\tif (capture.captureRequired) {\n\t\t\t\t\tawait this.createContentTable(input.slug, trx, fields, {\n\t\t\t\t\t\tifNotExists: capture.resuming,\n\t\t\t\t\t});\n\t\t\t\t\tawait installPreparedMediaUsageCollectionCapture(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t\tawait markMediaUsageCollectionCaptureReady(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t\tif (!capture.registrationExists) {\n\t\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(collectionValues).execute();\n\t\t\t\t\t\tschemaMutated = true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tawait trx.insertInto(\"_emdash_collections\").values(collectionValues).execute();\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t\tawait this.createContentTable(input.slug, trx, fields);\n\t\t\t\t}\n\n\t\t\t\tfor (const fieldBatch of chunks(rows, SEED_FIELD_INSERT_BATCH_SIZE)) {\n\t\t\t\t\tlet insert = trx.insertInto(\"_emdash_fields\").values(fieldBatch);\n\t\t\t\t\tif (capture.resuming) {\n\t\t\t\t\t\tinsert = insert.onConflict((conflict) =>\n\t\t\t\t\t\t\tconflict.columns([\"collection_id\", \"slug\"]).doNothing(),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tawait insert.execute();\n\t\t\t\t}\n\t\t\t\tlet indexedRows: readonly { id: string; slug: string; indexed?: number }[] = rows;\n\t\t\t\tif (capture.resuming) {\n\t\t\t\t\tindexedRows = await this.assertSeedFieldDefinitions(capture.collectionId, fields, trx);\n\t\t\t\t}\n\t\t\t\tfor (const field of indexedRows) {\n\t\t\t\t\tif (field.indexed === 1) {\n\t\t\t\t\t\tawait this.createFieldIndex(input.slug, field.id, field.slug, trx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (capture.captureRequired) {\n\t\t\t\t\tawait finalizeMediaUsageCollectionCapture(trx, {\n\t\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\t\tcollectionSlug: input.slug,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, input.slug, \"CONTENT_USAGE_STALE\");\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tinput.slug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async assertSeedFieldDefinitions(\n\t\tcollectionId: string,\n\t\tfields: readonly CreateFieldInput[],\n\t\tdb: Kysely<Database>,\n\t): Promise<Selectable<FieldTable>[]> {\n\t\tconst stored = await db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.selectAll()\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.execute();\n\t\tif (stored.length !== fields.length) {\n\t\t\tthrow new SchemaError(\"Interrupted seed collection fields do not match\", \"CREATE_FAILED\");\n\t\t}\n\n\t\tlet maxSortOrder = -1;\n\t\tfor (const field of fields) {\n\t\t\tconst sortOrder = field.sortOrder ?? maxSortOrder + 1;\n\t\t\tmaxSortOrder = Math.max(maxSortOrder, sortOrder);\n\t\t\tconst row = stored.find((candidate) => candidate.slug === field.slug);\n\t\t\tif (\n\t\t\t\t!row ||\n\t\t\t\trow.label !== field.label ||\n\t\t\t\trow.type !== field.type ||\n\t\t\t\trow.column_type !== FIELD_TYPE_TO_COLUMN[field.type] ||\n\t\t\t\trow.required !== (field.required ? 1 : 0) ||\n\t\t\t\trow.unique !== (field.unique ? 1 : 0) ||\n\t\t\t\trow.default_value !==\n\t\t\t\t\t(field.defaultValue !== undefined ? JSON.stringify(field.defaultValue) : null) ||\n\t\t\t\trow.validation !== (field.validation ? JSON.stringify(field.validation) : null) ||\n\t\t\t\trow.widget !== (field.widget ?? null) ||\n\t\t\t\trow.options !== (field.options ? JSON.stringify(field.options) : null) ||\n\t\t\t\trow.sort_order !== sortOrder ||\n\t\t\t\trow.searchable !== (field.searchable ? 1 : 0) ||\n\t\t\t\trow.indexed !== (field.indexed ? 1 : 0) ||\n\t\t\t\trow.translatable !== (field.translatable === false ? 0 : 1)\n\t\t\t) {\n\t\t\t\tthrow new SchemaError(\"Interrupted seed collection fields do not match\", \"CREATE_FAILED\");\n\t\t\t}\n\t\t}\n\t\treturn stored;\n\t}\n\n\t/**\n\t * Update a collection\n\t */\n\tasync updateCollection(slug: string, input: UpdateCollectionInput): Promise<Collection> {\n\t\treturn withTransaction(this.db, async (trx) => {\n\t\t\tconst existingRow = await trx\n\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t.selectAll()\n\t\t\t\t.executeTakeFirst();\n\t\t\tif (!existingRow) {\n\t\t\t\tthrow new SchemaError(`Collection \"${slug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t\t}\n\t\t\tconst existing = this.mapCollectionRow(existingRow);\n\t\t\tawait this.validateTitleDateFields(\n\t\t\t\texisting.id,\n\t\t\t\tslug,\n\t\t\t\t{\n\t\t\t\t\ttitleField: input.titleField,\n\t\t\t\t\tdateField: input.dateField,\n\t\t\t\t},\n\t\t\t\ttrx,\n\t\t\t);\n\t\t\tconst updates: Updateable<CollectionTable> = {};\n\n\t\t\tif (input.label !== undefined) updates.label = input.label;\n\t\t\tif (input.labelSingular !== undefined) updates.label_singular = input.labelSingular;\n\t\t\tif (input.description !== undefined) updates.description = input.description;\n\t\t\tif (input.icon !== undefined) updates.icon = input.icon;\n\t\t\tif (input.admin !== undefined) updates.admin_config = JSON.stringify(input.admin);\n\t\t\tif (input.supports !== undefined) updates.supports = JSON.stringify(input.supports);\n\t\t\tif (input.urlPattern !== undefined) updates.url_pattern = input.urlPattern;\n\t\t\tif (input.routable !== undefined) updates.routable = input.routable ? 1 : 0;\n\t\t\tif (input.hasSeo !== undefined) {\n\t\t\t\tupdates.has_seo = input.hasSeo ? 1 : 0;\n\t\t\t} else if (input.supports !== undefined) {\n\t\t\t\tupdates.has_seo = input.supports.includes(\"seo\") ? 1 : 0;\n\t\t\t}\n\t\t\tif (input.hidden !== undefined) updates.hidden = input.hidden ? 1 : 0;\n\t\t\tif (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;\n\t\t\tif (input.titleField !== undefined) updates.title_field = input.titleField || null;\n\t\t\tif (input.dateField !== undefined) updates.date_field = input.dateField || null;\n\t\t\tif (input.commentsEnabled !== undefined) {\n\t\t\t\tupdates.comments_enabled = input.commentsEnabled ? 1 : 0;\n\t\t\t}\n\t\t\tif (input.commentsModeration !== undefined) {\n\t\t\t\tupdates.comments_moderation = input.commentsModeration;\n\t\t\t}\n\t\t\tif (input.commentsClosedAfterDays !== undefined) {\n\t\t\t\tupdates.comments_closed_after_days = input.commentsClosedAfterDays;\n\t\t\t}\n\t\t\tif (input.commentsAutoApproveUsers !== undefined) {\n\t\t\t\tupdates.comments_auto_approve_users = input.commentsAutoApproveUsers ? 1 : 0;\n\t\t\t}\n\n\t\t\tupdates.updated_at = new Date().toISOString();\n\t\t\tawait trx\n\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t.set(updates)\n\t\t\t\t.where(\"id\", \"=\", existing.id)\n\t\t\t\t.execute();\n\n\t\t\tconst row = await trx\n\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t.selectAll()\n\t\t\t\t.executeTakeFirst();\n\n\t\t\tif (!row) {\n\t\t\t\tthrow new SchemaError(\"Failed to update collection\", \"UPDATE_FAILED\");\n\t\t\t}\n\n\t\t\t// Sync FTS state when the supports array changes (e.g. search toggled on/off)\n\t\t\tif (input.supports !== undefined) {\n\t\t\t\tconst hadSearch = existing.supports.includes(\"search\");\n\t\t\t\tconst hasSearch = parseSupports(row.supports).includes(\"search\");\n\t\t\t\tif (hadSearch !== hasSearch) {\n\t\t\t\t\tawait this.syncSearchState(slug, trx);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this.mapCollectionRow(row);\n\t\t});\n\t}\n\n\t/**\n\t * Delete a collection\n\t */\n\tasync deleteCollection(slug: string, options?: { force?: boolean }): Promise<void> {\n\t\tconst existing = await this.getCollection(slug);\n\t\tif (existing && !options?.force && (await this.collectionHasContent(slug))) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Collection \"${slug}\" has content. Use force: true to delete.`,\n\t\t\t\t\"COLLECTION_HAS_CONTENT\",\n\t\t\t);\n\t\t}\n\t\tconst activated = await deleteActivatedMediaUsageCollection(this.db, {\n\t\t\tcollectionId: existing?.id,\n\t\t\tcollectionSlug: slug,\n\t\t\tforceDelete: options?.force === true,\n\t\t});\n\t\tif (activated === \"has_content\") {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Collection \"${slug}\" has content. Use force: true to delete.`,\n\t\t\t\t\"COLLECTION_HAS_CONTENT\",\n\t\t\t);\n\t\t}\n\t\tif (activated === \"in_progress\") {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" deletion is already in progress`, \"CONFLICT\");\n\t\t}\n\t\tif (activated === \"deleted\") return;\n\t\tif (!existing) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\tlet contentTableDropped = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\t// Drop FTS table and triggers before dropping the content table\n\t\t\t\tconst ftsManager = new FTSManager(trx);\n\t\t\t\tawait ftsManager.dropFtsTable(slug);\n\n\t\t\t\t// Drop the content table\n\t\t\t\tconst tableName = this.getTableName(slug);\n\t\t\t\tawait sql`DROP TABLE IF EXISTS ${sql.ref(tableName)}`.execute(trx);\n\t\t\t\tcontentTableDropped = true;\n\n\t\t\t\t// Delete the collection record (fields will cascade)\n\t\t\t\tawait trx.deleteFrom(\"_emdash_collections\").where(\"id\", \"=\", existing.id).execute();\n\t\t\t});\n\t\t\tawait deleteContentMediaUsageCollection(this.db, slug);\n\t\t} catch (error) {\n\t\t\tif (contentTableDropped && !(await tableExists(this.db, this.getTableName(slug)))) {\n\t\t\t\tawait deleteContentMediaUsageCollection(this.db, slug);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ============================================\n\t// Field Operations\n\t// ============================================\n\n\t/**\n\t * List fields for a collection\n\t */\n\tasync listFields(collectionId: string): Promise<Field[]> {\n\t\tconst rows = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collectionId)\n\t\t\t.selectAll()\n\t\t\t.orderBy(\"sort_order\", \"asc\")\n\t\t\t.orderBy(\"created_at\", \"asc\")\n\t\t\t.execute();\n\n\t\treturn rows.map(this.mapFieldRow);\n\t}\n\n\t/**\n\t * Get a field by slug within a collection\n\t */\n\tasync getField(collectionSlug: string, fieldSlug: string): Promise<Field | null> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) return null;\n\n\t\tconst row = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t.selectAll()\n\t\t\t.executeTakeFirst();\n\n\t\treturn row ? this.mapFieldRow(row) : null;\n\t}\n\n\t/**\n\t * Create a new field\n\t */\n\tasync createField(collectionSlug: string, input: CreateFieldInput): Promise<Field> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(`Collection \"${collectionSlug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\t// Validate slug\n\t\tthis.validateSlug(input.slug, \"field\");\n\t\tif (RESERVED_FIELD_SLUGS.includes(input.slug)) {\n\t\t\tthrow new SchemaError(`Field slug \"${input.slug}\" is reserved`, \"RESERVED_SLUG\");\n\t\t}\n\n\t\t// Check if field already exists\n\t\tconst existing = await this.getField(collectionSlug, input.slug);\n\t\tif (existing) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Field \"${input.slug}\" already exists in collection \"${collectionSlug}\"`,\n\t\t\t\t\"FIELD_EXISTS\",\n\t\t\t);\n\t\t}\n\n\t\tconst id = ulid();\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[input.type];\n\t\tassertIndexableField(input.type, input.indexed, input.slug);\n\n\t\t// Get max sort order\n\t\tconst maxSort = await this.db\n\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t.select((eb) => eb.fn.max<number>(\"sort_order\").as(\"max\"))\n\t\t\t.executeTakeFirst();\n\n\t\tconst sortOrder = input.sortOrder ?? (maxSort?.max ?? -1) + 1;\n\t\tconst activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\tthis.db,\n\t\t\tcollectionSlug,\n\t\t);\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tconst created = await withTransaction(this.db, async (trx) => {\n\t\t\t\t// Insert field record\n\t\t\t\tawait trx\n\t\t\t\t\t.insertInto(\"_emdash_fields\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tcollection_id: collection.id,\n\t\t\t\t\t\tslug: input.slug,\n\t\t\t\t\t\tlabel: input.label,\n\t\t\t\t\t\ttype: input.type,\n\t\t\t\t\t\tcolumn_type: columnType,\n\t\t\t\t\t\trequired: input.required ? 1 : 0,\n\t\t\t\t\t\tunique: input.unique ? 1 : 0,\n\t\t\t\t\t\tdefault_value:\n\t\t\t\t\t\t\tinput.defaultValue !== undefined ? JSON.stringify(input.defaultValue) : null,\n\t\t\t\t\t\tvalidation: input.validation ? JSON.stringify(input.validation) : null,\n\t\t\t\t\t\twidget: input.widget ?? null,\n\t\t\t\t\t\toptions: input.options ? JSON.stringify(input.options) : null,\n\t\t\t\t\t\tsort_order: sortOrder,\n\t\t\t\t\t\tsearchable: input.searchable ? 1 : 0,\n\t\t\t\t\t\tindexed: input.indexed ? 1 : 0,\n\t\t\t\t\t\ttranslatable: input.translatable === false ? 0 : 1,\n\t\t\t\t\t})\n\t\t\t\t\t.execute();\n\t\t\t\tschemaMutated = true;\n\n\t\t\t\t// Add column to content table — pass trx to stay on the same connection\n\t\t\t\tawait this.addColumn(\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\tinput.slug,\n\t\t\t\t\tinput.type,\n\t\t\t\t\t{\n\t\t\t\t\t\trequired: input.required,\n\t\t\t\t\t\tdefaultValue: input.defaultValue,\n\t\t\t\t\t},\n\t\t\t\t\ttrx,\n\t\t\t\t);\n\n\t\t\t\tif (input.indexed) {\n\t\t\t\t\tawait this.createFieldIndex(collectionSlug, id, input.slug, trx);\n\t\t\t\t}\n\n\t\t\t\t// Read the created field via trx (not this.db) to avoid connection mutex deadlock\n\t\t\t\tconst fieldRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t\t\t.where(\"slug\", \"=\", input.slug)\n\t\t\t\t\t.selectAll()\n\t\t\t\t\t.executeTakeFirst();\n\n\t\t\t\tif (!fieldRow) {\n\t\t\t\t\tthrow new SchemaError(\"Failed to create field\", \"CREATE_FAILED\");\n\t\t\t\t}\n\n\t\t\t\tconst field = this.mapFieldRow(fieldRow);\n\n\t\t\t\t// Sync search state if this field is searchable; support checks are handled by syncSearchState()\n\t\t\t\tif (input.searchable) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\treturn field;\n\t\t\t});\n\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t} else {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn created;\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Update a field\n\t */\n\tasync updateField(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tinput: UpdateFieldInput,\n\t): Promise<Field> {\n\t\tlet activeCoverageInvalidated = false;\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tconst updatedField = await withTransaction(this.db, async (trx) => {\n\t\t\t\tconst collectionRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t\t\t.select([\"id\", \"title_field\", \"date_field\"])\n\t\t\t\t\t.executeTakeFirst();\n\t\t\t\tconst fieldRow = collectionRow\n\t\t\t\t\t? await trx\n\t\t\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t\t\t.where(\"collection_id\", \"=\", collectionRow.id)\n\t\t\t\t\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t\t\t\t\t.selectAll()\n\t\t\t\t\t\t\t.executeTakeFirst()\n\t\t\t\t\t: undefined;\n\t\t\t\tif (!fieldRow) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Field \"${fieldSlug}\" not found in collection \"${collectionSlug}\"`,\n\t\t\t\t\t\t\"FIELD_NOT_FOUND\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst field = this.mapFieldRow(fieldRow);\n\t\t\t\tconst updates: Updateable<FieldTable> = {};\n\t\t\t\tlet nextType = field.type;\n\n\t\t\t\tif (input.type !== undefined && input.type !== field.type) {\n\t\t\t\t\tconst newColumnType = FIELD_TYPE_TO_COLUMN[input.type];\n\t\t\t\t\tif (newColumnType !== field.columnType) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`Cannot change field \"${fieldSlug}\" in collection \"${collectionSlug}\" from type ` +\n\t\t\t\t\t\t\t\t`\"${field.type}\" to \"${input.type}\": the underlying column type would change from ` +\n\t\t\t\t\t\t\t\t`${field.columnType} to ${newColumnType}, which requires a manual content migration.`,\n\t\t\t\t\t\t\t\"FIELD_TYPE_COLUMN_CHANGE\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (!TEXT_ALIAS_FIELD_TYPES.has(field.type) || !TEXT_ALIAS_FIELD_TYPES.has(input.type)) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`Cannot change field \"${fieldSlug}\" in collection \"${collectionSlug}\" from type ` +\n\t\t\t\t\t\t\t\t`\"${field.type}\" to \"${input.type}\" without a manual content migration.`,\n\t\t\t\t\t\t\t\"FIELD_TYPE_CHANGE_REQUIRES_MIGRATION\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (collectionRow?.title_field === fieldSlug && !TITLE_FIELD_TYPES.has(input.type)) {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`titleField \"${fieldSlug}\" must stay a text field, not \"${input.type}\"`,\n\t\t\t\t\t\t\t\"INVALID_TITLE_FIELD\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (collectionRow?.date_field === fieldSlug && input.type !== \"datetime\") {\n\t\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t\t`dateField \"${fieldSlug}\" must stay a datetime field, not \"${input.type}\"`,\n\t\t\t\t\t\t\t\"INVALID_DATE_FIELD\",\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tnextType = input.type;\n\t\t\t\t\tupdates.type = input.type;\n\t\t\t\t\tupdates.column_type = newColumnType;\n\t\t\t\t}\n\n\t\t\t\tif (input.required !== undefined && input.required !== field.required) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing required for field \"${fieldSlug}\" requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (input.unique !== undefined && input.unique !== field.unique) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing unique for field \"${fieldSlug}\" requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (input.translatable === false && field.translatable) {\n\t\t\t\t\tthrow new SchemaError(\n\t\t\t\t\t\t`Changing field \"${fieldSlug}\" to non-translatable requires a manual content migration.`,\n\t\t\t\t\t\t\"FIELD_UPDATE_REQUIRES_MIGRATION\",\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (input.label !== undefined) updates.label = input.label;\n\t\t\t\tif (input.required !== undefined) updates.required = input.required ? 1 : 0;\n\t\t\t\tif (input.unique !== undefined) updates.unique = input.unique ? 1 : 0;\n\t\t\t\tif (input.searchable !== undefined) updates.searchable = input.searchable ? 1 : 0;\n\t\t\t\tconst indexedChanged = input.indexed !== undefined && input.indexed !== field.indexed;\n\t\t\t\tif (indexedChanged) updates.indexed = input.indexed ? 1 : 0;\n\t\t\t\tif (input.translatable !== undefined) {\n\t\t\t\t\tupdates.translatable = input.translatable ? 1 : 0;\n\t\t\t\t}\n\t\t\t\tif (input.defaultValue !== undefined) {\n\t\t\t\t\tupdates.default_value = JSON.stringify(input.defaultValue);\n\t\t\t\t}\n\t\t\t\tif (input.validation !== undefined) {\n\t\t\t\t\tupdates.validation = input.validation ? JSON.stringify(input.validation) : null;\n\t\t\t\t}\n\t\t\t\tif (input.widget !== undefined) updates.widget = input.widget;\n\t\t\t\tif (input.options !== undefined) updates.options = JSON.stringify(input.options);\n\t\t\t\tif (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;\n\n\t\t\t\tassertIndexableField(nextType, input.indexed ?? field.indexed, fieldSlug);\n\t\t\t\tif (Object.keys(updates).length === 0) return field;\n\n\t\t\t\tactiveCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\t\t\ttrx,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t);\n\n\t\t\t\tif (Object.keys(updates).length > 0) {\n\t\t\t\t\tawait trx.updateTable(\"_emdash_fields\").set(updates).where(\"id\", \"=\", field.id).execute();\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t}\n\n\t\t\t\tif (indexedChanged) {\n\t\t\t\t\tif (input.indexed) {\n\t\t\t\t\t\tawait this.createFieldIndex(collectionSlug, field.id, fieldSlug, trx);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait this.dropFieldIndex(field.id, trx);\n\t\t\t\t\t}\n\t\t\t\t\tschemaMutated = true;\n\t\t\t\t}\n\n\t\t\t\t// Read the updated field via trx (not this.db) to avoid connection mutex deadlock\n\t\t\t\tconst updatedRow = await trx\n\t\t\t\t\t.selectFrom(\"_emdash_fields\")\n\t\t\t\t\t.where(\"collection_id\", \"=\", field.collectionId)\n\t\t\t\t\t.where(\"slug\", \"=\", fieldSlug)\n\t\t\t\t\t.selectAll()\n\t\t\t\t\t.executeTakeFirst();\n\n\t\t\t\tif (!updatedRow) {\n\t\t\t\t\tthrow new SchemaError(\"Failed to update field\", \"UPDATE_FAILED\");\n\t\t\t\t}\n\n\t\t\t\tconst updated = this.mapFieldRow(updatedRow);\n\n\t\t\t\t// If searchable changed, sync FTS state for this collection\n\t\t\t\tconst searchableChanged =\n\t\t\t\t\tschemaMutated && input.searchable !== undefined && input.searchable !== field.searchable;\n\t\t\t\tif (searchableChanged) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\treturn updated;\n\t\t\t});\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn updatedField;\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Synchronize an existing FTS index with the collection's current state.\n\t *\n\t * Only rebuilds or disables — never first-time enables. First-time FTS\n\t * enablement is handled by the seed's explicit enableSearch call (which\n\t * is try-caught) or the admin UI toggle.\n\t *\n\t * - FTS active + still has search support and searchable fields → rebuild\n\t * - FTS active + lost search support or no searchable fields → disable\n\t * - FTS not active → no-op\n\t *\n\t * Pass `db` when calling from within a transaction so FTS operations\n\t * participate in the same transaction and are rolled back on failure.\n\t */\n\tprivate async syncSearchState(collectionSlug: string, db?: Kysely<Database>): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst ftsManager = new FTSManager(conn);\n\n\t\t// Query via conn (not this.db) to avoid connection mutex deadlock when called inside a transaction\n\t\tconst row = await conn\n\t\t\t.selectFrom(\"_emdash_collections\")\n\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t.select(\"supports\")\n\t\t\t.executeTakeFirst();\n\t\tif (!row) return;\n\n\t\tconst wantsSearch = parseSupports(row.supports).includes(\"search\");\n\t\tconst searchableFields = await ftsManager.getSearchableFields(collectionSlug);\n\t\tconst config = await ftsManager.getSearchConfig(collectionSlug);\n\t\tconst ftsActive = config?.enabled === true;\n\n\t\tif (wantsSearch && searchableFields.length > 0 && ftsActive) {\n\t\t\tawait ftsManager.rebuildIndex(\n\t\t\t\tcollectionSlug,\n\t\t\t\tsearchableFields,\n\t\t\t\tconfig?.weights,\n\t\t\t\tconfig?.tokenize,\n\t\t\t);\n\t\t} else if (ftsActive && (!wantsSearch || searchableFields.length === 0)) {\n\t\t\tawait ftsManager.disableSearch(collectionSlug);\n\t\t}\n\t}\n\n\t/**\n\t * Delete a field\n\t */\n\tasync deleteField(collectionSlug: string, fieldSlug: string): Promise<void> {\n\t\tconst field = await this.getField(collectionSlug, fieldSlug);\n\t\tif (!field) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Field \"${fieldSlug}\" not found in collection \"${collectionSlug}\"`,\n\t\t\t\t\"FIELD_NOT_FOUND\",\n\t\t\t);\n\t\t}\n\t\tconst activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(\n\t\t\tthis.db,\n\t\t\tcollectionSlug,\n\t\t);\n\n\t\t// If this field powers the collection's titleField/dateField,\n\t\t// clear that reference in the same transaction — otherwise the metadata\n\t\t// would point at a dropped column and later crash the content list sort.\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tconst clearTitle = collection?.titleField === fieldSlug;\n\t\tconst clearDate = collection?.dateField === fieldSlug;\n\n\t\tlet schemaMutated = false;\n\t\ttry {\n\t\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\t\t// Delete the field record first so syncSearchState sees the updated field list.\n\t\t\t\t// This ordering matters for searchable fields: SQLite prevents dropping a column\n\t\t\t\t// that is still referenced by a trigger. syncSearchState drops and recreates the\n\t\t\t\t// FTS triggers based on the remaining searchable fields, clearing the dependency\n\t\t\t\t// before we attempt the ALTER TABLE DROP COLUMN below.\n\t\t\t\tawait trx.deleteFrom(\"_emdash_fields\").where(\"id\", \"=\", field.id).execute();\n\t\t\t\tschemaMutated = true;\n\n\t\t\t\tif (clearTitle || clearDate) {\n\t\t\t\t\tawait trx\n\t\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t...(clearTitle ? { title_field: null } : {}),\n\t\t\t\t\t\t\t...(clearDate ? { date_field: null } : {}),\n\t\t\t\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.where(\"slug\", \"=\", collectionSlug)\n\t\t\t\t\t\t.execute();\n\t\t\t\t}\n\n\t\t\t\t// If the deleted field was searchable, sync FTS state (removes old triggers)\n\t\t\t\tif (field.searchable) {\n\t\t\t\t\tawait this.syncSearchState(collectionSlug, trx);\n\t\t\t\t}\n\n\t\t\t\tif (field.indexed) {\n\t\t\t\t\tawait this.dropFieldIndex(field.id, trx);\n\t\t\t\t}\n\n\t\t\t\t// Drop column from content table — safe now because FTS triggers are gone\n\t\t\t\tawait this.dropColumn(collectionSlug, fieldSlug, trx);\n\t\t\t});\n\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t} else {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\tthis.db,\n\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (schemaMutated) {\n\t\t\t\tif (activeCoverageInvalidated) {\n\t\t\t\t\tawait invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);\n\t\t\t\t} else {\n\t\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(\n\t\t\t\t\t\tthis.db,\n\t\t\t\t\t\tcollectionSlug,\n\t\t\t\t\t\t\"CONTENT_USAGE_STALE\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Reorder collections in the admin sidebar.\n\t *\n\t * `slugs` is the full desired order: every listed collection gets its\n\t * index as `sort_order`, and any collection left out has its explicit\n\t * position cleared, dropping it back to the alphabetical tail. Unknown or\n\t * duplicate slugs throw before anything is written.\n\t */\n\tasync reorderCollections(slugs: string[]): Promise<void> {\n\t\tconst known = new Set((await this.listCollections()).map((collection) => collection.slug));\n\n\t\tconst unknown = slugs.filter((slug) => !known.has(slug));\n\t\tif (unknown.length > 0) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Unknown collection${unknown.length > 1 ? \"s\" : \"\"}: ${unknown.join(\", \")}`,\n\t\t\t\t\"COLLECTION_NOT_FOUND\",\n\t\t\t\t{ slugs: unknown },\n\t\t\t);\n\t\t}\n\n\t\tconst duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);\n\t\tif (duplicates.length > 0) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`Duplicate collection${duplicates.length > 1 ? \"s\" : \"\"}: ${[...new Set(duplicates)].join(\", \")}`,\n\t\t\t\t\"DUPLICATE_SLUG\",\n\t\t\t\t{ slugs: [...new Set(duplicates)] },\n\t\t\t);\n\t\t}\n\n\t\tconst now = new Date().toISOString();\n\t\tconst ordered = new Set(slugs);\n\n\t\tawait withTransaction(this.db, async (trx) => {\n\t\t\tfor (const [index, slug] of slugs.entries()) {\n\t\t\t\tawait trx\n\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t.set({ sort_order: index, updated_at: now })\n\t\t\t\t\t.where(\"slug\", \"=\", slug)\n\t\t\t\t\t.execute();\n\t\t\t}\n\n\t\t\tconst cleared = [...known].filter((slug) => !ordered.has(slug));\n\t\t\t// Chunked to stay under D1's bound-parameter limit; typical sites\n\t\t\t// clear far fewer than one chunk.\n\t\t\tfor (const slugChunk of chunks(cleared, SQL_BATCH_SIZE)) {\n\t\t\t\tawait trx\n\t\t\t\t\t.updateTable(\"_emdash_collections\")\n\t\t\t\t\t.set({ sort_order: null, updated_at: now })\n\t\t\t\t\t.where(\"slug\", \"in\", slugChunk)\n\t\t\t\t\t.execute();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Reorder fields\n\t */\n\tasync reorderFields(collectionSlug: string, fieldSlugs: string[]): Promise<void> {\n\t\tconst collection = await this.getCollection(collectionSlug);\n\t\tif (!collection) {\n\t\t\tthrow new SchemaError(`Collection \"${collectionSlug}\" not found`, \"COLLECTION_NOT_FOUND\");\n\t\t}\n\n\t\t// Update sort_order for each field\n\t\tfor (let i = 0; i < fieldSlugs.length; i++) {\n\t\t\tawait this.db\n\t\t\t\t.updateTable(\"_emdash_fields\")\n\t\t\t\t.set({ sort_order: i })\n\t\t\t\t.where(\"collection_id\", \"=\", collection.id)\n\t\t\t\t.where(\"slug\", \"=\", fieldSlugs[i])\n\t\t\t\t.execute();\n\t\t}\n\t}\n\n\t// ============================================\n\t// DDL Operations\n\t// ============================================\n\n\t/**\n\t * Create a content table for a collection\n\t */\n\tprivate async createContentTable(\n\t\tslug: string,\n\t\tdb?: Kysely<Database>,\n\t\tfields: readonly CreateFieldInput[] = [],\n\t\toptions: { ifNotExists?: boolean } = {},\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(slug);\n\n\t\tlet table: CreateTableBuilder<string, string> = conn.schema\n\t\t\t.createTable(tableName)\n\t\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t\t.addColumn(\"slug\", \"text\")\n\t\t\t.addColumn(\"status\", \"text\", (col) => col.defaultTo(\"draft\"))\n\t\t\t.addColumn(\"author_id\", \"text\")\n\t\t\t.addColumn(\"primary_byline_id\", \"text\")\n\t\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(conn)))\n\t\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(conn)))\n\t\t\t.addColumn(\"published_at\", \"text\")\n\t\t\t.addColumn(\"scheduled_at\", \"text\")\n\t\t\t.addColumn(\"deleted_at\", \"text\")\n\t\t\t.addColumn(\"version\", \"integer\", (col) => col.defaultTo(1))\n\t\t\t.addColumn(\"live_revision_id\", \"text\", (col) => col.references(\"revisions.id\"))\n\t\t\t.addColumn(\"draft_revision_id\", \"text\", (col) => col.references(\"revisions.id\"))\n\t\t\t.addColumn(\"locale\", \"text\", (col) => col.notNull().defaultTo(\"en\"))\n\t\t\t.addColumn(\"translation_group\", \"text\");\n\t\tif (options.ifNotExists) table = table.ifNotExists();\n\n\t\tfor (const field of fields) {\n\t\t\tconst columnName = this.getColumnName(field.slug);\n\t\t\tconst columnType = COLUMN_TYPE_TO_DATA_TYPE[FIELD_TYPE_TO_COLUMN[field.type]];\n\t\t\ttable = table.addColumn(columnName, columnType, (column) => {\n\t\t\t\tif (!field.required) return column;\n\n\t\t\t\tconst defaultValue =\n\t\t\t\t\tfield.defaultValue !== undefined\n\t\t\t\t\t\t? this.formatDefaultValue(field.defaultValue, field.type)\n\t\t\t\t\t\t: this.getEmptyDefault(field.type);\n\t\t\t\treturn column.notNull().defaultTo(sql.raw(defaultValue));\n\t\t\t});\n\t\t}\n\n\t\tawait table\n\t\t\t.addUniqueConstraint(`${tableName}_slug_locale_unique`, [\"slug\", \"locale\"])\n\t\t\t.execute();\n\n\t\tconst createIndex = options.ifNotExists ? sql`CREATE INDEX IF NOT EXISTS` : sql`CREATE INDEX`;\n\n\t\t// Create standard indexes\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_slug`)}\n\t\t\tON ${sql.ref(tableName)} (slug)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_scheduled`)}\n\t\t\tON ${sql.ref(tableName)} (scheduled_at)\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_live_revision`)}\n\t\t\tON ${sql.ref(tableName)} (live_revision_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_draft_revision`)}\n\t\t\tON ${sql.ref(tableName)} (draft_revision_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_author`)}\n\t\t\tON ${sql.ref(tableName)} (author_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_primary_byline`)}\n\t\t\tON ${sql.ref(tableName)} (primary_byline_id)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_locale`)}\n\t\t\tON ${sql.ref(tableName)} (locale)\n\t\t`.execute(conn);\n\n\t\t// Names must stay identical to migration 055, which creates these indexes\n\t\t// on tables that already exist. Lookups that don't constrain `deleted_at`\n\t\t// (menu and reference resolution) need the first; reads that do need the\n\t\t// second.\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (translation_group, locale)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_del_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, translation_group, locale)\n\t\t`.execute(conn);\n\n\t\t// Composite indexes for optimized query performance (see migration 033)\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_updated_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, updated_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_status`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, status)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_created_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, created_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_deleted_published_id`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, published_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\t// Locale-aware composite indexes for i18n content lists (see migration 041).\n\t\t// Short `loc_upd`/`loc_crt` suffix keeps the updated/created discriminator\n\t\t// inside Postgres's 63-byte identifier limit for long slugs; keep these\n\t\t// names identical to migration 041.\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_loc_upd`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, updated_at DESC, id DESC)\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\t${createIndex} ${sql.ref(`idx_${tableName}_loc_crt`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, created_at DESC, id DESC)\n\t\t`.execute(conn);\n\t}\n\n\tprivate getFieldIndexName(fieldId: string): string {\n\t\tif (!FIELD_ID_PATTERN.test(fieldId)) {\n\t\t\tthrow new SchemaError(`Invalid field id \"${fieldId}\"`, \"INVALID_FIELD_ID\");\n\t\t}\n\t\treturn `idx_cf_${fieldId.toLowerCase()}`;\n\t}\n\n\tprivate getLocaleFieldIndexName(fieldId: string): string {\n\t\treturn `${this.getFieldIndexName(fieldId)}_loc`;\n\t}\n\n\tprivate async createFieldIndex(\n\t\tcollectionSlug: string,\n\t\tfieldId: string,\n\t\tfieldSlug: string,\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\t\tconst indexName = this.getFieldIndexName(fieldId);\n\t\tconst localeIndexName = this.getLocaleFieldIndexName(fieldId);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(indexName)}\n\t\t\tON ${sql.ref(tableName)} (\n\t\t\t\t(${sql.ref(columnName)} IS NOT NULL),\n\t\t\t\t${sql.ref(columnName)},\n\t\t\t\tid\n\t\t\t)\n\t\t\tWHERE deleted_at IS NULL\n\t\t`.execute(conn);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(localeIndexName)}\n\t\t\tON ${sql.ref(tableName)} (\n\t\t\t\tlocale,\n\t\t\t\t(${sql.ref(columnName)} IS NOT NULL),\n\t\t\t\t${sql.ref(columnName)},\n\t\t\t\tid\n\t\t\t)\n\t\t\tWHERE deleted_at IS NULL\n\t\t`.execute(conn);\n\t}\n\n\tprivate async dropFieldIndex(fieldId: string, db?: Kysely<Database>): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(this.getFieldIndexName(fieldId))}`.execute(conn);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(this.getLocaleFieldIndexName(fieldId))}`.execute(conn);\n\t}\n\n\t/**\n\t * Add a column to a content table\n\t */\n\tprivate async addColumn(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tfieldType: FieldType,\n\t\toptions?: { required?: boolean; defaultValue?: unknown },\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst conn = db ?? this.db;\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\n\t\t// Build ALTER TABLE statement\n\t\t// Note: SQLite requires DEFAULT for NOT NULL columns in ALTER TABLE\n\t\tif (options?.required && options?.defaultValue !== undefined) {\n\t\t\tconst defaultVal = this.formatDefaultValue(options.defaultValue, fieldType);\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}\n\t\t\t`.execute(conn);\n\t\t} else if (options?.required) {\n\t\t\t// For required fields without default, use empty string/0 as default\n\t\t\tconst defaultVal = this.getEmptyDefault(fieldType);\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)} NOT NULL DEFAULT ${sql.raw(defaultVal)}\n\t\t\t`.execute(conn);\n\t\t} else {\n\t\t\tawait sql`\n\t\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\t\tADD COLUMN ${sql.ref(columnName)} ${sql.raw(columnType)}\n\t\t\t`.execute(conn);\n\t\t}\n\t}\n\n\t/**\n\t * Drop a column from a content table\n\t */\n\tprivate async dropColumn(\n\t\tcollectionSlug: string,\n\t\tfieldSlug: string,\n\t\tdb?: Kysely<Database>,\n\t): Promise<void> {\n\t\tconst tableName = this.getTableName(collectionSlug);\n\t\tconst columnName = this.getColumnName(fieldSlug);\n\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tDROP COLUMN ${sql.ref(columnName)}\n\t\t`.execute(db ?? this.db);\n\t}\n\n\t// ============================================\n\t// Helpers\n\t// ============================================\n\n\t/**\n\t * Check if a collection has any content\n\t */\n\tprivate async collectionHasContent(slug: string): Promise<boolean> {\n\t\tconst tableName = this.getTableName(slug);\n\t\ttry {\n\t\t\tconst result = await sql<{ count: number }>`\n\t\t\t\tSELECT COUNT(*) as count FROM ${sql.ref(tableName)}\n\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t`.execute(this.db);\n\t\t\treturn (result.rows[0]?.count ?? 0) > 0;\n\t\t} catch {\n\t\t\t// Table might not exist\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get table name for a collection\n\t */\n\tprivate getTableName(slug: string): string {\n\t\tvalidateIdentifier(slug, \"collection slug\");\n\t\treturn `ec_${slug}`;\n\t}\n\n\t/**\n\t * Get column name for a field\n\t */\n\tprivate getColumnName(slug: string): string {\n\t\tvalidateIdentifier(slug, \"field slug\");\n\t\treturn slug;\n\t}\n\n\t/**\n\t * Validate a slug\n\t */\n\tprivate validateSlug(slug: string, type: \"collection\" | \"field\"): void {\n\t\tif (!slug || typeof slug !== \"string\") {\n\t\t\tthrow new SchemaError(`${type} slug is required`, \"INVALID_SLUG\");\n\t\t}\n\n\t\tif (!SLUG_VALIDATION_PATTERN.test(slug)) {\n\t\t\tthrow new SchemaError(\n\t\t\t\t`${type} slug must start with a letter and contain only lowercase letters, numbers, and underscores`,\n\t\t\t\t\"INVALID_SLUG\",\n\t\t\t);\n\t\t}\n\n\t\tif (slug.length > 63) {\n\t\t\tthrow new SchemaError(`${type} slug must be 63 characters or less`, \"INVALID_SLUG\");\n\t\t}\n\t}\n\n\t/**\n\t * Format a default value for SQL.\n\t *\n\t * SQLite `ALTER TABLE ADD COLUMN ... DEFAULT` requires a literal constant\n\t * expression — parameterized values cannot be used here. We manually escape\n\t * single quotes and coerce types to ensure the output is safe.\n\t *\n\t * INTEGER/REAL values are coerced through `Number()` which can only produce\n\t * digits, `.`, `-`, `e`, `Infinity`, or `NaN` — all safe in SQL.\n\t * TEXT/JSON values have single quotes escaped via SQL standard doubling (`''`).\n\t */\n\tprivate formatDefaultValue(value: unknown, fieldType: FieldType): string {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn \"NULL\";\n\t\t}\n\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\n\t\tif (columnType === \"JSON\") {\n\t\t\t// JSON.stringify produces valid JSON; escape single quotes for SQL literal\n\t\t\tconst json = JSON.stringify(value);\n\t\t\treturn `'${json.replace(SINGLE_QUOTE_PATTERN, \"''\")}'`;\n\t\t}\n\n\t\tif (columnType === \"INTEGER\") {\n\t\t\tif (typeof value === \"boolean\") {\n\t\t\t\treturn value ? \"1\" : \"0\";\n\t\t\t}\n\t\t\tconst num = Number(value);\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn \"0\";\n\t\t\t}\n\t\t\treturn String(Math.trunc(num));\n\t\t}\n\n\t\tif (columnType === \"REAL\") {\n\t\t\tconst num = Number(value);\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn \"0\";\n\t\t\t}\n\t\t\treturn String(num);\n\t\t}\n\n\t\t// TEXT — escape single quotes via SQL standard doubling\n\t\tlet text: string;\n\t\tif (typeof value === \"string\") {\n\t\t\ttext = value;\n\t\t} else if (typeof value === \"number\" || typeof value === \"boolean\") {\n\t\t\ttext = String(value);\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\ttext = JSON.stringify(value);\n\t\t} else {\n\t\t\ttext = \"\";\n\t\t}\n\t\treturn `'${text.replace(SINGLE_QUOTE_PATTERN, \"''\")}'`;\n\t}\n\n\t/**\n\t * Get empty default for a field type\n\t */\n\tprivate getEmptyDefault(fieldType: FieldType): string {\n\t\tconst columnType = FIELD_TYPE_TO_COLUMN[fieldType];\n\n\t\tswitch (columnType) {\n\t\t\tcase \"INTEGER\":\n\t\t\t\treturn \"0\";\n\t\t\tcase \"REAL\":\n\t\t\t\treturn \"0.0\";\n\t\t\tcase \"JSON\":\n\t\t\t\treturn \"'null'\";\n\t\t\tdefault:\n\t\t\t\treturn \"''\";\n\t\t}\n\t}\n\n\t/**\n\t * Map a collection row to a Collection object\n\t */\n\tprivate mapCollectionRow = (row: Selectable<CollectionTable>): Collection => {\n\t\tconst moderation = row.comments_moderation;\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tslug: row.slug,\n\t\t\tlabel: row.label,\n\t\t\tlabelSingular: row.label_singular ?? undefined,\n\t\t\tdescription: row.description ?? undefined,\n\t\t\ticon: row.icon ?? undefined,\n\t\t\tadmin: parseCollectionAdmin(row.admin_config),\n\t\t\tsupports: parseSupports(row.supports),\n\t\t\tsource: row.source && isCollectionSource(row.source) ? row.source : undefined,\n\t\t\thasSeo: row.has_seo === 1,\n\t\t\t// Raw value; undefined when unset. The admin list resolves the\n\t\t\t// default (title fallback chain / updatedAt) at the point of use.\n\t\t\ttitleField: row.title_field ?? undefined,\n\t\t\tdateField: row.date_field ?? undefined,\n\t\t\turlPattern: row.url_pattern ?? undefined,\n\t\t\troutable: row.routable !== 0,\n\t\t\thidden: row.hidden === 1,\n\t\t\tsortOrder: row.sort_order ?? undefined,\n\t\t\tcommentsEnabled: row.comments_enabled === 1,\n\t\t\tcommentsModeration:\n\t\t\t\tmoderation === \"all\" || moderation === \"first_time\" || moderation === \"none\"\n\t\t\t\t\t? moderation\n\t\t\t\t\t: \"first_time\",\n\t\t\tcommentsClosedAfterDays: row.comments_closed_after_days ?? 90,\n\t\t\tcommentsAutoApproveUsers: row.comments_auto_approve_users === 1,\n\t\t\tcreatedAt: row.created_at,\n\t\t\tupdatedAt: row.updated_at,\n\t\t};\n\t};\n\n\t/**\n\t * Map a field row to a Field object\n\t */\n\tprivate mapFieldRow = (row: Selectable<FieldTable>): Field => {\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\tcollectionId: row.collection_id,\n\t\t\tslug: row.slug,\n\t\t\tlabel: row.label,\n\t\t\ttype: isFieldType(row.type) ? row.type : \"string\",\n\t\t\tcolumnType: isColumnType(row.column_type) ? row.column_type : \"TEXT\",\n\t\t\trequired: row.required === 1,\n\t\t\tunique: row.unique === 1,\n\t\t\tdefaultValue: row.default_value ? JSON.parse(row.default_value) : undefined,\n\t\t\tvalidation: row.validation ? JSON.parse(row.validation) : undefined,\n\t\t\twidget: row.widget ?? undefined,\n\t\t\toptions: row.options ? JSON.parse(row.options) : undefined,\n\t\t\tsortOrder: row.sort_order,\n\t\t\tsearchable: row.searchable === 1,\n\t\t\tindexed: row.indexed === 1,\n\t\t\ttranslatable: row.translatable !== 0,\n\t\t\tcreatedAt: row.created_at,\n\t\t};\n\t};\n\n\t// ============================================\n\t// Discovery\n\t// ============================================\n\n\t/**\n\t * Discover orphaned content tables\n\t *\n\t * Finds ec_* tables that exist in the database but don't have a\n\t * corresponding entry in _emdash_collections.\n\t */\n\tasync discoverOrphanedTables(): Promise<\n\t\tArray<{ slug: string; tableName: string; rowCount: number }>\n\t> {\n\t\t// Get all ec_* tables\n\t\t// Content tables are ec_* (e.g., ec_posts, ec_pages)\n\t\t// Internal tables are _emdash_* (e.g., _emdash_collections, _emdash_fts_posts)\n\t\tconst allTables = await listTablesLike(this.db, \"ec_%\");\n\n\t\t// Get registered collections\n\t\tconst registered = await this.listCollections();\n\t\tconst registeredSlugs = new Set(registered.map((c) => c.slug));\n\n\t\t// Find orphans\n\t\tconst orphans: Array<{\n\t\t\tslug: string;\n\t\t\ttableName: string;\n\t\t\trowCount: number;\n\t\t}> = [];\n\n\t\tfor (const tableName of allTables) {\n\t\t\tconst slug = tableName.replace(EC_PREFIX_PATTERN, \"\");\n\n\t\t\tif (!registeredSlugs.has(slug)) {\n\t\t\t\t// Count rows in the orphaned table\n\t\t\t\ttry {\n\t\t\t\t\tconst countResult = await sql<{ count: number }>`\n\t\t\t\t\t\tSELECT COUNT(*) as count FROM ${sql.ref(tableName)}\n\t\t\t\t\t\tWHERE deleted_at IS NULL\n\t\t\t\t\t`.execute(this.db);\n\n\t\t\t\t\torphans.push({\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\trowCount: countResult.rows[0]?.count ?? 0,\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// Table might have unexpected schema, still report it\n\t\t\t\t\torphans.push({\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\ttableName,\n\t\t\t\t\t\trowCount: 0,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn orphans;\n\t}\n\n\t/**\n\t * Register an orphaned table as a collection\n\t *\n\t * Creates a _emdash_collections entry for an existing ec_* table.\n\t */\n\tasync registerOrphanedTable(\n\t\tslug: string,\n\t\toptions?: {\n\t\t\tlabel?: string;\n\t\t\tlabelSingular?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<Collection> {\n\t\t// Verify table exists\n\t\tconst tableName = this.getTableName(slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tconst exists = await tableExists(this.db, tableName);\n\n\t\tif (!exists) {\n\t\t\tthrow new SchemaError(`Table \"${tableName}\" does not exist`, \"TABLE_NOT_FOUND\");\n\t\t}\n\n\t\t// Check if already registered\n\t\tconst existing = await this.getCollection(slug);\n\t\tif (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\t\tif (\n\t\t\texisting &&\n\t\t\t!(await canResumeMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: existing.id,\n\t\t\t\tcollectionSlug: existing.slug,\n\t\t\t}))\n\t\t) {\n\t\t\tthrow new SchemaError(`Collection \"${slug}\" is already registered`, \"COLLECTION_EXISTS\");\n\t\t}\n\n\t\t// Create collection entry\n\t\tconst proposedId = existing?.id ?? ulid();\n\t\tconst label = options?.label || this.slugToLabel(slug);\n\n\t\tlet collectionRegistered = false;\n\t\ttry {\n\t\t\tconst capture = await prepareMediaUsageCollectionCapture(this.db, {\n\t\t\t\tcollectionId: proposedId,\n\t\t\t\tcollectionSlug: slug,\n\t\t\t\tregisteredCollectionId: existing?.id,\n\t\t\t});\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait installPreparedMediaUsageCollectionCapture(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t\tawait markMediaUsageCollectionCaptureReady(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (!capture.registrationExists) {\n\t\t\t\tawait this.db\n\t\t\t\t\t.insertInto(\"_emdash_collections\")\n\t\t\t\t\t.values({\n\t\t\t\t\t\tid: capture.collectionId,\n\t\t\t\t\t\tslug,\n\t\t\t\t\t\tlabel,\n\t\t\t\t\t\tlabel_singular: options?.labelSingular ?? null,\n\t\t\t\t\t\tdescription: options?.description ?? null,\n\t\t\t\t\t\ticon: null,\n\t\t\t\t\t\tsupports: JSON.stringify([]),\n\t\t\t\t\t\tsource: \"discovered\",\n\t\t\t\t\t\thas_seo: 0,\n\t\t\t\t\t\turl_pattern: null,\n\t\t\t\t\t})\n\t\t\t\t\t.execute();\n\t\t\t\tcollectionRegistered = true;\n\t\t\t}\n\t\t\tif (capture.captureRequired) {\n\t\t\t\tawait finalizeMediaUsageCollectionCapture(this.db, {\n\t\t\t\t\tcollectionId: capture.collectionId,\n\t\t\t\t\tcollectionSlug: slug,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst collection = await this.getCollection(slug);\n\t\t\tif (!collection) {\n\t\t\t\tthrow new SchemaError(\"Failed to register orphaned table\", \"REGISTER_FAILED\");\n\t\t\t}\n\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, slug, \"CONTENT_USAGE_STALE\");\n\n\t\t\treturn collection;\n\t\t} catch (error) {\n\t\t\tif (collectionRegistered) {\n\t\t\t\tawait markContentMediaUsageCollectionStaleSafely(this.db, slug, \"CONTENT_USAGE_STALE\");\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Convert slug to human-readable label\n\t */\n\tprivate slugToLabel(slug: string): string {\n\t\treturn slug\n\t\t\t.replace(UNDERSCORE_PATTERN, \" \")\n\t\t\t.replace(WORD_BOUNDARY_PATTERN, (c) => c.toUpperCase());\n\t}\n}\n"],"mappings":"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"}
|