emdash 0.18.0 → 0.19.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/api/route-utils.d.mts +2 -2
- package/dist/api/route-utils.mjs +14 -14
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-Cs7DAACP.mjs → api-BZ6bhjYs.mjs} +88 -16
- package/dist/api-BZ6bhjYs.mjs.map +1 -0
- package/dist/{apply-BWMV4Zmw.mjs → apply-hQkKKBCf.mjs} +23 -23
- package/dist/apply-hQkKKBCf.mjs.map +1 -0
- package/dist/astro/index.d.mts +8 -8
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +113 -23
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +7 -7
- package/dist/astro/middleware/auth.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware/request-context.mjs +2 -2
- package/dist/astro/middleware.d.mts +26 -4
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +205 -173
- 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 +2 -2
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
- 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 +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/index.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +30 -30
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +31 -31
- package/dist/astro/routes/api/admin/plugins/updates.mjs +30 -30
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +30 -30
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +30 -30
- 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 +3 -3
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +6 -6
- package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
- package/dist/astro/routes/api/auth/invite/register-options.mjs +6 -6
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +8 -8
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +6 -6
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.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 +7 -7
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/verify.mjs +6 -6
- package/dist/astro/routes/api/auth/signup/complete.mjs +6 -6
- 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 +2 -2
- 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 +7 -7
- 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.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +9 -9
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +28 -28
- 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 +6 -6
- package/dist/astro/routes/api/media.mjs +7 -7
- 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 +1 -1
- package/dist/astro/routes/api/oauth/device/authorize.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/code.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/token.mjs +5 -5
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +4 -4
- package/dist/astro/routes/api/oauth/token/revoke.mjs +4 -4
- package/dist/astro/routes/api/oauth/token.mjs +4 -4
- 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 +3 -3
- 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 +30 -30
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +30 -30
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +30 -30
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +30 -30
- package/dist/astro/routes/api/schema/collections/index.mjs +30 -30
- package/dist/astro/routes/api/schema/index.mjs +6 -6
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +30 -30
- package/dist/astro/routes/api/schema/orphans/index.mjs +30 -30
- 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 +6 -6
- package/dist/astro/routes/api/setup/admin.mjs +6 -6
- package/dist/astro/routes/api/setup/dev-bypass.mjs +18 -18
- package/dist/astro/routes/api/setup/dev-reset.mjs +3 -3
- package/dist/astro/routes/api/setup/index.mjs +21 -21
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +5 -5
- 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 +5 -5
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +8 -8
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +6 -6
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +6 -6
- package/dist/astro/routes/sitemap.xml.mjs +6 -6
- package/dist/astro/types.d.mts +15 -8
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{authorize-CotM4Yiu.mjs → authorize-C_8t2KGa.mjs} +2 -2
- package/dist/{authorize-CotM4Yiu.mjs.map → authorize-C_8t2KGa.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-Dr-xcb6S.mjs → byline-fields-51kg6Vuv.mjs} +3 -3
- package/dist/{byline-fields-Dr-xcb6S.mjs.map → byline-fields-51kg6Vuv.mjs.map} +1 -1
- package/dist/{byline-fields-DC3Wkk-U.mjs → byline-fields-C_OsR-KF.mjs} +2 -2
- package/dist/{byline-fields-DC3Wkk-U.mjs.map → byline-fields-C_OsR-KF.mjs.map} +1 -1
- package/dist/{byline-fields-BNy7Ng1U.d.mts → byline-fields-DYXKDuNX.d.mts} +26 -2
- package/dist/byline-fields-DYXKDuNX.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-Cx5n-WqP.mjs} +3 -3
- package/dist/{bylines-LJMgENMI.mjs.map → bylines-Cx5n-WqP.mjs.map} +1 -1
- package/dist/{bylines-BJSva1Un.mjs → bylines-wurS258E.mjs} +50 -6
- package/dist/{bylines-BJSva1Un.mjs.map → bylines-wurS258E.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/{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 +40 -27
- 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/{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-CJ0RZsYR.mjs} +3 -3
- package/dist/{comments-BTAbC0Ek.mjs.map → comments-CJ0RZsYR.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-GG52SPgh.mjs} +10 -10
- package/dist/{context-DZ7bEh5-.mjs.map → context-GG52SPgh.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/db/index.d.mts +2 -2
- package/dist/db/index.mjs +1 -1
- package/dist/{device-flow-ptLrVINd.mjs → device-flow-s6_q3T7A.mjs} +2 -2
- package/dist/{device-flow-ptLrVINd.mjs.map → device-flow-s6_q3T7A.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/{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-D60_SzHG.d.mts → index-BpYeJO1E.d.mts} +2 -2
- package/dist/{index-D60_SzHG.d.mts.map → index-BpYeJO1E.d.mts.map} +1 -1
- package/dist/{index-CjKdMZ3U.d.mts → index-FfiTQJq2.d.mts} +199 -17
- package/dist/index-FfiTQJq2.d.mts.map +1 -0
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +43 -43
- package/dist/{load-6ZrRhepW.mjs → load-B84ohfBk.mjs} +2 -2
- package/dist/{load-6ZrRhepW.mjs.map → load-B84ohfBk.mjs.map} +1 -1
- package/dist/{loader-Dyx8dhFV.mjs → loader-CpZKpFz0.mjs} +32 -30
- package/dist/loader-CpZKpFz0.mjs.map +1 -0
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- 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/{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-Dp9xporj.mjs} +86 -9
- package/dist/menus-Dp9xporj.mjs.map +1 -0
- 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-1aPAYjiC.mjs} +2 -2
- package/dist/{oauth-authorization-DvBAL75d.mjs.map → oauth-authorization-1aPAYjiC.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-D4MnavW_.d.mts} +3 -3
- package/dist/{options-tb7DJROi.d.mts.map → options-D4MnavW_.d.mts.map} +1 -1
- package/dist/{parse-BBkFmLVr.mjs → parse-CrGndy1A.mjs} +2 -2
- package/dist/{parse-BBkFmLVr.mjs.map → parse-CrGndy1A.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/plugin-utils.d.mts +7 -7
- package/dist/plugins/adapt-sandbox-entry.d.mts +7 -7
- package/dist/{query-Ctlq1aOk.mjs → query-BFQ029Ts.mjs} +21 -15
- package/dist/query-BFQ029Ts.mjs.map +1 -0
- package/dist/{rate-limit-CH6W6ikK.mjs → rate-limit-ClFFUga6.mjs} +2 -2
- package/dist/{rate-limit-CH6W6ikK.mjs.map → rate-limit-ClFFUga6.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-DEygMrRO.mjs} +25 -3
- package/dist/redirects-DEygMrRO.mjs.map +1 -0
- package/dist/{redirects-CacE9eQa.mjs → redirects-OIu6vQ2i.mjs} +5 -5
- package/dist/{redirects-CacE9eQa.mjs.map → redirects-OIu6vQ2i.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/{runner-pt6Wl-l-.mjs → runner--4wMWwKM.mjs} +217 -166
- package/dist/runner--4wMWwKM.mjs.map +1 -0
- package/dist/{runner-DM1yR5qd.d.mts → runner-BcRuXq_h.d.mts} +2 -2
- package/dist/{runner-DM1yR5qd.d.mts.map → runner-BcRuXq_h.d.mts.map} +1 -1
- package/dist/runtime.d.mts +7 -7
- package/dist/runtime.mjs +2 -2
- package/dist/{schema-B4tk0HAG.mjs → schema-CS7Eg5gh.mjs} +5 -5
- package/dist/{schema-B4tk0HAG.mjs.map → schema-CS7Eg5gh.mjs.map} +1 -1
- package/dist/{search-f-fNfwab.mjs → search-o-aQzHI1.mjs} +4 -4
- package/dist/{search-f-fNfwab.mjs.map → search-o-aQzHI1.mjs.map} +1 -1
- package/dist/{secrets-YYbTgB1w.mjs → secrets-C_ZtRos3.mjs} +2 -2
- package/dist/{secrets-YYbTgB1w.mjs.map → secrets-C_ZtRos3.mjs.map} +1 -1
- package/dist/{sections-biElLfT9.mjs → sections-DhsZ0ns9.mjs} +3 -3
- package/dist/{sections-biElLfT9.mjs.map → sections-DhsZ0ns9.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +16 -16
- package/dist/seo/index.d.mts +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/{service-BhR2acnc.mjs → service-DAxg8RPR.mjs} +2 -2
- package/dist/{service-BhR2acnc.mjs.map → service-DAxg8RPR.mjs.map} +1 -1
- package/dist/{settings-b5zW1R1T.mjs → settings-B1p-gPUK.mjs} +5 -5
- package/dist/{settings-b5zW1R1T.mjs.map → settings-B1p-gPUK.mjs.map} +1 -1
- package/dist/{settings-D_NJvjgN.mjs → settings-DIsbHTRE.mjs} +3 -3
- package/dist/{settings-D_NJvjgN.mjs.map → settings-DIsbHTRE.mjs.map} +1 -1
- package/dist/{setup-complete-VoEZfasi.mjs → setup-complete-Yuv78yua.mjs} +2 -2
- package/dist/{setup-complete-VoEZfasi.mjs.map → setup-complete-Yuv78yua.mjs.map} +1 -1
- package/dist/{site-url-Cm8-sJy7.mjs → site-url-mEVmwIFi.mjs} +2 -2
- package/dist/{site-url-Cm8-sJy7.mjs.map → site-url-mEVmwIFi.mjs.map} +1 -1
- package/dist/{taxonomies-Crtzy4MT.mjs → taxonomies-BEW7S5AI.mjs} +7 -6
- package/dist/taxonomies-BEW7S5AI.mjs.map +1 -0
- package/dist/{taxonomies-Mhn9rjTQ.mjs → taxonomies-UusDXv3C.mjs} +4 -4
- package/dist/{taxonomies-Mhn9rjTQ.mjs.map → taxonomies-UusDXv3C.mjs.map} +1 -1
- 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-BwQeeY2p.d.mts} +1 -1
- package/dist/{transport-OnMNbsIA.d.mts.map → transport-BwQeeY2p.d.mts.map} +1 -1
- 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-D8bhH891.mjs → types-DZk_y-MU.mjs} +1 -1
- package/dist/{types-D8bhH891.mjs.map → types-DZk_y-MU.mjs.map} +1 -1
- package/dist/{types-DawhLFwy.d.mts → types-OT_Es5mp.d.mts} +26 -1
- package/dist/{types-DawhLFwy.d.mts.map → types-OT_Es5mp.d.mts.map} +1 -1
- package/dist/{types-i8_uzhMD.d.mts → types-WVmpZBJV.d.mts} +18 -3
- package/dist/types-WVmpZBJV.d.mts.map +1 -0
- package/dist/{user-DzEUl5zA.mjs → user-C0um7wrg.mjs} +18 -2
- package/dist/user-C0um7wrg.mjs.map +1 -0
- package/dist/{validate-Dy6nkNls.d.mts → validate-BPAHUSge.d.mts} +10 -2
- package/dist/validate-BPAHUSge.d.mts.map +1 -0
- package/dist/{validate-JCXcsqiY.mjs → validate-ZP9Dvg0P.mjs} +6 -3
- package/dist/validate-ZP9Dvg0P.mjs.map +1 -0
- package/dist/{validation-Bq-VyKJg.mjs → validation-CE5i4q0c.mjs} +5 -5
- package/dist/{validation-Bq-VyKJg.mjs.map → validation-CE5i4q0c.mjs.map} +1 -1
- package/dist/version-Dw0JXu45.mjs +7 -0
- package/dist/{version-CnS-Cr8A.mjs.map → version-Dw0JXu45.mjs.map} +1 -1
- package/dist/{widgets-Bap1eS1X.mjs → widgets-ClEnYQCH.mjs} +2 -2
- package/dist/{widgets-Bap1eS1X.mjs.map → widgets-ClEnYQCH.mjs.map} +1 -1
- 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 +5 -5
- package/src/api/handlers/content.ts +107 -8
- package/src/api/handlers/index.ts +2 -0
- package/src/api/openapi/document.ts +25 -0
- package/src/api/schemas/content.ts +33 -0
- 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 +28 -0
- 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/types.ts +8 -1
- package/src/bylines/index.ts +57 -0
- package/src/cli/commands/export-seed.ts +28 -12
- package/src/components/EmDashImage.astro +22 -4
- package/src/components/Image.astro +20 -3
- 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 +141 -42
- package/src/index.ts +8 -1
- package/src/loader.ts +67 -34
- package/src/media/responsive.ts +125 -0
- package/src/plugins/cron.ts +3 -2
- package/src/plugins/index.ts +5 -0
- package/src/plugins/scheduler/node.ts +9 -2
- package/src/query.ts +32 -5
- package/src/scheduled-publish.ts +153 -0
- package/src/seed/apply.ts +16 -6
- package/src/seed/types.ts +9 -0
- package/src/seed/validate.ts +15 -0
- package/src/taxonomies/index.ts +1 -0
- package/src/virtual-modules.d.ts +11 -0
- 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/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/taxonomies-Crtzy4MT.mjs.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/src/plugins/scheduler/piggyback.ts +0 -71
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";
|
|
@@ -115,6 +118,7 @@ import { validateEncryptionKeyAtStartup } from "./config/secrets.js";
|
|
|
115
118
|
import { OptionsRepository } from "./database/repositories/options.js";
|
|
116
119
|
import {
|
|
117
120
|
handleContentList,
|
|
121
|
+
handleContentAuthors,
|
|
118
122
|
handleContentGet,
|
|
119
123
|
handleContentGetIncludingTrashed,
|
|
120
124
|
handleContentCreate,
|
|
@@ -158,13 +162,12 @@ import {
|
|
|
158
162
|
import { normalizeManifestRoute } from "./plugins/manifest-schema.js";
|
|
159
163
|
import { extractRequestMeta, sanitizeHeadersForSandbox } from "./plugins/request-meta.js";
|
|
160
164
|
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
165
|
import type { CronScheduler } from "./plugins/scheduler/types.js";
|
|
164
166
|
import { PluginStateRepository } from "./plugins/state.js";
|
|
165
167
|
import { normalizeRegistryConfig } from "./registry/config.js";
|
|
166
168
|
import { requestCached } from "./request-cache.js";
|
|
167
169
|
import { getRequestContext } from "./request-context.js";
|
|
170
|
+
import { publishDueContent, type PublishedRef } from "./scheduled-publish.js";
|
|
168
171
|
import { FTSManager } from "./search/fts-manager.js";
|
|
169
172
|
import { invalidateSiteSettingsCache } from "./settings/index.js";
|
|
170
173
|
|
|
@@ -237,6 +240,13 @@ export interface MediaProviderContext {
|
|
|
237
240
|
storage: Storage | null;
|
|
238
241
|
}
|
|
239
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Builds the timer-based scheduler that drives cron ticks and maintenance.
|
|
245
|
+
* Injected via `virtual:emdash/scheduler` so the platform — not core — decides
|
|
246
|
+
* whether a long-lived heartbeat exists.
|
|
247
|
+
*/
|
|
248
|
+
export type CreateSchedulerFn = (executor: CronExecutor) => CronScheduler;
|
|
249
|
+
|
|
240
250
|
/**
|
|
241
251
|
* Dependencies injected from virtual modules (middleware reads these)
|
|
242
252
|
*/
|
|
@@ -250,6 +260,16 @@ export interface RuntimeDependencies {
|
|
|
250
260
|
sandboxEnabled: boolean;
|
|
251
261
|
/** sandbox: false escape hatch - load sandboxed plugins in-process */
|
|
252
262
|
sandboxBypassed?: boolean;
|
|
263
|
+
/**
|
|
264
|
+
* Factory for the timer-based cron/maintenance heartbeat. Supplied by the
|
|
265
|
+
* generated `virtual:emdash/scheduler` module: a `NodeCronScheduler` factory
|
|
266
|
+
* on long-lived runtimes (Node/Bun), or `null` on serverless adapters where
|
|
267
|
+
* an external driver (e.g. the Cloudflare Worker's `scheduled()` Cron
|
|
268
|
+
* Trigger) calls `runScheduledTasks()` instead. When absent or null, the
|
|
269
|
+
* runtime starts no scheduler. Keeping the platform decision in the
|
|
270
|
+
* integration means core has no adapter-specific runtime checks.
|
|
271
|
+
*/
|
|
272
|
+
createScheduler?: CreateSchedulerFn | null;
|
|
253
273
|
/** Media provider entries from virtual module */
|
|
254
274
|
mediaProviderEntries?: MediaProviderEntry[];
|
|
255
275
|
sandboxedPluginEntries: SandboxedPluginEntry[];
|
|
@@ -479,14 +499,65 @@ export class EmDashRuntime {
|
|
|
479
499
|
}
|
|
480
500
|
|
|
481
501
|
/**
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
|
|
502
|
+
* Publish any content whose scheduled time has passed.
|
|
503
|
+
* Returns the items promoted so callers can invalidate their cache tags.
|
|
504
|
+
*/
|
|
505
|
+
async publishScheduled(): Promise<PublishedRef[]> {
|
|
506
|
+
return publishDueContent(this.db, {
|
|
507
|
+
publish: (collection, id, options) => this.handleContentPublish(collection, id, options),
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Run the full scheduled-maintenance batch: cron tasks, scheduled
|
|
513
|
+
* publishing, and system cleanup. For request-less drivers — the
|
|
514
|
+
* Cloudflare `scheduled()` handler invokes this from a Cron Trigger.
|
|
515
|
+
* (On Node the timer-based scheduler drives the same work itself.)
|
|
516
|
+
*
|
|
517
|
+
* Each step is independent and non-fatal. Returns the content promoted
|
|
518
|
+
* by the publishing sweep so the caller can purge edge-cache tags.
|
|
519
|
+
*
|
|
520
|
+
* `onPublished` (optional) is awaited after each collection's batch so a
|
|
521
|
+
* request-less driver can invalidate edge-cache tags incrementally rather
|
|
522
|
+
* than only after the whole sweep — bounding stale-cache exposure if the
|
|
523
|
+
* runtime is killed mid-sweep.
|
|
485
524
|
*/
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
525
|
+
async runScheduledTasks(
|
|
526
|
+
options: {
|
|
527
|
+
onPublished?: (refs: PublishedRef[]) => Promise<void>;
|
|
528
|
+
} = {},
|
|
529
|
+
): Promise<{ published: PublishedRef[] }> {
|
|
530
|
+
if (this.cronExecutor) {
|
|
531
|
+
try {
|
|
532
|
+
await this.cronExecutor.tick();
|
|
533
|
+
} catch (error) {
|
|
534
|
+
console.error("[cron] Tick failed:", error);
|
|
535
|
+
}
|
|
536
|
+
try {
|
|
537
|
+
await this.cronExecutor.recoverStaleLocks();
|
|
538
|
+
} catch (error) {
|
|
539
|
+
console.error("[cron] Stale lock recovery failed:", error);
|
|
540
|
+
}
|
|
489
541
|
}
|
|
542
|
+
|
|
543
|
+
let published: PublishedRef[] = [];
|
|
544
|
+
try {
|
|
545
|
+
// Route through the runtime wrapper so content:afterPublish hooks fire.
|
|
546
|
+
published = await publishDueContent(this.db, {
|
|
547
|
+
publish: (collection, id, opts) => this.handleContentPublish(collection, id, opts),
|
|
548
|
+
onPublished: options.onPublished,
|
|
549
|
+
});
|
|
550
|
+
} catch (error) {
|
|
551
|
+
console.error("[scheduled-publish] Sweep failed:", error);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
await runSystemCleanup(this.db, this.storage ?? undefined);
|
|
556
|
+
} catch (error) {
|
|
557
|
+
console.error("[cleanup] System cleanup failed:", error);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return { published };
|
|
490
561
|
}
|
|
491
562
|
|
|
492
563
|
/**
|
|
@@ -1173,6 +1244,11 @@ export class EmDashRuntime {
|
|
|
1173
1244
|
|
|
1174
1245
|
let cronExecutor: CronExecutor | null = null;
|
|
1175
1246
|
let cronScheduler: CronScheduler | null = null;
|
|
1247
|
+
// Populated with the constructed runtime just before this method returns,
|
|
1248
|
+
// so the timer scheduler's cleanup can route scheduled publishing through
|
|
1249
|
+
// the runtime wrapper (firing content:afterPublish hooks). The first tick
|
|
1250
|
+
// is ≥1s out, well after the synchronous assignment below.
|
|
1251
|
+
const runtimeRef: { current: EmDashRuntime | null } = { current: null };
|
|
1176
1252
|
|
|
1177
1253
|
await phase("rt.cron", "Cron init (recovery deferred post-response)", async () => {
|
|
1178
1254
|
try {
|
|
@@ -1198,46 +1274,57 @@ export class EmDashRuntime {
|
|
|
1198
1274
|
}
|
|
1199
1275
|
});
|
|
1200
1276
|
|
|
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
|
-
|
|
1277
|
+
// The platform decides whether a long-lived timer heartbeat exists.
|
|
1278
|
+
// `createScheduler` is injected by the generated virtual:emdash/scheduler
|
|
1279
|
+
// module: a NodeCronScheduler factory on Node/Bun, or null on serverless
|
|
1280
|
+
// adapters (e.g. Cloudflare) where the Worker's `scheduled()` handler
|
|
1281
|
+
// drives runScheduledTasks() instead. No adapter check lives here.
|
|
1282
|
+
if (deps.createScheduler) {
|
|
1283
|
+
const scheduler = deps.createScheduler(cronExecutor);
|
|
1284
|
+
cronScheduler = scheduler;
|
|
1285
|
+
|
|
1286
|
+
// Run scheduled publishing and system cleanup alongside each tick.
|
|
1287
|
+
// Pass storage so cleanupPendingUploads can delete orphaned files.
|
|
1288
|
+
scheduler.setSystemCleanup(async () => {
|
|
1289
|
+
try {
|
|
1290
|
+
// Route through the runtime so content:afterPublish hooks fire.
|
|
1291
|
+
// Falls back to the raw handler if (improbably) the tick beats
|
|
1292
|
+
// the post-construction ref assignment.
|
|
1293
|
+
const runtime = runtimeRef.current;
|
|
1294
|
+
await publishDueContent(db, {
|
|
1295
|
+
publish: runtime
|
|
1296
|
+
? (collection, id, options) =>
|
|
1297
|
+
runtime.handleContentPublish(collection, id, options)
|
|
1298
|
+
: undefined,
|
|
1299
|
+
});
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
console.error("[scheduled-publish] Sweep failed:", error);
|
|
1302
|
+
}
|
|
1303
|
+
try {
|
|
1304
|
+
await runSystemCleanup(db, storage ?? undefined);
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
// Non-fatal -- individual cleanup failures are already logged
|
|
1307
|
+
// by runSystemCleanup. This catches unexpected errors.
|
|
1308
|
+
console.error("[cleanup] System cleanup failed:", error);
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1226
1311
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1312
|
+
// Add cron reschedule callback (merges with existing factory options)
|
|
1313
|
+
pipeline.setContextFactory({
|
|
1314
|
+
cronReschedule: () => cronScheduler?.reschedule(),
|
|
1315
|
+
});
|
|
1231
1316
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1317
|
+
// start() is void on the timer scheduler but the interface
|
|
1318
|
+
// allows a promise (alarm-backed schedulers); we don't block on it.
|
|
1319
|
+
void scheduler.start();
|
|
1320
|
+
}
|
|
1234
1321
|
} catch (error) {
|
|
1235
1322
|
console.warn("[cron] Failed to initialize cron system:", error);
|
|
1236
1323
|
// Non-fatal — CMS works without cron
|
|
1237
1324
|
}
|
|
1238
1325
|
});
|
|
1239
1326
|
|
|
1240
|
-
|
|
1327
|
+
const runtime = new EmDashRuntime({
|
|
1241
1328
|
db,
|
|
1242
1329
|
storage,
|
|
1243
1330
|
// Include bypassed sandboxed plugins in configuredPlugins so route
|
|
@@ -1260,6 +1347,10 @@ export class EmDashRuntime {
|
|
|
1260
1347
|
runtimeDeps: deps,
|
|
1261
1348
|
pipelineRef,
|
|
1262
1349
|
});
|
|
1350
|
+
// Hand the constructed instance to the scheduler-cleanup closure so the
|
|
1351
|
+
// timer-driven sweep can fire publish hooks (see runtimeRef above).
|
|
1352
|
+
runtimeRef.current = runtime;
|
|
1353
|
+
return runtime;
|
|
1263
1354
|
}
|
|
1264
1355
|
|
|
1265
1356
|
/**
|
|
@@ -2174,11 +2265,19 @@ export class EmDashRuntime {
|
|
|
2174
2265
|
order?: "asc" | "desc";
|
|
2175
2266
|
locale?: string;
|
|
2176
2267
|
q?: string;
|
|
2268
|
+
authorId?: string;
|
|
2269
|
+
dateField?: ContentDateField;
|
|
2270
|
+
dateFrom?: string;
|
|
2271
|
+
dateTo?: string;
|
|
2177
2272
|
},
|
|
2178
2273
|
) {
|
|
2179
2274
|
return handleContentList(this.db, collection, params);
|
|
2180
2275
|
}
|
|
2181
2276
|
|
|
2277
|
+
async handleContentAuthors(collection: string) {
|
|
2278
|
+
return handleContentAuthors(this.db, collection);
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2182
2281
|
async handleContentGet(collection: string, id: string, locale?: string) {
|
|
2183
2282
|
const result = await handleContentGet(this.db, collection, id, locale);
|
|
2184
2283
|
return this.hydrateDraftData(result);
|
|
@@ -2553,7 +2652,7 @@ export class EmDashRuntime {
|
|
|
2553
2652
|
async handleContentPublish(
|
|
2554
2653
|
collection: string,
|
|
2555
2654
|
id: string,
|
|
2556
|
-
options: { publishedAt?: string } = {},
|
|
2655
|
+
options: { publishedAt?: string; requireScheduledDue?: boolean } = {},
|
|
2557
2656
|
) {
|
|
2558
2657
|
const result = await handleContentPublish(this.db, collection, id, options);
|
|
2559
2658
|
|
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,16 @@ 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
|
let taxonomyFilter: { name: string; slugs: string[] } | null = null;
|
|
684
|
+
// A byline filter matches entries credited to any of the given
|
|
685
|
+
// byline translation groups via the `_emdash_content_bylines`
|
|
686
|
+
// junction table. `null` means no byline filter; an empty
|
|
687
|
+
// `groups` array means the filter was requested but matches
|
|
688
|
+
// nothing (short-circuited to an empty result below).
|
|
689
|
+
let bylineFilter: { groups: string[] } | null = null;
|
|
683
690
|
const fieldFilters: Record<string, WhereValue> = {};
|
|
684
691
|
|
|
685
692
|
if (where && Object.keys(where).length > 0) {
|
|
@@ -687,7 +694,16 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
687
694
|
|
|
688
695
|
for (const [key, value] of Object.entries(where)) {
|
|
689
696
|
if (value == null) continue;
|
|
690
|
-
if (
|
|
697
|
+
if (key === "byline") {
|
|
698
|
+
if (isWhereRange(value)) {
|
|
699
|
+
console.warn(
|
|
700
|
+
`[emdash] where filter: range operators are not supported on "byline", ignored`,
|
|
701
|
+
);
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
const groups = Array.isArray(value) ? value : [value];
|
|
705
|
+
bylineFilter = { groups };
|
|
706
|
+
} else if (taxNames.has(key)) {
|
|
691
707
|
if (isWhereRange(value)) {
|
|
692
708
|
console.warn(
|
|
693
709
|
`[emdash] where filter: range operators are not supported on taxonomy "${key}", ignored`,
|
|
@@ -708,35 +724,27 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
708
724
|
}
|
|
709
725
|
}
|
|
710
726
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
fieldConds.length > 0 ? sql`${sql.join(fieldConds, sql` AND `)}` : null;
|
|
727
|
+
// A byline or taxonomy filter with no values matches nothing —
|
|
728
|
+
// short-circuit before building SQL (an empty `IN ()` is invalid
|
|
729
|
+
// SQL on both dialects).
|
|
730
|
+
if (
|
|
731
|
+
(bylineFilter && bylineFilter.groups.length === 0) ||
|
|
732
|
+
(taxonomyFilter && taxonomyFilter.slugs.length === 0)
|
|
733
|
+
) {
|
|
734
|
+
return { entries: [], cacheHint: { tags: [type] } };
|
|
735
|
+
}
|
|
721
736
|
|
|
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 {
|
|
737
|
+
{
|
|
738
|
+
// Taxonomy and byline filters are applied as correlated
|
|
739
|
+
// `EXISTS` semi-joins rather than `INNER JOIN ... DISTINCT`.
|
|
740
|
+
// A join fan-out would force `SELECT DISTINCT table.*`, and
|
|
741
|
+
// Postgres cannot apply DISTINCT to a row containing a `json`
|
|
742
|
+
// column (no equality operator), so the join approach throws
|
|
743
|
+
// there. EXISTS matches "credited/tagged at least once"
|
|
744
|
+
// without duplicating rows, needs no DISTINCT, and works on
|
|
745
|
+
// both SQLite and Postgres. The base query stays a single-
|
|
746
|
+
// table `SELECT *`, so all field/status/locale/cursor/order
|
|
747
|
+
// conditions reference unprefixed columns as before.
|
|
740
748
|
const orderByClause = buildOrderByClause(orderBy);
|
|
741
749
|
const statusCondition = buildStatusCondition(db, status);
|
|
742
750
|
const localeFilter = locale ? sql`AND locale = ${locale}` : sql``;
|
|
@@ -745,12 +753,37 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
745
753
|
const fieldCondsSQL =
|
|
746
754
|
fieldConds.length > 0 ? sql`${sql.join(fieldConds, sql` AND `)}` : null;
|
|
747
755
|
|
|
756
|
+
const taxonomyCond = taxonomyFilter
|
|
757
|
+
? sql`AND EXISTS (
|
|
758
|
+
SELECT 1 FROM content_taxonomies ct
|
|
759
|
+
INNER JOIN taxonomies t ON t.id = ct.taxonomy_id
|
|
760
|
+
WHERE ct.collection = ${type}
|
|
761
|
+
AND ct.entry_id = ${sql.ref(tableName)}.id
|
|
762
|
+
AND t.name = ${taxonomyFilter.name}
|
|
763
|
+
AND t.slug IN (${sql.join(taxonomyFilter.slugs.map((s) => sql`${s}`))})
|
|
764
|
+
)`
|
|
765
|
+
: sql``;
|
|
766
|
+
|
|
767
|
+
// `_emdash_content_bylines.byline_id` stores the byline's
|
|
768
|
+
// translation_group (migration 040), so a credit spans every
|
|
769
|
+
// locale variant of the byline and we match the group directly.
|
|
770
|
+
const bylineCond = bylineFilter
|
|
771
|
+
? sql`AND EXISTS (
|
|
772
|
+
SELECT 1 FROM _emdash_content_bylines cb
|
|
773
|
+
WHERE cb.collection_slug = ${type}
|
|
774
|
+
AND cb.content_id = ${sql.ref(tableName)}.id
|
|
775
|
+
AND cb.byline_id IN (${sql.join(bylineFilter.groups.map((g) => sql`${g}`))})
|
|
776
|
+
)`
|
|
777
|
+
: sql``;
|
|
778
|
+
|
|
748
779
|
result = await sql<Record<string, unknown>>`
|
|
749
780
|
SELECT * FROM ${sql.ref(tableName)}
|
|
750
781
|
WHERE deleted_at IS NULL
|
|
751
782
|
AND ${statusCondition}
|
|
752
783
|
${localeFilter}
|
|
753
784
|
${cursorCond}
|
|
785
|
+
${taxonomyCond}
|
|
786
|
+
${bylineCond}
|
|
754
787
|
${fieldCondsSQL ? sql`AND ${fieldCondsSQL}` : sql``}
|
|
755
788
|
${orderByClause}
|
|
756
789
|
${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responsive image helpers shared by the public Image components.
|
|
3
|
+
*
|
|
4
|
+
* These build a `srcset` for locally-stored / R2-stored media by delegating to
|
|
5
|
+
* Astro's configured image service (`astro:assets`). On Cloudflare that is the
|
|
6
|
+
* Images binding; on Node it is sharp; if neither is available it is a no-op
|
|
7
|
+
* passthrough. The calling `.astro` component passes Astro's `getImage` in so
|
|
8
|
+
* this module stays free of the `astro:assets` virtual import (which only
|
|
9
|
+
* resolves inside an Astro project, not in this precompiled package).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Standard responsive breakpoints. Matches CDN-provider srcset generation. */
|
|
13
|
+
export const RESPONSIVE_BREAKPOINTS = [640, 750, 828, 960, 1080, 1280, 1600, 1920];
|
|
14
|
+
|
|
15
|
+
/** Matches absolute http(s) URLs — the only shape Astro's image services optimize. */
|
|
16
|
+
const ABSOLUTE_HTTP_URL = /^https?:\/\//i;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Pick the srcset widths to generate for an image rendered at `maxWidth`.
|
|
20
|
+
* Includes breakpoints up to 2x (retina) plus the rendered width itself, so the
|
|
21
|
+
* browser always has an exact-fit candidate.
|
|
22
|
+
*/
|
|
23
|
+
export function responsiveWidths(maxWidth: number): number[] {
|
|
24
|
+
const cap = maxWidth * 2;
|
|
25
|
+
const widths = new Set(RESPONSIVE_BREAKPOINTS.filter((w) => w <= cap));
|
|
26
|
+
widths.add(maxWidth);
|
|
27
|
+
return [...widths].toSorted((a, b) => a - b);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Build the `sizes` attribute for an image with a known display width. */
|
|
31
|
+
export function responsiveSizes(width: number | undefined): string {
|
|
32
|
+
return width ? `(min-width: ${width}px) ${width}px, 100vw` : "100vw";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Make a same-origin media URL absolute so Astro's image service can optimize it.
|
|
37
|
+
*
|
|
38
|
+
* Astro only optimizes absolute http(s) URLs; a same-origin proxy path like
|
|
39
|
+
* `/_emdash/api/media/file/x.jpg` is otherwise treated as an unoptimizable
|
|
40
|
+
* public asset. Resolving it against the site's public origin (and authorizing
|
|
41
|
+
* that origin via `image.remotePatterns`) lets the service transform it.
|
|
42
|
+
*
|
|
43
|
+
* Only **same-origin** root-relative paths are resolved. Protocol-relative
|
|
44
|
+
* URLs (`//evil.com/x`) and backslash tricks (`/\evil.com`) also start with `/`
|
|
45
|
+
* but resolve to a different origin -- a classic SSRF vector once a
|
|
46
|
+
* remotePattern authorizes the media path -- so anything that escapes the
|
|
47
|
+
* origin is returned unchanged (and then skipped by `buildResponsiveImage`,
|
|
48
|
+
* which only accepts absolute http(s) URLs). Already-absolute URLs (CDN/public
|
|
49
|
+
* bucket) and non-path values (`data:`, `blob:`) are returned unchanged too.
|
|
50
|
+
*/
|
|
51
|
+
export function toAbsoluteMediaUrl(src: string, origin: string | undefined): string {
|
|
52
|
+
if (!src || !origin || !src.startsWith("/")) return src;
|
|
53
|
+
try {
|
|
54
|
+
const resolved = new URL(src, origin);
|
|
55
|
+
if (resolved.origin !== new URL(origin).origin) return src;
|
|
56
|
+
return resolved.href;
|
|
57
|
+
} catch {
|
|
58
|
+
return src;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Minimal structural subset of Astro's `getImage`. Astro's `ImageTransform`
|
|
64
|
+
* carries a `[key: string]: any` index signature, so the real `getImage` is
|
|
65
|
+
* assignable to this narrower type.
|
|
66
|
+
*/
|
|
67
|
+
export type GetImage = (options: {
|
|
68
|
+
src: string;
|
|
69
|
+
width?: number;
|
|
70
|
+
height?: number;
|
|
71
|
+
widths?: number[];
|
|
72
|
+
sizes?: string;
|
|
73
|
+
}) => Promise<{ src: string; srcSet?: { attribute?: string } | undefined }>;
|
|
74
|
+
|
|
75
|
+
export interface ResponsiveImage {
|
|
76
|
+
src: string;
|
|
77
|
+
srcset?: string;
|
|
78
|
+
sizes?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Generate a responsive `src`/`srcset`/`sizes` for a media URL via Astro's
|
|
83
|
+
* configured image service.
|
|
84
|
+
*
|
|
85
|
+
* Astro's image services (sharp, Cloudflare `/cdn-cgi/image`, and the default
|
|
86
|
+
* Cloudflare `cloudflare-binding` service) only optimize **absolute** URLs whose
|
|
87
|
+
* host is authorized via `image.domains` / `image.remotePatterns`. Anything else
|
|
88
|
+
* is passed through unchanged, which would yield a useless srcset (the same URL
|
|
89
|
+
* at every width descriptor). We therefore only attempt optimization for
|
|
90
|
+
* absolute http(s) URLs and verify the service actually rewrote the URL.
|
|
91
|
+
*
|
|
92
|
+
* Returns `null` so callers fall back to a plain `<img>` when:
|
|
93
|
+
* - dimensions are unknown (avoids an inferSize fetch on every render),
|
|
94
|
+
* - the URL is relative (a same-origin proxy/public asset Astro won't optimize),
|
|
95
|
+
* - the host isn't authorized (the service passed the URL through unchanged),
|
|
96
|
+
* - no image service is configured / `getImage` throws.
|
|
97
|
+
*/
|
|
98
|
+
export async function buildResponsiveImage(
|
|
99
|
+
getImage: GetImage,
|
|
100
|
+
opts: { src: string; width?: number; height?: number },
|
|
101
|
+
): Promise<ResponsiveImage | null> {
|
|
102
|
+
const { src, width, height } = opts;
|
|
103
|
+
if (!src || !width || !height) return null;
|
|
104
|
+
if (!ABSOLUTE_HTTP_URL.test(src)) return null;
|
|
105
|
+
try {
|
|
106
|
+
const sizes = responsiveSizes(width);
|
|
107
|
+
const result = await getImage({
|
|
108
|
+
src,
|
|
109
|
+
width,
|
|
110
|
+
height,
|
|
111
|
+
widths: responsiveWidths(width),
|
|
112
|
+
sizes,
|
|
113
|
+
});
|
|
114
|
+
// Passthrough: the service returned the source unchanged (unauthorized
|
|
115
|
+
// host or no optimization available). Don't emit a no-op srcset.
|
|
116
|
+
if (!result.src || result.src === src) return null;
|
|
117
|
+
return {
|
|
118
|
+
src: result.src,
|
|
119
|
+
srcset: result.srcSet?.attribute || undefined,
|
|
120
|
+
sizes,
|
|
121
|
+
};
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
package/src/plugins/cron.ts
CHANGED
|
@@ -34,8 +34,9 @@ export type RescheduleFn = () => void;
|
|
|
34
34
|
/**
|
|
35
35
|
* Executes overdue cron tasks.
|
|
36
36
|
*
|
|
37
|
-
* Called by platform
|
|
38
|
-
*
|
|
37
|
+
* Called by the platform driver: the NodeCronScheduler timer on Node, or the
|
|
38
|
+
* Worker's `scheduled()` handler (via runScheduledTasks) on Cloudflare.
|
|
39
|
+
* Stateless — all state lives in the database.
|
|
39
40
|
*/
|
|
40
41
|
export class CronExecutor {
|
|
41
42
|
constructor(
|
package/src/plugins/index.ts
CHANGED
|
@@ -63,6 +63,11 @@ export type { RouteResult, InvokeRouteOptions } from "./routes.js";
|
|
|
63
63
|
export { PluginManager, createPluginManager } from "./manager.js";
|
|
64
64
|
export type { PluginManagerOptions, PluginState } from "./manager.js";
|
|
65
65
|
|
|
66
|
+
// Scheduler (Node timer-based heartbeat; consumed by the generated
|
|
67
|
+
// virtual:emdash/scheduler module on non-serverless adapters)
|
|
68
|
+
export { NodeCronScheduler } from "./scheduler/node.js";
|
|
69
|
+
export type { CronScheduler, SystemCleanupFn } from "./scheduler/types.js";
|
|
70
|
+
|
|
66
71
|
// Sandbox
|
|
67
72
|
export {
|
|
68
73
|
NoopSandboxRunner,
|
|
@@ -15,8 +15,15 @@ import type { CronScheduler, SystemCleanupFn } from "./types.js";
|
|
|
15
15
|
/** Minimum polling interval (ms) — prevents tight loops if next_run_at is in the past */
|
|
16
16
|
const MIN_INTERVAL_MS = 1000;
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Maximum polling interval (ms). Each wake runs the maintenance pass — stale
|
|
20
|
+
* lock recovery *and* the scheduled-publishing sweep + system cleanup. The cap
|
|
21
|
+
* is the worst-case latency for scheduled content when no plugin cron task is
|
|
22
|
+
* due sooner (`getNextDueTime()` only knows about cron tasks, not content
|
|
23
|
+
* `scheduled_at`). Held at 60s so Node publish latency matches the Cloudflare
|
|
24
|
+
* Cron Trigger cadence (`* * * * *`) rather than lagging up to five minutes.
|
|
25
|
+
*/
|
|
26
|
+
const MAX_INTERVAL_MS = 60 * 1000;
|
|
20
27
|
|
|
21
28
|
export class NodeCronScheduler implements CronScheduler {
|
|
22
29
|
private timer: ReturnType<typeof setTimeout> | null = null;
|