emdash 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-C5AWLJSD.d.mts → adapters-BzIHV3sw.d.mts} +1 -1
- package/dist/{adapters-C5AWLJSD.d.mts.map → adapters-BzIHV3sw.d.mts.map} +1 -1
- package/dist/{allowed-origins-CyYLEJkp.mjs → allowed-origins-B1u7Qnvg.mjs} +2 -2
- package/dist/{allowed-origins-CyYLEJkp.mjs.map → allowed-origins-B1u7Qnvg.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +15 -15
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-Cs7DAACP.mjs → api-DStv36ik.mjs} +123 -20
- package/dist/api-DStv36ik.mjs.map +1 -0
- package/dist/{api-tokens-VrXNiNvV.mjs → api-tokens-DPfhPu5V.mjs} +2 -2
- package/dist/{api-tokens-VrXNiNvV.mjs.map → api-tokens-DPfhPu5V.mjs.map} +1 -1
- package/dist/{apply-BWMV4Zmw.mjs → apply-Dr7snAMT.mjs} +23 -23
- package/dist/apply-Dr7snAMT.mjs.map +1 -0
- package/dist/astro/index.d.mts +10 -10
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +115 -25
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +9 -9
- package/dist/astro/middleware/auth.mjs +6 -6
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware/request-context.mjs +2 -2
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +26 -4
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +242 -259
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +5 -5
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +5 -5
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +5 -5
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +8 -8
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +8 -8
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +12 -12
- package/dist/astro/routes/api/admin/bylines/index.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +11 -11
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +8 -8
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +8 -8
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +5 -5
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +33 -33
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/updates.mjs +33 -33
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +33 -33
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +33 -33
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
- package/dist/astro/routes/api/admin/users/index.mjs +5 -5
- package/dist/astro/routes/api/auth/dev-bypass.mjs +5 -5
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
- package/dist/astro/routes/api/auth/invite/register-options.mjs +9 -9
- package/dist/astro/routes/api/auth/logout.mjs +3 -3
- package/dist/astro/routes/api/auth/magic-link/send.mjs +8 -8
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +6 -6
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +4 -4
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +5 -5
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +9 -9
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/request.mjs +8 -8
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +9 -8
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.d.mts.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +12 -10
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +11 -11
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +9 -8
- package/dist/astro/routes/api/content/_collection_/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/content/_collection_/authors.d.mts +8 -0
- package/dist/astro/routes/api/content/_collection_/authors.d.mts.map +1 -0
- package/dist/astro/routes/api/content/_collection_/authors.mjs +19 -0
- package/dist/astro/routes/api/content/_collection_/authors.mjs.map +1 -0
- package/dist/astro/routes/api/content/_collection_/index.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/trash.mjs +6 -6
- package/dist/astro/routes/api/dashboard.mjs +7 -7
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/media.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +9 -9
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/mcp.mjs +29 -29
- package/dist/astro/routes/api/media/_id_/confirm.mjs +6 -6
- package/dist/astro/routes/api/media/_id_.mjs +6 -6
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.mjs +7 -7
- package/dist/astro/routes/api/media.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_/items.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_/translations.mjs +7 -7
- package/dist/astro/routes/api/menus/_name_.mjs +7 -7
- package/dist/astro/routes/api/menus/index.mjs +7 -7
- package/dist/astro/routes/api/oauth/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +8 -8
- package/dist/astro/routes/api/oauth/device/token.mjs +7 -7
- package/dist/astro/routes/api/oauth/register.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +17 -3
- package/dist/astro/routes/api/openapi.json.mjs.map +1 -1
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
- package/dist/astro/routes/api/redirects/404s/index.mjs +9 -9
- package/dist/astro/routes/api/redirects/404s/summary.mjs +9 -9
- package/dist/astro/routes/api/redirects/_id_.mjs +10 -10
- package/dist/astro/routes/api/redirects/index.mjs +10 -10
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +3 -3
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +3 -3
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +33 -33
- package/dist/astro/routes/api/schema/collections/index.mjs +33 -33
- package/dist/astro/routes/api/schema/index.mjs +9 -14
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +33 -33
- package/dist/astro/routes/api/schema/orphans/index.mjs +33 -33
- package/dist/astro/routes/api/search/enable.mjs +9 -9
- package/dist/astro/routes/api/search/index.mjs +8 -8
- package/dist/astro/routes/api/search/rebuild.mjs +9 -9
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +8 -8
- package/dist/astro/routes/api/sections/_slug_.mjs +8 -8
- package/dist/astro/routes/api/sections/index.mjs +8 -8
- package/dist/astro/routes/api/settings/email.mjs +5 -5
- package/dist/astro/routes/api/settings.mjs +12 -12
- package/dist/astro/routes/api/setup/admin-verify.mjs +11 -11
- package/dist/astro/routes/api/setup/admin.mjs +10 -10
- package/dist/astro/routes/api/setup/dev-bypass.mjs +23 -23
- package/dist/astro/routes/api/setup/dev-reset.mjs +3 -3
- package/dist/astro/routes/api/setup/index.mjs +23 -23
- package/dist/astro/routes/api/setup/status.mjs +4 -4
- package/dist/astro/routes/api/snapshot.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +11 -11
- package/dist/astro/routes/api/taxonomies/index.mjs +11 -11
- package/dist/astro/routes/api/themes/preview.mjs +6 -6
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +2 -2
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +9 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +9 -8
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -5
- package/dist/astro/routes/api/widget-areas/_name_.mjs.map +1 -1
- package/dist/astro/routes/api/widget-areas/index.mjs +9 -8
- package/dist/astro/routes/api/widget-areas/index.mjs.map +1 -1
- package/dist/astro/routes/api/widget-components.mjs +3 -3
- package/dist/astro/routes/robots.txt.mjs +7 -7
- package/dist/astro/routes/sitemap-_collection_.xml.d.mts.map +1 -1
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +16 -9
- package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
- package/dist/astro/routes/sitemap.xml.mjs +8 -8
- package/dist/astro/types.d.mts +19 -12
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-CotM4Yiu.mjs → authorize-DsMSVSaY.mjs} +2 -2
- package/dist/{authorize-CotM4Yiu.mjs.map → authorize-DsMSVSaY.mjs.map} +1 -1
- package/dist/{byline-CWQ9aSoz.mjs → byline-DUx48sJp.mjs} +6 -6
- package/dist/{byline-CWQ9aSoz.mjs.map → byline-DUx48sJp.mjs.map} +1 -1
- package/dist/{byline-fields-DC3Wkk-U.mjs → byline-fields--WxSNS79.mjs} +2 -2
- package/dist/{byline-fields-DC3Wkk-U.mjs.map → byline-fields--WxSNS79.mjs.map} +1 -1
- package/dist/{byline-fields-Dr-xcb6S.mjs → byline-fields-8TMtkBnH.mjs} +3 -3
- package/dist/{byline-fields-Dr-xcb6S.mjs.map → byline-fields-8TMtkBnH.mjs.map} +1 -1
- package/dist/{byline-fields-BNy7Ng1U.d.mts → byline-fields-DbibsvTl.d.mts} +30 -2
- package/dist/byline-fields-DbibsvTl.d.mts.map +1 -0
- package/dist/{byline-registry-CxK5g559.mjs → byline-registry-CWP7I71B.mjs} +3 -3
- package/dist/{byline-registry-CxK5g559.mjs.map → byline-registry-CWP7I71B.mjs.map} +1 -1
- package/dist/{bylines-LJMgENMI.mjs → bylines-BdxWCnPL.mjs} +3 -3
- package/dist/{bylines-LJMgENMI.mjs.map → bylines-BdxWCnPL.mjs.map} +1 -1
- package/dist/{bylines-BJSva1Un.mjs → bylines-s8c2DXbH.mjs} +50 -6
- package/dist/{bylines-BJSva1Un.mjs.map → bylines-s8c2DXbH.mjs.map} +1 -1
- package/dist/{cache-lZL7SgVb.mjs → cache-B_HzASVT.mjs} +3 -3
- package/dist/{cache-lZL7SgVb.mjs.map → cache-B_HzASVT.mjs.map} +1 -1
- package/dist/{challenge-store-DGwuCc4R.mjs → challenge-store-DXX3rfdI.mjs} +1 -1
- package/dist/{challenge-store-DGwuCc4R.mjs.map → challenge-store-DXX3rfdI.mjs.map} +1 -1
- package/dist/{chunks-BU-vP9Dh.mjs → chunks-BerYVuve.mjs} +2 -2
- package/dist/{chunks-BU-vP9Dh.mjs.map → chunks-BerYVuve.mjs.map} +1 -1
- package/dist/cli/index.mjs +46 -32
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/{comment-C4jVbCM8.mjs → comment-sqQxNpN3.mjs} +2 -2
- package/dist/{comment-C4jVbCM8.mjs.map → comment-sqQxNpN3.mjs.map} +1 -1
- package/dist/{comments-BTAbC0Ek.mjs → comments-Vkivawyl.mjs} +3 -3
- package/dist/{comments-BTAbC0Ek.mjs.map → comments-Vkivawyl.mjs.map} +1 -1
- package/dist/{components-CTfpu3PZ.mjs → components-CK0cuUoH.mjs} +1 -1
- package/dist/{components-CTfpu3PZ.mjs.map → components-CK0cuUoH.mjs.map} +1 -1
- package/dist/{content-CyqOmOzm.mjs → content-BIlVx-RX.mjs} +132 -43
- package/dist/content-BIlVx-RX.mjs.map +1 -0
- package/dist/{context-DZ7bEh5-.mjs → context-Y7BRkWes.mjs} +10 -10
- package/dist/{context-DZ7bEh5-.mjs.map → context-Y7BRkWes.mjs.map} +1 -1
- package/dist/{cron-DZovZUnC.mjs → cron-BJ2ClIlj.mjs} +4 -3
- package/dist/cron-BJ2ClIlj.mjs.map +1 -0
- package/dist/{dashboard-B5WQpNTP.mjs → dashboard-2JgAMWxK.mjs} +4 -4
- package/dist/{dashboard-B5WQpNTP.mjs.map → dashboard-2JgAMWxK.mjs.map} +1 -1
- package/dist/database/instrumentation.d.mts +10 -1
- package/dist/database/instrumentation.d.mts.map +1 -1
- package/dist/database/instrumentation.mjs +13 -1
- package/dist/database/instrumentation.mjs.map +1 -1
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{default-xLFNSsZ9.mjs → default-IlBaTFxM.mjs} +1 -1
- package/dist/{default-xLFNSsZ9.mjs.map → default-IlBaTFxM.mjs.map} +1 -1
- package/dist/{device-flow-ptLrVINd.mjs → device-flow-R23SIbQ2.mjs} +5 -5
- package/dist/{device-flow-ptLrVINd.mjs.map → device-flow-R23SIbQ2.mjs.map} +1 -1
- package/dist/{error-DJOsMVSt.mjs → error-RwM4dD35.mjs} +2 -2
- package/dist/{error-DJOsMVSt.mjs.map → error-RwM4dD35.mjs.map} +1 -1
- package/dist/{escape-bIyGoW5W.mjs → escape-Ds07EEyu.mjs} +1 -1
- package/dist/{escape-bIyGoW5W.mjs.map → escape-Ds07EEyu.mjs.map} +1 -1
- package/dist/{fts-manager-DR1ERA0c.mjs → fts-manager-1RgHmopc.mjs} +2 -2
- package/dist/{fts-manager-DR1ERA0c.mjs.map → fts-manager-1RgHmopc.mjs.map} +1 -1
- package/dist/{index-CjKdMZ3U.d.mts → index-B1keaX5Y.d.mts} +237 -24
- package/dist/index-B1keaX5Y.d.mts.map +1 -0
- package/dist/{index-D60_SzHG.d.mts → index-DR56od45.d.mts} +3 -3
- package/dist/{index-D60_SzHG.d.mts.map → index-DR56od45.d.mts.map} +1 -1
- package/dist/index.d.mts +17 -17
- package/dist/index.mjs +46 -46
- package/dist/{load-6ZrRhepW.mjs → load-BBetCvLC.mjs} +2 -2
- package/dist/{load-6ZrRhepW.mjs.map → load-BBetCvLC.mjs.map} +1 -1
- package/dist/{loader-Dyx8dhFV.mjs → loader-ZN1ll-d-.mjs} +36 -37
- package/dist/loader-ZN1ll-d-.mjs.map +1 -0
- package/dist/{manifest-schema-Cj-YrzrF.mjs → manifest-schema-BtwbL_vj.mjs} +55 -2
- package/dist/manifest-schema-BtwbL_vj.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +11 -11
- package/dist/media/local-runtime.mjs +6 -6
- package/dist/{media-C-oovGCG.mjs → media-JOf3pNkw.mjs} +2 -2
- package/dist/{media-C-oovGCG.mjs.map → media-JOf3pNkw.mjs.map} +1 -1
- package/dist/{media-allowlist-CMcoYIjQ.mjs → media-allowlist-Dknq-OFY.mjs} +1 -1
- package/dist/{media-allowlist-CMcoYIjQ.mjs.map → media-allowlist-Dknq-OFY.mjs.map} +1 -1
- package/dist/media-url-VClf8glU.mjs +26 -0
- package/dist/media-url-VClf8glU.mjs.map +1 -0
- package/dist/{menus-DugoYwTX.mjs → menus-DX4_E01q.mjs} +3 -3
- package/dist/{menus-DugoYwTX.mjs.map → menus-DX4_E01q.mjs.map} +1 -1
- package/dist/{menus-BKkxXCmd.mjs → menus-DrQLusqj.mjs} +87 -37
- package/dist/menus-DrQLusqj.mjs.map +1 -0
- package/dist/{mode-BjlXswIw.mjs → mode-CO2vQHfq.mjs} +1 -1
- package/dist/{mode-BjlXswIw.mjs.map → mode-CO2vQHfq.mjs.map} +1 -1
- package/dist/{normalize-DVV8nbrL.mjs → normalize-CK5o04zr.mjs} +2 -2
- package/dist/{normalize-DVV8nbrL.mjs.map → normalize-CK5o04zr.mjs.map} +1 -1
- package/dist/{oauth-authorization-DvBAL75d.mjs → oauth-authorization-Bw4NdF_S.mjs} +5 -5
- package/dist/{oauth-authorization-DvBAL75d.mjs.map → oauth-authorization-Bw4NdF_S.mjs.map} +1 -1
- package/dist/{oauth-clients-8mPDStMv.mjs → oauth-clients-BGGFp57s.mjs} +1 -1
- package/dist/{oauth-clients-8mPDStMv.mjs.map → oauth-clients-BGGFp57s.mjs.map} +1 -1
- package/dist/{oauth-state-store-BJ7YtrfD.mjs → oauth-state-store-97x0xtN2.mjs} +1 -1
- package/dist/{oauth-state-store-BJ7YtrfD.mjs.map → oauth-state-store-97x0xtN2.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-BdDSDvjF.mjs → oauth-user-lookup-B_vnZHKO.mjs} +1 -1
- package/dist/{oauth-user-lookup-BdDSDvjF.mjs.map → oauth-user-lookup-B_vnZHKO.mjs.map} +1 -1
- package/dist/{options-BL4X94qY.mjs → options-BPCVnesz.mjs} +1 -1
- package/dist/{options-BL4X94qY.mjs.map → options-BPCVnesz.mjs.map} +1 -1
- package/dist/{options-tb7DJROi.d.mts → options-DyYIYpPd.d.mts} +3 -3
- package/dist/{options-tb7DJROi.d.mts.map → options-DyYIYpPd.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-BBkFmLVr.mjs → parse-CrGndy1A.mjs} +2 -2
- package/dist/{parse-BBkFmLVr.mjs.map → parse-CrGndy1A.mjs.map} +1 -1
- package/dist/{passkey-config-BDVM86Tj.mjs → passkey-config-C3QgnQnU.mjs} +1 -1
- package/dist/{passkey-config-BDVM86Tj.mjs.map → passkey-config-C3QgnQnU.mjs.map} +1 -1
- package/dist/{patterns-CqG5Ya3i.mjs → patterns-p-RBdTbM.mjs} +1 -1
- package/dist/{patterns-CqG5Ya3i.mjs.map → patterns-p-RBdTbM.mjs.map} +1 -1
- package/dist/{placeholder-B9lUUEmj.d.mts → placeholder-CVBv5z8k.d.mts} +1 -1
- package/dist/{placeholder-B9lUUEmj.d.mts.map → placeholder-CVBv5z8k.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{public-url-egRHCy1m.mjs → public-url-BFVC2OTJ.mjs} +1 -1
- package/dist/{public-url-egRHCy1m.mjs.map → public-url-BFVC2OTJ.mjs.map} +1 -1
- package/dist/{query-Ctlq1aOk.mjs → query-CbUcI4Xk.mjs} +33 -17
- package/dist/query-CbUcI4Xk.mjs.map +1 -0
- package/dist/{rate-limit-CH6W6ikK.mjs → rate-limit-C7hjdkS5.mjs} +2 -2
- package/dist/{rate-limit-CH6W6ikK.mjs.map → rate-limit-C7hjdkS5.mjs.map} +1 -1
- package/dist/{redirect-Cw3JTlmj.mjs → redirect-B_q19j4v.mjs} +1 -1
- package/dist/{redirect-Cw3JTlmj.mjs.map → redirect-B_q19j4v.mjs.map} +1 -1
- package/dist/{redirect-C6tJA7tk.mjs → redirect-CRWIt8Zj.mjs} +3 -3
- package/dist/{redirect-C6tJA7tk.mjs.map → redirect-CRWIt8Zj.mjs.map} +1 -1
- package/dist/{redirects-C0L9JUk4.mjs → redirects-CCbCqCCd.mjs} +28 -4
- package/dist/redirects-CCbCqCCd.mjs.map +1 -0
- package/dist/{redirects-CacE9eQa.mjs → redirects-DxVoR7PI.mjs} +5 -5
- package/dist/{redirects-CacE9eQa.mjs.map → redirects-DxVoR7PI.mjs.map} +1 -1
- package/dist/{registry-CIDxZbhh.mjs → registry-brYh-rAT.mjs} +6 -6
- package/dist/{registry-CIDxZbhh.mjs.map → registry-brYh-rAT.mjs.map} +1 -1
- package/dist/{request-cache-BYMs-BGX.mjs → request-cache-D32LpnmI.mjs} +1 -1
- package/dist/{request-cache-BYMs-BGX.mjs.map → request-cache-D32LpnmI.mjs.map} +1 -1
- package/dist/request-context.d.mts +7 -0
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs +2 -1
- package/dist/request-context.mjs.map +1 -1
- package/dist/{runner-pt6Wl-l-.mjs → runner--4wMWwKM.mjs} +217 -166
- package/dist/runner--4wMWwKM.mjs.map +1 -0
- package/dist/{runner-DM1yR5qd.d.mts → runner-DTdhuI9i.d.mts} +2 -2
- package/dist/{runner-DM1yR5qd.d.mts.map → runner-DTdhuI9i.d.mts.map} +1 -1
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +2 -2
- package/dist/{schema-B4tk0HAG.mjs → schema-C1E70ug_.mjs} +5 -5
- package/dist/{schema-B4tk0HAG.mjs.map → schema-C1E70ug_.mjs.map} +1 -1
- package/dist/{search-f-fNfwab.mjs → search-B3SGZw91.mjs} +4 -4
- package/dist/{search-f-fNfwab.mjs.map → search-B3SGZw91.mjs.map} +1 -1
- package/dist/{secrets-YYbTgB1w.mjs → secrets-ChPTmy9x.mjs} +2 -2
- package/dist/{secrets-YYbTgB1w.mjs.map → secrets-ChPTmy9x.mjs.map} +1 -1
- package/dist/{sections-biElLfT9.mjs → sections-D_lVzwRZ.mjs} +3 -3
- package/dist/{sections-biElLfT9.mjs.map → sections-D_lVzwRZ.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +17 -17
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.d.mts.map +1 -1
- package/dist/seo/index.mjs +3 -12
- package/dist/seo/index.mjs.map +1 -1
- package/dist/{seo-BR39kvTF.mjs → seo-B5e6y9Wk.mjs} +2 -2
- package/dist/{seo-BR39kvTF.mjs.map → seo-B5e6y9Wk.mjs.map} +1 -1
- package/dist/{seo-DfjLvu8i.mjs → seo-D_LPkOtu.mjs} +4 -3
- package/dist/seo-D_LPkOtu.mjs.map +1 -0
- package/dist/{service-BhR2acnc.mjs → service-ChDcsTBs.mjs} +3 -3
- package/dist/{service-BhR2acnc.mjs.map → service-ChDcsTBs.mjs.map} +1 -1
- package/dist/{settings-D_NJvjgN.mjs → settings-Cv47v9u8.mjs} +3 -3
- package/dist/{settings-D_NJvjgN.mjs.map → settings-Cv47v9u8.mjs.map} +1 -1
- package/dist/settings-DfxiWY_s.mjs +411 -0
- package/dist/settings-DfxiWY_s.mjs.map +1 -0
- package/dist/{setup-complete-VoEZfasi.mjs → setup-complete-yvPE4OsP.mjs} +2 -2
- package/dist/{setup-complete-VoEZfasi.mjs.map → setup-complete-yvPE4OsP.mjs.map} +1 -1
- package/dist/{setup-nonce-Bm0uKqmf.mjs → setup-nonce-C9aFzb94.mjs} +1 -1
- package/dist/{setup-nonce-Bm0uKqmf.mjs.map → setup-nonce-C9aFzb94.mjs.map} +1 -1
- package/dist/{site-url-Cm8-sJy7.mjs → site-url-CnHlmAs9.mjs} +2 -2
- package/dist/{site-url-Cm8-sJy7.mjs.map → site-url-CnHlmAs9.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/{taxonomies-Mhn9rjTQ.mjs → taxonomies-BILwiyGk.mjs} +4 -4
- package/dist/{taxonomies-Mhn9rjTQ.mjs.map → taxonomies-BILwiyGk.mjs.map} +1 -1
- package/dist/{taxonomies-Crtzy4MT.mjs → taxonomies-BdAmbOwx.mjs} +50 -12
- package/dist/taxonomies-BdAmbOwx.mjs.map +1 -0
- package/dist/{taxonomy-DTZrIQpi.mjs → taxonomy-CdllE4oq.mjs} +3 -3
- package/dist/{taxonomy-DTZrIQpi.mjs.map → taxonomy-CdllE4oq.mjs.map} +1 -1
- package/dist/{transaction-NQj4VJ7Z.mjs → transaction-x2tJQ-A1.mjs} +1 -1
- package/dist/{transaction-NQj4VJ7Z.mjs.map → transaction-x2tJQ-A1.mjs.map} +1 -1
- package/dist/{transport-OnMNbsIA.d.mts → transport-B7PPP2CC.d.mts} +1 -1
- package/dist/{transport-OnMNbsIA.d.mts.map → transport-B7PPP2CC.d.mts.map} +1 -1
- package/dist/{transport--Ck3RBin.mjs → transport-CmpLD7W3.mjs} +1 -1
- package/dist/{transport--Ck3RBin.mjs.map → transport-CmpLD7W3.mjs.map} +1 -1
- package/dist/{types-DWnN7weG.d.mts → types-BFgrqwSk.d.mts} +1 -1
- package/dist/{types-DWnN7weG.d.mts.map → types-BFgrqwSk.d.mts.map} +1 -1
- package/dist/{types-Qa7-HJJC.d.mts → types-BH8-30hc.d.mts} +1 -1
- package/dist/{types-Qa7-HJJC.d.mts.map → types-BH8-30hc.d.mts.map} +1 -1
- package/dist/{types-DawhLFwy.d.mts → types-BPzXTV9x.d.mts} +26 -1
- package/dist/{types-DawhLFwy.d.mts.map → types-BPzXTV9x.d.mts.map} +1 -1
- package/dist/{types-DbCWhHet.d.mts → types-BUUVn1zr.d.mts} +2 -2
- package/dist/types-BUUVn1zr.d.mts.map +1 -0
- package/dist/{types-K3MDsxpy.mjs → types-BXSUSAjt.mjs} +16 -3
- package/dist/{types-K3MDsxpy.mjs.map → types-BXSUSAjt.mjs.map} +1 -1
- package/dist/{types-DMwSpvcw.d.mts → types-CPAPl93j.d.mts} +9 -3
- package/dist/{types-DMwSpvcw.d.mts.map → types-CPAPl93j.d.mts.map} +1 -1
- package/dist/types-CZI4E3qG.mjs +3 -0
- package/dist/{types-kwqCOUxj.d.mts → types-D4kUqbHh.d.mts} +1 -1
- package/dist/{types-kwqCOUxj.d.mts.map → types-D4kUqbHh.d.mts.map} +1 -1
- package/dist/{types-i8_uzhMD.d.mts → types-DTniiNto.d.mts} +19 -4
- package/dist/types-DTniiNto.d.mts.map +1 -0
- package/dist/{types-D8bhH891.mjs → types-DZk_y-MU.mjs} +1 -1
- package/dist/types-DZk_y-MU.mjs.map +1 -0
- package/dist/{types-DX6v9KzJ.d.mts → types-S15DXXNi.d.mts} +1 -1
- package/dist/{types-DX6v9KzJ.d.mts.map → types-S15DXXNi.d.mts.map} +1 -1
- package/dist/{user-DzEUl5zA.mjs → user-C0um7wrg.mjs} +18 -2
- package/dist/user-C0um7wrg.mjs.map +1 -0
- package/dist/{validate-JCXcsqiY.mjs → validate-Bz4vqcX1.mjs} +6 -3
- package/dist/validate-Bz4vqcX1.mjs.map +1 -0
- package/dist/{validate-Dy6nkNls.d.mts → validate-CNwkPWzz.d.mts} +13 -5
- package/dist/validate-CNwkPWzz.d.mts.map +1 -0
- package/dist/{validation-Bq-VyKJg.mjs → validation-DgGTJm3u.mjs} +5 -5
- package/dist/{validation-Bq-VyKJg.mjs.map → validation-DgGTJm3u.mjs.map} +1 -1
- package/dist/version-D-5txk2m.mjs +7 -0
- package/dist/{version-CnS-Cr8A.mjs.map → version-D-5txk2m.mjs.map} +1 -1
- package/dist/{widgets-Bap1eS1X.mjs → widgets-DZfmAbE4.mjs} +47 -44
- package/dist/widgets-DZfmAbE4.mjs.map +1 -0
- package/dist/{zod-generator-BSDpkqSH.mjs → zod-generator-Djo_VHCt.mjs} +2 -2
- package/dist/{zod-generator-BSDpkqSH.mjs.map → zod-generator-Djo_VHCt.mjs.map} +1 -1
- package/package.json +10 -10
- package/src/api/handlers/content.ts +107 -8
- package/src/api/handlers/index.ts +2 -0
- package/src/api/handlers/marketplace.ts +2 -5
- package/src/api/handlers/registry.ts +70 -0
- package/src/api/handlers/seo.ts +9 -1
- package/src/api/openapi/document.ts +25 -0
- package/src/api/schemas/content.ts +33 -0
- package/src/api/schemas/schema.ts +13 -1
- package/src/astro/integration/index.ts +98 -0
- package/src/astro/integration/routes.ts +6 -0
- package/src/astro/integration/virtual-modules.ts +39 -0
- package/src/astro/integration/vite-config.ts +12 -0
- package/src/astro/middleware.ts +48 -6
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +8 -4
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id].ts +4 -2
- package/src/astro/routes/api/content/[collection]/authors.ts +34 -0
- package/src/astro/routes/api/schema/index.ts +7 -15
- package/src/astro/routes/sitemap-[collection].xml.ts +13 -2
- package/src/astro/types.ts +8 -1
- package/src/bylines/index.ts +57 -0
- package/src/cli/commands/bundle-utils.ts +2 -0
- package/src/cli/commands/export-seed.ts +28 -12
- package/src/cli/commands/secrets.ts +2 -2
- package/src/components/EmDashImage.astro +22 -4
- package/src/components/Image.astro +20 -3
- package/src/database/instrumentation.ts +13 -0
- package/src/database/migrations/043_content_references.ts +121 -0
- package/src/database/migrations/runner.ts +2 -0
- package/src/database/repositories/content.ts +225 -67
- package/src/database/repositories/index.ts +7 -0
- package/src/database/repositories/relation.ts +467 -0
- package/src/database/repositories/types.ts +31 -0
- package/src/database/repositories/user.ts +18 -0
- package/src/database/types.ts +34 -0
- package/src/emdash-runtime.ts +172 -67
- package/src/index.ts +8 -1
- package/src/loader.ts +81 -39
- package/src/media/responsive.ts +125 -0
- package/src/plugins/cron.ts +3 -2
- package/src/plugins/index.ts +5 -0
- package/src/plugins/manifest-schema.ts +75 -0
- package/src/plugins/marketplace.ts +2 -5
- package/src/plugins/scheduler/node.ts +9 -2
- package/src/plugins/types.ts +12 -0
- package/src/query.ts +45 -7
- package/src/request-context.ts +8 -0
- package/src/scheduled-publish.ts +153 -0
- package/src/schema/types.ts +11 -1
- package/src/seed/apply.ts +16 -6
- package/src/seed/types.ts +9 -0
- package/src/seed/validate.ts +15 -0
- package/src/seo/index.ts +2 -28
- package/src/seo/media-url.ts +32 -0
- package/src/settings/index.ts +32 -40
- package/src/taxonomies/index.ts +79 -12
- package/src/utils/isolate-cache.ts +189 -0
- package/src/virtual-modules.d.ts +11 -0
- package/src/widgets/index.ts +57 -54
- package/dist/api-Cs7DAACP.mjs.map +0 -1
- package/dist/apply-BWMV4Zmw.mjs.map +0 -1
- package/dist/byline-fields-BNy7Ng1U.d.mts.map +0 -1
- package/dist/content-CyqOmOzm.mjs.map +0 -1
- package/dist/cron-DZovZUnC.mjs.map +0 -1
- package/dist/index-CjKdMZ3U.d.mts.map +0 -1
- package/dist/loader-Dyx8dhFV.mjs.map +0 -1
- package/dist/manifest-schema-Cj-YrzrF.mjs.map +0 -1
- package/dist/menus-BKkxXCmd.mjs.map +0 -1
- package/dist/query-Ctlq1aOk.mjs.map +0 -1
- package/dist/redirects-C0L9JUk4.mjs.map +0 -1
- package/dist/runner-pt6Wl-l-.mjs.map +0 -1
- package/dist/seo-DfjLvu8i.mjs.map +0 -1
- package/dist/settings-b5zW1R1T.mjs +0 -235
- package/dist/settings-b5zW1R1T.mjs.map +0 -1
- package/dist/taxonomies-Crtzy4MT.mjs.map +0 -1
- package/dist/types-Cj2S6FuC.mjs +0 -3
- package/dist/types-D8bhH891.mjs.map +0 -1
- package/dist/types-DbCWhHet.d.mts.map +0 -1
- package/dist/types-i8_uzhMD.d.mts.map +0 -1
- package/dist/user-DzEUl5zA.mjs.map +0 -1
- package/dist/validate-Dy6nkNls.d.mts.map +0 -1
- package/dist/validate-JCXcsqiY.mjs.map +0 -1
- package/dist/version-CnS-Cr8A.mjs +0 -7
- package/dist/widgets-Bap1eS1X.mjs.map +0 -1
- package/src/plugins/scheduler/piggyback.ts +0 -71
- /package/dist/{api-tokens-B6VgoE6M.mjs → api-tokens-Oq39ba-Z.mjs} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emdash",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Astro-native CMS with WordPress migration support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
"#types": "./src/astro/types.js"
|
|
179
179
|
},
|
|
180
180
|
"dependencies": {
|
|
181
|
-
"@atcute/lexicons": "^
|
|
181
|
+
"@atcute/lexicons": "^2.0.0",
|
|
182
182
|
"@atcute/multibase": "^1.2.0",
|
|
183
183
|
"@floating-ui/react": "^0.27.16",
|
|
184
184
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
@@ -217,12 +217,12 @@
|
|
|
217
217
|
"ulidx": "^2.4.1",
|
|
218
218
|
"upng-js": "^2.1.0",
|
|
219
219
|
"zod": "^4.4.1",
|
|
220
|
-
"@atcute/client": "^
|
|
221
|
-
"@emdash-cms/admin": "0.
|
|
222
|
-
"@emdash-cms/
|
|
223
|
-
"@emdash-cms/
|
|
224
|
-
"@emdash-cms/
|
|
225
|
-
"@emdash-cms/
|
|
220
|
+
"@atcute/client": "^5.0.0",
|
|
221
|
+
"@emdash-cms/admin": "0.20.0",
|
|
222
|
+
"@emdash-cms/auth": "0.20.0",
|
|
223
|
+
"@emdash-cms/gutenberg-to-portable-text": "0.20.0",
|
|
224
|
+
"@emdash-cms/plugin-types": "0.1.0",
|
|
225
|
+
"@emdash-cms/registry-client": "0.3.2"
|
|
226
226
|
},
|
|
227
227
|
"optionalDependencies": {
|
|
228
228
|
"@libsql/kysely-libsql": "^0.4.0",
|
|
@@ -248,14 +248,14 @@
|
|
|
248
248
|
"@types/react": "19.2.14",
|
|
249
249
|
"@types/sanitize-html": "^2.16.0",
|
|
250
250
|
"@types/sax": "^1.2.7",
|
|
251
|
-
"@vitest/ui": "^4.1.
|
|
251
|
+
"@vitest/ui": "^4.1.8",
|
|
252
252
|
"publint": "0.3.17",
|
|
253
253
|
"tsdown": "0.20.3",
|
|
254
254
|
"typescript": "^6.0.3",
|
|
255
255
|
"vite": "^6.0.0",
|
|
256
256
|
"vitest": "^4.1.5",
|
|
257
257
|
"zod-openapi": "^5.4.6",
|
|
258
|
-
"@emdash-cms/blocks": "0.
|
|
258
|
+
"@emdash-cms/blocks": "0.20.0"
|
|
259
259
|
},
|
|
260
260
|
"repository": {
|
|
261
261
|
"type": "git",
|
|
@@ -14,13 +14,17 @@ import { RevisionRepository } from "../../database/repositories/revision.js";
|
|
|
14
14
|
import { SeoRepository } from "../../database/repositories/seo.js";
|
|
15
15
|
import {
|
|
16
16
|
EmDashValidationError,
|
|
17
|
+
ScheduledNotDueError,
|
|
17
18
|
InvalidCursorError,
|
|
18
19
|
type BylineSummary,
|
|
19
20
|
type ContentBylineCredit,
|
|
21
|
+
type ContentDateField,
|
|
20
22
|
type ContentItem,
|
|
21
23
|
type ContentSeo,
|
|
22
24
|
type ContentSeoInput,
|
|
25
|
+
type FindManyOptions,
|
|
23
26
|
} from "../../database/repositories/types.js";
|
|
27
|
+
import { UserRepository } from "../../database/repositories/user.js";
|
|
24
28
|
import { withTransaction } from "../../database/transaction.js";
|
|
25
29
|
import type { Database } from "../../database/types.js";
|
|
26
30
|
import { validateIdentifier } from "../../database/validate.js";
|
|
@@ -323,6 +327,24 @@ async function resolveSearchColumns(db: Kysely<Database>, collection: string): P
|
|
|
323
327
|
return columns;
|
|
324
328
|
}
|
|
325
329
|
|
|
330
|
+
/** Matches a date-only `YYYY-MM-DD` bound (no time component). */
|
|
331
|
+
const DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Normalize a date-range bound to an ISO datetime for lexicographic comparison
|
|
335
|
+
* against stored ISO 8601 timestamps. A bare `YYYY-MM-DD` is widened to the
|
|
336
|
+
* appropriate UTC day boundary so the range stays inclusive: a `start` bound
|
|
337
|
+
* becomes the start of the day and an `end` bound the end of the day.
|
|
338
|
+
* Otherwise a date-only upper bound would exclude every same-day row (since
|
|
339
|
+
* `2024-06-01T12:00:00Z` sorts after `2024-06-01`). Full datetimes pass
|
|
340
|
+
* through unchanged.
|
|
341
|
+
*/
|
|
342
|
+
function normalizeDateBound(value: string | undefined, edge: "start" | "end"): string | undefined {
|
|
343
|
+
if (!value) return undefined;
|
|
344
|
+
if (!DATE_ONLY_RE.test(value)) return value;
|
|
345
|
+
return edge === "start" ? `${value}T00:00:00.000Z` : `${value}T23:59:59.999Z`;
|
|
346
|
+
}
|
|
347
|
+
|
|
326
348
|
/**
|
|
327
349
|
* Create content list handler
|
|
328
350
|
*/
|
|
@@ -337,18 +359,28 @@ export async function handleContentList(
|
|
|
337
359
|
order?: "asc" | "desc";
|
|
338
360
|
locale?: string;
|
|
339
361
|
q?: string;
|
|
362
|
+
authorId?: string;
|
|
363
|
+
dateField?: ContentDateField;
|
|
364
|
+
dateFrom?: string;
|
|
365
|
+
dateTo?: string;
|
|
340
366
|
},
|
|
341
367
|
): Promise<ApiResult<ContentListResponse>> {
|
|
342
368
|
try {
|
|
343
369
|
const repo = new ContentRepository(db);
|
|
344
|
-
const where: {
|
|
345
|
-
status?: string;
|
|
346
|
-
locale?: string;
|
|
347
|
-
q?: string;
|
|
348
|
-
searchColumns?: string[];
|
|
349
|
-
} = {};
|
|
370
|
+
const where: FindManyOptions["where"] = {};
|
|
350
371
|
if (params.status) where.status = params.status;
|
|
351
372
|
if (params.locale) where.locale = params.locale;
|
|
373
|
+
if (params.authorId) where.authorId = params.authorId;
|
|
374
|
+
|
|
375
|
+
// A date range requires a target column; ignore stray from/to without
|
|
376
|
+
// a field so a half-specified filter doesn't silently drop all rows.
|
|
377
|
+
if (params.dateField && (params.dateFrom || params.dateTo)) {
|
|
378
|
+
where.dateFilter = {
|
|
379
|
+
field: params.dateField,
|
|
380
|
+
from: normalizeDateBound(params.dateFrom, "start"),
|
|
381
|
+
to: normalizeDateBound(params.dateTo, "end"),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
352
384
|
|
|
353
385
|
const q = params.q?.trim();
|
|
354
386
|
if (q) {
|
|
@@ -412,6 +444,62 @@ export async function handleContentList(
|
|
|
412
444
|
}
|
|
413
445
|
}
|
|
414
446
|
|
|
447
|
+
/** A content author option for the admin author filter. */
|
|
448
|
+
export interface ContentAuthor {
|
|
449
|
+
id: string;
|
|
450
|
+
name: string | null;
|
|
451
|
+
email: string;
|
|
452
|
+
avatarUrl: string | null;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* List the distinct authors of a collection's live content.
|
|
457
|
+
*
|
|
458
|
+
* Backs the admin content-list author filter. Unlike `/admin/users` (ADMIN
|
|
459
|
+
* only), this is gated on `content:read`, so any editor can filter by author.
|
|
460
|
+
* Returns only users who have authored at least one non-trashed entry, sorted
|
|
461
|
+
* by display name then email for a stable dropdown order.
|
|
462
|
+
*/
|
|
463
|
+
export async function handleContentAuthors(
|
|
464
|
+
db: Kysely<Database>,
|
|
465
|
+
collection: string,
|
|
466
|
+
): Promise<ApiResult<{ items: ContentAuthor[] }>> {
|
|
467
|
+
try {
|
|
468
|
+
const repo = new ContentRepository(db);
|
|
469
|
+
const authorIds = await repo.findDistinctAuthorIds(collection);
|
|
470
|
+
if (authorIds.length === 0) {
|
|
471
|
+
return { success: true, data: { items: [] } };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const userRepo = new UserRepository(db);
|
|
475
|
+
const users = await userRepo.findByIds(authorIds);
|
|
476
|
+
|
|
477
|
+
const items: ContentAuthor[] = users
|
|
478
|
+
.map((u) => ({ id: u.id, name: u.name, email: u.email, avatarUrl: u.avatarUrl }))
|
|
479
|
+
.toSorted((a, b) => (a.name ?? a.email).localeCompare(b.name ?? b.email));
|
|
480
|
+
|
|
481
|
+
return { success: true, data: { items } };
|
|
482
|
+
} catch (error) {
|
|
483
|
+
if (isMissingTableError(error)) {
|
|
484
|
+
return {
|
|
485
|
+
success: false,
|
|
486
|
+
error: {
|
|
487
|
+
code: "COLLECTION_NOT_FOUND",
|
|
488
|
+
message: `Collection '${collection}' not found`,
|
|
489
|
+
},
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
console.error("Content authors error:", error);
|
|
493
|
+
return {
|
|
494
|
+
success: false,
|
|
495
|
+
error: {
|
|
496
|
+
code: "CONTENT_AUTHORS_ERROR",
|
|
497
|
+
message: "Failed to list content authors",
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
415
503
|
/**
|
|
416
504
|
* Get single content item
|
|
417
505
|
*/
|
|
@@ -1268,13 +1356,13 @@ export async function handleContentPublish(
|
|
|
1268
1356
|
db: Kysely<Database>,
|
|
1269
1357
|
collection: string,
|
|
1270
1358
|
id: string,
|
|
1271
|
-
options: { publishedAt?: string } = {},
|
|
1359
|
+
options: { publishedAt?: string; requireScheduledDue?: boolean } = {},
|
|
1272
1360
|
): Promise<ApiResult<ContentResponse>> {
|
|
1273
1361
|
try {
|
|
1274
1362
|
const item = await withTransaction(db, async (trx) => {
|
|
1275
1363
|
const repo = new ContentRepository(trx);
|
|
1276
1364
|
const resolvedId = (await resolveId(repo, collection, id)) ?? id;
|
|
1277
|
-
return repo.publish(collection, resolvedId, options.publishedAt);
|
|
1365
|
+
return repo.publish(collection, resolvedId, options.publishedAt, options.requireScheduledDue);
|
|
1278
1366
|
});
|
|
1279
1367
|
|
|
1280
1368
|
const hasSeo = await collectionHasSeo(db, collection);
|
|
@@ -1285,6 +1373,17 @@ export async function handleContentPublish(
|
|
|
1285
1373
|
data: { item },
|
|
1286
1374
|
};
|
|
1287
1375
|
} catch (error) {
|
|
1376
|
+
// The scheduled sweep gates publish on the row still being due; a row
|
|
1377
|
+
// unscheduled in the meantime is a silent skip, not a failure.
|
|
1378
|
+
if (error instanceof ScheduledNotDueError) {
|
|
1379
|
+
return {
|
|
1380
|
+
success: false,
|
|
1381
|
+
error: {
|
|
1382
|
+
code: "NOT_DUE",
|
|
1383
|
+
message: error.message,
|
|
1384
|
+
},
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1288
1387
|
if (error instanceof EmDashValidationError) {
|
|
1289
1388
|
return {
|
|
1290
1389
|
success: false,
|
|
@@ -9,7 +9,7 @@ import type { Kysely } from "kysely";
|
|
|
9
9
|
|
|
10
10
|
import type { Database } from "../../database/types.js";
|
|
11
11
|
import { validatePluginIdentifier } from "../../database/validate.js";
|
|
12
|
-
import { pluginManifestSchema } from "../../plugins/manifest-schema.js";
|
|
12
|
+
import { pluginManifestSchema, reconcileManifestAccess } from "../../plugins/manifest-schema.js";
|
|
13
13
|
import { normalizeManifestRoute } from "../../plugins/manifest-schema.js";
|
|
14
14
|
import {
|
|
15
15
|
createMarketplaceClient,
|
|
@@ -271,10 +271,7 @@ export async function loadBundleFromR2(
|
|
|
271
271
|
const parsed: unknown = JSON.parse(manifestText);
|
|
272
272
|
const result = pluginManifestSchema.safeParse(parsed);
|
|
273
273
|
if (!result.success) return null;
|
|
274
|
-
|
|
275
|
-
// for the Element[] type (Block Kit validation happens at render time).
|
|
276
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Zod types elements as unknown[]; Element type validated at render time
|
|
277
|
-
const manifest = result.data as unknown as PluginManifest;
|
|
274
|
+
const manifest = reconcileManifestAccess(result.data);
|
|
278
275
|
|
|
279
276
|
// Try to load admin code (optional)
|
|
280
277
|
let adminCode: string | undefined;
|
|
@@ -49,6 +49,8 @@ import { extractBundle } from "../../plugins/marketplace.js";
|
|
|
49
49
|
import type { PluginBundle } from "../../plugins/marketplace.js";
|
|
50
50
|
import type { SandboxRunner } from "../../plugins/sandbox/types.js";
|
|
51
51
|
import { PluginStateRepository } from "../../plugins/state.js";
|
|
52
|
+
import { declaredAccessToCapabilities } from "../../plugins/types.js";
|
|
53
|
+
import type { DeclaredAccess } from "../../plugins/types.js";
|
|
52
54
|
import {
|
|
53
55
|
canonicalCapabilitiesForDriftCheck,
|
|
54
56
|
coerceRegistryConfig,
|
|
@@ -70,6 +72,26 @@ import {
|
|
|
70
72
|
storeBundleInR2,
|
|
71
73
|
} from "./marketplace.js";
|
|
72
74
|
|
|
75
|
+
const RELEASE_EXTENSION_NSID = "com.emdashcms.experimental.package.releaseExtension";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Whether two `declaredAccess` blocks grant exactly the same enforced access --
|
|
79
|
+
* the same capabilities AND the same host allow-list. Both are lowered through
|
|
80
|
+
* the canonical converter so that constraint content (`allowedHosts`), not just
|
|
81
|
+
* the capability set, is part of the comparison. The capability-set consent
|
|
82
|
+
* gate is blind to host scope; this is what keeps a bundle from being installed
|
|
83
|
+
* with a wider (or simply different) host allow-list than its published record
|
|
84
|
+
* advertised and the user consented to.
|
|
85
|
+
*/
|
|
86
|
+
export function enforcedAccessEqual(a: DeclaredAccess, b: DeclaredAccess): boolean {
|
|
87
|
+
const aa = declaredAccessToCapabilities(a);
|
|
88
|
+
const bb = declaredAccessToCapabilities(b);
|
|
89
|
+
return (
|
|
90
|
+
JSON.stringify(aa.capabilities.toSorted()) === JSON.stringify(bb.capabilities.toSorted()) &&
|
|
91
|
+
JSON.stringify(aa.allowedHosts.toSorted()) === JSON.stringify(bb.allowedHosts.toSorted())
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
73
95
|
// ── Types ──────────────────────────────────────────────────────────
|
|
74
96
|
|
|
75
97
|
export interface RegistryInstallInput {
|
|
@@ -999,6 +1021,31 @@ export async function handleRegistryInstall(
|
|
|
999
1021
|
// marketplace plugins that happen to share the publisher's slug.
|
|
1000
1022
|
bundle.manifest = { ...bundle.manifest, id: pluginId };
|
|
1001
1023
|
|
|
1024
|
+
// Integrity: the bundle that will run MUST declare exactly the access
|
|
1025
|
+
// the signed release record advertises. The consent dialog is driven
|
|
1026
|
+
// from the record's `declaredAccess`, so a bundle enforcing something
|
|
1027
|
+
// different -- a wider host allow-list, an extra capability -- would run
|
|
1028
|
+
// outside what the user reviewed. The capability-set consent gate below
|
|
1029
|
+
// is blind to constraint content (host scope), so compare the full
|
|
1030
|
+
// enforced access of record vs bundle here and refuse on any difference.
|
|
1031
|
+
const recordExt =
|
|
1032
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension
|
|
1033
|
+
(release?.extensions as Record<string, { declaredAccess?: DeclaredAccess }> | undefined)?.[
|
|
1034
|
+
RELEASE_EXTENSION_NSID
|
|
1035
|
+
];
|
|
1036
|
+
if (
|
|
1037
|
+
!enforcedAccessEqual(recordExt?.declaredAccess ?? {}, bundle.manifest.declaredAccess ?? {})
|
|
1038
|
+
) {
|
|
1039
|
+
return {
|
|
1040
|
+
success: false,
|
|
1041
|
+
error: {
|
|
1042
|
+
code: "DECLARED_ACCESS_DRIFT",
|
|
1043
|
+
message:
|
|
1044
|
+
"The plugin bundle declares different permissions than its published record. Installation refused.",
|
|
1045
|
+
},
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1002
1049
|
// Capability consent gate: the admin MUST acknowledge the
|
|
1003
1050
|
// capabilities the bundle's manifest actually declares before we
|
|
1004
1051
|
// install it. The bundle manifest is the only source of truth
|
|
@@ -1488,6 +1535,29 @@ export async function handleRegistryUpdate(
|
|
|
1488
1535
|
// and R2 layout stay in sync across install and update.
|
|
1489
1536
|
bundle.manifest = { ...bundle.manifest, id: pluginId };
|
|
1490
1537
|
|
|
1538
|
+
// Integrity: same gate as install. The new bundle must declare exactly
|
|
1539
|
+
// the access its signed release record advertises. Without it, an update
|
|
1540
|
+
// that changes only the host scope (e.g. api.good.com -> evil.com) keeps
|
|
1541
|
+
// the capability set identical, sails through the escalation diff below,
|
|
1542
|
+
// and installs a bundle enforcing a scope the record never showed.
|
|
1543
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- extensions is the lexicon's open `unknown` map; narrow to read our own extension
|
|
1544
|
+
const updateRecordExtensions = signedRelease?.extensions as
|
|
1545
|
+
| Record<string, { declaredAccess?: DeclaredAccess }>
|
|
1546
|
+
| undefined;
|
|
1547
|
+
const recordExt = updateRecordExtensions?.[RELEASE_EXTENSION_NSID];
|
|
1548
|
+
if (
|
|
1549
|
+
!enforcedAccessEqual(recordExt?.declaredAccess ?? {}, bundle.manifest.declaredAccess ?? {})
|
|
1550
|
+
) {
|
|
1551
|
+
return {
|
|
1552
|
+
success: false,
|
|
1553
|
+
error: {
|
|
1554
|
+
code: "DECLARED_ACCESS_DRIFT",
|
|
1555
|
+
message:
|
|
1556
|
+
"The plugin bundle declares different permissions than its published record. Update refused.",
|
|
1557
|
+
},
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1491
1561
|
// Diff capabilities + route visibility against the currently
|
|
1492
1562
|
// installed bundle. Loading from R2 keeps us honest: the diff is
|
|
1493
1563
|
// against the bytes the sandbox is actually running, not whatever
|
package/src/api/handlers/seo.ts
CHANGED
|
@@ -29,6 +29,12 @@ export interface SitemapContentEntry {
|
|
|
29
29
|
* alternates between siblings.
|
|
30
30
|
*/
|
|
31
31
|
translationGroup: string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Stored SEO image reference (`_emdash_seo.seo_image`), or null when
|
|
34
|
+
* the entry has no SEO image. The route resolves it to an absolute
|
|
35
|
+
* URL and emits it as an `<image:image>` sitemap entry.
|
|
36
|
+
*/
|
|
37
|
+
image: string | null;
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
/** Per-collection sitemap data with entries and URL pattern */
|
|
@@ -106,8 +112,9 @@ export async function handleSitemapData(
|
|
|
106
112
|
updated_at: string;
|
|
107
113
|
locale: string;
|
|
108
114
|
translation_group: string | null;
|
|
115
|
+
seo_image: string | null;
|
|
109
116
|
}>`
|
|
110
|
-
SELECT c.slug, c.id, c.updated_at, c.locale, c.translation_group
|
|
117
|
+
SELECT c.slug, c.id, c.updated_at, c.locale, c.translation_group, s.seo_image
|
|
111
118
|
FROM ${sql.ref(tableName)} c
|
|
112
119
|
LEFT JOIN _emdash_seo s
|
|
113
120
|
ON s.collection = ${col.slug}
|
|
@@ -129,6 +136,7 @@ export async function handleSitemapData(
|
|
|
129
136
|
updatedAt: row.updated_at,
|
|
130
137
|
locale: row.locale,
|
|
131
138
|
translationGroup: row.translation_group,
|
|
139
|
+
image: row.seo_image ?? null,
|
|
132
140
|
});
|
|
133
141
|
}
|
|
134
142
|
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { apiErrorSchema, deleteResponseSchema, successEnvelope } from "../schemas/common.js";
|
|
25
25
|
import {
|
|
26
26
|
contentCompareResponseSchema,
|
|
27
|
+
contentAuthorsResponseSchema,
|
|
27
28
|
contentCreateBody,
|
|
28
29
|
contentItemSchema,
|
|
29
30
|
contentListQuery,
|
|
@@ -597,6 +598,30 @@ const contentPaths = {
|
|
|
597
598
|
},
|
|
598
599
|
},
|
|
599
600
|
|
|
601
|
+
"/_emdash/api/content/{collection}/authors": {
|
|
602
|
+
get: {
|
|
603
|
+
operationId: "listContentAuthors",
|
|
604
|
+
summary: "List distinct authors of a collection's content",
|
|
605
|
+
tags: ["Content"],
|
|
606
|
+
requestParams: {
|
|
607
|
+
path: z.object({
|
|
608
|
+
collection: z.string().meta({ description: "Collection slug" }),
|
|
609
|
+
}),
|
|
610
|
+
},
|
|
611
|
+
responses: {
|
|
612
|
+
"200": {
|
|
613
|
+
description: "Content authors",
|
|
614
|
+
content: {
|
|
615
|
+
[JSON_CONTENT]: {
|
|
616
|
+
schema: successEnvelope(contentAuthorsResponseSchema),
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
...authErrors,
|
|
621
|
+
...standardErrors(500),
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
},
|
|
600
625
|
"/_emdash/api/content/{collection}/trash": {
|
|
601
626
|
get: {
|
|
602
627
|
operationId: "listTrashedContent",
|
|
@@ -18,6 +18,14 @@ export const contentSeoInput = z
|
|
|
18
18
|
})
|
|
19
19
|
.meta({ id: "ContentSeoInput" });
|
|
20
20
|
|
|
21
|
+
/** ISO 8601 date or datetime bound for the content-list date range filter. */
|
|
22
|
+
const contentDateBound = z
|
|
23
|
+
.union([
|
|
24
|
+
z.iso.datetime({ offset: true, message: "must be an ISO 8601 datetime" }),
|
|
25
|
+
z.iso.date({ message: "must be an ISO 8601 date" }),
|
|
26
|
+
])
|
|
27
|
+
.optional();
|
|
28
|
+
|
|
21
29
|
export const contentListQuery = cursorPaginationQuery
|
|
22
30
|
.extend({
|
|
23
31
|
status: z.string().optional(),
|
|
@@ -26,6 +34,14 @@ export const contentListQuery = cursorPaginationQuery
|
|
|
26
34
|
locale: localeCode.optional(),
|
|
27
35
|
/** Case-insensitive substring search across the collection's title/name/slug. */
|
|
28
36
|
q: z.string().trim().min(1).max(200).optional(),
|
|
37
|
+
/** Filter to entries authored by this user (the `author_id` column). */
|
|
38
|
+
authorId: z.string().min(1).max(64).optional(),
|
|
39
|
+
/** Which timestamp column the `dateFrom`/`dateTo` range applies to. */
|
|
40
|
+
dateField: z.enum(["createdAt", "updatedAt", "publishedAt"]).optional(),
|
|
41
|
+
/** Inclusive lower bound for the date range. Requires `dateField`. */
|
|
42
|
+
dateFrom: contentDateBound,
|
|
43
|
+
/** Inclusive upper bound for the date range. Requires `dateField`. */
|
|
44
|
+
dateTo: contentDateBound,
|
|
29
45
|
})
|
|
30
46
|
.meta({ id: "ContentListQuery" });
|
|
31
47
|
|
|
@@ -168,6 +184,23 @@ export const contentListResponseSchema = z
|
|
|
168
184
|
})
|
|
169
185
|
.meta({ id: "ContentListResponse" });
|
|
170
186
|
|
|
187
|
+
/** A distinct content author for the admin author filter */
|
|
188
|
+
export const contentAuthorSchema = z
|
|
189
|
+
.object({
|
|
190
|
+
id: z.string(),
|
|
191
|
+
name: z.string().nullable(),
|
|
192
|
+
email: z.string(),
|
|
193
|
+
avatarUrl: z.string().nullable(),
|
|
194
|
+
})
|
|
195
|
+
.meta({ id: "ContentAuthor" });
|
|
196
|
+
|
|
197
|
+
/** Response for the content authors endpoint */
|
|
198
|
+
export const contentAuthorsResponseSchema = z
|
|
199
|
+
.object({
|
|
200
|
+
items: z.array(contentAuthorSchema),
|
|
201
|
+
})
|
|
202
|
+
.meta({ id: "ContentAuthorsResponse" });
|
|
203
|
+
|
|
171
204
|
/** Trashed content item */
|
|
172
205
|
export const trashedContentItemSchema = z
|
|
173
206
|
.object({
|
|
@@ -31,7 +31,19 @@ const fieldTypeValues = z.enum([
|
|
|
31
31
|
|
|
32
32
|
const repeaterSubFieldSchema = z.object({
|
|
33
33
|
slug: z.string().min(1).max(63).regex(slugPattern, "Invalid slug format"),
|
|
34
|
-
|
|
34
|
+
// Keep in sync with REPEATER_SUB_FIELD_TYPES in schema/types.ts.
|
|
35
|
+
// ("url" was already a documented sub-field type but missing here.)
|
|
36
|
+
type: z.enum([
|
|
37
|
+
"string",
|
|
38
|
+
"text",
|
|
39
|
+
"url",
|
|
40
|
+
"number",
|
|
41
|
+
"integer",
|
|
42
|
+
"boolean",
|
|
43
|
+
"datetime",
|
|
44
|
+
"select",
|
|
45
|
+
"image",
|
|
46
|
+
]),
|
|
35
47
|
label: z.string().min(1),
|
|
36
48
|
required: z.boolean().optional(),
|
|
37
49
|
options: z.array(z.string()).optional(),
|
|
@@ -15,6 +15,7 @@ import { createRequire } from "node:module";
|
|
|
15
15
|
import type { AstroIntegration, AstroIntegrationLogger } from "astro";
|
|
16
16
|
|
|
17
17
|
import { validateAllowedOrigins, validateOriginShape } from "../../auth/allowed-origins.js";
|
|
18
|
+
import { INTERNAL_MEDIA_PREFIX } from "../../media/normalize.js";
|
|
18
19
|
import type { ResolvedPlugin } from "../../plugins/types.js";
|
|
19
20
|
import { local } from "../storage/adapters.js";
|
|
20
21
|
import { notoSans } from "./font-provider.js";
|
|
@@ -59,6 +60,92 @@ const DEFAULT_STORAGE = local({
|
|
|
59
60
|
baseUrl: "/_emdash/api/media/file",
|
|
60
61
|
});
|
|
61
62
|
|
|
63
|
+
interface ImageRemotePattern {
|
|
64
|
+
protocol?: "http" | "https";
|
|
65
|
+
hostname?: string;
|
|
66
|
+
pathname?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Build `image.remotePatterns` entries so Astro will optimize EmDash media.
|
|
71
|
+
*
|
|
72
|
+
* Astro's image services only transform **absolute** URLs whose host is
|
|
73
|
+
* authorized; everything else is passed through unoptimized. We authorize the
|
|
74
|
+
* media sources automatically:
|
|
75
|
+
*
|
|
76
|
+
* 1. The storage adapter's public URL host (R2 custom domain, S3/CDN), so
|
|
77
|
+
* media served directly from a public bucket is optimized.
|
|
78
|
+
* 2. The site's own origin, scoped to the media proxy route
|
|
79
|
+
* (`/_emdash/api/media/file/**`), so same-origin proxied media (local
|
|
80
|
+
* storage, or R2 without a public URL) is optimized too. The pathname
|
|
81
|
+
* scope keeps Astro's image endpoint from acting as an open proxy for the
|
|
82
|
+
* whole origin. Only registered when `siteUrl` is known at build time;
|
|
83
|
+
* `getPublicOrigin` resolves the matching origin at render time.
|
|
84
|
+
* 3. In `astro dev` the dev-server origin (`localhost:<port>`) isn't known at
|
|
85
|
+
* build time, so we register a host-agnostic pattern scoped to the media
|
|
86
|
+
* route. This is dev-only — it never ships in a production build — so the
|
|
87
|
+
* missing host check can't be abused on a deployed site.
|
|
88
|
+
*
|
|
89
|
+
* Returns an empty array when no source is statically known (e.g. a production
|
|
90
|
+
* build using local storage with no `siteUrl`), in which case media renders as
|
|
91
|
+
* a plain `<img>`.
|
|
92
|
+
*
|
|
93
|
+
* @internal Exported for unit testing.
|
|
94
|
+
*/
|
|
95
|
+
export function buildImageRemotePatterns(
|
|
96
|
+
storage: { config?: unknown } | undefined,
|
|
97
|
+
siteUrl: string | undefined,
|
|
98
|
+
command: "dev" | "build" | "preview" | "sync",
|
|
99
|
+
): ImageRemotePattern[] {
|
|
100
|
+
const patterns: ImageRemotePattern[] = [];
|
|
101
|
+
|
|
102
|
+
const config = storage?.config;
|
|
103
|
+
const publicUrl =
|
|
104
|
+
config && typeof config === "object"
|
|
105
|
+
? (config as { publicUrl?: unknown }).publicUrl
|
|
106
|
+
: undefined;
|
|
107
|
+
if (typeof publicUrl === "string" && publicUrl) {
|
|
108
|
+
try {
|
|
109
|
+
const url = new URL(publicUrl);
|
|
110
|
+
// Only authorize http(s) hosts — a `file:`/`ftp:` URL is not a media
|
|
111
|
+
// origin Astro can fetch.
|
|
112
|
+
if (url.protocol === "http:" || url.protocol === "https:") {
|
|
113
|
+
const pattern: ImageRemotePattern = {
|
|
114
|
+
protocol: url.protocol === "http:" ? "http" : "https",
|
|
115
|
+
hostname: url.hostname,
|
|
116
|
+
};
|
|
117
|
+
// When the public URL has a path prefix (CDN sub-path), scope the
|
|
118
|
+
// pattern to it so we don't authorize the entire host. Media keys
|
|
119
|
+
// are appended as `${publicUrl}/${key}`, so the prefix is exact.
|
|
120
|
+
const prefix = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
|
|
121
|
+
if (prefix && prefix !== "/") {
|
|
122
|
+
pattern.pathname = `${prefix}/**`;
|
|
123
|
+
}
|
|
124
|
+
patterns.push(pattern);
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
// ignore an unparseable public URL
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (siteUrl) {
|
|
132
|
+
try {
|
|
133
|
+
patterns.push({
|
|
134
|
+
hostname: new URL(siteUrl).hostname,
|
|
135
|
+
pathname: `${INTERNAL_MEDIA_PREFIX}**`,
|
|
136
|
+
});
|
|
137
|
+
} catch {
|
|
138
|
+
// ignore an unparseable site URL
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (command === "dev") {
|
|
143
|
+
patterns.push({ pathname: `${INTERNAL_MEDIA_PREFIX}**` });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return patterns;
|
|
147
|
+
}
|
|
148
|
+
|
|
62
149
|
// Terminal formatting
|
|
63
150
|
const dim = (s: string) => `\x1b[2m${s}\x1b[22m`;
|
|
64
151
|
const bold = (s: string) => `\x1b[1m${s}\x1b[22m`;
|
|
@@ -298,8 +385,19 @@ export function emdash(config: EmDashConfig = {}): AstroIntegration {
|
|
|
298
385
|
},
|
|
299
386
|
];
|
|
300
387
|
|
|
388
|
+
// Authorize media sources for Astro image optimization so the
|
|
389
|
+
// Image components can generate a responsive srcset for R2/S3 and
|
|
390
|
+
// same-origin proxied media. `updateConfig` merges arrays, so any
|
|
391
|
+
// user-configured remotePatterns are preserved.
|
|
392
|
+
const imageRemotePatterns = buildImageRemotePatterns(
|
|
393
|
+
resolvedConfig.storage,
|
|
394
|
+
resolvedConfig.siteUrl,
|
|
395
|
+
command,
|
|
396
|
+
);
|
|
397
|
+
|
|
301
398
|
updateConfig({
|
|
302
399
|
security: securityConfig,
|
|
400
|
+
...(imageRemotePatterns.length ? { image: { remotePatterns: imageRemotePatterns } } : {}),
|
|
303
401
|
// fonts is a valid AstroConfig key but may not be in the
|
|
304
402
|
// type definition for the minimum supported Astro version
|
|
305
403
|
...({ fonts: emdashFonts } as Record<string, unknown>),
|
|
@@ -88,6 +88,12 @@ export function injectCoreRoutes(injectRoute: InjectRoute): void {
|
|
|
88
88
|
entrypoint: resolveRoute("api/content/[collection]/[id]/preview-url.ts"),
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
// Content authors (for the admin author filter)
|
|
92
|
+
injectRoute({
|
|
93
|
+
pattern: "/_emdash/api/content/[collection]/authors",
|
|
94
|
+
entrypoint: resolveRoute("api/content/[collection]/authors.ts"),
|
|
95
|
+
});
|
|
96
|
+
|
|
91
97
|
// Trash/restore routes
|
|
92
98
|
injectRoute({
|
|
93
99
|
pattern: "/_emdash/api/content/[collection]/trash",
|