emdash 0.30.0 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/route-utils.mjs +13 -13
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-b8WIiGU4.mjs → api-BkqFzIu4.mjs} +23 -23
- package/dist/api-BkqFzIu4.mjs.map +1 -0
- package/dist/{apply-CmIJK9j8.mjs → apply-DuXsqll7.mjs} +26 -25
- package/dist/apply-DuXsqll7.mjs.map +1 -0
- package/dist/astro/index.d.mts +2 -2
- package/dist/astro/index.mjs +1 -1
- package/dist/astro/middleware/auth.d.mts +2 -2
- package/dist/astro/middleware/auth.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +5 -5
- package/dist/astro/middleware/request-context.mjs +1 -1
- package/dist/astro/middleware.d.mts +2 -2
- package/dist/astro/middleware.mjs +146 -70
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +3 -3
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +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 +14 -14
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +14 -14
- package/dist/astro/routes/api/admin/bylines/index.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/bylines/index.mjs +18 -17
- package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +9 -9
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +9 -9
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +10 -10
- 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 +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/mcp.mjs +4 -4
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/index.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +35 -35
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +34 -34
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +35 -35
- package/dist/astro/routes/api/admin/plugins/updates.mjs +34 -34
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +34 -34
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +34 -34
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +6 -6
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- package/dist/astro/routes/api/admin/users/index.mjs +6 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +6 -6
- package/dist/astro/routes/api/auth/invite/index.mjs +6 -6
- package/dist/astro/routes/api/auth/invite/register-options.mjs +6 -6
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +7 -7
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +7 -7
- package/dist/astro/routes/api/auth/mode.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +7 -7
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/verify.mjs +6 -6
- package/dist/astro/routes/api/auth/signup/complete.mjs +6 -6
- package/dist/astro/routes/api/auth/signup/request.mjs +7 -7
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +10 -10
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +10 -10
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +9 -9
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/children.mjs +14 -14
- package/dist/astro/routes/api/content/_collection_/_id_/references/_relation_/parents.mjs +14 -14
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +13 -13
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/authors.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/index.mjs +7 -7
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
- package/dist/astro/routes/api/dashboard.mjs +7 -7
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.mjs +12 -12
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +2 -2
- package/dist/astro/routes/api/import/wordpress/execute.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/media.d.mts +2 -2
- package/dist/astro/routes/api/import/wordpress/media.mjs +7 -7
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +2 -2
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +12 -12
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +2 -2
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +19 -19
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +39 -39
- package/dist/astro/routes/api/media/_id_/confirm.mjs +7 -7
- package/dist/astro/routes/api/media/_id_/usage.mjs +11 -11
- package/dist/astro/routes/api/media/_id_.mjs +10 -10
- 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 +10 -10
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/items.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_/translations.mjs +8 -8
- package/dist/astro/routes/api/menus/_name_.mjs +8 -8
- package/dist/astro/routes/api/menus/index.mjs +8 -8
- package/dist/astro/routes/api/oauth/authorize.d.mts.map +1 -1
- package/dist/astro/routes/api/oauth/authorize.mjs +23 -9
- package/dist/astro/routes/api/oauth/authorize.mjs.map +1 -1
- package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
- package/dist/astro/routes/api/oauth/token.mjs +2 -2
- package/dist/astro/routes/api/openapi.json.mjs +4 -4
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +4 -4
- package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
- package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
- package/dist/astro/routes/api/redirects/_id_.mjs +11 -11
- package/dist/astro/routes/api/redirects/index.mjs +11 -11
- package/dist/astro/routes/api/relations/_id_/index.mjs +14 -14
- package/dist/astro/routes/api/relations/_id_/translations.mjs +10 -10
- package/dist/astro/routes/api/relations/index.mjs +14 -14
- 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 +34 -34
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +34 -34
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +34 -34
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +34 -34
- package/dist/astro/routes/api/schema/collections/index.mjs +34 -34
- package/dist/astro/routes/api/schema/index.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +34 -34
- package/dist/astro/routes/api/schema/orphans/index.mjs +34 -34
- package/dist/astro/routes/api/search/enable.mjs +11 -11
- package/dist/astro/routes/api/search/index.mjs +10 -10
- package/dist/astro/routes/api/search/rebuild.mjs +11 -11
- package/dist/astro/routes/api/search/stats.mjs +7 -7
- package/dist/astro/routes/api/search/suggest.mjs +10 -10
- package/dist/astro/routes/api/sections/_slug_.mjs +10 -10
- package/dist/astro/routes/api/sections/index.mjs +10 -10
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +4 -4
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +4 -4
- package/dist/astro/routes/api/settings/backups/export.mjs +4 -4
- package/dist/astro/routes/api/settings/backups/index.mjs +5 -5
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +12 -12
- package/dist/astro/routes/api/setup/admin-verify.mjs +6 -6
- package/dist/astro/routes/api/setup/admin.mjs +6 -6
- package/dist/astro/routes/api/setup/dev-bypass.mjs +18 -18
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +22 -22
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +4 -4
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +13 -13
- package/dist/astro/routes/api/taxonomies/index.mjs +13 -13
- package/dist/astro/routes/api/themes/preview.mjs +4 -4
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +7 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +10 -10
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +10 -10
- package/dist/astro/routes/api/widget-areas/_name_.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/index.mjs +10 -10
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +5 -5
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +7 -7
- package/dist/astro/routes/sitemap.xml.mjs +5 -5
- package/dist/astro/types.d.mts +2 -2
- package/dist/{authorize-WxsiePzy.mjs → authorize-6WPZa1xZ.mjs} +2 -2
- package/dist/{authorize-WxsiePzy.mjs.map → authorize-6WPZa1xZ.mjs.map} +1 -1
- package/dist/{backup-S8kFWtwD.mjs → backup-DoW-H0r4.mjs} +2 -2
- package/dist/{backup-S8kFWtwD.mjs.map → backup-DoW-H0r4.mjs.map} +1 -1
- package/dist/{byline-XEjchwzZ.mjs → byline-BTOxUlFF.mjs} +6 -6
- package/dist/{byline-XEjchwzZ.mjs.map → byline-BTOxUlFF.mjs.map} +1 -1
- package/dist/{bylines-czseViYo.mjs → bylines-Bc-3xtpz.mjs} +6 -6
- package/dist/{bylines-czseViYo.mjs.map → bylines-Bc-3xtpz.mjs.map} +1 -1
- package/dist/{bylines-BJbT4gKS.mjs → bylines-oVN0iPwX.mjs} +12 -8
- package/dist/bylines-oVN0iPwX.mjs.map +1 -0
- package/dist/{cache-CGCd6AVM.mjs → cache-DDwy3JDW.mjs} +2 -2
- package/dist/{cache-CGCd6AVM.mjs.map → cache-DDwy3JDW.mjs.map} +1 -1
- package/dist/{chunks-BxXyunY-.mjs → chunks-Bq-5DPrD.mjs} +2 -2
- package/dist/{chunks-BxXyunY-.mjs.map → chunks-Bq-5DPrD.mjs.map} +1 -1
- package/dist/cli/index.mjs +18 -18
- package/dist/{comment-DPT0WKyd.mjs → comment-B9cUud8H.mjs} +2 -2
- package/dist/{comment-DPT0WKyd.mjs.map → comment-B9cUud8H.mjs.map} +1 -1
- package/dist/{comment-reaction-C65MldIB.mjs → comment-reaction-VUXO981F.mjs} +2 -2
- package/dist/{comment-reaction-C65MldIB.mjs.map → comment-reaction-VUXO981F.mjs.map} +1 -1
- package/dist/{comments-Bz6sCbgD.mjs → comments-PXj1E9Si.mjs} +3 -3
- package/dist/{comments-Bz6sCbgD.mjs.map → comments-PXj1E9Si.mjs.map} +1 -1
- package/dist/{config-CVssduLe.mjs → config-B6b_BobR.mjs} +6 -2
- package/dist/config-B6b_BobR.mjs.map +1 -0
- package/dist/{content-Ci04z2z-.mjs → content-BC43pICM.mjs} +4 -4
- package/dist/{content-Ci04z2z-.mjs.map → content-BC43pICM.mjs.map} +1 -1
- package/dist/{content-refresh-D4khvC0R.mjs → content-refresh-CPMSauD3.mjs} +4 -4
- package/dist/{content-refresh-D4khvC0R.mjs.map → content-refresh-CPMSauD3.mjs.map} +1 -1
- package/dist/{context-B6hc7zJL.mjs → context-DOurRIrb.mjs} +10 -10
- package/dist/{context-B6hc7zJL.mjs.map → context-DOurRIrb.mjs.map} +1 -1
- package/dist/{dashboard-C5NkXFbi.mjs → dashboard-CBm4zhc3.mjs} +4 -4
- package/dist/{dashboard-C5NkXFbi.mjs.map → dashboard-CBm4zhc3.mjs.map} +1 -1
- package/dist/database/pg-migration-lock.d.mts.map +1 -1
- package/dist/database/pg-migration-lock.mjs +1 -4
- package/dist/database/pg-migration-lock.mjs.map +1 -1
- package/dist/db/index.mjs +2 -2
- package/dist/{dialect-helpers-HxEQGl_1.mjs → dialect-helpers-BtOwo2us.mjs} +4 -4
- package/dist/dialect-helpers-BtOwo2us.mjs.map +1 -0
- package/dist/{error-CEGF6UZb.mjs → error-DJ98TRbQ.mjs} +2 -2
- package/dist/{error-CEGF6UZb.mjs.map → error-DJ98TRbQ.mjs.map} +1 -1
- package/dist/{field-defs-cache-DvmlgP-D.mjs → field-defs-cache-KfNJ_qLQ.mjs} +2 -2
- package/dist/{field-defs-cache-DvmlgP-D.mjs.map → field-defs-cache-KfNJ_qLQ.mjs.map} +1 -1
- package/dist/{fts-manager-DzqIBrrW.mjs → fts-manager-hyCU9OOp.mjs} +3 -3
- package/dist/{fts-manager-DzqIBrrW.mjs.map → fts-manager-hyCU9OOp.mjs.map} +1 -1
- package/dist/{hreflang-db2Tpb2o.mjs → hreflang-CbYeCzl9.mjs} +7 -7
- package/dist/{hreflang-db2Tpb2o.mjs.map → hreflang-CbYeCzl9.mjs.map} +1 -1
- package/dist/{import-Dmkm8S1W.mjs → import-oDYCgNYQ.mjs} +2 -2
- package/dist/{import-Dmkm8S1W.mjs.map → import-oDYCgNYQ.mjs.map} +1 -1
- package/dist/{index-wYnC6u4o.d.mts → index-BZJ_5FOX.d.mts} +50 -4
- package/dist/index-BZJ_5FOX.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +48 -48
- package/dist/{load-Cx27ki1l.mjs → load-Dg5gtWma.mjs} +2 -2
- package/dist/{load-Cx27ki1l.mjs.map → load-Dg5gtWma.mjs.map} +1 -1
- package/dist/{loader-Be3ouI5L.mjs → loader-D4rigvjR.mjs} +4 -4
- package/dist/{loader-Be3ouI5L.mjs.map → loader-D4rigvjR.mjs.map} +1 -1
- package/dist/media/local-runtime.d.mts +2 -2
- package/dist/media/local-runtime.mjs +5 -5
- package/dist/{media-kIV1IxFf.mjs → media-BJ6SRY3g.mjs} +8 -4
- package/dist/media-BJ6SRY3g.mjs.map +1 -0
- package/dist/{media-BjhhENaJ.mjs → media-its1WLYv.mjs} +2 -2
- package/dist/{media-BjhhENaJ.mjs.map → media-its1WLYv.mjs.map} +1 -1
- package/dist/{media-upload-D2wk3EIt.mjs → media-upload-DNu3QySc.mjs} +4 -4
- package/dist/{media-upload-D2wk3EIt.mjs.map → media-upload-DNu3QySc.mjs.map} +1 -1
- package/dist/{media-usage-CljdO1mc.mjs → media-usage-DPQ3K4xB.mjs} +6 -6
- package/dist/{media-usage-CljdO1mc.mjs.map → media-usage-DPQ3K4xB.mjs.map} +1 -1
- package/dist/{menus-CZyG6rvx.mjs → menus-BMGzWyWH.mjs} +15 -8
- package/dist/menus-BMGzWyWH.mjs.map +1 -0
- package/dist/{menus-BcyElFtI.mjs → menus-CPf_SW7u.mjs} +86 -8
- package/dist/menus-CPf_SW7u.mjs.map +1 -0
- package/dist/{parse-C_-6klII.mjs → parse-53zyxlpv.mjs} +2 -2
- package/dist/{parse-C_-6klII.mjs.map → parse-53zyxlpv.mjs.map} +1 -1
- package/dist/plugin-utils.d.mts +2 -2
- package/dist/plugins/adapt-sandbox-entry.d.mts +2 -2
- package/dist/{query-DR73ZNfm.mjs → query-Bz9bWZfm.mjs} +14 -14
- package/dist/{query-DR73ZNfm.mjs.map → query-Bz9bWZfm.mjs.map} +1 -1
- package/dist/{query-Di7DOmPV.mjs → query-DdFGHzQF.mjs} +3 -3
- package/dist/{query-Di7DOmPV.mjs.map → query-DdFGHzQF.mjs.map} +1 -1
- package/dist/{rate-limit-CMj83JNW.mjs → rate-limit-D3IEiUi2.mjs} +2 -2
- package/dist/{rate-limit-CMj83JNW.mjs.map → rate-limit-D3IEiUi2.mjs.map} +1 -1
- package/dist/{redirect-CgLPYflR.mjs → redirect-BoUac4NT.mjs} +3 -3
- package/dist/{redirect-CgLPYflR.mjs.map → redirect-BoUac4NT.mjs.map} +1 -1
- package/dist/{redirects-B7t9bKsL.mjs → redirects-ChbpqKiS.mjs} +3 -3
- package/dist/{redirects-B7t9bKsL.mjs.map → redirects-ChbpqKiS.mjs.map} +1 -1
- package/dist/{redirects-BBq3n2Rj.mjs → redirects-D457qwt0.mjs} +8 -8
- package/dist/redirects-D457qwt0.mjs.map +1 -0
- package/dist/{registry-FV15nLge.mjs → registry-y21kjhfG.mjs} +6 -6
- package/dist/{registry-FV15nLge.mjs.map → registry-y21kjhfG.mjs.map} +1 -1
- package/dist/{relation-9M9j_UWo.mjs → relation-Du0r04E0.mjs} +3 -3
- package/dist/{relation-9M9j_UWo.mjs.map → relation-Du0r04E0.mjs.map} +1 -1
- package/dist/{relations-5_avdrN_.mjs → relations-CZMpBqEd.mjs} +3 -3
- package/dist/{relations-5_avdrN_.mjs.map → relations-CZMpBqEd.mjs.map} +1 -1
- package/dist/{relations-BKvO39-9.mjs → relations-DtX1UMBH.mjs} +13 -7
- package/dist/relations-DtX1UMBH.mjs.map +1 -0
- package/dist/{relations-Bh8jlFsU.d.mts → relations-qKmSj9KM.d.mts} +27 -23
- package/dist/relations-qKmSj9KM.d.mts.map +1 -0
- package/dist/{resolve-Cd9dzclN.mjs → resolve-DFr4kq72.mjs} +2 -2
- package/dist/{resolve-Cd9dzclN.mjs.map → resolve-DFr4kq72.mjs.map} +1 -1
- package/dist/{runner-DfnZ5eUr.mjs → runner-BdeLEgad.mjs} +3 -3
- package/dist/{runner-DfnZ5eUr.mjs.map → runner-BdeLEgad.mjs.map} +1 -1
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.mjs +3 -3
- package/dist/{schema-BXxlHeAf.mjs → schema-BH8P8vaC.mjs} +3 -3
- package/dist/{schema-BXxlHeAf.mjs.map → schema-BH8P8vaC.mjs.map} +1 -1
- package/dist/{scopes-Bl4IwHA-.mjs → scopes-DTIkIaGA.mjs} +2 -2
- package/dist/{scopes-Bl4IwHA-.mjs.map → scopes-DTIkIaGA.mjs.map} +1 -1
- package/dist/{search-Bff-7jFt.mjs → search-D-Yhpmvs.mjs} +8 -7
- package/dist/search-D-Yhpmvs.mjs.map +1 -0
- package/dist/{secrets-CSwQIl4q.mjs → secrets-CQ-69kF9.mjs} +23 -17
- package/dist/secrets-CQ-69kF9.mjs.map +1 -0
- package/dist/{sections-CwW4s1al.mjs → sections-CzVBAKjw.mjs} +3 -3
- package/dist/{sections-CwW4s1al.mjs.map → sections-CzVBAKjw.mjs.map} +1 -1
- package/dist/seed/index.mjs +16 -16
- package/dist/seo/index.mjs +12 -12
- package/dist/{seo-2cmFHZns.mjs → seo-kJzVAmmu.mjs} +2 -2
- package/dist/{seo-2cmFHZns.mjs.map → seo-kJzVAmmu.mjs.map} +1 -1
- package/dist/{service-DkGTWGIi.mjs → service-CQnc276a.mjs} +2 -2
- package/dist/{service-DkGTWGIi.mjs.map → service-CQnc276a.mjs.map} +1 -1
- package/dist/{settings-CpA4lQFt.mjs → settings-C2PaBS49.mjs} +3 -3
- package/dist/{settings-CpA4lQFt.mjs.map → settings-C2PaBS49.mjs.map} +1 -1
- package/dist/{settings-C4s8hFQm.mjs → settings-Dtc0DU31.mjs} +3 -3
- package/dist/{settings-C4s8hFQm.mjs.map → settings-Dtc0DU31.mjs.map} +1 -1
- package/dist/{taxonomies-Ce49uIzY.mjs → taxonomies-BN6M9Nrv.mjs} +26 -17
- package/dist/taxonomies-BN6M9Nrv.mjs.map +1 -0
- package/dist/{taxonomies-DjSKBZpq.mjs → taxonomies-r7pqLBCn.mjs} +53 -24
- package/dist/taxonomies-r7pqLBCn.mjs.map +1 -0
- package/dist/{taxonomy-DfVooU4W.mjs → taxonomy-0mCI25HN.mjs} +3 -3
- package/dist/{taxonomy-DfVooU4W.mjs.map → taxonomy-0mCI25HN.mjs.map} +1 -1
- package/dist/{types-D1iJ3DpO.mjs → types-CrRUFdej.mjs} +2 -2
- package/dist/{types-D1iJ3DpO.mjs.map → types-CrRUFdej.mjs.map} +1 -1
- package/dist/{user-Bh-L1qo6.mjs → user-aTXqmXBz.mjs} +3 -3
- package/dist/{user-Bh-L1qo6.mjs.map → user-aTXqmXBz.mjs.map} +1 -1
- package/dist/{validate-V9nCwq_-.mjs → validate-CDT1jl6_.mjs} +11 -6
- package/dist/validate-CDT1jl6_.mjs.map +1 -0
- package/dist/validate-DULfgpJP.d.mts.map +1 -1
- package/dist/{validation-BsVUJfsP.mjs → validation-DlD0E35W.mjs} +8 -8
- package/dist/{validation-BsVUJfsP.mjs.map → validation-DlD0E35W.mjs.map} +1 -1
- package/dist/version-BRMngh9I.mjs +7 -0
- package/dist/{version-907opKac.mjs.map → version-BRMngh9I.mjs.map} +1 -1
- package/dist/{widgets-DGv1Z04V.mjs → widgets-5fipNkMT.mjs} +2 -2
- package/dist/{widgets-DGv1Z04V.mjs.map → widgets-5fipNkMT.mjs.map} +1 -1
- package/dist/{wxr-taxonomies-Cs1N6O8z.mjs → wxr-taxonomies-DrihupaC.mjs} +4 -4
- package/dist/{wxr-taxonomies-Cs1N6O8z.mjs.map → wxr-taxonomies-DrihupaC.mjs.map} +1 -1
- package/dist/{zod-generator-B5prQ5M4.mjs → zod-generator-CwkHWz4f.mjs} +2 -2
- package/dist/{zod-generator-B5prQ5M4.mjs.map → zod-generator-CwkHWz4f.mjs.map} +1 -1
- package/package.json +7 -7
- package/src/api/handlers/bylines.ts +5 -4
- package/src/api/handlers/content.ts +25 -7
- package/src/api/handlers/menus.ts +11 -6
- package/src/api/handlers/relations.ts +7 -2
- package/src/api/handlers/taxonomies.ts +22 -14
- package/src/api/schemas/bylines.ts +4 -4
- package/src/api/schemas/common.ts +7 -6
- package/src/api/schemas/menus.ts +2 -1
- package/src/api/schemas/relations.ts +3 -1
- package/src/api/schemas/taxonomies.ts +4 -2
- package/src/astro/prefetch.ts +7 -2
- package/src/astro/routes/api/admin/bylines/index.ts +5 -8
- package/src/astro/routes/api/oauth/authorize.ts +42 -10
- package/src/components/Gallery.astro +141 -26
- package/src/components/widgets/Categories.astro +1 -1
- package/src/components/widgets/Tags.astro +1 -1
- package/src/config/secrets.ts +25 -19
- package/src/content/converters/gallery.ts +61 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +19 -0
- package/src/content/converters/prosemirror-to-portable-text.ts +18 -0
- package/src/content/converters/types.ts +36 -0
- package/src/database/dialect-helpers.ts +3 -4
- package/src/database/pg-migration-lock.ts +1 -4
- package/src/emdash-runtime.ts +55 -17
- package/src/i18n/config.ts +8 -0
- package/src/i18n/repair-locale-casing.ts +51 -0
- package/src/search/query.ts +3 -2
- package/src/seed/apply.ts +9 -7
- package/src/seed/validate.ts +15 -4
- package/src/taxonomies/index.ts +64 -23
- package/dist/api-b8WIiGU4.mjs.map +0 -1
- package/dist/apply-CmIJK9j8.mjs.map +0 -1
- package/dist/bylines-BJbT4gKS.mjs.map +0 -1
- package/dist/config-CVssduLe.mjs.map +0 -1
- package/dist/dialect-helpers-HxEQGl_1.mjs.map +0 -1
- package/dist/index-wYnC6u4o.d.mts.map +0 -1
- package/dist/media-kIV1IxFf.mjs.map +0 -1
- package/dist/menus-BcyElFtI.mjs.map +0 -1
- package/dist/menus-CZyG6rvx.mjs.map +0 -1
- package/dist/redirects-BBq3n2Rj.mjs.map +0 -1
- package/dist/relations-BKvO39-9.mjs.map +0 -1
- package/dist/relations-Bh8jlFsU.d.mts.map +0 -1
- package/dist/search-Bff-7jFt.mjs.map +0 -1
- package/dist/secrets-CSwQIl4q.mjs.map +0 -1
- package/dist/taxonomies-Ce49uIzY.mjs.map +0 -1
- package/dist/taxonomies-DjSKBZpq.mjs.map +0 -1
- package/dist/validate-V9nCwq_-.mjs.map +0 -1
- package/dist/version-907opKac.mjs +0 -7
|
@@ -30,7 +30,7 @@ import { UserRepository } from "../../database/repositories/user.js";
|
|
|
30
30
|
import { withTransaction } from "../../database/transaction.js";
|
|
31
31
|
import type { Database } from "../../database/types.js";
|
|
32
32
|
import { validateIdentifier } from "../../database/validate.js";
|
|
33
|
-
import { getI18nConfig, isI18nEnabled } from "../../i18n/config.js";
|
|
33
|
+
import { getI18nConfig, isI18nEnabled, resolveConfiguredLocale } from "../../i18n/config.js";
|
|
34
34
|
import { invalidateRedirectCache } from "../../redirects/cache.js";
|
|
35
35
|
import { FTSManager } from "../../search/fts-manager.js";
|
|
36
36
|
import { invalidateTermCache } from "../../taxonomies/index.js";
|
|
@@ -269,7 +269,11 @@ async function resolveId(
|
|
|
269
269
|
identifier: string,
|
|
270
270
|
locale?: string,
|
|
271
271
|
): Promise<string | null> {
|
|
272
|
-
const item = await repo.findByIdOrSlug(
|
|
272
|
+
const item = await repo.findByIdOrSlug(
|
|
273
|
+
collection,
|
|
274
|
+
identifier,
|
|
275
|
+
locale ? resolveConfiguredLocale(locale) : undefined,
|
|
276
|
+
);
|
|
273
277
|
return item?.id ?? null;
|
|
274
278
|
}
|
|
275
279
|
|
|
@@ -283,7 +287,11 @@ async function resolveIdIncludingTrashed(
|
|
|
283
287
|
identifier: string,
|
|
284
288
|
locale?: string,
|
|
285
289
|
): Promise<string | null> {
|
|
286
|
-
const item = await repo.findByIdOrSlugIncludingTrashed(
|
|
290
|
+
const item = await repo.findByIdOrSlugIncludingTrashed(
|
|
291
|
+
collection,
|
|
292
|
+
identifier,
|
|
293
|
+
locale ? resolveConfiguredLocale(locale) : undefined,
|
|
294
|
+
);
|
|
287
295
|
return item?.id ?? null;
|
|
288
296
|
}
|
|
289
297
|
|
|
@@ -427,7 +435,7 @@ export async function handleContentList(
|
|
|
427
435
|
const repo = new ContentRepository(db);
|
|
428
436
|
const where: FindManyOptions["where"] = {};
|
|
429
437
|
if (params.status) where.status = params.status;
|
|
430
|
-
if (params.locale) where.locale = params.locale;
|
|
438
|
+
if (params.locale) where.locale = resolveConfiguredLocale(params.locale);
|
|
431
439
|
if (params.authorId) where.authorId = params.authorId;
|
|
432
440
|
|
|
433
441
|
// A date range requires a target column; ignore stray from/to without
|
|
@@ -579,7 +587,11 @@ export async function handleContentGet(
|
|
|
579
587
|
): Promise<ApiResult<ContentResponse>> {
|
|
580
588
|
try {
|
|
581
589
|
const repo = new ContentRepository(db);
|
|
582
|
-
const item = await repo.findByIdOrSlug(
|
|
590
|
+
const item = await repo.findByIdOrSlug(
|
|
591
|
+
collection,
|
|
592
|
+
id,
|
|
593
|
+
locale ? resolveConfiguredLocale(locale) : undefined,
|
|
594
|
+
);
|
|
583
595
|
|
|
584
596
|
if (!item) {
|
|
585
597
|
return {
|
|
@@ -624,7 +636,11 @@ export async function handleContentGetIncludingTrashed(
|
|
|
624
636
|
): Promise<ApiResult<ContentResponse>> {
|
|
625
637
|
try {
|
|
626
638
|
const repo = new ContentRepository(db);
|
|
627
|
-
const item = await repo.findByIdOrSlugIncludingTrashed(
|
|
639
|
+
const item = await repo.findByIdOrSlugIncludingTrashed(
|
|
640
|
+
collection,
|
|
641
|
+
id,
|
|
642
|
+
locale ? resolveConfiguredLocale(locale) : undefined,
|
|
643
|
+
);
|
|
628
644
|
|
|
629
645
|
if (!item) {
|
|
630
646
|
return {
|
|
@@ -706,7 +722,9 @@ export async function handleContentCreate(
|
|
|
706
722
|
// Default to the configured site locale rather than the repo's
|
|
707
723
|
// hard-coded "en" — otherwise non-English default-locale sites
|
|
708
724
|
// silently create entries in a locale the editor never chose.
|
|
709
|
-
const effectiveLocale = body.locale
|
|
725
|
+
const effectiveLocale = body.locale
|
|
726
|
+
? resolveConfiguredLocale(body.locale)
|
|
727
|
+
: getI18nConfig()?.defaultLocale;
|
|
710
728
|
|
|
711
729
|
let slug: string | null | undefined = body.slug;
|
|
712
730
|
if (!slug) {
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
type UpdateMenuItemInput as UpdateMenuItemRepoInput,
|
|
26
26
|
} from "../../database/repositories/menu.js";
|
|
27
27
|
import type { Database } from "../../database/types.js";
|
|
28
|
-
import { getI18nConfig } from "../../i18n/config.js";
|
|
28
|
+
import { getI18nConfig, resolveConfiguredLocale } from "../../i18n/config.js";
|
|
29
29
|
import type { ApiResult } from "../types.js";
|
|
30
30
|
|
|
31
31
|
// Re-export entity types so route files and tests can import them from the
|
|
@@ -89,7 +89,8 @@ async function resolveMenu(
|
|
|
89
89
|
name: string,
|
|
90
90
|
options: { locale?: string },
|
|
91
91
|
): Promise<ResolveMenuResult> {
|
|
92
|
-
const
|
|
92
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
93
|
+
const matches = await repo.findByName(name, { locale });
|
|
93
94
|
if (matches.length === 0) {
|
|
94
95
|
return {
|
|
95
96
|
success: false,
|
|
@@ -124,7 +125,8 @@ export async function handleMenuList(
|
|
|
124
125
|
): Promise<ApiResult<MenuListItem[]>> {
|
|
125
126
|
try {
|
|
126
127
|
const repo = new MenuRepository(db);
|
|
127
|
-
const
|
|
128
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
129
|
+
const items = await repo.findMany({ locale });
|
|
128
130
|
return { success: true, data: items };
|
|
129
131
|
} catch {
|
|
130
132
|
return {
|
|
@@ -175,7 +177,9 @@ export async function handleMenuCreate(
|
|
|
175
177
|
|
|
176
178
|
// Duplicate guard: same (name, locale). Falls back to the configured
|
|
177
179
|
// defaultLocale to match the column DEFAULT set by migration 036.
|
|
178
|
-
const effectiveLocale = input.locale
|
|
180
|
+
const effectiveLocale = input.locale
|
|
181
|
+
? resolveConfiguredLocale(input.locale)
|
|
182
|
+
: (getI18nConfig()?.defaultLocale ?? "en");
|
|
179
183
|
if (await repo.existsByNameAndLocale(input.name, effectiveLocale)) {
|
|
180
184
|
return {
|
|
181
185
|
success: false,
|
|
@@ -188,7 +192,7 @@ export async function handleMenuCreate(
|
|
|
188
192
|
};
|
|
189
193
|
}
|
|
190
194
|
|
|
191
|
-
const menu = await repo.create(input);
|
|
195
|
+
const menu = await repo.create({ ...input, locale: effectiveLocale });
|
|
192
196
|
return { success: true, data: menu };
|
|
193
197
|
} catch {
|
|
194
198
|
return {
|
|
@@ -212,7 +216,8 @@ export async function handleMenuGet(
|
|
|
212
216
|
): Promise<ApiResult<MenuWithItems>> {
|
|
213
217
|
try {
|
|
214
218
|
const repo = new MenuRepository(db);
|
|
215
|
-
const
|
|
219
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
220
|
+
const matches = await repo.findByName(name, { locale });
|
|
216
221
|
if (matches.length === 0) {
|
|
217
222
|
return {
|
|
218
223
|
success: false,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { InvalidCursorError } from "../../database/repositories/types.js";
|
|
11
11
|
import type { ContentItem } from "../../database/repositories/types.js";
|
|
12
12
|
import type { Database } from "../../database/types.js";
|
|
13
|
+
import { resolveConfiguredLocale } from "../../i18n/config.js";
|
|
13
14
|
import { SchemaRegistry } from "../../schema/registry.js";
|
|
14
15
|
import type { ApiResult } from "../types.js";
|
|
15
16
|
|
|
@@ -70,7 +71,10 @@ export async function handleRelationCreate(
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
const relation = await repo.create(
|
|
74
|
+
const relation = await repo.create({
|
|
75
|
+
...input,
|
|
76
|
+
locale: input.locale ? resolveConfiguredLocale(input.locale) : undefined,
|
|
77
|
+
});
|
|
74
78
|
return { success: true, data: { relation } };
|
|
75
79
|
} catch (error) {
|
|
76
80
|
// A bad `translationOf` makes the repo throw loudly rather than mint an
|
|
@@ -127,7 +131,8 @@ export async function handleRelationList(
|
|
|
127
131
|
): Promise<ApiResult<{ relations: Relation[] }>> {
|
|
128
132
|
try {
|
|
129
133
|
const repo = new RelationRepository(db);
|
|
130
|
-
const
|
|
134
|
+
const locale = opts.locale ? resolveConfiguredLocale(opts.locale) : undefined;
|
|
135
|
+
const relations = await repo.list(locale);
|
|
131
136
|
return { success: true, data: { relations } };
|
|
132
137
|
} catch {
|
|
133
138
|
return {
|
|
@@ -12,6 +12,7 @@ import { ulid } from "ulidx";
|
|
|
12
12
|
|
|
13
13
|
import { TaxonomyRepository } from "../../database/repositories/taxonomy.js";
|
|
14
14
|
import type { Database, TaxonomyDefTable } from "../../database/types.js";
|
|
15
|
+
import { resolveConfiguredLocale } from "../../i18n/config.js";
|
|
15
16
|
import { invalidateTaxonomyDefsCache, invalidateTermCache } from "../../taxonomies/index.js";
|
|
16
17
|
import { fetchVisibleTermCounts } from "../../taxonomies/term-counts.js";
|
|
17
18
|
import type { ApiResult } from "../types.js";
|
|
@@ -174,8 +175,9 @@ export async function handleTaxonomyList(
|
|
|
174
175
|
options: { locale?: string } = {},
|
|
175
176
|
): Promise<ApiResult<TaxonomyListResponse>> {
|
|
176
177
|
try {
|
|
178
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
177
179
|
let query = db.selectFrom("_emdash_taxonomy_defs").selectAll();
|
|
178
|
-
if (
|
|
180
|
+
if (locale !== undefined) query = query.where("locale", "=", locale);
|
|
179
181
|
const [rows, collectionRows] = await Promise.all([
|
|
180
182
|
query.execute(),
|
|
181
183
|
db.selectFrom("_emdash_collections").select("slug").execute(),
|
|
@@ -216,6 +218,7 @@ export async function handleTaxonomyCreate(
|
|
|
216
218
|
},
|
|
217
219
|
): Promise<ApiResult<{ taxonomy: TaxonomyDef }>> {
|
|
218
220
|
try {
|
|
221
|
+
const locale = input.locale ? resolveConfiguredLocale(input.locale) : undefined;
|
|
219
222
|
if (!NAME_PATTERN.test(input.name)) {
|
|
220
223
|
return {
|
|
221
224
|
success: false,
|
|
@@ -265,19 +268,19 @@ export async function handleTaxonomyCreate(
|
|
|
265
268
|
|
|
266
269
|
// Duplicate guard scoped to locale (so the same name can exist in ES
|
|
267
270
|
// and EN).
|
|
268
|
-
if (
|
|
271
|
+
if (locale !== undefined) {
|
|
269
272
|
const existing = await db
|
|
270
273
|
.selectFrom("_emdash_taxonomy_defs")
|
|
271
274
|
.select("id")
|
|
272
275
|
.where("name", "=", input.name)
|
|
273
|
-
.where("locale", "=",
|
|
276
|
+
.where("locale", "=", locale)
|
|
274
277
|
.executeTakeFirst();
|
|
275
278
|
if (existing) {
|
|
276
279
|
return {
|
|
277
280
|
success: false,
|
|
278
281
|
error: {
|
|
279
282
|
code: "CONFLICT",
|
|
280
|
-
message: `Taxonomy '${input.name}' already exists in locale '${
|
|
283
|
+
message: `Taxonomy '${input.name}' already exists in locale '${locale}'`,
|
|
281
284
|
},
|
|
282
285
|
};
|
|
283
286
|
}
|
|
@@ -293,7 +296,7 @@ export async function handleTaxonomyCreate(
|
|
|
293
296
|
label_singular: input.labelSingular ?? null,
|
|
294
297
|
hierarchical: input.hierarchical ? 1 : 0,
|
|
295
298
|
collections: JSON.stringify(collections),
|
|
296
|
-
...(
|
|
299
|
+
...(locale !== undefined ? { locale } : {}),
|
|
297
300
|
translation_group: translationGroup ?? id,
|
|
298
301
|
})
|
|
299
302
|
.execute();
|
|
@@ -390,7 +393,8 @@ export async function handleTermList(
|
|
|
390
393
|
if (!lookup.success) return lookup;
|
|
391
394
|
|
|
392
395
|
const repo = new TaxonomyRepository(db);
|
|
393
|
-
const
|
|
396
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
397
|
+
const terms = await repo.findByName(taxonomyName, { locale });
|
|
394
398
|
|
|
395
399
|
// Counts match what visitors see on the public site: published (or
|
|
396
400
|
// scheduled-and-due) entries that aren't soft-deleted, scoped to the
|
|
@@ -535,6 +539,7 @@ export async function handleTermCreate(
|
|
|
535
539
|
},
|
|
536
540
|
): Promise<ApiResult<TermResponse>> {
|
|
537
541
|
try {
|
|
542
|
+
const locale = input.locale ? resolveConfiguredLocale(input.locale) : undefined;
|
|
538
543
|
// Taxonomy definitions are per-locale, but terms can exist in any locale
|
|
539
544
|
// regardless of whether the def has been translated there. Look up the
|
|
540
545
|
// def across all locales — we only care that it *exists*.
|
|
@@ -548,14 +553,14 @@ export async function handleTermCreate(
|
|
|
548
553
|
input.parentId === "" || input.parentId === undefined ? undefined : input.parentId;
|
|
549
554
|
|
|
550
555
|
// Conflict check is scoped to locale (per-locale slugs are unique).
|
|
551
|
-
const existing = await repo.findBySlug(taxonomyName, input.slug,
|
|
556
|
+
const existing = await repo.findBySlug(taxonomyName, input.slug, locale);
|
|
552
557
|
if (existing) {
|
|
553
558
|
return {
|
|
554
559
|
success: false,
|
|
555
560
|
error: {
|
|
556
561
|
code: "CONFLICT",
|
|
557
|
-
message:
|
|
558
|
-
? `Term '${input.slug}' already exists in '${taxonomyName}' (${
|
|
562
|
+
message: locale
|
|
563
|
+
? `Term '${input.slug}' already exists in '${taxonomyName}' (${locale})`
|
|
559
564
|
: `Term with slug '${input.slug}' already exists in taxonomy '${taxonomyName}'`,
|
|
560
565
|
},
|
|
561
566
|
};
|
|
@@ -594,7 +599,7 @@ export async function handleTermCreate(
|
|
|
594
599
|
label: input.label,
|
|
595
600
|
parentId: parentId ?? undefined,
|
|
596
601
|
data: input.description ? { description: input.description } : undefined,
|
|
597
|
-
locale
|
|
602
|
+
locale,
|
|
598
603
|
translationOf: input.translationOf,
|
|
599
604
|
});
|
|
600
605
|
|
|
@@ -635,7 +640,8 @@ export async function handleTermGet(
|
|
|
635
640
|
): Promise<ApiResult<TermGetResponse>> {
|
|
636
641
|
try {
|
|
637
642
|
const repo = new TaxonomyRepository(db);
|
|
638
|
-
const
|
|
643
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
644
|
+
const term = await repo.findBySlug(taxonomyName, termSlug, locale);
|
|
639
645
|
|
|
640
646
|
if (!term) {
|
|
641
647
|
return {
|
|
@@ -743,7 +749,8 @@ export async function handleTermUpdate(
|
|
|
743
749
|
): Promise<ApiResult<TermResponse>> {
|
|
744
750
|
try {
|
|
745
751
|
const repo = new TaxonomyRepository(db);
|
|
746
|
-
const
|
|
752
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
753
|
+
const term = await repo.findBySlug(taxonomyName, termSlug, locale);
|
|
747
754
|
|
|
748
755
|
if (!term) {
|
|
749
756
|
return {
|
|
@@ -763,7 +770,7 @@ export async function handleTermUpdate(
|
|
|
763
770
|
|
|
764
771
|
// Check if new slug conflicts (per-locale uniqueness).
|
|
765
772
|
if (newSlug !== undefined && newSlug !== termSlug) {
|
|
766
|
-
const existing = await repo.findBySlug(taxonomyName, newSlug,
|
|
773
|
+
const existing = await repo.findBySlug(taxonomyName, newSlug, locale);
|
|
767
774
|
if (existing && existing.id !== term.id) {
|
|
768
775
|
return {
|
|
769
776
|
success: false,
|
|
@@ -832,7 +839,8 @@ export async function handleTermDelete(
|
|
|
832
839
|
): Promise<ApiResult<{ deleted: true }>> {
|
|
833
840
|
try {
|
|
834
841
|
const repo = new TaxonomyRepository(db);
|
|
835
|
-
const
|
|
842
|
+
const locale = options.locale ? resolveConfiguredLocale(options.locale) : undefined;
|
|
843
|
+
const term = await repo.findBySlug(taxonomyName, termSlug, locale);
|
|
836
844
|
|
|
837
845
|
if (!term) {
|
|
838
846
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import { cursorPaginationQuery, httpUrl } from "./common.js";
|
|
3
|
+
import { cursorPaginationQuery, httpUrl, localeCode } from "./common.js";
|
|
4
4
|
|
|
5
5
|
/** Slug pattern: lowercase letters, digits, and hyphens; must start with a letter */
|
|
6
6
|
const bylineSlugPattern = /^[a-z][a-z0-9-]*$/;
|
|
@@ -81,7 +81,7 @@ export const bylinesListQuery = cursorPaginationQuery
|
|
|
81
81
|
* Rejects empty strings so the picker can't silently fetch the
|
|
82
82
|
* unfiltered list when the admin URL has `?locale=` with no value.
|
|
83
83
|
*/
|
|
84
|
-
locale:
|
|
84
|
+
locale: localeCode.optional(),
|
|
85
85
|
})
|
|
86
86
|
.meta({ id: "BylinesListQuery" });
|
|
87
87
|
|
|
@@ -102,7 +102,7 @@ export const bylineCreateBody = z
|
|
|
102
102
|
* configured `defaultLocale`) is used. Rejects empty strings — an
|
|
103
103
|
* empty locale would create rows no resolver requests.
|
|
104
104
|
*/
|
|
105
|
-
locale:
|
|
105
|
+
locale: localeCode.optional(),
|
|
106
106
|
/**
|
|
107
107
|
* When set, the new row joins the source byline's translation_group
|
|
108
108
|
* rather than minting a fresh one. Requires `locale`.
|
|
@@ -126,7 +126,7 @@ export const bylineCreateBody = z
|
|
|
126
126
|
|
|
127
127
|
export const bylineTranslationCreateBody = z
|
|
128
128
|
.object({
|
|
129
|
-
locale:
|
|
129
|
+
locale: localeCode,
|
|
130
130
|
slug: z
|
|
131
131
|
.string()
|
|
132
132
|
.min(1)
|
|
@@ -53,16 +53,17 @@ export const httpUrl = z
|
|
|
53
53
|
.url()
|
|
54
54
|
.refine((url) => HTTP_SCHEME_RE.test(url), "URL must use http or https");
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
/**
|
|
57
|
+
* BCP 47 locale code — language with optional script/region subtags (e.g. en, en-US, pt-BR, es-419, zh-Hant).
|
|
58
|
+
* Validation is case-insensitive, but the value is preserved verbatim because the site config, stored
|
|
59
|
+
* `locale` columns, and public query path all keep the raw BCP-47 casing.
|
|
60
|
+
*/
|
|
61
|
+
export const localeCode = z.string().regex(/^[a-z]{2,3}(-[a-z0-9]{2,8})*$/i, "Invalid locale code");
|
|
61
62
|
|
|
62
63
|
/** Shared `?locale=xx` query shape for endpoints that filter by locale. */
|
|
63
64
|
export const localeFilterQuery = z
|
|
64
65
|
.object({
|
|
65
|
-
locale:
|
|
66
|
+
locale: localeCode.optional(),
|
|
66
67
|
})
|
|
67
68
|
.meta({ id: "LocaleFilterQuery" });
|
|
68
69
|
|
package/src/api/schemas/menus.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
import { isSafeHref } from "../../utils/url.js";
|
|
4
|
+
import { localeCode } from "./common.js";
|
|
4
5
|
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// Menus: Input schemas
|
|
@@ -24,7 +25,7 @@ export const createMenuBody = z
|
|
|
24
25
|
.object({
|
|
25
26
|
name: z.string().min(1),
|
|
26
27
|
label: z.string().min(1),
|
|
27
|
-
locale:
|
|
28
|
+
locale: localeCode.optional(),
|
|
28
29
|
/** When set, clones the items from the source menu. The new menu joins
|
|
29
30
|
* the source's translation_group. */
|
|
30
31
|
translationOf: z.string().min(1).optional(),
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
import { localeCode } from "./common.js";
|
|
4
|
+
|
|
3
5
|
const slugPattern = /^[a-z][a-z0-9_]*$/;
|
|
4
6
|
const collectionSlug = z
|
|
5
7
|
.string()
|
|
@@ -18,7 +20,7 @@ export const createRelationBody = z
|
|
|
18
20
|
childCollection: collectionSlug.optional(),
|
|
19
21
|
parentLabel: z.string().min(1).max(200),
|
|
20
22
|
childLabel: z.string().min(1).max(200),
|
|
21
|
-
locale:
|
|
23
|
+
locale: localeCode.optional(),
|
|
22
24
|
translationOf: z.string().min(1).optional(),
|
|
23
25
|
})
|
|
24
26
|
// A translation inherits its structural fields (name, parentCollection,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
import { localeCode } from "./common.js";
|
|
4
|
+
|
|
3
5
|
// ---------------------------------------------------------------------------
|
|
4
6
|
// Taxonomy definitions: Input schemas
|
|
5
7
|
// ---------------------------------------------------------------------------
|
|
@@ -24,7 +26,7 @@ export const createTaxonomyDefBody = z
|
|
|
24
26
|
.max(100)
|
|
25
27
|
.optional()
|
|
26
28
|
.default([]),
|
|
27
|
-
locale:
|
|
29
|
+
locale: localeCode.optional(),
|
|
28
30
|
translationOf: z.string().min(1).optional(),
|
|
29
31
|
})
|
|
30
32
|
.meta({ id: "CreateTaxonomyDefBody" });
|
|
@@ -39,7 +41,7 @@ export const createTermBody = z
|
|
|
39
41
|
label: z.string().min(1),
|
|
40
42
|
parentId: z.string().nullish(),
|
|
41
43
|
description: z.string().optional(),
|
|
42
|
-
locale:
|
|
44
|
+
locale: localeCode.optional(),
|
|
43
45
|
translationOf: z.string().min(1).optional(),
|
|
44
46
|
})
|
|
45
47
|
.meta({ id: "CreateTermBody" });
|
package/src/astro/prefetch.ts
CHANGED
|
@@ -40,10 +40,15 @@ async function prefetchWidgetAreas(): Promise<void> {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Warm every taxonomy's term list via the real helper (primes per-name keys).
|
|
45
|
+
* Counts are left out: they cost an aggregate over the whole assignment pivot
|
|
46
|
+
* per taxonomy, and only a consumer that renders one can say it's needed. A
|
|
47
|
+
* consumer that does asks for it and reuses the term list warmed here.
|
|
48
|
+
*/
|
|
44
49
|
async function prefetchTaxonomyTerms(): Promise<void> {
|
|
45
50
|
const defs = await getTaxonomyDefs();
|
|
46
|
-
await Promise.allSettled(defs.map((def) => getTaxonomyTerms(def.name)));
|
|
51
|
+
await Promise.allSettled(defs.map((def) => getTaxonomyTerms(def.name, { includeCounts: false })));
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
/** Warm every menu via the real helper (primes `menu:${name}:${locale}`). */
|
|
@@ -8,7 +8,7 @@ import { bylineCreateBody, bylinesListQuery } from "#api/schemas.js";
|
|
|
8
8
|
import { invalidateBylineCache } from "#bylines/index.js";
|
|
9
9
|
import { BylineRepository } from "#db/repositories/byline.js";
|
|
10
10
|
|
|
11
|
-
import { getI18nConfig } from "../../../../../i18n/config.js";
|
|
11
|
+
import { getI18nConfig, resolveConfiguredLocale } from "../../../../../i18n/config.js";
|
|
12
12
|
|
|
13
13
|
export const prerender = false;
|
|
14
14
|
|
|
@@ -25,13 +25,10 @@ export const GET: APIRoute = async ({ url, locals }) => {
|
|
|
25
25
|
const query = parseQuery(url, bylinesListQuery);
|
|
26
26
|
if (isParseError(query)) return query;
|
|
27
27
|
|
|
28
|
+
const locale = query.locale ? resolveConfiguredLocale(query.locale) : undefined;
|
|
28
29
|
const i18n = getI18nConfig();
|
|
29
|
-
if (
|
|
30
|
-
return apiError(
|
|
31
|
-
"VALIDATION_ERROR",
|
|
32
|
-
`Locale "${query.locale}" is not configured for this site`,
|
|
33
|
-
400,
|
|
34
|
-
);
|
|
30
|
+
if (locale && i18n && !i18n.locales.includes(locale)) {
|
|
31
|
+
return apiError("VALIDATION_ERROR", `Locale "${locale}" is not configured for this site`, 400);
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
try {
|
|
@@ -40,7 +37,7 @@ export const GET: APIRoute = async ({ url, locals }) => {
|
|
|
40
37
|
search: query.search,
|
|
41
38
|
isGuest: query.isGuest,
|
|
42
39
|
userId: query.userId,
|
|
43
|
-
locale
|
|
40
|
+
locale,
|
|
44
41
|
cursor: query.cursor,
|
|
45
42
|
limit: query.limit,
|
|
46
43
|
});
|
|
@@ -69,11 +69,16 @@ function getCsrfCookie(request: Request): string | null {
|
|
|
69
69
|
|
|
70
70
|
const SCOPE_LABELS: Record<string, string> = {
|
|
71
71
|
"content:read": "Read content (posts, pages, etc.)",
|
|
72
|
-
"content:write": "Create, edit, and delete content",
|
|
72
|
+
"content:write": "Create, edit, and delete content; manage menus and taxonomies",
|
|
73
73
|
"media:read": "View media files",
|
|
74
74
|
"media:write": "Upload and manage media files",
|
|
75
75
|
"schema:read": "View collection schemas",
|
|
76
76
|
"schema:write": "Create and modify collection schemas",
|
|
77
|
+
"taxonomies:manage": "Manage taxonomies",
|
|
78
|
+
"menus:manage": "Manage navigation menus",
|
|
79
|
+
"settings:read": "View site settings",
|
|
80
|
+
"settings:manage": "Modify site settings",
|
|
81
|
+
"mcp:tools": "Use MCP tools from all enabled plugins",
|
|
77
82
|
admin: "Full administrative access",
|
|
78
83
|
};
|
|
79
84
|
|
|
@@ -141,7 +146,9 @@ export const GET: APIRoute = async ({ url, request, locals }) => {
|
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
// Parse and validate scopes
|
|
144
|
-
const requestedScopes =
|
|
149
|
+
const requestedScopes = [
|
|
150
|
+
...new Set((scope ?? "").split(" ").filter(Boolean).filter(isValidScope)),
|
|
151
|
+
];
|
|
145
152
|
|
|
146
153
|
if (requestedScopes.length === 0) {
|
|
147
154
|
return new Response(renderErrorPage("No valid scopes requested."), {
|
|
@@ -201,6 +208,21 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
201
208
|
const v = formData.get(name);
|
|
202
209
|
return typeof v === "string" ? v : fallback;
|
|
203
210
|
};
|
|
211
|
+
const requestedScopes = new Set(
|
|
212
|
+
(new URL(request.url).searchParams.get("scope") ?? "")
|
|
213
|
+
.split(" ")
|
|
214
|
+
.filter(Boolean)
|
|
215
|
+
.filter(isValidScope),
|
|
216
|
+
);
|
|
217
|
+
const selectedScopes = [
|
|
218
|
+
...new Set(
|
|
219
|
+
formData
|
|
220
|
+
.getAll("scope")
|
|
221
|
+
.filter((value): value is string => typeof value === "string")
|
|
222
|
+
.filter(isValidScope)
|
|
223
|
+
.filter((scope) => requestedScopes.has(scope)),
|
|
224
|
+
),
|
|
225
|
+
];
|
|
204
226
|
|
|
205
227
|
// SEC-18: Validate CSRF token (double-submit cookie pattern).
|
|
206
228
|
// The form includes a hidden csrf_token field; the cookie has the same value.
|
|
@@ -285,7 +307,7 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
285
307
|
response_type: field("response_type", "code"),
|
|
286
308
|
client_id: field("client_id"),
|
|
287
309
|
redirect_uri: redirectUri,
|
|
288
|
-
scope:
|
|
310
|
+
scope: selectedScopes.join(" "),
|
|
289
311
|
state,
|
|
290
312
|
code_challenge: field("code_challenge"),
|
|
291
313
|
code_challenge_method: field("code_challenge_method", "S256"),
|
|
@@ -294,12 +316,16 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
|
|
294
316
|
|
|
295
317
|
if (!result.success) {
|
|
296
318
|
const errMsg = result.error?.message ?? "Authorization failed";
|
|
319
|
+
const invalidScope = result.error?.code === "INVALID_SCOPE";
|
|
297
320
|
// On error, redirect back with error params — use generic description to avoid
|
|
298
321
|
// leaking internal error details to the (already-validated) redirect target
|
|
299
322
|
try {
|
|
300
323
|
const errorUrl = new URL(redirectUri);
|
|
301
|
-
errorUrl.searchParams.set("error", "server_error");
|
|
302
|
-
errorUrl.searchParams.set(
|
|
324
|
+
errorUrl.searchParams.set("error", invalidScope ? "invalid_scope" : "server_error");
|
|
325
|
+
errorUrl.searchParams.set(
|
|
326
|
+
"error_description",
|
|
327
|
+
invalidScope ? "No selected permission can be granted" : "Authorization failed",
|
|
328
|
+
);
|
|
303
329
|
if (state) errorUrl.searchParams.set("state", state);
|
|
304
330
|
return Response.redirect(errorUrl.toString(), 302);
|
|
305
331
|
} catch {
|
|
@@ -331,8 +357,8 @@ function renderConsentPage(params: {
|
|
|
331
357
|
}): string {
|
|
332
358
|
const scopeList = params.scopes
|
|
333
359
|
.map((s) => {
|
|
334
|
-
const label = SCOPE_LABELS[s] ?? s;
|
|
335
|
-
return `<li>${escapeHtml(label)}</li>`;
|
|
360
|
+
const label = SCOPE_LABELS[s] ?? getPluginScopeLabel(s) ?? s;
|
|
361
|
+
return `<li><label><input type="checkbox" name="scope" value="${escapeHtml(s)}" checked><span>${escapeHtml(label)}</span></label></li>`;
|
|
336
362
|
})
|
|
337
363
|
.join("\n");
|
|
338
364
|
|
|
@@ -353,6 +379,8 @@ function renderConsentPage(params: {
|
|
|
353
379
|
ul { list-style: none; margin-bottom: 1.5rem; }
|
|
354
380
|
li { padding: 0.5rem 0; border-bottom: 1px solid #262626; font-size: 0.875rem; }
|
|
355
381
|
li:last-child { border-bottom: none; }
|
|
382
|
+
label { display: flex; align-items: flex-start; gap: 0.625rem; cursor: pointer; }
|
|
383
|
+
input[type="checkbox"] { margin-top: 0.125rem; accent-color: #2563eb; }
|
|
356
384
|
.actions { display: flex; gap: 0.75rem; }
|
|
357
385
|
button { flex: 1; padding: 0.625rem 1rem; border-radius: 8px; border: none; font-size: 0.875rem; font-weight: 500; cursor: pointer; }
|
|
358
386
|
.approve { background: #2563eb; color: white; }
|
|
@@ -366,14 +394,13 @@ function renderConsentPage(params: {
|
|
|
366
394
|
<h1>Authorize Application</h1>
|
|
367
395
|
<p class="client-id">${escapeHtml(params.clientId)}</p>
|
|
368
396
|
<p class="user">Signed in as <strong>${escapeHtml(params.userName)}</strong></p>
|
|
369
|
-
<h2>Permissions requested</h2>
|
|
370
|
-
<ul>${scopeList}</ul>
|
|
371
397
|
<form method="POST">
|
|
398
|
+
<h2>Permissions requested</h2>
|
|
399
|
+
<ul>${scopeList}</ul>
|
|
372
400
|
<input type="hidden" name="csrf_token" value="${escapeHtml(params.csrfToken)}">
|
|
373
401
|
<input type="hidden" name="response_type" value="${escapeHtml(params.responseType)}">
|
|
374
402
|
<input type="hidden" name="client_id" value="${escapeHtml(params.clientId)}">
|
|
375
403
|
<input type="hidden" name="redirect_uri" value="${escapeHtml(params.redirectUri)}">
|
|
376
|
-
<input type="hidden" name="scope" value="${escapeHtml(params.scopes.join(" "))}">
|
|
377
404
|
<input type="hidden" name="state" value="${escapeHtml(params.state)}">
|
|
378
405
|
<input type="hidden" name="code_challenge" value="${escapeHtml(params.codeChallenge)}">
|
|
379
406
|
<input type="hidden" name="code_challenge_method" value="${escapeHtml(params.codeChallengeMethod)}">
|
|
@@ -388,6 +415,11 @@ function renderConsentPage(params: {
|
|
|
388
415
|
</html>`;
|
|
389
416
|
}
|
|
390
417
|
|
|
418
|
+
function getPluginScopeLabel(scope: string): string | null {
|
|
419
|
+
const pluginId = scope.startsWith("mcp:tools:") ? scope.slice("mcp:tools:".length) : "";
|
|
420
|
+
return pluginId ? `Use MCP tools from ${pluginId}` : null;
|
|
421
|
+
}
|
|
422
|
+
|
|
391
423
|
function renderErrorPage(message: string): string {
|
|
392
424
|
return `<!DOCTYPE html>
|
|
393
425
|
<html lang="en">
|