emdash 0.17.1 → 0.18.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.mjs +11 -11
- package/dist/{api-Dmz40c2V.mjs → api-Cs7DAACP.mjs} +12 -12
- package/dist/{api-Dmz40c2V.mjs.map → api-Cs7DAACP.mjs.map} +1 -1
- package/dist/{apply-CuuZG6op.mjs → apply-BWMV4Zmw.mjs} +16 -16
- package/dist/{apply-CuuZG6op.mjs.map → apply-BWMV4Zmw.mjs.map} +1 -1
- package/dist/astro/index.mjs +1 -1
- package/dist/astro/middleware/auth.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +5 -5
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +274 -91
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +3 -3
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +3 -3
- 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 +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +4 -4
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +9 -9
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +9 -9
- package/dist/astro/routes/api/admin/bylines/index.mjs +9 -9
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +6 -6
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- 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 +26 -26
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/index.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +26 -26
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/updates.mjs +26 -26
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +26 -26
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +26 -26
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- package/dist/astro/routes/api/admin/users/index.mjs +3 -3
- package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/index.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +4 -4
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/request.mjs +4 -4
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- 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 +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +4 -4
- 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.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/index.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/trash.mjs +4 -4
- 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.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress/media.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +5 -5
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +6 -6
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +26 -26
- package/dist/astro/routes/api/media/_id_/confirm.mjs +4 -4
- package/dist/astro/routes/api/media/_id_.mjs +4 -4
- 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 +4 -4
- package/dist/astro/routes/api/media.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/items.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/translations.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_.mjs +5 -5
- package/dist/astro/routes/api/menus/index.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
- package/dist/astro/routes/api/oauth/token.mjs +2 -2
- package/dist/astro/routes/api/openapi.json.mjs +2 -2
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/index.mjs +7 -7
- package/dist/astro/routes/api/redirects/404s/summary.mjs +7 -7
- package/dist/astro/routes/api/redirects/_id_.mjs +8 -8
- package/dist/astro/routes/api/redirects/index.mjs +8 -8
- 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 +26 -26
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +26 -26
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +26 -26
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +26 -26
- package/dist/astro/routes/api/schema/collections/index.mjs +26 -26
- package/dist/astro/routes/api/schema/index.mjs +7 -7
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +26 -26
- package/dist/astro/routes/api/schema/orphans/index.mjs +26 -26
- package/dist/astro/routes/api/search/enable.mjs +8 -8
- package/dist/astro/routes/api/search/index.mjs +7 -7
- package/dist/astro/routes/api/search/rebuild.mjs +8 -8
- package/dist/astro/routes/api/search/stats.mjs +7 -7
- package/dist/astro/routes/api/search/suggest.mjs +7 -7
- package/dist/astro/routes/api/sections/_slug_.mjs +7 -7
- package/dist/astro/routes/api/sections/index.mjs +7 -7
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +9 -9
- package/dist/astro/routes/api/setup/admin-verify.mjs +3 -3
- package/dist/astro/routes/api/setup/admin.mjs +3 -3
- package/dist/astro/routes/api/setup/dev-bypass.mjs +16 -16
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +17 -17
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +3 -3
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +9 -9
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +9 -9
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +9 -9
- package/dist/astro/routes/api/taxonomies/index.mjs +9 -9
- package/dist/astro/routes/api/themes/preview.mjs +3 -3
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +4 -4
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/index.mjs +7 -7
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +5 -5
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +5 -5
- package/dist/astro/routes/sitemap.xml.mjs +5 -5
- package/dist/{authorize-_wWM_44T.mjs → authorize-CotM4Yiu.mjs} +2 -2
- package/dist/{authorize-_wWM_44T.mjs.map → authorize-CotM4Yiu.mjs.map} +1 -1
- package/dist/{byline-BrIVWLm-.mjs → byline-CWQ9aSoz.mjs} +4 -4
- package/dist/{byline-BrIVWLm-.mjs.map → byline-CWQ9aSoz.mjs.map} +1 -1
- package/dist/{bylines-C_POWmGT.mjs → bylines-BJSva1Un.mjs} +4 -4
- package/dist/{bylines-C_POWmGT.mjs.map → bylines-BJSva1Un.mjs.map} +1 -1
- package/dist/{bylines-sqExMElV.mjs → bylines-LJMgENMI.mjs} +3 -3
- package/dist/{bylines-sqExMElV.mjs.map → bylines-LJMgENMI.mjs.map} +1 -1
- package/dist/{cache-wsDkA8ru.mjs → cache-lZL7SgVb.mjs} +2 -2
- package/dist/{cache-wsDkA8ru.mjs.map → cache-lZL7SgVb.mjs.map} +1 -1
- package/dist/{chunks-BAYkM-CF.mjs → chunks-BU-vP9Dh.mjs} +2 -2
- package/dist/{chunks-BAYkM-CF.mjs.map → chunks-BU-vP9Dh.mjs.map} +1 -1
- package/dist/cli/index.mjs +14 -14
- package/dist/{comment-Cd29aktf.mjs → comment-C4jVbCM8.mjs} +2 -2
- package/dist/{comment-Cd29aktf.mjs.map → comment-C4jVbCM8.mjs.map} +1 -1
- package/dist/{comments-B7ufhkxN.mjs → comments-BTAbC0Ek.mjs} +3 -3
- package/dist/{comments-B7ufhkxN.mjs.map → comments-BTAbC0Ek.mjs.map} +1 -1
- package/dist/{content-BbqKo3Kc.mjs → content-CyqOmOzm.mjs} +3 -3
- package/dist/{content-BbqKo3Kc.mjs.map → content-CyqOmOzm.mjs.map} +1 -1
- package/dist/{context-BsF1rhoI.mjs → context-DZ7bEh5-.mjs} +8 -8
- package/dist/{context-BsF1rhoI.mjs.map → context-DZ7bEh5-.mjs.map} +1 -1
- package/dist/{dashboard-BwIX9r-X.mjs → dashboard-B5WQpNTP.mjs} +4 -4
- package/dist/{dashboard-BwIX9r-X.mjs.map → dashboard-B5WQpNTP.mjs.map} +1 -1
- package/dist/db/index.mjs +2 -2
- package/dist/{dialect-helpers-BKCvISIQ.mjs → dialect-helpers-DRI5pyY3.mjs} +3 -3
- package/dist/dialect-helpers-DRI5pyY3.mjs.map +1 -0
- package/dist/{error-npZWBSb7.mjs → error-DJOsMVSt.mjs} +2 -2
- package/dist/{error-npZWBSb7.mjs.map → error-DJOsMVSt.mjs.map} +1 -1
- package/dist/{fts-manager-DmUAk-kQ.mjs → fts-manager-DR1ERA0c.mjs} +3 -3
- package/dist/{fts-manager-DmUAk-kQ.mjs.map → fts-manager-DR1ERA0c.mjs.map} +1 -1
- package/dist/index-CjKdMZ3U.d.mts.map +1 -1
- package/dist/index.mjs +35 -35
- package/dist/{load-DsoLq7ex.mjs → load-6ZrRhepW.mjs} +2 -2
- package/dist/{load-DsoLq7ex.mjs.map → load-6ZrRhepW.mjs.map} +1 -1
- package/dist/{loader-CJ6lWO0d.mjs → loader-Dyx8dhFV.mjs} +4 -4
- package/dist/{loader-CJ6lWO0d.mjs.map → loader-Dyx8dhFV.mjs.map} +1 -1
- package/dist/media/local-runtime.mjs +5 -5
- package/dist/{media-jk_HzzOl.mjs → media-C-oovGCG.mjs} +2 -2
- package/dist/{media-jk_HzzOl.mjs.map → media-C-oovGCG.mjs.map} +1 -1
- package/dist/{menus-CyMO6GBx.mjs → menus-BKkxXCmd.mjs} +30 -11
- package/dist/menus-BKkxXCmd.mjs.map +1 -0
- package/dist/{menus-B-5-3aon.mjs → menus-DugoYwTX.mjs} +2 -2
- package/dist/{menus-B-5-3aon.mjs.map → menus-DugoYwTX.mjs.map} +1 -1
- package/dist/{parse-4zO5Y2DL.mjs → parse-BBkFmLVr.mjs} +2 -2
- package/dist/{parse-4zO5Y2DL.mjs.map → parse-BBkFmLVr.mjs.map} +1 -1
- package/dist/{query-Bt52mHXp.mjs → query-Ctlq1aOk.mjs} +10 -10
- package/dist/{query-Bt52mHXp.mjs.map → query-Ctlq1aOk.mjs.map} +1 -1
- package/dist/{rate-limit-D6VQqBk_.mjs → rate-limit-CH6W6ikK.mjs} +2 -2
- package/dist/{rate-limit-D6VQqBk_.mjs.map → rate-limit-CH6W6ikK.mjs.map} +1 -1
- package/dist/{redirect-BZUJltlj.mjs → redirect-C6tJA7tk.mjs} +3 -3
- package/dist/{redirect-BZUJltlj.mjs.map → redirect-C6tJA7tk.mjs.map} +1 -1
- package/dist/{redirects-DnYuqsEf.mjs → redirects-CacE9eQa.mjs} +3 -3
- package/dist/{redirects-DnYuqsEf.mjs.map → redirects-CacE9eQa.mjs.map} +1 -1
- package/dist/{registry-Dn6gsx3L.mjs → registry-CIDxZbhh.mjs} +5 -5
- package/dist/{registry-Dn6gsx3L.mjs.map → registry-CIDxZbhh.mjs.map} +1 -1
- package/dist/runner-DM1yR5qd.d.mts.map +1 -1
- package/dist/{runner-eAgyIkeg.mjs → runner-pt6Wl-l-.mjs} +11 -6
- package/dist/runner-pt6Wl-l-.mjs.map +1 -0
- package/dist/runtime.mjs +3 -3
- package/dist/{schema--mYZX4D7.mjs → schema-B4tk0HAG.mjs} +4 -4
- package/dist/{schema--mYZX4D7.mjs.map → schema-B4tk0HAG.mjs.map} +1 -1
- package/dist/{search-C6U_NvZI.mjs → search-f-fNfwab.mjs} +4 -4
- package/dist/{search-C6U_NvZI.mjs.map → search-f-fNfwab.mjs.map} +1 -1
- package/dist/{sections-Ba-rJLKb.mjs → sections-biElLfT9.mjs} +3 -3
- package/dist/{sections-Ba-rJLKb.mjs.map → sections-biElLfT9.mjs.map} +1 -1
- package/dist/seed/index.mjs +14 -14
- package/dist/seo/index.mjs +1 -0
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-BTzb5ksq.mjs → seo-BR39kvTF.mjs} +2 -2
- package/dist/{seo-BTzb5ksq.mjs.map → seo-BR39kvTF.mjs.map} +1 -1
- package/dist/{service-Cn-kIfZn.mjs → service-BhR2acnc.mjs} +2 -2
- package/dist/{service-Cn-kIfZn.mjs.map → service-BhR2acnc.mjs.map} +1 -1
- package/dist/{settings-C65OSm41.mjs → settings-D_NJvjgN.mjs} +3 -3
- package/dist/{settings-C65OSm41.mjs.map → settings-D_NJvjgN.mjs.map} +1 -1
- package/dist/{settings-ChlQbwU0.mjs → settings-b5zW1R1T.mjs} +3 -3
- package/dist/{settings-ChlQbwU0.mjs.map → settings-b5zW1R1T.mjs.map} +1 -1
- package/dist/{taxonomies-ByLlXrv5.mjs → taxonomies-Crtzy4MT.mjs} +8 -7
- package/dist/taxonomies-Crtzy4MT.mjs.map +1 -0
- package/dist/{taxonomies-CbO6v7EE.mjs → taxonomies-Mhn9rjTQ.mjs} +4 -4
- package/dist/{taxonomies-CbO6v7EE.mjs.map → taxonomies-Mhn9rjTQ.mjs.map} +1 -1
- package/dist/{taxonomy-BBK-UAEo.mjs → taxonomy-DTZrIQpi.mjs} +3 -3
- package/dist/{taxonomy-BBK-UAEo.mjs.map → taxonomy-DTZrIQpi.mjs.map} +1 -1
- package/dist/{types-SF1DwGf2.mjs → types-K3MDsxpy.mjs} +2 -2
- package/dist/{types-SF1DwGf2.mjs.map → types-K3MDsxpy.mjs.map} +1 -1
- package/dist/{user-X4rtyO4Y.mjs → user-DzEUl5zA.mjs} +2 -2
- package/dist/{user-X4rtyO4Y.mjs.map → user-DzEUl5zA.mjs.map} +1 -1
- package/dist/{validate-DactmcJG.mjs → validate-JCXcsqiY.mjs} +2 -2
- package/dist/{validate-DactmcJG.mjs.map → validate-JCXcsqiY.mjs.map} +1 -1
- package/dist/{validation-BYA4i85b.mjs → validation-Bq-VyKJg.mjs} +6 -6
- package/dist/{validation-BYA4i85b.mjs.map → validation-Bq-VyKJg.mjs.map} +1 -1
- package/dist/version-CnS-Cr8A.mjs +7 -0
- package/dist/{version-CWbvq9LG.mjs.map → version-CnS-Cr8A.mjs.map} +1 -1
- package/dist/{widgets-DG-1jxnz.mjs → widgets-Bap1eS1X.mjs} +2 -2
- package/dist/{widgets-DG-1jxnz.mjs.map → widgets-Bap1eS1X.mjs.map} +1 -1
- package/dist/{zod-generator-BNAObjSt.mjs → zod-generator-BSDpkqSH.mjs} +4 -3
- package/dist/zod-generator-BSDpkqSH.mjs.map +1 -0
- package/package.json +7 -7
- package/src/astro/middleware/stream-end-metrics.ts +96 -0
- package/src/astro/middleware.ts +114 -40
- package/src/components/EmDashImage.astro +1 -0
- package/src/database/dialect-helpers.ts +8 -2
- package/src/database/migrations/019_i18n.ts +2 -2
- package/src/database/migrations/runner.ts +7 -2
- package/src/emdash-runtime.ts +177 -126
- package/src/menus/index.ts +27 -9
- package/src/plugins/hooks.ts +35 -6
- package/src/plugins/manager.ts +1 -0
- package/src/schema/zod-generator.ts +6 -2
- package/src/seo/index.ts +10 -1
- package/src/taxonomies/index.ts +12 -8
- package/src/utils/init-lock.ts +143 -0
- package/dist/dialect-helpers-BKCvISIQ.mjs.map +0 -1
- package/dist/menus-CyMO6GBx.mjs.map +0 -1
- package/dist/runner-eAgyIkeg.mjs.map +0 -1
- package/dist/taxonomies-ByLlXrv5.mjs.map +0 -1
- package/dist/version-CWbvq9LG.mjs +0 -7
- package/dist/zod-generator-BNAObjSt.mjs.map +0 -1
|
@@ -1,50 +1,51 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as runMigrations, t as MIGRATION_RACE_WAIT_MS } from "../runner-pt6Wl-l-.mjs";
|
|
2
2
|
import { t as validateIdentifier } from "../validate-VPnKoIzW.mjs";
|
|
3
|
-
import { o as isSqlite } from "../dialect-helpers-
|
|
3
|
+
import { o as isSqlite } from "../dialect-helpers-DRI5pyY3.mjs";
|
|
4
4
|
import { i as setI18nConfig } from "../config-CVssduLe.mjs";
|
|
5
|
-
import { _ as definePlugin, f as PluginRouteRegistry, g as resolveExclusiveHooks, h as createHookPipeline, p as EmailPipeline, v as after } from "../menus-
|
|
6
|
-
import { r as RevisionRepository } from "../content-
|
|
5
|
+
import { _ as definePlugin, f as PluginRouteRegistry, g as resolveExclusiveHooks, h as createHookPipeline, p as EmailPipeline, v as after } from "../menus-BKkxXCmd.mjs";
|
|
6
|
+
import { r as RevisionRepository } from "../content-CyqOmOzm.mjs";
|
|
7
7
|
import "../base64-CqR-7kqF.mjs";
|
|
8
|
-
import "../types-
|
|
9
|
-
import { t as MediaRepository } from "../media-
|
|
10
|
-
import "../user-
|
|
11
|
-
import "../taxonomy-
|
|
12
|
-
import "../comment-
|
|
8
|
+
import "../types-K3MDsxpy.mjs";
|
|
9
|
+
import { t as MediaRepository } from "../media-C-oovGCG.mjs";
|
|
10
|
+
import "../user-DzEUl5zA.mjs";
|
|
11
|
+
import "../taxonomy-DTZrIQpi.mjs";
|
|
12
|
+
import "../comment-C4jVbCM8.mjs";
|
|
13
13
|
import { t as OptionsRepository } from "../options-BL4X94qY.mjs";
|
|
14
|
-
import "../context-
|
|
15
|
-
import "../menus-
|
|
16
|
-
import "../redirect-
|
|
14
|
+
import "../context-DZ7bEh5-.mjs";
|
|
15
|
+
import "../menus-DugoYwTX.mjs";
|
|
16
|
+
import "../redirect-C6tJA7tk.mjs";
|
|
17
17
|
import { createRequestMetrics, getRequestContext, runWithContext } from "../request-context.mjs";
|
|
18
18
|
import { r as requestCached } from "../request-cache-BYMs-BGX.mjs";
|
|
19
19
|
import "../byline-registry-CxK5g559.mjs";
|
|
20
|
-
import "../byline-
|
|
20
|
+
import "../byline-CWQ9aSoz.mjs";
|
|
21
21
|
import { t as normalizeMediaValue } from "../normalize-DVV8nbrL.mjs";
|
|
22
22
|
import "../placeholder-BZxr8W1j.mjs";
|
|
23
|
-
import "../seo-
|
|
24
|
-
import {
|
|
25
|
-
import "../
|
|
23
|
+
import "../seo-BR39kvTF.mjs";
|
|
24
|
+
import { n as isMissingTableError } from "../db-errors-CtzxKBxe.mjs";
|
|
25
|
+
import { $ as handleContentGetIncludingTrashed, B as handleMediaUpdate, G as handleContentCompare, H as handleRevisionList, I as handleMediaCreate, J as handleContentCreate, K as handleContentCountScheduled, L as handleMediaDelete, Q as handleContentGet, R as handleMediaGet, S as PluginStateRepository, U as handleRevisionRestore, V as handleRevisionGet, X as handleContentDiscardDraft, Y as handleContentDelete, Z as handleContentDuplicate, _ as loadBundleFromR2, at as handleContentSchedule, ct as handleContentUnschedule, et as handleContentList, it as handleContentRestore, lt as handleContentUpdate, nt as handleContentPermanentDelete, ot as handleContentTranslations, q as handleContentCountTrashed, rt as handleContentPublish, s as normalizeRegistryConfig, st as handleContentUnpublish, tt as handleContentListTrashed, ut as validateRev, z as handleMediaList } from "../api-Cs7DAACP.mjs";
|
|
26
|
+
import "../dashboard-B5WQpNTP.mjs";
|
|
26
27
|
import { n as hashString } from "../hash-9w3pd3-m.mjs";
|
|
27
|
-
import { t as FTSManager } from "../fts-manager-
|
|
28
|
-
import { n as SchemaRegistry } from "../registry-
|
|
28
|
+
import { t as FTSManager } from "../fts-manager-DR1ERA0c.mjs";
|
|
29
|
+
import { n as SchemaRegistry } from "../registry-CIDxZbhh.mjs";
|
|
29
30
|
import { createRecorder, flushRecorder, isInstrumentationEnabled, kyselyLogOption } from "../database/instrumentation.mjs";
|
|
30
|
-
import { r as getDb } from "../loader-
|
|
31
|
-
import "../schema
|
|
32
|
-
import "../zod-generator-
|
|
31
|
+
import { r as getDb } from "../loader-Dyx8dhFV.mjs";
|
|
32
|
+
import "../schema-B4tk0HAG.mjs";
|
|
33
|
+
import "../zod-generator-BSDpkqSH.mjs";
|
|
33
34
|
import "../seo-DfjLvu8i.mjs";
|
|
34
|
-
import "../sections-
|
|
35
|
-
import { o as invalidateSiteSettingsCache } from "../settings-
|
|
36
|
-
import "../settings-
|
|
35
|
+
import "../sections-biElLfT9.mjs";
|
|
36
|
+
import { o as invalidateSiteSettingsCache } from "../settings-b5zW1R1T.mjs";
|
|
37
|
+
import "../settings-D_NJvjgN.mjs";
|
|
37
38
|
import "../resolve-BqYMVG0D.mjs";
|
|
38
|
-
import "../taxonomies-
|
|
39
|
-
import "../taxonomies-
|
|
39
|
+
import "../taxonomies-Crtzy4MT.mjs";
|
|
40
|
+
import "../taxonomies-Mhn9rjTQ.mjs";
|
|
40
41
|
import { r as normalizeManifestRoute } from "../manifest-schema-Cj-YrzrF.mjs";
|
|
41
42
|
import "../types-Cj2S6FuC.mjs";
|
|
42
43
|
import "../ssrf-BsVGIE0Z.mjs";
|
|
43
|
-
import "../error-
|
|
44
|
-
import "../parse-
|
|
44
|
+
import "../error-DJOsMVSt.mjs";
|
|
45
|
+
import "../parse-BBkFmLVr.mjs";
|
|
45
46
|
import "../redirects-C0L9JUk4.mjs";
|
|
46
47
|
import "../byline-fields-Dr-xcb6S.mjs";
|
|
47
|
-
import { a as invalidateUrlPatternCache } from "../query-
|
|
48
|
+
import { a as invalidateUrlPatternCache } from "../query-Ctlq1aOk.mjs";
|
|
48
49
|
import "../import-Dh8bWmyq.mjs";
|
|
49
50
|
import { t as getTrustedProxyHeaders } from "../trusted-proxy-B4AfnoAp.mjs";
|
|
50
51
|
import { n as sanitizeHeadersForSandbox, t as extractRequestMeta } from "../request-meta-7ByVLxB-.mjs";
|
|
@@ -54,13 +55,13 @@ import { r as devConsoleEmailDeliver, t as DEV_CONSOLE_EMAIL_PLUGIN_ID } from ".
|
|
|
54
55
|
import "../utils-C4Ih4DML.mjs";
|
|
55
56
|
import "../tokens-Bx2afeT-.mjs";
|
|
56
57
|
import "../preview-BfuRkVKW.mjs";
|
|
57
|
-
import "../bylines-
|
|
58
|
-
import "../widgets-
|
|
59
|
-
import "../apply-
|
|
60
|
-
import "../load-
|
|
61
|
-
import "../search-
|
|
58
|
+
import "../bylines-BJSva1Un.mjs";
|
|
59
|
+
import "../widgets-Bap1eS1X.mjs";
|
|
60
|
+
import "../apply-BWMV4Zmw.mjs";
|
|
61
|
+
import "../load-6ZrRhepW.mjs";
|
|
62
|
+
import "../search-f-fNfwab.mjs";
|
|
62
63
|
import "../index.mjs";
|
|
63
|
-
import { n as VERSION, t as COMMIT } from "../version-
|
|
64
|
+
import { n as VERSION, t as COMMIT } from "../version-CnS-Cr8A.mjs";
|
|
64
65
|
import { t as getAuthMode } from "../mode-BjlXswIw.mjs";
|
|
65
66
|
import { t as cleanupExpiredChallenges } from "../challenge-store-DGwuCc4R.mjs";
|
|
66
67
|
import { a as validateEncryptionKeyAtStartup } from "../secrets-YYbTgB1w.mjs";
|
|
@@ -75,6 +76,60 @@ import { sandboxedPlugins } from "virtual:emdash/sandboxed-plugins";
|
|
|
75
76
|
import { createStorage } from "virtual:emdash/storage";
|
|
76
77
|
import { createKyselyAdapter } from "@emdash-cms/auth/adapters/kysely";
|
|
77
78
|
|
|
79
|
+
//#region src/utils/init-lock.ts
|
|
80
|
+
function createInitLock() {
|
|
81
|
+
return {
|
|
82
|
+
ownerStartedAt: null,
|
|
83
|
+
generation: 0
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const DEFAULT_DEADLINE_MS = 15e3;
|
|
87
|
+
const DEFAULT_POLL_MS = 50;
|
|
88
|
+
const MAX_WAIT_HEADROOM_MS = 15e3;
|
|
89
|
+
function sleep(ms) {
|
|
90
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Return the cached value if present, otherwise initialize it under the
|
|
94
|
+
* lock. `init` is responsible for storing the value so that `getCached`
|
|
95
|
+
* returns it on subsequent calls — waiters re-check `getCached` after the
|
|
96
|
+
* owner finishes rather than sharing the owner's promise.
|
|
97
|
+
*
|
|
98
|
+
* `init` receives an `isCurrentClaim` predicate and must gate its cache
|
|
99
|
+
* publication on it: a slow init that was reclaimed past the deadline
|
|
100
|
+
* must not overwrite the value published by the reclaimer (for the
|
|
101
|
+
* runtime singleton that would orphan the reclaimer's active cron
|
|
102
|
+
* scheduler). A losing init should also tear down any side resources it
|
|
103
|
+
* started, since its result will never be published.
|
|
104
|
+
*/
|
|
105
|
+
async function initWithLock(lock, getCached, init, options) {
|
|
106
|
+
const deadlineMs = options?.deadlineMs ?? DEFAULT_DEADLINE_MS;
|
|
107
|
+
const pollMs = options?.pollMs ?? DEFAULT_POLL_MS;
|
|
108
|
+
const maxWaitMs = options?.maxWaitMs ?? deadlineMs + MAX_WAIT_HEADROOM_MS;
|
|
109
|
+
const waitStart = Date.now();
|
|
110
|
+
for (;;) {
|
|
111
|
+
const cached = getCached();
|
|
112
|
+
if (cached !== null && cached !== void 0) return cached;
|
|
113
|
+
const ownerStartedAt = lock.ownerStartedAt;
|
|
114
|
+
if (ownerStartedAt === null || Date.now() - ownerStartedAt > deadlineMs) {
|
|
115
|
+
lock.generation += 1;
|
|
116
|
+
const claim = lock.generation;
|
|
117
|
+
lock.ownerStartedAt = Date.now();
|
|
118
|
+
try {
|
|
119
|
+
const isCurrentClaim = () => lock.generation === claim;
|
|
120
|
+
const initPromise = Promise.resolve().then(() => init(isCurrentClaim));
|
|
121
|
+
options?.anchor?.(initPromise.then(() => void 0, () => void 0));
|
|
122
|
+
return await initPromise;
|
|
123
|
+
} finally {
|
|
124
|
+
if (lock.generation === claim) lock.ownerStartedAt = null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (Date.now() - waitStart > maxWaitMs) throw new Error(`initWithLock: timed out after ${maxWaitMs}ms waiting for initialization`);
|
|
128
|
+
await sleep(pollMs);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
78
133
|
//#region src/cleanup.ts
|
|
79
134
|
/**
|
|
80
135
|
* System cleanup
|
|
@@ -380,8 +435,35 @@ const FIELD_TYPE_TO_KIND = {
|
|
|
380
435
|
function contentItemToRecord(item) {
|
|
381
436
|
return { ...item };
|
|
382
437
|
}
|
|
383
|
-
|
|
384
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Db init lock reclaim deadline. Derived from the migration race wait so
|
|
440
|
+
* they can't drift apart: a healthy init can legitimately block for the
|
|
441
|
+
* full MIGRATION_RACE_WAIT_MS inside waitForConcurrentMigrator, plus cold
|
|
442
|
+
* connect and migrator work, before it should be presumed dead. The outer
|
|
443
|
+
* runtime init lock (middleware.ts) must use a strictly larger deadline —
|
|
444
|
+
* it wraps create() → getDatabase() → this lock, and equal deadlines would
|
|
445
|
+
* let the outer reclaim while the inner is legitimately still working.
|
|
446
|
+
*/
|
|
447
|
+
const DB_INIT_DEADLINE_MS = MIGRATION_RACE_WAIT_MS + 2e4;
|
|
448
|
+
/**
|
|
449
|
+
* Db cache + its init lock live on globalThis behind a Symbol: the bundler
|
|
450
|
+
* can duplicate this module across SSR chunks (same reasoning as
|
|
451
|
+
* request-cache.ts), and a duplicated cache/lock would mean concurrent
|
|
452
|
+
* independent db inits — and duplicate migrators — per isolate.
|
|
453
|
+
*/
|
|
454
|
+
const DB_HOLDER_KEY = Symbol.for("emdash:db-cache");
|
|
455
|
+
const globalSymbolStore = globalThis;
|
|
456
|
+
function getDbHolder() {
|
|
457
|
+
let holder = globalSymbolStore[DB_HOLDER_KEY];
|
|
458
|
+
if (!holder) {
|
|
459
|
+
holder = {
|
|
460
|
+
cache: /* @__PURE__ */ new Map(),
|
|
461
|
+
lock: createInitLock()
|
|
462
|
+
};
|
|
463
|
+
globalSymbolStore[DB_HOLDER_KEY] = holder;
|
|
464
|
+
}
|
|
465
|
+
return holder;
|
|
466
|
+
}
|
|
385
467
|
const storageCache = /* @__PURE__ */ new Map();
|
|
386
468
|
const sandboxedPluginCache = /* @__PURE__ */ new Map();
|
|
387
469
|
/**
|
|
@@ -794,19 +876,13 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
794
876
|
await phase("rt.secrets", "Validate encryption key", () => validateEncryptionKeyAtStartup());
|
|
795
877
|
const storage = EmDashRuntime.getStorage(deps);
|
|
796
878
|
let pluginStates = /* @__PURE__ */ new Map();
|
|
797
|
-
|
|
879
|
+
let siteInfo;
|
|
880
|
+
await Promise.all([phase("rt.plugins", "Plugin states", async () => {
|
|
798
881
|
try {
|
|
799
882
|
const states = await db.selectFrom("_plugin_state").select(["plugin_id", "status"]).execute();
|
|
800
883
|
pluginStates = new Map(states.map((s) => [s.plugin_id, s.status]));
|
|
801
884
|
} catch {}
|
|
802
|
-
})
|
|
803
|
-
const enabledPlugins = /* @__PURE__ */ new Set();
|
|
804
|
-
for (const plugin of deps.plugins) {
|
|
805
|
-
const status = pluginStates.get(plugin.id);
|
|
806
|
-
if (status === void 0 || status === "active") enabledPlugins.add(plugin.id);
|
|
807
|
-
}
|
|
808
|
-
let siteInfo;
|
|
809
|
-
await phase("rt.site", "Site info options", async () => {
|
|
885
|
+
}), phase("rt.site", "Site info options", async () => {
|
|
810
886
|
try {
|
|
811
887
|
const siteOpts = await new OptionsRepository(db).getMany([
|
|
812
888
|
"emdash:site_title",
|
|
@@ -819,7 +895,12 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
819
895
|
locale: siteOpts.get("emdash:locale") ?? void 0
|
|
820
896
|
};
|
|
821
897
|
} catch {}
|
|
822
|
-
});
|
|
898
|
+
})]);
|
|
899
|
+
const enabledPlugins = /* @__PURE__ */ new Set();
|
|
900
|
+
for (const plugin of deps.plugins) {
|
|
901
|
+
const status = pluginStates.get(plugin.id);
|
|
902
|
+
if (status === void 0 || status === "active") enabledPlugins.add(plugin.id);
|
|
903
|
+
}
|
|
823
904
|
const allPipelinePlugins = [...deps.plugins];
|
|
824
905
|
const bypassedPluginsList = [];
|
|
825
906
|
if (import.meta.env.DEV) try {
|
|
@@ -880,8 +961,10 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
880
961
|
};
|
|
881
962
|
const pipeline = createHookPipeline(enabledPluginList, pipelineFactoryOptions);
|
|
882
963
|
const sandboxedPlugins = await phase("rt.sandbox", "Sandboxed plugins", () => EmDashRuntime.loadSandboxedPlugins(deps, db, storage));
|
|
883
|
-
|
|
884
|
-
if (deps.config.
|
|
964
|
+
const installedTierPhases = [];
|
|
965
|
+
if (deps.config.marketplace && storage && !deps.sandboxBypassed) installedTierPhases.push(phase("rt.market", "Marketplace plugins", () => EmDashRuntime.loadInstalledSandboxedPlugins("marketplace", db, storage, deps, sandboxedPlugins)));
|
|
966
|
+
if (deps.config.experimental?.registry && storage) installedTierPhases.push(phase("rt.registry", "Registry plugins", () => EmDashRuntime.loadInstalledSandboxedPlugins("registry", db, storage, deps, sandboxedPlugins)));
|
|
967
|
+
if (installedTierPhases.length > 0) await Promise.all(installedTierPhases);
|
|
885
968
|
const mediaProviders = /* @__PURE__ */ new Map();
|
|
886
969
|
const mediaProviderEntries = deps.mediaProviderEntries ?? [];
|
|
887
970
|
const providerContext = {
|
|
@@ -986,10 +1069,8 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
986
1069
|
throw new Error("EmDash database not configured. Either configure database in astro.config.mjs or use emdashLoader in live.config.ts");
|
|
987
1070
|
}
|
|
988
1071
|
const cacheKey = dbConfig.entrypoint;
|
|
989
|
-
const
|
|
990
|
-
|
|
991
|
-
if (dbInitPromise) return dbInitPromise;
|
|
992
|
-
dbInitPromise = (async () => {
|
|
1072
|
+
const holder = getDbHolder();
|
|
1073
|
+
return initWithLock(holder.lock, () => holder.cache.get(cacheKey), async (isCurrentClaim) => {
|
|
993
1074
|
const db = new Kysely({
|
|
994
1075
|
dialect: deps.createDialect(dbConfig.config),
|
|
995
1076
|
log: kyselyLogOption()
|
|
@@ -1005,9 +1086,9 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1005
1086
|
}
|
|
1006
1087
|
})();
|
|
1007
1088
|
if (collectionCount.count === 0 && !setupDone) {
|
|
1008
|
-
const { applySeed } = await import("../apply-
|
|
1009
|
-
const { loadSeed } = await import("../load-
|
|
1010
|
-
const { validateSeed } = await import("../validate-
|
|
1089
|
+
const { applySeed } = await import("../apply-BWMV4Zmw.mjs").then((n) => n.n);
|
|
1090
|
+
const { loadSeed } = await import("../load-6ZrRhepW.mjs").then((n) => n.r);
|
|
1091
|
+
const { validateSeed } = await import("../validate-JCXcsqiY.mjs").then((n) => n.n);
|
|
1011
1092
|
const seed = await loadSeed();
|
|
1012
1093
|
if (validateSeed(seed).valid) {
|
|
1013
1094
|
await applySeed(db, seed, { onConflict: "skip" });
|
|
@@ -1015,14 +1096,12 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1015
1096
|
}
|
|
1016
1097
|
}
|
|
1017
1098
|
} catch {}
|
|
1018
|
-
|
|
1099
|
+
if (isCurrentClaim()) holder.cache.set(cacheKey, db);
|
|
1019
1100
|
return db;
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
}
|
|
1024
|
-
dbInitPromise = null;
|
|
1025
|
-
}
|
|
1101
|
+
}, {
|
|
1102
|
+
deadlineMs: DB_INIT_DEADLINE_MS,
|
|
1103
|
+
anchor: (promise) => after(() => promise)
|
|
1104
|
+
});
|
|
1026
1105
|
}
|
|
1027
1106
|
/**
|
|
1028
1107
|
* Get or create storage instance
|
|
@@ -1281,6 +1360,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1281
1360
|
pipeline,
|
|
1282
1361
|
isActive: () => true,
|
|
1283
1362
|
getOption: (key) => optionsRepo.get(key),
|
|
1363
|
+
getOptions: (keys) => optionsRepo.getMany(keys),
|
|
1284
1364
|
setOption: (key, value) => optionsRepo.set(key, value),
|
|
1285
1365
|
deleteOption: async (key) => {
|
|
1286
1366
|
await optionsRepo.delete(key);
|
|
@@ -1539,7 +1619,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1539
1619
|
if (this.hooks.hasHooks("content:beforeSave")) processedData = (await this.hooks.runContentBeforeSave(body.data, collection, true)).content;
|
|
1540
1620
|
processedData = await this.runSandboxedBeforeSave(processedData, collection, true);
|
|
1541
1621
|
processedData = await this.normalizeMediaFields(collection, processedData);
|
|
1542
|
-
const { validateContentData } = await import("../validation-
|
|
1622
|
+
const { validateContentData } = await import("../validation-Bq-VyKJg.mjs");
|
|
1543
1623
|
const validation = await validateContentData(this.db, collection, processedData, { partial: false });
|
|
1544
1624
|
if (!validation.ok) return {
|
|
1545
1625
|
success: false,
|
|
@@ -1555,7 +1635,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1555
1635
|
return result;
|
|
1556
1636
|
}
|
|
1557
1637
|
async handleContentUpdate(collection, id, body) {
|
|
1558
|
-
const { ContentRepository } = await import("../content-
|
|
1638
|
+
const { ContentRepository } = await import("../content-CyqOmOzm.mjs").then((n) => n.n);
|
|
1559
1639
|
const repo = new ContentRepository(this.db);
|
|
1560
1640
|
const resolvedItem = await repo.findByIdOrSlug(collection, id, body.locale);
|
|
1561
1641
|
const resolvedId = resolvedItem?.id ?? id;
|
|
@@ -1582,7 +1662,7 @@ var EmDashRuntime = class EmDashRuntime {
|
|
|
1582
1662
|
if (this.hooks.hasHooks("content:beforeSave")) processedData = (await this.hooks.runContentBeforeSave(bodyWithoutRev.data, collection, false)).content;
|
|
1583
1663
|
processedData = await this.runSandboxedBeforeSave(processedData, collection, false);
|
|
1584
1664
|
processedData = await this.normalizeMediaFields(collection, processedData);
|
|
1585
|
-
const { validateContentData } = await import("../validation-
|
|
1665
|
+
const { validateContentData } = await import("../validation-Bq-VyKJg.mjs");
|
|
1586
1666
|
const validation = await validateContentData(this.db, collection, processedData, { partial: true });
|
|
1587
1667
|
if (!validation.ok) return {
|
|
1588
1668
|
success: false,
|
|
@@ -2122,6 +2202,70 @@ function createPublicMediaUrlResolver(storage) {
|
|
|
2122
2202
|
return (key) => resolvePublicMediaUrl(storage, key);
|
|
2123
2203
|
}
|
|
2124
2204
|
|
|
2205
|
+
//#endregion
|
|
2206
|
+
//#region src/astro/middleware/stream-end-metrics.ts
|
|
2207
|
+
/**
|
|
2208
|
+
* Stream-end metrics
|
|
2209
|
+
*
|
|
2210
|
+
* Server-Timing db.* counters are snapshotted when middleware's next()
|
|
2211
|
+
* returns — but at that point only the response *headers* are final.
|
|
2212
|
+
* Astro streams the body afterwards, and components rendered during
|
|
2213
|
+
* streaming issue further DB queries that the headers can never report.
|
|
2214
|
+
*
|
|
2215
|
+
* This module wraps the response body in an identity TransformStream and
|
|
2216
|
+
* snapshots the request metrics in flush(), i.e. when the body actually
|
|
2217
|
+
* finishes streaming. The metrics object lives on the request context
|
|
2218
|
+
* (AsyncLocalStorage) and is mutated in-place by the Kysely log hook, so
|
|
2219
|
+
* a reference captured before wrapping observes every post-header query.
|
|
2220
|
+
* The snapshot is emitted as prefixed NDJSON on stdout (same transport as
|
|
2221
|
+
* [emdash-query-log] — console.log works in both Node and workerd).
|
|
2222
|
+
*
|
|
2223
|
+
* Gated on isInstrumentationEnabled() (EMDASH_QUERY_LOG=1): zero overhead
|
|
2224
|
+
* in normal production traffic.
|
|
2225
|
+
*/
|
|
2226
|
+
const STREAM_END_PREFIX = "[emdash-stream-end]";
|
|
2227
|
+
/**
|
|
2228
|
+
* Astro attaches AstroCookies to outgoing responses via a well-known global
|
|
2229
|
+
* symbol. Constructing a new Response drops non-header metadata, so the
|
|
2230
|
+
* symbol must be forwarded explicitly or `cookies.set()` calls are silently
|
|
2231
|
+
* dropped. Same pattern as finalizeResponse in ../middleware.ts.
|
|
2232
|
+
*/
|
|
2233
|
+
const ASTRO_COOKIES_SYMBOL$1 = Symbol.for("astro.cookies");
|
|
2234
|
+
/**
|
|
2235
|
+
* Wrap a response body so the FINAL request metrics are emitted when the
|
|
2236
|
+
* body finishes streaming. Returns the response unchanged when
|
|
2237
|
+
* instrumentation is disabled, the body is null, or no request metrics
|
|
2238
|
+
* are attached (e.g. outside the middleware's ALS context).
|
|
2239
|
+
*/
|
|
2240
|
+
function wrapBodyForStreamMetrics(response) {
|
|
2241
|
+
if (!isInstrumentationEnabled()) return response;
|
|
2242
|
+
if (!response.body) return response;
|
|
2243
|
+
const ctx = getRequestContext();
|
|
2244
|
+
const metrics = ctx?.metrics;
|
|
2245
|
+
if (!metrics) return response;
|
|
2246
|
+
const recorder = ctx?.queryRecorder;
|
|
2247
|
+
const transform = new TransformStream({ flush() {
|
|
2248
|
+
const snapshot = {
|
|
2249
|
+
route: recorder?.route,
|
|
2250
|
+
method: recorder?.method,
|
|
2251
|
+
phase: recorder?.phase,
|
|
2252
|
+
totalMs: performance.now() - metrics.start,
|
|
2253
|
+
dbCount: metrics.dbCount,
|
|
2254
|
+
dbTotalMs: metrics.dbTotalMs,
|
|
2255
|
+
dbFirstOffset: metrics.dbFirstOffset,
|
|
2256
|
+
dbLastOffset: metrics.dbLastOffset,
|
|
2257
|
+
cacheHits: metrics.cacheHits,
|
|
2258
|
+
cacheMisses: metrics.cacheMisses
|
|
2259
|
+
};
|
|
2260
|
+
console.log(`${STREAM_END_PREFIX} ${JSON.stringify(snapshot)}`);
|
|
2261
|
+
} });
|
|
2262
|
+
const wrapped = new Response(response.body.pipeThrough(transform), response);
|
|
2263
|
+
const astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL$1);
|
|
2264
|
+
if (astroCookies !== void 0) Reflect.set(wrapped, ASTRO_COOKIES_SYMBOL$1, astroCookies);
|
|
2265
|
+
wrapped.headers.delete("Content-Length");
|
|
2266
|
+
return wrapped;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2125
2269
|
//#endregion
|
|
2126
2270
|
//#region src/astro/public-plugin-api-routes.ts
|
|
2127
2271
|
function pluginRouteNotFound() {
|
|
@@ -2148,10 +2292,14 @@ function createPublicPluginApiRouteHandler(runtime) {
|
|
|
2148
2292
|
* Thin wrapper that initializes EmDashRuntime and attaches it to locals.
|
|
2149
2293
|
* All heavy lifting happens in EmDashRuntime.
|
|
2150
2294
|
*/
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
let
|
|
2295
|
+
/**
|
|
2296
|
+
* Runtime init lock reclaim deadline. Must be strictly larger than the db
|
|
2297
|
+
* init deadline: this lock wraps EmDashRuntime.create() → getDatabase() →
|
|
2298
|
+
* the db init lock, and equal deadlines would let this outer lock reclaim
|
|
2299
|
+
* (spawning a second cron scheduler and sandbox runner) while the inner db
|
|
2300
|
+
* init is legitimately still working through a contended migration.
|
|
2301
|
+
*/
|
|
2302
|
+
const RUNTIME_INIT_DEADLINE_MS = DB_INIT_DEADLINE_MS + 15e3;
|
|
2155
2303
|
/**
|
|
2156
2304
|
* Whether we've verified the database has been set up.
|
|
2157
2305
|
* On a fresh deployment the first request may hit a public page, bypassing
|
|
@@ -2159,8 +2307,43 @@ let i18nInitialized = false;
|
|
|
2159
2307
|
* would query an empty database and crash. Once verified (or once the runtime
|
|
2160
2308
|
* has initialized via an admin/API request), this stays true for the worker's
|
|
2161
2309
|
* lifetime.
|
|
2310
|
+
*
|
|
2311
|
+
* Stored on globalThis behind a Symbol key so the flag is a true singleton
|
|
2312
|
+
* even when the bundler duplicates this module across SSR chunks (same
|
|
2313
|
+
* pattern as request-cache.ts). A plain module-scoped `let` becomes multiple
|
|
2314
|
+
* independent variables, which would make the setup probe re-run far more
|
|
2315
|
+
* often than intended — and every re-run is another chance for a transient
|
|
2316
|
+
* DB error to be misread as "fresh install" and bounce visitors to setup.
|
|
2162
2317
|
*/
|
|
2163
|
-
|
|
2318
|
+
const SETUP_VERIFIED_KEY = Symbol.for("emdash:setup-verified");
|
|
2319
|
+
const setupFlagStore = globalThis;
|
|
2320
|
+
function isSetupVerified() {
|
|
2321
|
+
return setupFlagStore[SETUP_VERIFIED_KEY] === true;
|
|
2322
|
+
}
|
|
2323
|
+
function markSetupVerified() {
|
|
2324
|
+
setupFlagStore[SETUP_VERIFIED_KEY] = true;
|
|
2325
|
+
}
|
|
2326
|
+
/**
|
|
2327
|
+
* The runtime singleton and its init lock live on globalThis behind a
|
|
2328
|
+
* Symbol — same reasoning as SETUP_VERIFIED_KEY above: the bundler can
|
|
2329
|
+
* duplicate this module across SSR chunks, and a duplicated instance/lock
|
|
2330
|
+
* would mean multiple runtimes (each with its own cron scheduler) per
|
|
2331
|
+
* isolate, initializing and reclaiming independently.
|
|
2332
|
+
*/
|
|
2333
|
+
const RUNTIME_HOLDER_KEY = Symbol.for("emdash:runtime-holder");
|
|
2334
|
+
function getRuntimeHolder() {
|
|
2335
|
+
let holder = setupFlagStore[RUNTIME_HOLDER_KEY];
|
|
2336
|
+
if (!holder) {
|
|
2337
|
+
holder = {
|
|
2338
|
+
instance: null,
|
|
2339
|
+
lock: createInitLock()
|
|
2340
|
+
};
|
|
2341
|
+
setupFlagStore[RUNTIME_HOLDER_KEY] = holder;
|
|
2342
|
+
}
|
|
2343
|
+
return holder;
|
|
2344
|
+
}
|
|
2345
|
+
/** Whether i18n config has been initialized from the virtual module */
|
|
2346
|
+
let i18nInitialized = false;
|
|
2164
2347
|
/**
|
|
2165
2348
|
* Get EmDash configuration from virtual module
|
|
2166
2349
|
*/
|
|
@@ -2208,20 +2391,19 @@ function buildDependencies(config) {
|
|
|
2208
2391
|
* as "warm, nothing to report".
|
|
2209
2392
|
*/
|
|
2210
2393
|
async function getRuntime(config, initTimings) {
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
2214
|
-
return getRuntime(config, initTimings);
|
|
2215
|
-
}
|
|
2216
|
-
runtimeInitializing = true;
|
|
2217
|
-
try {
|
|
2394
|
+
const holder = getRuntimeHolder();
|
|
2395
|
+
return initWithLock(holder.lock, () => holder.instance, async (isCurrentClaim) => {
|
|
2218
2396
|
const deps = buildDependencies(config);
|
|
2219
2397
|
const runtime = await EmDashRuntime.create(deps, initTimings);
|
|
2220
|
-
|
|
2398
|
+
if (isCurrentClaim()) holder.instance = runtime;
|
|
2399
|
+
else runtime.stopCron().catch((error) => {
|
|
2400
|
+
console.error("[emdash] failed to stop superseded runtime's cron:", error);
|
|
2401
|
+
});
|
|
2221
2402
|
return runtime;
|
|
2222
|
-
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2403
|
+
}, {
|
|
2404
|
+
deadlineMs: RUNTIME_INIT_DEADLINE_MS,
|
|
2405
|
+
anchor: (promise) => after(() => promise)
|
|
2406
|
+
});
|
|
2225
2407
|
}
|
|
2226
2408
|
/**
|
|
2227
2409
|
* Astro attaches AstroCookies to outgoing responses via a well-known global
|
|
@@ -2324,14 +2506,15 @@ const onRequest = defineMiddleware(async (context, next) => {
|
|
|
2324
2506
|
if (!sessionUser && !playgroundDb) {
|
|
2325
2507
|
const timings = [];
|
|
2326
2508
|
const mwStart = performance.now();
|
|
2327
|
-
if (!
|
|
2509
|
+
if (!isSetupVerified()) {
|
|
2328
2510
|
const t0 = performance.now();
|
|
2329
2511
|
try {
|
|
2330
|
-
const { getDb } = await import("../loader-
|
|
2512
|
+
const { getDb } = await import("../loader-Dyx8dhFV.mjs").then((n) => n.i);
|
|
2331
2513
|
await (await getDb()).selectFrom("_emdash_migrations").selectAll().limit(1).execute();
|
|
2332
|
-
|
|
2333
|
-
} catch {
|
|
2334
|
-
return context.redirect("/_emdash/admin/setup");
|
|
2514
|
+
markSetupVerified();
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
if (isMissingTableError(error)) return context.redirect("/_emdash/admin/setup");
|
|
2517
|
+
console.error("Setup probe failed (non-fatal):", error);
|
|
2335
2518
|
}
|
|
2336
2519
|
timings.push({
|
|
2337
2520
|
name: "setup",
|
|
@@ -2345,7 +2528,7 @@ const onRequest = defineMiddleware(async (context, next) => {
|
|
|
2345
2528
|
const t0 = performance.now();
|
|
2346
2529
|
try {
|
|
2347
2530
|
const runtime = await getRuntime(config, initSubTimings);
|
|
2348
|
-
|
|
2531
|
+
markSetupVerified();
|
|
2349
2532
|
locals.emdash = {
|
|
2350
2533
|
handlePublicPluginApiRoute: createPublicPluginApiRouteHandler(runtime),
|
|
2351
2534
|
collectPageMetadata: runtime.collectPageMetadata.bind(runtime),
|
|
@@ -2381,7 +2564,7 @@ const onRequest = defineMiddleware(async (context, next) => {
|
|
|
2381
2564
|
desc: "Total middleware"
|
|
2382
2565
|
});
|
|
2383
2566
|
pushMetricsTimings(timings, metrics);
|
|
2384
|
-
return finalizeResponse(response, timings);
|
|
2567
|
+
return wrapBodyForStreamMetrics(finalizeResponse(response, timings));
|
|
2385
2568
|
};
|
|
2386
2569
|
if (anonScoped) {
|
|
2387
2570
|
const parent = getRequestContext();
|
|
@@ -2419,7 +2602,7 @@ const onRequest = defineMiddleware(async (context, next) => {
|
|
|
2419
2602
|
desc: "Runtime init"
|
|
2420
2603
|
});
|
|
2421
2604
|
for (const sub of initSubTimings) timings.push(sub);
|
|
2422
|
-
|
|
2605
|
+
markSetupVerified();
|
|
2423
2606
|
locals.emdash = {
|
|
2424
2607
|
handleContentList: runtime.handleContentList.bind(runtime),
|
|
2425
2608
|
handleContentGet: runtime.handleContentGet.bind(runtime),
|
|
@@ -2495,7 +2678,7 @@ const onRequest = defineMiddleware(async (context, next) => {
|
|
|
2495
2678
|
desc: "Total middleware"
|
|
2496
2679
|
});
|
|
2497
2680
|
pushMetricsTimings(timings, metrics);
|
|
2498
|
-
return finalizeResponse(response, timings);
|
|
2681
|
+
return wrapBodyForStreamMetrics(finalizeResponse(response, timings));
|
|
2499
2682
|
};
|
|
2500
2683
|
if (scoped) {
|
|
2501
2684
|
const parent = getRequestContext();
|