emdash 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-hUy8vOB-.d.mts → adapters-DAAjOID4.d.mts} +1 -1
- package/dist/{adapters-hUy8vOB-.d.mts.map → adapters-DAAjOID4.d.mts.map} +1 -1
- package/dist/{allowed-origins-DjlhyfNN.mjs → allowed-origins-D5FxMUo8.mjs} +2 -2
- package/dist/{allowed-origins-DjlhyfNN.mjs.map → allowed-origins-D5FxMUo8.mjs.map} +1 -1
- package/dist/api/route-utils.d.mts +3 -3
- package/dist/api/route-utils.mjs +15 -15
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/api/schemas/index.mjs +3 -3
- package/dist/{api-BDuM4bEy.mjs → api-d_0wi9fW.mjs} +20 -20
- package/dist/{api-BDuM4bEy.mjs.map → api-d_0wi9fW.mjs.map} +1 -1
- package/dist/{api-tokens-ViYKeIp9.mjs → api-tokens-D6ppjIVi.mjs} +2 -2
- package/dist/{api-tokens-ViYKeIp9.mjs.map → api-tokens-D6ppjIVi.mjs.map} +1 -1
- package/dist/{apply-wDqQoxX4.mjs → apply-BE9UaWX0.mjs} +17 -17
- package/dist/{apply-wDqQoxX4.mjs.map → apply-BE9UaWX0.mjs.map} +1 -1
- package/dist/astro/index.d.mts +11 -11
- package/dist/astro/index.mjs +36 -11
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +9 -9
- package/dist/astro/middleware/auth.mjs +7 -7
- package/dist/astro/middleware/redirect.mjs +5 -5
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +5 -3
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +362 -141
- 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 +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 +4 -4
- 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 +13 -12
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +13 -12
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs.map +1 -1
- package/dist/astro/routes/api/admin/bylines/index.mjs +13 -12
- package/dist/astro/routes/api/admin/bylines/index.mjs.map +1 -1
- 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 +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/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 +35 -34
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +35 -34
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/index.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/index.mjs.map +1 -1
- 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 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +35 -34
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +35 -34
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +34 -33
- package/dist/astro/routes/api/admin/plugins/updates.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +34 -33
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs.map +1 -1
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +34 -33
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs.map +1 -1
- 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 +3 -3
- package/dist/astro/routes/api/admin/users/index.mjs +6 -6
- 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 +9 -9
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/me.mjs +7 -7
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +11 -11
- 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 +9 -9
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +12 -12
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.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 +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 +10 -10
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +7 -7
- 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 +12 -12
- 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 +3 -3
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +9 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs +10 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/media.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +9 -9
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +11 -11
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +13 -12
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs.map +1 -1
- package/dist/astro/routes/api/manifest.mjs +4 -4
- 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_.mjs +7 -7
- 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 +9 -9
- package/dist/astro/routes/api/media.mjs +12 -12
- 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.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
- package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
- 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 +4 -4
- 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 +34 -33
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +34 -33
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +34 -33
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +34 -33
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/collections/index.mjs +34 -33
- package/dist/astro/routes/api/schema/collections/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/index.mjs +8 -7
- package/dist/astro/routes/api/schema/index.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +34 -33
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs.map +1 -1
- package/dist/astro/routes/api/schema/orphans/index.mjs +34 -33
- package/dist/astro/routes/api/schema/orphans/index.mjs.map +1 -1
- package/dist/astro/routes/api/search/enable.mjs +10 -10
- package/dist/astro/routes/api/search/index.mjs +9 -9
- package/dist/astro/routes/api/search/rebuild.mjs +10 -10
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +9 -9
- package/dist/astro/routes/api/sections/_slug_.mjs +9 -9
- package/dist/astro/routes/api/sections/index.mjs +9 -9
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +11 -11
- 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 -22
- package/dist/astro/routes/api/setup/dev-bypass.mjs.map +1 -1
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +24 -23
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- 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 +12 -12
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +12 -12
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +12 -12
- package/dist/astro/routes/api/taxonomies/index.mjs +12 -12
- package/dist/astro/routes/api/themes/preview.mjs +5 -5
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/well-known/auth.mjs +1 -1
- package/dist/astro/routes/api/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 +7 -7
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +9 -9
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +9 -9
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +9 -9
- package/dist/astro/routes/api/widget-components.mjs +3 -3
- package/dist/astro/routes/robots.txt.mjs +5 -5
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +8 -8
- package/dist/astro/routes/sitemap.xml.mjs +6 -6
- package/dist/astro/types.d.mts +12 -12
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{authorize-C0EYoUeV.mjs → authorize-B__u-iZJ.mjs} +2 -2
- package/dist/{authorize-C0EYoUeV.mjs.map → authorize-B__u-iZJ.mjs.map} +1 -1
- package/dist/{byline-CC0WS4Gu.mjs → byline-C-YajfV7.mjs} +18 -76
- package/dist/byline-C-YajfV7.mjs.map +1 -0
- package/dist/{byline-fields-53i9Et0x.mjs → byline-fields-B-KXkg5R.mjs} +2 -2
- package/dist/{byline-fields-53i9Et0x.mjs.map → byline-fields-B-KXkg5R.mjs.map} +1 -1
- package/dist/{byline-fields-DNp0HNKc.mjs → byline-fields-DaMKzkhO.mjs} +1 -1
- package/dist/{byline-fields-DNp0HNKc.mjs.map → byline-fields-DaMKzkhO.mjs.map} +1 -1
- package/dist/{byline-fields-jy07PZJM.d.mts → byline-fields-RFPkDLRw.d.mts} +21 -1
- package/dist/byline-fields-RFPkDLRw.d.mts.map +1 -0
- package/dist/{bylines-CCO1CYnH.mjs → bylines-BMtJnPnG.mjs} +7 -7
- package/dist/{bylines-CCO1CYnH.mjs.map → bylines-BMtJnPnG.mjs.map} +1 -1
- package/dist/{bylines-DGekMGGm.mjs → bylines-D4KADDAi.mjs} +4 -4
- package/dist/{bylines-DGekMGGm.mjs.map → bylines-D4KADDAi.mjs.map} +1 -1
- package/dist/{cache-pXTpun6s.mjs → cache-BUJe2w6Z.mjs} +2 -2
- package/dist/{cache-pXTpun6s.mjs.map → cache-BUJe2w6Z.mjs.map} +1 -1
- package/dist/{challenge-store-C6E_kWTs.mjs → challenge-store-LhiqMccz.mjs} +1 -1
- package/dist/{challenge-store-C6E_kWTs.mjs.map → challenge-store-LhiqMccz.mjs.map} +1 -1
- package/dist/{chunks-SyjZaYwV.mjs → chunks-BHgcHh2C.mjs} +2 -2
- package/dist/{chunks-SyjZaYwV.mjs.map → chunks-BHgcHh2C.mjs.map} +1 -1
- package/dist/cli/index.mjs +25 -24
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +2 -2
- package/dist/{comment-80UyJJUR.mjs → comment-BDx50EX5.mjs} +2 -2
- package/dist/{comment-80UyJJUR.mjs.map → comment-BDx50EX5.mjs.map} +1 -1
- package/dist/{comment-reaction-B6AdcpXw.mjs → comment-reaction-Gv_pgfI3.mjs} +2 -2
- package/dist/{comment-reaction-B6AdcpXw.mjs.map → comment-reaction-Gv_pgfI3.mjs.map} +1 -1
- package/dist/{comments-CtPkT2tp.mjs → comments-CXHuQ6jI.mjs} +3 -3
- package/dist/{comments-CtPkT2tp.mjs.map → comments-CXHuQ6jI.mjs.map} +1 -1
- package/dist/{components-D6ZnrzbB.mjs → components-CYt4uVK9.mjs} +1 -1
- package/dist/{components-D6ZnrzbB.mjs.map → components-CYt4uVK9.mjs.map} +1 -1
- package/dist/{content-2PTDNnoh.mjs → content-PoVGKjp0.mjs} +3 -3
- package/dist/{content-2PTDNnoh.mjs.map → content-PoVGKjp0.mjs.map} +1 -1
- package/dist/{context-f1__jpzF.mjs → context-Dvws41_A.mjs} +62 -24
- package/dist/context-Dvws41_A.mjs.map +1 -0
- package/dist/{cron-CQPxBjYs.mjs → cron-C5LVoNmP.mjs} +14 -2
- package/dist/cron-C5LVoNmP.mjs.map +1 -0
- package/dist/{dashboard-BOESNcwE.mjs → dashboard-DsYU8JKS.mjs} +4 -4
- package/dist/{dashboard-BOESNcwE.mjs.map → dashboard-DsYU8JKS.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/{db-errors-CrC5w3Jz.mjs → db-errors-UXB1CK42.mjs} +1 -1
- package/dist/{db-errors-CrC5w3Jz.mjs.map → db-errors-UXB1CK42.mjs.map} +1 -1
- package/dist/{default-CpBEPn6a.mjs → default-CeRG-Ot4.mjs} +1 -1
- package/dist/{default-CpBEPn6a.mjs.map → default-CeRG-Ot4.mjs.map} +1 -1
- package/dist/{device-flow-Bn-qiDrw.mjs → device-flow-DENDCQ9F.mjs} +4 -4
- package/dist/{device-flow-Bn-qiDrw.mjs.map → device-flow-DENDCQ9F.mjs.map} +1 -1
- package/dist/{email-console-B9VS6eN4.mjs → email-console-DJP32ucW.mjs} +1 -1
- package/dist/{email-console-B9VS6eN4.mjs.map → email-console-DJP32ucW.mjs.map} +1 -1
- package/dist/{error-BQ1pxs0L.mjs → error-lq_fL-RS.mjs} +2 -2
- package/dist/{error-BQ1pxs0L.mjs.map → error-lq_fL-RS.mjs.map} +1 -1
- package/dist/{escape-BtbJWyaW.mjs → escape-Bjio4ZsM.mjs} +1 -1
- package/dist/{escape-BtbJWyaW.mjs.map → escape-Bjio4ZsM.mjs.map} +1 -1
- package/dist/field-defs-cache-CYYx8xlG.mjs +77 -0
- package/dist/field-defs-cache-CYYx8xlG.mjs.map +1 -0
- package/dist/{fts-manager-DtiWqwNJ.mjs → fts-manager-DW_NR-Fs.mjs} +2 -2
- package/dist/{fts-manager-DtiWqwNJ.mjs.map → fts-manager-DW_NR-Fs.mjs.map} +1 -1
- package/dist/{hash-BowcuPwv.mjs → hash-DrvzIXcz.mjs} +1 -1
- package/dist/{hash-BowcuPwv.mjs.map → hash-DrvzIXcz.mjs.map} +1 -1
- package/dist/{import-DqSGOlmR.mjs → import-Bb1T9WJS.mjs} +3 -3
- package/dist/{import-DqSGOlmR.mjs.map → import-Bb1T9WJS.mjs.map} +1 -1
- package/dist/{index-C2dLNT6S.d.mts → index-BroXT-vD.d.mts} +136 -38
- package/dist/index-BroXT-vD.d.mts.map +1 -0
- package/dist/{index-ZDEwR7qW.d.mts → index-qbL02uhs.d.mts} +3 -3
- package/dist/{index-ZDEwR7qW.d.mts.map → index-qbL02uhs.d.mts.map} +1 -1
- package/dist/index.d.mts +17 -17
- package/dist/index.mjs +55 -54
- package/dist/{load-Dtoo7KcJ.mjs → load-DMFi4n1p.mjs} +2 -2
- package/dist/{load-Dtoo7KcJ.mjs.map → load-DMFi4n1p.mjs.map} +1 -1
- package/dist/{loader-C8Z48Uof.mjs → loader-BR2SgfEc.mjs} +77 -12
- package/dist/loader-BR2SgfEc.mjs.map +1 -0
- package/dist/{manifest-schema-DDSbwkAL.mjs → manifest-schema-BWKnQYQF.mjs} +1 -1
- package/dist/{manifest-schema-DDSbwkAL.mjs.map → manifest-schema-BWKnQYQF.mjs.map} +1 -1
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +2 -2
- package/dist/media/local-runtime.d.mts +19 -11
- package/dist/media/local-runtime.d.mts.map +1 -1
- package/dist/media/local-runtime.mjs +11 -9
- package/dist/media/local-runtime.mjs.map +1 -1
- package/dist/{media-Bw0sA6rb.mjs → media-CTjJoJTh.mjs} +2 -2
- package/dist/{media-Bw0sA6rb.mjs.map → media-CTjJoJTh.mjs.map} +1 -1
- package/dist/{media-allowlist-DunzrKFM.mjs → media-allowlist-B77I5_bw.mjs} +2 -2
- package/dist/{media-allowlist-DunzrKFM.mjs.map → media-allowlist-B77I5_bw.mjs.map} +1 -1
- package/dist/{media-url-S22B6aPr.mjs → media-url-O4rm9-SQ.mjs} +1 -1
- package/dist/{media-url-S22B6aPr.mjs.map → media-url-O4rm9-SQ.mjs.map} +1 -1
- package/dist/{menus-CYs0WZoL.mjs → menus-8O6xNcYW.mjs} +2 -2
- package/dist/{menus-CYs0WZoL.mjs.map → menus-8O6xNcYW.mjs.map} +1 -1
- package/dist/{menus-DOs1MQJg.mjs → menus-GvRSpgIA.mjs} +57 -14
- package/dist/menus-GvRSpgIA.mjs.map +1 -0
- package/dist/{mime-DYpsOAny.mjs → mime-BTzvzJ8M.mjs} +1 -1
- package/dist/{mime-DYpsOAny.mjs.map → mime-BTzvzJ8M.mjs.map} +1 -1
- package/dist/{mode-CFh8Dw8G.mjs → mode-BB0F8xTC.mjs} +1 -1
- package/dist/{mode-CFh8Dw8G.mjs.map → mode-BB0F8xTC.mjs.map} +1 -1
- package/dist/{normalize-Uam4_Vkr.mjs → normalize-ClbNIenm.mjs} +1 -1
- package/dist/{normalize-Uam4_Vkr.mjs.map → normalize-ClbNIenm.mjs.map} +1 -1
- package/dist/{oauth-authorization-BN-t83Uy.mjs → oauth-authorization-CsvzIp_F.mjs} +4 -4
- package/dist/{oauth-authorization-BN-t83Uy.mjs.map → oauth-authorization-CsvzIp_F.mjs.map} +1 -1
- package/dist/{oauth-clients-R6I_V0qz.mjs → oauth-clients-C9SYwEbZ.mjs} +1 -1
- package/dist/{oauth-clients-R6I_V0qz.mjs.map → oauth-clients-C9SYwEbZ.mjs.map} +1 -1
- package/dist/{oauth-state-store-Csoc6kkR.mjs → oauth-state-store---zrApfB.mjs} +1 -1
- package/dist/{oauth-state-store-Csoc6kkR.mjs.map → oauth-state-store---zrApfB.mjs.map} +1 -1
- package/dist/{oauth-user-lookup-d7VYjTpx.mjs → oauth-user-lookup-SHsWRlG9.mjs} +1 -1
- package/dist/{oauth-user-lookup-d7VYjTpx.mjs.map → oauth-user-lookup-SHsWRlG9.mjs.map} +1 -1
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/{options-DFFpvNJU.d.mts → options-CqECG0gM.d.mts} +3 -3
- package/dist/{options-DFFpvNJU.d.mts.map → options-CqECG0gM.d.mts.map} +1 -1
- package/dist/page/index.d.mts +2 -2
- package/dist/{parse-qrHlnzTy.mjs → parse-DHI873qU.mjs} +2 -2
- package/dist/{parse-qrHlnzTy.mjs.map → parse-DHI873qU.mjs.map} +1 -1
- package/dist/{passkey-config-BJTbcnI5.mjs → passkey-config-C0YfSBko.mjs} +1 -1
- package/dist/{passkey-config-BJTbcnI5.mjs.map → passkey-config-C0YfSBko.mjs.map} +1 -1
- package/dist/{placeholder-CIDCpyEY.mjs → placeholder-B0s2mWu5.mjs} +1 -1
- package/dist/{placeholder-CIDCpyEY.mjs.map → placeholder-B0s2mWu5.mjs.map} +1 -1
- package/dist/{placeholder-CirPauNG.d.mts → placeholder-QoI4gEzP.d.mts} +1 -1
- package/dist/{placeholder-CirPauNG.d.mts.map → placeholder-QoI4gEzP.d.mts.map} +1 -1
- package/dist/plugin-types.d.mts +1 -1
- package/dist/plugin-utils.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +9 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +2 -2
- package/dist/{portable-text-BIFhrU6T.mjs → portable-text-BICg8bTk.mjs} +1 -1
- package/dist/{portable-text-BIFhrU6T.mjs.map → portable-text-BICg8bTk.mjs.map} +1 -1
- package/dist/{preview-IDye9SPQ.mjs → preview-DKGCt2_p.mjs} +2 -2
- package/dist/{preview-IDye9SPQ.mjs.map → preview-DKGCt2_p.mjs.map} +1 -1
- package/dist/{public-url-8lFyQ8ZF.mjs → public-url-CTVqgMmg.mjs} +1 -1
- package/dist/{public-url-8lFyQ8ZF.mjs.map → public-url-CTVqgMmg.mjs.map} +1 -1
- package/dist/{query-B1RVFUNM.mjs → query-DZdlS6Cu.mjs} +92 -14
- package/dist/query-DZdlS6Cu.mjs.map +1 -0
- package/dist/{rate-limit-DfNAqIWW.mjs → rate-limit-D1Y6VFVP.mjs} +2 -2
- package/dist/{rate-limit-DfNAqIWW.mjs.map → rate-limit-D1Y6VFVP.mjs.map} +1 -1
- package/dist/{redirect-Bifv_yHv.mjs → redirect-C1KVGYWe.mjs} +2 -2
- package/dist/{redirect-Bifv_yHv.mjs.map → redirect-C1KVGYWe.mjs.map} +1 -1
- package/dist/{redirect-B4wuX2A5.mjs → redirect-CS-PHtNh.mjs} +1 -1
- package/dist/{redirect-B4wuX2A5.mjs.map → redirect-CS-PHtNh.mjs.map} +1 -1
- package/dist/{redirects-CT_vb3NV.mjs → redirects-DgnypVir.mjs} +4 -4
- package/dist/{redirects-CT_vb3NV.mjs.map → redirects-DgnypVir.mjs.map} +1 -1
- package/dist/{redirects-DUvXgzCX.mjs → redirects-x9eFPeyi.mjs} +4 -2
- package/dist/redirects-x9eFPeyi.mjs.map +1 -0
- package/dist/{registry-CjK96fHD.mjs → registry-CJkCubOJ.mjs} +4 -4
- package/dist/{registry-CjK96fHD.mjs.map → registry-CJkCubOJ.mjs.map} +1 -1
- package/dist/{request-meta-ei3ATilC.mjs → request-meta-CmS1tDFf.mjs} +2 -2
- package/dist/{request-meta-ei3ATilC.mjs.map → request-meta-CmS1tDFf.mjs.map} +1 -1
- package/dist/{resolve-CpbBglV0.mjs → resolve-C7I0qiR0.mjs} +1 -1
- package/dist/{resolve-CpbBglV0.mjs.map → resolve-C7I0qiR0.mjs.map} +1 -1
- package/dist/{runner-DYYnW530.mjs → runner-BsquExlK.mjs} +246 -174
- package/dist/runner-BsquExlK.mjs.map +1 -0
- package/dist/{runner-jdXtFEd3.d.mts → runner-CEtq-6Sm.d.mts} +2 -2
- package/dist/{runner-jdXtFEd3.d.mts.map → runner-CEtq-6Sm.d.mts.map} +1 -1
- package/dist/runtime.d.mts +10 -10
- package/dist/runtime.mjs +2 -2
- package/dist/{schema-5z2Jfnbm.mjs → schema-DMjXuvNU.mjs} +4 -4
- package/dist/{schema-5z2Jfnbm.mjs.map → schema-DMjXuvNU.mjs.map} +1 -1
- package/dist/{search-D9FkoM-k.mjs → search-D1wKk3lc.mjs} +4 -4
- package/dist/{search-D9FkoM-k.mjs.map → search-D1wKk3lc.mjs.map} +1 -1
- package/dist/{secrets-BUE5UQys.mjs → secrets-BSf9pRRY.mjs} +2 -2
- package/dist/{secrets-BUE5UQys.mjs.map → secrets-BSf9pRRY.mjs.map} +1 -1
- package/dist/{sections-DWYZo6HK.mjs → sections-CCMic8dB.mjs} +3 -3
- package/dist/{sections-DWYZo6HK.mjs.map → sections-CCMic8dB.mjs.map} +1 -1
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +17 -16
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/{seo-Cu47j3mJ.mjs → seo-3QrZ1EPT.mjs} +2 -2
- package/dist/{seo-Cu47j3mJ.mjs.map → seo-3QrZ1EPT.mjs.map} +1 -1
- package/dist/{seo-DRnqnstF.mjs → seo-DCEF-AVr.mjs} +1 -1
- package/dist/{seo-DRnqnstF.mjs.map → seo-DCEF-AVr.mjs.map} +1 -1
- package/dist/{service-DTmtqCnw.mjs → service-CQHK_10l.mjs} +3 -3
- package/dist/{service-DTmtqCnw.mjs.map → service-CQHK_10l.mjs.map} +1 -1
- package/dist/{session-user-DmtPMNa1.mjs → session-user-B8aLtKAH.mjs} +1 -1
- package/dist/{session-user-DmtPMNa1.mjs.map → session-user-B8aLtKAH.mjs.map} +1 -1
- package/dist/{settings-DUrVDN0c.mjs → settings-C3Yi5Co3.mjs} +3 -3
- package/dist/{settings-DUrVDN0c.mjs.map → settings-C3Yi5Co3.mjs.map} +1 -1
- package/dist/{settings-yaxu9mU-.mjs → settings-D9rvaLuY.mjs} +4 -4
- package/dist/{settings-yaxu9mU-.mjs.map → settings-D9rvaLuY.mjs.map} +1 -1
- package/dist/{setup-complete-BWFHzgvZ.mjs → setup-complete-gEiySUc-.mjs} +1 -1
- package/dist/{setup-complete-BWFHzgvZ.mjs.map → setup-complete-gEiySUc-.mjs.map} +1 -1
- package/dist/{setup-nonce-DFWG-J5c.mjs → setup-nonce-DzS50zme.mjs} +1 -1
- package/dist/{setup-nonce-DFWG-J5c.mjs.map → setup-nonce-DzS50zme.mjs.map} +1 -1
- package/dist/{single-flight-cache-B5p5cNOL.mjs → single-flight-cache-Cdfkic3t.mjs} +1 -1
- package/dist/{single-flight-cache-B5p5cNOL.mjs.map → single-flight-cache-Cdfkic3t.mjs.map} +1 -1
- package/dist/{site-url-BOTPgmeM.mjs → site-url-BLebyON8.mjs} +1 -1
- package/dist/{site-url-BOTPgmeM.mjs.map → site-url-BLebyON8.mjs.map} +1 -1
- package/dist/{ssrf-B0VeRLrp.mjs → ssrf-BRKb343l.mjs} +1 -1
- package/dist/{ssrf-B0VeRLrp.mjs.map → ssrf-BRKb343l.mjs.map} +1 -1
- package/dist/{status-CB2basWJ.mjs → status-vUK0SA17.mjs} +1 -1
- package/dist/{status-CB2basWJ.mjs.map → status-vUK0SA17.mjs.map} +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/{taxonomies-Be9B1jEj.mjs → taxonomies-CurpLtNC.mjs} +52 -24
- package/dist/taxonomies-CurpLtNC.mjs.map +1 -0
- package/dist/{taxonomies-CJmkEpdS.mjs → taxonomies-DhLaNJji.mjs} +34 -27
- package/dist/taxonomies-DhLaNJji.mjs.map +1 -0
- package/dist/{taxonomy-B2HQuVf5.mjs → taxonomy-BBUlGug5.mjs} +33 -7
- package/dist/taxonomy-BBUlGug5.mjs.map +1 -0
- package/dist/{tokens-BvrJvIB6.mjs → tokens-LeuXF9gG.mjs} +1 -1
- package/dist/{tokens-BvrJvIB6.mjs.map → tokens-LeuXF9gG.mjs.map} +1 -1
- package/dist/{transport-k7YjfmEn.d.mts → transport-BZ-03aFT.d.mts} +1 -1
- package/dist/{transport-k7YjfmEn.d.mts.map → transport-BZ-03aFT.d.mts.map} +1 -1
- package/dist/{transport-B4rXnSTf.mjs → transport-_2nBz7e9.mjs} +1 -1
- package/dist/{transport-B4rXnSTf.mjs.map → transport-_2nBz7e9.mjs.map} +1 -1
- package/dist/{trusted-proxy-DY1_UqHr.mjs → trusted-proxy-DZY5WCn2.mjs} +1 -1
- package/dist/{trusted-proxy-DY1_UqHr.mjs.map → trusted-proxy-DZY5WCn2.mjs.map} +1 -1
- package/dist/{types-Czg1S3gB.d.mts → types-B-gIxFCE.d.mts} +1 -1
- package/dist/{types-Czg1S3gB.d.mts.map → types-B-gIxFCE.d.mts.map} +1 -1
- package/dist/{types-DXLVzV2w.d.mts → types-BG8_XTLj.d.mts} +1 -1
- package/dist/{types-DXLVzV2w.d.mts.map → types-BG8_XTLj.d.mts.map} +1 -1
- package/dist/{types-CWGYDYqF.d.mts → types-BWh2MjmA.d.mts} +15 -2
- package/dist/types-BWh2MjmA.d.mts.map +1 -0
- package/dist/{types-CgvhU-hF.d.mts → types-BWqGVhFn.d.mts} +1 -1
- package/dist/{types-CgvhU-hF.d.mts.map → types-BWqGVhFn.d.mts.map} +1 -1
- package/dist/{types-DYF2_Vf4.mjs → types-CQAugunJ.mjs} +1 -1
- package/dist/{types-DYF2_Vf4.mjs.map → types-CQAugunJ.mjs.map} +1 -1
- package/dist/{types-DPCpkzvS.d.mts → types-CTLJYMhN.d.mts} +1 -1
- package/dist/{types-DPCpkzvS.d.mts.map → types-CTLJYMhN.d.mts.map} +1 -1
- package/dist/{types-CmivgZbp.d.mts → types-CUsXGRVj.d.mts} +1 -1
- package/dist/{types-CmivgZbp.d.mts.map → types-CUsXGRVj.d.mts.map} +1 -1
- package/dist/{types-DXOIbece.mjs → types-CmI1OyQg.mjs} +2 -2
- package/dist/types-CmI1OyQg.mjs.map +1 -0
- package/dist/{types-CKNXt63n.d.mts → types-DrYMm9s5.d.mts} +1 -1
- package/dist/{types-CKNXt63n.d.mts.map → types-DrYMm9s5.d.mts.map} +1 -1
- package/dist/{types-CIKX2HO5.d.mts → types-TTx8uP7V.d.mts} +2 -2
- package/dist/{types-CIKX2HO5.d.mts.map → types-TTx8uP7V.d.mts.map} +1 -1
- package/dist/{types-DCNNfeCv.d.mts → types-fS7-VTdR.d.mts} +1 -1
- package/dist/{types-DCNNfeCv.d.mts.map → types-fS7-VTdR.d.mts.map} +1 -1
- package/dist/{user-Db-Rti_z.mjs → user-JAuc8VUf.mjs} +3 -3
- package/dist/{user-Db-Rti_z.mjs.map → user-JAuc8VUf.mjs.map} +1 -1
- package/dist/{utils-LbHcSRTj.mjs → utils-B7A57fm9.mjs} +1 -1
- package/dist/{utils-LbHcSRTj.mjs.map → utils-B7A57fm9.mjs.map} +1 -1
- package/dist/{validate-Bwl64sxv.mjs → validate-BzXHWnkV.mjs} +2 -2
- package/dist/{validate-Bwl64sxv.mjs.map → validate-BzXHWnkV.mjs.map} +1 -1
- package/dist/{validate-CE2M0nvx.d.mts → validate-DOFrIb-c.d.mts} +5 -5
- package/dist/{validate-CE2M0nvx.d.mts.map → validate-DOFrIb-c.d.mts.map} +1 -1
- package/dist/{validation-C_yOvFaO.mjs → validation-CY1gk4Ar.mjs} +6 -6
- package/dist/{validation-C_yOvFaO.mjs.map → validation-CY1gk4Ar.mjs.map} +1 -1
- package/dist/version-B5nHxI5Y.mjs +7 -0
- package/dist/{version-RnYXYjzD.mjs.map → version-B5nHxI5Y.mjs.map} +1 -1
- package/dist/{widgets-8GONuRHo.mjs → widgets-Dri0spI4.mjs} +3 -3
- package/dist/{widgets-8GONuRHo.mjs.map → widgets-Dri0spI4.mjs.map} +1 -1
- package/dist/{zod-generator-D481JjzD.mjs → zod-generator-xEHODMJf.mjs} +51 -15
- package/dist/zod-generator-xEHODMJf.mjs.map +1 -0
- package/package.json +8 -7
- package/src/api/handlers/taxonomies.ts +62 -32
- package/src/api/schemas/bylines.ts +8 -0
- package/src/astro/integration/shims/use-sync-external-store-with-selector.js +71 -0
- package/src/astro/integration/shims/use-sync-external-store.js +1 -0
- package/src/astro/integration/virtual-modules.ts +8 -0
- package/src/astro/integration/vite-config.ts +56 -9
- package/src/astro/middleware/request-context.ts +6 -1
- package/src/astro/middleware/scoped-db.ts +131 -0
- package/src/astro/middleware/stream-end-metrics.ts +4 -8
- package/src/astro/middleware.ts +103 -31
- package/src/astro/routes/admin.astro +5 -1
- package/src/astro/routes/api/schema/index.ts +9 -3
- package/src/cli/commands/export-seed.ts +15 -11
- package/src/components/Block.astro +71 -0
- package/src/components/EmDashMedia.astro +1 -2
- package/src/components/Image.astro +36 -2
- package/src/components/InlinePortableTextEditor.tsx +10 -2
- package/src/components/index.ts +5 -0
- package/src/components/portable-text-text-align.ts +38 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +5 -1
- package/src/content/converters/prosemirror-to-portable-text.ts +8 -0
- package/src/content/converters/types.ts +1 -0
- package/src/database/migrations/045_taxonomy_parent_group.ts +68 -0
- package/src/database/migrations/runner.ts +2 -0
- package/src/database/repositories/byline.ts +14 -0
- package/src/database/repositories/taxonomy.ts +50 -7
- package/src/database/repositories/types.ts +13 -0
- package/src/emdash-runtime.ts +226 -71
- package/src/loader.ts +161 -13
- package/src/media/local-runtime.ts +18 -5
- package/src/plugins/context.ts +94 -22
- package/src/plugins/cron.ts +18 -2
- package/src/plugins/routes.ts +40 -1
- package/src/query.ts +201 -19
- package/src/schema/zod-generator.ts +53 -8
- package/src/taxonomies/index.ts +46 -8
- package/src/ui.ts +5 -0
- package/src/virtual-modules.d.ts +13 -0
- package/dist/byline-CC0WS4Gu.mjs.map +0 -1
- package/dist/byline-fields-jy07PZJM.d.mts.map +0 -1
- package/dist/context-f1__jpzF.mjs.map +0 -1
- package/dist/cron-CQPxBjYs.mjs.map +0 -1
- package/dist/index-C2dLNT6S.d.mts.map +0 -1
- package/dist/loader-C8Z48Uof.mjs.map +0 -1
- package/dist/menus-DOs1MQJg.mjs.map +0 -1
- package/dist/query-B1RVFUNM.mjs.map +0 -1
- package/dist/redirects-DUvXgzCX.mjs.map +0 -1
- package/dist/runner-DYYnW530.mjs.map +0 -1
- package/dist/taxonomies-Be9B1jEj.mjs.map +0 -1
- package/dist/taxonomies-CJmkEpdS.mjs.map +0 -1
- package/dist/taxonomy-B2HQuVf5.mjs.map +0 -1
- package/dist/types-CWGYDYqF.d.mts.map +0 -1
- package/dist/types-DXOIbece.mjs.map +0 -1
- package/dist/version-RnYXYjzD.mjs +0 -7
- package/dist/zod-generator-D481JjzD.mjs.map +0 -1
- /package/dist/{api-tokens-BVH-H8Z9.mjs → api-tokens-B4BQybOp.mjs} +0 -0
- /package/dist/{ssrf-Cz1e2QPn.mjs → ssrf-CcX9zvMK.mjs} +0 -0
- /package/dist/{types-Bfr2Pj6z.mjs → types-BvbeGEtr.mjs} +0 -0
|
@@ -29,6 +29,14 @@ export interface LocalMediaRuntimeConfig {
|
|
|
29
29
|
enabled?: boolean;
|
|
30
30
|
// These are injected by the runtime, not from user config
|
|
31
31
|
db?: Kysely<Database>;
|
|
32
|
+
/**
|
|
33
|
+
* Resolver for the live connection, preferred over `db`. The runtime
|
|
34
|
+
* injects it so a connection-backed adapter (Postgres over Hyperdrive)
|
|
35
|
+
* serves provider queries from the current request-scoped connection in ALS
|
|
36
|
+
* rather than a snapshot of the per-isolate singleton. Omitted for stateless
|
|
37
|
+
* adapters (D1, Node SQLite), where `db` is used directly.
|
|
38
|
+
*/
|
|
39
|
+
getDb?: () => Kysely<Database>;
|
|
32
40
|
storage?: Storage;
|
|
33
41
|
}
|
|
34
42
|
|
|
@@ -42,11 +50,15 @@ export const createMediaProvider: CreateMediaProviderFn<LocalMediaRuntimeConfig>
|
|
|
42
50
|
throw new Error("Local media provider requires database connection");
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
// Resolve the connection per operation (not captured once) so a
|
|
54
|
+
// connection-backed adapter uses the current event-scoped connection; falls
|
|
55
|
+
// back to the injected `db` for stateless adapters.
|
|
56
|
+
const resolveDb = config.getDb ?? (() => db);
|
|
57
|
+
const repo = () => new MediaRepository(resolveDb());
|
|
46
58
|
|
|
47
59
|
const provider: MediaProvider = {
|
|
48
60
|
async list(options: MediaListOptions) {
|
|
49
|
-
const result = await repo.findMany({
|
|
61
|
+
const result = await repo().findMany({
|
|
50
62
|
cursor: options.cursor,
|
|
51
63
|
limit: options.limit,
|
|
52
64
|
mimeType: options.mimeType,
|
|
@@ -75,7 +87,7 @@ export const createMediaProvider: CreateMediaProviderFn<LocalMediaRuntimeConfig>
|
|
|
75
87
|
},
|
|
76
88
|
|
|
77
89
|
async get(id: string) {
|
|
78
|
-
const item = await repo.findById(id);
|
|
90
|
+
const item = await repo().findById(id);
|
|
79
91
|
if (!item) return null;
|
|
80
92
|
|
|
81
93
|
return {
|
|
@@ -108,7 +120,8 @@ export const createMediaProvider: CreateMediaProviderFn<LocalMediaRuntimeConfig>
|
|
|
108
120
|
},
|
|
109
121
|
|
|
110
122
|
async delete(id: string) {
|
|
111
|
-
const
|
|
123
|
+
const repoInstance = repo();
|
|
124
|
+
const item = await repoInstance.findById(id);
|
|
112
125
|
if (!item) return;
|
|
113
126
|
|
|
114
127
|
// Delete from storage if available
|
|
@@ -120,7 +133,7 @@ export const createMediaProvider: CreateMediaProviderFn<LocalMediaRuntimeConfig>
|
|
|
120
133
|
}
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
await
|
|
136
|
+
await repoInstance.delete(id);
|
|
124
137
|
|
|
125
138
|
// If this row was referenced by `logo`, `favicon`, or
|
|
126
139
|
// `seo.defaultOgImage`, the worker-scoped settings cache now
|
package/src/plugins/context.ts
CHANGED
|
@@ -432,23 +432,55 @@ export function createMediaAccess(db: Kysely<Database>): MediaAccess {
|
|
|
432
432
|
|
|
433
433
|
/**
|
|
434
434
|
* Create full media access with write operations.
|
|
435
|
-
*
|
|
435
|
+
*
|
|
436
|
+
* `getUploadUrlFn` is optional: when omitted, `getUploadUrl()` is derived from
|
|
437
|
+
* `storage` (create a pending record + a signed PUT URL), mirroring the REST
|
|
438
|
+
* `/_emdash/api/media/upload-url` endpoint. `upload()` only needs `storage`.
|
|
439
|
+
* If storage is not provided, both throw at call time.
|
|
436
440
|
*/
|
|
437
441
|
export function createMediaAccessWithWrite(
|
|
438
442
|
db: Kysely<Database>,
|
|
439
|
-
getUploadUrlFn:
|
|
440
|
-
filename: string,
|
|
441
|
-
|
|
442
|
-
) => Promise<{ uploadUrl: string; mediaId: string }>,
|
|
443
|
+
getUploadUrlFn:
|
|
444
|
+
| ((filename: string, contentType: string) => Promise<{ uploadUrl: string; mediaId: string }>)
|
|
445
|
+
| undefined,
|
|
443
446
|
storage?: Storage,
|
|
444
447
|
): MediaAccessWithWrite {
|
|
445
448
|
const mediaRepo = new MediaRepository(db);
|
|
446
449
|
const readAccess = createMediaAccess(db);
|
|
447
450
|
|
|
451
|
+
const getUploadUrl =
|
|
452
|
+
getUploadUrlFn ??
|
|
453
|
+
(async (filename: string, contentType: string) => {
|
|
454
|
+
if (!storage) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
"Media getUploadUrl() requires a storage backend. Configure storage in PluginContextFactoryOptions.",
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const basename = filename.split("/").pop() ?? filename;
|
|
461
|
+
const dotIdx = basename.lastIndexOf(".");
|
|
462
|
+
const ext = dotIdx > 0 ? basename.slice(dotIdx).toLowerCase() : "";
|
|
463
|
+
const storageKey = `${ulid()}${ext}`;
|
|
464
|
+
|
|
465
|
+
const media = await mediaRepo.createPending({
|
|
466
|
+
filename: basename,
|
|
467
|
+
mimeType: contentType,
|
|
468
|
+
storageKey,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const signed = await storage.getSignedUploadUrl({
|
|
472
|
+
key: storageKey,
|
|
473
|
+
contentType,
|
|
474
|
+
expiresIn: 3600,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
return { uploadUrl: signed.url, mediaId: media.id };
|
|
478
|
+
});
|
|
479
|
+
|
|
448
480
|
return {
|
|
449
481
|
...readAccess,
|
|
450
482
|
|
|
451
|
-
getUploadUrl
|
|
483
|
+
getUploadUrl,
|
|
452
484
|
|
|
453
485
|
async upload(
|
|
454
486
|
filename: string,
|
|
@@ -836,14 +868,26 @@ export function createUserAccess(db: Kysely<Database>): UserAccess {
|
|
|
836
868
|
|
|
837
869
|
export interface PluginContextFactoryOptions {
|
|
838
870
|
db: Kysely<Database>;
|
|
871
|
+
/**
|
|
872
|
+
* Resolver for the database connection, preferred over `db` when present.
|
|
873
|
+
* Called per `createContext()` so connection-backed adapters (e.g. Postgres
|
|
874
|
+
* over Hyperdrive) get the current request/event-scoped connection from ALS
|
|
875
|
+
* rather than a snapshot of the per-isolate singleton — reusing the
|
|
876
|
+
* singleton's socket from a later event trips workerd's cross-request I/O
|
|
877
|
+
* guard. When omitted, `db` is used directly (correct for stateless
|
|
878
|
+
* adapters like D1 and Node SQLite). `db` remains required as the fallback.
|
|
879
|
+
*/
|
|
880
|
+
getDb?: () => Kysely<Database>;
|
|
839
881
|
/**
|
|
840
882
|
* Storage backend for direct media uploads.
|
|
841
883
|
* If not provided, upload() will throw.
|
|
842
884
|
*/
|
|
843
885
|
storage?: Storage;
|
|
844
886
|
/**
|
|
845
|
-
*
|
|
846
|
-
*
|
|
887
|
+
* Explicit provider for `ctx.media.getUploadUrl()`. Optional: when omitted
|
|
888
|
+
* but `storage` is configured, the factory derives a working `getUploadUrl()`
|
|
889
|
+
* (and `upload()`) from storage. Only when neither `getUploadUrl` nor
|
|
890
|
+
* `storage` is present do media write operations become unavailable.
|
|
847
891
|
*/
|
|
848
892
|
getUploadUrl?: (
|
|
849
893
|
filename: string,
|
|
@@ -877,8 +921,7 @@ export interface PluginContextFactoryOptions {
|
|
|
877
921
|
* Factory for creating plugin contexts
|
|
878
922
|
*/
|
|
879
923
|
export class PluginContextFactory {
|
|
880
|
-
private
|
|
881
|
-
private db: Kysely<Database>;
|
|
924
|
+
private resolveDb: () => Kysely<Database>;
|
|
882
925
|
private storage?: Storage;
|
|
883
926
|
private getUploadUrl?: (
|
|
884
927
|
filename: string,
|
|
@@ -888,10 +931,16 @@ export class PluginContextFactory {
|
|
|
888
931
|
private urlHelper: (path: string) => string;
|
|
889
932
|
private cronReschedule?: () => void;
|
|
890
933
|
private emailPipeline?: EmailPipeline;
|
|
934
|
+
/**
|
|
935
|
+
* Plugin IDs already warned about a missing media-write backend, so the
|
|
936
|
+
* warning fires once per factory instead of on every hook/route context
|
|
937
|
+
* creation (which would spam logs for hook-participating plugins).
|
|
938
|
+
*/
|
|
939
|
+
private warnedMissingMediaBackend = new Set<string>();
|
|
891
940
|
|
|
892
941
|
constructor(options: PluginContextFactoryOptions) {
|
|
893
|
-
|
|
894
|
-
this.
|
|
942
|
+
const fixedDb = options.db;
|
|
943
|
+
this.resolveDb = options.getDb ?? (() => fixedDb);
|
|
895
944
|
this.storage = options.storage;
|
|
896
945
|
this.getUploadUrl = options.getUploadUrl;
|
|
897
946
|
this.site = createSiteInfo(options.siteInfo ?? {});
|
|
@@ -906,10 +955,17 @@ export class PluginContextFactory {
|
|
|
906
955
|
createContext(plugin: ResolvedPlugin): PluginContext {
|
|
907
956
|
const capabilities = new Set(plugin.capabilities);
|
|
908
957
|
|
|
958
|
+
// Resolve the connection once per context. For stateless adapters this
|
|
959
|
+
// is the singleton; for connection-backed adapters it's the current
|
|
960
|
+
// request/event-scoped connection from ALS. All repos below are built
|
|
961
|
+
// from this local `db` so a hook never queries a stale singleton socket.
|
|
962
|
+
const db = this.resolveDb();
|
|
963
|
+
const optionsRepo = new OptionsRepository(db);
|
|
964
|
+
|
|
909
965
|
// Always available
|
|
910
|
-
const kv = createKVAccess(
|
|
966
|
+
const kv = createKVAccess(optionsRepo, plugin.id);
|
|
911
967
|
const log = createLogAccess(plugin.id);
|
|
912
|
-
const storage = createStorageAccess(
|
|
968
|
+
const storage = createStorageAccess(db, plugin.id, plugin.storage);
|
|
913
969
|
|
|
914
970
|
// Capability-gated: content
|
|
915
971
|
// Note: capabilities reach this point already normalized to the
|
|
@@ -917,17 +973,33 @@ export class PluginContextFactory {
|
|
|
917
973
|
// names ("read:content", "write:content") never appear here.
|
|
918
974
|
let content: ContentAccess | ContentAccessWithWrite | undefined;
|
|
919
975
|
if (capabilities.has("content:write")) {
|
|
920
|
-
content = createContentAccessWithWrite(
|
|
976
|
+
content = createContentAccessWithWrite(db);
|
|
921
977
|
} else if (capabilities.has("content:read")) {
|
|
922
|
-
content = createContentAccess(
|
|
978
|
+
content = createContentAccess(db);
|
|
923
979
|
}
|
|
924
980
|
|
|
925
981
|
// Capability-gated: media
|
|
982
|
+
// `upload()` only needs `storage`; `getUploadUrl()` is derived from
|
|
983
|
+
// storage when no explicit provider is wired. Granting write access on
|
|
984
|
+
// either avoids silently degrading media:write to read-only — the bug
|
|
985
|
+
// where the runtime threads `storage` but not `getUploadUrl`.
|
|
926
986
|
let media: MediaAccess | MediaAccessWithWrite | undefined;
|
|
927
|
-
if (capabilities.has("media:write")
|
|
928
|
-
|
|
987
|
+
if (capabilities.has("media:write")) {
|
|
988
|
+
if (this.getUploadUrl || this.storage) {
|
|
989
|
+
media = createMediaAccessWithWrite(db, this.getUploadUrl, this.storage);
|
|
990
|
+
} else {
|
|
991
|
+
if (!this.warnedMissingMediaBackend.has(plugin.id)) {
|
|
992
|
+
this.warnedMissingMediaBackend.add(plugin.id);
|
|
993
|
+
log.warn(
|
|
994
|
+
"declares the media:write capability but no storage backend is configured; upload() is unavailable.",
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
if (capabilities.has("media:read")) {
|
|
998
|
+
media = createMediaAccess(db);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
929
1001
|
} else if (capabilities.has("media:read")) {
|
|
930
|
-
media = createMediaAccess(
|
|
1002
|
+
media = createMediaAccess(db);
|
|
931
1003
|
}
|
|
932
1004
|
|
|
933
1005
|
// Capability-gated: http
|
|
@@ -941,14 +1013,14 @@ export class PluginContextFactory {
|
|
|
941
1013
|
// Capability-gated: users
|
|
942
1014
|
let users: UserAccess | undefined;
|
|
943
1015
|
if (capabilities.has("users:read")) {
|
|
944
|
-
users = createUserAccess(
|
|
1016
|
+
users = createUserAccess(db);
|
|
945
1017
|
}
|
|
946
1018
|
|
|
947
|
-
// Cron access
|
|
1019
|
+
// Cron access — always available (scoped to plugin), but only if
|
|
948
1020
|
// the runtime provided a reschedule callback (i.e. cron is wired up).
|
|
949
1021
|
let cron: CronAccess | undefined;
|
|
950
1022
|
if (this.cronReschedule) {
|
|
951
|
-
cron = new CronAccessImpl(
|
|
1023
|
+
cron = new CronAccessImpl(db, plugin.id, this.cronReschedule);
|
|
952
1024
|
}
|
|
953
1025
|
|
|
954
1026
|
// Email access — requires email:send capability AND a configured provider
|
package/src/plugins/cron.ts
CHANGED
|
@@ -39,10 +39,26 @@ export type RescheduleFn = () => void;
|
|
|
39
39
|
* Stateless — all state lives in the database.
|
|
40
40
|
*/
|
|
41
41
|
export class CronExecutor {
|
|
42
|
+
/**
|
|
43
|
+
* Resolves the database connection to use for this tick. A resolver (not a
|
|
44
|
+
* captured instance) so connection-backed adapters work across events: on
|
|
45
|
+
* Cloudflare the `scheduled()` handler installs an event-scoped connection
|
|
46
|
+
* in ALS, and this resolves to it instead of the per-isolate singleton
|
|
47
|
+
* whose socket belongs to an earlier request. Accepts a plain `Kysely` too
|
|
48
|
+
* (wrapped in a constant resolver) for callers/tests that don't need ALS.
|
|
49
|
+
*/
|
|
50
|
+
private readonly resolveDb: () => Kysely<Database>;
|
|
51
|
+
|
|
42
52
|
constructor(
|
|
43
|
-
|
|
53
|
+
db: Kysely<Database> | (() => Kysely<Database>),
|
|
44
54
|
private invokeCronHook: InvokeCronHookFn,
|
|
45
|
-
) {
|
|
55
|
+
) {
|
|
56
|
+
this.resolveDb = typeof db === "function" ? db : () => db;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private get db(): Kysely<Database> {
|
|
60
|
+
return this.resolveDb();
|
|
61
|
+
}
|
|
46
62
|
|
|
47
63
|
/**
|
|
48
64
|
* Process all overdue tasks.
|
package/src/plugins/routes.ts
CHANGED
|
@@ -12,6 +12,42 @@ import { PluginContextFactory, type PluginContextFactoryOptions } from "./contex
|
|
|
12
12
|
import { extractRequestMeta } from "./request-meta.js";
|
|
13
13
|
import type { ResolvedPlugin, RouteContext, PluginRoute } from "./types.js";
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Body-reading methods on `Request`. EmDash parses the request body once before
|
|
17
|
+
* the handler runs and exposes the result as `ctx.input`, leaving the underlying
|
|
18
|
+
* stream consumed. Calling any of these on `ctx.request` would re-read a spent
|
|
19
|
+
* stream and throw an opaque platform error ("Body is unusable: Body has already
|
|
20
|
+
* been read") with no hint about `ctx.input` — so the guard replaces them with an
|
|
21
|
+
* actionable message instead (#1293).
|
|
22
|
+
*/
|
|
23
|
+
const CONSUMED_BODY_METHODS = new Set(["json", "text", "arrayBuffer", "blob", "formData", "bytes"]);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Wrap the request handed to a plugin route handler so an accidental
|
|
27
|
+
* `ctx.request.json()` (or `.text()`, `.formData()`, …) fails with a message
|
|
28
|
+
* pointing at `ctx.input` rather than the runtime's cryptic "body already read"
|
|
29
|
+
* error. Every non-body member passes through unchanged; function members are
|
|
30
|
+
* bound to the underlying request so methods like `clone()` don't throw an
|
|
31
|
+
* "Illegal invocation" when called on the proxy.
|
|
32
|
+
*/
|
|
33
|
+
function guardConsumedRequestBody(request: Request): Request {
|
|
34
|
+
return new Proxy(request, {
|
|
35
|
+
get(target, prop) {
|
|
36
|
+
if (typeof prop === "string" && CONSUMED_BODY_METHODS.has(prop)) {
|
|
37
|
+
return () => {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`[emdash] ctx.request.${prop}() is not available inside a plugin route handler: ` +
|
|
40
|
+
`EmDash has already parsed the request body and exposes it as ctx.input. ` +
|
|
41
|
+
`Read ctx.input instead of ctx.request.${prop}().`,
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const value = Reflect.get(target, prop, target);
|
|
46
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
15
51
|
/**
|
|
16
52
|
* Route metadata (public flag) without the handler.
|
|
17
53
|
* Used by the catch-all route to decide auth before dispatch.
|
|
@@ -100,7 +136,10 @@ export class PluginRouteHandler {
|
|
|
100
136
|
const routeContext: RouteContext = {
|
|
101
137
|
...baseContext,
|
|
102
138
|
input: validatedInput,
|
|
103
|
-
|
|
139
|
+
// The body is already parsed into `input`; guard `ctx.request`'s
|
|
140
|
+
// body-reading methods so a re-read fails with an actionable message
|
|
141
|
+
// (#1293). Metadata extraction uses the original request (headers only).
|
|
142
|
+
request: guardConsumedRequestBody(options.request),
|
|
104
143
|
requestMeta: extractRequestMeta(options.request, this.trustedProxyHeaders),
|
|
105
144
|
};
|
|
106
145
|
|
package/src/query.ts
CHANGED
|
@@ -26,7 +26,13 @@
|
|
|
26
26
|
|
|
27
27
|
import { encodeCursor } from "./database/repositories/types.js";
|
|
28
28
|
import { getFallbackChain, getI18nConfig, isI18nEnabled } from "./i18n/config.js";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
CURSOR_RAW_VALUES,
|
|
31
|
+
FOLDED_BYLINES,
|
|
32
|
+
FOLDED_TERMS,
|
|
33
|
+
type WhereRange,
|
|
34
|
+
type WhereValue,
|
|
35
|
+
} from "./loader.js";
|
|
30
36
|
import {
|
|
31
37
|
cachedQuery,
|
|
32
38
|
contentNamespaces,
|
|
@@ -34,6 +40,7 @@ import {
|
|
|
34
40
|
} from "./object-cache/index.js";
|
|
35
41
|
import { requestCached } from "./request-cache.js";
|
|
36
42
|
import { getRequestContext } from "./request-context.js";
|
|
43
|
+
import type { TaxonomyTerm } from "./taxonomies/types.js";
|
|
37
44
|
import { isMissingTableError } from "./utils/db-errors.js";
|
|
38
45
|
import {
|
|
39
46
|
createEditable,
|
|
@@ -89,22 +96,17 @@ export type OrderBySpec = Record<string, SortDirection>;
|
|
|
89
96
|
|
|
90
97
|
export type { WhereRange, WhereValue };
|
|
91
98
|
|
|
92
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Fields shared by every collection query, independent of pagination mode.
|
|
101
|
+
*
|
|
102
|
+
* Cursor and offset pagination are mutually exclusive, so they live on the
|
|
103
|
+
* `CursorCollectionFilter` / `OffsetCollectionFilter` variants rather than
|
|
104
|
+
* here. Use the {@link CollectionFilter} union for any value that may be
|
|
105
|
+
* either.
|
|
106
|
+
*/
|
|
107
|
+
export interface CollectionFilterBase {
|
|
93
108
|
status?: "draft" | "published" | "archived";
|
|
94
109
|
limit?: number;
|
|
95
|
-
/**
|
|
96
|
-
* Opaque cursor for keyset pagination.
|
|
97
|
-
* Pass the `nextCursor` value from a previous result to fetch the next page.
|
|
98
|
-
* @example
|
|
99
|
-
* ```ts
|
|
100
|
-
* const cursor = Astro.url.searchParams.get("cursor") ?? undefined;
|
|
101
|
-
* const { entries, nextCursor } = await getEmDashCollection("posts", {
|
|
102
|
-
* limit: 10,
|
|
103
|
-
* cursor,
|
|
104
|
-
* });
|
|
105
|
-
* ```
|
|
106
|
-
*/
|
|
107
|
-
cursor?: string;
|
|
108
110
|
/**
|
|
109
111
|
* Filter by field values, taxonomy terms, byline credits, or ranges.
|
|
110
112
|
*
|
|
@@ -140,6 +142,56 @@ export interface CollectionFilter {
|
|
|
140
142
|
locale?: string;
|
|
141
143
|
}
|
|
142
144
|
|
|
145
|
+
/** Keyset-paginated query filter. Cannot also carry an `offset`. */
|
|
146
|
+
export interface CursorCollectionFilter extends CollectionFilterBase {
|
|
147
|
+
/**
|
|
148
|
+
* Opaque cursor for keyset pagination.
|
|
149
|
+
* Pass the `nextCursor` value from a previous result to fetch the next page.
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* const cursor = Astro.url.searchParams.get("cursor") ?? undefined;
|
|
153
|
+
* const { entries, nextCursor } = await getEmDashCollection("posts", {
|
|
154
|
+
* limit: 10,
|
|
155
|
+
* cursor,
|
|
156
|
+
* });
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
cursor?: string;
|
|
160
|
+
offset?: never;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Offset-paginated query filter. Cannot also carry a `cursor`. */
|
|
164
|
+
export interface OffsetCollectionFilter extends CollectionFilterBase {
|
|
165
|
+
/**
|
|
166
|
+
* Skip this many entries before returning results (offset pagination).
|
|
167
|
+
*
|
|
168
|
+
* Use with `limit` to render numbered archive routes like `/page/2`
|
|
169
|
+
* without walking cursors or over-fetching from the start:
|
|
170
|
+
*
|
|
171
|
+
* ```ts
|
|
172
|
+
* const perPage = 20;
|
|
173
|
+
* const { entries, hasMore } = await getEmDashCollection("posts", {
|
|
174
|
+
* limit: perPage,
|
|
175
|
+
* offset: (page - 1) * perPage,
|
|
176
|
+
* orderBy: { published_at: "desc" },
|
|
177
|
+
* });
|
|
178
|
+
* ```
|
|
179
|
+
*
|
|
180
|
+
* Only a positive integer applies.
|
|
181
|
+
*/
|
|
182
|
+
offset?: number;
|
|
183
|
+
cursor?: never;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Filter for `getEmDashCollection`.
|
|
188
|
+
*
|
|
189
|
+
* A union of the cursor and offset pagination variants: supplying both
|
|
190
|
+
* `cursor` and `offset` is a compile-time error, since they are mutually
|
|
191
|
+
* exclusive ways to express "the next page" (cursor wins at runtime).
|
|
192
|
+
*/
|
|
193
|
+
export type CollectionFilter = CursorCollectionFilter | OffsetCollectionFilter;
|
|
194
|
+
|
|
143
195
|
export interface ContentEntry<T = Record<string, unknown>> {
|
|
144
196
|
id: string;
|
|
145
197
|
data: T;
|
|
@@ -169,6 +221,12 @@ export interface CollectionResult<T> {
|
|
|
169
221
|
* Pass this as `cursor` in the next query to get the next page.
|
|
170
222
|
*/
|
|
171
223
|
nextCursor?: string;
|
|
224
|
+
/**
|
|
225
|
+
* Whether more entries exist beyond this page. Set whenever `limit` is
|
|
226
|
+
* provided (cursor or offset pagination), so numbered archive routes can
|
|
227
|
+
* render a "next page" link without computing a total count.
|
|
228
|
+
*/
|
|
229
|
+
hasMore?: boolean;
|
|
172
230
|
}
|
|
173
231
|
|
|
174
232
|
/**
|
|
@@ -343,6 +401,7 @@ export async function getEmDashCollection<T extends string, D = InferCollectionD
|
|
|
343
401
|
interface CachedCollectionValue {
|
|
344
402
|
entries: unknown[];
|
|
345
403
|
nextCursor?: string;
|
|
404
|
+
hasMore?: boolean;
|
|
346
405
|
cacheHint: CacheHint;
|
|
347
406
|
}
|
|
348
407
|
|
|
@@ -370,6 +429,7 @@ async function loadCollectionCached<T extends string, D = InferCollectionData<T>
|
|
|
370
429
|
value: {
|
|
371
430
|
entries: result.entries.map(entrySnapshot),
|
|
372
431
|
nextCursor: result.nextCursor,
|
|
432
|
+
hasMore: result.hasMore,
|
|
373
433
|
cacheHint: result.cacheHint,
|
|
374
434
|
},
|
|
375
435
|
};
|
|
@@ -383,6 +443,7 @@ async function loadCollectionCached<T extends string, D = InferCollectionData<T>
|
|
|
383
443
|
return {
|
|
384
444
|
entries: snapshot.value.entries.map((entry) => reviveEntry<D>(entry)),
|
|
385
445
|
nextCursor: snapshot.value.nextCursor,
|
|
446
|
+
hasMore: snapshot.value.hasMore,
|
|
386
447
|
cacheHint: snapshot.value.cacheHint,
|
|
387
448
|
};
|
|
388
449
|
}
|
|
@@ -410,7 +471,10 @@ export function bucketFilter(filter: CollectionFilter | undefined): BucketedFilt
|
|
|
410
471
|
limit === undefined ||
|
|
411
472
|
limit >= BUCKET_LIMIT_THRESHOLD ||
|
|
412
473
|
limit <= 0 ||
|
|
413
|
-
filter?.cursor !== undefined
|
|
474
|
+
filter?.cursor !== undefined ||
|
|
475
|
+
// Offset paginates a deliberate page window; its limit is part of the
|
|
476
|
+
// pagination contract, so don't round it up the way "recent N" widgets get.
|
|
477
|
+
filter?.offset !== undefined
|
|
414
478
|
) {
|
|
415
479
|
return { fetchFilter: filter, requestedLimit: undefined };
|
|
416
480
|
}
|
|
@@ -442,7 +506,8 @@ export function sliceCollectionResult<D>(
|
|
|
442
506
|
// buildCursorCondition in loader.ts — it filters strictly past this row.
|
|
443
507
|
const lastEntry = sliced.at(-1);
|
|
444
508
|
const nextCursor = lastEntry ? encodeEntryCursor(lastEntry, orderBy) : undefined;
|
|
445
|
-
|
|
509
|
+
// Truncating to the requested limit means at least one more entry existed.
|
|
510
|
+
return { ...cached, entries: sliced, nextCursor, hasMore: true };
|
|
446
511
|
}
|
|
447
512
|
|
|
448
513
|
/** Map of database column names to camelCase keys present on entry.data. */
|
|
@@ -526,6 +591,7 @@ function collectionCacheKey(type: string, filter?: CollectionFilter): string {
|
|
|
526
591
|
filter.status ?? "",
|
|
527
592
|
filter.limit ?? "",
|
|
528
593
|
filter.cursor ?? "",
|
|
594
|
+
filter.offset ?? "",
|
|
529
595
|
filter.where ? stableStringify(filter.where) : "",
|
|
530
596
|
filter.orderBy ? JSON.stringify(filter.orderBy) : "",
|
|
531
597
|
filter.locale ?? "",
|
|
@@ -626,11 +692,19 @@ async function getEmDashCollectionUncached<T extends string, D = InferCollection
|
|
|
626
692
|
filter?.locale ?? ctx?.locale ?? (isI18nEnabled() ? i18nConfig!.defaultLocale : undefined);
|
|
627
693
|
|
|
628
694
|
const requestedLimit = filter?.limit;
|
|
695
|
+
// cursor and offset are mutually exclusive on the loader filter union, so
|
|
696
|
+
// spread only the one in play (cursor wins) rather than emitting both keys.
|
|
697
|
+
const pageParam =
|
|
698
|
+
filter?.cursor !== undefined
|
|
699
|
+
? { cursor: filter.cursor }
|
|
700
|
+
: filter?.offset !== undefined
|
|
701
|
+
? { offset: filter.offset }
|
|
702
|
+
: {};
|
|
629
703
|
const result = await getLiveCollection(COLLECTION_NAME, {
|
|
630
704
|
type,
|
|
631
705
|
status: filter?.status,
|
|
632
706
|
limit: requestedLimit && requestedLimit > 0 ? requestedLimit + 1 : filter?.limit,
|
|
633
|
-
|
|
707
|
+
...pageParam,
|
|
634
708
|
where: filter?.where,
|
|
635
709
|
orderBy: filter?.orderBy,
|
|
636
710
|
locale: resolvedLocale,
|
|
@@ -645,6 +719,9 @@ async function getEmDashCollectionUncached<T extends string, D = InferCollection
|
|
|
645
719
|
const hasMore = requestedLimit != null && requestedLimit > 0 && entries.length > requestedLimit;
|
|
646
720
|
const pageEntries = hasMore ? entries.slice(0, requestedLimit) : entries;
|
|
647
721
|
const nextCursor = hasMore ? encodeEntryCursor(pageEntries.at(-1), filter?.orderBy) : undefined;
|
|
722
|
+
// `hasMore` is only meaningful when a limit bounds the page; otherwise the
|
|
723
|
+
// query returned everything and there is no "next page" to report.
|
|
724
|
+
const hasMoreResult = requestedLimit != null && requestedLimit > 0 ? hasMore : undefined;
|
|
648
725
|
|
|
649
726
|
const isEditMode = ctx?.editMode ?? false;
|
|
650
727
|
const entriesWithEdit = pageEntries.map((entry: ContentEntry<D>) => {
|
|
@@ -668,7 +745,12 @@ async function getEmDashCollectionUncached<T extends string, D = InferCollection
|
|
|
668
745
|
hydrateEntryTerms(type, entriesWithEdit, resolvedLocale),
|
|
669
746
|
]);
|
|
670
747
|
|
|
671
|
-
return {
|
|
748
|
+
return {
|
|
749
|
+
entries: entriesWithEdit,
|
|
750
|
+
nextCursor,
|
|
751
|
+
hasMore: hasMoreResult,
|
|
752
|
+
cacheHint: cacheHint ?? {},
|
|
753
|
+
};
|
|
672
754
|
}
|
|
673
755
|
|
|
674
756
|
/**
|
|
@@ -930,6 +1012,67 @@ interface CachedEntryValue {
|
|
|
930
1012
|
async function hydrateEntryBylines<D>(type: string, entries: ContentEntry<D>[]): Promise<void> {
|
|
931
1013
|
if (entries.length === 0) return;
|
|
932
1014
|
|
|
1015
|
+
// Fast path: bylines were folded into the content query. Parse the JSON
|
|
1016
|
+
// (no extra round trip) for the common case — explicit credits, no byline
|
|
1017
|
+
// custom fields, no author fallback. The query path below handles the rest:
|
|
1018
|
+
// - author fallback (entry has authorId but no explicit credit), and
|
|
1019
|
+
// - custom byline fields (can't be expressed in the folded subquery).
|
|
1020
|
+
if (entries.every((e) => FOLDED_BYLINES in entryData(e))) {
|
|
1021
|
+
const parsed = entries.map((entry) => {
|
|
1022
|
+
const data = entryData(entry);
|
|
1023
|
+
const folded = Reflect.get(data, FOLDED_BYLINES);
|
|
1024
|
+
const rows = Array.isArray(folded) ? folded : [];
|
|
1025
|
+
const credits = rows
|
|
1026
|
+
.map((raw) => {
|
|
1027
|
+
const b = raw?.byline ?? {};
|
|
1028
|
+
return {
|
|
1029
|
+
roleLabel: raw?.roleLabel ?? null,
|
|
1030
|
+
sortOrder: Number(raw?.sortOrder ?? 0),
|
|
1031
|
+
source: "explicit" as const,
|
|
1032
|
+
byline: { ...b, isGuest: Boolean(b.isGuest), customFields: {} },
|
|
1033
|
+
};
|
|
1034
|
+
})
|
|
1035
|
+
.toSorted((a, b) => a.sortOrder - b.sortOrder);
|
|
1036
|
+
return { data, credits };
|
|
1037
|
+
});
|
|
1038
|
+
|
|
1039
|
+
// Fall back to the full query path when the fold can't be trusted to be
|
|
1040
|
+
// complete: an entry with a byline reference (explicit primary, or an
|
|
1041
|
+
// author for the author-fallback) but no folded credits — e.g. a credit
|
|
1042
|
+
// in a different locale than the row, which the locale-correlated subquery
|
|
1043
|
+
// skips, or the author-fallback path which the fold doesn't express.
|
|
1044
|
+
let needsQueryPath = parsed.some(
|
|
1045
|
+
(p) =>
|
|
1046
|
+
p.credits.length === 0 &&
|
|
1047
|
+
(dataStr(p.data, "authorId") !== "" || dataStr(p.data, "primaryBylineId") !== ""),
|
|
1048
|
+
);
|
|
1049
|
+
let hasCustomFields = false;
|
|
1050
|
+
if (!needsQueryPath) {
|
|
1051
|
+
try {
|
|
1052
|
+
const { getDb } = await import("./loader.js");
|
|
1053
|
+
const db = await getDb();
|
|
1054
|
+
const { getBylineFieldDefs } = await import("./bylines/field-defs-cache.js");
|
|
1055
|
+
hasCustomFields = (await getBylineFieldDefs(db)).length > 0;
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
// A missing table is expected pre-migration and means there are no
|
|
1058
|
+
// custom fields — the fold's values are complete. Any other error
|
|
1059
|
+
// (lock-init failure, dialect error) means the probe can't be
|
|
1060
|
+
// trusted, so fall back to the query path rather than risk serving
|
|
1061
|
+
// folded bylines with empty customFields.
|
|
1062
|
+
if (!isMissingTableError(error)) needsQueryPath = true;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if (!needsQueryPath && !hasCustomFields) {
|
|
1067
|
+
for (const p of parsed) {
|
|
1068
|
+
p.data.bylines = p.credits;
|
|
1069
|
+
p.data.byline = p.credits[0]?.byline ?? null;
|
|
1070
|
+
}
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
// Fall through to the full query path for fallback / custom-field cases.
|
|
1074
|
+
}
|
|
1075
|
+
|
|
933
1076
|
try {
|
|
934
1077
|
const { getBylinesForEntries } = await import("./bylines/index.js");
|
|
935
1078
|
|
|
@@ -1005,6 +1148,45 @@ async function hydrateEntryTerms<D>(
|
|
|
1005
1148
|
): Promise<void> {
|
|
1006
1149
|
if (entries.length === 0) return;
|
|
1007
1150
|
|
|
1151
|
+
// Fast path: terms were folded into the content query. Group the JSON and
|
|
1152
|
+
// skip the separate content_taxonomies query.
|
|
1153
|
+
if (entries.every((e) => FOLDED_TERMS in entryData(e))) {
|
|
1154
|
+
const perEntry: Array<{ entryId: string; byTaxonomy: Record<string, TaxonomyTerm[]> }> = [];
|
|
1155
|
+
for (const entry of entries) {
|
|
1156
|
+
const data = entryData(entry);
|
|
1157
|
+
const folded = Reflect.get(data, FOLDED_TERMS);
|
|
1158
|
+
const rows = Array.isArray(folded) ? folded : [];
|
|
1159
|
+
const grouped: Record<string, TaxonomyTerm[]> = {};
|
|
1160
|
+
for (const r of rows) {
|
|
1161
|
+
const name = String(r?.name);
|
|
1162
|
+
(grouped[name] ??= []).push({
|
|
1163
|
+
id: r?.id,
|
|
1164
|
+
name,
|
|
1165
|
+
slug: r?.slug,
|
|
1166
|
+
label: r?.label,
|
|
1167
|
+
parentId: r?.parent_id ?? undefined,
|
|
1168
|
+
children: [],
|
|
1169
|
+
locale: r?.locale,
|
|
1170
|
+
translationGroup: r?.translation_group,
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
// Match getAllTermsForEntries' ORDER BY label (dropped from the
|
|
1174
|
+
// aggregate since SQLite and Postgres order it differently).
|
|
1175
|
+
for (const [name, arr] of Object.entries(grouped)) {
|
|
1176
|
+
grouped[name] = arr.toSorted((a, b) => String(a.label).localeCompare(String(b.label)));
|
|
1177
|
+
}
|
|
1178
|
+
data.terms = grouped;
|
|
1179
|
+
const entryId = dataStr(data, "id");
|
|
1180
|
+
if (entryId) perEntry.push({ entryId, byTaxonomy: grouped });
|
|
1181
|
+
}
|
|
1182
|
+
// Prime the per-entry request cache (wildcard + present taxonomies) so
|
|
1183
|
+
// subsequent getEntryTerms(...) calls in this render hit the cache instead
|
|
1184
|
+
// of issuing an N+1 query. No DB lookup — purely from the folded data.
|
|
1185
|
+
const { primeFoldedEntryTerms } = await import("./taxonomies/index.js");
|
|
1186
|
+
primeFoldedEntryTerms(type, perEntry, { locale });
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1008
1190
|
try {
|
|
1009
1191
|
const { getAllTermsForEntries } = await import("./taxonomies/index.js");
|
|
1010
1192
|
|