emdash 0.17.2 → 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-B7GATEYo.mjs → api-BZ6bhjYs.mjs} +88 -16
- package/dist/api-BZ6bhjYs.mjs.map +1 -0
- package/dist/{apply-BrVqULFe.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 +414 -215
- 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-CLTmOUyx.mjs → authorize-C_8t2KGa.mjs} +2 -2
- package/dist/{authorize-CLTmOUyx.mjs.map → authorize-C_8t2KGa.mjs.map} +1 -1
- package/dist/{byline-CAhk4FrG.mjs → byline-DUx48sJp.mjs} +6 -6
- package/dist/{byline-CAhk4FrG.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-CR5hGLMw.d.mts → byline-fields-DYXKDuNX.d.mts} +53 -29
- 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-CbrD7STW.mjs → bylines-Cx5n-WqP.mjs} +3 -3
- package/dist/{bylines-CbrD7STW.mjs.map → bylines-Cx5n-WqP.mjs.map} +1 -1
- package/dist/{bylines-DCczH3AV.mjs → bylines-wurS258E.mjs} +50 -6
- package/dist/{bylines-DCczH3AV.mjs.map → bylines-wurS258E.mjs.map} +1 -1
- package/dist/{cache-DIHHyPkt.mjs → cache-B_HzASVT.mjs} +3 -3
- package/dist/{cache-DIHHyPkt.mjs.map → cache-B_HzASVT.mjs.map} +1 -1
- package/dist/{chunks-DnnHlRG3.mjs → chunks-BerYVuve.mjs} +2 -2
- package/dist/{chunks-DnnHlRG3.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-DkAfGX9E.mjs → comment-sqQxNpN3.mjs} +2 -2
- package/dist/{comment-DkAfGX9E.mjs.map → comment-sqQxNpN3.mjs.map} +1 -1
- package/dist/{comments-DLFnXs7J.mjs → comments-CJ0RZsYR.mjs} +3 -3
- package/dist/{comments-DLFnXs7J.mjs.map → comments-CJ0RZsYR.mjs.map} +1 -1
- package/dist/{content-C7aJ7keg.mjs → content-BIlVx-RX.mjs} +132 -43
- package/dist/content-BIlVx-RX.mjs.map +1 -0
- package/dist/{context-Ca0HkaIh.mjs → context-GG52SPgh.mjs} +10 -10
- package/dist/{context-Ca0HkaIh.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-BrfLIsX1.mjs → dashboard-2JgAMWxK.mjs} +4 -4
- package/dist/{dashboard-BrfLIsX1.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-Bk9s3Ism.mjs → error-RwM4dD35.mjs} +2 -2
- package/dist/{error-Bk9s3Ism.mjs.map → error-RwM4dD35.mjs.map} +1 -1
- package/dist/{fts-manager-XpDfbIKo.mjs → fts-manager-1RgHmopc.mjs} +2 -2
- package/dist/{fts-manager-XpDfbIKo.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-C8ciqSMJ.d.mts → index-FfiTQJq2.d.mts} +202 -20
- 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-CF5oETkh.mjs → load-B84ohfBk.mjs} +2 -2
- package/dist/{load-CF5oETkh.mjs.map → load-B84ohfBk.mjs.map} +1 -1
- package/dist/{loader-BxyvbrZP.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-Cyz5BhSN.mjs → media-JOf3pNkw.mjs} +2 -2
- package/dist/{media-Cyz5BhSN.mjs.map → media-JOf3pNkw.mjs.map} +1 -1
- package/dist/{menus-PFp8FDuO.mjs → menus-DX4_E01q.mjs} +3 -3
- package/dist/{menus-PFp8FDuO.mjs.map → menus-DX4_E01q.mjs.map} +1 -1
- package/dist/{menus-CIdZ_Q6U.mjs → menus-Dp9xporj.mjs} +112 -16
- 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-B-K21lvm.mjs → parse-CrGndy1A.mjs} +2 -2
- package/dist/{parse-B-K21lvm.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-Cc649nDl.mjs → query-BFQ029Ts.mjs} +21 -15
- package/dist/query-BFQ029Ts.mjs.map +1 -0
- package/dist/{rate-limit-BI1OdpQH.mjs → rate-limit-ClFFUga6.mjs} +2 -2
- package/dist/{rate-limit-BI1OdpQH.mjs.map → rate-limit-ClFFUga6.mjs.map} +1 -1
- package/dist/{redirect-C-FeA4j9.mjs → redirect-CRWIt8Zj.mjs} +3 -3
- package/dist/{redirect-C-FeA4j9.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-C1UgU9E0.mjs → redirects-OIu6vQ2i.mjs} +5 -5
- package/dist/{redirects-C1UgU9E0.mjs.map → redirects-OIu6vQ2i.mjs.map} +1 -1
- package/dist/{registry-C-T_PWgp.mjs → registry-brYh-rAT.mjs} +6 -6
- package/dist/{registry-C-T_PWgp.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-BiuUfx-V.mjs → runner--4wMWwKM.mjs} +224 -168
- 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-BpCJh2lU.mjs → schema-CS7Eg5gh.mjs} +5 -5
- package/dist/{schema-BpCJh2lU.mjs.map → schema-CS7Eg5gh.mjs.map} +1 -1
- package/dist/{search-BrF7k0Ho.mjs → search-o-aQzHI1.mjs} +4 -4
- package/dist/{search-BrF7k0Ho.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-8DEa-dWt.mjs → sections-DhsZ0ns9.mjs} +3 -3
- package/dist/{sections-8DEa-dWt.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-CKr7pLfA.mjs → seo-B5e6y9Wk.mjs} +2 -2
- package/dist/{seo-CKr7pLfA.mjs.map → seo-B5e6y9Wk.mjs.map} +1 -1
- package/dist/{service-9P2cdyR_.mjs → service-DAxg8RPR.mjs} +2 -2
- package/dist/{service-9P2cdyR_.mjs.map → service-DAxg8RPR.mjs.map} +1 -1
- package/dist/{settings-Jro4YcUb.mjs → settings-B1p-gPUK.mjs} +5 -5
- package/dist/{settings-Jro4YcUb.mjs.map → settings-B1p-gPUK.mjs.map} +1 -1
- package/dist/{settings-DYVzINdn.mjs → settings-DIsbHTRE.mjs} +3 -3
- package/dist/{settings-DYVzINdn.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-CGD6y79Q.mjs → taxonomies-BEW7S5AI.mjs} +10 -8
- package/dist/taxonomies-BEW7S5AI.mjs.map +1 -0
- package/dist/{taxonomies-C0bVme_m.mjs → taxonomies-UusDXv3C.mjs} +4 -4
- package/dist/{taxonomies-C0bVme_m.mjs.map → taxonomies-UusDXv3C.mjs.map} +1 -1
- package/dist/{taxonomy-Db5xwphL.mjs → taxonomy-CdllE4oq.mjs} +3 -3
- package/dist/{taxonomy-Db5xwphL.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-CfyYQ7eY.mjs → types-BXSUSAjt.mjs} +16 -3
- package/dist/{types-CfyYQ7eY.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-tLdHUEXV.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-DWmnRg6E.mjs → validate-ZP9Dvg0P.mjs} +6 -3
- package/dist/validate-ZP9Dvg0P.mjs.map +1 -0
- package/dist/{validation-BQ_TP-On.mjs → validation-CE5i4q0c.mjs} +5 -5
- package/dist/{validation-BQ_TP-On.mjs.map → validation-CE5i4q0c.mjs.map} +1 -1
- package/dist/version-Dw0JXu45.mjs +7 -0
- package/dist/{version-CgcnMvqS.mjs.map → version-Dw0JXu45.mjs.map} +1 -1
- package/dist/{widgets-DzlINGI6.mjs → widgets-ClEnYQCH.mjs} +2 -2
- package/dist/{widgets-DzlINGI6.mjs.map → widgets-ClEnYQCH.mjs.map} +1 -1
- package/dist/{zod-generator-MMm56Prt.mjs → zod-generator-Djo_VHCt.mjs} +4 -3
- package/dist/zod-generator-Djo_VHCt.mjs.map +1 -0
- package/package.json +7 -7
- 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/stream-end-metrics.ts +96 -0
- package/src/astro/middleware.ts +107 -31
- 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 +23 -4
- package/src/components/Image.astro +20 -3
- package/src/database/migrations/043_content_references.ts +121 -0
- package/src/database/migrations/runner.ts +9 -2
- 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 +318 -168
- package/src/index.ts +8 -1
- package/src/loader.ts +67 -34
- package/src/media/responsive.ts +125 -0
- package/src/menus/index.ts +27 -9
- package/src/plugins/cron.ts +3 -2
- package/src/plugins/hooks.ts +35 -6
- package/src/plugins/index.ts +5 -0
- package/src/plugins/manager.ts +1 -0
- package/src/plugins/scheduler/node.ts +9 -2
- package/src/query.ts +32 -5
- package/src/scheduled-publish.ts +153 -0
- package/src/schema/zod-generator.ts +6 -2
- 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 +13 -8
- package/src/utils/init-lock.ts +143 -0
- package/src/virtual-modules.d.ts +11 -0
- package/dist/api-B7GATEYo.mjs.map +0 -1
- package/dist/apply-BrVqULFe.mjs.map +0 -1
- package/dist/byline-fields-CR5hGLMw.d.mts.map +0 -1
- package/dist/content-C7aJ7keg.mjs.map +0 -1
- package/dist/cron-DZovZUnC.mjs.map +0 -1
- package/dist/index-C8ciqSMJ.d.mts.map +0 -1
- package/dist/loader-BxyvbrZP.mjs.map +0 -1
- package/dist/menus-CIdZ_Q6U.mjs.map +0 -1
- package/dist/query-Cc649nDl.mjs.map +0 -1
- package/dist/redirects-C0L9JUk4.mjs.map +0 -1
- package/dist/runner-BiuUfx-V.mjs.map +0 -1
- package/dist/taxonomies-CGD6y79Q.mjs.map +0 -1
- package/dist/types-i8_uzhMD.d.mts.map +0 -1
- package/dist/user-tLdHUEXV.mjs.map +0 -1
- package/dist/validate-DWmnRg6E.mjs.map +0 -1
- package/dist/validate-Dy6nkNls.d.mts.map +0 -1
- package/dist/version-CgcnMvqS.mjs +0 -7
- package/dist/zod-generator-MMm56Prt.mjs.map +0 -1
- package/src/plugins/scheduler/piggyback.ts +0 -71
package/src/emdash-runtime.ts
CHANGED
|
@@ -22,9 +22,12 @@ import { getAuthMode } from "./auth/mode.js";
|
|
|
22
22
|
import { getTrustedProxyHeaders } from "./auth/trusted-proxy.js";
|
|
23
23
|
import { isSqlite } from "./database/dialect-helpers.js";
|
|
24
24
|
import { kyselyLogOption } from "./database/instrumentation.js";
|
|
25
|
-
import { runMigrations } from "./database/migrations/runner.js";
|
|
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";
|
|
@@ -41,6 +44,7 @@ import type {
|
|
|
41
44
|
} from "./plugins/types.js";
|
|
42
45
|
import type { FieldType } from "./schema/types.js";
|
|
43
46
|
import { hashString } from "./utils/hash.js";
|
|
47
|
+
import { createInitLock, type InitLock, initWithLock } from "./utils/init-lock.js";
|
|
44
48
|
import { COMMIT, VERSION } from "./version.js";
|
|
45
49
|
|
|
46
50
|
const LEADING_SLASH_PATTERN = /^\//;
|
|
@@ -114,6 +118,7 @@ import { validateEncryptionKeyAtStartup } from "./config/secrets.js";
|
|
|
114
118
|
import { OptionsRepository } from "./database/repositories/options.js";
|
|
115
119
|
import {
|
|
116
120
|
handleContentList,
|
|
121
|
+
handleContentAuthors,
|
|
117
122
|
handleContentGet,
|
|
118
123
|
handleContentGetIncludingTrashed,
|
|
119
124
|
handleContentCreate,
|
|
@@ -157,13 +162,12 @@ import {
|
|
|
157
162
|
import { normalizeManifestRoute } from "./plugins/manifest-schema.js";
|
|
158
163
|
import { extractRequestMeta, sanitizeHeadersForSandbox } from "./plugins/request-meta.js";
|
|
159
164
|
import { PluginRouteRegistry, type RouteMeta } from "./plugins/routes.js";
|
|
160
|
-
import { NodeCronScheduler } from "./plugins/scheduler/node.js";
|
|
161
|
-
import { PiggybackScheduler } from "./plugins/scheduler/piggyback.js";
|
|
162
165
|
import type { CronScheduler } from "./plugins/scheduler/types.js";
|
|
163
166
|
import { PluginStateRepository } from "./plugins/state.js";
|
|
164
167
|
import { normalizeRegistryConfig } from "./registry/config.js";
|
|
165
168
|
import { requestCached } from "./request-cache.js";
|
|
166
169
|
import { getRequestContext } from "./request-context.js";
|
|
170
|
+
import { publishDueContent, type PublishedRef } from "./scheduled-publish.js";
|
|
167
171
|
import { FTSManager } from "./search/fts-manager.js";
|
|
168
172
|
import { invalidateSiteSettingsCache } from "./settings/index.js";
|
|
169
173
|
|
|
@@ -236,6 +240,13 @@ export interface MediaProviderContext {
|
|
|
236
240
|
storage: Storage | null;
|
|
237
241
|
}
|
|
238
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
|
+
|
|
239
250
|
/**
|
|
240
251
|
* Dependencies injected from virtual modules (middleware reads these)
|
|
241
252
|
*/
|
|
@@ -249,6 +260,16 @@ export interface RuntimeDependencies {
|
|
|
249
260
|
sandboxEnabled: boolean;
|
|
250
261
|
/** sandbox: false escape hatch - load sandboxed plugins in-process */
|
|
251
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;
|
|
252
273
|
/** Media provider entries from virtual module */
|
|
253
274
|
mediaProviderEntries?: MediaProviderEntry[];
|
|
254
275
|
sandboxedPluginEntries: SandboxedPluginEntry[];
|
|
@@ -310,9 +331,38 @@ function contentItemToRecord(item: ContentItemInternal): Record<string, unknown>
|
|
|
310
331
|
return { ...item };
|
|
311
332
|
}
|
|
312
333
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
334
|
+
/**
|
|
335
|
+
* Db init lock reclaim deadline. Derived from the migration race wait so
|
|
336
|
+
* they can't drift apart: a healthy init can legitimately block for the
|
|
337
|
+
* full MIGRATION_RACE_WAIT_MS inside waitForConcurrentMigrator, plus cold
|
|
338
|
+
* connect and migrator work, before it should be presumed dead. The outer
|
|
339
|
+
* runtime init lock (middleware.ts) must use a strictly larger deadline —
|
|
340
|
+
* it wraps create() → getDatabase() → this lock, and equal deadlines would
|
|
341
|
+
* let the outer reclaim while the inner is legitimately still working.
|
|
342
|
+
*/
|
|
343
|
+
export const DB_INIT_DEADLINE_MS = MIGRATION_RACE_WAIT_MS + 20_000;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Db cache + its init lock live on globalThis behind a Symbol: the bundler
|
|
347
|
+
* can duplicate this module across SSR chunks (same reasoning as
|
|
348
|
+
* request-cache.ts), and a duplicated cache/lock would mean concurrent
|
|
349
|
+
* independent db inits — and duplicate migrators — per isolate.
|
|
350
|
+
*/
|
|
351
|
+
const DB_HOLDER_KEY = Symbol.for("emdash:db-cache");
|
|
352
|
+
interface DbHolder {
|
|
353
|
+
cache: Map<string, Kysely<Database>>;
|
|
354
|
+
lock: InitLock;
|
|
355
|
+
}
|
|
356
|
+
const globalSymbolStore = globalThis as Record<symbol, unknown>;
|
|
357
|
+
function getDbHolder(): DbHolder {
|
|
358
|
+
// eslint-disable-next-line typescript/no-unsafe-type-assertion -- globalThis symbol slot, written only below
|
|
359
|
+
let holder = globalSymbolStore[DB_HOLDER_KEY] as DbHolder | undefined;
|
|
360
|
+
if (!holder) {
|
|
361
|
+
holder = { cache: new Map<string, Kysely<Database>>(), lock: createInitLock() };
|
|
362
|
+
globalSymbolStore[DB_HOLDER_KEY] = holder;
|
|
363
|
+
}
|
|
364
|
+
return holder;
|
|
365
|
+
}
|
|
316
366
|
const storageCache = new Map<string, Storage>();
|
|
317
367
|
const sandboxedPluginCache = new Map<string, SandboxedPluginInstance>();
|
|
318
368
|
/**
|
|
@@ -449,14 +499,65 @@ export class EmDashRuntime {
|
|
|
449
499
|
}
|
|
450
500
|
|
|
451
501
|
/**
|
|
452
|
-
*
|
|
453
|
-
*
|
|
454
|
-
* execute even when no dedicated scheduler is available.
|
|
502
|
+
* Publish any content whose scheduled time has passed.
|
|
503
|
+
* Returns the items promoted so callers can invalidate their cache tags.
|
|
455
504
|
*/
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
this.
|
|
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.
|
|
524
|
+
*/
|
|
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
|
+
}
|
|
459
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 };
|
|
460
561
|
}
|
|
461
562
|
|
|
462
563
|
/**
|
|
@@ -887,19 +988,45 @@ export class EmDashRuntime {
|
|
|
887
988
|
// Initialize storage (sync)
|
|
888
989
|
const storage = EmDashRuntime.getStorage(deps);
|
|
889
990
|
|
|
890
|
-
// Fetch plugin states
|
|
991
|
+
// Fetch plugin states and site info concurrently — independent reads
|
|
992
|
+
// against different tables (_plugin_state vs options), so they share
|
|
993
|
+
// one round-trip window instead of paying two sequential ones. Each
|
|
994
|
+
// phase() wrapper still records that phase's own duration, and each
|
|
995
|
+
// body keeps its own non-fatal catch.
|
|
891
996
|
let pluginStates: Map<string, string> = new Map();
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
997
|
+
let siteInfo: { siteName?: string; siteUrl?: string; locale?: string } | undefined;
|
|
998
|
+
await Promise.all([
|
|
999
|
+
// Fetch plugin states from database
|
|
1000
|
+
phase("rt.plugins", "Plugin states", async () => {
|
|
1001
|
+
try {
|
|
1002
|
+
const states = await db
|
|
1003
|
+
.selectFrom("_plugin_state")
|
|
1004
|
+
.select(["plugin_id", "status"])
|
|
1005
|
+
.execute();
|
|
1006
|
+
pluginStates = new Map(states.map((s) => [s.plugin_id, s.status]));
|
|
1007
|
+
} catch {
|
|
1008
|
+
// Plugin state table may not exist yet
|
|
1009
|
+
}
|
|
1010
|
+
}),
|
|
1011
|
+
// Load site info for plugin context extensions (1 batch query instead of 3)
|
|
1012
|
+
phase("rt.site", "Site info options", async () => {
|
|
1013
|
+
try {
|
|
1014
|
+
const optionsRepo = new OptionsRepository(db);
|
|
1015
|
+
const siteOpts = await optionsRepo.getMany<string>([
|
|
1016
|
+
"emdash:site_title",
|
|
1017
|
+
"emdash:site_url",
|
|
1018
|
+
"emdash:locale",
|
|
1019
|
+
]);
|
|
1020
|
+
siteInfo = {
|
|
1021
|
+
siteName: siteOpts.get("emdash:site_title") ?? undefined,
|
|
1022
|
+
siteUrl: siteOpts.get("emdash:site_url") ?? undefined,
|
|
1023
|
+
locale: siteOpts.get("emdash:locale") ?? undefined,
|
|
1024
|
+
};
|
|
1025
|
+
} catch {
|
|
1026
|
+
// Options table may not exist yet (pre-setup)
|
|
1027
|
+
}
|
|
1028
|
+
}),
|
|
1029
|
+
]);
|
|
903
1030
|
|
|
904
1031
|
// Build set of enabled plugins
|
|
905
1032
|
const enabledPlugins = new Set<string>();
|
|
@@ -910,26 +1037,6 @@ export class EmDashRuntime {
|
|
|
910
1037
|
}
|
|
911
1038
|
}
|
|
912
1039
|
|
|
913
|
-
// Load site info for plugin context extensions (1 batch query instead of 3)
|
|
914
|
-
let siteInfo: { siteName?: string; siteUrl?: string; locale?: string } | undefined;
|
|
915
|
-
await phase("rt.site", "Site info options", async () => {
|
|
916
|
-
try {
|
|
917
|
-
const optionsRepo = new OptionsRepository(db);
|
|
918
|
-
const siteOpts = await optionsRepo.getMany<string>([
|
|
919
|
-
"emdash:site_title",
|
|
920
|
-
"emdash:site_url",
|
|
921
|
-
"emdash:locale",
|
|
922
|
-
]);
|
|
923
|
-
siteInfo = {
|
|
924
|
-
siteName: siteOpts.get("emdash:site_title") ?? undefined,
|
|
925
|
-
siteUrl: siteOpts.get("emdash:site_url") ?? undefined,
|
|
926
|
-
locale: siteOpts.get("emdash:locale") ?? undefined,
|
|
927
|
-
};
|
|
928
|
-
} catch {
|
|
929
|
-
// Options table may not exist yet (pre-setup)
|
|
930
|
-
}
|
|
931
|
-
});
|
|
932
|
-
|
|
933
1040
|
// Build the full list of pipeline-eligible plugins: all configured
|
|
934
1041
|
// plugins (regardless of current enabled status) plus built-in plugins.
|
|
935
1042
|
// rebuildHookPipeline() filters this to only enabled plugins.
|
|
@@ -1050,32 +1157,43 @@ export class EmDashRuntime {
|
|
|
1050
1157
|
EmDashRuntime.loadSandboxedPlugins(deps, db, storage),
|
|
1051
1158
|
);
|
|
1052
1159
|
|
|
1053
|
-
// Cold-start: load marketplace-installed plugins from
|
|
1054
|
-
// the sandbox runner.
|
|
1160
|
+
// Cold-start: load marketplace- and registry-installed plugins from
|
|
1161
|
+
// site R2 via the sandbox runner. The two tiers only depend on the
|
|
1162
|
+
// sandbox phase above, not on each other, so when both are enabled
|
|
1163
|
+
// they run concurrently instead of paying two sequential loads.
|
|
1164
|
+
// In bypass mode marketplace plugins were already handled above.
|
|
1165
|
+
const installedTierPhases: Promise<void>[] = [];
|
|
1055
1166
|
if (deps.config.marketplace && storage && !deps.sandboxBypassed) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1167
|
+
installedTierPhases.push(
|
|
1168
|
+
phase("rt.market", "Marketplace plugins", () =>
|
|
1169
|
+
EmDashRuntime.loadInstalledSandboxedPlugins(
|
|
1170
|
+
"marketplace",
|
|
1171
|
+
db,
|
|
1172
|
+
storage,
|
|
1173
|
+
deps,
|
|
1174
|
+
sandboxedPlugins,
|
|
1175
|
+
),
|
|
1063
1176
|
),
|
|
1064
1177
|
);
|
|
1065
1178
|
}
|
|
1066
1179
|
|
|
1067
1180
|
// Cold-start: load registry-installed plugins from site R2
|
|
1068
1181
|
if (deps.config.experimental?.registry && storage) {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1182
|
+
installedTierPhases.push(
|
|
1183
|
+
phase("rt.registry", "Registry plugins", () =>
|
|
1184
|
+
EmDashRuntime.loadInstalledSandboxedPlugins(
|
|
1185
|
+
"registry",
|
|
1186
|
+
db,
|
|
1187
|
+
storage,
|
|
1188
|
+
deps,
|
|
1189
|
+
sandboxedPlugins,
|
|
1190
|
+
),
|
|
1076
1191
|
),
|
|
1077
1192
|
);
|
|
1078
1193
|
}
|
|
1194
|
+
if (installedTierPhases.length > 0) {
|
|
1195
|
+
await Promise.all(installedTierPhases);
|
|
1196
|
+
}
|
|
1079
1197
|
|
|
1080
1198
|
// Initialize media providers
|
|
1081
1199
|
const mediaProviders = new Map<string, MediaProvider>();
|
|
@@ -1126,6 +1244,11 @@ export class EmDashRuntime {
|
|
|
1126
1244
|
|
|
1127
1245
|
let cronExecutor: CronExecutor | null = null;
|
|
1128
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 };
|
|
1129
1252
|
|
|
1130
1253
|
await phase("rt.cron", "Cron init (recovery deferred post-response)", async () => {
|
|
1131
1254
|
try {
|
|
@@ -1151,46 +1274,57 @@ export class EmDashRuntime {
|
|
|
1151
1274
|
}
|
|
1152
1275
|
});
|
|
1153
1276
|
|
|
1154
|
-
//
|
|
1155
|
-
//
|
|
1156
|
-
//
|
|
1157
|
-
//
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
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
|
+
});
|
|
1179
1311
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1312
|
+
// Add cron reschedule callback (merges with existing factory options)
|
|
1313
|
+
pipeline.setContextFactory({
|
|
1314
|
+
cronReschedule: () => cronScheduler?.reschedule(),
|
|
1315
|
+
});
|
|
1184
1316
|
|
|
1185
|
-
|
|
1186
|
-
|
|
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
|
+
}
|
|
1187
1321
|
} catch (error) {
|
|
1188
1322
|
console.warn("[cron] Failed to initialize cron system:", error);
|
|
1189
1323
|
// Non-fatal — CMS works without cron
|
|
1190
1324
|
}
|
|
1191
1325
|
});
|
|
1192
1326
|
|
|
1193
|
-
|
|
1327
|
+
const runtime = new EmDashRuntime({
|
|
1194
1328
|
db,
|
|
1195
1329
|
storage,
|
|
1196
1330
|
// Include bypassed sandboxed plugins in configuredPlugins so route
|
|
@@ -1213,6 +1347,10 @@ export class EmDashRuntime {
|
|
|
1213
1347
|
runtimeDeps: deps,
|
|
1214
1348
|
pipelineRef,
|
|
1215
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;
|
|
1216
1354
|
}
|
|
1217
1355
|
|
|
1218
1356
|
/**
|
|
@@ -1270,83 +1408,86 @@ export class EmDashRuntime {
|
|
|
1270
1408
|
|
|
1271
1409
|
const cacheKey = dbConfig.entrypoint;
|
|
1272
1410
|
|
|
1273
|
-
//
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
//
|
|
1280
|
-
//
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
console.log("Auto-seeded default collections");
|
|
1411
|
+
// Waiters poll the cache rather than sharing the initializing request's
|
|
1412
|
+
// promise: if the request that owns the init is cancelled mid-await
|
|
1413
|
+
// (e.g. client disconnect during cold migrations), a shared promise
|
|
1414
|
+
// never settles — and the owner's `finally` that would clear it never
|
|
1415
|
+
// runs — deadlocking every later request in the isolate. Prevention:
|
|
1416
|
+
// the in-flight init is anchored via after()/waitUntil so a cancelled
|
|
1417
|
+
// owner's init still completes and populates the cache. Net: a stale
|
|
1418
|
+
// lock is reclaimed after a deadline.
|
|
1419
|
+
const holder = getDbHolder();
|
|
1420
|
+
return initWithLock(
|
|
1421
|
+
holder.lock,
|
|
1422
|
+
() => holder.cache.get(cacheKey),
|
|
1423
|
+
async (isCurrentClaim) => {
|
|
1424
|
+
const dialect = deps.createDialect(dbConfig.config);
|
|
1425
|
+
const db = new Kysely<Database>({ dialect, log: kyselyLogOption() });
|
|
1426
|
+
|
|
1427
|
+
await runMigrations(db);
|
|
1428
|
+
|
|
1429
|
+
// Note: legacy installs may carry a stray `emdash:manifest_cache`
|
|
1430
|
+
// row in the options table from versions that persisted a JSON
|
|
1431
|
+
// manifest. The runtime no longer reads or writes it. We do not
|
|
1432
|
+
// proactively delete it: the row is a few hundred bytes of dead
|
|
1433
|
+
// weight and is never on the read path, whereas a one-shot
|
|
1434
|
+
// cleanup-flag check costs an extra `options.get()` on every
|
|
1435
|
+
// isolate cold boot forever. Cheaper to leave it.
|
|
1436
|
+
|
|
1437
|
+
// Auto-seed schema if no collections exist and setup hasn't run.
|
|
1438
|
+
// This covers first-load on sites that skip the setup wizard.
|
|
1439
|
+
// Dev-bypass and the wizard apply seeds explicitly.
|
|
1440
|
+
try {
|
|
1441
|
+
const [collectionCount, setupOption] = await Promise.all([
|
|
1442
|
+
db
|
|
1443
|
+
.selectFrom("_emdash_collections")
|
|
1444
|
+
.select((eb) => eb.fn.countAll<number>().as("count"))
|
|
1445
|
+
.executeTakeFirstOrThrow(),
|
|
1446
|
+
db
|
|
1447
|
+
.selectFrom("options")
|
|
1448
|
+
.select("value")
|
|
1449
|
+
.where("name", "=", "emdash:setup_complete")
|
|
1450
|
+
.executeTakeFirst(),
|
|
1451
|
+
]);
|
|
1452
|
+
|
|
1453
|
+
const setupDone = (() => {
|
|
1454
|
+
try {
|
|
1455
|
+
return setupOption && JSON.parse(setupOption.value) === true;
|
|
1456
|
+
} catch {
|
|
1457
|
+
return false;
|
|
1458
|
+
}
|
|
1459
|
+
})();
|
|
1460
|
+
|
|
1461
|
+
if (collectionCount.count === 0 && !setupDone) {
|
|
1462
|
+
const { applySeed } = await import("./seed/apply.js");
|
|
1463
|
+
const { loadSeed } = await import("./seed/load.js");
|
|
1464
|
+
const { validateSeed } = await import("./seed/validate.js");
|
|
1465
|
+
|
|
1466
|
+
const seed = await loadSeed();
|
|
1467
|
+
const validation = validateSeed(seed);
|
|
1468
|
+
if (validation.valid) {
|
|
1469
|
+
await applySeed(db, seed, { onConflict: "skip" });
|
|
1470
|
+
console.log("Auto-seeded default collections");
|
|
1471
|
+
}
|
|
1335
1472
|
}
|
|
1473
|
+
} catch {
|
|
1474
|
+
// Tables may not exist yet. Non-fatal.
|
|
1336
1475
|
}
|
|
1337
|
-
} catch {
|
|
1338
|
-
// Tables may not exist yet. Non-fatal.
|
|
1339
|
-
}
|
|
1340
1476
|
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1477
|
+
// Publish only while still the current owner: a reclaimed slow
|
|
1478
|
+
// init must not flip the cached Kysely identity back after the
|
|
1479
|
+
// reclaimer has published its own. The unpublished instance is
|
|
1480
|
+
// still returned and fully valid for the request that built it.
|
|
1481
|
+
if (isCurrentClaim()) {
|
|
1482
|
+
holder.cache.set(cacheKey, db);
|
|
1483
|
+
}
|
|
1484
|
+
return db;
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
deadlineMs: DB_INIT_DEADLINE_MS,
|
|
1488
|
+
anchor: (promise) => after(() => promise),
|
|
1489
|
+
},
|
|
1490
|
+
);
|
|
1350
1491
|
}
|
|
1351
1492
|
|
|
1352
1493
|
/**
|
|
@@ -1778,6 +1919,7 @@ export class EmDashRuntime {
|
|
|
1778
1919
|
pipeline,
|
|
1779
1920
|
isActive: () => true,
|
|
1780
1921
|
getOption: (key) => optionsRepo.get<string>(key),
|
|
1922
|
+
getOptions: (keys) => optionsRepo.getMany<string>(keys),
|
|
1781
1923
|
setOption: (key, value) => optionsRepo.set(key, value),
|
|
1782
1924
|
deleteOption: async (key) => {
|
|
1783
1925
|
await optionsRepo.delete(key);
|
|
@@ -2123,11 +2265,19 @@ export class EmDashRuntime {
|
|
|
2123
2265
|
order?: "asc" | "desc";
|
|
2124
2266
|
locale?: string;
|
|
2125
2267
|
q?: string;
|
|
2268
|
+
authorId?: string;
|
|
2269
|
+
dateField?: ContentDateField;
|
|
2270
|
+
dateFrom?: string;
|
|
2271
|
+
dateTo?: string;
|
|
2126
2272
|
},
|
|
2127
2273
|
) {
|
|
2128
2274
|
return handleContentList(this.db, collection, params);
|
|
2129
2275
|
}
|
|
2130
2276
|
|
|
2277
|
+
async handleContentAuthors(collection: string) {
|
|
2278
|
+
return handleContentAuthors(this.db, collection);
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2131
2281
|
async handleContentGet(collection: string, id: string, locale?: string) {
|
|
2132
2282
|
const result = await handleContentGet(this.db, collection, id, locale);
|
|
2133
2283
|
return this.hydrateDraftData(result);
|
|
@@ -2502,7 +2652,7 @@ export class EmDashRuntime {
|
|
|
2502
2652
|
async handleContentPublish(
|
|
2503
2653
|
collection: string,
|
|
2504
2654
|
id: string,
|
|
2505
|
-
options: { publishedAt?: string } = {},
|
|
2655
|
+
options: { publishedAt?: string; requireScheduledDue?: boolean } = {},
|
|
2506
2656
|
) {
|
|
2507
2657
|
const result = await handleContentPublish(this.db, collection, id, options);
|
|
2508
2658
|
|