emdash 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-hUy8vOB-.d.mts → adapters-DAAjOID4.d.mts} +1 -1
- package/dist/{adapters-hUy8vOB-.d.mts.map → adapters-DAAjOID4.d.mts.map} +1 -1
- package/dist/{allowed-origins-DjlhyfNN.mjs → allowed-origins-D5FxMUo8.mjs} +2 -2
- package/dist/{allowed-origins-DjlhyfNN.mjs.map → allowed-origins-D5FxMUo8.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +11 -11
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-BDuM4bEy.mjs → api-BQ_yKRdG.mjs} +12 -12
- package/dist/{api-BDuM4bEy.mjs.map → api-BQ_yKRdG.mjs.map} +1 -1
- package/dist/{api-tokens-ViYKeIp9.mjs → api-tokens-D6ppjIVi.mjs} +2 -2
- package/dist/{api-tokens-ViYKeIp9.mjs.map → api-tokens-D6ppjIVi.mjs.map} +1 -1
- package/dist/{apply-wDqQoxX4.mjs → apply-CWRyxIoO.mjs} +12 -12
- package/dist/{apply-wDqQoxX4.mjs.map → apply-CWRyxIoO.mjs.map} +1 -1
- package/dist/astro/index.d.mts +11 -11
- package/dist/astro/index.mjs +12 -6
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +9 -9
- package/dist/astro/middleware/auth.mjs +6 -6
- package/dist/astro/middleware/redirect.mjs +3 -3
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +5 -3
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +349 -128
- 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 +3 -3
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +7 -7
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +7 -7
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +7 -7
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +12 -11
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +12 -11
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/index.mjs +12 -11
- package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +9 -9
- package/dist/astro/routes/api/admin/comments/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/bulk.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/counts.mjs +3 -3
- package/dist/astro/routes/api/admin/comments/index.mjs +7 -7
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +3 -3
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +2 -2
- 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 +27 -26
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +27 -26
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +27 -26
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +27 -26
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +26 -25
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +26 -25
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +26 -25
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +5 -5
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- 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 +1 -1
- package/dist/astro/routes/api/auth/invite/complete.mjs +9 -9
- package/dist/astro/routes/api/auth/invite/index.mjs +6 -6
- package/dist/astro/routes/api/auth/invite/register-options.mjs +8 -8
- 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 +5 -5
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +8 -8
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +9 -9
- package/dist/astro/routes/api/auth/passkey/verify.mjs +9 -9
- package/dist/astro/routes/api/auth/signup/complete.mjs +9 -9
- package/dist/astro/routes/api/auth/signup/request.mjs +8 -8
- package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +10 -10
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- 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 +3 -3
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +10 -10
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +2 -2
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs +8 -7
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +10 -10
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +12 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +29 -29
- package/dist/astro/routes/api/media/_id_/confirm.mjs +6 -6
- package/dist/astro/routes/api/media/_id_.mjs +6 -6
- package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +8 -8
- package/dist/astro/routes/api/media.mjs +10 -10
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +6 -6
- package/dist/astro/routes/api/menus/_name_/items.mjs +6 -6
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/menus/_name_/translations.mjs +6 -6
- package/dist/astro/routes/api/menus/_name_.mjs +6 -6
- package/dist/astro/routes/api/menus/index.mjs +6 -6
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
- package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +5 -5
- package/dist/astro/routes/api/oauth/token/revoke.mjs +5 -5
- package/dist/astro/routes/api/oauth/token.mjs +5 -5
- package/dist/astro/routes/api/openapi.json.mjs +3 -3
- 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 +2 -2
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +26 -25
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +26 -25
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +26 -25
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +26 -25
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +26 -25
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +7 -6
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +26 -25
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +26 -25
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- 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 +5 -5
- 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 +3 -3
- package/dist/astro/routes/api/settings.mjs +9 -9
- package/dist/astro/routes/api/setup/admin-verify.mjs +10 -10
- package/dist/astro/routes/api/setup/admin.mjs +9 -9
- package/dist/astro/routes/api/setup/dev-bypass.mjs +17 -16
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +1 -1
- package/dist/astro/routes/api/setup/index.mjs +18 -17
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- 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 +10 -10
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +10 -10
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +10 -10
- package/dist/astro/routes/api/taxonomies/index.mjs +10 -10
- package/dist/astro/routes/api/themes/preview.mjs +4 -4
- package/dist/astro/routes/api/typegen.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +8 -8
- package/dist/astro/routes/api/widget-areas/_name_.mjs +4 -4
- 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 +3 -3
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +6 -6
- package/dist/astro/routes/sitemap.xml.mjs +4 -4
- package/dist/astro/types.d.mts +12 -12
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-C0EYoUeV.mjs → authorize-Cse0k7B0.mjs} +2 -2
- package/dist/{authorize-C0EYoUeV.mjs.map → authorize-Cse0k7B0.mjs.map} +1 -1
- package/dist/{byline-CC0WS4Gu.mjs → byline-DVo-liaP.mjs} +15 -73
- package/dist/byline-DVo-liaP.mjs.map +1 -0
- package/dist/{byline-fields-53i9Et0x.mjs → byline-fields-B-KXkg5R.mjs} +2 -2
- package/dist/{byline-fields-53i9Et0x.mjs.map → byline-fields-B-KXkg5R.mjs.map} +1 -1
- package/dist/{byline-fields-DNp0HNKc.mjs → byline-fields-DaMKzkhO.mjs} +1 -1
- package/dist/{byline-fields-DNp0HNKc.mjs.map → byline-fields-DaMKzkhO.mjs.map} +1 -1
- package/dist/{byline-fields-jy07PZJM.d.mts → byline-fields-RFPkDLRw.d.mts} +21 -1
- package/dist/byline-fields-RFPkDLRw.d.mts.map +1 -0
- package/dist/{bylines-CCO1CYnH.mjs → bylines-f5ppBpD5.mjs} +6 -6
- package/dist/{bylines-CCO1CYnH.mjs.map → bylines-f5ppBpD5.mjs.map} +1 -1
- package/dist/{bylines-DGekMGGm.mjs → bylines-pIKgc_TL.mjs} +2 -2
- package/dist/{bylines-DGekMGGm.mjs.map → bylines-pIKgc_TL.mjs.map} +1 -1
- package/dist/{cache-pXTpun6s.mjs → cache-Q6V4U-Xz.mjs} +1 -1
- package/dist/{cache-pXTpun6s.mjs.map → cache-Q6V4U-Xz.mjs.map} +1 -1
- package/dist/{challenge-store-C6E_kWTs.mjs → challenge-store-LhiqMccz.mjs} +1 -1
- package/dist/{challenge-store-C6E_kWTs.mjs.map → challenge-store-LhiqMccz.mjs.map} +1 -1
- package/dist/cli/index.mjs +16 -15
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -2
- package/dist/{comment-reaction-B6AdcpXw.mjs → comment-reaction-Dyb-AutY.mjs} +1 -1
- package/dist/{comment-reaction-B6AdcpXw.mjs.map → comment-reaction-Dyb-AutY.mjs.map} +1 -1
- package/dist/{comments-CtPkT2tp.mjs → comments-Cczx7AvD.mjs} +1 -1
- package/dist/{comments-CtPkT2tp.mjs.map → comments-Cczx7AvD.mjs.map} +1 -1
- package/dist/{components-D6ZnrzbB.mjs → components-CYt4uVK9.mjs} +1 -1
- package/dist/{components-D6ZnrzbB.mjs.map → components-CYt4uVK9.mjs.map} +1 -1
- package/dist/{context-f1__jpzF.mjs → context-B1W8soLv.mjs} +58 -20
- package/dist/context-B1W8soLv.mjs.map +1 -0
- package/dist/{cron-CQPxBjYs.mjs → cron-C5LVoNmP.mjs} +14 -2
- package/dist/cron-C5LVoNmP.mjs.map +1 -0
- package/dist/{dashboard-BOESNcwE.mjs → dashboard-CYjpQZ6-.mjs} +1 -1
- package/dist/{dashboard-BOESNcwE.mjs.map → dashboard-CYjpQZ6-.mjs.map} +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{db-errors-CrC5w3Jz.mjs → db-errors-UXB1CK42.mjs} +1 -1
- package/dist/{db-errors-CrC5w3Jz.mjs.map → db-errors-UXB1CK42.mjs.map} +1 -1
- package/dist/{default-CpBEPn6a.mjs → default-CeRG-Ot4.mjs} +1 -1
- package/dist/{default-CpBEPn6a.mjs.map → default-CeRG-Ot4.mjs.map} +1 -1
- package/dist/{device-flow-Bn-qiDrw.mjs → device-flow-DENDCQ9F.mjs} +4 -4
- package/dist/{device-flow-Bn-qiDrw.mjs.map → device-flow-DENDCQ9F.mjs.map} +1 -1
- package/dist/{email-console-B9VS6eN4.mjs → email-console-DJP32ucW.mjs} +1 -1
- package/dist/{email-console-B9VS6eN4.mjs.map → email-console-DJP32ucW.mjs.map} +1 -1
- package/dist/{error-BQ1pxs0L.mjs → error-Bl6kZgtt.mjs} +1 -1
- package/dist/{error-BQ1pxs0L.mjs.map → error-Bl6kZgtt.mjs.map} +1 -1
- package/dist/{escape-BtbJWyaW.mjs → escape-Bjio4ZsM.mjs} +1 -1
- package/dist/{escape-BtbJWyaW.mjs.map → escape-Bjio4ZsM.mjs.map} +1 -1
- package/dist/field-defs-cache-ztrdzUGb.mjs +77 -0
- package/dist/field-defs-cache-ztrdzUGb.mjs.map +1 -0
- package/dist/{fts-manager-DtiWqwNJ.mjs → fts-manager-CQuHt-vS.mjs} +1 -1
- package/dist/{fts-manager-DtiWqwNJ.mjs.map → fts-manager-CQuHt-vS.mjs.map} +1 -1
- package/dist/{hash-BowcuPwv.mjs → hash-DrvzIXcz.mjs} +1 -1
- package/dist/{hash-BowcuPwv.mjs.map → hash-DrvzIXcz.mjs.map} +1 -1
- package/dist/{import-DqSGOlmR.mjs → import-Bb1T9WJS.mjs} +3 -3
- package/dist/{import-DqSGOlmR.mjs.map → import-Bb1T9WJS.mjs.map} +1 -1
- package/dist/{index-C2dLNT6S.d.mts → index-BaIj5hIq.d.mts} +39 -17
- package/dist/index-BaIj5hIq.d.mts.map +1 -0
- package/dist/{index-ZDEwR7qW.d.mts → index-qbL02uhs.d.mts} +3 -3
- package/dist/{index-ZDEwR7qW.d.mts.map → index-qbL02uhs.d.mts.map} +1 -1
- package/dist/index.d.mts +17 -17
- package/dist/index.mjs +46 -45
- package/dist/{load-Dtoo7KcJ.mjs → load-BNo-ER8C.mjs} +1 -1
- package/dist/{load-Dtoo7KcJ.mjs.map → load-BNo-ER8C.mjs.map} +1 -1
- package/dist/{loader-C8Z48Uof.mjs → loader-Dn74CG1b.mjs} +68 -9
- package/dist/loader-Dn74CG1b.mjs.map +1 -0
- package/dist/{manifest-schema-DDSbwkAL.mjs → manifest-schema-BWKnQYQF.mjs} +1 -1
- package/dist/{manifest-schema-DDSbwkAL.mjs.map → manifest-schema-BWKnQYQF.mjs.map} +1 -1
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +2 -2
- package/dist/media/local-runtime.d.mts +19 -11
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +9 -7
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-allowlist-DunzrKFM.mjs → media-allowlist-B77I5_bw.mjs} +2 -2
- package/dist/{media-allowlist-DunzrKFM.mjs.map → media-allowlist-B77I5_bw.mjs.map} +1 -1
- package/dist/{media-url-S22B6aPr.mjs → media-url-O4rm9-SQ.mjs} +1 -1
- package/dist/{media-url-S22B6aPr.mjs.map → media-url-O4rm9-SQ.mjs.map} +1 -1
- package/dist/{menus-DOs1MQJg.mjs → menus-DS8VTZgo.mjs} +56 -13
- package/dist/menus-DS8VTZgo.mjs.map +1 -0
- package/dist/{mime-DYpsOAny.mjs → mime-BTzvzJ8M.mjs} +1 -1
- package/dist/{mime-DYpsOAny.mjs.map → mime-BTzvzJ8M.mjs.map} +1 -1
- package/dist/{mode-CFh8Dw8G.mjs → mode-BB0F8xTC.mjs} +1 -1
- package/dist/{mode-CFh8Dw8G.mjs.map → mode-BB0F8xTC.mjs.map} +1 -1
- package/dist/{normalize-Uam4_Vkr.mjs → normalize-ClbNIenm.mjs} +1 -1
- package/dist/{normalize-Uam4_Vkr.mjs.map → normalize-ClbNIenm.mjs.map} +1 -1
- package/dist/{oauth-authorization-BN-t83Uy.mjs → oauth-authorization-CsvzIp_F.mjs} +4 -4
- package/dist/{oauth-authorization-BN-t83Uy.mjs.map → oauth-authorization-CsvzIp_F.mjs.map} +1 -1
- package/dist/{oauth-clients-R6I_V0qz.mjs → oauth-clients-C9SYwEbZ.mjs} +1 -1
- package/dist/{oauth-clients-R6I_V0qz.mjs.map → oauth-clients-C9SYwEbZ.mjs.map} +1 -1
- package/dist/{oauth-state-store-Csoc6kkR.mjs → oauth-state-store---zrApfB.mjs} +1 -1
- package/dist/{oauth-state-store-Csoc6kkR.mjs.map → oauth-state-store---zrApfB.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-d7VYjTpx.mjs → oauth-user-lookup-SHsWRlG9.mjs} +1 -1
- package/dist/{oauth-user-lookup-d7VYjTpx.mjs.map → oauth-user-lookup-SHsWRlG9.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{options-DFFpvNJU.d.mts → options-CqECG0gM.d.mts} +3 -3
- package/dist/{options-DFFpvNJU.d.mts.map → options-CqECG0gM.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-qrHlnzTy.mjs → parse-C9hpjUyY.mjs} +2 -2
- package/dist/{parse-qrHlnzTy.mjs.map → parse-C9hpjUyY.mjs.map} +1 -1
- package/dist/{passkey-config-BJTbcnI5.mjs → passkey-config-C0YfSBko.mjs} +1 -1
- package/dist/{passkey-config-BJTbcnI5.mjs.map → passkey-config-C0YfSBko.mjs.map} +1 -1
- package/dist/{placeholder-CIDCpyEY.mjs → placeholder-B0s2mWu5.mjs} +1 -1
- package/dist/{placeholder-CIDCpyEY.mjs.map → placeholder-B0s2mWu5.mjs.map} +1 -1
- package/dist/{placeholder-CirPauNG.d.mts → placeholder-QoI4gEzP.d.mts} +1 -1
- package/dist/{placeholder-CirPauNG.d.mts.map → placeholder-QoI4gEzP.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{portable-text-BIFhrU6T.mjs → portable-text-BICg8bTk.mjs} +1 -1
- package/dist/{portable-text-BIFhrU6T.mjs.map → portable-text-BICg8bTk.mjs.map} +1 -1
- package/dist/{preview-IDye9SPQ.mjs → preview-DKGCt2_p.mjs} +2 -2
- package/dist/{preview-IDye9SPQ.mjs.map → preview-DKGCt2_p.mjs.map} +1 -1
- package/dist/{public-url-8lFyQ8ZF.mjs → public-url-CTVqgMmg.mjs} +1 -1
- package/dist/{public-url-8lFyQ8ZF.mjs.map → public-url-CTVqgMmg.mjs.map} +1 -1
- package/dist/{query-B1RVFUNM.mjs → query-DgUHZLWB.mjs} +79 -8
- package/dist/query-DgUHZLWB.mjs.map +1 -0
- package/dist/{rate-limit-DfNAqIWW.mjs → rate-limit-BuQ5d4X_.mjs} +2 -2
- package/dist/{rate-limit-DfNAqIWW.mjs.map → rate-limit-BuQ5d4X_.mjs.map} +1 -1
- package/dist/{redirect-B4wuX2A5.mjs → redirect-CS-PHtNh.mjs} +1 -1
- package/dist/{redirect-B4wuX2A5.mjs.map → redirect-CS-PHtNh.mjs.map} +1 -1
- package/dist/{redirects-CT_vb3NV.mjs → redirects-BZ8Mxn8H.mjs} +2 -2
- package/dist/{redirects-CT_vb3NV.mjs.map → redirects-BZ8Mxn8H.mjs.map} +1 -1
- package/dist/{redirects-DUvXgzCX.mjs → redirects-x9eFPeyi.mjs} +4 -2
- package/dist/redirects-x9eFPeyi.mjs.map +1 -0
- package/dist/{registry-CjK96fHD.mjs → registry-CK1SS6pi.mjs} +2 -2
- package/dist/{registry-CjK96fHD.mjs.map → registry-CK1SS6pi.mjs.map} +1 -1
- package/dist/{request-meta-ei3ATilC.mjs → request-meta-CmS1tDFf.mjs} +2 -2
- package/dist/{request-meta-ei3ATilC.mjs.map → request-meta-CmS1tDFf.mjs.map} +1 -1
- package/dist/{resolve-CpbBglV0.mjs → resolve-C7I0qiR0.mjs} +1 -1
- package/dist/{resolve-CpbBglV0.mjs.map → resolve-C7I0qiR0.mjs.map} +1 -1
- package/dist/{runner-jdXtFEd3.d.mts → runner-CEtq-6Sm.d.mts} +2 -2
- package/dist/{runner-jdXtFEd3.d.mts.map → runner-CEtq-6Sm.d.mts.map} +1 -1
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +1 -1
- package/dist/{schema-5z2Jfnbm.mjs → schema-CbFRFoM7.mjs} +3 -3
- package/dist/{schema-5z2Jfnbm.mjs.map → schema-CbFRFoM7.mjs.map} +1 -1
- package/dist/{search-D9FkoM-k.mjs → search-C-waE_YW.mjs} +3 -3
- package/dist/{search-D9FkoM-k.mjs.map → search-C-waE_YW.mjs.map} +1 -1
- package/dist/{secrets-BUE5UQys.mjs → secrets-BSf9pRRY.mjs} +2 -2
- package/dist/{secrets-BUE5UQys.mjs.map → secrets-BSf9pRRY.mjs.map} +1 -1
- package/dist/{sections-DWYZo6HK.mjs → sections-osX9U-YJ.mjs} +2 -2
- package/dist/{sections-DWYZo6HK.mjs.map → sections-osX9U-YJ.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +12 -11
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/{seo-Cu47j3mJ.mjs → seo-4FovMwNS.mjs} +1 -1
- package/dist/{seo-Cu47j3mJ.mjs.map → seo-4FovMwNS.mjs.map} +1 -1
- package/dist/{seo-DRnqnstF.mjs → seo-DCEF-AVr.mjs} +1 -1
- package/dist/{seo-DRnqnstF.mjs.map → seo-DCEF-AVr.mjs.map} +1 -1
- package/dist/{service-DTmtqCnw.mjs → service-jby1RRmU.mjs} +2 -2
- package/dist/{service-DTmtqCnw.mjs.map → service-jby1RRmU.mjs.map} +1 -1
- package/dist/{session-user-DmtPMNa1.mjs → session-user-B8aLtKAH.mjs} +1 -1
- package/dist/{session-user-DmtPMNa1.mjs.map → session-user-B8aLtKAH.mjs.map} +1 -1
- package/dist/{settings-DUrVDN0c.mjs → settings-4bRT2NIf.mjs} +2 -2
- package/dist/{settings-DUrVDN0c.mjs.map → settings-4bRT2NIf.mjs.map} +1 -1
- package/dist/{settings-yaxu9mU-.mjs → settings-t-H2jPuJ.mjs} +3 -3
- package/dist/{settings-yaxu9mU-.mjs.map → settings-t-H2jPuJ.mjs.map} +1 -1
- package/dist/{setup-complete-BWFHzgvZ.mjs → setup-complete-gEiySUc-.mjs} +1 -1
- package/dist/{setup-complete-BWFHzgvZ.mjs.map → setup-complete-gEiySUc-.mjs.map} +1 -1
- package/dist/{setup-nonce-DFWG-J5c.mjs → setup-nonce-DzS50zme.mjs} +1 -1
- package/dist/{setup-nonce-DFWG-J5c.mjs.map → setup-nonce-DzS50zme.mjs.map} +1 -1
- package/dist/{single-flight-cache-B5p5cNOL.mjs → single-flight-cache-Cdfkic3t.mjs} +1 -1
- package/dist/{single-flight-cache-B5p5cNOL.mjs.map → single-flight-cache-Cdfkic3t.mjs.map} +1 -1
- package/dist/{site-url-BOTPgmeM.mjs → site-url-BLebyON8.mjs} +1 -1
- package/dist/{site-url-BOTPgmeM.mjs.map → site-url-BLebyON8.mjs.map} +1 -1
- package/dist/{ssrf-B0VeRLrp.mjs → ssrf-BRKb343l.mjs} +1 -1
- package/dist/{ssrf-B0VeRLrp.mjs.map → ssrf-BRKb343l.mjs.map} +1 -1
- package/dist/{status-CB2basWJ.mjs → status-vUK0SA17.mjs} +1 -1
- package/dist/{status-CB2basWJ.mjs.map → status-vUK0SA17.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-CJmkEpdS.mjs → taxonomies-BBB8O-CU.mjs} +2 -2
- package/dist/{taxonomies-CJmkEpdS.mjs.map → taxonomies-BBB8O-CU.mjs.map} +1 -1
- package/dist/{taxonomies-Be9B1jEj.mjs → taxonomies-Dxrc_szG.mjs} +26 -6
- package/dist/{taxonomies-Be9B1jEj.mjs.map → taxonomies-Dxrc_szG.mjs.map} +1 -1
- package/dist/{tokens-BvrJvIB6.mjs → tokens-LeuXF9gG.mjs} +1 -1
- package/dist/{tokens-BvrJvIB6.mjs.map → tokens-LeuXF9gG.mjs.map} +1 -1
- package/dist/{transport-k7YjfmEn.d.mts → transport-BZ-03aFT.d.mts} +1 -1
- package/dist/{transport-k7YjfmEn.d.mts.map → transport-BZ-03aFT.d.mts.map} +1 -1
- package/dist/{transport-B4rXnSTf.mjs → transport-_2nBz7e9.mjs} +1 -1
- package/dist/{transport-B4rXnSTf.mjs.map → transport-_2nBz7e9.mjs.map} +1 -1
- package/dist/{trusted-proxy-DY1_UqHr.mjs → trusted-proxy-DZY5WCn2.mjs} +1 -1
- package/dist/{trusted-proxy-DY1_UqHr.mjs.map → trusted-proxy-DZY5WCn2.mjs.map} +1 -1
- package/dist/{types-Czg1S3gB.d.mts → types-B-gIxFCE.d.mts} +1 -1
- package/dist/{types-Czg1S3gB.d.mts.map → types-B-gIxFCE.d.mts.map} +1 -1
- package/dist/{types-DXLVzV2w.d.mts → types-BG8_XTLj.d.mts} +1 -1
- package/dist/{types-DXLVzV2w.d.mts.map → types-BG8_XTLj.d.mts.map} +1 -1
- package/dist/{types-CWGYDYqF.d.mts → types-BWh2MjmA.d.mts} +15 -2
- package/dist/types-BWh2MjmA.d.mts.map +1 -0
- package/dist/{types-CgvhU-hF.d.mts → types-BWqGVhFn.d.mts} +1 -1
- package/dist/{types-CgvhU-hF.d.mts.map → types-BWqGVhFn.d.mts.map} +1 -1
- package/dist/{types-DYF2_Vf4.mjs → types-CQAugunJ.mjs} +1 -1
- package/dist/{types-DYF2_Vf4.mjs.map → types-CQAugunJ.mjs.map} +1 -1
- package/dist/{types-DPCpkzvS.d.mts → types-CTLJYMhN.d.mts} +1 -1
- package/dist/{types-DPCpkzvS.d.mts.map → types-CTLJYMhN.d.mts.map} +1 -1
- package/dist/{types-CmivgZbp.d.mts → types-CUsXGRVj.d.mts} +1 -1
- package/dist/{types-CmivgZbp.d.mts.map → types-CUsXGRVj.d.mts.map} +1 -1
- package/dist/types-DXOIbece.mjs.map +1 -1
- package/dist/{types-CKNXt63n.d.mts → types-DrYMm9s5.d.mts} +1 -1
- package/dist/{types-CKNXt63n.d.mts.map → types-DrYMm9s5.d.mts.map} +1 -1
- package/dist/{types-CIKX2HO5.d.mts → types-TTx8uP7V.d.mts} +2 -2
- package/dist/{types-CIKX2HO5.d.mts.map → types-TTx8uP7V.d.mts.map} +1 -1
- package/dist/{types-DCNNfeCv.d.mts → types-fS7-VTdR.d.mts} +1 -1
- package/dist/{types-DCNNfeCv.d.mts.map → types-fS7-VTdR.d.mts.map} +1 -1
- package/dist/{utils-LbHcSRTj.mjs → utils-B7A57fm9.mjs} +1 -1
- package/dist/{utils-LbHcSRTj.mjs.map → utils-B7A57fm9.mjs.map} +1 -1
- package/dist/{validate-Bwl64sxv.mjs → validate-0Utjspbs.mjs} +1 -1
- package/dist/{validate-Bwl64sxv.mjs.map → validate-0Utjspbs.mjs.map} +1 -1
- package/dist/{validate-CE2M0nvx.d.mts → validate-DOFrIb-c.d.mts} +5 -5
- package/dist/{validate-CE2M0nvx.d.mts.map → validate-DOFrIb-c.d.mts.map} +1 -1
- package/dist/{validation-C_yOvFaO.mjs → validation-D9EnzH1T.mjs} +5 -5
- package/dist/{validation-C_yOvFaO.mjs.map → validation-D9EnzH1T.mjs.map} +1 -1
- package/dist/version-BgVM_1MU.mjs +7 -0
- package/dist/{version-RnYXYjzD.mjs.map → version-BgVM_1MU.mjs.map} +1 -1
- package/dist/{widgets-8GONuRHo.mjs → widgets-BSTzPUYa.mjs} +3 -3
- package/dist/{widgets-8GONuRHo.mjs.map → widgets-BSTzPUYa.mjs.map} +1 -1
- package/dist/{zod-generator-D481JjzD.mjs → zod-generator-ElVQOav7.mjs} +50 -14
- package/dist/zod-generator-ElVQOav7.mjs.map +1 -0
- package/package.json +8 -7
- package/src/api/schemas/bylines.ts +8 -0
- package/src/astro/integration/virtual-modules.ts +8 -0
- package/src/astro/integration/vite-config.ts +9 -1
- package/src/astro/middleware/request-context.ts +6 -1
- package/src/astro/middleware/scoped-db.ts +131 -0
- package/src/astro/middleware/stream-end-metrics.ts +4 -8
- package/src/astro/middleware.ts +90 -28
- package/src/astro/routes/admin.astro +5 -1
- package/src/astro/routes/api/schema/index.ts +9 -3
- package/src/cli/commands/export-seed.ts +10 -7
- package/src/components/Block.astro +71 -0
- package/src/components/EmDashMedia.astro +1 -2
- package/src/components/Image.astro +36 -2
- package/src/components/InlinePortableTextEditor.tsx +10 -2
- package/src/components/index.ts +5 -0
- package/src/components/portable-text-text-align.ts +38 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +5 -1
- package/src/content/converters/prosemirror-to-portable-text.ts +8 -0
- package/src/content/converters/types.ts +1 -0
- package/src/database/repositories/byline.ts +14 -0
- package/src/database/repositories/types.ts +13 -0
- package/src/emdash-runtime.ts +226 -71
- package/src/loader.ts +97 -5
- package/src/media/local-runtime.ts +18 -5
- package/src/plugins/context.ts +94 -22
- package/src/plugins/cron.ts +18 -2
- package/src/plugins/routes.ts +40 -1
- package/src/query.ts +108 -1
- package/src/schema/zod-generator.ts +53 -8
- package/src/taxonomies/index.ts +26 -0
- package/src/ui.ts +5 -0
- package/src/virtual-modules.d.ts +13 -0
- package/dist/byline-CC0WS4Gu.mjs.map +0 -1
- package/dist/byline-fields-jy07PZJM.d.mts.map +0 -1
- package/dist/context-f1__jpzF.mjs.map +0 -1
- package/dist/cron-CQPxBjYs.mjs.map +0 -1
- package/dist/index-C2dLNT6S.d.mts.map +0 -1
- package/dist/loader-C8Z48Uof.mjs.map +0 -1
- package/dist/menus-DOs1MQJg.mjs.map +0 -1
- package/dist/query-B1RVFUNM.mjs.map +0 -1
- package/dist/redirects-DUvXgzCX.mjs.map +0 -1
- package/dist/types-CWGYDYqF.d.mts.map +0 -1
- package/dist/version-RnYXYjzD.mjs +0 -7
- package/dist/zod-generator-D481JjzD.mjs.map +0 -1
- /package/dist/{api-tokens-BVH-H8Z9.mjs → api-tokens-B4BQybOp.mjs} +0 -0
- /package/dist/{ssrf-Cz1e2QPn.mjs → ssrf-CcX9zvMK.mjs} +0 -0
- /package/dist/{types-Bfr2Pj6z.mjs → types-BvbeGEtr.mjs} +0 -0
|
@@ -307,6 +307,10 @@ export function createVirtualModulesPlugin(options: VitePluginOptions): Plugin {
|
|
|
307
307
|
* On Cloudflare, the adapter handles its own externalization — setting
|
|
308
308
|
* ssr.external there conflicts with @cloudflare/vite-plugin's validation.
|
|
309
309
|
*/
|
|
310
|
+
// Matches the admin stylesheet import with or without a trailing query (e.g.
|
|
311
|
+
// `?url`), so both forms resolve to dist rather than the source alias.
|
|
312
|
+
const ADMIN_STYLES_ALIAS = /^@emdash-cms\/admin\/styles\.css/;
|
|
313
|
+
|
|
310
314
|
const NODE_NATIVE_EXTERNALS = [
|
|
311
315
|
"better-sqlite3",
|
|
312
316
|
"bindings",
|
|
@@ -353,8 +357,12 @@ export function createViteConfig(
|
|
|
353
357
|
// The styles.css alias must come before the package alias, otherwise
|
|
354
358
|
// Vite's prefix matching on "@emdash-cms/admin" would resolve
|
|
355
359
|
// "@emdash-cms/admin/styles.css" through the source directory.
|
|
360
|
+
// Regex (not string) so the `?url` variant — admin.astro imports the
|
|
361
|
+
// stylesheet as `?url` to keep it out of the page CSS graph — also
|
|
362
|
+
// resolves to dist; a string `find` only matches on a `/` or end
|
|
363
|
+
// boundary, so `styles.css?url` would slip through to the source alias.
|
|
356
364
|
alias: [
|
|
357
|
-
{ find:
|
|
365
|
+
{ find: ADMIN_STYLES_ALIAS, replacement: resolve(adminDistPath, "styles.css") },
|
|
358
366
|
{ find: "@emdash-cms/admin", replacement: useSource ? adminSourcePath : adminDistPath },
|
|
359
367
|
// `use-sync-external-store/shim` is a React <18 polyfill that ships
|
|
360
368
|
// only as CJS. It's pulled in transitively by `@tiptap/react`. With
|
|
@@ -30,10 +30,15 @@ async function injectToolbar(response: Response, toolbarHtml: string): Promise<R
|
|
|
30
30
|
if (!html.includes("</body>")) return new Response(html, response);
|
|
31
31
|
|
|
32
32
|
const injected = html.replace("</body>", `${toolbarHtml}</body>`);
|
|
33
|
-
|
|
33
|
+
const result = new Response(injected, {
|
|
34
34
|
status: response.status,
|
|
35
35
|
headers: response.headers,
|
|
36
36
|
});
|
|
37
|
+
// Toolbar-injected HTML is session-specific (its presence reveals an active
|
|
38
|
+
// editor session); it must never be stored in a shared CDN cache and served
|
|
39
|
+
// to anonymous visitors. Mirrors the preview branch's guard (#1398).
|
|
40
|
+
result.headers.set("Cache-Control", "private, no-store");
|
|
41
|
+
return result;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
export const onRequest = defineMiddleware(async (context, next) => {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-scoped database lifecycle helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from middleware.ts so they can be unit-tested without pulling in
|
|
5
|
+
* the virtual:emdash/* module graph. The middleware imports these to settle a
|
|
6
|
+
* request-scoped db adapter's lifecycle around the response.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Astro attaches AstroCookies to outgoing responses via a well-known global
|
|
11
|
+
* symbol. Cloning a Response (`new Response(body, init)`) drops non-header
|
|
12
|
+
* metadata, so any helper that wraps the response must explicitly forward this
|
|
13
|
+
* symbol or `cookies.set()` calls will be silently dropped. `Symbol.for`
|
|
14
|
+
* returns the same registry symbol everywhere, so this matches the copy in
|
|
15
|
+
* middleware.ts.
|
|
16
|
+
*/
|
|
17
|
+
export const ASTRO_COOKIES_SYMBOL = Symbol.for("astro.cookies");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Run a request-scoped db's `close()` once the response body has finished
|
|
21
|
+
* streaming. Astro streams HTML and components issue DB queries during that
|
|
22
|
+
* stream, so a connection-backed adapter (e.g. Postgres over Hyperdrive) must
|
|
23
|
+
* not be torn down until the body is flushed. Bodyless responses (redirects,
|
|
24
|
+
* 304s, errors) close immediately. A guard makes close idempotent and a stream
|
|
25
|
+
* `cancel` (client disconnect) still triggers it so connections never leak.
|
|
26
|
+
*
|
|
27
|
+
* No-op for adapters without a `close` (D1): the response passes through.
|
|
28
|
+
*/
|
|
29
|
+
export function wrapResponseForScopedClose(response: Response, close: () => void): Response {
|
|
30
|
+
let closed = false;
|
|
31
|
+
const runClose = () => {
|
|
32
|
+
if (closed) return;
|
|
33
|
+
closed = true;
|
|
34
|
+
try {
|
|
35
|
+
close();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error("[emdash] request-scoped db close failed:", error);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (!response.body) {
|
|
42
|
+
runClose();
|
|
43
|
+
return response;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const transform = new TransformStream<Uint8Array, Uint8Array>({
|
|
47
|
+
flush: runClose,
|
|
48
|
+
cancel: runClose,
|
|
49
|
+
});
|
|
50
|
+
const wrapped = new Response(response.body.pipeThrough(transform), response);
|
|
51
|
+
const astroCookies = Reflect.get(response, ASTRO_COOKIES_SYMBOL);
|
|
52
|
+
if (astroCookies !== undefined) {
|
|
53
|
+
Reflect.set(wrapped, ASTRO_COOKIES_SYMBOL, astroCookies);
|
|
54
|
+
}
|
|
55
|
+
// Byte counts are preserved by the identity transform, but a stale
|
|
56
|
+
// Content-Length on a reconstructed streaming Response risks truncation.
|
|
57
|
+
wrapped.headers.delete("Content-Length");
|
|
58
|
+
return wrapped;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Run the request body under a request-scoped db, then settle its lifecycle:
|
|
63
|
+
* `commit()` runs before the response is returned (so per-request state like a
|
|
64
|
+
* D1 bookmark cookie is persisted in the headers, even if render throws), while
|
|
65
|
+
* `close()` (if any) is deferred to stream-end so a connection-backed adapter
|
|
66
|
+
* isn't torn down mid-render. On error the connection is closed immediately
|
|
67
|
+
* before rethrowing so it never leaks.
|
|
68
|
+
*
|
|
69
|
+
* On the error path both `commit()` and `close()` are defended: a throw from
|
|
70
|
+
* either is logged and swallowed so it can't replace the propagating render
|
|
71
|
+
* error (which is the one the caller needs to see). On the success path
|
|
72
|
+
* `commit()` is guarded too — if it throws, the connection is closed before the
|
|
73
|
+
* failure is surfaced, so it never leaks. For the current adapters `commit()`
|
|
74
|
+
* is a no-op (Hyperdrive) or a cookie write (D1, no `close`) and `close()` is
|
|
75
|
+
* fire-and-forget, so these guards only matter for a future connection-backed
|
|
76
|
+
* adapter with throwing teardown, but the helper is generic and must not leak
|
|
77
|
+
* or mask.
|
|
78
|
+
*/
|
|
79
|
+
export async function finishScoped(
|
|
80
|
+
scoped: { commit: () => void; close?: () => void },
|
|
81
|
+
run: () => Promise<Response>,
|
|
82
|
+
): Promise<Response> {
|
|
83
|
+
let response: Response;
|
|
84
|
+
try {
|
|
85
|
+
response = await run();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// A render error is already propagating; neither commit nor close may
|
|
88
|
+
// mask it, and close must still run so the connection doesn't leak.
|
|
89
|
+
commitSafely(scoped.commit);
|
|
90
|
+
closeSafely(scoped.close);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
scoped.commit();
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// commit() failed on the success path: close the connection now (the
|
|
97
|
+
// response won't be wrapped, so stream-end close would never run) and
|
|
98
|
+
// surface the failure. close is swallowed so it can't mask the commit
|
|
99
|
+
// error that the caller needs to see.
|
|
100
|
+
closeSafely(scoped.close);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
return scoped.close ? wrapResponseForScopedClose(response, scoped.close) : response;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Run commit() swallowing any error. Used where an exception is already
|
|
108
|
+
* propagating (or about to be thrown) and a commit failure must neither mask it
|
|
109
|
+
* nor skip the subsequent close().
|
|
110
|
+
*/
|
|
111
|
+
function commitSafely(commit: () => void): void {
|
|
112
|
+
try {
|
|
113
|
+
commit();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error("[emdash] request-scoped db commit failed during error handling:", error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Run close() swallowing any error. Used on the error/commit-failure paths
|
|
121
|
+
* where another exception is the one the caller must see; a throwing teardown
|
|
122
|
+
* must not replace it.
|
|
123
|
+
*/
|
|
124
|
+
function closeSafely(close: (() => void) | undefined): void {
|
|
125
|
+
if (!close) return;
|
|
126
|
+
try {
|
|
127
|
+
close();
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error("[emdash] request-scoped db close failed during error handling:", error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -20,17 +20,13 @@
|
|
|
20
20
|
|
|
21
21
|
import { flushRecorder, isInstrumentationEnabled } from "../../database/instrumentation.js";
|
|
22
22
|
import { getRequestContext } from "../../request-context.js";
|
|
23
|
+
// Reuse the single source of truth for Astro's well-known cookies symbol
|
|
24
|
+
// rather than redefining `Symbol.for("astro.cookies")` here — it must stay in
|
|
25
|
+
// lockstep with the copy the rest of the middleware forwards.
|
|
26
|
+
import { ASTRO_COOKIES_SYMBOL } from "./scoped-db.js";
|
|
23
27
|
|
|
24
28
|
export const STREAM_END_PREFIX = "[emdash-stream-end]";
|
|
25
29
|
|
|
26
|
-
/**
|
|
27
|
-
* Astro attaches AstroCookies to outgoing responses via a well-known global
|
|
28
|
-
* symbol. Constructing a new Response drops non-header metadata, so the
|
|
29
|
-
* symbol must be forwarded explicitly or `cookies.set()` calls are silently
|
|
30
|
-
* dropped. Same pattern as finalizeResponse in ../middleware.ts.
|
|
31
|
-
*/
|
|
32
|
-
const ASTRO_COOKIES_SYMBOL = Symbol.for("astro.cookies");
|
|
33
|
-
|
|
34
30
|
/** Shape of the NDJSON snapshot emitted when the body finishes streaming. */
|
|
35
31
|
export interface StreamEndSnapshot {
|
|
36
32
|
route?: string;
|
package/src/astro/middleware.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { Kysely } from "kysely";
|
|
|
12
12
|
import virtualConfig from "virtual:emdash/config";
|
|
13
13
|
// @ts-ignore - virtual module
|
|
14
14
|
import {
|
|
15
|
+
createCoalescingDialect as virtualCreateCoalescingDialect,
|
|
15
16
|
createDialect as virtualCreateDialect,
|
|
16
17
|
createRequestScopedDb as virtualCreateRequestScopedDb,
|
|
17
18
|
} from "virtual:emdash/dialect";
|
|
@@ -59,6 +60,7 @@ import type { PublishedRef } from "../scheduled-publish.js";
|
|
|
59
60
|
import { isMissingTableError } from "../utils/db-errors.js";
|
|
60
61
|
import { createInitLock, type InitLock, initWithLock } from "../utils/init-lock.js";
|
|
61
62
|
import type { EmDashConfig } from "./integration/runtime.js";
|
|
63
|
+
import { ASTRO_COOKIES_SYMBOL, finishScoped } from "./middleware/scoped-db.js";
|
|
62
64
|
import { wrapBodyForStreamMetrics } from "./middleware/stream-end-metrics.js";
|
|
63
65
|
import { prefetchLayoutData } from "./prefetch.js";
|
|
64
66
|
import { createPublicPluginApiRouteHandler } from "./public-plugin-api-routes.js";
|
|
@@ -179,6 +181,10 @@ function buildDependencies(config: EmDashConfig): RuntimeDependencies {
|
|
|
179
181
|
config,
|
|
180
182
|
plugins: getPlugins(),
|
|
181
183
|
createDialect: virtualCreateDialect as (config: Record<string, unknown>) => unknown,
|
|
184
|
+
// Optional: only batching backends (D1, DO) export this; undefined otherwise.
|
|
185
|
+
createCoalescingDialect: virtualCreateCoalescingDialect as
|
|
186
|
+
| ((config: Record<string, unknown>) => unknown)
|
|
187
|
+
| undefined,
|
|
182
188
|
createStorage: virtualCreateStorage as ((config: Record<string, unknown>) => Storage) | null,
|
|
183
189
|
createScheduler: virtualCreateScheduler as CreateSchedulerFn | null,
|
|
184
190
|
sandboxEnabled: sandboxModule.sandboxEnabled as boolean,
|
|
@@ -270,16 +276,71 @@ export async function runScheduledTasks(
|
|
|
270
276
|
const config = getConfig();
|
|
271
277
|
if (!config) return { published: [] };
|
|
272
278
|
const runtime = await getRuntime(config);
|
|
273
|
-
|
|
279
|
+
|
|
280
|
+
// Connection-backed adapters (e.g. Postgres over Hyperdrive) cannot reuse
|
|
281
|
+
// the per-isolate singleton from a Cron Trigger: its socket belongs to the
|
|
282
|
+
// request that opened it, and workerd rejects cross-event I/O. Open an
|
|
283
|
+
// event-scoped connection for the sweep and run the batch under it in ALS —
|
|
284
|
+
// the runtime's db getter, the cron executor, and plugin cron contexts all
|
|
285
|
+
// resolve the connection from ALS — then close it. Gated on the adapter
|
|
286
|
+
// being connection-backed (it exposes `close()`); stateless adapters (D1,
|
|
287
|
+
// Node SQLite) return null or a close-less scope and keep using the
|
|
288
|
+
// singleton, so their cron path is unchanged.
|
|
289
|
+
const scoped = createRequestScopedDb({
|
|
290
|
+
config: config.database?.config,
|
|
291
|
+
isAuthenticated: false,
|
|
292
|
+
// The sweep publishes and cleans up — a write workload — so a
|
|
293
|
+
// connection-backed adapter routes it to the primary.
|
|
294
|
+
isWrite: true,
|
|
295
|
+
cookies: NOOP_COOKIE_JAR,
|
|
296
|
+
url: CRON_EVENT_URL,
|
|
297
|
+
});
|
|
298
|
+
if (!scoped?.close) {
|
|
299
|
+
// Stateless adapter (or no per-request scoping): the singleton is safe
|
|
300
|
+
// outside a request. Any close-less scope created above is discarded.
|
|
301
|
+
return runtime.runScheduledTasks(options);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const parent = getRequestContext();
|
|
305
|
+
const ctx = parent
|
|
306
|
+
? { ...parent, db: scoped.db }
|
|
307
|
+
: { editMode: false, db: scoped.db, metrics: createRequestMetrics(performance.now()) };
|
|
308
|
+
try {
|
|
309
|
+
return await runWithContext(ctx, () => runtime.runScheduledTasks(options));
|
|
310
|
+
} finally {
|
|
311
|
+
// Guard both so a throw in teardown can't mask the sweep result or skip
|
|
312
|
+
// close() and leak the connection. Mirrors closeSafely() in scoped-db.ts.
|
|
313
|
+
try {
|
|
314
|
+
scoped.commit();
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.error("[scheduled] request-scoped db commit failed:", error);
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
scoped.close();
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error("[scheduled] request-scoped db close failed:", error);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
274
324
|
}
|
|
275
325
|
|
|
276
326
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
327
|
+
* A cookie jar that reads nothing and writes nothing, for request-scoped db
|
|
328
|
+
* adapters invoked outside an HTTP request (the Cron Trigger sweep). Connection
|
|
329
|
+
* adapters like Hyperdrive ignore cookies entirely; the D1 session adapter
|
|
330
|
+
* reads/writes a bookmark cookie, but cron never reaches that path (it has no
|
|
331
|
+
* `close()`), so the no-ops are never observed.
|
|
281
332
|
*/
|
|
282
|
-
const
|
|
333
|
+
const NOOP_COOKIE_JAR = {
|
|
334
|
+
get: () => undefined,
|
|
335
|
+
set: () => {},
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Synthetic URL for the cron sweep's request-scoped db opts. Only the D1
|
|
340
|
+
* session adapter inspects `url` (for cookie `secure`), and cron doesn't take
|
|
341
|
+
* that path, so the value is never used — it exists to satisfy the contract.
|
|
342
|
+
*/
|
|
343
|
+
const CRON_EVENT_URL = new URL("https://cron.emdash.internal/");
|
|
283
344
|
|
|
284
345
|
/**
|
|
285
346
|
* Baseline security headers applied to all responses.
|
|
@@ -356,12 +417,12 @@ const SITEMAP_COLLECTION_RE = /^\/sitemap-[a-z][a-z0-9_]*\.xml$/;
|
|
|
356
417
|
*/
|
|
357
418
|
function createRequestScopedDb(
|
|
358
419
|
opts: RequestScopedDbOpts,
|
|
359
|
-
): { db: Kysely<Database>; commit: () => void } | null {
|
|
420
|
+
): { db: Kysely<Database>; commit: () => void; close?: () => void } | null {
|
|
360
421
|
if (typeof virtualCreateRequestScopedDb !== "function") return null;
|
|
361
422
|
// eslint-disable-next-line typescript/no-unsafe-type-assertion -- adapter returns Kysely<unknown>; cast to Database since core owns that type
|
|
362
423
|
const fn = virtualCreateRequestScopedDb as (
|
|
363
424
|
o: RequestScopedDbOpts,
|
|
364
|
-
) => { db: Kysely<Database>; commit: () => void } | null;
|
|
425
|
+
) => { db: Kysely<Database>; commit: () => void; close?: () => void } | null;
|
|
365
426
|
return fn(opts);
|
|
366
427
|
}
|
|
367
428
|
|
|
@@ -546,15 +607,10 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
546
607
|
.startsWith("text/html");
|
|
547
608
|
return runWithContext(ctx, async () => {
|
|
548
609
|
if (acceptsHtml) after(() => prefetchLayoutData());
|
|
549
|
-
// commit()
|
|
550
|
-
//
|
|
551
|
-
//
|
|
552
|
-
|
|
553
|
-
try {
|
|
554
|
-
return await runAnon();
|
|
555
|
-
} finally {
|
|
556
|
-
anonScoped.commit();
|
|
557
|
-
}
|
|
610
|
+
// commit() persists per-request state (e.g. the D1 bookmark cookie)
|
|
611
|
+
// before the response is returned, even if render throws; close()
|
|
612
|
+
// (connection teardown) is deferred to stream-end. See finishScoped.
|
|
613
|
+
return finishScoped(anonScoped, runAnon);
|
|
558
614
|
});
|
|
559
615
|
}
|
|
560
616
|
return runAnon();
|
|
@@ -658,7 +714,17 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
658
714
|
|
|
659
715
|
// Direct access (for advanced use cases)
|
|
660
716
|
storage: runtime.storage,
|
|
661
|
-
|
|
717
|
+
// Lazy getter, not an eager snapshot: `locals.emdash` is built
|
|
718
|
+
// before the per-request scoped db is installed in ALS, so reading
|
|
719
|
+
// `runtime.db` here would capture the per-isolate singleton. Routes
|
|
720
|
+
// access `emdash.db` later, during the request, when the scoped db
|
|
721
|
+
// is active. For a stateless binding (D1) the two are equivalent,
|
|
722
|
+
// but for a request-bound connection (pg/Hyperdrive) the singleton
|
|
723
|
+
// belongs to the cold-start request and reusing it from a warm
|
|
724
|
+
// request hangs on workerd's cross-request I/O guard.
|
|
725
|
+
get db() {
|
|
726
|
+
return runtime.db;
|
|
727
|
+
},
|
|
662
728
|
getPublicMediaUrl: createPublicMediaUrlResolver(runtime.storage),
|
|
663
729
|
hooks: runtime.hooks,
|
|
664
730
|
email: runtime.email,
|
|
@@ -722,16 +788,12 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
722
788
|
const ctx = parent
|
|
723
789
|
? { ...parent, db: scoped.db }
|
|
724
790
|
: { editMode: false, db: scoped.db, metrics };
|
|
725
|
-
return runWithContext(ctx,
|
|
726
|
-
// commit()
|
|
727
|
-
//
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
} finally {
|
|
732
|
-
scoped.commit();
|
|
733
|
-
}
|
|
734
|
-
});
|
|
791
|
+
return runWithContext(ctx, () =>
|
|
792
|
+
// commit() persists per-request state (e.g. the D1 bookmark cookie)
|
|
793
|
+
// before the response returns, even if render throws; close()
|
|
794
|
+
// (connection teardown) is deferred to stream-end. See finishScoped.
|
|
795
|
+
finishScoped(scoped, renderAndFinalize),
|
|
796
|
+
);
|
|
735
797
|
}
|
|
736
798
|
|
|
737
799
|
return renderAndFinalize();
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
* This page serves the EmDash admin React SPA.
|
|
6
6
|
* AdminWrapper imports plugin admin modules and passes them to AdminApp.
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
// `?url` keeps the admin stylesheet out of the page's CSS module graph so
|
|
9
|
+
// Astro's dev SSR doesn't inject it into every public route's <head>. We emit
|
|
10
|
+
// it as a route-scoped <link> below instead. See #1281.
|
|
11
|
+
import adminStylesUrl from "@emdash-cms/admin/styles.css?url";
|
|
9
12
|
// Use package-qualified import so Astro generates a proper module URL
|
|
10
13
|
// (relative imports resolve to absolute paths which break client hydration)
|
|
11
14
|
import AdminWrapper from "emdash/routes/PluginRegistry";
|
|
@@ -28,6 +31,7 @@ const pageTitle = adminConfig?.siteName ? `${adminConfig.siteName} Admin` : "EmD
|
|
|
28
31
|
<head>
|
|
29
32
|
<meta charset="UTF-8" />
|
|
30
33
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
34
|
+
<link rel="stylesheet" href={adminStylesUrl} />
|
|
31
35
|
<Font cssVariable="--font-emdash" />
|
|
32
36
|
{adminConfig?.favicon ? (
|
|
33
37
|
<link rel="icon" href={adminConfig.favicon} />
|
|
@@ -15,7 +15,7 @@ import { hashString } from "emdash";
|
|
|
15
15
|
import { requirePerm } from "#api/authorize.js";
|
|
16
16
|
import { apiSuccess, handleError, requireDb } from "#api/error.js";
|
|
17
17
|
import { SchemaRegistry } from "#schema/registry.js";
|
|
18
|
-
import { generateTypeScript } from "#schema/zod-generator.js";
|
|
18
|
+
import { generateTypeScript, uniqueInterfaceNames } from "#schema/zod-generator.js";
|
|
19
19
|
|
|
20
20
|
export const prerender = false;
|
|
21
21
|
|
|
@@ -45,8 +45,14 @@ export const GET: APIRoute = async ({ request, locals }) => {
|
|
|
45
45
|
const format = url.searchParams.get("format");
|
|
46
46
|
|
|
47
47
|
if (format === "typescript") {
|
|
48
|
-
// Generate TypeScript definitions
|
|
49
|
-
|
|
48
|
+
// Generate TypeScript definitions. Singularizing slugs can collapse
|
|
49
|
+
// distinct collections onto the same interface name, so resolve names
|
|
50
|
+
// up front to keep every emitted interface unique (`book` + `books`
|
|
51
|
+
// would otherwise both emit `Book`).
|
|
52
|
+
const interfaceNames = uniqueInterfaceNames(collectionsWithFields);
|
|
53
|
+
const types = collectionsWithFields
|
|
54
|
+
.map((c) => generateTypeScript(c, interfaceNames.get(c.slug)))
|
|
55
|
+
.join("\n\n");
|
|
50
56
|
|
|
51
57
|
const header = `// Generated by EmDash CLI
|
|
52
58
|
// Do not edit manually - run \`emdash types\` to regenerate
|
|
@@ -153,13 +153,16 @@ export async function exportSeed(db: Kysely<Database>, withContent?: string): Pr
|
|
|
153
153
|
|
|
154
154
|
// 7. Export content (if requested)
|
|
155
155
|
if (withContent !== undefined) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
156
|
+
// Treat "all" as a synonym for the bare flag and "true". The args help
|
|
157
|
+
// text documents `all` as a valid value, but without this the literal
|
|
158
|
+
// string is read as a collection name and matches no collection (#1329).
|
|
159
|
+
const includeAll = withContent === "" || withContent === "true" || withContent === "all";
|
|
160
|
+
const collections = includeAll
|
|
161
|
+
? null // all collections
|
|
162
|
+
: withContent
|
|
163
|
+
.split(",")
|
|
164
|
+
.map((s) => s.trim())
|
|
165
|
+
.filter(Boolean);
|
|
163
166
|
|
|
164
167
|
seed.content = await exportContent(
|
|
165
168
|
db,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* EmDash custom block override for `astro-portabletext`.
|
|
4
|
+
*
|
|
5
|
+
* Renders the same HTML as the upstream Block component (h1..h6, blockquote,
|
|
6
|
+
* `<p>` for `normal`) and additionally surfaces `textAlign` from the rich-text
|
|
7
|
+
* editor as a WordPress-style `has-text-align-{value}` class on the rendered
|
|
8
|
+
* paragraph or heading.
|
|
9
|
+
*
|
|
10
|
+
* `left` is the editor default and intentionally does not produce a class —
|
|
11
|
+
* paragraphs/headings without explicit alignment render exactly as they did
|
|
12
|
+
* before this fix, so existing content is byte-for-byte unchanged.
|
|
13
|
+
*
|
|
14
|
+
* Related: https://github.com/emdash-cms/emdash/issues/1201
|
|
15
|
+
*/
|
|
16
|
+
import type { BlockProps } from "astro-portabletext/types";
|
|
17
|
+
|
|
18
|
+
import { textAlignClassName } from "./portable-text-text-align.js";
|
|
19
|
+
|
|
20
|
+
type TextAlignedNode = BlockProps["node"] & { textAlign?: string };
|
|
21
|
+
type Props = Omit<BlockProps, "node"> & { node: TextAlignedNode };
|
|
22
|
+
|
|
23
|
+
const props = Astro.props as Props;
|
|
24
|
+
const { node, index: _index, isInline: _isInline, ...attrs } = props;
|
|
25
|
+
|
|
26
|
+
const styleIs = (style: string) => style === node.style;
|
|
27
|
+
|
|
28
|
+
// Allowlist-based; attacker-controlled PT data cannot inject arbitrary classes.
|
|
29
|
+
// See `./portable-text-text-align.ts`.
|
|
30
|
+
const alignClass = textAlignClassName(node.textAlign);
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
{
|
|
34
|
+
styleIs("h1") ? (
|
|
35
|
+
<h1 class={alignClass} {...attrs}>
|
|
36
|
+
<slot />
|
|
37
|
+
</h1>
|
|
38
|
+
) : styleIs("h2") ? (
|
|
39
|
+
<h2 class={alignClass} {...attrs}>
|
|
40
|
+
<slot />
|
|
41
|
+
</h2>
|
|
42
|
+
) : styleIs("h3") ? (
|
|
43
|
+
<h3 class={alignClass} {...attrs}>
|
|
44
|
+
<slot />
|
|
45
|
+
</h3>
|
|
46
|
+
) : styleIs("h4") ? (
|
|
47
|
+
<h4 class={alignClass} {...attrs}>
|
|
48
|
+
<slot />
|
|
49
|
+
</h4>
|
|
50
|
+
) : styleIs("h5") ? (
|
|
51
|
+
<h5 class={alignClass} {...attrs}>
|
|
52
|
+
<slot />
|
|
53
|
+
</h5>
|
|
54
|
+
) : styleIs("h6") ? (
|
|
55
|
+
<h6 class={alignClass} {...attrs}>
|
|
56
|
+
<slot />
|
|
57
|
+
</h6>
|
|
58
|
+
) : styleIs("blockquote") ? (
|
|
59
|
+
<blockquote class={alignClass} {...attrs}>
|
|
60
|
+
<slot />
|
|
61
|
+
</blockquote>
|
|
62
|
+
) : styleIs("normal") ? (
|
|
63
|
+
<p class={alignClass} {...attrs}>
|
|
64
|
+
<slot />
|
|
65
|
+
</p>
|
|
66
|
+
) : (
|
|
67
|
+
<p class={alignClass} {...attrs}>
|
|
68
|
+
<slot />
|
|
69
|
+
</p>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -158,9 +158,8 @@ const finalAlt = alt ?? (embed?.type === "image" ? embed.alt : undefined) ?? med
|
|
|
158
158
|
</audio>
|
|
159
159
|
)}
|
|
160
160
|
|
|
161
|
+
{/* Custom component embeds (Mux player, etc.) require dynamic import. Placeholder until that lands. */}
|
|
161
162
|
{embed?.type === "component" && (
|
|
162
|
-
<!-- Custom component embeds (Mux player, etc.) require dynamic import -->
|
|
163
|
-
<!-- For now, show a placeholder - full implementation TBD -->
|
|
164
163
|
<div class="emdash-media-component" data-package={embed.package} data-export={embed.export}>
|
|
165
164
|
<p>Custom media component: {embed.package}</p>
|
|
166
165
|
</div>
|
|
@@ -38,6 +38,8 @@ export interface Props {
|
|
|
38
38
|
displayWidth?: number;
|
|
39
39
|
/** Display height for this instance (overrides original) */
|
|
40
40
|
displayHeight?: number;
|
|
41
|
+
/** Alignment for this image instance (e.g. from a WordPress import) */
|
|
42
|
+
alignment?: "left" | "center" | "right" | "wide" | "full";
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -63,7 +65,7 @@ if (!node?.asset) {
|
|
|
63
65
|
return null;
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
const { asset, alt = "", caption, width, height, displayWidth, displayHeight } = node;
|
|
68
|
+
const { asset, alt = "", caption, width, height, displayWidth, displayHeight, alignment } = node;
|
|
67
69
|
|
|
68
70
|
// Calculate aspect ratio from original dimensions
|
|
69
71
|
const aspectRatio = width && height ? width / height : undefined;
|
|
@@ -168,7 +170,7 @@ const baseStyle = aspectRatio
|
|
|
168
170
|
const imgStyle = placeholderStyle ? `${baseStyle} ${placeholderStyle}` : baseStyle;
|
|
169
171
|
---
|
|
170
172
|
|
|
171
|
-
<figure class="emdash-image">
|
|
173
|
+
<figure class:list={["emdash-image", alignment && `emdash-image--align-${alignment}`]}>
|
|
172
174
|
<img
|
|
173
175
|
src={src}
|
|
174
176
|
srcset={srcset}
|
|
@@ -197,4 +199,36 @@ const imgStyle = placeholderStyle ? `${baseStyle} ${placeholderStyle}` : baseSty
|
|
|
197
199
|
margin-top: 0.5rem;
|
|
198
200
|
text-align: center;
|
|
199
201
|
}
|
|
202
|
+
/* Alignment (WordPress-style). Floats wrap surrounding text; wide/full span the column. */
|
|
203
|
+
.emdash-image--align-left,
|
|
204
|
+
.emdash-image--align-right {
|
|
205
|
+
width: fit-content;
|
|
206
|
+
max-width: 50%;
|
|
207
|
+
}
|
|
208
|
+
.emdash-image--align-left {
|
|
209
|
+
float: left;
|
|
210
|
+
margin: 0 1.5rem 1rem 0;
|
|
211
|
+
}
|
|
212
|
+
.emdash-image--align-right {
|
|
213
|
+
float: right;
|
|
214
|
+
margin: 0 0 1rem 1.5rem;
|
|
215
|
+
}
|
|
216
|
+
.emdash-image--align-center {
|
|
217
|
+
width: fit-content;
|
|
218
|
+
margin-inline: auto;
|
|
219
|
+
}
|
|
220
|
+
.emdash-image--align-wide,
|
|
221
|
+
.emdash-image--align-full {
|
|
222
|
+
width: 100%;
|
|
223
|
+
}
|
|
224
|
+
/* On narrow screens, un-float left/right so the image is not cramped beside text. */
|
|
225
|
+
@media (max-width: 640px) {
|
|
226
|
+
.emdash-image--align-left,
|
|
227
|
+
.emdash-image--align-right {
|
|
228
|
+
float: none;
|
|
229
|
+
width: 100%;
|
|
230
|
+
max-width: 100%;
|
|
231
|
+
margin-inline: 0;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
200
234
|
</style>
|
|
@@ -51,6 +51,7 @@ interface PTTextBlock {
|
|
|
51
51
|
level?: number;
|
|
52
52
|
children: PTSpan[];
|
|
53
53
|
markDefs?: PTMarkDef[];
|
|
54
|
+
textAlign?: "left" | "center" | "right" | "justify";
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
type PTBlock = PTTextBlock | { _type: string; _key: string; [key: string]: unknown };
|
|
@@ -119,12 +120,15 @@ function convertPMNode(node: PMNode): PTBlock | PTBlock[] | null {
|
|
|
119
120
|
case "paragraph": {
|
|
120
121
|
const { children, markDefs } = convertInline(node.content || []);
|
|
121
122
|
if (children.length === 0) return null;
|
|
123
|
+
const ta = node.attrs?.textAlign;
|
|
124
|
+
const textAlign = ta === "center" || ta === "right" || ta === "justify" ? ta : undefined;
|
|
122
125
|
return {
|
|
123
126
|
_type: "block",
|
|
124
127
|
_key: k(),
|
|
125
128
|
style: "normal",
|
|
126
129
|
children,
|
|
127
130
|
markDefs: markDefs.length > 0 ? markDefs : undefined,
|
|
131
|
+
...(textAlign ? { textAlign } : {}),
|
|
128
132
|
};
|
|
129
133
|
}
|
|
130
134
|
case "heading": {
|
|
@@ -140,12 +144,15 @@ function convertPMNode(node: PMNode): PTBlock | PTBlock[] | null {
|
|
|
140
144
|
6: "h6",
|
|
141
145
|
};
|
|
142
146
|
const headingStyle = headingStyles[level] ?? "h1";
|
|
147
|
+
const ta = node.attrs?.textAlign;
|
|
148
|
+
const textAlign = ta === "center" || ta === "right" || ta === "justify" ? ta : undefined;
|
|
143
149
|
return {
|
|
144
150
|
_type: "block",
|
|
145
151
|
_key: k(),
|
|
146
152
|
style: headingStyle,
|
|
147
153
|
children,
|
|
148
154
|
markDefs: markDefs.length > 0 ? markDefs : undefined,
|
|
155
|
+
...(textAlign ? { textAlign } : {}),
|
|
149
156
|
};
|
|
150
157
|
}
|
|
151
158
|
case "bulletList":
|
|
@@ -361,7 +368,7 @@ function portableTextToPM(blocks: PTBlock[]): JSONContent {
|
|
|
361
368
|
|
|
362
369
|
function convertPTBlock(block: PTBlock): JSONContent | null {
|
|
363
370
|
if (isPTTextBlock(block)) {
|
|
364
|
-
const { style = "normal", children, markDefs = [] } = block;
|
|
371
|
+
const { style = "normal", children, markDefs = [], textAlign } = block;
|
|
365
372
|
const pmContent = convertPTSpans(children, markDefs);
|
|
366
373
|
|
|
367
374
|
if (style === "blockquote") {
|
|
@@ -379,12 +386,13 @@ function convertPTBlock(block: PTBlock): JSONContent | null {
|
|
|
379
386
|
const level = parseInt(style.substring(1), 10);
|
|
380
387
|
return {
|
|
381
388
|
type: "heading",
|
|
382
|
-
attrs: { level },
|
|
389
|
+
attrs: { level, ...(textAlign ? { textAlign } : {}) },
|
|
383
390
|
content: pmContent.length > 0 ? pmContent : undefined,
|
|
384
391
|
};
|
|
385
392
|
}
|
|
386
393
|
return {
|
|
387
394
|
type: "paragraph",
|
|
395
|
+
attrs: textAlign ? { textAlign } : undefined,
|
|
388
396
|
content: pmContent.length > 0 ? pmContent : undefined,
|
|
389
397
|
};
|
|
390
398
|
}
|