emdash 0.18.0 → 0.20.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-C5AWLJSD.d.mts → adapters-BzIHV3sw.d.mts} +1 -1
- package/dist/{adapters-C5AWLJSD.d.mts.map → adapters-BzIHV3sw.d.mts.map} +1 -1
- package/dist/{allowed-origins-CyYLEJkp.mjs → allowed-origins-B1u7Qnvg.mjs} +2 -2
- package/dist/{allowed-origins-CyYLEJkp.mjs.map → allowed-origins-B1u7Qnvg.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +15 -15
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-Cs7DAACP.mjs → api-DStv36ik.mjs} +123 -20
- package/dist/api-DStv36ik.mjs.map +1 -0
- package/dist/{api-tokens-VrXNiNvV.mjs → api-tokens-DPfhPu5V.mjs} +2 -2
- package/dist/{api-tokens-VrXNiNvV.mjs.map → api-tokens-DPfhPu5V.mjs.map} +1 -1
- package/dist/{apply-BWMV4Zmw.mjs → apply-Dr7snAMT.mjs} +23 -23
- package/dist/apply-Dr7snAMT.mjs.map +1 -0
- package/dist/astro/index.d.mts +10 -10
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +115 -25
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +9 -9
- package/dist/astro/middleware/auth.mjs +6 -6
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware/request-context.mjs +2 -2
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +26 -4
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +242 -259
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -5
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +8 -8
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/index.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +11 -11
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +8 -8
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +8 -8
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +5 -5
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/updates.mjs +33 -33
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +33 -33
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +33 -33
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
- package/dist/astro/routes/api/admin/users/index.mjs +5 -5
- package/dist/astro/routes/api/auth/dev-bypass.mjs +5 -5
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
- package/dist/astro/routes/api/auth/invite/register-options.mjs +9 -9
- package/dist/astro/routes/api/auth/logout.mjs +3 -3
- package/dist/astro/routes/api/auth/magic-link/send.mjs +8 -8
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +6 -6
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- 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 +5 -5
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +9 -9
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/request.mjs +8 -8
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +9 -8
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +12 -10
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +9 -8
- package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/authors.d.mts +8 -0
- package/dist/astro/routes/api/content/_collection_/authors.d.mts.map +1 -0
- package/dist/astro/routes/api/content/_collection_/authors.mjs +19 -0
- package/dist/astro/routes/api/content/_collection_/authors.mjs.map +1 -0
- package/dist/astro/routes/api/content/_collection_/index.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -6
- package/dist/astro/routes/api/dashboard.mjs +7 -7
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/media.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +9 -9
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/mcp.mjs +29 -29
- package/dist/astro/routes/api/media/_id_/confirm.mjs +6 -6
- package/dist/astro/routes/api/media/_id_.mjs +6 -6
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.mjs +7 -7
- package/dist/astro/routes/api/media.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_/items.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_/translations.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_.mjs +7 -7
- package/dist/astro/routes/api/menus/index.mjs +7 -7
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
- package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
- package/dist/astro/routes/api/oauth/register.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +17 -3
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
- package/dist/astro/routes/api/redirects/404s/index.mjs +9 -9
- package/dist/astro/routes/api/redirects/404s/summary.mjs +9 -9
- package/dist/astro/routes/api/redirects/_id_.mjs +10 -10
- package/dist/astro/routes/api/redirects/index.mjs +10 -10
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/index.mjs +33 -33
- package/dist/astro/routes/api/schema/index.mjs +9 -14
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +33 -33
- package/dist/astro/routes/api/schema/orphans/index.mjs +33 -33
- package/dist/astro/routes/api/search/enable.mjs +9 -9
- package/dist/astro/routes/api/search/index.mjs +8 -8
- package/dist/astro/routes/api/search/rebuild.mjs +9 -9
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +8 -8
- package/dist/astro/routes/api/sections/_slug_.mjs +8 -8
- package/dist/astro/routes/api/sections/index.mjs +8 -8
- package/dist/astro/routes/api/settings/email.mjs +5 -5
- package/dist/astro/routes/api/settings.mjs +12 -12
- package/dist/astro/routes/api/setup/admin-verify.mjs +11 -11
- package/dist/astro/routes/api/setup/admin.mjs +10 -10
- package/dist/astro/routes/api/setup/dev-bypass.mjs +23 -23
- package/dist/astro/routes/api/setup/dev-reset.mjs +3 -3
- package/dist/astro/routes/api/setup/index.mjs +23 -23
- package/dist/astro/routes/api/setup/status.mjs +4 -4
- package/dist/astro/routes/api/snapshot.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/index.mjs +11 -11
- package/dist/astro/routes/api/themes/preview.mjs +6 -6
- package/dist/astro/routes/api/typegen.mjs +5 -5
- 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 +6 -6
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +9 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +9 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -5
- package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.mjs +9 -8
- package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
- package/dist/astro/routes/api/widget-components.mjs +3 -3
- package/dist/astro/routes/robots.txt.mjs +7 -7
- package/dist/astro/routes/sitemap-_collection_.xml.d.mts.map +1 -1
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +16 -9
- package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
- package/dist/astro/routes/sitemap.xml.mjs +8 -8
- package/dist/astro/types.d.mts +19 -12
- 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-CotM4Yiu.mjs → authorize-DsMSVSaY.mjs} +2 -2
- package/dist/{authorize-CotM4Yiu.mjs.map → authorize-DsMSVSaY.mjs.map} +1 -1
- package/dist/{byline-CWQ9aSoz.mjs → byline-DUx48sJp.mjs} +6 -6
- package/dist/{byline-CWQ9aSoz.mjs.map → byline-DUx48sJp.mjs.map} +1 -1
- package/dist/{byline-fields-DC3Wkk-U.mjs → byline-fields--WxSNS79.mjs} +2 -2
- package/dist/{byline-fields-DC3Wkk-U.mjs.map → byline-fields--WxSNS79.mjs.map} +1 -1
- package/dist/{byline-fields-Dr-xcb6S.mjs → byline-fields-8TMtkBnH.mjs} +3 -3
- package/dist/{byline-fields-Dr-xcb6S.mjs.map → byline-fields-8TMtkBnH.mjs.map} +1 -1
- package/dist/{byline-fields-BNy7Ng1U.d.mts → byline-fields-DbibsvTl.d.mts} +30 -2
- package/dist/byline-fields-DbibsvTl.d.mts.map +1 -0
- package/dist/{byline-registry-CxK5g559.mjs → byline-registry-CWP7I71B.mjs} +3 -3
- package/dist/{byline-registry-CxK5g559.mjs.map → byline-registry-CWP7I71B.mjs.map} +1 -1
- package/dist/{bylines-LJMgENMI.mjs → bylines-BdxWCnPL.mjs} +3 -3
- package/dist/{bylines-LJMgENMI.mjs.map → bylines-BdxWCnPL.mjs.map} +1 -1
- package/dist/{bylines-BJSva1Un.mjs → bylines-s8c2DXbH.mjs} +50 -6
- package/dist/{bylines-BJSva1Un.mjs.map → bylines-s8c2DXbH.mjs.map} +1 -1
- package/dist/{cache-lZL7SgVb.mjs → cache-B_HzASVT.mjs} +3 -3
- package/dist/{cache-lZL7SgVb.mjs.map → cache-B_HzASVT.mjs.map} +1 -1
- package/dist/{challenge-store-DGwuCc4R.mjs → challenge-store-DXX3rfdI.mjs} +1 -1
- package/dist/{challenge-store-DGwuCc4R.mjs.map → challenge-store-DXX3rfdI.mjs.map} +1 -1
- package/dist/{chunks-BU-vP9Dh.mjs → chunks-BerYVuve.mjs} +2 -2
- package/dist/{chunks-BU-vP9Dh.mjs.map → chunks-BerYVuve.mjs.map} +1 -1
- package/dist/cli/index.mjs +46 -32
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/{comment-C4jVbCM8.mjs → comment-sqQxNpN3.mjs} +2 -2
- package/dist/{comment-C4jVbCM8.mjs.map → comment-sqQxNpN3.mjs.map} +1 -1
- package/dist/{comments-BTAbC0Ek.mjs → comments-Vkivawyl.mjs} +3 -3
- package/dist/{comments-BTAbC0Ek.mjs.map → comments-Vkivawyl.mjs.map} +1 -1
- package/dist/{components-CTfpu3PZ.mjs → components-CK0cuUoH.mjs} +1 -1
- package/dist/{components-CTfpu3PZ.mjs.map → components-CK0cuUoH.mjs.map} +1 -1
- package/dist/{content-CyqOmOzm.mjs → content-BIlVx-RX.mjs} +132 -43
- package/dist/content-BIlVx-RX.mjs.map +1 -0
- package/dist/{context-DZ7bEh5-.mjs → context-Y7BRkWes.mjs} +10 -10
- package/dist/{context-DZ7bEh5-.mjs.map → context-Y7BRkWes.mjs.map} +1 -1
- package/dist/{cron-DZovZUnC.mjs → cron-BJ2ClIlj.mjs} +4 -3
- package/dist/cron-BJ2ClIlj.mjs.map +1 -0
- package/dist/{dashboard-B5WQpNTP.mjs → dashboard-2JgAMWxK.mjs} +4 -4
- package/dist/{dashboard-B5WQpNTP.mjs.map → dashboard-2JgAMWxK.mjs.map} +1 -1
- package/dist/database/instrumentation.d.mts +10 -1
- package/dist/database/instrumentation.d.mts.map +1 -1
- package/dist/database/instrumentation.mjs +13 -1
- package/dist/database/instrumentation.mjs.map +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{default-xLFNSsZ9.mjs → default-IlBaTFxM.mjs} +1 -1
- package/dist/{default-xLFNSsZ9.mjs.map → default-IlBaTFxM.mjs.map} +1 -1
- package/dist/{device-flow-ptLrVINd.mjs → device-flow-R23SIbQ2.mjs} +5 -5
- package/dist/{device-flow-ptLrVINd.mjs.map → device-flow-R23SIbQ2.mjs.map} +1 -1
- package/dist/{error-DJOsMVSt.mjs → error-RwM4dD35.mjs} +2 -2
- package/dist/{error-DJOsMVSt.mjs.map → error-RwM4dD35.mjs.map} +1 -1
- package/dist/{escape-bIyGoW5W.mjs → escape-Ds07EEyu.mjs} +1 -1
- package/dist/{escape-bIyGoW5W.mjs.map → escape-Ds07EEyu.mjs.map} +1 -1
- package/dist/{fts-manager-DR1ERA0c.mjs → fts-manager-1RgHmopc.mjs} +2 -2
- package/dist/{fts-manager-DR1ERA0c.mjs.map → fts-manager-1RgHmopc.mjs.map} +1 -1
- package/dist/{index-CjKdMZ3U.d.mts → index-B1keaX5Y.d.mts} +237 -24
- package/dist/index-B1keaX5Y.d.mts.map +1 -0
- package/dist/{index-D60_SzHG.d.mts → index-DR56od45.d.mts} +3 -3
- package/dist/{index-D60_SzHG.d.mts.map → index-DR56od45.d.mts.map} +1 -1
- package/dist/index.d.mts +17 -17
- package/dist/index.mjs +46 -46
- package/dist/{load-6ZrRhepW.mjs → load-BBetCvLC.mjs} +2 -2
- package/dist/{load-6ZrRhepW.mjs.map → load-BBetCvLC.mjs.map} +1 -1
- package/dist/{loader-Dyx8dhFV.mjs → loader-ZN1ll-d-.mjs} +36 -37
- package/dist/loader-ZN1ll-d-.mjs.map +1 -0
- package/dist/{manifest-schema-Cj-YrzrF.mjs → manifest-schema-BtwbL_vj.mjs} +55 -2
- package/dist/manifest-schema-BtwbL_vj.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +11 -11
- package/dist/media/local-runtime.mjs +6 -6
- package/dist/{media-C-oovGCG.mjs → media-JOf3pNkw.mjs} +2 -2
- package/dist/{media-C-oovGCG.mjs.map → media-JOf3pNkw.mjs.map} +1 -1
- package/dist/{media-allowlist-CMcoYIjQ.mjs → media-allowlist-Dknq-OFY.mjs} +1 -1
- package/dist/{media-allowlist-CMcoYIjQ.mjs.map → media-allowlist-Dknq-OFY.mjs.map} +1 -1
- package/dist/media-url-VClf8glU.mjs +26 -0
- package/dist/media-url-VClf8glU.mjs.map +1 -0
- package/dist/{menus-DugoYwTX.mjs → menus-DX4_E01q.mjs} +3 -3
- package/dist/{menus-DugoYwTX.mjs.map → menus-DX4_E01q.mjs.map} +1 -1
- package/dist/{menus-BKkxXCmd.mjs → menus-DrQLusqj.mjs} +87 -37
- package/dist/menus-DrQLusqj.mjs.map +1 -0
- package/dist/{mode-BjlXswIw.mjs → mode-CO2vQHfq.mjs} +1 -1
- package/dist/{mode-BjlXswIw.mjs.map → mode-CO2vQHfq.mjs.map} +1 -1
- package/dist/{normalize-DVV8nbrL.mjs → normalize-CK5o04zr.mjs} +2 -2
- package/dist/{normalize-DVV8nbrL.mjs.map → normalize-CK5o04zr.mjs.map} +1 -1
- package/dist/{oauth-authorization-DvBAL75d.mjs → oauth-authorization-Bw4NdF_S.mjs} +5 -5
- package/dist/{oauth-authorization-DvBAL75d.mjs.map → oauth-authorization-Bw4NdF_S.mjs.map} +1 -1
- package/dist/{oauth-clients-8mPDStMv.mjs → oauth-clients-BGGFp57s.mjs} +1 -1
- package/dist/{oauth-clients-8mPDStMv.mjs.map → oauth-clients-BGGFp57s.mjs.map} +1 -1
- package/dist/{oauth-state-store-BJ7YtrfD.mjs → oauth-state-store-97x0xtN2.mjs} +1 -1
- package/dist/{oauth-state-store-BJ7YtrfD.mjs.map → oauth-state-store-97x0xtN2.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-BdDSDvjF.mjs → oauth-user-lookup-B_vnZHKO.mjs} +1 -1
- package/dist/{oauth-user-lookup-BdDSDvjF.mjs.map → oauth-user-lookup-B_vnZHKO.mjs.map} +1 -1
- package/dist/{options-BL4X94qY.mjs → options-BPCVnesz.mjs} +1 -1
- package/dist/{options-BL4X94qY.mjs.map → options-BPCVnesz.mjs.map} +1 -1
- package/dist/{options-tb7DJROi.d.mts → options-DyYIYpPd.d.mts} +3 -3
- package/dist/{options-tb7DJROi.d.mts.map → options-DyYIYpPd.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-BBkFmLVr.mjs → parse-CrGndy1A.mjs} +2 -2
- package/dist/{parse-BBkFmLVr.mjs.map → parse-CrGndy1A.mjs.map} +1 -1
- package/dist/{passkey-config-BDVM86Tj.mjs → passkey-config-C3QgnQnU.mjs} +1 -1
- package/dist/{passkey-config-BDVM86Tj.mjs.map → passkey-config-C3QgnQnU.mjs.map} +1 -1
- package/dist/{patterns-CqG5Ya3i.mjs → patterns-p-RBdTbM.mjs} +1 -1
- package/dist/{patterns-CqG5Ya3i.mjs.map → patterns-p-RBdTbM.mjs.map} +1 -1
- package/dist/{placeholder-B9lUUEmj.d.mts → placeholder-CVBv5z8k.d.mts} +1 -1
- package/dist/{placeholder-B9lUUEmj.d.mts.map → placeholder-CVBv5z8k.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{public-url-egRHCy1m.mjs → public-url-BFVC2OTJ.mjs} +1 -1
- package/dist/{public-url-egRHCy1m.mjs.map → public-url-BFVC2OTJ.mjs.map} +1 -1
- package/dist/{query-Ctlq1aOk.mjs → query-CbUcI4Xk.mjs} +33 -17
- package/dist/query-CbUcI4Xk.mjs.map +1 -0
- package/dist/{rate-limit-CH6W6ikK.mjs → rate-limit-C7hjdkS5.mjs} +2 -2
- package/dist/{rate-limit-CH6W6ikK.mjs.map → rate-limit-C7hjdkS5.mjs.map} +1 -1
- package/dist/{redirect-Cw3JTlmj.mjs → redirect-B_q19j4v.mjs} +1 -1
- package/dist/{redirect-Cw3JTlmj.mjs.map → redirect-B_q19j4v.mjs.map} +1 -1
- package/dist/{redirect-C6tJA7tk.mjs → redirect-CRWIt8Zj.mjs} +3 -3
- package/dist/{redirect-C6tJA7tk.mjs.map → redirect-CRWIt8Zj.mjs.map} +1 -1
- package/dist/{redirects-C0L9JUk4.mjs → redirects-CCbCqCCd.mjs} +28 -4
- package/dist/redirects-CCbCqCCd.mjs.map +1 -0
- package/dist/{redirects-CacE9eQa.mjs → redirects-DxVoR7PI.mjs} +5 -5
- package/dist/{redirects-CacE9eQa.mjs.map → redirects-DxVoR7PI.mjs.map} +1 -1
- package/dist/{registry-CIDxZbhh.mjs → registry-brYh-rAT.mjs} +6 -6
- package/dist/{registry-CIDxZbhh.mjs.map → registry-brYh-rAT.mjs.map} +1 -1
- package/dist/{request-cache-BYMs-BGX.mjs → request-cache-D32LpnmI.mjs} +1 -1
- package/dist/{request-cache-BYMs-BGX.mjs.map → request-cache-D32LpnmI.mjs.map} +1 -1
- package/dist/request-context.d.mts +7 -0
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs +2 -1
- package/dist/request-context.mjs.map +1 -1
- package/dist/{runner-pt6Wl-l-.mjs → runner--4wMWwKM.mjs} +217 -166
- package/dist/runner--4wMWwKM.mjs.map +1 -0
- package/dist/{runner-DM1yR5qd.d.mts → runner-DTdhuI9i.d.mts} +2 -2
- package/dist/{runner-DM1yR5qd.d.mts.map → runner-DTdhuI9i.d.mts.map} +1 -1
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +2 -2
- package/dist/{schema-B4tk0HAG.mjs → schema-C1E70ug_.mjs} +5 -5
- package/dist/{schema-B4tk0HAG.mjs.map → schema-C1E70ug_.mjs.map} +1 -1
- package/dist/{search-f-fNfwab.mjs → search-B3SGZw91.mjs} +4 -4
- package/dist/{search-f-fNfwab.mjs.map → search-B3SGZw91.mjs.map} +1 -1
- package/dist/{secrets-YYbTgB1w.mjs → secrets-ChPTmy9x.mjs} +2 -2
- package/dist/{secrets-YYbTgB1w.mjs.map → secrets-ChPTmy9x.mjs.map} +1 -1
- package/dist/{sections-biElLfT9.mjs → sections-D_lVzwRZ.mjs} +3 -3
- package/dist/{sections-biElLfT9.mjs.map → sections-D_lVzwRZ.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +17 -17
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.d.mts.map +1 -1
- package/dist/seo/index.mjs +3 -12
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-BR39kvTF.mjs → seo-B5e6y9Wk.mjs} +2 -2
- package/dist/{seo-BR39kvTF.mjs.map → seo-B5e6y9Wk.mjs.map} +1 -1
- package/dist/{seo-DfjLvu8i.mjs → seo-D_LPkOtu.mjs} +4 -3
- package/dist/seo-D_LPkOtu.mjs.map +1 -0
- package/dist/{service-BhR2acnc.mjs → service-ChDcsTBs.mjs} +3 -3
- package/dist/{service-BhR2acnc.mjs.map → service-ChDcsTBs.mjs.map} +1 -1
- package/dist/{settings-D_NJvjgN.mjs → settings-Cv47v9u8.mjs} +3 -3
- package/dist/{settings-D_NJvjgN.mjs.map → settings-Cv47v9u8.mjs.map} +1 -1
- package/dist/settings-DfxiWY_s.mjs +411 -0
- package/dist/settings-DfxiWY_s.mjs.map +1 -0
- package/dist/{setup-complete-VoEZfasi.mjs → setup-complete-yvPE4OsP.mjs} +2 -2
- package/dist/{setup-complete-VoEZfasi.mjs.map → setup-complete-yvPE4OsP.mjs.map} +1 -1
- package/dist/{setup-nonce-Bm0uKqmf.mjs → setup-nonce-C9aFzb94.mjs} +1 -1
- package/dist/{setup-nonce-Bm0uKqmf.mjs.map → setup-nonce-C9aFzb94.mjs.map} +1 -1
- package/dist/{site-url-Cm8-sJy7.mjs → site-url-CnHlmAs9.mjs} +2 -2
- package/dist/{site-url-Cm8-sJy7.mjs.map → site-url-CnHlmAs9.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/{taxonomies-Mhn9rjTQ.mjs → taxonomies-BILwiyGk.mjs} +4 -4
- package/dist/{taxonomies-Mhn9rjTQ.mjs.map → taxonomies-BILwiyGk.mjs.map} +1 -1
- package/dist/{taxonomies-Crtzy4MT.mjs → taxonomies-BdAmbOwx.mjs} +50 -12
- package/dist/taxonomies-BdAmbOwx.mjs.map +1 -0
- package/dist/{taxonomy-DTZrIQpi.mjs → taxonomy-CdllE4oq.mjs} +3 -3
- package/dist/{taxonomy-DTZrIQpi.mjs.map → taxonomy-CdllE4oq.mjs.map} +1 -1
- package/dist/{transaction-NQj4VJ7Z.mjs → transaction-x2tJQ-A1.mjs} +1 -1
- package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-x2tJQ-A1.mjs.map} +1 -1
- package/dist/{transport-OnMNbsIA.d.mts → transport-B7PPP2CC.d.mts} +1 -1
- package/dist/{transport-OnMNbsIA.d.mts.map → transport-B7PPP2CC.d.mts.map} +1 -1
- package/dist/{transport--Ck3RBin.mjs → transport-CmpLD7W3.mjs} +1 -1
- package/dist/{transport--Ck3RBin.mjs.map → transport-CmpLD7W3.mjs.map} +1 -1
- package/dist/{types-DWnN7weG.d.mts → types-BFgrqwSk.d.mts} +1 -1
- package/dist/{types-DWnN7weG.d.mts.map → types-BFgrqwSk.d.mts.map} +1 -1
- package/dist/{types-Qa7-HJJC.d.mts → types-BH8-30hc.d.mts} +1 -1
- package/dist/{types-Qa7-HJJC.d.mts.map → types-BH8-30hc.d.mts.map} +1 -1
- package/dist/{types-DawhLFwy.d.mts → types-BPzXTV9x.d.mts} +26 -1
- package/dist/{types-DawhLFwy.d.mts.map → types-BPzXTV9x.d.mts.map} +1 -1
- package/dist/{types-DbCWhHet.d.mts → types-BUUVn1zr.d.mts} +2 -2
- package/dist/types-BUUVn1zr.d.mts.map +1 -0
- package/dist/{types-K3MDsxpy.mjs → types-BXSUSAjt.mjs} +16 -3
- package/dist/{types-K3MDsxpy.mjs.map → types-BXSUSAjt.mjs.map} +1 -1
- package/dist/{types-DMwSpvcw.d.mts → types-CPAPl93j.d.mts} +9 -3
- package/dist/{types-DMwSpvcw.d.mts.map → types-CPAPl93j.d.mts.map} +1 -1
- package/dist/types-CZI4E3qG.mjs +3 -0
- package/dist/{types-kwqCOUxj.d.mts → types-D4kUqbHh.d.mts} +1 -1
- package/dist/{types-kwqCOUxj.d.mts.map → types-D4kUqbHh.d.mts.map} +1 -1
- package/dist/{types-i8_uzhMD.d.mts → types-DTniiNto.d.mts} +19 -4
- package/dist/types-DTniiNto.d.mts.map +1 -0
- package/dist/{types-D8bhH891.mjs → types-DZk_y-MU.mjs} +1 -1
- package/dist/types-DZk_y-MU.mjs.map +1 -0
- package/dist/{types-DX6v9KzJ.d.mts → types-S15DXXNi.d.mts} +1 -1
- package/dist/{types-DX6v9KzJ.d.mts.map → types-S15DXXNi.d.mts.map} +1 -1
- package/dist/{user-DzEUl5zA.mjs → user-C0um7wrg.mjs} +18 -2
- package/dist/user-C0um7wrg.mjs.map +1 -0
- package/dist/{validate-JCXcsqiY.mjs → validate-Bz4vqcX1.mjs} +6 -3
- package/dist/validate-Bz4vqcX1.mjs.map +1 -0
- package/dist/{validate-Dy6nkNls.d.mts → validate-CNwkPWzz.d.mts} +13 -5
- package/dist/validate-CNwkPWzz.d.mts.map +1 -0
- package/dist/{validation-Bq-VyKJg.mjs → validation-DgGTJm3u.mjs} +5 -5
- package/dist/{validation-Bq-VyKJg.mjs.map → validation-DgGTJm3u.mjs.map} +1 -1
- package/dist/version-D-5txk2m.mjs +7 -0
- package/dist/{version-CnS-Cr8A.mjs.map → version-D-5txk2m.mjs.map} +1 -1
- package/dist/{widgets-Bap1eS1X.mjs → widgets-DZfmAbE4.mjs} +47 -44
- package/dist/widgets-DZfmAbE4.mjs.map +1 -0
- package/dist/{zod-generator-BSDpkqSH.mjs → zod-generator-Djo_VHCt.mjs} +2 -2
- package/dist/{zod-generator-BSDpkqSH.mjs.map → zod-generator-Djo_VHCt.mjs.map} +1 -1
- package/package.json +10 -10
- package/src/api/handlers/content.ts +107 -8
- package/src/api/handlers/index.ts +2 -0
- package/src/api/handlers/marketplace.ts +2 -5
- package/src/api/handlers/registry.ts +70 -0
- package/src/api/handlers/seo.ts +9 -1
- package/src/api/openapi/document.ts +25 -0
- package/src/api/schemas/content.ts +33 -0
- package/src/api/schemas/schema.ts +13 -1
- package/src/astro/integration/index.ts +98 -0
- package/src/astro/integration/routes.ts +6 -0
- package/src/astro/integration/virtual-modules.ts +39 -0
- package/src/astro/integration/vite-config.ts +12 -0
- package/src/astro/middleware.ts +48 -6
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +8 -4
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id].ts +4 -2
- package/src/astro/routes/api/content/[collection]/authors.ts +34 -0
- package/src/astro/routes/api/schema/index.ts +7 -15
- package/src/astro/routes/sitemap-[collection].xml.ts +13 -2
- package/src/astro/types.ts +8 -1
- package/src/bylines/index.ts +57 -0
- package/src/cli/commands/bundle-utils.ts +2 -0
- package/src/cli/commands/export-seed.ts +28 -12
- package/src/cli/commands/secrets.ts +2 -2
- package/src/components/EmDashImage.astro +22 -4
- package/src/components/Image.astro +20 -3
- package/src/database/instrumentation.ts +13 -0
- package/src/database/migrations/043_content_references.ts +121 -0
- package/src/database/migrations/runner.ts +2 -0
- package/src/database/repositories/content.ts +225 -67
- package/src/database/repositories/index.ts +7 -0
- package/src/database/repositories/relation.ts +467 -0
- package/src/database/repositories/types.ts +31 -0
- package/src/database/repositories/user.ts +18 -0
- package/src/database/types.ts +34 -0
- package/src/emdash-runtime.ts +172 -67
- package/src/index.ts +8 -1
- package/src/loader.ts +81 -39
- package/src/media/responsive.ts +125 -0
- package/src/plugins/cron.ts +3 -2
- package/src/plugins/index.ts +5 -0
- package/src/plugins/manifest-schema.ts +75 -0
- package/src/plugins/marketplace.ts +2 -5
- package/src/plugins/scheduler/node.ts +9 -2
- package/src/plugins/types.ts +12 -0
- package/src/query.ts +45 -7
- package/src/request-context.ts +8 -0
- package/src/scheduled-publish.ts +153 -0
- package/src/schema/types.ts +11 -1
- package/src/seed/apply.ts +16 -6
- package/src/seed/types.ts +9 -0
- package/src/seed/validate.ts +15 -0
- package/src/seo/index.ts +2 -28
- package/src/seo/media-url.ts +32 -0
- package/src/settings/index.ts +32 -40
- package/src/taxonomies/index.ts +79 -12
- package/src/utils/isolate-cache.ts +189 -0
- package/src/virtual-modules.d.ts +11 -0
- package/src/widgets/index.ts +57 -54
- package/dist/api-Cs7DAACP.mjs.map +0 -1
- package/dist/apply-BWMV4Zmw.mjs.map +0 -1
- package/dist/byline-fields-BNy7Ng1U.d.mts.map +0 -1
- package/dist/content-CyqOmOzm.mjs.map +0 -1
- package/dist/cron-DZovZUnC.mjs.map +0 -1
- package/dist/index-CjKdMZ3U.d.mts.map +0 -1
- package/dist/loader-Dyx8dhFV.mjs.map +0 -1
- package/dist/manifest-schema-Cj-YrzrF.mjs.map +0 -1
- package/dist/menus-BKkxXCmd.mjs.map +0 -1
- package/dist/query-Ctlq1aOk.mjs.map +0 -1
- package/dist/redirects-C0L9JUk4.mjs.map +0 -1
- package/dist/runner-pt6Wl-l-.mjs.map +0 -1
- package/dist/seo-DfjLvu8i.mjs.map +0 -1
- package/dist/settings-b5zW1R1T.mjs +0 -235
- package/dist/settings-b5zW1R1T.mjs.map +0 -1
- package/dist/taxonomies-Crtzy4MT.mjs.map +0 -1
- package/dist/types-Cj2S6FuC.mjs +0 -3
- package/dist/types-D8bhH891.mjs.map +0 -1
- package/dist/types-DbCWhHet.d.mts.map +0 -1
- package/dist/types-i8_uzhMD.d.mts.map +0 -1
- package/dist/user-DzEUl5zA.mjs.map +0 -1
- package/dist/validate-Dy6nkNls.d.mts.map +0 -1
- package/dist/validate-JCXcsqiY.mjs.map +0 -1
- package/dist/version-CnS-Cr8A.mjs +0 -7
- package/dist/widgets-Bap1eS1X.mjs.map +0 -1
- package/src/plugins/scheduler/piggyback.ts +0 -71
- /package/dist/{api-tokens-B6VgoE6M.mjs → api-tokens-Oq39ba-Z.mjs} +0 -0
package/src/emdash-runtime.ts
CHANGED
|
@@ -24,7 +24,10 @@ import { isSqlite } from "./database/dialect-helpers.js";
|
|
|
24
24
|
import { kyselyLogOption } from "./database/instrumentation.js";
|
|
25
25
|
import { MIGRATION_RACE_WAIT_MS, runMigrations } from "./database/migrations/runner.js";
|
|
26
26
|
import { RevisionRepository } from "./database/repositories/revision.js";
|
|
27
|
-
import type {
|
|
27
|
+
import type {
|
|
28
|
+
ContentItem as ContentItemInternal,
|
|
29
|
+
ContentDateField,
|
|
30
|
+
} from "./database/repositories/types.js";
|
|
28
31
|
import { validateIdentifier } from "./database/validate.js";
|
|
29
32
|
import { normalizeMediaValue } from "./media/normalize.js";
|
|
30
33
|
import type { MediaProvider, MediaProviderCapabilities } from "./media/types.js";
|
|
@@ -42,6 +45,7 @@ import type {
|
|
|
42
45
|
import type { FieldType } from "./schema/types.js";
|
|
43
46
|
import { hashString } from "./utils/hash.js";
|
|
44
47
|
import { createInitLock, type InitLock, initWithLock } from "./utils/init-lock.js";
|
|
48
|
+
import { createIsolateCache, isolateCachedAsync } from "./utils/isolate-cache.js";
|
|
45
49
|
import { COMMIT, VERSION } from "./version.js";
|
|
46
50
|
|
|
47
51
|
const LEADING_SLASH_PATTERN = /^\//;
|
|
@@ -115,6 +119,7 @@ import { validateEncryptionKeyAtStartup } from "./config/secrets.js";
|
|
|
115
119
|
import { OptionsRepository } from "./database/repositories/options.js";
|
|
116
120
|
import {
|
|
117
121
|
handleContentList,
|
|
122
|
+
handleContentAuthors,
|
|
118
123
|
handleContentGet,
|
|
119
124
|
handleContentGetIncludingTrashed,
|
|
120
125
|
handleContentCreate,
|
|
@@ -158,13 +163,12 @@ import {
|
|
|
158
163
|
import { normalizeManifestRoute } from "./plugins/manifest-schema.js";
|
|
159
164
|
import { extractRequestMeta, sanitizeHeadersForSandbox } from "./plugins/request-meta.js";
|
|
160
165
|
import { PluginRouteRegistry, type RouteMeta } from "./plugins/routes.js";
|
|
161
|
-
import { NodeCronScheduler } from "./plugins/scheduler/node.js";
|
|
162
|
-
import { PiggybackScheduler } from "./plugins/scheduler/piggyback.js";
|
|
163
166
|
import type { CronScheduler } from "./plugins/scheduler/types.js";
|
|
164
167
|
import { PluginStateRepository } from "./plugins/state.js";
|
|
165
168
|
import { normalizeRegistryConfig } from "./registry/config.js";
|
|
166
169
|
import { requestCached } from "./request-cache.js";
|
|
167
170
|
import { getRequestContext } from "./request-context.js";
|
|
171
|
+
import { publishDueContent, type PublishedRef } from "./scheduled-publish.js";
|
|
168
172
|
import { FTSManager } from "./search/fts-manager.js";
|
|
169
173
|
import { invalidateSiteSettingsCache } from "./settings/index.js";
|
|
170
174
|
|
|
@@ -237,6 +241,13 @@ export interface MediaProviderContext {
|
|
|
237
241
|
storage: Storage | null;
|
|
238
242
|
}
|
|
239
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Builds the timer-based scheduler that drives cron ticks and maintenance.
|
|
246
|
+
* Injected via `virtual:emdash/scheduler` so the platform — not core — decides
|
|
247
|
+
* whether a long-lived heartbeat exists.
|
|
248
|
+
*/
|
|
249
|
+
export type CreateSchedulerFn = (executor: CronExecutor) => CronScheduler;
|
|
250
|
+
|
|
240
251
|
/**
|
|
241
252
|
* Dependencies injected from virtual modules (middleware reads these)
|
|
242
253
|
*/
|
|
@@ -250,6 +261,16 @@ export interface RuntimeDependencies {
|
|
|
250
261
|
sandboxEnabled: boolean;
|
|
251
262
|
/** sandbox: false escape hatch - load sandboxed plugins in-process */
|
|
252
263
|
sandboxBypassed?: boolean;
|
|
264
|
+
/**
|
|
265
|
+
* Factory for the timer-based cron/maintenance heartbeat. Supplied by the
|
|
266
|
+
* generated `virtual:emdash/scheduler` module: a `NodeCronScheduler` factory
|
|
267
|
+
* on long-lived runtimes (Node/Bun), or `null` on serverless adapters where
|
|
268
|
+
* an external driver (e.g. the Cloudflare Worker's `scheduled()` Cron
|
|
269
|
+
* Trigger) calls `runScheduledTasks()` instead. When absent or null, the
|
|
270
|
+
* runtime starts no scheduler. Keeping the platform decision in the
|
|
271
|
+
* integration means core has no adapter-specific runtime checks.
|
|
272
|
+
*/
|
|
273
|
+
createScheduler?: CreateSchedulerFn | null;
|
|
253
274
|
/** Media provider entries from virtual module */
|
|
254
275
|
mediaProviderEntries?: MediaProviderEntry[];
|
|
255
276
|
sandboxedPluginEntries: SandboxedPluginEntry[];
|
|
@@ -397,12 +418,12 @@ export class EmDashRuntime {
|
|
|
397
418
|
private pluginStates: Map<string, string>;
|
|
398
419
|
|
|
399
420
|
/**
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
421
|
+
* Isolate-lifetime guard so FTS indexes are verified at most once per
|
|
422
|
+
* worker rather than on every admin request. See ensureSearchHealthy().
|
|
423
|
+
* Uses the poison-immune isolate cache (never a shared awaitable promise)
|
|
424
|
+
* so a cancelled first caller can't wedge later ones.
|
|
403
425
|
*/
|
|
404
|
-
private
|
|
405
|
-
private _searchHealthPromise: Promise<void> | null = null;
|
|
426
|
+
private readonly _searchHealthCache = createIsolateCache<void>();
|
|
406
427
|
|
|
407
428
|
/** Current hook pipeline. Use the `hooks` getter for external access. */
|
|
408
429
|
get hooks(): HookPipeline {
|
|
@@ -479,14 +500,65 @@ export class EmDashRuntime {
|
|
|
479
500
|
}
|
|
480
501
|
|
|
481
502
|
/**
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
|
|
503
|
+
* Publish any content whose scheduled time has passed.
|
|
504
|
+
* Returns the items promoted so callers can invalidate their cache tags.
|
|
505
|
+
*/
|
|
506
|
+
async publishScheduled(): Promise<PublishedRef[]> {
|
|
507
|
+
return publishDueContent(this.db, {
|
|
508
|
+
publish: (collection, id, options) => this.handleContentPublish(collection, id, options),
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Run the full scheduled-maintenance batch: cron tasks, scheduled
|
|
514
|
+
* publishing, and system cleanup. For request-less drivers — the
|
|
515
|
+
* Cloudflare `scheduled()` handler invokes this from a Cron Trigger.
|
|
516
|
+
* (On Node the timer-based scheduler drives the same work itself.)
|
|
517
|
+
*
|
|
518
|
+
* Each step is independent and non-fatal. Returns the content promoted
|
|
519
|
+
* by the publishing sweep so the caller can purge edge-cache tags.
|
|
520
|
+
*
|
|
521
|
+
* `onPublished` (optional) is awaited after each collection's batch so a
|
|
522
|
+
* request-less driver can invalidate edge-cache tags incrementally rather
|
|
523
|
+
* than only after the whole sweep — bounding stale-cache exposure if the
|
|
524
|
+
* runtime is killed mid-sweep.
|
|
485
525
|
*/
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
526
|
+
async runScheduledTasks(
|
|
527
|
+
options: {
|
|
528
|
+
onPublished?: (refs: PublishedRef[]) => Promise<void>;
|
|
529
|
+
} = {},
|
|
530
|
+
): Promise<{ published: PublishedRef[] }> {
|
|
531
|
+
if (this.cronExecutor) {
|
|
532
|
+
try {
|
|
533
|
+
await this.cronExecutor.tick();
|
|
534
|
+
} catch (error) {
|
|
535
|
+
console.error("[cron] Tick failed:", error);
|
|
536
|
+
}
|
|
537
|
+
try {
|
|
538
|
+
await this.cronExecutor.recoverStaleLocks();
|
|
539
|
+
} catch (error) {
|
|
540
|
+
console.error("[cron] Stale lock recovery failed:", error);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
let published: PublishedRef[] = [];
|
|
545
|
+
try {
|
|
546
|
+
// Route through the runtime wrapper so content:afterPublish hooks fire.
|
|
547
|
+
published = await publishDueContent(this.db, {
|
|
548
|
+
publish: (collection, id, opts) => this.handleContentPublish(collection, id, opts),
|
|
549
|
+
onPublished: options.onPublished,
|
|
550
|
+
});
|
|
551
|
+
} catch (error) {
|
|
552
|
+
console.error("[scheduled-publish] Sweep failed:", error);
|
|
489
553
|
}
|
|
554
|
+
|
|
555
|
+
try {
|
|
556
|
+
await runSystemCleanup(this.db, this.storage ?? undefined);
|
|
557
|
+
} catch (error) {
|
|
558
|
+
console.error("[cleanup] System cleanup failed:", error);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return { published };
|
|
490
562
|
}
|
|
491
563
|
|
|
492
564
|
/**
|
|
@@ -1173,6 +1245,11 @@ export class EmDashRuntime {
|
|
|
1173
1245
|
|
|
1174
1246
|
let cronExecutor: CronExecutor | null = null;
|
|
1175
1247
|
let cronScheduler: CronScheduler | null = null;
|
|
1248
|
+
// Populated with the constructed runtime just before this method returns,
|
|
1249
|
+
// so the timer scheduler's cleanup can route scheduled publishing through
|
|
1250
|
+
// the runtime wrapper (firing content:afterPublish hooks). The first tick
|
|
1251
|
+
// is ≥1s out, well after the synchronous assignment below.
|
|
1252
|
+
const runtimeRef: { current: EmDashRuntime | null } = { current: null };
|
|
1176
1253
|
|
|
1177
1254
|
await phase("rt.cron", "Cron init (recovery deferred post-response)", async () => {
|
|
1178
1255
|
try {
|
|
@@ -1198,46 +1275,57 @@ export class EmDashRuntime {
|
|
|
1198
1275
|
}
|
|
1199
1276
|
});
|
|
1200
1277
|
|
|
1201
|
-
//
|
|
1202
|
-
//
|
|
1203
|
-
//
|
|
1204
|
-
//
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1278
|
+
// The platform decides whether a long-lived timer heartbeat exists.
|
|
1279
|
+
// `createScheduler` is injected by the generated virtual:emdash/scheduler
|
|
1280
|
+
// module: a NodeCronScheduler factory on Node/Bun, or null on serverless
|
|
1281
|
+
// adapters (e.g. Cloudflare) where the Worker's `scheduled()` handler
|
|
1282
|
+
// drives runScheduledTasks() instead. No adapter check lives here.
|
|
1283
|
+
if (deps.createScheduler) {
|
|
1284
|
+
const scheduler = deps.createScheduler(cronExecutor);
|
|
1285
|
+
cronScheduler = scheduler;
|
|
1286
|
+
|
|
1287
|
+
// Run scheduled publishing and system cleanup alongside each tick.
|
|
1288
|
+
// Pass storage so cleanupPendingUploads can delete orphaned files.
|
|
1289
|
+
scheduler.setSystemCleanup(async () => {
|
|
1290
|
+
try {
|
|
1291
|
+
// Route through the runtime so content:afterPublish hooks fire.
|
|
1292
|
+
// Falls back to the raw handler if (improbably) the tick beats
|
|
1293
|
+
// the post-construction ref assignment.
|
|
1294
|
+
const runtime = runtimeRef.current;
|
|
1295
|
+
await publishDueContent(db, {
|
|
1296
|
+
publish: runtime
|
|
1297
|
+
? (collection, id, options) =>
|
|
1298
|
+
runtime.handleContentPublish(collection, id, options)
|
|
1299
|
+
: undefined,
|
|
1300
|
+
});
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
console.error("[scheduled-publish] Sweep failed:", error);
|
|
1303
|
+
}
|
|
1304
|
+
try {
|
|
1305
|
+
await runSystemCleanup(db, storage ?? undefined);
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
// Non-fatal -- individual cleanup failures are already logged
|
|
1308
|
+
// by runSystemCleanup. This catches unexpected errors.
|
|
1309
|
+
console.error("[cleanup] System cleanup failed:", error);
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1226
1312
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1313
|
+
// Add cron reschedule callback (merges with existing factory options)
|
|
1314
|
+
pipeline.setContextFactory({
|
|
1315
|
+
cronReschedule: () => cronScheduler?.reschedule(),
|
|
1316
|
+
});
|
|
1231
1317
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1318
|
+
// start() is void on the timer scheduler but the interface
|
|
1319
|
+
// allows a promise (alarm-backed schedulers); we don't block on it.
|
|
1320
|
+
void scheduler.start();
|
|
1321
|
+
}
|
|
1234
1322
|
} catch (error) {
|
|
1235
1323
|
console.warn("[cron] Failed to initialize cron system:", error);
|
|
1236
1324
|
// Non-fatal — CMS works without cron
|
|
1237
1325
|
}
|
|
1238
1326
|
});
|
|
1239
1327
|
|
|
1240
|
-
|
|
1328
|
+
const runtime = new EmDashRuntime({
|
|
1241
1329
|
db,
|
|
1242
1330
|
storage,
|
|
1243
1331
|
// Include bypassed sandboxed plugins in configuredPlugins so route
|
|
@@ -1260,6 +1348,10 @@ export class EmDashRuntime {
|
|
|
1260
1348
|
runtimeDeps: deps,
|
|
1261
1349
|
pipelineRef,
|
|
1262
1350
|
});
|
|
1351
|
+
// Hand the constructed instance to the scheduler-cleanup closure so the
|
|
1352
|
+
// timer-driven sweep can fire publish hooks (see runtimeRef above).
|
|
1353
|
+
runtimeRef.current = runtime;
|
|
1354
|
+
return runtime;
|
|
1263
1355
|
}
|
|
1264
1356
|
|
|
1265
1357
|
/**
|
|
@@ -2137,27 +2229,32 @@ export class EmDashRuntime {
|
|
|
2137
2229
|
* defend against FTS not existing yet (pre-setup).
|
|
2138
2230
|
*/
|
|
2139
2231
|
async ensureSearchHealthy(): Promise<void> {
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
if (!isSqlite(this._db))
|
|
2143
|
-
|
|
2144
|
-
|
|
2232
|
+
// Non-SQLite has no FTS to verify; the check is a cheap synchronous
|
|
2233
|
+
// branch, no need to cache it.
|
|
2234
|
+
if (!isSqlite(this._db)) return;
|
|
2235
|
+
try {
|
|
2236
|
+
await isolateCachedAsync(
|
|
2237
|
+
this._searchHealthCache,
|
|
2238
|
+
async () => {
|
|
2239
|
+
try {
|
|
2240
|
+
const ftsManager = new FTSManager(this._db);
|
|
2241
|
+
const repaired = await ftsManager.verifyAndRepairAll();
|
|
2242
|
+
if (repaired > 0) {
|
|
2243
|
+
console.log(`Repaired ${repaired} corrupted FTS index(es)`);
|
|
2244
|
+
}
|
|
2245
|
+
} catch {
|
|
2246
|
+
// FTS tables may not exist yet (pre-setup). Non-fatal — cache
|
|
2247
|
+
// the "checked" state regardless so we don't re-scan.
|
|
2248
|
+
}
|
|
2249
|
+
},
|
|
2250
|
+
{ anchor: (promise) => after(() => promise), ownerTimeoutMs: 30_000 },
|
|
2251
|
+
);
|
|
2252
|
+
} catch {
|
|
2253
|
+
// This check is best-effort and must never fail the calling request.
|
|
2254
|
+
// The inner body already swallows verify errors; this guards the
|
|
2255
|
+
// outer failure modes (owner timeout, waiter give-up) so a slow FTS
|
|
2256
|
+
// scan degrades to "unverified", not a 500 on admin/search routes.
|
|
2145
2257
|
}
|
|
2146
|
-
this._searchHealthPromise = (async () => {
|
|
2147
|
-
try {
|
|
2148
|
-
const ftsManager = new FTSManager(this._db);
|
|
2149
|
-
const repaired = await ftsManager.verifyAndRepairAll();
|
|
2150
|
-
if (repaired > 0) {
|
|
2151
|
-
console.log(`Repaired ${repaired} corrupted FTS index(es)`);
|
|
2152
|
-
}
|
|
2153
|
-
} catch {
|
|
2154
|
-
// FTS tables may not exist yet (pre-setup). Non-fatal.
|
|
2155
|
-
} finally {
|
|
2156
|
-
this._searchHealthChecked = true;
|
|
2157
|
-
this._searchHealthPromise = null;
|
|
2158
|
-
}
|
|
2159
|
-
})();
|
|
2160
|
-
return this._searchHealthPromise;
|
|
2161
2258
|
}
|
|
2162
2259
|
|
|
2163
2260
|
// =========================================================================
|
|
@@ -2174,11 +2271,19 @@ export class EmDashRuntime {
|
|
|
2174
2271
|
order?: "asc" | "desc";
|
|
2175
2272
|
locale?: string;
|
|
2176
2273
|
q?: string;
|
|
2274
|
+
authorId?: string;
|
|
2275
|
+
dateField?: ContentDateField;
|
|
2276
|
+
dateFrom?: string;
|
|
2277
|
+
dateTo?: string;
|
|
2177
2278
|
},
|
|
2178
2279
|
) {
|
|
2179
2280
|
return handleContentList(this.db, collection, params);
|
|
2180
2281
|
}
|
|
2181
2282
|
|
|
2283
|
+
async handleContentAuthors(collection: string) {
|
|
2284
|
+
return handleContentAuthors(this.db, collection);
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2182
2287
|
async handleContentGet(collection: string, id: string, locale?: string) {
|
|
2183
2288
|
const result = await handleContentGet(this.db, collection, id, locale);
|
|
2184
2289
|
return this.hydrateDraftData(result);
|
|
@@ -2553,7 +2658,7 @@ export class EmDashRuntime {
|
|
|
2553
2658
|
async handleContentPublish(
|
|
2554
2659
|
collection: string,
|
|
2555
2660
|
id: string,
|
|
2556
|
-
options: { publishedAt?: string } = {},
|
|
2661
|
+
options: { publishedAt?: string; requireScheduledDue?: boolean } = {},
|
|
2557
2662
|
) {
|
|
2558
2663
|
const result = await handleContentPublish(this.db, collection, id, options);
|
|
2559
2664
|
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ export type {
|
|
|
46
46
|
// API handlers
|
|
47
47
|
export {
|
|
48
48
|
handleContentList,
|
|
49
|
+
handleContentAuthors,
|
|
49
50
|
handleContentGet,
|
|
50
51
|
handleContentGetIncludingTrashed,
|
|
51
52
|
handleContentCreate,
|
|
@@ -201,6 +202,8 @@ export {
|
|
|
201
202
|
PluginManager,
|
|
202
203
|
createPluginManager,
|
|
203
204
|
PluginRouteError,
|
|
205
|
+
// Scheduler (Node timer heartbeat — used by virtual:emdash/scheduler)
|
|
206
|
+
NodeCronScheduler,
|
|
204
207
|
// Sandbox
|
|
205
208
|
NoopSandboxRunner,
|
|
206
209
|
SandboxNotAvailableError,
|
|
@@ -253,6 +256,10 @@ export type {
|
|
|
253
256
|
CollectionCommentSettings,
|
|
254
257
|
StoredComment,
|
|
255
258
|
|
|
259
|
+
// Scheduler types
|
|
260
|
+
CronScheduler,
|
|
261
|
+
SystemCleanupFn,
|
|
262
|
+
|
|
256
263
|
// Sandbox runtime types
|
|
257
264
|
SandboxRunner,
|
|
258
265
|
SandboxedPluginInstance,
|
|
@@ -406,7 +413,7 @@ export type {
|
|
|
406
413
|
} from "./menus/types.js";
|
|
407
414
|
|
|
408
415
|
// Bylines
|
|
409
|
-
export { getByline, getBylineBySlug } from "./bylines/index.js";
|
|
416
|
+
export { getByline, getBylineBySlug, getEntriesByByline } from "./bylines/index.js";
|
|
410
417
|
export type { BylineSummary, ContentBylineCredit } from "./database/repositories/types.js";
|
|
411
418
|
|
|
412
419
|
// Taxonomies
|
package/src/loader.ts
CHANGED
|
@@ -540,12 +540,16 @@ export interface CollectionFilter {
|
|
|
540
540
|
*/
|
|
541
541
|
cursor?: string;
|
|
542
542
|
/**
|
|
543
|
-
* Filter by field values, taxonomy terms, or ranges.
|
|
543
|
+
* Filter by field values, taxonomy terms, byline credits, or ranges.
|
|
544
544
|
*
|
|
545
545
|
* Taxonomy names are detected automatically and filtered via JOIN.
|
|
546
|
+
* The reserved `byline` key filters by byline credit (any credit, not
|
|
547
|
+
* just the primary one) via the `_emdash_content_bylines` junction
|
|
548
|
+
* table; its value is one or more byline translation groups.
|
|
546
549
|
* Other keys are treated as column filters on the content table.
|
|
547
550
|
*
|
|
548
551
|
* @example { category: 'news' } - taxonomy term
|
|
552
|
+
* @example { byline: '01HXYZ...' } - entries credited to a byline (any position)
|
|
549
553
|
* @example { series: 'main' } - exact match on a content field
|
|
550
554
|
* @example { published_at: { gte: '2024-01-01', lt: '2025-01-01' } } - date range
|
|
551
555
|
*/
|
|
@@ -673,13 +677,21 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
673
677
|
|
|
674
678
|
// Build cursor condition if cursor is provided
|
|
675
679
|
const cursorCondition = cursor ? buildCursorCondition(cursor, orderBy) : null;
|
|
676
|
-
const cursorConditionPrefixed = cursor
|
|
677
|
-
? buildCursorCondition(cursor, orderBy, tableName)
|
|
678
|
-
: null;
|
|
679
680
|
|
|
680
|
-
// Separate taxonomy filters from field filters
|
|
681
|
+
// Separate taxonomy / byline filters from field filters
|
|
681
682
|
let result: { rows: Record<string, unknown>[] };
|
|
682
|
-
|
|
683
|
+
// Taxonomy filters AND together: each entry constrains the base
|
|
684
|
+
// row to match at least one of its slugs *within that taxonomy*.
|
|
685
|
+
// Term slugs are unique only within a taxonomy, so every filter
|
|
686
|
+
// keeps its own `name` and emits its own `EXISTS` clause rather
|
|
687
|
+
// than pooling slugs into one `IN`.
|
|
688
|
+
const taxonomyFilters: { name: string; slugs: string[] }[] = [];
|
|
689
|
+
// A byline filter matches entries credited to any of the given
|
|
690
|
+
// byline translation groups via the `_emdash_content_bylines`
|
|
691
|
+
// junction table. `null` means no byline filter; an empty
|
|
692
|
+
// `groups` array means the filter was requested but matches
|
|
693
|
+
// nothing (short-circuited to an empty result below).
|
|
694
|
+
let bylineFilter: { groups: string[] } | null = null;
|
|
683
695
|
const fieldFilters: Record<string, WhereValue> = {};
|
|
684
696
|
|
|
685
697
|
if (where && Object.keys(where).length > 0) {
|
|
@@ -687,56 +699,51 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
687
699
|
|
|
688
700
|
for (const [key, value] of Object.entries(where)) {
|
|
689
701
|
if (value == null) continue;
|
|
690
|
-
if (
|
|
702
|
+
if (key === "byline") {
|
|
691
703
|
if (isWhereRange(value)) {
|
|
692
704
|
console.warn(
|
|
693
|
-
`[emdash] where filter: range operators are not supported on
|
|
705
|
+
`[emdash] where filter: range operators are not supported on "byline", ignored`,
|
|
694
706
|
);
|
|
695
707
|
continue;
|
|
696
708
|
}
|
|
697
|
-
|
|
709
|
+
const groups = Array.isArray(value) ? value : [value];
|
|
710
|
+
bylineFilter = { groups };
|
|
711
|
+
} else if (taxNames.has(key)) {
|
|
712
|
+
if (isWhereRange(value)) {
|
|
698
713
|
console.warn(
|
|
699
|
-
`[emdash] where filter:
|
|
714
|
+
`[emdash] where filter: range operators are not supported on taxonomy "${key}", ignored`,
|
|
700
715
|
);
|
|
701
716
|
continue;
|
|
702
717
|
}
|
|
703
718
|
const slugs = Array.isArray(value) ? value : [value];
|
|
704
|
-
|
|
719
|
+
taxonomyFilters.push({ name: key, slugs });
|
|
705
720
|
} else {
|
|
706
721
|
fieldFilters[key] = value;
|
|
707
722
|
}
|
|
708
723
|
}
|
|
709
724
|
}
|
|
710
725
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
fieldConds.length > 0 ? sql`${sql.join(fieldConds, sql` AND `)}` : null;
|
|
726
|
+
// A byline or taxonomy filter with no values matches nothing —
|
|
727
|
+
// short-circuit before building SQL (an empty `IN ()` is invalid
|
|
728
|
+
// SQL on both dialects).
|
|
729
|
+
if (
|
|
730
|
+
(bylineFilter && bylineFilter.groups.length === 0) ||
|
|
731
|
+
taxonomyFilters.some((f) => f.slugs.length === 0)
|
|
732
|
+
) {
|
|
733
|
+
return { entries: [], cacheHint: { tags: [type] } };
|
|
734
|
+
}
|
|
721
735
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
AND t.name = ${taxonomyFilter.name}
|
|
734
|
-
AND t.slug IN (${sql.join(taxonomyFilter.slugs.map((s) => sql`${s}`))})
|
|
735
|
-
${fieldCondsSQL ? sql`AND ${fieldCondsSQL}` : sql``}
|
|
736
|
-
${orderByClause}
|
|
737
|
-
${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}
|
|
738
|
-
`.execute(db);
|
|
739
|
-
} else {
|
|
736
|
+
{
|
|
737
|
+
// Taxonomy and byline filters are applied as correlated
|
|
738
|
+
// `EXISTS` semi-joins rather than `INNER JOIN ... DISTINCT`.
|
|
739
|
+
// A join fan-out would force `SELECT DISTINCT table.*`, and
|
|
740
|
+
// Postgres cannot apply DISTINCT to a row containing a `json`
|
|
741
|
+
// column (no equality operator), so the join approach throws
|
|
742
|
+
// there. EXISTS matches "credited/tagged at least once"
|
|
743
|
+
// without duplicating rows, needs no DISTINCT, and works on
|
|
744
|
+
// both SQLite and Postgres. The base query stays a single-
|
|
745
|
+
// table `SELECT *`, so all field/status/locale/cursor/order
|
|
746
|
+
// conditions reference unprefixed columns as before.
|
|
740
747
|
const orderByClause = buildOrderByClause(orderBy);
|
|
741
748
|
const statusCondition = buildStatusCondition(db, status);
|
|
742
749
|
const localeFilter = locale ? sql`AND locale = ${locale}` : sql``;
|
|
@@ -745,12 +752,47 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
745
752
|
const fieldCondsSQL =
|
|
746
753
|
fieldConds.length > 0 ? sql`${sql.join(fieldConds, sql` AND `)}` : null;
|
|
747
754
|
|
|
755
|
+
// One `EXISTS` per taxonomy, AND'd together: an entry must be
|
|
756
|
+
// tagged with a matching term in *every* requested taxonomy.
|
|
757
|
+
// Each clause pins its own `t.name`, so slugs never pool
|
|
758
|
+
// across taxonomies (they're only unique within one).
|
|
759
|
+
const taxonomyCond =
|
|
760
|
+
taxonomyFilters.length > 0
|
|
761
|
+
? sql`${sql.join(
|
|
762
|
+
taxonomyFilters.map(
|
|
763
|
+
(f) => sql`AND EXISTS (
|
|
764
|
+
SELECT 1 FROM content_taxonomies ct
|
|
765
|
+
INNER JOIN taxonomies t ON t.id = ct.taxonomy_id
|
|
766
|
+
WHERE ct.collection = ${type}
|
|
767
|
+
AND ct.entry_id = ${sql.ref(tableName)}.id
|
|
768
|
+
AND t.name = ${f.name}
|
|
769
|
+
AND t.slug IN (${sql.join(f.slugs.map((s) => sql`${s}`))})
|
|
770
|
+
)`,
|
|
771
|
+
),
|
|
772
|
+
sql` `,
|
|
773
|
+
)}`
|
|
774
|
+
: sql``;
|
|
775
|
+
|
|
776
|
+
// `_emdash_content_bylines.byline_id` stores the byline's
|
|
777
|
+
// translation_group (migration 040), so a credit spans every
|
|
778
|
+
// locale variant of the byline and we match the group directly.
|
|
779
|
+
const bylineCond = bylineFilter
|
|
780
|
+
? sql`AND EXISTS (
|
|
781
|
+
SELECT 1 FROM _emdash_content_bylines cb
|
|
782
|
+
WHERE cb.collection_slug = ${type}
|
|
783
|
+
AND cb.content_id = ${sql.ref(tableName)}.id
|
|
784
|
+
AND cb.byline_id IN (${sql.join(bylineFilter.groups.map((g) => sql`${g}`))})
|
|
785
|
+
)`
|
|
786
|
+
: sql``;
|
|
787
|
+
|
|
748
788
|
result = await sql<Record<string, unknown>>`
|
|
749
789
|
SELECT * FROM ${sql.ref(tableName)}
|
|
750
790
|
WHERE deleted_at IS NULL
|
|
751
791
|
AND ${statusCondition}
|
|
752
792
|
${localeFilter}
|
|
753
793
|
${cursorCond}
|
|
794
|
+
${taxonomyCond}
|
|
795
|
+
${bylineCond}
|
|
754
796
|
${fieldCondsSQL ? sql`AND ${fieldCondsSQL}` : sql``}
|
|
755
797
|
${orderByClause}
|
|
756
798
|
${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}
|