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
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { o as __exportAll } from "./runner-
|
|
2
|
-
import { t as validateIdentifier } from "./validate-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { n as
|
|
6
|
-
import { n as
|
|
7
|
-
import {
|
|
1
|
+
import { o as __exportAll } from "./runner-BiDvq5dv.mjs";
|
|
2
|
+
import { t as validateIdentifier } from "./validate-BpwoDOgt.mjs";
|
|
3
|
+
import { c as invalidateCollectionCache } from "./object-cache-71tPfV4A.mjs";
|
|
4
|
+
import { c as isIndexableFieldType } from "./types-75KIqzL9.mjs";
|
|
5
|
+
import { n as chunks, t as SQL_BATCH_SIZE } from "./chunks-DfPax1I0.mjs";
|
|
6
|
+
import { n as isMissingTableError } from "./db-errors-Cl-sKYTm.mjs";
|
|
7
|
+
import { n as slugify } from "./slugify-B994zkXC.mjs";
|
|
8
|
+
import { a as ScheduledNotDueError, i as InvalidCursorError, n as ContentMutationConflictError, o as decodeCursor, r as EmDashValidationError, s as encodeCursor, t as ContentCollectionNotFoundError } from "./types-CQPWhPxE.mjs";
|
|
8
9
|
import { sql } from "kysely";
|
|
9
10
|
import { monotonicFactory, ulid } from "ulidx";
|
|
10
11
|
|
|
@@ -76,6 +77,15 @@ var RevisionRepository = class {
|
|
|
76
77
|
await this.db.insertInto("revisions").values(row).execute();
|
|
77
78
|
const revision = await this.findById(id);
|
|
78
79
|
if (!revision) throw new Error("Failed to create revision");
|
|
80
|
+
try {
|
|
81
|
+
await this.db.insertInto("_emdash_revision_prune_queue").values({
|
|
82
|
+
collection: input.collection,
|
|
83
|
+
entry_id: input.entryId,
|
|
84
|
+
revision_id: id
|
|
85
|
+
}).onConflict((conflict) => conflict.columns(["collection", "entry_id"]).doUpdateSet({ revision_id: id })).execute();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(`[revisions] Failed to queue revision pruning for ${input.collection}/${input.entryId}:`, error);
|
|
88
|
+
}
|
|
79
89
|
return revision;
|
|
80
90
|
}
|
|
81
91
|
/**
|
|
@@ -115,23 +125,56 @@ var RevisionRepository = class {
|
|
|
115
125
|
*/
|
|
116
126
|
async deleteByEntry(collection, entryId) {
|
|
117
127
|
const result = await this.db.deleteFrom("revisions").where("collection", "=", collection).where("entry_id", "=", entryId).executeTakeFirst();
|
|
128
|
+
try {
|
|
129
|
+
await this.db.deleteFrom("_emdash_revision_prune_queue").where("collection", "=", collection).where("entry_id", "=", entryId).execute();
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error(`[revisions] Failed to clear queued revision pruning for ${collection}/${entryId}:`, error);
|
|
132
|
+
}
|
|
118
133
|
return Number(result.numDeletedRows ?? 0);
|
|
119
134
|
}
|
|
120
135
|
/**
|
|
121
136
|
* Delete old revisions, keeping the most recent N
|
|
122
137
|
*/
|
|
123
|
-
async pruneOldRevisions(collection, entryId, keepCount) {
|
|
124
|
-
|
|
138
|
+
async pruneOldRevisions(collection, entryId, keepCount, throughRevisionId) {
|
|
139
|
+
validateIdentifier(collection, "collection");
|
|
140
|
+
const tableName = `ec_${collection}`;
|
|
141
|
+
let keepQuery = this.db.selectFrom("revisions").select("id").where("collection", "=", collection).where("entry_id", "=", entryId).orderBy("created_at", "desc").orderBy("id", "desc").limit(keepCount);
|
|
142
|
+
if (throughRevisionId) keepQuery = keepQuery.where("id", "<=", throughRevisionId);
|
|
143
|
+
const keepIds = (await keepQuery.execute()).map((r) => r.id);
|
|
125
144
|
if (keepIds.length === 0) return 0;
|
|
126
|
-
const result = await
|
|
127
|
-
|
|
145
|
+
const result = await sql`
|
|
146
|
+
DELETE FROM revisions
|
|
147
|
+
WHERE collection = ${collection}
|
|
148
|
+
AND entry_id = ${entryId}
|
|
149
|
+
${throughRevisionId ? sql`AND id <= ${throughRevisionId}` : sql``}
|
|
150
|
+
AND id NOT IN (${sql.join(keepIds.map((id) => sql`${id}`))})
|
|
151
|
+
AND NOT EXISTS (
|
|
152
|
+
SELECT 1 FROM ${sql.ref(tableName)} AS content
|
|
153
|
+
WHERE content.live_revision_id = revisions.id
|
|
154
|
+
OR content.draft_revision_id = revisions.id
|
|
155
|
+
)
|
|
156
|
+
`.execute(this.db);
|
|
157
|
+
return Number(result.numAffectedRows ?? 0);
|
|
128
158
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
159
|
+
async pruneQueuedEntry(collection, entryId, queuedRevisionId, keepCount) {
|
|
160
|
+
const pruned = await this.pruneOldRevisions(collection, entryId, keepCount, queuedRevisionId);
|
|
161
|
+
await this.db.deleteFrom("_emdash_revision_prune_queue").where("collection", "=", collection).where("entry_id", "=", entryId).where("revision_id", "=", queuedRevisionId).execute();
|
|
162
|
+
return pruned;
|
|
163
|
+
}
|
|
164
|
+
async deleteIfUnreferenced(collection, entryId, revisionId) {
|
|
165
|
+
validateIdentifier(collection, "collection");
|
|
166
|
+
const tableName = `ec_${collection}`;
|
|
167
|
+
return ((await sql`
|
|
168
|
+
DELETE FROM revisions
|
|
169
|
+
WHERE id = ${revisionId}
|
|
170
|
+
AND collection = ${collection}
|
|
171
|
+
AND entry_id = ${entryId}
|
|
172
|
+
AND NOT EXISTS (
|
|
173
|
+
SELECT 1 FROM ${sql.ref(tableName)} AS content
|
|
174
|
+
WHERE content.live_revision_id = revisions.id
|
|
175
|
+
OR content.draft_revision_id = revisions.id
|
|
176
|
+
)
|
|
177
|
+
`.execute(this.db)).numAffectedRows ?? 0n) > 0n;
|
|
135
178
|
}
|
|
136
179
|
/**
|
|
137
180
|
* Convert database row to Revision object
|
|
@@ -150,9 +193,65 @@ var RevisionRepository = class {
|
|
|
150
193
|
|
|
151
194
|
//#endregion
|
|
152
195
|
//#region src/database/repositories/content.ts
|
|
153
|
-
var content_exports = /* @__PURE__ */ __exportAll({
|
|
196
|
+
var content_exports = /* @__PURE__ */ __exportAll({
|
|
197
|
+
ContentRepository: () => ContentRepository,
|
|
198
|
+
isSystemOrderField: () => isSystemOrderField
|
|
199
|
+
});
|
|
154
200
|
const ULID_PATTERN = /^[0-9A-Z]{26}$/;
|
|
201
|
+
const SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/;
|
|
202
|
+
const MAX_DRAFT_STAGE_ATTEMPTS = 32;
|
|
155
203
|
const LIKE_WILDCARD_RE = /[\\%_]/g;
|
|
204
|
+
const MAX_INDEXED_FIELD_FILTERS = 20;
|
|
205
|
+
const MAX_IN_FILTER_VALUES = SQL_BATCH_SIZE;
|
|
206
|
+
const MAX_FILTER_STRING_LENGTH = 2048;
|
|
207
|
+
function nullableColumnMatch(column, value) {
|
|
208
|
+
validateIdentifier(column, "content column");
|
|
209
|
+
return value === null ? sql`${sql.ref(column)} IS NULL` : sql`${sql.ref(column)} = ${value}`;
|
|
210
|
+
}
|
|
211
|
+
function sameStoredValue(left, right) {
|
|
212
|
+
return Object.is(serializeValue(left), serializeValue(right));
|
|
213
|
+
}
|
|
214
|
+
function matchesPublication(observed, existing, revision, revisionId, slug, publishedAt, updatedAt) {
|
|
215
|
+
if (observed.version !== existing.version + 1 || observed.status !== "published" || observed.slug !== slug || observed.liveRevisionId !== revisionId || observed.draftRevisionId !== null || observed.scheduledAt !== null || observed.publishedAt !== publishedAt || observed.updatedAt !== updatedAt) return false;
|
|
216
|
+
return Object.entries(revision.data).every(([key, value]) => SYSTEM_COLUMNS.has(key) || key.startsWith("_") || sameStoredValue(observed.data[key], value));
|
|
217
|
+
}
|
|
218
|
+
function matchesLifecyclePublication(observed, existing, liveRevisionId, publishedAt, updatedAt) {
|
|
219
|
+
if (observed.version !== existing.version + 1 || observed.status !== "published" || observed.slug !== existing.slug || observed.liveRevisionId !== liveRevisionId || observed.draftRevisionId !== null || observed.scheduledAt !== null || observed.publishedAt !== publishedAt || observed.updatedAt !== updatedAt) return false;
|
|
220
|
+
return Object.entries(existing.data).every(([key, value]) => sameStoredValue(observed.data[key], value));
|
|
221
|
+
}
|
|
222
|
+
function matchesPublicationFence(observed, existing) {
|
|
223
|
+
return observed.version === existing.version && observed.status === existing.status && observed.liveRevisionId === existing.liveRevisionId && observed.draftRevisionId === existing.draftRevisionId && observed.scheduledAt === existing.scheduledAt;
|
|
224
|
+
}
|
|
225
|
+
function isConfirmedStatementFailure(error) {
|
|
226
|
+
if (typeof error !== "object" || error === null || !("code" in error)) return false;
|
|
227
|
+
const code = error.code;
|
|
228
|
+
return typeof code === "string" && (code.startsWith("SQLITE_") || SQLSTATE_PATTERN.test(code));
|
|
229
|
+
}
|
|
230
|
+
function encodeIndexedFieldCursor(field, value, id) {
|
|
231
|
+
const payload = {
|
|
232
|
+
version: 1,
|
|
233
|
+
field,
|
|
234
|
+
value
|
|
235
|
+
};
|
|
236
|
+
return encodeCursor(JSON.stringify(payload), id);
|
|
237
|
+
}
|
|
238
|
+
function decodeIndexedFieldCursor(cursor, field) {
|
|
239
|
+
const { orderValue, id } = decodeCursor(cursor);
|
|
240
|
+
let payload;
|
|
241
|
+
try {
|
|
242
|
+
payload = JSON.parse(orderValue);
|
|
243
|
+
} catch {
|
|
244
|
+
throw new InvalidCursorError(cursor);
|
|
245
|
+
}
|
|
246
|
+
if (payload === null || typeof payload !== "object") throw new InvalidCursorError(cursor);
|
|
247
|
+
const candidate = payload;
|
|
248
|
+
const validValue = candidate.value === null || typeof candidate.value === "string" || typeof candidate.value === "number";
|
|
249
|
+
if (candidate.version !== 1 || candidate.field !== field || !validValue) throw new InvalidCursorError(cursor);
|
|
250
|
+
return {
|
|
251
|
+
value: candidate.value,
|
|
252
|
+
id
|
|
253
|
+
};
|
|
254
|
+
}
|
|
156
255
|
/**
|
|
157
256
|
* Whitelist mapping a public date-filter field to its physical column. Keeping
|
|
158
257
|
* this separate from `mapOrderField` makes the filterable set explicit and
|
|
@@ -164,6 +263,27 @@ const DATE_FILTER_COLUMNS = {
|
|
|
164
263
|
publishedAt: "published_at"
|
|
165
264
|
};
|
|
166
265
|
/**
|
|
266
|
+
* Built-in sort fields → their physical columns. A closed set that blocks
|
|
267
|
+
* sorting by arbitrary columns; per-collection fields are allowed
|
|
268
|
+
* separately via `mapOrderField`'s `sortableExtras`.
|
|
269
|
+
*/
|
|
270
|
+
const ORDER_FIELD_COLUMNS = {
|
|
271
|
+
createdAt: "created_at",
|
|
272
|
+
updatedAt: "updated_at",
|
|
273
|
+
publishedAt: "published_at",
|
|
274
|
+
scheduledAt: "scheduled_at",
|
|
275
|
+
deletedAt: "deleted_at",
|
|
276
|
+
title: "title",
|
|
277
|
+
name: "name",
|
|
278
|
+
slug: "slug",
|
|
279
|
+
status: "status",
|
|
280
|
+
locale: "locale"
|
|
281
|
+
};
|
|
282
|
+
/** True when `field` maps to a system column and needs no per-collection resolution. */
|
|
283
|
+
function isSystemOrderField(field) {
|
|
284
|
+
return field in ORDER_FIELD_COLUMNS;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
167
287
|
* System columns that exist in every ec_* table
|
|
168
288
|
*/
|
|
169
289
|
const SYSTEM_COLUMNS = new Set([
|
|
@@ -201,6 +321,15 @@ function serializeValue(value) {
|
|
|
201
321
|
if (typeof value === "object") return JSON.stringify(value);
|
|
202
322
|
return value;
|
|
203
323
|
}
|
|
324
|
+
function writableContentData(data) {
|
|
325
|
+
const writable = {};
|
|
326
|
+
for (const [key, value] of Object.entries(data)) {
|
|
327
|
+
if (SYSTEM_COLUMNS.has(key)) continue;
|
|
328
|
+
validateIdentifier(key, "content field name");
|
|
329
|
+
writable[key] = value;
|
|
330
|
+
}
|
|
331
|
+
return writable;
|
|
332
|
+
}
|
|
204
333
|
/**
|
|
205
334
|
* Deserialize a value from database storage
|
|
206
335
|
* Attempts to parse JSON strings that look like objects/arrays
|
|
@@ -237,7 +366,7 @@ var ContentRepository = class {
|
|
|
237
366
|
* Create a new content item
|
|
238
367
|
*/
|
|
239
368
|
async create(input) {
|
|
240
|
-
const id = ulid();
|
|
369
|
+
const id = input.id ?? ulid();
|
|
241
370
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
242
371
|
const { type, slug, data, status = "draft", authorId, primaryBylineId, locale, translationOf, publishedAt, createdAt } = input;
|
|
243
372
|
if (!type) throw new EmDashValidationError("Content type is required");
|
|
@@ -299,7 +428,7 @@ var ContentRepository = class {
|
|
|
299
428
|
* (optionally scoped to a locale) and appends a numeric suffix (`-1`,
|
|
300
429
|
* `-2`, etc.) on collision to guarantee uniqueness.
|
|
301
430
|
*
|
|
302
|
-
* Returns
|
|
431
|
+
* Returns null when slug normalization cannot produce a value.
|
|
303
432
|
*/
|
|
304
433
|
async generateUniqueSlug(type, text, locale) {
|
|
305
434
|
const baseSlug = slugify(text);
|
|
@@ -462,7 +591,9 @@ var ContentRepository = class {
|
|
|
462
591
|
const limit = Math.min(options.limit || 50, 100);
|
|
463
592
|
const orderField = options.orderBy?.field || "createdAt";
|
|
464
593
|
const orderDirection = options.orderBy?.direction || "desc";
|
|
465
|
-
const
|
|
594
|
+
const resolvedOrderField = await this.resolveOrderField(type, orderField, options.sortableExtras);
|
|
595
|
+
const dbField = resolvedOrderField.column;
|
|
596
|
+
const resolvedFieldFilters = await this.resolveFieldFilters(type, options.where?.fieldFilters);
|
|
466
597
|
const safeOrderDirection = orderDirection.toLowerCase() === "asc" ? "ASC" : "DESC";
|
|
467
598
|
let query = this.db.selectFrom(tableName).selectAll().where("deleted_at", "is", null);
|
|
468
599
|
if (options.where?.status) query = query.where("status", "=", options.where.status);
|
|
@@ -470,13 +601,42 @@ var ContentRepository = class {
|
|
|
470
601
|
if (options.where?.locale) query = query.where("locale", "=", options.where.locale);
|
|
471
602
|
query = this.applySearchFilter(query, options.where, type);
|
|
472
603
|
query = this.applyDateFilter(query, options.where);
|
|
473
|
-
|
|
604
|
+
query = this.applyBylineFilter(query, options.where, type);
|
|
605
|
+
query = this.applyFieldFilters(query, resolvedFieldFilters);
|
|
606
|
+
if (options.cursor) if (resolvedOrderField.indexedCustomField) {
|
|
607
|
+
const { value, id: cursorId } = decodeIndexedFieldCursor(options.cursor, orderField);
|
|
608
|
+
const isPresent = sql`${sql.ref(dbField)} IS NOT NULL`;
|
|
609
|
+
const falseLiteral = sql`FALSE`;
|
|
610
|
+
const trueLiteral = sql`TRUE`;
|
|
611
|
+
if (safeOrderDirection === "ASC" && value === null) query = query.where(sql`
|
|
612
|
+
(${isPresent}) > ${falseLiteral}
|
|
613
|
+
OR ((${isPresent}) = ${falseLiteral} AND ${sql.ref("id")} > ${cursorId})
|
|
614
|
+
`);
|
|
615
|
+
else if (safeOrderDirection === "DESC" && value === null) query = query.where(sql`
|
|
616
|
+
(${isPresent}) = ${falseLiteral} AND ${sql.ref("id")} < ${cursorId}
|
|
617
|
+
`);
|
|
618
|
+
else if (safeOrderDirection === "ASC") query = query.where(sql`
|
|
619
|
+
(${isPresent}, ${sql.ref(dbField)}, ${sql.ref("id")})
|
|
620
|
+
> (${trueLiteral}, ${value}, ${cursorId})
|
|
621
|
+
`);
|
|
622
|
+
else query = query.where(sql`
|
|
623
|
+
(${isPresent}, ${sql.ref(dbField)}, ${sql.ref("id")})
|
|
624
|
+
< (${trueLiteral}, ${value}, ${cursorId})
|
|
625
|
+
`);
|
|
626
|
+
} else {
|
|
474
627
|
const { orderValue, id: cursorId } = decodeCursor(options.cursor);
|
|
475
628
|
if (safeOrderDirection === "DESC") query = query.where((eb) => eb.or([eb(dbField, "<", orderValue), eb.and([eb(dbField, "=", orderValue), eb("id", "<", cursorId)])]));
|
|
476
629
|
else query = query.where((eb) => eb.or([eb(dbField, ">", orderValue), eb.and([eb(dbField, "=", orderValue), eb("id", ">", cursorId)])]));
|
|
477
630
|
}
|
|
478
|
-
|
|
479
|
-
|
|
631
|
+
const indexedOrderFilter = resolvedOrderField.indexedCustomField ? resolvedFieldFilters.find((filter) => filter.column === dbField) : void 0;
|
|
632
|
+
if (resolvedOrderField.indexedCustomField && !indexedOrderFilter) query = query.orderBy(sql`${sql.ref(dbField)} IS NOT NULL`, safeOrderDirection === "ASC" ? "asc" : "desc");
|
|
633
|
+
if (indexedOrderFilter?.kind !== "null") query = query.orderBy(dbField, safeOrderDirection === "ASC" ? "asc" : "desc");
|
|
634
|
+
query = query.orderBy("id", safeOrderDirection === "ASC" ? "asc" : "desc").limit(limit + 1);
|
|
635
|
+
const [rowsResult, countResult] = await Promise.allSettled([query.execute(), this.countWithResolvedFilters(type, options.where, resolvedFieldFilters)]);
|
|
636
|
+
if (rowsResult.status === "rejected") throw rowsResult.reason;
|
|
637
|
+
if (countResult.status === "rejected") throw countResult.reason;
|
|
638
|
+
const rows = rowsResult.value;
|
|
639
|
+
const total = countResult.value;
|
|
480
640
|
const hasMore = rows.length > limit;
|
|
481
641
|
const items = rows.slice(0, limit);
|
|
482
642
|
const mappedResult = {
|
|
@@ -486,7 +646,10 @@ var ContentRepository = class {
|
|
|
486
646
|
if (hasMore && items.length > 0) {
|
|
487
647
|
const lastRow = items.at(-1);
|
|
488
648
|
const lastOrderValue = lastRow[dbField];
|
|
489
|
-
|
|
649
|
+
if (resolvedOrderField.indexedCustomField) {
|
|
650
|
+
if (lastOrderValue !== null && typeof lastOrderValue !== "string" && typeof lastOrderValue !== "number") throw new EmDashValidationError(`Invalid indexed value for order field: ${orderField}`);
|
|
651
|
+
mappedResult.nextCursor = encodeIndexedFieldCursor(orderField, lastOrderValue, String(lastRow.id));
|
|
652
|
+
} else mappedResult.nextCursor = encodeCursor(typeof lastOrderValue === "string" || typeof lastOrderValue === "number" ? String(lastOrderValue) : "", String(lastRow.id));
|
|
490
653
|
}
|
|
491
654
|
return mappedResult;
|
|
492
655
|
}
|
|
@@ -503,23 +666,137 @@ var ContentRepository = class {
|
|
|
503
666
|
if (input.scheduledAt !== void 0) updates.scheduled_at = input.scheduledAt;
|
|
504
667
|
if (input.authorId !== void 0) updates.author_id = input.authorId;
|
|
505
668
|
if (input.primaryBylineId !== void 0) updates.primary_byline_id = input.primaryBylineId;
|
|
506
|
-
if (input.data !== void 0 && typeof input.data === "object")
|
|
507
|
-
for (const [key, value] of Object.entries(input.data)) if (!SYSTEM_COLUMNS.has(key)) {
|
|
508
|
-
validateIdentifier(key, "content field name");
|
|
509
|
-
updates[key] = serializeValue(value);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
669
|
+
if (input.data !== void 0 && typeof input.data === "object") for (const [key, value] of Object.entries(writableContentData(input.data))) updates[key] = serializeValue(value);
|
|
512
670
|
const hasColumnWrites = Object.keys(updates).length > 0;
|
|
513
671
|
if (hasColumnWrites) updates.updated_at = now;
|
|
514
672
|
updates.version = sql`version + 1`;
|
|
515
673
|
await this.db.updateTable(tableName).set(updates).where("id", "=", id).where("deleted_at", "is", null).execute();
|
|
516
|
-
if (input.status !== void 0 || input.publishedAt !== void 0 || input.scheduledAt !== void 0) await this.restampEntryPivot(type, id);
|
|
517
674
|
if (hasColumnWrites) invalidateCollectionCache(type);
|
|
518
675
|
const updated = await this.findById(type, id);
|
|
519
676
|
if (!updated) throw new Error("Content not found");
|
|
520
677
|
return updated;
|
|
521
678
|
}
|
|
522
679
|
/**
|
|
680
|
+
* Update plugin-authored fields without letting content columns diverge
|
|
681
|
+
* from the revision pointers that publication promotes.
|
|
682
|
+
*/
|
|
683
|
+
async updateDraftAware(type, id, input) {
|
|
684
|
+
const data = input.data ? writableContentData(input.data) : {};
|
|
685
|
+
const stagedSlug = typeof input.slug === "string" ? input.slug : void 0;
|
|
686
|
+
if (!(Object.keys(data).length > 0 || stagedSlug !== void 0)) return this.update(type, id, {
|
|
687
|
+
...input,
|
|
688
|
+
data
|
|
689
|
+
});
|
|
690
|
+
const collectionRows = await this.db.selectFrom("_emdash_collections as collection").leftJoin("_emdash_fields as field", "field.collection_id", "collection.id").select(["collection.supports", "field.slug as fieldSlug"]).where("collection.slug", "=", type).execute();
|
|
691
|
+
const supportsRaw = collectionRows[0]?.supports;
|
|
692
|
+
const supports = supportsRaw ? JSON.parse(supportsRaw) : [];
|
|
693
|
+
if (!Array.isArray(supports) || !supports.includes("revisions")) return this.update(type, id, {
|
|
694
|
+
...input,
|
|
695
|
+
data
|
|
696
|
+
});
|
|
697
|
+
const fieldSlugs = new Set(collectionRows.map((row) => row.fieldSlug).filter(Boolean));
|
|
698
|
+
for (const field of Object.keys(data)) if (!fieldSlugs.has(field)) throw new EmDashValidationError(`Unknown field '${field}' in collection '${type}'`);
|
|
699
|
+
const revisionRepo = new RevisionRepository(this.db);
|
|
700
|
+
let existing = await this.findById(type, id);
|
|
701
|
+
for (let attempt = 0; existing && attempt < MAX_DRAFT_STAGE_ATTEMPTS; attempt++) {
|
|
702
|
+
let baseData = existing.data;
|
|
703
|
+
if (existing.draftRevisionId) {
|
|
704
|
+
const draft = await revisionRepo.findById(existing.draftRevisionId);
|
|
705
|
+
if (draft) baseData = draft.data;
|
|
706
|
+
}
|
|
707
|
+
const mergedData = {
|
|
708
|
+
...baseData,
|
|
709
|
+
...data
|
|
710
|
+
};
|
|
711
|
+
if (stagedSlug !== void 0) mergedData._slug = stagedSlug;
|
|
712
|
+
const revision = await revisionRepo.create({
|
|
713
|
+
collection: type,
|
|
714
|
+
entryId: id,
|
|
715
|
+
data: mergedData,
|
|
716
|
+
...input.authorId ? { authorId: input.authorId } : {}
|
|
717
|
+
});
|
|
718
|
+
let staged;
|
|
719
|
+
try {
|
|
720
|
+
staged = await this.replaceDraftRevisionForUpdate(type, id, revision.id, existing, input);
|
|
721
|
+
} catch (error) {
|
|
722
|
+
await this.deleteUnstagedRevision(revisionRepo, type, id, revision.id);
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
if (staged) {
|
|
726
|
+
const updated = await this.findById(type, id);
|
|
727
|
+
if (!updated) throw new Error("Content not found");
|
|
728
|
+
const draftData = {};
|
|
729
|
+
for (const [key, value] of Object.entries(mergedData)) if (!key.startsWith("_")) draftData[key] = value;
|
|
730
|
+
return {
|
|
731
|
+
...updated,
|
|
732
|
+
data: {
|
|
733
|
+
...updated.data,
|
|
734
|
+
...draftData
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
await this.deleteUnstagedRevision(revisionRepo, type, id, revision.id);
|
|
739
|
+
existing = await this.findById(type, id);
|
|
740
|
+
}
|
|
741
|
+
if (!existing) throw new Error("Content not found");
|
|
742
|
+
throw new ContentMutationConflictError();
|
|
743
|
+
}
|
|
744
|
+
async deleteUnstagedRevision(revisionRepo, type, id, revisionId) {
|
|
745
|
+
try {
|
|
746
|
+
await revisionRepo.deleteIfUnreferenced(type, id, revisionId);
|
|
747
|
+
} catch (error) {
|
|
748
|
+
console.error(`[content] Failed to clean up unstaged revision ${revisionId}:`, error);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
async replaceDraftRevisionForUpdate(type, id, revisionId, expected, input) {
|
|
752
|
+
const tableName = getTableName(type);
|
|
753
|
+
const assignments = [sql`draft_revision_id = ${revisionId}`];
|
|
754
|
+
let liveMetadataChanged = false;
|
|
755
|
+
if (input.status !== void 0) {
|
|
756
|
+
assignments.push(sql`status = ${input.status}`);
|
|
757
|
+
liveMetadataChanged = true;
|
|
758
|
+
}
|
|
759
|
+
if (input.slug === null) {
|
|
760
|
+
assignments.push(sql`slug = NULL`);
|
|
761
|
+
liveMetadataChanged = true;
|
|
762
|
+
}
|
|
763
|
+
if (input.publishedAt !== void 0) {
|
|
764
|
+
assignments.push(sql`published_at = ${input.publishedAt}`);
|
|
765
|
+
liveMetadataChanged = true;
|
|
766
|
+
}
|
|
767
|
+
if (input.scheduledAt !== void 0) {
|
|
768
|
+
assignments.push(sql`scheduled_at = ${input.scheduledAt}`);
|
|
769
|
+
liveMetadataChanged = true;
|
|
770
|
+
}
|
|
771
|
+
if (input.authorId !== void 0) {
|
|
772
|
+
assignments.push(sql`author_id = ${input.authorId}`);
|
|
773
|
+
liveMetadataChanged = true;
|
|
774
|
+
}
|
|
775
|
+
if (input.primaryBylineId !== void 0) {
|
|
776
|
+
assignments.push(sql`primary_byline_id = ${input.primaryBylineId}`);
|
|
777
|
+
liveMetadataChanged = true;
|
|
778
|
+
}
|
|
779
|
+
if (liveMetadataChanged) assignments.push(sql`updated_at = ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
780
|
+
assignments.push(sql`version = version + 1`);
|
|
781
|
+
const changed = ((await sql`
|
|
782
|
+
UPDATE ${sql.ref(tableName)}
|
|
783
|
+
SET ${sql.join(assignments, sql`, `)}
|
|
784
|
+
WHERE id = ${id}
|
|
785
|
+
AND deleted_at IS NULL
|
|
786
|
+
AND version = ${expected.version}
|
|
787
|
+
AND ${nullableColumnMatch("live_revision_id", expected.liveRevisionId)}
|
|
788
|
+
AND ${nullableColumnMatch("draft_revision_id", expected.draftRevisionId)}
|
|
789
|
+
AND EXISTS (
|
|
790
|
+
SELECT 1 FROM revisions
|
|
791
|
+
WHERE revisions.id = ${revisionId}
|
|
792
|
+
AND revisions.collection = ${type}
|
|
793
|
+
AND revisions.entry_id = ${id}
|
|
794
|
+
)
|
|
795
|
+
`.execute(this.db)).numAffectedRows ?? 0n) > 0n;
|
|
796
|
+
if (changed && liveMetadataChanged) invalidateCollectionCache(type);
|
|
797
|
+
return changed;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
523
800
|
* Delete content (soft delete - moves to trash)
|
|
524
801
|
*/
|
|
525
802
|
async delete(type, id) {
|
|
@@ -531,10 +808,7 @@ var ContentRepository = class {
|
|
|
531
808
|
WHERE id = ${id}
|
|
532
809
|
AND deleted_at IS NULL
|
|
533
810
|
`.execute(this.db)).numAffectedRows ?? 0n) > 0n;
|
|
534
|
-
if (changed)
|
|
535
|
-
await this.restampEntryPivot(type, id);
|
|
536
|
-
invalidateCollectionCache(type);
|
|
537
|
-
}
|
|
811
|
+
if (changed) invalidateCollectionCache(type);
|
|
538
812
|
return changed;
|
|
539
813
|
}
|
|
540
814
|
/**
|
|
@@ -550,35 +824,10 @@ var ContentRepository = class {
|
|
|
550
824
|
RETURNING *
|
|
551
825
|
`.execute(this.db)).rows[0];
|
|
552
826
|
if (!restored) return null;
|
|
553
|
-
await this.restampEntryPivot(type, id);
|
|
554
827
|
invalidateCollectionCache(type);
|
|
555
828
|
return this.mapRow(type, restored);
|
|
556
829
|
}
|
|
557
830
|
/**
|
|
558
|
-
* Re-stamp the denormalized filter + sort columns on every
|
|
559
|
-
* `content_taxonomies` pivot row for an entry from its authoritative `ec_*`
|
|
560
|
-
* row (migration 051). Called after any mutation that moves one of those
|
|
561
|
-
* columns so a taxonomy-filtered listing can seek the entry directly.
|
|
562
|
-
*
|
|
563
|
-
* A single correlated `UPDATE` reads the post-mutation values from `ec_*`, so
|
|
564
|
-
* the pivot converges to the authoritative row. This is NOT atomic with the
|
|
565
|
-
* `ec_*` mutation on D1 (no transactions), which is why the read path
|
|
566
|
-
* re-checks the real predicates on the joined `ec_*` row. Untagged entries
|
|
567
|
-
* have no pivot rows, so the statement is a cheap no-op for them.
|
|
568
|
-
*/
|
|
569
|
-
async restampEntryPivot(type, id) {
|
|
570
|
-
const tableName = getTableName(type);
|
|
571
|
-
await sql`
|
|
572
|
-
UPDATE content_taxonomies
|
|
573
|
-
SET (status, scheduled_at, deleted_at, locale, published_at, created_at) = (
|
|
574
|
-
SELECT status, scheduled_at, deleted_at, locale, published_at, created_at
|
|
575
|
-
FROM ${sql.ref(tableName)}
|
|
576
|
-
WHERE ${sql.ref(tableName)}.id = ${id}
|
|
577
|
-
)
|
|
578
|
-
WHERE collection = ${type} AND entry_id = ${id}
|
|
579
|
-
`.execute(this.db);
|
|
580
|
-
}
|
|
581
|
-
/**
|
|
582
831
|
* Permanently delete content (cannot be undone)
|
|
583
832
|
*/
|
|
584
833
|
/**
|
|
@@ -675,7 +924,7 @@ var ContentRepository = class {
|
|
|
675
924
|
const pattern = `%${term.replace(LIKE_WILDCARD_RE, (c) => `\\${c}`)}%`;
|
|
676
925
|
return query.where((eb) => eb.or(columns.map((col) => {
|
|
677
926
|
validateIdentifier(col, "search column");
|
|
678
|
-
return
|
|
927
|
+
return sql`lower(CAST(${sql.ref(col)} AS TEXT)) LIKE lower(${pattern}) ESCAPE '\\'`;
|
|
679
928
|
})));
|
|
680
929
|
}
|
|
681
930
|
/**
|
|
@@ -697,9 +946,57 @@ var ContentRepository = class {
|
|
|
697
946
|
return next;
|
|
698
947
|
}
|
|
699
948
|
/**
|
|
949
|
+
* Apply the optional byline filter as a correlated (NOT) EXISTS against
|
|
950
|
+
* `_emdash_content_bylines`.
|
|
951
|
+
*
|
|
952
|
+
* Correlating from the content table preserves the outer sort index so
|
|
953
|
+
* `LIMIT` can short-circuit. `mode: "none"` tests the junction rather than
|
|
954
|
+
* `primary_byline_id` because the two are written in the same call but
|
|
955
|
+
* are not atomically consistent, so the junction is authoritative.
|
|
956
|
+
*
|
|
957
|
+
* Whether a credit *renders* is locale-scoped; whether one *exists* is
|
|
958
|
+
* not. Both are needed: the first decides what the filter matches, the
|
|
959
|
+
* second decides whether the author fallback applies at all.
|
|
960
|
+
*/
|
|
961
|
+
applyBylineFilter(query, where, type) {
|
|
962
|
+
const filter = where?.bylineFilter;
|
|
963
|
+
if (!filter) return query;
|
|
964
|
+
const tableName = getTableName(type);
|
|
965
|
+
const idColumn = `${tableName}.id`;
|
|
966
|
+
const authorColumn = `${tableName}.author_id`;
|
|
967
|
+
const localeColumn = `${tableName}.locale`;
|
|
968
|
+
const creditRenders = (eb, bylineIds) => {
|
|
969
|
+
let sub = eb.selectFrom("_emdash_content_bylines as cb").innerJoin("_emdash_bylines as b", "b.translation_group", "cb.byline_id").select("cb.id").where("cb.collection_slug", "=", type).whereRef("cb.content_id", "=", idColumn);
|
|
970
|
+
sub = filter.locale ? sub.where("b.locale", "=", filter.locale) : sub.whereRef("b.locale", "=", localeColumn);
|
|
971
|
+
if (bylineIds) sub = sub.where("cb.byline_id", "in", bylineIds);
|
|
972
|
+
return eb.exists(sub);
|
|
973
|
+
};
|
|
974
|
+
const hasExplicitCredit = (eb) => eb.exists(eb.selectFrom("_emdash_content_bylines as cb").select("cb.id").where("cb.collection_slug", "=", type).whereRef("cb.content_id", "=", idColumn));
|
|
975
|
+
const authorHasByline = (eb, bylineIds) => {
|
|
976
|
+
let sub = eb.selectFrom("_emdash_bylines as b").select("b.id").whereRef("b.user_id", "=", authorColumn);
|
|
977
|
+
sub = filter.locale ? sub.where("b.locale", "=", filter.locale) : sub.whereRef("b.locale", "=", localeColumn);
|
|
978
|
+
if (bylineIds) sub = sub.where("b.translation_group", "in", bylineIds);
|
|
979
|
+
return eb.exists(sub);
|
|
980
|
+
};
|
|
981
|
+
if (filter.mode === "none") return query.where((eb) => {
|
|
982
|
+
const uncredited = eb.not(creditRenders(eb));
|
|
983
|
+
return filter.includeInferred ? eb.and([uncredited, eb.or([hasExplicitCredit(eb), eb.not(authorHasByline(eb))])]) : uncredited;
|
|
984
|
+
});
|
|
985
|
+
const bylineIds = filter.bylineIds ?? [];
|
|
986
|
+
if (bylineIds.length === 0) return query.where(() => sql`1 = 0`);
|
|
987
|
+
return query.where((eb) => {
|
|
988
|
+
if (!filter.includeInferred) return creditRenders(eb, bylineIds);
|
|
989
|
+
return eb.or([creditRenders(eb, bylineIds), eb.and([eb.not(hasExplicitCredit(eb)), authorHasByline(eb, bylineIds)])]);
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
700
993
|
* Count content items
|
|
701
994
|
*/
|
|
702
995
|
async count(type, where) {
|
|
996
|
+
const resolvedFieldFilters = await this.resolveFieldFilters(type, where?.fieldFilters);
|
|
997
|
+
return this.countWithResolvedFilters(type, where, resolvedFieldFilters);
|
|
998
|
+
}
|
|
999
|
+
async countWithResolvedFilters(type, where, resolvedFieldFilters) {
|
|
703
1000
|
const tableName = getTableName(type);
|
|
704
1001
|
let query = this.db.selectFrom(tableName).select((eb) => eb.fn.count("id").as("count")).where("deleted_at", "is", null);
|
|
705
1002
|
if (where?.status) query = query.where("status", "=", where.status);
|
|
@@ -707,6 +1004,8 @@ var ContentRepository = class {
|
|
|
707
1004
|
if (where?.locale) query = query.where("locale", "=", where.locale);
|
|
708
1005
|
query = this.applySearchFilter(query, where, type);
|
|
709
1006
|
query = this.applyDateFilter(query, where);
|
|
1007
|
+
query = this.applyBylineFilter(query, where, type);
|
|
1008
|
+
query = this.applyFieldFilters(query, resolvedFieldFilters);
|
|
710
1009
|
const result = await query.executeTakeFirst();
|
|
711
1010
|
return Number(result?.count || 0);
|
|
712
1011
|
}
|
|
@@ -720,19 +1019,22 @@ var ContentRepository = class {
|
|
|
720
1019
|
const tableName = getTableName(type);
|
|
721
1020
|
return (await this.db.selectFrom(tableName).select("author_id").distinct().where("deleted_at", "is", null).where("author_id", "is not", null).execute()).map((row) => row.author_id).filter((id) => id !== null);
|
|
722
1021
|
}
|
|
723
|
-
async getStats(type) {
|
|
1022
|
+
async getStats(type, now = /* @__PURE__ */ new Date()) {
|
|
724
1023
|
const tableName = getTableName(type);
|
|
1024
|
+
const nowIso = now.toISOString();
|
|
725
1025
|
const result = await this.db.selectFrom(tableName).select((eb) => [
|
|
726
1026
|
eb.fn.count("id").as("total"),
|
|
727
1027
|
eb.fn.sum(eb.case().when("status", "=", "published").then(1).else(0).end()).as("published"),
|
|
728
1028
|
eb.fn.sum(eb.case().when("status", "=", "draft").then(1).else(0).end()).as("draft"),
|
|
729
|
-
sql`SUM(CASE WHEN scheduled_at IS NOT NULL THEN 1 ELSE 0 END)`.as("scheduled")
|
|
1029
|
+
sql`SUM(CASE WHEN scheduled_at IS NOT NULL THEN 1 ELSE 0 END)`.as("scheduled"),
|
|
1030
|
+
sql`SUM(CASE WHEN scheduled_at IS NOT NULL AND scheduled_at <= ${nowIso} THEN 1 ELSE 0 END)`.as("overdue_scheduled")
|
|
730
1031
|
]).where("deleted_at", "is", null).executeTakeFirst();
|
|
731
1032
|
return {
|
|
732
1033
|
total: Number(result?.total || 0),
|
|
733
1034
|
published: Number(result?.published || 0),
|
|
734
1035
|
draft: Number(result?.draft || 0),
|
|
735
|
-
scheduled: Number(result?.scheduled || 0)
|
|
1036
|
+
scheduled: Number(result?.scheduled || 0),
|
|
1037
|
+
overdueScheduled: Number(result?.overdue_scheduled || 0)
|
|
736
1038
|
};
|
|
737
1039
|
}
|
|
738
1040
|
/**
|
|
@@ -758,7 +1060,6 @@ var ContentRepository = class {
|
|
|
758
1060
|
WHERE id = ${id}
|
|
759
1061
|
AND deleted_at IS NULL
|
|
760
1062
|
`.execute(this.db);
|
|
761
|
-
await this.restampEntryPivot(type, id);
|
|
762
1063
|
invalidateCollectionCache(type);
|
|
763
1064
|
const updated = await this.findById(type, id);
|
|
764
1065
|
if (!updated) throw new Error("Content not found");
|
|
@@ -785,7 +1086,6 @@ var ContentRepository = class {
|
|
|
785
1086
|
AND scheduled_at IS NOT NULL
|
|
786
1087
|
AND deleted_at IS NULL
|
|
787
1088
|
`.execute(this.db);
|
|
788
|
-
await this.restampEntryPivot(type, id);
|
|
789
1089
|
invalidateCollectionCache(type);
|
|
790
1090
|
const updated = await this.findById(type, id);
|
|
791
1091
|
if (!updated) throw new Error("Content not found");
|
|
@@ -835,6 +1135,10 @@ var ContentRepository = class {
|
|
|
835
1135
|
* Lets callers resolve many edge groups without an N+1 per group. The caller
|
|
836
1136
|
* groups the flat result by `translationGroup` itself.
|
|
837
1137
|
*
|
|
1138
|
+
* `translation_group` leads the sort so the ordering follows
|
|
1139
|
+
* `idx_{table}_del_tg_locale` past its `deleted_at` equality; callers group by
|
|
1140
|
+
* `translationGroup`, so the per-group locale order they rely on is preserved.
|
|
1141
|
+
*
|
|
838
1142
|
* `publishedOnly` restricts the result to `status = 'published'` — reference
|
|
839
1143
|
* reads pass this for callers without `content:read_drafts` so draft/scheduled
|
|
840
1144
|
* entries never leak through an edge traversal.
|
|
@@ -856,7 +1160,7 @@ var ContentRepository = class {
|
|
|
856
1160
|
WHERE translation_group IN (${sql.join(chunk)})
|
|
857
1161
|
AND deleted_at IS NULL
|
|
858
1162
|
${publishedFilter}
|
|
859
|
-
ORDER BY locale ASC
|
|
1163
|
+
ORDER BY translation_group ASC, locale ASC
|
|
860
1164
|
`.execute(this.db);
|
|
861
1165
|
for (const row of result.rows) items.push(this.mapRow(type, row));
|
|
862
1166
|
}
|
|
@@ -925,6 +1229,8 @@ var ContentRepository = class {
|
|
|
925
1229
|
* Syncs the draft revision's data into the content table columns so the
|
|
926
1230
|
* content table always reflects the published version.
|
|
927
1231
|
* If no draft revision exists, creates one from current data and publishes it.
|
|
1232
|
+
* When `promoteRevision` is false, publishes the current content-table data
|
|
1233
|
+
* by changing lifecycle metadata only.
|
|
928
1234
|
*
|
|
929
1235
|
* `publishedAt` (optional) overrides the publication timestamp. If omitted,
|
|
930
1236
|
* the existing `published_at` is preserved (idempotent re-publish keeps the
|
|
@@ -932,98 +1238,155 @@ var ContentRepository = class {
|
|
|
932
1238
|
* an explicit value to backdate a publish (e.g. when migrating content from
|
|
933
1239
|
* another CMS).
|
|
934
1240
|
*
|
|
935
|
-
* `requireDue`
|
|
936
|
-
* `
|
|
937
|
-
*
|
|
938
|
-
* between selection and publish. It claims the row with a single conditional
|
|
939
|
-
* UPDATE (clearing `scheduled_at`) before any other write, so it is atomic
|
|
940
|
-
* even on D1 (no multi-statement transactions) and serialises against
|
|
941
|
-
* `unschedule()` and concurrent sweeps — no TOCTOU and no double publish.
|
|
1241
|
+
* `requireDue` gates the final update on the row still being due.
|
|
1242
|
+
* `expectedScheduledAt` additionally fences changes made after a sweep
|
|
1243
|
+
* selected the row but before publication preparation began.
|
|
942
1244
|
*/
|
|
943
|
-
async publish(type, id, publishedAt, requireDue = false) {
|
|
1245
|
+
async publish(type, id, publishedAt, requireDue = false, expectedScheduledAt, promoteRevision = true, requireSlug = true) {
|
|
944
1246
|
const tableName = getTableName(type);
|
|
945
1247
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
946
1248
|
const existing = await this.findById(type, id);
|
|
947
1249
|
if (!existing) throw new EmDashValidationError("Content item not found");
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if (
|
|
951
|
-
if (((await sql`
|
|
952
|
-
UPDATE ${sql.ref(tableName)}
|
|
953
|
-
SET scheduled_at = NULL,
|
|
954
|
-
updated_at = ${now}
|
|
955
|
-
WHERE id = ${id}
|
|
956
|
-
AND scheduled_at IS NOT NULL
|
|
957
|
-
AND scheduled_at <= ${now}
|
|
958
|
-
AND deleted_at IS NULL
|
|
959
|
-
`.execute(this.db)).numAffectedRows ?? 0n) === 0n) throw new ScheduledNotDueError();
|
|
960
|
-
claimedScheduledAt = existing.scheduledAt;
|
|
961
|
-
claimedUpdatedAt = existing.updatedAt;
|
|
962
|
-
}
|
|
963
|
-
let publishCommitted = false;
|
|
964
|
-
try {
|
|
1250
|
+
if (requireDue && expectedScheduledAt !== void 0 && existing.scheduledAt !== expectedScheduledAt) throw new ScheduledNotDueError();
|
|
1251
|
+
if (!promoteRevision && requireSlug && !existing.slug?.trim()) throw new EmDashValidationError("Cannot publish routable content without a slug");
|
|
1252
|
+
if (!promoteRevision) {
|
|
965
1253
|
const revisionRepo = new RevisionRepository(this.db);
|
|
966
|
-
let
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
if (conflict && conflict.id !== id) throw new EmDashValidationError(`Cannot publish: slug '${stagedSlug}' is already used by another entry in this collection (id: ${conflict.id}). Choose a different slug.`, { code: "SLUG_CONFLICT" });
|
|
1254
|
+
let provisionalRevisionId = null;
|
|
1255
|
+
try {
|
|
1256
|
+
let liveRevisionId = existing.liveRevisionId;
|
|
1257
|
+
if (!liveRevisionId) {
|
|
1258
|
+
const revision = await revisionRepo.create({
|
|
1259
|
+
collection: type,
|
|
1260
|
+
entryId: id,
|
|
1261
|
+
data: existing.data
|
|
1262
|
+
});
|
|
1263
|
+
liveRevisionId = revision.id;
|
|
1264
|
+
provisionalRevisionId = revision.id;
|
|
978
1265
|
}
|
|
979
|
-
|
|
1266
|
+
const intendedPublishedAt = publishedAt ?? existing.publishedAt ?? now;
|
|
1267
|
+
const duePredicate = requireDue ? sql`AND scheduled_at IS NOT NULL AND scheduled_at <= ${now}` : sql``;
|
|
1268
|
+
let published = false;
|
|
1269
|
+
try {
|
|
1270
|
+
published = ((await sql`
|
|
980
1271
|
UPDATE ${sql.ref(tableName)}
|
|
981
|
-
SET
|
|
1272
|
+
SET live_revision_id = ${liveRevisionId},
|
|
1273
|
+
draft_revision_id = NULL,
|
|
1274
|
+
status = 'published',
|
|
1275
|
+
scheduled_at = NULL,
|
|
1276
|
+
published_at = ${intendedPublishedAt},
|
|
1277
|
+
updated_at = ${now},
|
|
1278
|
+
version = version + 1
|
|
982
1279
|
WHERE id = ${id}
|
|
983
|
-
|
|
984
|
-
|
|
1280
|
+
AND deleted_at IS NULL
|
|
1281
|
+
AND version = ${existing.version}
|
|
1282
|
+
AND status = ${existing.status}
|
|
1283
|
+
AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
|
|
1284
|
+
AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
|
|
1285
|
+
AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
|
|
1286
|
+
${duePredicate}
|
|
1287
|
+
`.execute(this.db)).numAffectedRows ?? 0n) > 0n;
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
if (isConfirmedStatementFailure(error)) throw error;
|
|
1290
|
+
let observed;
|
|
1291
|
+
try {
|
|
1292
|
+
observed = await this.findById(type, id);
|
|
1293
|
+
} catch (reconciliationError) {
|
|
1294
|
+
throw new Error("Unable to confirm whether content publication completed", { cause: reconciliationError });
|
|
1295
|
+
}
|
|
1296
|
+
if (!observed) throw new Error("Unable to confirm whether content publication completed", { cause: error });
|
|
1297
|
+
published = matchesLifecyclePublication(observed, existing, liveRevisionId, intendedPublishedAt, now);
|
|
1298
|
+
if (!published && matchesPublicationFence(observed, existing)) throw error;
|
|
1299
|
+
if (!published) throw new Error("Unable to confirm whether content publication completed", { cause: error });
|
|
1300
|
+
}
|
|
1301
|
+
if (!published) throw requireDue ? new ScheduledNotDueError() : new ContentMutationConflictError();
|
|
1302
|
+
invalidateCollectionCache(type);
|
|
1303
|
+
const updated = await this.findById(type, id);
|
|
1304
|
+
if (!updated) throw new Error("Content not found");
|
|
1305
|
+
return updated;
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
if (provisionalRevisionId) try {
|
|
1308
|
+
await revisionRepo.deleteIfUnreferenced(type, id, provisionalRevisionId);
|
|
1309
|
+
} catch (cleanupError) {
|
|
1310
|
+
console.error(`[content] Failed to clean up provisional revision ${provisionalRevisionId}:`, cleanupError);
|
|
1311
|
+
}
|
|
1312
|
+
throw error;
|
|
985
1313
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1314
|
+
}
|
|
1315
|
+
const revisionRepo = new RevisionRepository(this.db);
|
|
1316
|
+
let provisionalRevisionId = null;
|
|
1317
|
+
try {
|
|
1318
|
+
let revisionToPublish = existing.draftRevisionId || existing.liveRevisionId;
|
|
1319
|
+
if (!revisionToPublish) {
|
|
1320
|
+
const revision = await revisionRepo.create({
|
|
1321
|
+
collection: type,
|
|
1322
|
+
entryId: id,
|
|
1323
|
+
data: existing.data
|
|
1324
|
+
});
|
|
1325
|
+
revisionToPublish = revision.id;
|
|
1326
|
+
provisionalRevisionId = revision.id;
|
|
1327
|
+
}
|
|
1328
|
+
const revision = await revisionRepo.findById(revisionToPublish);
|
|
1329
|
+
if (!revision || revision.collection !== type || revision.entryId !== id) throw new EmDashValidationError("Revision does not belong to the specified content item");
|
|
1330
|
+
const stagedSlug = typeof revision.data._slug === "string" ? revision.data._slug : null;
|
|
1331
|
+
const intendedSlug = stagedSlug ?? existing.slug;
|
|
1332
|
+
if (requireSlug && !intendedSlug?.trim()) throw new EmDashValidationError("Cannot publish routable content without a slug");
|
|
1333
|
+
const intendedPublishedAt = publishedAt ?? existing.publishedAt ?? now;
|
|
1334
|
+
if (stagedSlug !== null && stagedSlug !== existing.slug && existing.locale !== null) {
|
|
1335
|
+
const conflict = await this.findBySlugIncludingTrashed(type, stagedSlug, existing.locale);
|
|
1336
|
+
if (conflict && conflict.id !== id) throw new EmDashValidationError(`Cannot publish: slug '${stagedSlug}' is already used by another entry in this collection (id: ${conflict.id}). Choose a different slug.`, { code: "SLUG_CONFLICT" });
|
|
1337
|
+
}
|
|
1338
|
+
const assignments = [];
|
|
1339
|
+
if (stagedSlug !== null) assignments.push(sql`slug = ${stagedSlug}`);
|
|
1340
|
+
for (const [key, value] of Object.entries(revision.data)) {
|
|
1341
|
+
if (SYSTEM_COLUMNS.has(key) || key.startsWith("_")) continue;
|
|
1342
|
+
validateIdentifier(key, "content field name");
|
|
1343
|
+
assignments.push(sql`${sql.ref(key)} = ${serializeValue(value)}`);
|
|
1344
|
+
}
|
|
1345
|
+
assignments.push(sql`live_revision_id = ${revisionToPublish}`, sql`draft_revision_id = NULL`, sql`status = 'published'`, sql`scheduled_at = NULL`, sql`published_at = ${intendedPublishedAt}`, sql`updated_at = ${now}`, sql`version = version + 1`);
|
|
1346
|
+
const duePredicate = requireDue ? sql`AND scheduled_at IS NOT NULL AND scheduled_at <= ${now}` : sql``;
|
|
1347
|
+
let promoted = false;
|
|
1348
|
+
try {
|
|
1349
|
+
promoted = ((await sql`
|
|
998
1350
|
UPDATE ${sql.ref(tableName)}
|
|
999
|
-
SET
|
|
1000
|
-
draft_revision_id = NULL,
|
|
1001
|
-
status = 'published',
|
|
1002
|
-
scheduled_at = NULL,
|
|
1003
|
-
published_at = COALESCE(published_at, ${now}),
|
|
1004
|
-
updated_at = ${now}
|
|
1351
|
+
SET ${sql.join(assignments, sql`, `)}
|
|
1005
1352
|
WHERE id = ${id}
|
|
1006
1353
|
AND deleted_at IS NULL
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1354
|
+
AND version = ${existing.version}
|
|
1355
|
+
AND status = ${existing.status}
|
|
1356
|
+
AND ${nullableColumnMatch("live_revision_id", existing.liveRevisionId)}
|
|
1357
|
+
AND ${nullableColumnMatch("draft_revision_id", existing.draftRevisionId)}
|
|
1358
|
+
AND ${nullableColumnMatch("scheduled_at", existing.scheduledAt)}
|
|
1359
|
+
${duePredicate}
|
|
1360
|
+
AND EXISTS (
|
|
1361
|
+
SELECT 1 FROM revisions
|
|
1362
|
+
WHERE revisions.id = ${revisionToPublish}
|
|
1363
|
+
AND revisions.collection = ${type}
|
|
1364
|
+
AND revisions.entry_id = ${id}
|
|
1365
|
+
)
|
|
1366
|
+
`.execute(this.db)).numAffectedRows ?? 0n) > 0n;
|
|
1367
|
+
} catch (error) {
|
|
1368
|
+
if (isConfirmedStatementFailure(error)) throw error;
|
|
1369
|
+
let observed;
|
|
1370
|
+
try {
|
|
1371
|
+
observed = await this.findById(type, id);
|
|
1372
|
+
} catch (reconciliationError) {
|
|
1373
|
+
throw new Error("Unable to confirm whether content publication completed", { cause: reconciliationError });
|
|
1374
|
+
}
|
|
1375
|
+
if (!observed) throw new Error("Unable to confirm whether content publication completed", { cause: error });
|
|
1376
|
+
promoted = matchesPublication(observed, existing, revision, revisionToPublish, intendedSlug, intendedPublishedAt, now);
|
|
1377
|
+
if (!promoted && matchesPublicationFence(observed, existing)) throw error;
|
|
1378
|
+
if (!promoted) throw new Error("Unable to confirm whether content publication completed", { cause: error });
|
|
1379
|
+
}
|
|
1380
|
+
if (!promoted) throw requireDue ? new ScheduledNotDueError() : new ContentMutationConflictError();
|
|
1381
|
+
invalidateCollectionCache(type);
|
|
1010
1382
|
const updated = await this.findById(type, id);
|
|
1011
1383
|
if (!updated) throw new Error("Content not found");
|
|
1012
|
-
invalidateCollectionCache(type);
|
|
1013
1384
|
return updated;
|
|
1014
1385
|
} catch (error) {
|
|
1015
|
-
if (
|
|
1016
|
-
await
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
updated_at = ${claimedUpdatedAt ?? now}
|
|
1020
|
-
WHERE id = ${id}
|
|
1021
|
-
AND scheduled_at IS NULL
|
|
1022
|
-
AND deleted_at IS NULL
|
|
1023
|
-
AND (status != 'published' OR draft_revision_id IS NOT NULL)
|
|
1024
|
-
`.execute(this.db);
|
|
1025
|
-
} catch (restoreError) {
|
|
1026
|
-
console.error(`[content] Failed to restore schedule for ${type}/${id} after publish failure:`, restoreError);
|
|
1386
|
+
if (provisionalRevisionId) try {
|
|
1387
|
+
await revisionRepo.deleteIfUnreferenced(type, id, provisionalRevisionId);
|
|
1388
|
+
} catch (cleanupError) {
|
|
1389
|
+
console.error(`[content] Failed to clean up provisional revision ${provisionalRevisionId}:`, cleanupError);
|
|
1027
1390
|
}
|
|
1028
1391
|
throw error;
|
|
1029
1392
|
}
|
|
@@ -1064,7 +1427,6 @@ var ContentRepository = class {
|
|
|
1064
1427
|
WHERE id = ${id}
|
|
1065
1428
|
AND deleted_at IS NULL
|
|
1066
1429
|
`.execute(this.db);
|
|
1067
|
-
await this.restampEntryPivot(type, id);
|
|
1068
1430
|
invalidateCollectionCache(type);
|
|
1069
1431
|
const updated = await this.findById(type, id);
|
|
1070
1432
|
if (!updated) throw new Error("Content not found");
|
|
@@ -1082,17 +1444,31 @@ var ContentRepository = class {
|
|
|
1082
1444
|
* content row pointing at a missing or unrelated revision.
|
|
1083
1445
|
*/
|
|
1084
1446
|
async setDraftRevision(type, id, revisionId) {
|
|
1085
|
-
const
|
|
1086
|
-
if (!
|
|
1447
|
+
const existing = await this.findById(type, id);
|
|
1448
|
+
if (!existing) throw new EmDashValidationError("Content item not found");
|
|
1087
1449
|
const revision = await new RevisionRepository(this.db).findById(revisionId);
|
|
1088
1450
|
if (!revision) throw new EmDashValidationError("Revision not found");
|
|
1089
1451
|
if (revision.collection !== type || revision.entryId !== id) throw new EmDashValidationError("Revision does not belong to the specified content item");
|
|
1090
|
-
await
|
|
1452
|
+
if (!await this.replaceDraftRevision(type, id, revisionId, existing)) throw new ContentMutationConflictError();
|
|
1453
|
+
}
|
|
1454
|
+
async replaceDraftRevision(type, id, revisionId, expected) {
|
|
1455
|
+
const tableName = getTableName(type);
|
|
1456
|
+
return ((await sql`
|
|
1091
1457
|
UPDATE ${sql.ref(tableName)}
|
|
1092
|
-
SET draft_revision_id = ${revisionId}
|
|
1458
|
+
SET draft_revision_id = ${revisionId},
|
|
1459
|
+
version = version + 1
|
|
1093
1460
|
WHERE id = ${id}
|
|
1094
1461
|
AND deleted_at IS NULL
|
|
1095
|
-
|
|
1462
|
+
AND version = ${expected.version}
|
|
1463
|
+
AND ${nullableColumnMatch("live_revision_id", expected.liveRevisionId)}
|
|
1464
|
+
AND ${nullableColumnMatch("draft_revision_id", expected.draftRevisionId)}
|
|
1465
|
+
AND EXISTS (
|
|
1466
|
+
SELECT 1 FROM revisions
|
|
1467
|
+
WHERE revisions.id = ${revisionId}
|
|
1468
|
+
AND revisions.collection = ${type}
|
|
1469
|
+
AND revisions.entry_id = ${id}
|
|
1470
|
+
)
|
|
1471
|
+
`.execute(this.db)).numAffectedRows ?? 0n) > 0n;
|
|
1096
1472
|
}
|
|
1097
1473
|
/**
|
|
1098
1474
|
* Discard pending draft changes
|
|
@@ -1116,23 +1492,6 @@ var ContentRepository = class {
|
|
|
1116
1492
|
return updated;
|
|
1117
1493
|
}
|
|
1118
1494
|
/**
|
|
1119
|
-
* Sync data columns in the content table from a data object.
|
|
1120
|
-
* Used to promote revision data into the content table on publish.
|
|
1121
|
-
* Keys starting with _ are revision metadata (e.g. _slug) and are skipped.
|
|
1122
|
-
*/
|
|
1123
|
-
async syncDataColumns(type, id, data) {
|
|
1124
|
-
const tableName = getTableName(type);
|
|
1125
|
-
const updates = {};
|
|
1126
|
-
for (const [key, value] of Object.entries(data)) {
|
|
1127
|
-
if (SYSTEM_COLUMNS.has(key)) continue;
|
|
1128
|
-
if (key.startsWith("_")) continue;
|
|
1129
|
-
validateIdentifier(key, "content field name");
|
|
1130
|
-
updates[key] = serializeValue(value);
|
|
1131
|
-
}
|
|
1132
|
-
if (Object.keys(updates).length === 0) return;
|
|
1133
|
-
await this.db.updateTable(tableName).set(updates).where("id", "=", id).execute();
|
|
1134
|
-
}
|
|
1135
|
-
/**
|
|
1136
1495
|
* Count content items with a pending schedule.
|
|
1137
1496
|
* Includes both draft-scheduled (status='scheduled') and published
|
|
1138
1497
|
* posts with scheduled draft changes (status='published', scheduled_at set).
|
|
@@ -1173,28 +1532,153 @@ var ContentRepository = class {
|
|
|
1173
1532
|
translationGroup: row.translation_group ?? null
|
|
1174
1533
|
};
|
|
1175
1534
|
}
|
|
1535
|
+
normalizeFilterScalar(field, type, value) {
|
|
1536
|
+
if (type === "number" || type === "integer") {
|
|
1537
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new EmDashValidationError(`Filter for field "${field}" must use a finite number`);
|
|
1538
|
+
if (type === "integer" && !Number.isInteger(value)) throw new EmDashValidationError(`Filter for field "${field}" must use an integer`);
|
|
1539
|
+
return value;
|
|
1540
|
+
}
|
|
1541
|
+
if (type === "boolean") {
|
|
1542
|
+
if (typeof value !== "boolean") throw new EmDashValidationError(`Filter for field "${field}" must use a boolean`);
|
|
1543
|
+
return value ? 1 : 0;
|
|
1544
|
+
}
|
|
1545
|
+
if (typeof value !== "string") throw new EmDashValidationError(`Filter for field "${field}" must use a string`);
|
|
1546
|
+
if (value.length > MAX_FILTER_STRING_LENGTH) throw new EmDashValidationError(`Filter value for field "${field}" exceeds ${MAX_FILTER_STRING_LENGTH} characters`);
|
|
1547
|
+
return value;
|
|
1548
|
+
}
|
|
1549
|
+
normalizeFieldFilter(field, type, value) {
|
|
1550
|
+
if (value === null) return {
|
|
1551
|
+
column: field,
|
|
1552
|
+
kind: "null"
|
|
1553
|
+
};
|
|
1554
|
+
if (typeof value !== "object") return {
|
|
1555
|
+
column: field,
|
|
1556
|
+
kind: "exact",
|
|
1557
|
+
value: this.normalizeFilterScalar(field, type, value)
|
|
1558
|
+
};
|
|
1559
|
+
if (Array.isArray(value)) throw new EmDashValidationError(`Invalid filter for field "${field}"`);
|
|
1560
|
+
const record = value;
|
|
1561
|
+
const keys = Object.keys(record);
|
|
1562
|
+
if (keys.length === 1 && keys[0] === "in") {
|
|
1563
|
+
if (!Array.isArray(record.in) || record.in.length === 0) throw new EmDashValidationError(`IN filter for field "${field}" must not be empty`);
|
|
1564
|
+
if (record.in.length > MAX_IN_FILTER_VALUES) throw new EmDashValidationError(`IN filter for field "${field}" exceeds ${MAX_IN_FILTER_VALUES} values`);
|
|
1565
|
+
return {
|
|
1566
|
+
column: field,
|
|
1567
|
+
kind: "in",
|
|
1568
|
+
values: record.in.map((entry) => this.normalizeFilterScalar(field, type, entry))
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
const rangeKeys = new Set([
|
|
1572
|
+
"gt",
|
|
1573
|
+
"gte",
|
|
1574
|
+
"lt",
|
|
1575
|
+
"lte"
|
|
1576
|
+
]);
|
|
1577
|
+
if (keys.length === 0 || keys.some((key) => !rangeKeys.has(key))) throw new EmDashValidationError(`Invalid filter operator for field "${field}"`);
|
|
1578
|
+
if (type === "boolean") throw new EmDashValidationError(`Boolean field "${field}" does not support range filters`);
|
|
1579
|
+
const bounds = {};
|
|
1580
|
+
for (const key of keys) {
|
|
1581
|
+
if (record[key] === void 0) continue;
|
|
1582
|
+
bounds[key] = this.normalizeFilterScalar(field, type, record[key]);
|
|
1583
|
+
}
|
|
1584
|
+
if (Object.keys(bounds).length === 0) throw new EmDashValidationError(`Range filter for field "${field}" has no bounds`);
|
|
1585
|
+
return {
|
|
1586
|
+
column: field,
|
|
1587
|
+
kind: "range",
|
|
1588
|
+
bounds
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
async collectionExists(type) {
|
|
1592
|
+
return await this.db.selectFrom("_emdash_collections").where("slug", "=", type).select("id").executeTakeFirst() !== void 0;
|
|
1593
|
+
}
|
|
1594
|
+
async resolveFieldFilters(type, filters) {
|
|
1595
|
+
const resolvedFilters = filters ?? {};
|
|
1596
|
+
const fields = Object.keys(resolvedFilters);
|
|
1597
|
+
if (fields.length === 0) return [];
|
|
1598
|
+
if (fields.length > MAX_INDEXED_FIELD_FILTERS) {
|
|
1599
|
+
if (!await this.collectionExists(type)) return [];
|
|
1600
|
+
throw new EmDashValidationError(`Content list queries support at most ${MAX_INDEXED_FIELD_FILTERS} indexed field filters`);
|
|
1601
|
+
}
|
|
1602
|
+
const rows = await this.db.selectFrom("_emdash_fields as field").innerJoin("_emdash_collections as collection", "collection.id", "field.collection_id").where("collection.slug", "=", type).where("field.slug", "in", fields).where("field.indexed", "=", 1).select(["field.slug", "field.type"]).execute();
|
|
1603
|
+
const metadata = new Map(rows.map((row) => [row.slug, row.type]));
|
|
1604
|
+
if (metadata.size === 0 && !await this.collectionExists(type)) return [];
|
|
1605
|
+
for (const field of fields) try {
|
|
1606
|
+
validateIdentifier(field, "content filter field");
|
|
1607
|
+
} catch {
|
|
1608
|
+
throw new EmDashValidationError(`Invalid content filter field: ${field}`);
|
|
1609
|
+
}
|
|
1610
|
+
const normalized = fields.map((field) => {
|
|
1611
|
+
const fieldType = metadata.get(field);
|
|
1612
|
+
if (!fieldType || !isIndexableFieldType(fieldType)) throw new EmDashValidationError(`Cannot filter by field "${field}". Custom fields must be indexed before filtering.`);
|
|
1613
|
+
return this.normalizeFieldFilter(field, fieldType, resolvedFilters[field]);
|
|
1614
|
+
});
|
|
1615
|
+
if (normalized.reduce((total, filter) => {
|
|
1616
|
+
if (filter.kind === "null") return total;
|
|
1617
|
+
if (filter.kind === "exact") return total + 1;
|
|
1618
|
+
if (filter.kind === "in") return total + filter.values.length;
|
|
1619
|
+
return total + Object.keys(filter.bounds).length;
|
|
1620
|
+
}, 0) > SQL_BATCH_SIZE) throw new EmDashValidationError(`Indexed field filters have a total operand budget of ${SQL_BATCH_SIZE}`);
|
|
1621
|
+
return normalized;
|
|
1622
|
+
}
|
|
1623
|
+
applyFieldFilters(query, filters) {
|
|
1624
|
+
let next = query;
|
|
1625
|
+
for (const filter of filters) {
|
|
1626
|
+
const column = sql.ref(filter.column);
|
|
1627
|
+
const isPresent = sql`${column} IS NOT NULL`;
|
|
1628
|
+
if (filter.kind === "null") {
|
|
1629
|
+
next = next.where(() => sql`(${isPresent}) = FALSE AND ${column} IS NULL`);
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
if (filter.kind === "exact") {
|
|
1633
|
+
next = next.where(() => sql`(${isPresent}) = TRUE AND ${column} = ${filter.value}`);
|
|
1634
|
+
continue;
|
|
1635
|
+
}
|
|
1636
|
+
if (filter.kind === "in") {
|
|
1637
|
+
const values = sql.join(filter.values.map((value) => sql`${value}`), sql`, `);
|
|
1638
|
+
next = next.where(() => sql`(${isPresent}) = TRUE AND ${column} IN (${values})`);
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
next = next.where(() => sql`(${isPresent}) = TRUE`);
|
|
1642
|
+
if (filter.bounds.gt !== void 0) next = next.where(() => sql`${column} > ${filter.bounds.gt}`);
|
|
1643
|
+
if (filter.bounds.gte !== void 0) next = next.where(() => sql`${column} >= ${filter.bounds.gte}`);
|
|
1644
|
+
if (filter.bounds.lt !== void 0) next = next.where(() => sql`${column} < ${filter.bounds.lt}`);
|
|
1645
|
+
if (filter.bounds.lte !== void 0) next = next.where(() => sql`${column} <= ${filter.bounds.lte}`);
|
|
1646
|
+
}
|
|
1647
|
+
return next;
|
|
1648
|
+
}
|
|
1176
1649
|
/**
|
|
1177
1650
|
* Map order field names to database columns.
|
|
1178
1651
|
* Only allows known fields to prevent column enumeration via crafted orderBy values.
|
|
1179
1652
|
*/
|
|
1180
|
-
mapOrderField(field) {
|
|
1181
|
-
const mapped =
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1653
|
+
mapOrderField(field, sortableExtras = []) {
|
|
1654
|
+
const mapped = ORDER_FIELD_COLUMNS[field];
|
|
1655
|
+
if (mapped) return mapped;
|
|
1656
|
+
if (sortableExtras.includes(field)) {
|
|
1657
|
+
validateIdentifier(field, "order field");
|
|
1658
|
+
return field;
|
|
1659
|
+
}
|
|
1660
|
+
throw new EmDashValidationError(`Invalid order field: ${field}`);
|
|
1661
|
+
}
|
|
1662
|
+
async resolveOrderField(type, field, sortableExtras = []) {
|
|
1663
|
+
try {
|
|
1664
|
+
return {
|
|
1665
|
+
column: this.mapOrderField(field, sortableExtras),
|
|
1666
|
+
indexedCustomField: false
|
|
1667
|
+
};
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
if (!(error instanceof EmDashValidationError)) throw error;
|
|
1670
|
+
}
|
|
1671
|
+
const customField = await this.db.selectFrom("_emdash_collections as collection").leftJoin("_emdash_fields as field", (join) => join.onRef("field.collection_id", "=", "collection.id").on("field.slug", "=", field).on("field.indexed", "=", 1)).where("collection.slug", "=", type).select(["collection.id as collectionId", "field.slug as fieldSlug"]).executeTakeFirst();
|
|
1672
|
+
if (!customField) throw new ContentCollectionNotFoundError(type);
|
|
1673
|
+
if (!customField.fieldSlug) throw new EmDashValidationError(`Invalid order field: ${field}. Custom fields must be indexed before sorting.`);
|
|
1674
|
+
validateIdentifier(customField.fieldSlug, "content order field");
|
|
1675
|
+
return {
|
|
1676
|
+
column: customField.fieldSlug,
|
|
1677
|
+
indexedCustomField: true
|
|
1678
|
+
};
|
|
1195
1679
|
}
|
|
1196
1680
|
};
|
|
1197
1681
|
|
|
1198
1682
|
//#endregion
|
|
1199
|
-
export { content_exports as n,
|
|
1200
|
-
//# sourceMappingURL=content-
|
|
1683
|
+
export { RevisionRepository as i, content_exports as n, isSystemOrderField as r, ContentRepository as t };
|
|
1684
|
+
//# sourceMappingURL=content-BNfwh7xX.mjs.map
|