emdash 0.32.0 → 0.34.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-C0ONzkUP.d.mts → adapters-34EN2eXO.d.mts} +34 -2
- package/dist/adapters-34EN2eXO.d.mts.map +1 -0
- package/dist/after-CBU2vkoO.mjs +96 -0
- package/dist/after-CBU2vkoO.mjs.map +1 -0
- package/dist/{allowed-origins-CCEi9bPI.mjs → allowed-origins-DW7sYyqg.mjs} +2 -2
- package/dist/{allowed-origins-CCEi9bPI.mjs.map → allowed-origins-DW7sYyqg.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -4
- package/dist/api/route-utils.d.mts.map +1 -1
- package/dist/api/route-utils.mjs +26 -25
- package/dist/api/route-utils.mjs.map +1 -1
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +6 -5
- package/dist/{api-DgPbMlng.mjs → api-DdAsirBe.mjs} +321 -428
- package/dist/api-DdAsirBe.mjs.map +1 -0
- package/dist/{api-tokens-p-iMzvXR.mjs → api-tokens-CFXu8F_2.mjs} +2 -2
- package/dist/{api-tokens-p-iMzvXR.mjs.map → api-tokens-CFXu8F_2.mjs.map} +1 -1
- package/dist/{apply-1_6ra7NP.mjs → apply-D5zfeD09.mjs} +139 -102
- package/dist/apply-D5zfeD09.mjs.map +1 -0
- package/dist/astro/index.d.mts +12 -12
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +103 -35
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +10 -10
- package/dist/astro/middleware/auth.mjs +9 -9
- package/dist/astro/middleware/media-usage-write-fence.d.mts +7 -0
- package/dist/astro/middleware/media-usage-write-fence.d.mts.map +1 -0
- package/dist/astro/middleware/media-usage-write-fence.mjs +35 -0
- package/dist/astro/middleware/media-usage-write-fence.mjs.map +1 -0
- package/dist/astro/middleware/redirect.d.mts.map +1 -1
- package/dist/astro/middleware/redirect.mjs +13 -10
- package/dist/astro/middleware/redirect.mjs.map +1 -1
- package/dist/astro/middleware/request-context.mjs +5 -5
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +12 -11
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +1915 -223
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +9 -8
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +9 -8
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +6 -6
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +12 -11
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +12 -11
- package/dist/astro/routes/api/admin/byline-fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +12 -11
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +20 -20
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +20 -20
- package/dist/astro/routes/api/admin/bylines/index.mjs +20 -20
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +18 -17
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_.mjs +8 -8
- package/dist/astro/routes/api/admin/comments/bulk.mjs +14 -13
- package/dist/astro/routes/api/admin/comments/bulk.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/counts.mjs +8 -8
- package/dist/astro/routes/api/admin/comments/index.mjs +14 -13
- package/dist/astro/routes/api/admin/comments/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +6 -6
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +5 -5
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.d.mts +8 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs +36 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/index.mjs.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.d.mts +8 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs +36 -0
- package/dist/astro/routes/api/admin/media-usage/collection-deletions/retry.mjs.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +15 -14
- package/dist/astro/routes/api/admin/media-usage/repair.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/work/index.d.mts +8 -0
- package/dist/astro/routes/api/admin/media-usage/work/index.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs +37 -0
- package/dist/astro/routes/api/admin/media-usage/work/index.mjs.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/work/retry.d.mts +8 -0
- package/dist/astro/routes/api/admin/media-usage/work/retry.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs +37 -0
- package/dist/astro/routes/api/admin/media-usage/work/retry.mjs.map +1 -0
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +5 -5
- package/dist/astro/routes/api/admin/plugins/_id_/disable.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -47
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -47
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +6 -6
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +55 -46
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +55 -46
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +55 -46
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +55 -46
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +56 -47
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +56 -47
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +52 -46
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +52 -46
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +4 -4
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +52 -46
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +4 -4
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +10 -9
- package/dist/astro/routes/api/admin/users/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +6 -6
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/index.mjs +9 -8
- package/dist/astro/routes/api/admin/users/index.mjs.map +1 -1
- package/dist/astro/routes/api/auth/dev-bypass.mjs +8 -8
- package/dist/astro/routes/api/auth/invite/accept.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/complete.mjs +14 -13
- package/dist/astro/routes/api/auth/invite/complete.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/index.mjs +12 -11
- package/dist/astro/routes/api/auth/invite/index.mjs.map +1 -1
- package/dist/astro/routes/api/auth/invite/register-options.mjs +13 -12
- package/dist/astro/routes/api/auth/invite/register-options.mjs.map +1 -1
- package/dist/astro/routes/api/auth/logout.mjs +4 -4
- package/dist/astro/routes/api/auth/magic-link/send.mjs +14 -13
- package/dist/astro/routes/api/auth/magic-link/send.mjs.map +1 -1
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +4 -4
- package/dist/astro/routes/api/auth/me.mjs +10 -9
- package/dist/astro/routes/api/auth/me.mjs.map +1 -1
- package/dist/astro/routes/api/auth/mode.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +9 -8
- package/dist/astro/routes/api/auth/passkey/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/index.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/options.mjs +15 -14
- package/dist/astro/routes/api/auth/passkey/options.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +13 -12
- package/dist/astro/routes/api/auth/passkey/register/options.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +14 -13
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs.map +1 -1
- package/dist/astro/routes/api/auth/passkey/verify.mjs +14 -13
- package/dist/astro/routes/api/auth/passkey/verify.mjs.map +1 -1
- package/dist/astro/routes/api/auth/signup/complete.mjs +14 -13
- package/dist/astro/routes/api/auth/signup/complete.mjs.map +1 -1
- package/dist/astro/routes/api/auth/signup/request.mjs +14 -13
- package/dist/astro/routes/api/auth/signup/request.mjs.map +1 -1
- package/dist/astro/routes/api/auth/signup/verify.mjs +3 -3
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +20 -19
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs.map +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +17 -16
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +15 -15
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +21 -20
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +21 -20
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +47 -34
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +5 -5
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/authors.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/index.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/index.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/trash.mjs +10 -9
- package/dist/astro/routes/api/content/_collection_/trash.mjs.map +1 -1
- package/dist/astro/routes/api/dashboard.mjs +12 -10
- package/dist/astro/routes/api/dashboard.mjs.map +1 -1
- package/dist/astro/routes/api/dev/emails.mjs +4 -4
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +22 -20
- package/dist/astro/routes/api/import/probe.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/execute.mjs +18 -18
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.d.mts +10 -10
- package/dist/astro/routes/api/import/wordpress/media.mjs +14 -12
- package/dist/astro/routes/api/import/wordpress/media.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +13 -12
- package/dist/astro/routes/api/import/wordpress/prepare.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +15 -15
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +22 -20
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/callback.mjs +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +32 -31
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +6 -6
- package/dist/astro/routes/api/mcp.d.mts.map +1 -1
- package/dist/astro/routes/api/mcp.mjs +174 -72
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +14 -12
- package/dist/astro/routes/api/media/_id_/confirm.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/upload.mjs +7 -7
- package/dist/astro/routes/api/media/_id_/usage.mjs +17 -16
- package/dist/astro/routes/api/media/_id_/usage.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_.mjs +16 -15
- package/dist/astro/routes/api/media/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/media/file/_...key_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +4 -4
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +4 -4
- package/dist/astro/routes/api/media/providers/index.mjs +4 -4
- package/dist/astro/routes/api/media/upload-url.mjs +11 -10
- package/dist/astro/routes/api/media/upload-url.mjs.map +1 -1
- package/dist/astro/routes/api/media.mjs +19 -16
- package/dist/astro/routes/api/media.mjs.map +1 -1
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +13 -13
- package/dist/astro/routes/api/menus/_name_/items.mjs +13 -13
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +13 -13
- package/dist/astro/routes/api/menus/_name_/translations.mjs +13 -13
- package/dist/astro/routes/api/menus/_name_.mjs +13 -13
- package/dist/astro/routes/api/menus/index.mjs +13 -13
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +7 -7
- package/dist/astro/routes/api/oauth/device/code.mjs +10 -10
- package/dist/astro/routes/api/oauth/device/token.mjs +9 -9
- package/dist/astro/routes/api/oauth/register.mjs +4 -4
- package/dist/astro/routes/api/oauth/token/refresh.mjs +7 -7
- package/dist/astro/routes/api/oauth/token/revoke.mjs +7 -7
- package/dist/astro/routes/api/oauth/token.mjs +7 -7
- package/dist/astro/routes/api/openapi.json.mjs +183 -6
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.d.mts.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +8 -7
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/404s/index.mjs +14 -13
- package/dist/astro/routes/api/redirects/404s/index.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/404s/summary.mjs +14 -13
- package/dist/astro/routes/api/redirects/404s/summary.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/_id_.mjs +16 -14
- package/dist/astro/routes/api/redirects/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/redirects/index.mjs +16 -14
- package/dist/astro/routes/api/redirects/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/index.mjs +21 -20
- package/dist/astro/routes/api/relations/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/relations/_id_/translations.mjs +16 -15
- package/dist/astro/routes/api/relations/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/relations/index.mjs +21 -20
- package/dist/astro/routes/api/relations/index.mjs.map +1 -1
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +4 -4
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +5 -5
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +52 -46
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +52 -46
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +52 -46
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.d.mts.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +52 -46
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +52 -46
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/reorder.d.mts +8 -0
- package/dist/astro/routes/api/schema/collections/reorder.d.mts.map +1 -0
- package/dist/astro/routes/api/schema/collections/reorder.mjs +71 -0
- package/dist/astro/routes/api/schema/collections/reorder.mjs.map +1 -0
- package/dist/astro/routes/api/schema/index.mjs +14 -11
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +52 -46
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +52 -46
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.d.mts +1 -0
- package/dist/astro/routes/api/search/enable.d.mts.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +19 -15
- package/dist/astro/routes/api/search/enable.mjs.map +1 -1
- package/dist/astro/routes/api/search/index.mjs +13 -13
- package/dist/astro/routes/api/search/rebuild.mjs +15 -15
- package/dist/astro/routes/api/search/rebuild.mjs.map +1 -1
- package/dist/astro/routes/api/search/stats.mjs +9 -9
- package/dist/astro/routes/api/search/suggest.mjs +13 -13
- package/dist/astro/routes/api/sections/_slug_.mjs +13 -12
- package/dist/astro/routes/api/sections/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/sections/index.mjs +13 -12
- package/dist/astro/routes/api/sections/index.mjs.map +1 -1
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +8 -8
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +7 -7
- package/dist/astro/routes/api/settings/backups/export.mjs +8 -8
- package/dist/astro/routes/api/settings/backups/index.mjs +9 -9
- package/dist/astro/routes/api/settings/email.mjs +6 -6
- package/dist/astro/routes/api/settings.mjs +18 -17
- package/dist/astro/routes/api/settings.mjs.map +1 -1
- package/dist/astro/routes/api/setup/admin-verify.mjs +15 -14
- package/dist/astro/routes/api/setup/admin-verify.mjs.map +1 -1
- package/dist/astro/routes/api/setup/admin.mjs +14 -13
- package/dist/astro/routes/api/setup/admin.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-bypass.mjs +34 -32
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +4 -4
- package/dist/astro/routes/api/setup/index.mjs +36 -34
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/setup/status.mjs +5 -5
- package/dist/astro/routes/api/snapshot.mjs +10 -10
- package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts +8 -0
- package/dist/astro/routes/api/taxonomies/_name_/reorder.d.mts.map +1 -0
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs +44 -0
- package/dist/astro/routes/api/taxonomies/_name_/reorder.mjs.map +1 -0
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +19 -18
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +19 -18
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.d.mts.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +21 -19
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/translations.d.mts +8 -0
- package/dist/astro/routes/api/taxonomies/_name_/translations.d.mts.map +1 -0
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs +46 -0
- package/dist/astro/routes/api/taxonomies/_name_/translations.mjs.map +1 -0
- package/dist/astro/routes/api/taxonomies/_name_.d.mts +19 -0
- package/dist/astro/routes/api/taxonomies/_name_.d.mts.map +1 -0
- package/dist/astro/routes/api/taxonomies/_name_.mjs +88 -0
- package/dist/astro/routes/api/taxonomies/_name_.mjs.map +1 -0
- package/dist/astro/routes/api/taxonomies/index.mjs +19 -18
- package/dist/astro/routes/api/taxonomies/index.mjs.map +1 -1
- package/dist/astro/routes/api/themes/preview.mjs +8 -8
- package/dist/astro/routes/api/typegen.mjs +6 -6
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +10 -9
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +13 -12
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +13 -12
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.mjs +8 -7
- package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.mjs +13 -12
- package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
- package/dist/astro/routes/api/widget-components.mjs +4 -4
- package/dist/astro/routes/robots.txt.mjs +11 -10
- package/dist/astro/routes/robots.txt.mjs.map +1 -1
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +14 -14
- package/dist/astro/routes/sitemap.xml.mjs +12 -11
- package/dist/astro/routes/sitemap.xml.mjs.map +1 -1
- package/dist/astro/types.d.mts +41 -17
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-R5iYYrpa.mjs → authorize-1iFJzPaZ.mjs} +2 -2
- package/dist/{authorize-R5iYYrpa.mjs.map → authorize-1iFJzPaZ.mjs.map} +1 -1
- package/dist/{backup-LpQ1V_sR.mjs → backup-LHjIAVNP.mjs} +5 -5
- package/dist/{backup-LpQ1V_sR.mjs.map → backup-LHjIAVNP.mjs.map} +1 -1
- package/dist/{base64-B-PsqheR.mjs → base64-CvLhUKQv.mjs} +1 -1
- package/dist/{base64-B-PsqheR.mjs.map → base64-CvLhUKQv.mjs.map} +1 -1
- package/dist/bundle-CBEXrBnb.mjs +663 -0
- package/dist/bundle-CBEXrBnb.mjs.map +1 -0
- package/dist/{byline-fields-T9ZLZMxJ.mjs → byline-fields-C5S8wygO.mjs} +2 -2
- package/dist/{byline-fields-T9ZLZMxJ.mjs.map → byline-fields-C5S8wygO.mjs.map} +1 -1
- package/dist/{byline-C5TAqs8N.mjs → byline-pEcoDps8.mjs} +9 -9
- package/dist/{byline-C5TAqs8N.mjs.map → byline-pEcoDps8.mjs.map} +1 -1
- package/dist/{byline-registry-BCuOp4UF.mjs → byline-registry-DDun8sxh.mjs} +4 -4
- package/dist/{byline-registry-BCuOp4UF.mjs.map → byline-registry-DDun8sxh.mjs.map} +1 -1
- package/dist/{bylines-B8-WGdja.mjs → bylines-BWemdX2k.mjs} +8 -8
- package/dist/{bylines-B8-WGdja.mjs.map → bylines-BWemdX2k.mjs.map} +1 -1
- package/dist/{bylines-DGuqBLjV.mjs → bylines-DJfSfXCL.mjs} +5 -5
- package/dist/{bylines-DGuqBLjV.mjs.map → bylines-DJfSfXCL.mjs.map} +1 -1
- package/dist/cache-CBJaDy_E.mjs +108 -0
- package/dist/cache-CBJaDy_E.mjs.map +1 -0
- package/dist/{challenge-store-BFzgFRog.mjs → challenge-store-CZyFVaad.mjs} +1 -1
- package/dist/{challenge-store-BFzgFRog.mjs.map → challenge-store-CZyFVaad.mjs.map} +1 -1
- package/dist/{chunks-D5dlPeRb.mjs → chunks-DfPax1I0.mjs} +2 -2
- package/dist/{chunks-D5dlPeRb.mjs.map → chunks-DfPax1I0.mjs.map} +1 -1
- package/dist/cli/index.mjs +193 -707
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +74 -4
- package/dist/client/index.d.mts.map +1 -1
- package/dist/client/index.mjs +25 -2
- package/dist/client/index.mjs.map +1 -1
- package/dist/collection-deletion-C8BugSGz.mjs +779 -0
- package/dist/collection-deletion-C8BugSGz.mjs.map +1 -0
- package/dist/{comment-DnTxxVHv.mjs → comment-Dxlep6cE.mjs} +3 -3
- package/dist/{comment-DnTxxVHv.mjs.map → comment-Dxlep6cE.mjs.map} +1 -1
- package/dist/{comment-reaction-BUlUz2bh.mjs → comment-reaction-D6qfHKBc.mjs} +2 -2
- package/dist/{comment-reaction-BUlUz2bh.mjs.map → comment-reaction-D6qfHKBc.mjs.map} +1 -1
- package/dist/{comments-QPKgK3nk.mjs → comments-CTOuhTSX.mjs} +3 -3
- package/dist/{comments-QPKgK3nk.mjs.map → comments-CTOuhTSX.mjs.map} +1 -1
- package/dist/{components-DZkL4Fx0.mjs → components-CQy3lh9x.mjs} +1 -1
- package/dist/{components-DZkL4Fx0.mjs.map → components-CQy3lh9x.mjs.map} +1 -1
- package/dist/{config-DyF3OKg_.mjs → config-C3EBstHi.mjs} +18 -2
- package/dist/config-C3EBstHi.mjs.map +1 -0
- package/dist/{content-CpfKV9QE.mjs → content-BNfwh7xX.mjs} +667 -183
- package/dist/content-BNfwh7xX.mjs.map +1 -0
- package/dist/content-list-query-DhQ84eVV.d.mts +26 -0
- package/dist/content-list-query-DhQ84eVV.d.mts.map +1 -0
- package/dist/content-refresh-DNERP4-f.mjs +2560 -0
- package/dist/content-refresh-DNERP4-f.mjs.map +1 -0
- package/dist/{context-kO4YZB03.mjs → context-DRgOvSnW.mjs} +28 -20
- package/dist/context-DRgOvSnW.mjs.map +1 -0
- package/dist/{cron-BlKIMD_e.mjs → cron-Y9aLtgrA.mjs} +1 -1
- package/dist/{cron-BlKIMD_e.mjs.map → cron-Y9aLtgrA.mjs.map} +1 -1
- package/dist/{dashboard-DoBwPnON.mjs → dashboard-CB8J89rJ.mjs} +45 -11
- package/dist/dashboard-CB8J89rJ.mjs.map +1 -0
- package/dist/db/index.d.mts +4 -4
- package/dist/db/index.mjs +3 -3
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{db-errors-CcWLaRiR.mjs → db-errors-Cl-sKYTm.mjs} +1 -1
- package/dist/{db-errors-CcWLaRiR.mjs.map → db-errors-Cl-sKYTm.mjs.map} +1 -1
- package/dist/{default-C4v9KP56.mjs → default-DdDl3fxv.mjs} +1 -1
- package/dist/{default-C4v9KP56.mjs.map → default-DdDl3fxv.mjs.map} +1 -1
- package/dist/deferred-tasks-BMsa9F64.d.mts +9 -0
- package/dist/deferred-tasks-BMsa9F64.d.mts.map +1 -0
- package/dist/{device-flow-DhUsyJU6.mjs → device-flow-CaioYGK3.mjs} +5 -5
- package/dist/{device-flow-DhUsyJU6.mjs.map → device-flow-CaioYGK3.mjs.map} +1 -1
- package/dist/{dialect-helpers-DFwyWjTM.mjs → dialect-helpers-BSbfaO3F.mjs} +24 -16
- package/dist/dialect-helpers-BSbfaO3F.mjs.map +1 -0
- package/dist/dist-D_6CKR8z.mjs +810 -0
- package/dist/dist-D_6CKR8z.mjs.map +1 -0
- package/dist/{email-console-C-9Ng8DM.mjs → email-console-BHNjIwiB.mjs} +1 -1
- package/dist/{email-console-C-9Ng8DM.mjs.map → email-console-BHNjIwiB.mjs.map} +1 -1
- package/dist/{enrich-CFJJgxs_.mjs → enrich-_ySMEvEE.mjs} +2 -2
- package/dist/{enrich-CFJJgxs_.mjs.map → enrich-_ySMEvEE.mjs.map} +1 -1
- package/dist/{error-DmmN74gW.mjs → error-DupyJZE2.mjs} +3 -3
- package/dist/{error-DmmN74gW.mjs.map → error-DupyJZE2.mjs.map} +1 -1
- package/dist/{errors-DtEXIQQV.mjs → errors-CLUPm7aD.mjs} +15 -2
- package/dist/errors-CLUPm7aD.mjs.map +1 -0
- package/dist/{escape-CdW31WsG.mjs → escape-C_aLKP3H.mjs} +1 -1
- package/dist/{escape-CdW31WsG.mjs.map → escape-C_aLKP3H.mjs.map} +1 -1
- package/dist/{field-defs-cache-QMVnzTH6.mjs → field-defs-cache-hg6QkjV9.mjs} +5 -5
- package/dist/{field-defs-cache-QMVnzTH6.mjs.map → field-defs-cache-hg6QkjV9.mjs.map} +1 -1
- package/dist/{fts-manager-vncsZIJm.mjs → fts-manager-CZ-5dVwJ.mjs} +111 -61
- package/dist/fts-manager-CZ-5dVwJ.mjs.map +1 -0
- package/dist/{hash-Cb7U1b5M.mjs → hash-Cg0TXJPV.mjs} +1 -1
- package/dist/{hash-Cb7U1b5M.mjs.map → hash-Cg0TXJPV.mjs.map} +1 -1
- package/dist/{hreflang-CZbSx6Bg.d.mts → hreflang-D3Uehfv2.d.mts} +2 -2
- package/dist/{hreflang-CZbSx6Bg.d.mts.map → hreflang-D3Uehfv2.d.mts.map} +1 -1
- package/dist/{hreflang-Bg-zMpkr.mjs → hreflang-DNswoYsi.mjs} +7 -7
- package/dist/{hreflang-Bg-zMpkr.mjs.map → hreflang-DNswoYsi.mjs.map} +1 -1
- package/dist/{import-PKhLeXvn.mjs → import-B41u8Rot.mjs} +7 -7
- package/dist/{import-PKhLeXvn.mjs.map → import-B41u8Rot.mjs.map} +1 -1
- package/dist/{index-j9rTh3cp.d.mts → index-C3Y-rsyN.d.mts} +286 -93
- package/dist/index-C3Y-rsyN.d.mts.map +1 -0
- package/dist/{index-BeNto_Vs.d.mts → index-Cof4-Fab.d.mts} +5 -10
- package/dist/index-Cof4-Fab.d.mts.map +1 -0
- package/dist/index.d.mts +20 -19
- package/dist/index.mjs +79 -71
- package/dist/{init-lock-DJkX6Hto.mjs → init-lock-6b309ZrF.mjs} +1 -1
- package/dist/{init-lock-DJkX6Hto.mjs.map → init-lock-6b309ZrF.mjs.map} +1 -1
- package/dist/{load-BwTdWE8B.mjs → load-DmvJrtxS.mjs} +2 -2
- package/dist/{load-BwTdWE8B.mjs.map → load-DmvJrtxS.mjs.map} +1 -1
- package/dist/{loader-C1XOLV5b.mjs → loader-DLBqRiGP.mjs} +120 -64
- package/dist/loader-DLBqRiGP.mjs.map +1 -0
- package/dist/{manifest-schema-bCq54i7F.mjs → manifest-schema-BXbUAHod.mjs} +1 -1
- package/dist/{manifest-schema-bCq54i7F.mjs.map → manifest-schema-BXbUAHod.mjs.map} +1 -1
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +3 -2
- package/dist/media/index.mjs.map +1 -1
- package/dist/media/local-runtime.d.mts +12 -12
- package/dist/media/local-runtime.mjs +10 -9
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-DWIzoVFL.mjs → media-BWWWb8kg.mjs} +92 -3
- package/dist/media-BWWWb8kg.mjs.map +1 -0
- package/dist/{media-tQDZEdu7.mjs → media-CzhAn2--.mjs} +2 -2
- package/dist/{media-tQDZEdu7.mjs.map → media-CzhAn2--.mjs.map} +1 -1
- package/dist/{media-allowlist-B7TPD6aq.mjs → media-allowlist-BD6HhgXA.mjs} +14 -3
- package/dist/media-allowlist-BD6HhgXA.mjs.map +1 -0
- package/dist/{media-upload-Bc9Nc5Zf.mjs → media-upload-9yas9zGH.mjs} +11 -9
- package/dist/{media-upload-Bc9Nc5Zf.mjs.map → media-upload-9yas9zGH.mjs.map} +1 -1
- package/dist/{media-url-BCm5vBn6.mjs → media-url-BvoX88FA.mjs} +1 -1
- package/dist/{media-url-BCm5vBn6.mjs.map → media-url-BvoX88FA.mjs.map} +1 -1
- package/dist/{media-usage-DPJcNaAq.mjs → media-usage-De3qvwjc.mjs} +138 -61
- package/dist/media-usage-De3qvwjc.mjs.map +1 -0
- package/dist/media-usage-work-B9Bksh_b.mjs +152 -0
- package/dist/media-usage-work-B9Bksh_b.mjs.map +1 -0
- package/dist/media-usage-work-DrCbqBcl.mjs +424 -0
- package/dist/media-usage-work-DrCbqBcl.mjs.map +1 -0
- package/dist/media-usage-write-fence-aHEgN1zB.mjs +42 -0
- package/dist/media-usage-write-fence-aHEgN1zB.mjs.map +1 -0
- package/dist/{menus-C6qhqP4X.mjs → menus-CC-yn1F9.mjs} +554 -121
- package/dist/menus-CC-yn1F9.mjs.map +1 -0
- package/dist/{menus-BxPLFfIc.mjs → menus-fxvEDYE6.mjs} +5 -5
- package/dist/{menus-BxPLFfIc.mjs.map → menus-fxvEDYE6.mjs.map} +1 -1
- package/dist/{mode-fiXRMfeA.mjs → mode-Bpe-tNNy.mjs} +1 -1
- package/dist/{mode-fiXRMfeA.mjs.map → mode-Bpe-tNNy.mjs.map} +1 -1
- package/dist/{normalize-C-SHXmra.mjs → normalize-Bc-s_r-a.mjs} +1 -1
- package/dist/{normalize-C-SHXmra.mjs.map → normalize-Bc-s_r-a.mjs.map} +1 -1
- package/dist/{oauth-authorization-C9tFinly.mjs → oauth-authorization-BXO1MKej.mjs} +5 -5
- package/dist/{oauth-authorization-C9tFinly.mjs.map → oauth-authorization-BXO1MKej.mjs.map} +1 -1
- package/dist/{oauth-clients-BwTxE1aE.mjs → oauth-clients-C_DNqtRu.mjs} +1 -1
- package/dist/{oauth-clients-BwTxE1aE.mjs.map → oauth-clients-C_DNqtRu.mjs.map} +1 -1
- package/dist/{oauth-state-store-DaRUwPWO.mjs → oauth-state-store-DwAxMtk4.mjs} +1 -1
- package/dist/{oauth-state-store-DaRUwPWO.mjs.map → oauth-state-store-DwAxMtk4.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-CzuzOSlF.mjs → oauth-user-lookup-BxfZizdk.mjs} +1 -1
- package/dist/{oauth-user-lookup-CzuzOSlF.mjs.map → oauth-user-lookup-BxfZizdk.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{object-cache-Bok5j2ae.mjs → object-cache-71tPfV4A.mjs} +106 -5
- package/dist/object-cache-71tPfV4A.mjs.map +1 -0
- package/dist/{options-BlmBHTvX.mjs → options-CBXIHUoT.mjs} +1 -1
- package/dist/{options-BlmBHTvX.mjs.map → options-CBXIHUoT.mjs.map} +1 -1
- package/dist/{options-aeIs8WFu.d.mts → options-DRe1pIBL.d.mts} +4 -3
- package/dist/options-DRe1pIBL.d.mts.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-BL49yb9D.mjs → parse-DFbuc-Fh.mjs} +2 -2
- package/dist/{parse-BL49yb9D.mjs.map → parse-DFbuc-Fh.mjs.map} +1 -1
- package/dist/{passkey-config-KMhgFjdQ.mjs → passkey-config-_2GOwwK8.mjs} +1 -1
- package/dist/{passkey-config-KMhgFjdQ.mjs.map → passkey-config-_2GOwwK8.mjs.map} +1 -1
- package/dist/{placeholder-Dxo5Zt58.d.mts → placeholder-CzW7Lshs.d.mts} +1 -1
- package/dist/{placeholder-Dxo5Zt58.d.mts.map → placeholder-CzW7Lshs.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +8 -1
- package/dist/plugin-types.d.mts.map +1 -1
- package/dist/plugin-utils.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.d.mts +10 -10
- package/dist/plugins/adapt-sandbox-entry.mjs +7 -4
- package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
- package/dist/{portable-text-CNahFfj6.mjs → portable-text-DhKbDAGr.mjs} +1 -1
- package/dist/{portable-text-CNahFfj6.mjs.map → portable-text-DhKbDAGr.mjs.map} +1 -1
- package/dist/{preview-D4Jnbfx7.mjs → preview-PW-nOnq4.mjs} +2 -2
- package/dist/{preview-D4Jnbfx7.mjs.map → preview-PW-nOnq4.mjs.map} +1 -1
- package/dist/{public-url-DSGTnJFw.mjs → public-url-CWoFhQT8.mjs} +14 -3
- package/dist/public-url-CWoFhQT8.mjs.map +1 -0
- package/dist/{query-DCiXI7OZ.mjs → query-BgjfiLUQ.mjs} +38 -77
- package/dist/query-BgjfiLUQ.mjs.map +1 -0
- package/dist/{query-1xOcEy18.mjs → query-vgZNwIhh.mjs} +4 -4
- package/dist/{query-1xOcEy18.mjs.map → query-vgZNwIhh.mjs.map} +1 -1
- package/dist/{rate-limit-Vkp9gQ1Y.mjs → rate-limit-L5anykgo.mjs} +2 -2
- package/dist/{rate-limit-Vkp9gQ1Y.mjs.map → rate-limit-L5anykgo.mjs.map} +1 -1
- package/dist/{redirect-BJ6d1yHh.mjs → redirect-COsS6SWJ.mjs} +3 -3
- package/dist/{redirect-BJ6d1yHh.mjs.map → redirect-COsS6SWJ.mjs.map} +1 -1
- package/dist/{redirect-CDgfv9vh.mjs → redirect-KBNE3UEs.mjs} +1 -1
- package/dist/{redirect-CDgfv9vh.mjs.map → redirect-KBNE3UEs.mjs.map} +1 -1
- package/dist/{redirects-Cwkq2H-G.mjs → redirects-CufRQt0Q.mjs} +5 -5
- package/dist/{redirects-Cwkq2H-G.mjs.map → redirects-CufRQt0Q.mjs.map} +1 -1
- package/dist/{redirects-BOn2DIUs.mjs → redirects-cV9g7V-0.mjs} +149 -16
- package/dist/redirects-cV9g7V-0.mjs.map +1 -0
- package/dist/registry-C581V4TP.mjs +1275 -0
- package/dist/registry-C581V4TP.mjs.map +1 -0
- package/dist/{relation-DJ5DZh7B.mjs → relation-DtmAQH7s.mjs} +3 -3
- package/dist/{relation-DJ5DZh7B.mjs.map → relation-DtmAQH7s.mjs.map} +1 -1
- package/dist/{relations-C4duJnwI.mjs → relations-BBApI4e2.mjs} +3 -3
- package/dist/{relations-C4duJnwI.mjs.map → relations-BBApI4e2.mjs.map} +1 -1
- package/dist/{relations-DpTjftCl.mjs → relations-DICSyfsh.mjs} +6 -6
- package/dist/{relations-DpTjftCl.mjs.map → relations-DICSyfsh.mjs.map} +1 -1
- package/dist/{relations-DHFQGX0h.d.mts → relations-DItI3MN9.d.mts} +401 -40
- package/dist/relations-DItI3MN9.d.mts.map +1 -0
- package/dist/request-context.d.mts +3 -0
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs.map +1 -1
- package/dist/{request-meta-DzXYYI-n.mjs → request-meta-DgZJh3zG.mjs} +2 -2
- package/dist/{request-meta-DzXYYI-n.mjs.map → request-meta-DgZJh3zG.mjs.map} +1 -1
- package/dist/{resolve-BUvFE0Lr.mjs → resolve-DOhVEvty.mjs} +2 -2
- package/dist/{resolve-BUvFE0Lr.mjs.map → resolve-DOhVEvty.mjs.map} +1 -1
- package/dist/{runner-BsI18UgP.mjs → runner-BiDvq5dv.mjs} +1404 -235
- package/dist/runner-BiDvq5dv.mjs.map +1 -0
- package/dist/{runner-CHdkERbt.d.mts → runner-CgQbELYC.d.mts} +2 -2
- package/dist/{runner-CHdkERbt.d.mts.map → runner-CgQbELYC.d.mts.map} +1 -1
- package/dist/runtime.d.mts +11 -11
- package/dist/runtime.mjs +5 -4
- package/dist/runtime.mjs.map +1 -1
- package/dist/{schema-eqZhkR0S.mjs → schema-BFyTg38T.mjs} +3 -3
- package/dist/{schema-eqZhkR0S.mjs.map → schema-BFyTg38T.mjs.map} +1 -1
- package/dist/schema-CeQSNwB3.mjs +390 -0
- package/dist/schema-CeQSNwB3.mjs.map +1 -0
- package/dist/{scopes-Be2d8bSb.mjs → scopes-BVeuhGBE.mjs} +3 -3
- package/dist/{scopes-Be2d8bSb.mjs.map → scopes-BVeuhGBE.mjs.map} +1 -1
- package/dist/{search-CeO678cp.mjs → search-BpuPKZb0.mjs} +26 -16
- package/dist/search-BpuPKZb0.mjs.map +1 -0
- package/dist/{secrets-870d-7yA.mjs → secrets-dtYs2hDo.mjs} +5 -5
- package/dist/{secrets-870d-7yA.mjs.map → secrets-dtYs2hDo.mjs.map} +1 -1
- package/dist/{sections-DSuEfirn.mjs → sections-COctk6hf.mjs} +3 -3
- package/dist/{sections-DSuEfirn.mjs.map → sections-COctk6hf.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +28 -26
- package/dist/seo/index.d.mts +3 -3
- package/dist/seo/index.mjs +20 -19
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-C3GDfT0V.mjs → seo-AFpVvTqr.mjs} +6 -4
- package/dist/seo-AFpVvTqr.mjs.map +1 -0
- package/dist/{seo-BAPKiED8.mjs → seo-CvDxfvFb.mjs} +3 -3
- package/dist/{seo-BAPKiED8.mjs.map → seo-CvDxfvFb.mjs.map} +1 -1
- package/dist/{service-DyFT-k-R.mjs → service-xMiMSTsR.mjs} +4 -4
- package/dist/{service-DyFT-k-R.mjs.map → service-xMiMSTsR.mjs.map} +1 -1
- package/dist/{session-user-BrK2zz7S.mjs → session-user-BzVemLkw.mjs} +2 -2
- package/dist/{session-user-BrK2zz7S.mjs.map → session-user-BzVemLkw.mjs.map} +1 -1
- package/dist/{settings-DgKouY2S.mjs → settings-B372MV0p.mjs} +7 -7
- package/dist/{settings-DgKouY2S.mjs.map → settings-B372MV0p.mjs.map} +1 -1
- package/dist/{settings-CREKyj8I.mjs → settings-BE9aqn_9.mjs} +3 -3
- package/dist/{settings-CREKyj8I.mjs.map → settings-BE9aqn_9.mjs.map} +1 -1
- package/dist/{setup-complete-L7bMK2VI.mjs → setup-complete-DF7TAfD5.mjs} +2 -2
- package/dist/{setup-complete-L7bMK2VI.mjs.map → setup-complete-DF7TAfD5.mjs.map} +1 -1
- package/dist/{setup-nonce-Ct5tjWcq.mjs → setup-nonce-Do-U_cg9.mjs} +1 -1
- package/dist/{setup-nonce-Ct5tjWcq.mjs.map → setup-nonce-Do-U_cg9.mjs.map} +1 -1
- package/dist/{single-flight-cache-C2exrGAi.mjs → single-flight-cache-B0eLKS6K.mjs} +2 -2
- package/dist/{single-flight-cache-C2exrGAi.mjs.map → single-flight-cache-B0eLKS6K.mjs.map} +1 -1
- package/dist/site-url-Bowsi9T-.mjs +16 -0
- package/dist/site-url-Bowsi9T-.mjs.map +1 -0
- package/dist/slugify-B994zkXC.mjs +17 -0
- package/dist/slugify-B994zkXC.mjs.map +1 -0
- package/dist/{snapshot-BdpUJKD-.mjs → snapshot-B5h6aPMh.mjs} +2 -2
- package/dist/{snapshot-BdpUJKD-.mjs.map → snapshot-B5h6aPMh.mjs.map} +1 -1
- package/dist/{ssrf-CviKqWmq.mjs → ssrf-Cpvg_n3A.mjs} +1 -1
- package/dist/{ssrf-CviKqWmq.mjs.map → ssrf-Cpvg_n3A.mjs.map} +1 -1
- package/dist/{state-xxv6ZTMv.mjs → state-DYwPue40.mjs} +1 -1
- package/dist/{state-xxv6ZTMv.mjs.map → state-DYwPue40.mjs.map} +1 -1
- package/dist/{status-COfycGIU.mjs → status-CWVYpbD8.mjs} +1 -1
- package/dist/{status-COfycGIU.mjs.map → status-CWVYpbD8.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-B61CRSha.mjs → taxonomies-B0o9ExNV.mjs} +85 -75
- package/dist/taxonomies-B0o9ExNV.mjs.map +1 -0
- package/dist/{taxonomies-Dbdki3Un.mjs → taxonomies-Dk3MAklL.mjs} +360 -56
- package/dist/taxonomies-Dk3MAklL.mjs.map +1 -0
- package/dist/taxonomy-CPeM8d2o.mjs +484 -0
- package/dist/taxonomy-CPeM8d2o.mjs.map +1 -0
- package/dist/{tokens-DVltpO2D.mjs → tokens-C39klX8R.mjs} +2 -2
- package/dist/{tokens-DVltpO2D.mjs.map → tokens-C39klX8R.mjs.map} +1 -1
- package/dist/{transaction-D0FOsb3X.mjs → transaction-NQj4VJ7Z.mjs} +1 -1
- package/dist/{transaction-D0FOsb3X.mjs.map → transaction-NQj4VJ7Z.mjs.map} +1 -1
- package/dist/{transport-BWQo0Foy.mjs → transport--CfOflrp.mjs} +1 -1
- package/dist/{transport-BWQo0Foy.mjs.map → transport--CfOflrp.mjs.map} +1 -1
- package/dist/{transport-DokJOScS.d.mts → transport-B_rfeU-a.d.mts} +1 -1
- package/dist/{transport-DokJOScS.d.mts.map → transport-B_rfeU-a.d.mts.map} +1 -1
- package/dist/{trusted-proxy-CwjQj0YG.mjs → trusted-proxy-CEVekpby.mjs} +1 -1
- package/dist/{trusted-proxy-CwjQj0YG.mjs.map → trusted-proxy-CEVekpby.mjs.map} +1 -1
- package/dist/{types-o7xo7VgH.mjs → types-75KIqzL9.mjs} +20 -3
- package/dist/types-75KIqzL9.mjs.map +1 -0
- package/dist/{types-C9P3ysxq.d.mts → types-BPuloofb.d.mts} +1 -1
- package/dist/{types-C9P3ysxq.d.mts.map → types-BPuloofb.d.mts.map} +1 -1
- package/dist/{types-BD40g7zz.mjs → types-BW1syrlw.mjs} +1 -1
- package/dist/{types-BD40g7zz.mjs.map → types-BW1syrlw.mjs.map} +1 -1
- package/dist/{types-jISEalTX.d.mts → types-C3_AbkDP.d.mts} +53 -10
- package/dist/types-C3_AbkDP.d.mts.map +1 -0
- package/dist/{types-84Bque3-.d.mts → types-CAGdePf3.d.mts} +133 -1
- package/dist/types-CAGdePf3.d.mts.map +1 -0
- package/dist/{types-CcSZCcF1.d.mts → types-CFUfyt1s.d.mts} +1 -1
- package/dist/{types-CcSZCcF1.d.mts.map → types-CFUfyt1s.d.mts.map} +1 -1
- package/dist/{types-DQLZ58GV.d.mts → types-CPx2lIGo.d.mts} +1 -1
- package/dist/{types-DQLZ58GV.d.mts.map → types-CPx2lIGo.d.mts.map} +1 -1
- package/dist/{types-XrQQ-Aex.mjs → types-CQPWhPxE.mjs} +15 -11
- package/dist/types-CQPWhPxE.mjs.map +1 -0
- package/dist/{types-BQY7VYw6.d.mts → types-Cs-TFLNf.d.mts} +28 -8
- package/dist/types-Cs-TFLNf.d.mts.map +1 -0
- package/dist/types-CtzytVXn.mjs +18 -0
- package/dist/types-CtzytVXn.mjs.map +1 -0
- package/dist/{types-DqZVPILm.d.mts → types-D4_1Ga4L.d.mts} +44 -5
- package/dist/types-D4_1Ga4L.d.mts.map +1 -0
- package/dist/{types-Dr2GLlwu.d.mts → types-DefV3OiO.d.mts} +1 -1
- package/dist/{types-Dr2GLlwu.d.mts.map → types-DefV3OiO.d.mts.map} +1 -1
- package/dist/{types-DsAMyD_o.d.mts → types-X0MThr7x.d.mts} +1 -1
- package/dist/{types-DsAMyD_o.d.mts.map → types-X0MThr7x.d.mts.map} +1 -1
- package/dist/{upload-attempts-C5yd6Gae.mjs → upload-attempts-hlne3iA1.mjs} +1 -1
- package/dist/{upload-attempts-C5yd6Gae.mjs.map → upload-attempts-hlne3iA1.mjs.map} +1 -1
- package/dist/url-pattern-BHS0V5Ng.mjs +33 -0
- package/dist/url-pattern-BHS0V5Ng.mjs.map +1 -0
- package/dist/{user-BAumEmpA.mjs → user-sHgJ62SP.mjs} +3 -3
- package/dist/{user-BAumEmpA.mjs.map → user-sHgJ62SP.mjs.map} +1 -1
- package/dist/{utils-BbUgmeZG.mjs → utils-D4vD_Y98.mjs} +3 -3
- package/dist/{utils-BbUgmeZG.mjs.map → utils-D4vD_Y98.mjs.map} +1 -1
- package/dist/{validate-Bs_wT2ul.mjs → validate-BkqIG_Uj.mjs} +24 -5
- package/dist/validate-BkqIG_Uj.mjs.map +1 -0
- package/dist/{validate-VPnKoIzW.mjs → validate-BpwoDOgt.mjs} +25 -2
- package/dist/validate-BpwoDOgt.mjs.map +1 -0
- package/dist/{validate-CdYcY-Ln.d.mts → validate-DeGtBN84.d.mts} +25 -7
- package/dist/validate-DeGtBN84.d.mts.map +1 -0
- package/dist/{validation-BngR0nit.mjs → validation-CiJb_X5o.mjs} +14 -13
- package/dist/{validation-BngR0nit.mjs.map → validation-CiJb_X5o.mjs.map} +1 -1
- package/dist/version-x0NjTScG.mjs +7 -0
- package/dist/{version-Bdfb34kk.mjs.map → version-x0NjTScG.mjs.map} +1 -1
- package/dist/{widgets-L-VndTKD.mjs → widgets-Cwzvww0W.mjs} +3 -3
- package/dist/{widgets-L-VndTKD.mjs.map → widgets-Cwzvww0W.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-DueaLFd9.d.mts → wxr-taxonomies-CmC0ZsDr.d.mts} +1 -1
- package/dist/{wxr-taxonomies-DueaLFd9.d.mts.map → wxr-taxonomies-CmC0ZsDr.d.mts.map} +1 -1
- package/dist/{wxr-taxonomies-BexjtNid.mjs → wxr-taxonomies-QZjWxjyk.mjs} +13 -32
- package/dist/{wxr-taxonomies-BexjtNid.mjs.map → wxr-taxonomies-QZjWxjyk.mjs.map} +1 -1
- package/dist/{zod-generator-DMzfga3g.mjs → zod-generator-D11MhxG4.mjs} +53 -6
- package/dist/zod-generator-D11MhxG4.mjs.map +1 -0
- package/package.json +18 -7
- package/src/after.ts +6 -1
- package/src/api/errors.ts +13 -0
- package/src/api/handlers/content.ts +172 -33
- package/src/api/handlers/dashboard.ts +12 -2
- package/src/api/handlers/index.ts +16 -0
- package/src/api/handlers/manifest.ts +2 -0
- package/src/api/handlers/marketplace.ts +14 -0
- package/src/api/handlers/media-allowlist.ts +13 -2
- package/src/api/handlers/media-usage-work.ts +188 -0
- package/src/api/handlers/media-usage.ts +5 -3
- package/src/api/handlers/registry.ts +14 -0
- package/src/api/handlers/revision.ts +19 -5
- package/src/api/handlers/schema.ts +46 -3
- package/src/api/handlers/seo.ts +5 -2
- package/src/api/handlers/snapshot.ts +1 -1
- package/src/api/handlers/taxonomies.ts +446 -83
- package/src/api/media-usage-write-fence.ts +64 -0
- package/src/api/openapi/document.ts +247 -0
- package/src/api/public-url.ts +14 -2
- package/src/api/schemas/common.ts +3 -1
- package/src/api/schemas/content.ts +122 -1
- package/src/api/schemas/media-usage.ts +133 -0
- package/src/api/schemas/schema.ts +91 -3
- package/src/api/schemas/search.ts +2 -0
- package/src/api/schemas/taxonomies.ts +58 -8
- package/src/api/site-url.ts +16 -6
- package/src/api/types.ts +1 -0
- package/src/astro/integration/index.ts +55 -42
- package/src/astro/integration/routes.ts +48 -4
- package/src/astro/integration/runtime.ts +28 -0
- package/src/astro/integration/virtual-modules.ts +27 -1
- package/src/astro/integration/vite-config.ts +16 -1
- package/src/astro/middleware/media-usage-write-fence.ts +30 -0
- package/src/astro/middleware/redirect.ts +14 -12
- package/src/astro/middleware/scoped-db.ts +35 -4
- package/src/astro/middleware.ts +131 -30
- package/src/astro/routes/api/admin/comments/[id]/status.ts +1 -1
- package/src/astro/routes/api/admin/media-usage/collection-deletions/index.ts +22 -0
- package/src/astro/routes/api/admin/media-usage/collection-deletions/retry.ts +22 -0
- package/src/astro/routes/api/admin/media-usage/work/index.ts +26 -0
- package/src/astro/routes/api/admin/media-usage/work/retry.ts +26 -0
- package/src/astro/routes/api/admin/plugins/[id]/disable.ts +4 -0
- package/src/astro/routes/api/admin/plugins/[id]/enable.ts +4 -0
- package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +4 -0
- package/src/astro/routes/api/admin/plugins/[id]/update.ts +4 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/[id]/uninstall.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/install.ts +4 -0
- package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +2 -2
- package/src/astro/routes/api/auth/invite/index.ts +2 -2
- package/src/astro/routes/api/auth/magic-link/send.ts +2 -2
- package/src/astro/routes/api/auth/signup/request.ts +2 -2
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +1 -1
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +67 -31
- package/src/astro/routes/api/import/wordpress/execute.ts +3 -10
- package/src/astro/routes/api/mcp.ts +1 -0
- package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +7 -1
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +1 -7
- package/src/astro/routes/api/schema/collections/reorder.ts +31 -0
- package/src/astro/routes/api/schema/index.ts +2 -0
- package/src/astro/routes/api/search/enable.ts +5 -1
- package/src/astro/routes/api/search/rebuild.ts +6 -1
- package/src/astro/routes/api/taxonomies/[name]/reorder.ts +41 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +1 -0
- package/src/astro/routes/api/taxonomies/[name]/translations.ts +48 -0
- package/src/astro/routes/api/taxonomies/[name].ts +102 -0
- package/src/astro/types.ts +31 -4
- package/src/cleanup.ts +25 -26
- package/src/cli/commands/dev.ts +13 -3
- package/src/cli/commands/doctor.ts +193 -6
- package/src/cli/commands/export-seed.ts +6 -1
- package/src/cli/index.ts +1 -1
- package/src/client/index.ts +119 -0
- package/src/components/Block.astro +12 -0
- package/src/components/CommentForm.astro +1 -1
- package/src/components/Comments.astro +1 -1
- package/src/components/InlinePortableTextEditor.tsx +260 -32
- package/src/components/OrderedList.astro +14 -0
- package/src/components/PortableText.astro +26 -4
- package/src/components/index.ts +12 -1
- package/src/components/ordered-list.ts +549 -0
- package/src/content/converters/mark-safety.ts +120 -0
- package/src/content/converters/numbered-list.ts +148 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +84 -25
- package/src/content/converters/prosemirror-to-portable-text.ts +35 -14
- package/src/content/converters/types.ts +2 -0
- package/src/content/portable-text-lists.ts +205 -0
- package/src/content-list-query.ts +29 -0
- package/src/database/dialect-helpers.ts +3 -22
- package/src/database/migrations/055_content_translation_group_locale_index.ts +67 -0
- package/src/database/migrations/056_taxonomy_term_sort_order.ts +189 -0
- package/src/database/migrations/057_collection_hidden.ts +24 -0
- package/src/database/migrations/058_collection_sort_order.ts +20 -0
- package/src/database/migrations/059_revision_prune_queue.ts +36 -0
- package/src/database/migrations/060_collection_admin_config.ts +16 -0
- package/src/database/migrations/061_media_usage_cleanup.ts +77 -0
- package/src/database/migrations/062_media_usage_cleanup_fence.ts +295 -0
- package/src/database/migrations/063_media_usage_incremental_work.ts +397 -0
- package/src/database/migrations/064_fts_plain_text.ts +253 -0
- package/src/database/migrations/065_media_usage_collection_deletion.ts +85 -0
- package/src/database/migrations/066_media_usage_reconciliation.ts +115 -0
- package/src/database/migrations/067_indexed_content_fields.ts +39 -0
- package/src/database/migrations/068_content_taxonomy_entry_groups.ts +61 -0
- package/src/database/migrations/069_collection_title_date_fields.ts +45 -0
- package/src/database/migrations/070_collection_routable.ts +18 -0
- package/src/database/migrations/runner.ts +32 -0
- package/src/database/repositories/content.ts +1051 -273
- package/src/database/repositories/media-usage-work.ts +848 -0
- package/src/database/repositories/media-usage.ts +1502 -138
- package/src/database/repositories/revision.ts +98 -22
- package/src/database/repositories/taxonomy.ts +429 -103
- package/src/database/repositories/types.ts +58 -2
- package/src/database/types.ts +147 -17
- package/src/database/validate.ts +39 -0
- package/src/db/adapters.ts +36 -0
- package/src/db/index.ts +3 -0
- package/src/deferred-tasks.ts +71 -0
- package/src/emdash-runtime.ts +493 -110
- package/src/fields/file.ts +7 -4
- package/src/fields/image.ts +8 -1
- package/src/fields/types.ts +18 -5
- package/src/i18n/config.ts +32 -0
- package/src/i18n/repair-locale-casing.ts +0 -14
- package/src/i18n/taxonomy-locale-diagnostic.ts +47 -0
- package/src/import/wxr-taxonomies.ts +7 -47
- package/src/index.ts +27 -2
- package/src/loader.ts +165 -89
- package/src/mcp/server.ts +242 -34
- package/src/media/usage/activation.ts +626 -0
- package/src/media/usage/capture-triggers.ts +543 -0
- package/src/media/usage/cleanup.ts +305 -0
- package/src/media/usage/collection-deletion-processor.ts +356 -0
- package/src/media/usage/collection-deletion.ts +836 -0
- package/src/media/usage/content-fields.ts +40 -3
- package/src/media/usage/content-refresh.ts +356 -47
- package/src/media/usage/content-repair.ts +180 -61
- package/src/media/usage/content-snapshots.ts +85 -105
- package/src/media/usage/gc.ts +50 -0
- package/src/media/usage/projection-fingerprint.ts +129 -0
- package/src/media/usage/reconciliation-processor.ts +372 -0
- package/src/media/usage/reconciliation.ts +1117 -0
- package/src/media/usage/source-key.ts +4 -0
- package/src/media/usage/types.ts +2 -0
- package/src/media/usage/work-processor.ts +248 -0
- package/src/menus/index.ts +246 -141
- package/src/object-cache/index.ts +160 -3
- package/src/plugin-types.ts +8 -0
- package/src/plugins/adapt-sandbox-entry.ts +24 -5
- package/src/plugins/context.ts +24 -7
- package/src/plugins/index.ts +9 -0
- package/src/plugins/routes.ts +77 -1
- package/src/plugins/sandbox/index.ts +10 -1
- package/src/plugins/sandbox/types.ts +78 -1
- package/src/plugins/scheduler/node.ts +13 -1
- package/src/plugins/scheduler/types.ts +2 -0
- package/src/plugins/storage-indexes.ts +50 -11
- package/src/plugins/types.ts +39 -2
- package/src/query.ts +50 -79
- package/src/redirects/cache.ts +79 -19
- package/src/request-context.ts +3 -0
- package/src/scheduled-publish.ts +6 -1
- package/src/scheduler-health.ts +46 -0
- package/src/schema/registry.ts +863 -249
- package/src/schema/types.ts +74 -8
- package/src/schema/url-pattern.ts +33 -0
- package/src/schema/zod-generator.ts +77 -5
- package/src/search/fts-manager.ts +167 -86
- package/src/search/index.ts +2 -0
- package/src/search/query.ts +32 -11
- package/src/search/types.ts +8 -0
- package/src/seed/apply.ts +89 -16
- package/src/seed/types.ts +21 -3
- package/src/seed/validate.ts +52 -2
- package/src/taxonomies/index.ts +88 -82
- package/src/taxonomies/term-counts.ts +23 -12
- package/src/ui-comments.ts +19 -0
- package/src/ui.ts +11 -3
- package/src/utils/slugify.ts +1 -29
- package/src/virtual-modules.d.ts +24 -0
- package/dist/adapters-C0ONzkUP.d.mts.map +0 -1
- package/dist/after-B1IIdH3Y.mjs +0 -29
- package/dist/after-B1IIdH3Y.mjs.map +0 -1
- package/dist/api-DgPbMlng.mjs.map +0 -1
- package/dist/apply-1_6ra7NP.mjs.map +0 -1
- package/dist/cache-B26cufFd.mjs +0 -65
- package/dist/cache-B26cufFd.mjs.map +0 -1
- package/dist/config-DyF3OKg_.mjs.map +0 -1
- package/dist/content-CpfKV9QE.mjs.map +0 -1
- package/dist/content-refresh-DgqUBeTv.mjs +0 -1629
- package/dist/content-refresh-DgqUBeTv.mjs.map +0 -1
- package/dist/context-kO4YZB03.mjs.map +0 -1
- package/dist/dashboard-DoBwPnON.mjs.map +0 -1
- package/dist/dialect-helpers-DFwyWjTM.mjs.map +0 -1
- package/dist/errors-DtEXIQQV.mjs.map +0 -1
- package/dist/fts-manager-vncsZIJm.mjs.map +0 -1
- package/dist/index-BeNto_Vs.d.mts.map +0 -1
- package/dist/index-j9rTh3cp.d.mts.map +0 -1
- package/dist/loader-C1XOLV5b.mjs.map +0 -1
- package/dist/media-DWIzoVFL.mjs.map +0 -1
- package/dist/media-allowlist-B7TPD6aq.mjs.map +0 -1
- package/dist/media-usage-DPJcNaAq.mjs.map +0 -1
- package/dist/menus-C6qhqP4X.mjs.map +0 -1
- package/dist/object-cache-Bok5j2ae.mjs.map +0 -1
- package/dist/options-aeIs8WFu.d.mts.map +0 -1
- package/dist/public-url-DSGTnJFw.mjs.map +0 -1
- package/dist/query-DCiXI7OZ.mjs.map +0 -1
- package/dist/redirects-BOn2DIUs.mjs.map +0 -1
- package/dist/registry-BP1JK2xh.mjs +0 -795
- package/dist/registry-BP1JK2xh.mjs.map +0 -1
- package/dist/relations-DHFQGX0h.d.mts.map +0 -1
- package/dist/runner-BsI18UgP.mjs.map +0 -1
- package/dist/search-CeO678cp.mjs.map +0 -1
- package/dist/seo-C3GDfT0V.mjs.map +0 -1
- package/dist/site-url-8N3kSyVZ.mjs +0 -13
- package/dist/site-url-8N3kSyVZ.mjs.map +0 -1
- package/dist/slugify-C_tqlU4G.mjs +0 -30
- package/dist/slugify-C_tqlU4G.mjs.map +0 -1
- package/dist/taxonomies-B61CRSha.mjs.map +0 -1
- package/dist/taxonomies-Dbdki3Un.mjs.map +0 -1
- package/dist/taxonomy-DvwWAPvA.mjs +0 -304
- package/dist/taxonomy-DvwWAPvA.mjs.map +0 -1
- package/dist/types-84Bque3-.d.mts.map +0 -1
- package/dist/types-BQY7VYw6.d.mts.map +0 -1
- package/dist/types-DqZVPILm.d.mts.map +0 -1
- package/dist/types-XrQQ-Aex.mjs.map +0 -1
- package/dist/types-jISEalTX.d.mts.map +0 -1
- package/dist/types-o7xo7VgH.mjs.map +0 -1
- package/dist/validate-Bs_wT2ul.mjs.map +0 -1
- package/dist/validate-CdYcY-Ln.d.mts.map +0 -1
- package/dist/validate-VPnKoIzW.mjs.map +0 -1
- package/dist/version-Bdfb34kk.mjs +0 -7
- package/dist/zod-generator-DMzfga3g.mjs.map +0 -1
- /package/dist/{api-tokens-CEsW_jCg.mjs → api-tokens-CyTsLszZ.mjs} +0 -0
- /package/dist/{ssrf-BB__xfNM.mjs → ssrf-DpXgKsna.mjs} +0 -0
- /package/dist/{types-CK0LxPUQ.mjs → types-CP1CflWf.mjs} +0 -0
package/src/schema/registry.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ColumnDataType,
|
|
3
|
+
CreateTableBuilder,
|
|
4
|
+
Insertable,
|
|
5
|
+
Kysely,
|
|
6
|
+
Selectable,
|
|
7
|
+
Updateable,
|
|
8
|
+
} from "kysely";
|
|
2
9
|
import { sql } from "kysely";
|
|
3
10
|
import { ulid } from "ulidx";
|
|
4
11
|
|
|
@@ -6,14 +13,27 @@ import { currentTimestamp, listTablesLike, tableExists } from "../database/diale
|
|
|
6
13
|
import { withTransaction } from "../database/transaction.js";
|
|
7
14
|
import type { CollectionTable, Database, FieldTable } from "../database/types.js";
|
|
8
15
|
import { validateIdentifier } from "../database/validate.js";
|
|
16
|
+
import {
|
|
17
|
+
canResumeMediaUsageCollectionCapture,
|
|
18
|
+
finalizeMediaUsageCollectionCapture,
|
|
19
|
+
installPreparedMediaUsageCollectionCapture,
|
|
20
|
+
markMediaUsageCollectionCaptureReady,
|
|
21
|
+
prepareMediaUsageCollectionCapture,
|
|
22
|
+
} from "../media/usage/activation.js";
|
|
23
|
+
import {
|
|
24
|
+
deleteActivatedMediaUsageCollection,
|
|
25
|
+
isMediaUsageCollectionSlugDeleting,
|
|
26
|
+
} from "../media/usage/collection-deletion.js";
|
|
9
27
|
import {
|
|
10
28
|
deleteContentMediaUsageCollection,
|
|
29
|
+
invalidateContentMediaUsageSchemaChange,
|
|
11
30
|
markContentMediaUsageCollectionStaleSafely,
|
|
12
31
|
} from "../media/usage/content-refresh.js";
|
|
13
32
|
import { FTSManager } from "../search/fts-manager.js";
|
|
14
33
|
import { chunks, SQL_BATCH_SIZE } from "../utils/chunks.js";
|
|
15
34
|
import {
|
|
16
35
|
type Collection,
|
|
36
|
+
type CollectionAdminConfig,
|
|
17
37
|
type CollectionSource,
|
|
18
38
|
type CollectionSupport,
|
|
19
39
|
type ColumnType,
|
|
@@ -25,6 +45,7 @@ import {
|
|
|
25
45
|
type CollectionWithFields,
|
|
26
46
|
type FieldType,
|
|
27
47
|
FIELD_TYPE_TO_COLUMN,
|
|
48
|
+
isIndexableFieldType,
|
|
28
49
|
RESERVED_FIELD_SLUGS,
|
|
29
50
|
RESERVED_COLLECTION_SLUGS,
|
|
30
51
|
} from "./types.js";
|
|
@@ -35,6 +56,7 @@ const EC_PREFIX_PATTERN = /^ec_/;
|
|
|
35
56
|
const SINGLE_QUOTE_PATTERN = /'/g;
|
|
36
57
|
const UNDERSCORE_PATTERN = /_/g;
|
|
37
58
|
const WORD_BOUNDARY_PATTERN = /\b\w/g;
|
|
59
|
+
const FIELD_ID_PATTERN = /^[0-9A-Z]{26}$/;
|
|
38
60
|
|
|
39
61
|
/** Valid column types for runtime validation */
|
|
40
62
|
const COLUMN_TYPES: ReadonlySet<string> = new Set(["TEXT", "REAL", "INTEGER", "JSON"]);
|
|
@@ -44,6 +66,10 @@ const COLUMN_TYPE_TO_DATA_TYPE = {
|
|
|
44
66
|
INTEGER: "integer",
|
|
45
67
|
JSON: "json",
|
|
46
68
|
} satisfies Record<ColumnType, ColumnDataType>;
|
|
69
|
+
const TEXT_ALIAS_FIELD_TYPES: ReadonlySet<FieldType> = new Set(["string", "text", "slug"]);
|
|
70
|
+
|
|
71
|
+
/** Field types usable as a `titleField` — plain text that reads well as a title. */
|
|
72
|
+
const TITLE_FIELD_TYPES: ReadonlySet<string> = new Set(["string", "text", "slug"]);
|
|
47
73
|
|
|
48
74
|
/** Valid collection source prefixes/values */
|
|
49
75
|
const VALID_SOURCES: ReadonlySet<string> = new Set(["manual", "discovered", "seed"]);
|
|
@@ -69,10 +95,32 @@ const VALID_COLLECTION_SUPPORTS: ReadonlySet<string> = new Set<CollectionSupport
|
|
|
69
95
|
"seo",
|
|
70
96
|
]);
|
|
71
97
|
|
|
72
|
-
// Each _emdash_fields row uses
|
|
98
|
+
// Each _emdash_fields row uses 16 bound parameters. Six rows keep every
|
|
73
99
|
// multi-row INSERT below D1's 100-parameter statement limit.
|
|
74
100
|
const SEED_FIELD_INSERT_BATCH_SIZE = 6;
|
|
75
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Rank given to collections without an explicit `sort_order`. SQLite sorts
|
|
104
|
+
* NULL first on ASC while Postgres sorts it last, so the fallback is
|
|
105
|
+
* materialised with COALESCE rather than left to the dialect.
|
|
106
|
+
*/
|
|
107
|
+
const UNORDERED_COLLECTION_RANK = 2147483647;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Collection ordering shared by every list read: explicit `sort_order`
|
|
111
|
+
* first (ascending), then alphabetically by slug.
|
|
112
|
+
*/
|
|
113
|
+
const collectionOrder = sql<number>`coalesce(sort_order, ${sql.lit(UNORDERED_COLLECTION_RANK)})`;
|
|
114
|
+
|
|
115
|
+
function assertIndexableField(type: FieldType, indexed: boolean | undefined, slug: string): void {
|
|
116
|
+
if (indexed && !isIndexableFieldType(type)) {
|
|
117
|
+
throw new SchemaError(
|
|
118
|
+
`Field "${slug}" cannot be indexed because type "${type}" is not a scalar query type`,
|
|
119
|
+
"FIELD_NOT_INDEXABLE",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
76
124
|
function isCollectionSupport(value: unknown): value is CollectionSupport {
|
|
77
125
|
return typeof value === "string" && VALID_COLLECTION_SUPPORTS.has(value);
|
|
78
126
|
}
|
|
@@ -92,6 +140,87 @@ function parseSupports(raw: string | null | undefined): CollectionSupport[] {
|
|
|
92
140
|
return parsed.filter(isCollectionSupport);
|
|
93
141
|
}
|
|
94
142
|
|
|
143
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
144
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function parseCollectionAdmin(raw: string | null | undefined): CollectionAdminConfig | undefined {
|
|
148
|
+
if (!raw) return undefined;
|
|
149
|
+
const parsed: unknown = JSON.parse(raw);
|
|
150
|
+
if (!isRecord(parsed)) return undefined;
|
|
151
|
+
const listColumns = parsed.listColumns;
|
|
152
|
+
return {
|
|
153
|
+
listColumns: Array.isArray(listColumns)
|
|
154
|
+
? listColumns.filter((value): value is string => typeof value === "string")
|
|
155
|
+
: undefined,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function buildSeedCollectionCaptureFingerprint(
|
|
160
|
+
input: Omit<CreateCollectionInput, "source">,
|
|
161
|
+
fields: readonly CreateFieldInput[],
|
|
162
|
+
): Promise<string> {
|
|
163
|
+
const supports = input.supports ?? ["drafts", "revisions"];
|
|
164
|
+
const hasSeo = input.hasSeo ?? supports.includes("seo") ?? false;
|
|
165
|
+
let maxSortOrder = -1;
|
|
166
|
+
const definitions = fields.map((field) => {
|
|
167
|
+
const sortOrder = field.sortOrder ?? maxSortOrder + 1;
|
|
168
|
+
maxSortOrder = Math.max(maxSortOrder, sortOrder);
|
|
169
|
+
return {
|
|
170
|
+
slug: field.slug,
|
|
171
|
+
label: field.label,
|
|
172
|
+
type: field.type,
|
|
173
|
+
required: field.required ?? false,
|
|
174
|
+
unique: field.unique ?? false,
|
|
175
|
+
defaultValue: field.defaultValue === undefined ? null : JSON.stringify(field.defaultValue),
|
|
176
|
+
validation: field.validation ? JSON.stringify(field.validation) : null,
|
|
177
|
+
widget: field.widget ?? null,
|
|
178
|
+
options: field.options ? JSON.stringify(field.options) : null,
|
|
179
|
+
sortOrder,
|
|
180
|
+
searchable: field.searchable ?? false,
|
|
181
|
+
translatable: field.translatable ?? true,
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
const payload = JSON.stringify(
|
|
185
|
+
canonicalizeFingerprintValue({
|
|
186
|
+
version: 1,
|
|
187
|
+
collection: {
|
|
188
|
+
slug: input.slug,
|
|
189
|
+
label: input.label,
|
|
190
|
+
labelSingular: input.labelSingular ?? null,
|
|
191
|
+
description: input.description ?? null,
|
|
192
|
+
icon: input.icon ?? null,
|
|
193
|
+
admin: input.admin ?? null,
|
|
194
|
+
supports,
|
|
195
|
+
hasSeo,
|
|
196
|
+
hidden: input.hidden ?? false,
|
|
197
|
+
sortOrder: input.sortOrder ?? null,
|
|
198
|
+
commentsEnabled: input.commentsEnabled ?? false,
|
|
199
|
+
urlPattern: input.urlPattern ?? null,
|
|
200
|
+
routable: input.routable ?? true,
|
|
201
|
+
},
|
|
202
|
+
fields: definitions,
|
|
203
|
+
}),
|
|
204
|
+
);
|
|
205
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
|
|
206
|
+
const hex = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
207
|
+
"",
|
|
208
|
+
);
|
|
209
|
+
return `media-usage-seed:v1:sha256:${hex}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function canonicalizeFingerprintValue(value: unknown): unknown {
|
|
213
|
+
if (value === undefined) return { __emdashUndefined: true };
|
|
214
|
+
if (Array.isArray(value)) return value.map(canonicalizeFingerprintValue);
|
|
215
|
+
if (typeof value !== "object" || value === null) return value;
|
|
216
|
+
|
|
217
|
+
const canonical: Record<string, unknown> = {};
|
|
218
|
+
for (const [key, entry] of Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b))) {
|
|
219
|
+
canonical[key] = canonicalizeFingerprintValue(entry);
|
|
220
|
+
}
|
|
221
|
+
return canonical;
|
|
222
|
+
}
|
|
223
|
+
|
|
95
224
|
/**
|
|
96
225
|
* Error thrown when a schema operation fails
|
|
97
226
|
*/
|
|
@@ -126,6 +255,7 @@ export class SchemaRegistry {
|
|
|
126
255
|
const rows = await this.db
|
|
127
256
|
.selectFrom("_emdash_collections")
|
|
128
257
|
.selectAll()
|
|
258
|
+
.orderBy(collectionOrder, "asc")
|
|
129
259
|
.orderBy("slug", "asc")
|
|
130
260
|
.execute();
|
|
131
261
|
|
|
@@ -177,6 +307,7 @@ export class SchemaRegistry {
|
|
|
177
307
|
const collectionRows = await this.db
|
|
178
308
|
.selectFrom("_emdash_collections")
|
|
179
309
|
.selectAll()
|
|
310
|
+
.orderBy(collectionOrder, "asc")
|
|
180
311
|
.orderBy("slug", "asc")
|
|
181
312
|
.execute();
|
|
182
313
|
|
|
@@ -212,6 +343,61 @@ export class SchemaRegistry {
|
|
|
212
343
|
}));
|
|
213
344
|
}
|
|
214
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Validate `titleField`/`dateField` against the collection's fields:
|
|
348
|
+
* `titleField` must be a text-like field; `dateField` must be a `datetime` field.
|
|
349
|
+
* Only truthy values are checked (undefined = unchanged, null/"" = cleared).
|
|
350
|
+
*/
|
|
351
|
+
private async validateTitleDateFields(
|
|
352
|
+
collectionId: string,
|
|
353
|
+
collectionSlug: string,
|
|
354
|
+
input: { titleField?: string | null; dateField?: string | null },
|
|
355
|
+
db: Kysely<Database> = this.db,
|
|
356
|
+
): Promise<void> {
|
|
357
|
+
const slugs = [input.titleField, input.dateField].filter((slug): slug is string => !!slug);
|
|
358
|
+
if (slugs.length === 0) return;
|
|
359
|
+
|
|
360
|
+
const rows = await db
|
|
361
|
+
.selectFrom("_emdash_fields")
|
|
362
|
+
.where("collection_id", "=", collectionId)
|
|
363
|
+
.where("slug", "in", slugs)
|
|
364
|
+
.select(["slug", "type"])
|
|
365
|
+
.execute();
|
|
366
|
+
const typeBySlug = new Map(rows.map((row) => [row.slug, row.type]));
|
|
367
|
+
|
|
368
|
+
if (input.titleField) {
|
|
369
|
+
const type = typeBySlug.get(input.titleField);
|
|
370
|
+
if (type === undefined) {
|
|
371
|
+
throw new SchemaError(
|
|
372
|
+
`titleField "${input.titleField}" is not a field on "${collectionSlug}"`,
|
|
373
|
+
"INVALID_TITLE_FIELD",
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
if (!TITLE_FIELD_TYPES.has(type)) {
|
|
377
|
+
throw new SchemaError(
|
|
378
|
+
`titleField "${input.titleField}" must be a text field (got "${type}")`,
|
|
379
|
+
"INVALID_TITLE_FIELD",
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (input.dateField) {
|
|
385
|
+
const type = typeBySlug.get(input.dateField);
|
|
386
|
+
if (type === undefined) {
|
|
387
|
+
throw new SchemaError(
|
|
388
|
+
`dateField "${input.dateField}" is not a field on "${collectionSlug}"`,
|
|
389
|
+
"INVALID_DATE_FIELD",
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
if (type !== "datetime") {
|
|
393
|
+
throw new SchemaError(
|
|
394
|
+
`dateField "${input.dateField}" must be a datetime field (got "${type}")`,
|
|
395
|
+
"INVALID_DATE_FIELD",
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
215
401
|
/**
|
|
216
402
|
* Create a new collection
|
|
217
403
|
*/
|
|
@@ -221,14 +407,26 @@ export class SchemaRegistry {
|
|
|
221
407
|
if (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {
|
|
222
408
|
throw new SchemaError(`Collection slug "${input.slug}" is reserved`, "RESERVED_SLUG");
|
|
223
409
|
}
|
|
410
|
+
if (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {
|
|
411
|
+
throw new SchemaError(`Collection "${input.slug}" already exists`, "COLLECTION_EXISTS");
|
|
412
|
+
}
|
|
224
413
|
|
|
225
414
|
// Check if collection already exists
|
|
226
415
|
const existing = await this.getCollection(input.slug);
|
|
227
|
-
if (
|
|
416
|
+
if (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {
|
|
417
|
+
throw new SchemaError(`Collection "${input.slug}" already exists`, "COLLECTION_EXISTS");
|
|
418
|
+
}
|
|
419
|
+
if (
|
|
420
|
+
existing &&
|
|
421
|
+
!(await canResumeMediaUsageCollectionCapture(this.db, {
|
|
422
|
+
collectionId: existing.id,
|
|
423
|
+
collectionSlug: existing.slug,
|
|
424
|
+
}))
|
|
425
|
+
) {
|
|
228
426
|
throw new SchemaError(`Collection "${input.slug}" already exists`, "COLLECTION_EXISTS");
|
|
229
427
|
}
|
|
230
428
|
|
|
231
|
-
const
|
|
429
|
+
const proposedId = existing?.id ?? ulid();
|
|
232
430
|
|
|
233
431
|
// Default `supports` to drafts + revisions when the caller didn't
|
|
234
432
|
// specify it. Explicit empty array (`[]`) is preserved as an opt-out
|
|
@@ -244,24 +442,52 @@ export class SchemaRegistry {
|
|
|
244
442
|
const hasSeo = input.hasSeo ?? supports.includes("seo") ?? false;
|
|
245
443
|
|
|
246
444
|
await withTransaction(this.db, async (trx) => {
|
|
247
|
-
await trx
|
|
248
|
-
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
445
|
+
const capture = await prepareMediaUsageCollectionCapture(trx, {
|
|
446
|
+
collectionId: proposedId,
|
|
447
|
+
collectionSlug: input.slug,
|
|
448
|
+
registeredCollectionId: existing?.id,
|
|
449
|
+
});
|
|
450
|
+
const values: Insertable<CollectionTable> = {
|
|
451
|
+
id: capture.collectionId,
|
|
452
|
+
slug: input.slug,
|
|
453
|
+
label: input.label,
|
|
454
|
+
label_singular: input.labelSingular ?? null,
|
|
455
|
+
description: input.description ?? null,
|
|
456
|
+
icon: input.icon ?? null,
|
|
457
|
+
admin_config: input.admin ? JSON.stringify(input.admin) : null,
|
|
458
|
+
supports: JSON.stringify(supports),
|
|
459
|
+
source: input.source ?? "manual",
|
|
460
|
+
has_seo: hasSeo ? 1 : 0,
|
|
461
|
+
routable: input.routable === false ? 0 : 1,
|
|
462
|
+
hidden: input.hidden ? 1 : 0,
|
|
463
|
+
sort_order: input.sortOrder ?? null,
|
|
464
|
+
comments_enabled: input.commentsEnabled ? 1 : 0,
|
|
465
|
+
url_pattern: input.urlPattern ?? null,
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
if (capture.captureRequired) {
|
|
469
|
+
await this.createContentTable(input.slug, trx, [], {
|
|
470
|
+
ifNotExists: capture.resuming,
|
|
471
|
+
});
|
|
472
|
+
await installPreparedMediaUsageCollectionCapture(trx, {
|
|
473
|
+
collectionId: capture.collectionId,
|
|
474
|
+
collectionSlug: input.slug,
|
|
475
|
+
});
|
|
476
|
+
await markMediaUsageCollectionCaptureReady(trx, {
|
|
477
|
+
collectionId: capture.collectionId,
|
|
478
|
+
collectionSlug: input.slug,
|
|
479
|
+
});
|
|
480
|
+
if (!capture.registrationExists) {
|
|
481
|
+
await trx.insertInto("_emdash_collections").values(values).execute();
|
|
482
|
+
}
|
|
483
|
+
await finalizeMediaUsageCollectionCapture(trx, {
|
|
484
|
+
collectionId: capture.collectionId,
|
|
485
|
+
collectionSlug: input.slug,
|
|
486
|
+
});
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
263
489
|
|
|
264
|
-
|
|
490
|
+
await trx.insertInto("_emdash_collections").values(values).execute();
|
|
265
491
|
await this.createContentTable(input.slug, trx);
|
|
266
492
|
});
|
|
267
493
|
|
|
@@ -291,15 +517,14 @@ export class SchemaRegistry {
|
|
|
291
517
|
if (RESERVED_COLLECTION_SLUGS.includes(input.slug)) {
|
|
292
518
|
throw new SchemaError(`Collection slug "${input.slug}" is reserved`, "RESERVED_SLUG");
|
|
293
519
|
}
|
|
294
|
-
|
|
295
|
-
const existing = await this.getCollection(input.slug);
|
|
296
|
-
if (existing) {
|
|
520
|
+
if (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {
|
|
297
521
|
throw new SchemaError(`Collection "${input.slug}" already exists`, "COLLECTION_EXISTS");
|
|
298
522
|
}
|
|
299
523
|
|
|
300
524
|
const fieldSlugs = new Set<string>();
|
|
301
525
|
for (const field of fields) {
|
|
302
526
|
this.validateSlug(field.slug, "field");
|
|
527
|
+
assertIndexableField(field.type, field.indexed, field.slug);
|
|
303
528
|
if (RESERVED_FIELD_SLUGS.includes(field.slug)) {
|
|
304
529
|
throw new SchemaError(`Field slug "${field.slug}" is reserved`, "RESERVED_SLUG");
|
|
305
530
|
}
|
|
@@ -312,9 +537,25 @@ export class SchemaRegistry {
|
|
|
312
537
|
fieldSlugs.add(field.slug);
|
|
313
538
|
}
|
|
314
539
|
|
|
315
|
-
const collectionId = ulid();
|
|
316
540
|
const supports = input.supports ?? ["drafts", "revisions"];
|
|
317
541
|
const hasSeo = input.hasSeo ?? supports.includes("seo") ?? false;
|
|
542
|
+
const creationFingerprint = await buildSeedCollectionCaptureFingerprint(input, fields);
|
|
543
|
+
const existing = await this.getCollection(input.slug);
|
|
544
|
+
if (await isMediaUsageCollectionSlugDeleting(this.db, input.slug)) {
|
|
545
|
+
throw new SchemaError(`Collection "${input.slug}" already exists`, "COLLECTION_EXISTS");
|
|
546
|
+
}
|
|
547
|
+
if (
|
|
548
|
+
existing &&
|
|
549
|
+
!(await canResumeMediaUsageCollectionCapture(this.db, {
|
|
550
|
+
collectionId: existing.id,
|
|
551
|
+
collectionSlug: existing.slug,
|
|
552
|
+
creationFingerprint,
|
|
553
|
+
}))
|
|
554
|
+
) {
|
|
555
|
+
throw new SchemaError(`Collection "${input.slug}" already exists`, "COLLECTION_EXISTS");
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const proposedCollectionId = existing?.id ?? ulid();
|
|
318
559
|
let maxSortOrder = -1;
|
|
319
560
|
const fieldRows: Insertable<FieldTable>[] = fields.map((field) => {
|
|
320
561
|
const sortOrder = field.sortOrder ?? maxSortOrder + 1;
|
|
@@ -322,7 +563,7 @@ export class SchemaRegistry {
|
|
|
322
563
|
|
|
323
564
|
return {
|
|
324
565
|
id: ulid(),
|
|
325
|
-
collection_id:
|
|
566
|
+
collection_id: proposedCollectionId,
|
|
326
567
|
slug: field.slug,
|
|
327
568
|
label: field.label,
|
|
328
569
|
type: field.type,
|
|
@@ -335,6 +576,7 @@ export class SchemaRegistry {
|
|
|
335
576
|
options: field.options ? JSON.stringify(field.options) : null,
|
|
336
577
|
sort_order: sortOrder,
|
|
337
578
|
searchable: field.searchable ? 1 : 0,
|
|
579
|
+
indexed: field.indexed ? 1 : 0,
|
|
338
580
|
translatable: field.translatable === false ? 0 : 1,
|
|
339
581
|
};
|
|
340
582
|
});
|
|
@@ -342,28 +584,79 @@ export class SchemaRegistry {
|
|
|
342
584
|
let schemaMutated = false;
|
|
343
585
|
try {
|
|
344
586
|
await withTransaction(this.db, async (trx) => {
|
|
345
|
-
await trx
|
|
346
|
-
|
|
347
|
-
.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
587
|
+
const capture = await prepareMediaUsageCollectionCapture(trx, {
|
|
588
|
+
collectionId: proposedCollectionId,
|
|
589
|
+
collectionSlug: input.slug,
|
|
590
|
+
creationFingerprint,
|
|
591
|
+
registeredCollectionId: existing?.id,
|
|
592
|
+
});
|
|
593
|
+
const collectionValues: Insertable<CollectionTable> = {
|
|
594
|
+
id: capture.collectionId,
|
|
595
|
+
slug: input.slug,
|
|
596
|
+
label: input.label,
|
|
597
|
+
label_singular: input.labelSingular ?? null,
|
|
598
|
+
description: input.description ?? null,
|
|
599
|
+
icon: input.icon ?? null,
|
|
600
|
+
admin_config: input.admin ? JSON.stringify(input.admin) : null,
|
|
601
|
+
supports: JSON.stringify(supports),
|
|
602
|
+
source: "seed",
|
|
603
|
+
has_seo: hasSeo ? 1 : 0,
|
|
604
|
+
routable: input.routable === false ? 0 : 1,
|
|
605
|
+
hidden: input.hidden ? 1 : 0,
|
|
606
|
+
sort_order: input.sortOrder ?? null,
|
|
607
|
+
comments_enabled: input.commentsEnabled ? 1 : 0,
|
|
608
|
+
url_pattern: input.urlPattern ?? null,
|
|
609
|
+
};
|
|
610
|
+
const rows = fieldRows.map((row) => ({
|
|
611
|
+
...row,
|
|
612
|
+
collection_id: capture.collectionId,
|
|
613
|
+
}));
|
|
614
|
+
|
|
615
|
+
if (capture.captureRequired) {
|
|
616
|
+
await this.createContentTable(input.slug, trx, fields, {
|
|
617
|
+
ifNotExists: capture.resuming,
|
|
618
|
+
});
|
|
619
|
+
await installPreparedMediaUsageCollectionCapture(trx, {
|
|
620
|
+
collectionId: capture.collectionId,
|
|
621
|
+
collectionSlug: input.slug,
|
|
622
|
+
});
|
|
623
|
+
await markMediaUsageCollectionCaptureReady(trx, {
|
|
624
|
+
collectionId: capture.collectionId,
|
|
625
|
+
collectionSlug: input.slug,
|
|
626
|
+
});
|
|
627
|
+
if (!capture.registrationExists) {
|
|
628
|
+
await trx.insertInto("_emdash_collections").values(collectionValues).execute();
|
|
629
|
+
schemaMutated = true;
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
await trx.insertInto("_emdash_collections").values(collectionValues).execute();
|
|
633
|
+
schemaMutated = true;
|
|
634
|
+
await this.createContentTable(input.slug, trx, fields);
|
|
635
|
+
}
|
|
364
636
|
|
|
365
|
-
for (const fieldBatch of chunks(
|
|
366
|
-
|
|
637
|
+
for (const fieldBatch of chunks(rows, SEED_FIELD_INSERT_BATCH_SIZE)) {
|
|
638
|
+
let insert = trx.insertInto("_emdash_fields").values(fieldBatch);
|
|
639
|
+
if (capture.resuming) {
|
|
640
|
+
insert = insert.onConflict((conflict) =>
|
|
641
|
+
conflict.columns(["collection_id", "slug"]).doNothing(),
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
await insert.execute();
|
|
645
|
+
}
|
|
646
|
+
let indexedRows: readonly { id: string; slug: string; indexed?: number }[] = rows;
|
|
647
|
+
if (capture.resuming) {
|
|
648
|
+
indexedRows = await this.assertSeedFieldDefinitions(capture.collectionId, fields, trx);
|
|
649
|
+
}
|
|
650
|
+
for (const field of indexedRows) {
|
|
651
|
+
if (field.indexed === 1) {
|
|
652
|
+
await this.createFieldIndex(input.slug, field.id, field.slug, trx);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (capture.captureRequired) {
|
|
656
|
+
await finalizeMediaUsageCollectionCapture(trx, {
|
|
657
|
+
collectionId: capture.collectionId,
|
|
658
|
+
collectionSlug: input.slug,
|
|
659
|
+
});
|
|
367
660
|
}
|
|
368
661
|
});
|
|
369
662
|
|
|
@@ -380,66 +673,108 @@ export class SchemaRegistry {
|
|
|
380
673
|
}
|
|
381
674
|
}
|
|
382
675
|
|
|
676
|
+
private async assertSeedFieldDefinitions(
|
|
677
|
+
collectionId: string,
|
|
678
|
+
fields: readonly CreateFieldInput[],
|
|
679
|
+
db: Kysely<Database>,
|
|
680
|
+
): Promise<Selectable<FieldTable>[]> {
|
|
681
|
+
const stored = await db
|
|
682
|
+
.selectFrom("_emdash_fields")
|
|
683
|
+
.selectAll()
|
|
684
|
+
.where("collection_id", "=", collectionId)
|
|
685
|
+
.execute();
|
|
686
|
+
if (stored.length !== fields.length) {
|
|
687
|
+
throw new SchemaError("Interrupted seed collection fields do not match", "CREATE_FAILED");
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
let maxSortOrder = -1;
|
|
691
|
+
for (const field of fields) {
|
|
692
|
+
const sortOrder = field.sortOrder ?? maxSortOrder + 1;
|
|
693
|
+
maxSortOrder = Math.max(maxSortOrder, sortOrder);
|
|
694
|
+
const row = stored.find((candidate) => candidate.slug === field.slug);
|
|
695
|
+
if (
|
|
696
|
+
!row ||
|
|
697
|
+
row.label !== field.label ||
|
|
698
|
+
row.type !== field.type ||
|
|
699
|
+
row.column_type !== FIELD_TYPE_TO_COLUMN[field.type] ||
|
|
700
|
+
row.required !== (field.required ? 1 : 0) ||
|
|
701
|
+
row.unique !== (field.unique ? 1 : 0) ||
|
|
702
|
+
row.default_value !==
|
|
703
|
+
(field.defaultValue !== undefined ? JSON.stringify(field.defaultValue) : null) ||
|
|
704
|
+
row.validation !== (field.validation ? JSON.stringify(field.validation) : null) ||
|
|
705
|
+
row.widget !== (field.widget ?? null) ||
|
|
706
|
+
row.options !== (field.options ? JSON.stringify(field.options) : null) ||
|
|
707
|
+
row.sort_order !== sortOrder ||
|
|
708
|
+
row.searchable !== (field.searchable ? 1 : 0) ||
|
|
709
|
+
row.indexed !== (field.indexed ? 1 : 0) ||
|
|
710
|
+
row.translatable !== (field.translatable === false ? 0 : 1)
|
|
711
|
+
) {
|
|
712
|
+
throw new SchemaError("Interrupted seed collection fields do not match", "CREATE_FAILED");
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return stored;
|
|
716
|
+
}
|
|
717
|
+
|
|
383
718
|
/**
|
|
384
719
|
* Update a collection
|
|
385
720
|
*/
|
|
386
721
|
async updateCollection(slug: string, input: UpdateCollectionInput): Promise<Collection> {
|
|
387
|
-
const existing = await this.getCollection(slug);
|
|
388
|
-
if (!existing) {
|
|
389
|
-
throw new SchemaError(`Collection "${slug}" not found`, "COLLECTION_NOT_FOUND");
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const now = new Date().toISOString();
|
|
393
|
-
|
|
394
|
-
// Derive hasSeo from supports array if supports is being updated and hasSeo not explicitly set
|
|
395
|
-
const supportsArray = input.supports ?? existing.supports;
|
|
396
|
-
const hasSeo =
|
|
397
|
-
input.hasSeo !== undefined
|
|
398
|
-
? input.hasSeo
|
|
399
|
-
: input.supports !== undefined
|
|
400
|
-
? supportsArray.includes("seo")
|
|
401
|
-
: existing.hasSeo;
|
|
402
|
-
|
|
403
722
|
return withTransaction(this.db, async (trx) => {
|
|
723
|
+
const existingRow = await trx
|
|
724
|
+
.selectFrom("_emdash_collections")
|
|
725
|
+
.where("slug", "=", slug)
|
|
726
|
+
.selectAll()
|
|
727
|
+
.executeTakeFirst();
|
|
728
|
+
if (!existingRow) {
|
|
729
|
+
throw new SchemaError(`Collection "${slug}" not found`, "COLLECTION_NOT_FOUND");
|
|
730
|
+
}
|
|
731
|
+
const existing = this.mapCollectionRow(existingRow);
|
|
732
|
+
await this.validateTitleDateFields(
|
|
733
|
+
existing.id,
|
|
734
|
+
slug,
|
|
735
|
+
{
|
|
736
|
+
titleField: input.titleField,
|
|
737
|
+
dateField: input.dateField,
|
|
738
|
+
},
|
|
739
|
+
trx,
|
|
740
|
+
);
|
|
741
|
+
const updates: Updateable<CollectionTable> = {};
|
|
742
|
+
|
|
743
|
+
if (input.label !== undefined) updates.label = input.label;
|
|
744
|
+
if (input.labelSingular !== undefined) updates.label_singular = input.labelSingular;
|
|
745
|
+
if (input.description !== undefined) updates.description = input.description;
|
|
746
|
+
if (input.icon !== undefined) updates.icon = input.icon;
|
|
747
|
+
if (input.admin !== undefined) updates.admin_config = JSON.stringify(input.admin);
|
|
748
|
+
if (input.supports !== undefined) updates.supports = JSON.stringify(input.supports);
|
|
749
|
+
if (input.urlPattern !== undefined) updates.url_pattern = input.urlPattern;
|
|
750
|
+
if (input.routable !== undefined) updates.routable = input.routable ? 1 : 0;
|
|
751
|
+
if (input.hasSeo !== undefined) {
|
|
752
|
+
updates.has_seo = input.hasSeo ? 1 : 0;
|
|
753
|
+
} else if (input.supports !== undefined) {
|
|
754
|
+
updates.has_seo = input.supports.includes("seo") ? 1 : 0;
|
|
755
|
+
}
|
|
756
|
+
if (input.hidden !== undefined) updates.hidden = input.hidden ? 1 : 0;
|
|
757
|
+
if (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;
|
|
758
|
+
if (input.titleField !== undefined) updates.title_field = input.titleField || null;
|
|
759
|
+
if (input.dateField !== undefined) updates.date_field = input.dateField || null;
|
|
760
|
+
if (input.commentsEnabled !== undefined) {
|
|
761
|
+
updates.comments_enabled = input.commentsEnabled ? 1 : 0;
|
|
762
|
+
}
|
|
763
|
+
if (input.commentsModeration !== undefined) {
|
|
764
|
+
updates.comments_moderation = input.commentsModeration;
|
|
765
|
+
}
|
|
766
|
+
if (input.commentsClosedAfterDays !== undefined) {
|
|
767
|
+
updates.comments_closed_after_days = input.commentsClosedAfterDays;
|
|
768
|
+
}
|
|
769
|
+
if (input.commentsAutoApproveUsers !== undefined) {
|
|
770
|
+
updates.comments_auto_approve_users = input.commentsAutoApproveUsers ? 1 : 0;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
updates.updated_at = new Date().toISOString();
|
|
404
774
|
await trx
|
|
405
775
|
.updateTable("_emdash_collections")
|
|
406
|
-
.set(
|
|
407
|
-
|
|
408
|
-
label_singular: input.labelSingular ?? existing.labelSingular ?? null,
|
|
409
|
-
description: input.description ?? existing.description ?? null,
|
|
410
|
-
icon: input.icon ?? existing.icon ?? null,
|
|
411
|
-
supports: input.supports
|
|
412
|
-
? JSON.stringify(input.supports)
|
|
413
|
-
: JSON.stringify(existing.supports),
|
|
414
|
-
url_pattern:
|
|
415
|
-
input.urlPattern !== undefined
|
|
416
|
-
? (input.urlPattern ?? null)
|
|
417
|
-
: (existing.urlPattern ?? null),
|
|
418
|
-
has_seo: hasSeo ? 1 : 0,
|
|
419
|
-
comments_enabled:
|
|
420
|
-
input.commentsEnabled !== undefined
|
|
421
|
-
? input.commentsEnabled
|
|
422
|
-
? 1
|
|
423
|
-
: 0
|
|
424
|
-
: existing.commentsEnabled
|
|
425
|
-
? 1
|
|
426
|
-
: 0,
|
|
427
|
-
comments_moderation: input.commentsModeration ?? existing.commentsModeration,
|
|
428
|
-
comments_closed_after_days:
|
|
429
|
-
input.commentsClosedAfterDays !== undefined
|
|
430
|
-
? input.commentsClosedAfterDays
|
|
431
|
-
: existing.commentsClosedAfterDays,
|
|
432
|
-
comments_auto_approve_users:
|
|
433
|
-
input.commentsAutoApproveUsers !== undefined
|
|
434
|
-
? input.commentsAutoApproveUsers
|
|
435
|
-
? 1
|
|
436
|
-
: 0
|
|
437
|
-
: existing.commentsAutoApproveUsers
|
|
438
|
-
? 1
|
|
439
|
-
: 0,
|
|
440
|
-
updated_at: now,
|
|
441
|
-
})
|
|
442
|
-
.where("slug", "=", slug)
|
|
776
|
+
.set(updates)
|
|
777
|
+
.where("id", "=", existing.id)
|
|
443
778
|
.execute();
|
|
444
779
|
|
|
445
780
|
const row = await trx
|
|
@@ -470,21 +805,31 @@ export class SchemaRegistry {
|
|
|
470
805
|
*/
|
|
471
806
|
async deleteCollection(slug: string, options?: { force?: boolean }): Promise<void> {
|
|
472
807
|
const existing = await this.getCollection(slug);
|
|
808
|
+
if (existing && !options?.force && (await this.collectionHasContent(slug))) {
|
|
809
|
+
throw new SchemaError(
|
|
810
|
+
`Collection "${slug}" has content. Use force: true to delete.`,
|
|
811
|
+
"COLLECTION_HAS_CONTENT",
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
const activated = await deleteActivatedMediaUsageCollection(this.db, {
|
|
815
|
+
collectionId: existing?.id,
|
|
816
|
+
collectionSlug: slug,
|
|
817
|
+
forceDelete: options?.force === true,
|
|
818
|
+
});
|
|
819
|
+
if (activated === "has_content") {
|
|
820
|
+
throw new SchemaError(
|
|
821
|
+
`Collection "${slug}" has content. Use force: true to delete.`,
|
|
822
|
+
"COLLECTION_HAS_CONTENT",
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
if (activated === "in_progress") {
|
|
826
|
+
throw new SchemaError(`Collection "${slug}" deletion is already in progress`, "CONFLICT");
|
|
827
|
+
}
|
|
828
|
+
if (activated === "deleted") return;
|
|
473
829
|
if (!existing) {
|
|
474
830
|
throw new SchemaError(`Collection "${slug}" not found`, "COLLECTION_NOT_FOUND");
|
|
475
831
|
}
|
|
476
832
|
|
|
477
|
-
// Check if collection has content
|
|
478
|
-
if (!options?.force) {
|
|
479
|
-
const hasContent = await this.collectionHasContent(slug);
|
|
480
|
-
if (hasContent) {
|
|
481
|
-
throw new SchemaError(
|
|
482
|
-
`Collection "${slug}" has content. Use force: true to delete.`,
|
|
483
|
-
"COLLECTION_HAS_CONTENT",
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
833
|
let contentTableDropped = false;
|
|
489
834
|
try {
|
|
490
835
|
await withTransaction(this.db, async (trx) => {
|
|
@@ -571,6 +916,7 @@ export class SchemaRegistry {
|
|
|
571
916
|
|
|
572
917
|
const id = ulid();
|
|
573
918
|
const columnType = FIELD_TYPE_TO_COLUMN[input.type];
|
|
919
|
+
assertIndexableField(input.type, input.indexed, input.slug);
|
|
574
920
|
|
|
575
921
|
// Get max sort order
|
|
576
922
|
const maxSort = await this.db
|
|
@@ -580,6 +926,10 @@ export class SchemaRegistry {
|
|
|
580
926
|
.executeTakeFirst();
|
|
581
927
|
|
|
582
928
|
const sortOrder = input.sortOrder ?? (maxSort?.max ?? -1) + 1;
|
|
929
|
+
const activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(
|
|
930
|
+
this.db,
|
|
931
|
+
collectionSlug,
|
|
932
|
+
);
|
|
583
933
|
|
|
584
934
|
let schemaMutated = false;
|
|
585
935
|
try {
|
|
@@ -603,6 +953,7 @@ export class SchemaRegistry {
|
|
|
603
953
|
options: input.options ? JSON.stringify(input.options) : null,
|
|
604
954
|
sort_order: sortOrder,
|
|
605
955
|
searchable: input.searchable ? 1 : 0,
|
|
956
|
+
indexed: input.indexed ? 1 : 0,
|
|
606
957
|
translatable: input.translatable === false ? 0 : 1,
|
|
607
958
|
})
|
|
608
959
|
.execute();
|
|
@@ -620,6 +971,10 @@ export class SchemaRegistry {
|
|
|
620
971
|
trx,
|
|
621
972
|
);
|
|
622
973
|
|
|
974
|
+
if (input.indexed) {
|
|
975
|
+
await this.createFieldIndex(collectionSlug, id, input.slug, trx);
|
|
976
|
+
}
|
|
977
|
+
|
|
623
978
|
// Read the created field via trx (not this.db) to avoid connection mutex deadlock
|
|
624
979
|
const fieldRow = await trx
|
|
625
980
|
.selectFrom("_emdash_fields")
|
|
@@ -641,20 +996,28 @@ export class SchemaRegistry {
|
|
|
641
996
|
|
|
642
997
|
return field;
|
|
643
998
|
});
|
|
644
|
-
|
|
645
|
-
this.db,
|
|
646
|
-
|
|
647
|
-
"CONTENT_USAGE_STALE",
|
|
648
|
-
);
|
|
649
|
-
return created;
|
|
650
|
-
} catch (error) {
|
|
651
|
-
if (schemaMutated) {
|
|
999
|
+
if (activeCoverageInvalidated) {
|
|
1000
|
+
await invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);
|
|
1001
|
+
} else {
|
|
652
1002
|
await markContentMediaUsageCollectionStaleSafely(
|
|
653
1003
|
this.db,
|
|
654
1004
|
collectionSlug,
|
|
655
1005
|
"CONTENT_USAGE_STALE",
|
|
656
1006
|
);
|
|
657
1007
|
}
|
|
1008
|
+
return created;
|
|
1009
|
+
} catch (error) {
|
|
1010
|
+
if (schemaMutated) {
|
|
1011
|
+
if (activeCoverageInvalidated) {
|
|
1012
|
+
await invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);
|
|
1013
|
+
} else {
|
|
1014
|
+
await markContentMediaUsageCollectionStaleSafely(
|
|
1015
|
+
this.db,
|
|
1016
|
+
collectionSlug,
|
|
1017
|
+
"CONTENT_USAGE_STALE",
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
658
1021
|
throw error;
|
|
659
1022
|
}
|
|
660
1023
|
}
|
|
@@ -667,87 +1030,126 @@ export class SchemaRegistry {
|
|
|
667
1030
|
fieldSlug: string,
|
|
668
1031
|
input: UpdateFieldInput,
|
|
669
1032
|
): Promise<Field> {
|
|
670
|
-
|
|
671
|
-
if (!field) {
|
|
672
|
-
throw new SchemaError(
|
|
673
|
-
`Field "${fieldSlug}" not found in collection "${collectionSlug}"`,
|
|
674
|
-
"FIELD_NOT_FOUND",
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// `input.validation === undefined` means "no change" (keep existing);
|
|
679
|
-
// an explicit `null` clears the column.
|
|
680
|
-
const nextValidation = input.validation === undefined ? field.validation : input.validation;
|
|
681
|
-
|
|
682
|
-
// A field-type change is only safe when the underlying column type stays
|
|
683
|
-
// the same. There is no in-place column migration (only addColumn/
|
|
684
|
-
// dropColumn), so a change that would alter the SQLite column affinity
|
|
685
|
-
// (e.g. `text` TEXT -> `portableText` JSON) is rejected rather than
|
|
686
|
-
// silently rewriting only the metadata — which would leave `column_type`
|
|
687
|
-
// pointing at a column type the real `ec_*` column doesn't have (#1397).
|
|
688
|
-
let nextType = field.type;
|
|
689
|
-
let nextColumnType = field.columnType;
|
|
690
|
-
if (input.type !== undefined && input.type !== field.type) {
|
|
691
|
-
const newColumnType = FIELD_TYPE_TO_COLUMN[input.type];
|
|
692
|
-
if (newColumnType !== field.columnType) {
|
|
693
|
-
throw new SchemaError(
|
|
694
|
-
`Cannot change field "${fieldSlug}" in collection "${collectionSlug}" from type ` +
|
|
695
|
-
`"${field.type}" to "${input.type}": the underlying column type would change from ` +
|
|
696
|
-
`${field.columnType} to ${newColumnType}, which requires a manual content migration. ` +
|
|
697
|
-
`Drop and re-create the field, or migrate the column data, before changing its type.`,
|
|
698
|
-
"FIELD_TYPE_COLUMN_CHANGE",
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
nextType = input.type;
|
|
702
|
-
nextColumnType = newColumnType;
|
|
703
|
-
}
|
|
704
|
-
|
|
1033
|
+
let activeCoverageInvalidated = false;
|
|
705
1034
|
let schemaMutated = false;
|
|
706
1035
|
try {
|
|
707
1036
|
const updatedField = await withTransaction(this.db, async (trx) => {
|
|
708
|
-
await trx
|
|
709
|
-
.
|
|
710
|
-
.
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1037
|
+
const collectionRow = await trx
|
|
1038
|
+
.selectFrom("_emdash_collections")
|
|
1039
|
+
.where("slug", "=", collectionSlug)
|
|
1040
|
+
.select(["id", "title_field", "date_field"])
|
|
1041
|
+
.executeTakeFirst();
|
|
1042
|
+
const fieldRow = collectionRow
|
|
1043
|
+
? await trx
|
|
1044
|
+
.selectFrom("_emdash_fields")
|
|
1045
|
+
.where("collection_id", "=", collectionRow.id)
|
|
1046
|
+
.where("slug", "=", fieldSlug)
|
|
1047
|
+
.selectAll()
|
|
1048
|
+
.executeTakeFirst()
|
|
1049
|
+
: undefined;
|
|
1050
|
+
if (!fieldRow) {
|
|
1051
|
+
throw new SchemaError(
|
|
1052
|
+
`Field "${fieldSlug}" not found in collection "${collectionSlug}"`,
|
|
1053
|
+
"FIELD_NOT_FOUND",
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
const field = this.mapFieldRow(fieldRow);
|
|
1057
|
+
const updates: Updateable<FieldTable> = {};
|
|
1058
|
+
let nextType = field.type;
|
|
1059
|
+
|
|
1060
|
+
if (input.type !== undefined && input.type !== field.type) {
|
|
1061
|
+
const newColumnType = FIELD_TYPE_TO_COLUMN[input.type];
|
|
1062
|
+
if (newColumnType !== field.columnType) {
|
|
1063
|
+
throw new SchemaError(
|
|
1064
|
+
`Cannot change field "${fieldSlug}" in collection "${collectionSlug}" from type ` +
|
|
1065
|
+
`"${field.type}" to "${input.type}": the underlying column type would change from ` +
|
|
1066
|
+
`${field.columnType} to ${newColumnType}, which requires a manual content migration.`,
|
|
1067
|
+
"FIELD_TYPE_COLUMN_CHANGE",
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
if (!TEXT_ALIAS_FIELD_TYPES.has(field.type) || !TEXT_ALIAS_FIELD_TYPES.has(input.type)) {
|
|
1071
|
+
throw new SchemaError(
|
|
1072
|
+
`Cannot change field "${fieldSlug}" in collection "${collectionSlug}" from type ` +
|
|
1073
|
+
`"${field.type}" to "${input.type}" without a manual content migration.`,
|
|
1074
|
+
"FIELD_TYPE_CHANGE_REQUIRES_MIGRATION",
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1077
|
+
if (collectionRow?.title_field === fieldSlug && !TITLE_FIELD_TYPES.has(input.type)) {
|
|
1078
|
+
throw new SchemaError(
|
|
1079
|
+
`titleField "${fieldSlug}" must stay a text field, not "${input.type}"`,
|
|
1080
|
+
"INVALID_TITLE_FIELD",
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
if (collectionRow?.date_field === fieldSlug && input.type !== "datetime") {
|
|
1084
|
+
throw new SchemaError(
|
|
1085
|
+
`dateField "${fieldSlug}" must stay a datetime field, not "${input.type}"`,
|
|
1086
|
+
"INVALID_DATE_FIELD",
|
|
1087
|
+
);
|
|
1088
|
+
}
|
|
1089
|
+
nextType = input.type;
|
|
1090
|
+
updates.type = input.type;
|
|
1091
|
+
updates.column_type = newColumnType;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (input.required !== undefined && input.required !== field.required) {
|
|
1095
|
+
throw new SchemaError(
|
|
1096
|
+
`Changing required for field "${fieldSlug}" requires a manual content migration.`,
|
|
1097
|
+
"FIELD_UPDATE_REQUIRES_MIGRATION",
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
if (input.unique !== undefined && input.unique !== field.unique) {
|
|
1101
|
+
throw new SchemaError(
|
|
1102
|
+
`Changing unique for field "${fieldSlug}" requires a manual content migration.`,
|
|
1103
|
+
"FIELD_UPDATE_REQUIRES_MIGRATION",
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
if (input.translatable === false && field.translatable) {
|
|
1107
|
+
throw new SchemaError(
|
|
1108
|
+
`Changing field "${fieldSlug}" to non-translatable requires a manual content migration.`,
|
|
1109
|
+
"FIELD_UPDATE_REQUIRES_MIGRATION",
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if (input.label !== undefined) updates.label = input.label;
|
|
1114
|
+
if (input.required !== undefined) updates.required = input.required ? 1 : 0;
|
|
1115
|
+
if (input.unique !== undefined) updates.unique = input.unique ? 1 : 0;
|
|
1116
|
+
if (input.searchable !== undefined) updates.searchable = input.searchable ? 1 : 0;
|
|
1117
|
+
const indexedChanged = input.indexed !== undefined && input.indexed !== field.indexed;
|
|
1118
|
+
if (indexedChanged) updates.indexed = input.indexed ? 1 : 0;
|
|
1119
|
+
if (input.translatable !== undefined) {
|
|
1120
|
+
updates.translatable = input.translatable ? 1 : 0;
|
|
1121
|
+
}
|
|
1122
|
+
if (input.defaultValue !== undefined) {
|
|
1123
|
+
updates.default_value = JSON.stringify(input.defaultValue);
|
|
1124
|
+
}
|
|
1125
|
+
if (input.validation !== undefined) {
|
|
1126
|
+
updates.validation = input.validation ? JSON.stringify(input.validation) : null;
|
|
1127
|
+
}
|
|
1128
|
+
if (input.widget !== undefined) updates.widget = input.widget;
|
|
1129
|
+
if (input.options !== undefined) updates.options = JSON.stringify(input.options);
|
|
1130
|
+
if (input.sortOrder !== undefined) updates.sort_order = input.sortOrder;
|
|
1131
|
+
|
|
1132
|
+
assertIndexableField(nextType, input.indexed ?? field.indexed, fieldSlug);
|
|
1133
|
+
if (Object.keys(updates).length === 0) return field;
|
|
1134
|
+
|
|
1135
|
+
activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(
|
|
1136
|
+
trx,
|
|
1137
|
+
collectionSlug,
|
|
1138
|
+
);
|
|
1139
|
+
|
|
1140
|
+
if (Object.keys(updates).length > 0) {
|
|
1141
|
+
await trx.updateTable("_emdash_fields").set(updates).where("id", "=", field.id).execute();
|
|
1142
|
+
schemaMutated = true;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (indexedChanged) {
|
|
1146
|
+
if (input.indexed) {
|
|
1147
|
+
await this.createFieldIndex(collectionSlug, field.id, fieldSlug, trx);
|
|
1148
|
+
} else {
|
|
1149
|
+
await this.dropFieldIndex(field.id, trx);
|
|
1150
|
+
}
|
|
1151
|
+
schemaMutated = true;
|
|
1152
|
+
}
|
|
751
1153
|
|
|
752
1154
|
// Read the updated field via trx (not this.db) to avoid connection mutex deadlock
|
|
753
1155
|
const updatedRow = await trx
|
|
@@ -765,26 +1167,36 @@ export class SchemaRegistry {
|
|
|
765
1167
|
|
|
766
1168
|
// If searchable changed, sync FTS state for this collection
|
|
767
1169
|
const searchableChanged =
|
|
768
|
-
input.searchable !== undefined && input.searchable !== field.searchable;
|
|
1170
|
+
schemaMutated && input.searchable !== undefined && input.searchable !== field.searchable;
|
|
769
1171
|
if (searchableChanged) {
|
|
770
1172
|
await this.syncSearchState(collectionSlug, trx);
|
|
771
1173
|
}
|
|
772
1174
|
|
|
773
1175
|
return updated;
|
|
774
1176
|
});
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1177
|
+
if (schemaMutated) {
|
|
1178
|
+
if (activeCoverageInvalidated) {
|
|
1179
|
+
await invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);
|
|
1180
|
+
} else {
|
|
1181
|
+
await markContentMediaUsageCollectionStaleSafely(
|
|
1182
|
+
this.db,
|
|
1183
|
+
collectionSlug,
|
|
1184
|
+
"CONTENT_USAGE_STALE",
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
780
1188
|
return updatedField;
|
|
781
1189
|
} catch (error) {
|
|
782
1190
|
if (schemaMutated) {
|
|
783
|
-
|
|
784
|
-
this.db,
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1191
|
+
if (activeCoverageInvalidated) {
|
|
1192
|
+
await invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);
|
|
1193
|
+
} else {
|
|
1194
|
+
await markContentMediaUsageCollectionStaleSafely(
|
|
1195
|
+
this.db,
|
|
1196
|
+
collectionSlug,
|
|
1197
|
+
"CONTENT_USAGE_STALE",
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
788
1200
|
}
|
|
789
1201
|
throw error;
|
|
790
1202
|
}
|
|
@@ -822,7 +1234,12 @@ export class SchemaRegistry {
|
|
|
822
1234
|
const ftsActive = config?.enabled === true;
|
|
823
1235
|
|
|
824
1236
|
if (wantsSearch && searchableFields.length > 0 && ftsActive) {
|
|
825
|
-
await ftsManager.rebuildIndex(
|
|
1237
|
+
await ftsManager.rebuildIndex(
|
|
1238
|
+
collectionSlug,
|
|
1239
|
+
searchableFields,
|
|
1240
|
+
config?.weights,
|
|
1241
|
+
config?.tokenize,
|
|
1242
|
+
);
|
|
826
1243
|
} else if (ftsActive && (!wantsSearch || searchableFields.length === 0)) {
|
|
827
1244
|
await ftsManager.disableSearch(collectionSlug);
|
|
828
1245
|
}
|
|
@@ -839,6 +1256,17 @@ export class SchemaRegistry {
|
|
|
839
1256
|
"FIELD_NOT_FOUND",
|
|
840
1257
|
);
|
|
841
1258
|
}
|
|
1259
|
+
const activeCoverageInvalidated = await invalidateContentMediaUsageSchemaChange(
|
|
1260
|
+
this.db,
|
|
1261
|
+
collectionSlug,
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1264
|
+
// If this field powers the collection's titleField/dateField,
|
|
1265
|
+
// clear that reference in the same transaction — otherwise the metadata
|
|
1266
|
+
// would point at a dropped column and later crash the content list sort.
|
|
1267
|
+
const collection = await this.getCollection(collectionSlug);
|
|
1268
|
+
const clearTitle = collection?.titleField === fieldSlug;
|
|
1269
|
+
const clearDate = collection?.dateField === fieldSlug;
|
|
842
1270
|
|
|
843
1271
|
let schemaMutated = false;
|
|
844
1272
|
try {
|
|
@@ -851,31 +1279,109 @@ export class SchemaRegistry {
|
|
|
851
1279
|
await trx.deleteFrom("_emdash_fields").where("id", "=", field.id).execute();
|
|
852
1280
|
schemaMutated = true;
|
|
853
1281
|
|
|
1282
|
+
if (clearTitle || clearDate) {
|
|
1283
|
+
await trx
|
|
1284
|
+
.updateTable("_emdash_collections")
|
|
1285
|
+
.set({
|
|
1286
|
+
...(clearTitle ? { title_field: null } : {}),
|
|
1287
|
+
...(clearDate ? { date_field: null } : {}),
|
|
1288
|
+
updated_at: new Date().toISOString(),
|
|
1289
|
+
})
|
|
1290
|
+
.where("slug", "=", collectionSlug)
|
|
1291
|
+
.execute();
|
|
1292
|
+
}
|
|
1293
|
+
|
|
854
1294
|
// If the deleted field was searchable, sync FTS state (removes old triggers)
|
|
855
1295
|
if (field.searchable) {
|
|
856
1296
|
await this.syncSearchState(collectionSlug, trx);
|
|
857
1297
|
}
|
|
858
1298
|
|
|
1299
|
+
if (field.indexed) {
|
|
1300
|
+
await this.dropFieldIndex(field.id, trx);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
859
1303
|
// Drop column from content table — safe now because FTS triggers are gone
|
|
860
1304
|
await this.dropColumn(collectionSlug, fieldSlug, trx);
|
|
861
1305
|
});
|
|
862
|
-
|
|
863
|
-
this.db,
|
|
864
|
-
|
|
865
|
-
"CONTENT_USAGE_STALE",
|
|
866
|
-
);
|
|
867
|
-
} catch (error) {
|
|
868
|
-
if (schemaMutated) {
|
|
1306
|
+
if (activeCoverageInvalidated) {
|
|
1307
|
+
await invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);
|
|
1308
|
+
} else {
|
|
869
1309
|
await markContentMediaUsageCollectionStaleSafely(
|
|
870
1310
|
this.db,
|
|
871
1311
|
collectionSlug,
|
|
872
1312
|
"CONTENT_USAGE_STALE",
|
|
873
1313
|
);
|
|
874
1314
|
}
|
|
1315
|
+
} catch (error) {
|
|
1316
|
+
if (schemaMutated) {
|
|
1317
|
+
if (activeCoverageInvalidated) {
|
|
1318
|
+
await invalidateContentMediaUsageSchemaChange(this.db, collectionSlug);
|
|
1319
|
+
} else {
|
|
1320
|
+
await markContentMediaUsageCollectionStaleSafely(
|
|
1321
|
+
this.db,
|
|
1322
|
+
collectionSlug,
|
|
1323
|
+
"CONTENT_USAGE_STALE",
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
875
1327
|
throw error;
|
|
876
1328
|
}
|
|
877
1329
|
}
|
|
878
1330
|
|
|
1331
|
+
/**
|
|
1332
|
+
* Reorder collections in the admin sidebar.
|
|
1333
|
+
*
|
|
1334
|
+
* `slugs` is the full desired order: every listed collection gets its
|
|
1335
|
+
* index as `sort_order`, and any collection left out has its explicit
|
|
1336
|
+
* position cleared, dropping it back to the alphabetical tail. Unknown or
|
|
1337
|
+
* duplicate slugs throw before anything is written.
|
|
1338
|
+
*/
|
|
1339
|
+
async reorderCollections(slugs: string[]): Promise<void> {
|
|
1340
|
+
const known = new Set((await this.listCollections()).map((collection) => collection.slug));
|
|
1341
|
+
|
|
1342
|
+
const unknown = slugs.filter((slug) => !known.has(slug));
|
|
1343
|
+
if (unknown.length > 0) {
|
|
1344
|
+
throw new SchemaError(
|
|
1345
|
+
`Unknown collection${unknown.length > 1 ? "s" : ""}: ${unknown.join(", ")}`,
|
|
1346
|
+
"COLLECTION_NOT_FOUND",
|
|
1347
|
+
{ slugs: unknown },
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
const duplicates = slugs.filter((slug, index) => slugs.indexOf(slug) !== index);
|
|
1352
|
+
if (duplicates.length > 0) {
|
|
1353
|
+
throw new SchemaError(
|
|
1354
|
+
`Duplicate collection${duplicates.length > 1 ? "s" : ""}: ${[...new Set(duplicates)].join(", ")}`,
|
|
1355
|
+
"DUPLICATE_SLUG",
|
|
1356
|
+
{ slugs: [...new Set(duplicates)] },
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
const now = new Date().toISOString();
|
|
1361
|
+
const ordered = new Set(slugs);
|
|
1362
|
+
|
|
1363
|
+
await withTransaction(this.db, async (trx) => {
|
|
1364
|
+
for (const [index, slug] of slugs.entries()) {
|
|
1365
|
+
await trx
|
|
1366
|
+
.updateTable("_emdash_collections")
|
|
1367
|
+
.set({ sort_order: index, updated_at: now })
|
|
1368
|
+
.where("slug", "=", slug)
|
|
1369
|
+
.execute();
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const cleared = [...known].filter((slug) => !ordered.has(slug));
|
|
1373
|
+
// Chunked to stay under D1's bound-parameter limit; typical sites
|
|
1374
|
+
// clear far fewer than one chunk.
|
|
1375
|
+
for (const slugChunk of chunks(cleared, SQL_BATCH_SIZE)) {
|
|
1376
|
+
await trx
|
|
1377
|
+
.updateTable("_emdash_collections")
|
|
1378
|
+
.set({ sort_order: null, updated_at: now })
|
|
1379
|
+
.where("slug", "in", slugChunk)
|
|
1380
|
+
.execute();
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
|
|
879
1385
|
/**
|
|
880
1386
|
* Reorder fields
|
|
881
1387
|
*/
|
|
@@ -907,6 +1413,7 @@ export class SchemaRegistry {
|
|
|
907
1413
|
slug: string,
|
|
908
1414
|
db?: Kysely<Database>,
|
|
909
1415
|
fields: readonly CreateFieldInput[] = [],
|
|
1416
|
+
options: { ifNotExists?: boolean } = {},
|
|
910
1417
|
): Promise<void> {
|
|
911
1418
|
const conn = db ?? this.db;
|
|
912
1419
|
const tableName = this.getTableName(slug);
|
|
@@ -928,6 +1435,7 @@ export class SchemaRegistry {
|
|
|
928
1435
|
.addColumn("draft_revision_id", "text", (col) => col.references("revisions.id"))
|
|
929
1436
|
.addColumn("locale", "text", (col) => col.notNull().defaultTo("en"))
|
|
930
1437
|
.addColumn("translation_group", "text");
|
|
1438
|
+
if (options.ifNotExists) table = table.ifNotExists();
|
|
931
1439
|
|
|
932
1440
|
for (const field of fields) {
|
|
933
1441
|
const columnName = this.getColumnName(field.slug);
|
|
@@ -947,66 +1455,77 @@ export class SchemaRegistry {
|
|
|
947
1455
|
.addUniqueConstraint(`${tableName}_slug_locale_unique`, ["slug", "locale"])
|
|
948
1456
|
.execute();
|
|
949
1457
|
|
|
1458
|
+
const createIndex = options.ifNotExists ? sql`CREATE INDEX IF NOT EXISTS` : sql`CREATE INDEX`;
|
|
1459
|
+
|
|
950
1460
|
// Create standard indexes
|
|
951
1461
|
await sql`
|
|
952
|
-
|
|
1462
|
+
${createIndex} ${sql.ref(`idx_${tableName}_slug`)}
|
|
953
1463
|
ON ${sql.ref(tableName)} (slug)
|
|
954
1464
|
`.execute(conn);
|
|
955
1465
|
|
|
956
1466
|
await sql`
|
|
957
|
-
|
|
1467
|
+
${createIndex} ${sql.ref(`idx_${tableName}_scheduled`)}
|
|
958
1468
|
ON ${sql.ref(tableName)} (scheduled_at)
|
|
959
1469
|
WHERE scheduled_at IS NOT NULL
|
|
960
1470
|
`.execute(conn);
|
|
961
1471
|
|
|
962
1472
|
await sql`
|
|
963
|
-
|
|
1473
|
+
${createIndex} ${sql.ref(`idx_${tableName}_live_revision`)}
|
|
964
1474
|
ON ${sql.ref(tableName)} (live_revision_id)
|
|
965
1475
|
`.execute(conn);
|
|
966
1476
|
|
|
967
1477
|
await sql`
|
|
968
|
-
|
|
1478
|
+
${createIndex} ${sql.ref(`idx_${tableName}_draft_revision`)}
|
|
969
1479
|
ON ${sql.ref(tableName)} (draft_revision_id)
|
|
970
1480
|
`.execute(conn);
|
|
971
1481
|
|
|
972
1482
|
await sql`
|
|
973
|
-
|
|
1483
|
+
${createIndex} ${sql.ref(`idx_${tableName}_author`)}
|
|
974
1484
|
ON ${sql.ref(tableName)} (author_id)
|
|
975
1485
|
`.execute(conn);
|
|
976
1486
|
|
|
977
1487
|
await sql`
|
|
978
|
-
|
|
1488
|
+
${createIndex} ${sql.ref(`idx_${tableName}_primary_byline`)}
|
|
979
1489
|
ON ${sql.ref(tableName)} (primary_byline_id)
|
|
980
1490
|
`.execute(conn);
|
|
981
1491
|
|
|
982
1492
|
await sql`
|
|
983
|
-
|
|
1493
|
+
${createIndex} ${sql.ref(`idx_${tableName}_locale`)}
|
|
984
1494
|
ON ${sql.ref(tableName)} (locale)
|
|
985
1495
|
`.execute(conn);
|
|
986
1496
|
|
|
1497
|
+
// Names must stay identical to migration 055, which creates these indexes
|
|
1498
|
+
// on tables that already exist. Lookups that don't constrain `deleted_at`
|
|
1499
|
+
// (menu and reference resolution) need the first; reads that do need the
|
|
1500
|
+
// second.
|
|
987
1501
|
await sql`
|
|
988
|
-
|
|
989
|
-
ON ${sql.ref(tableName)} (translation_group)
|
|
1502
|
+
${createIndex} ${sql.ref(`idx_${tableName}_tg_locale`)}
|
|
1503
|
+
ON ${sql.ref(tableName)} (translation_group, locale)
|
|
1504
|
+
`.execute(conn);
|
|
1505
|
+
|
|
1506
|
+
await sql`
|
|
1507
|
+
${createIndex} ${sql.ref(`idx_${tableName}_del_tg_locale`)}
|
|
1508
|
+
ON ${sql.ref(tableName)} (deleted_at, translation_group, locale)
|
|
990
1509
|
`.execute(conn);
|
|
991
1510
|
|
|
992
1511
|
// Composite indexes for optimized query performance (see migration 033)
|
|
993
1512
|
await sql`
|
|
994
|
-
|
|
1513
|
+
${createIndex} ${sql.ref(`idx_${tableName}_deleted_updated_id`)}
|
|
995
1514
|
ON ${sql.ref(tableName)} (deleted_at, updated_at DESC, id DESC)
|
|
996
1515
|
`.execute(conn);
|
|
997
1516
|
|
|
998
1517
|
await sql`
|
|
999
|
-
|
|
1518
|
+
${createIndex} ${sql.ref(`idx_${tableName}_deleted_status`)}
|
|
1000
1519
|
ON ${sql.ref(tableName)} (deleted_at, status)
|
|
1001
1520
|
`.execute(conn);
|
|
1002
1521
|
|
|
1003
1522
|
await sql`
|
|
1004
|
-
|
|
1523
|
+
${createIndex} ${sql.ref(`idx_${tableName}_deleted_created_id`)}
|
|
1005
1524
|
ON ${sql.ref(tableName)} (deleted_at, created_at DESC, id DESC)
|
|
1006
1525
|
`.execute(conn);
|
|
1007
1526
|
|
|
1008
1527
|
await sql`
|
|
1009
|
-
|
|
1528
|
+
${createIndex} ${sql.ref(`idx_${tableName}_deleted_published_id`)}
|
|
1010
1529
|
ON ${sql.ref(tableName)} (deleted_at, published_at DESC, id DESC)
|
|
1011
1530
|
`.execute(conn);
|
|
1012
1531
|
|
|
@@ -1015,16 +1534,67 @@ export class SchemaRegistry {
|
|
|
1015
1534
|
// inside Postgres's 63-byte identifier limit for long slugs; keep these
|
|
1016
1535
|
// names identical to migration 041.
|
|
1017
1536
|
await sql`
|
|
1018
|
-
|
|
1537
|
+
${createIndex} ${sql.ref(`idx_${tableName}_loc_upd`)}
|
|
1019
1538
|
ON ${sql.ref(tableName)} (deleted_at, locale, updated_at DESC, id DESC)
|
|
1020
1539
|
`.execute(conn);
|
|
1021
1540
|
|
|
1022
1541
|
await sql`
|
|
1023
|
-
|
|
1542
|
+
${createIndex} ${sql.ref(`idx_${tableName}_loc_crt`)}
|
|
1024
1543
|
ON ${sql.ref(tableName)} (deleted_at, locale, created_at DESC, id DESC)
|
|
1025
1544
|
`.execute(conn);
|
|
1026
1545
|
}
|
|
1027
1546
|
|
|
1547
|
+
private getFieldIndexName(fieldId: string): string {
|
|
1548
|
+
if (!FIELD_ID_PATTERN.test(fieldId)) {
|
|
1549
|
+
throw new SchemaError(`Invalid field id "${fieldId}"`, "INVALID_FIELD_ID");
|
|
1550
|
+
}
|
|
1551
|
+
return `idx_cf_${fieldId.toLowerCase()}`;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
private getLocaleFieldIndexName(fieldId: string): string {
|
|
1555
|
+
return `${this.getFieldIndexName(fieldId)}_loc`;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
private async createFieldIndex(
|
|
1559
|
+
collectionSlug: string,
|
|
1560
|
+
fieldId: string,
|
|
1561
|
+
fieldSlug: string,
|
|
1562
|
+
db?: Kysely<Database>,
|
|
1563
|
+
): Promise<void> {
|
|
1564
|
+
const conn = db ?? this.db;
|
|
1565
|
+
const tableName = this.getTableName(collectionSlug);
|
|
1566
|
+
const columnName = this.getColumnName(fieldSlug);
|
|
1567
|
+
const indexName = this.getFieldIndexName(fieldId);
|
|
1568
|
+
const localeIndexName = this.getLocaleFieldIndexName(fieldId);
|
|
1569
|
+
|
|
1570
|
+
await sql`
|
|
1571
|
+
CREATE INDEX IF NOT EXISTS ${sql.ref(indexName)}
|
|
1572
|
+
ON ${sql.ref(tableName)} (
|
|
1573
|
+
(${sql.ref(columnName)} IS NOT NULL),
|
|
1574
|
+
${sql.ref(columnName)},
|
|
1575
|
+
id
|
|
1576
|
+
)
|
|
1577
|
+
WHERE deleted_at IS NULL
|
|
1578
|
+
`.execute(conn);
|
|
1579
|
+
|
|
1580
|
+
await sql`
|
|
1581
|
+
CREATE INDEX IF NOT EXISTS ${sql.ref(localeIndexName)}
|
|
1582
|
+
ON ${sql.ref(tableName)} (
|
|
1583
|
+
locale,
|
|
1584
|
+
(${sql.ref(columnName)} IS NOT NULL),
|
|
1585
|
+
${sql.ref(columnName)},
|
|
1586
|
+
id
|
|
1587
|
+
)
|
|
1588
|
+
WHERE deleted_at IS NULL
|
|
1589
|
+
`.execute(conn);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
private async dropFieldIndex(fieldId: string, db?: Kysely<Database>): Promise<void> {
|
|
1593
|
+
const conn = db ?? this.db;
|
|
1594
|
+
await sql`DROP INDEX IF EXISTS ${sql.ref(this.getFieldIndexName(fieldId))}`.execute(conn);
|
|
1595
|
+
await sql`DROP INDEX IF EXISTS ${sql.ref(this.getLocaleFieldIndexName(fieldId))}`.execute(conn);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1028
1598
|
/**
|
|
1029
1599
|
* Add a column to a content table
|
|
1030
1600
|
*/
|
|
@@ -1224,10 +1794,18 @@ export class SchemaRegistry {
|
|
|
1224
1794
|
labelSingular: row.label_singular ?? undefined,
|
|
1225
1795
|
description: row.description ?? undefined,
|
|
1226
1796
|
icon: row.icon ?? undefined,
|
|
1797
|
+
admin: parseCollectionAdmin(row.admin_config),
|
|
1227
1798
|
supports: parseSupports(row.supports),
|
|
1228
1799
|
source: row.source && isCollectionSource(row.source) ? row.source : undefined,
|
|
1229
1800
|
hasSeo: row.has_seo === 1,
|
|
1801
|
+
// Raw value; undefined when unset. The admin list resolves the
|
|
1802
|
+
// default (title fallback chain / updatedAt) at the point of use.
|
|
1803
|
+
titleField: row.title_field ?? undefined,
|
|
1804
|
+
dateField: row.date_field ?? undefined,
|
|
1230
1805
|
urlPattern: row.url_pattern ?? undefined,
|
|
1806
|
+
routable: row.routable !== 0,
|
|
1807
|
+
hidden: row.hidden === 1,
|
|
1808
|
+
sortOrder: row.sort_order ?? undefined,
|
|
1231
1809
|
commentsEnabled: row.comments_enabled === 1,
|
|
1232
1810
|
commentsModeration:
|
|
1233
1811
|
moderation === "all" || moderation === "first_time" || moderation === "none"
|
|
@@ -1259,6 +1837,7 @@ export class SchemaRegistry {
|
|
|
1259
1837
|
options: row.options ? JSON.parse(row.options) : undefined,
|
|
1260
1838
|
sortOrder: row.sort_order,
|
|
1261
1839
|
searchable: row.searchable === 1,
|
|
1840
|
+
indexed: row.indexed === 1,
|
|
1262
1841
|
translatable: row.translatable !== 0,
|
|
1263
1842
|
createdAt: row.created_at,
|
|
1264
1843
|
};
|
|
@@ -1338,6 +1917,9 @@ export class SchemaRegistry {
|
|
|
1338
1917
|
): Promise<Collection> {
|
|
1339
1918
|
// Verify table exists
|
|
1340
1919
|
const tableName = this.getTableName(slug);
|
|
1920
|
+
if (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {
|
|
1921
|
+
throw new SchemaError(`Collection "${slug}" is already registered`, "COLLECTION_EXISTS");
|
|
1922
|
+
}
|
|
1341
1923
|
const exists = await tableExists(this.db, tableName);
|
|
1342
1924
|
|
|
1343
1925
|
if (!exists) {
|
|
@@ -1346,32 +1928,64 @@ export class SchemaRegistry {
|
|
|
1346
1928
|
|
|
1347
1929
|
// Check if already registered
|
|
1348
1930
|
const existing = await this.getCollection(slug);
|
|
1349
|
-
if (
|
|
1931
|
+
if (await isMediaUsageCollectionSlugDeleting(this.db, slug)) {
|
|
1932
|
+
throw new SchemaError(`Collection "${slug}" is already registered`, "COLLECTION_EXISTS");
|
|
1933
|
+
}
|
|
1934
|
+
if (
|
|
1935
|
+
existing &&
|
|
1936
|
+
!(await canResumeMediaUsageCollectionCapture(this.db, {
|
|
1937
|
+
collectionId: existing.id,
|
|
1938
|
+
collectionSlug: existing.slug,
|
|
1939
|
+
}))
|
|
1940
|
+
) {
|
|
1350
1941
|
throw new SchemaError(`Collection "${slug}" is already registered`, "COLLECTION_EXISTS");
|
|
1351
1942
|
}
|
|
1352
1943
|
|
|
1353
1944
|
// Create collection entry
|
|
1354
|
-
const
|
|
1945
|
+
const proposedId = existing?.id ?? ulid();
|
|
1355
1946
|
const label = options?.label || this.slugToLabel(slug);
|
|
1356
1947
|
|
|
1357
1948
|
let collectionRegistered = false;
|
|
1358
1949
|
try {
|
|
1359
|
-
await this.db
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
})
|
|
1373
|
-
|
|
1374
|
-
|
|
1950
|
+
const capture = await prepareMediaUsageCollectionCapture(this.db, {
|
|
1951
|
+
collectionId: proposedId,
|
|
1952
|
+
collectionSlug: slug,
|
|
1953
|
+
registeredCollectionId: existing?.id,
|
|
1954
|
+
});
|
|
1955
|
+
if (capture.captureRequired) {
|
|
1956
|
+
await installPreparedMediaUsageCollectionCapture(this.db, {
|
|
1957
|
+
collectionId: capture.collectionId,
|
|
1958
|
+
collectionSlug: slug,
|
|
1959
|
+
});
|
|
1960
|
+
await markMediaUsageCollectionCaptureReady(this.db, {
|
|
1961
|
+
collectionId: capture.collectionId,
|
|
1962
|
+
collectionSlug: slug,
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
if (!capture.registrationExists) {
|
|
1966
|
+
await this.db
|
|
1967
|
+
.insertInto("_emdash_collections")
|
|
1968
|
+
.values({
|
|
1969
|
+
id: capture.collectionId,
|
|
1970
|
+
slug,
|
|
1971
|
+
label,
|
|
1972
|
+
label_singular: options?.labelSingular ?? null,
|
|
1973
|
+
description: options?.description ?? null,
|
|
1974
|
+
icon: null,
|
|
1975
|
+
supports: JSON.stringify([]),
|
|
1976
|
+
source: "discovered",
|
|
1977
|
+
has_seo: 0,
|
|
1978
|
+
url_pattern: null,
|
|
1979
|
+
})
|
|
1980
|
+
.execute();
|
|
1981
|
+
collectionRegistered = true;
|
|
1982
|
+
}
|
|
1983
|
+
if (capture.captureRequired) {
|
|
1984
|
+
await finalizeMediaUsageCollectionCapture(this.db, {
|
|
1985
|
+
collectionId: capture.collectionId,
|
|
1986
|
+
collectionSlug: slug,
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1375
1989
|
|
|
1376
1990
|
const collection = await this.getCollection(slug);
|
|
1377
1991
|
if (!collection) {
|