emdash 0.15.0 → 0.16.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 +10 -10
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/{api-CLwG_3dh.mjs → api-BNKqxyFX.mjs} +54 -14
- package/dist/{api-CLwG_3dh.mjs.map → api-BNKqxyFX.mjs.map} +1 -1
- package/dist/{apply-wJhM_bwU.mjs → apply-BOPaD-s9.mjs} +16 -16
- package/dist/{apply-wJhM_bwU.mjs.map → apply-BOPaD-s9.mjs.map} +1 -1
- package/dist/astro/index.d.mts +3 -3
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +33 -1
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +3 -3
- package/dist/astro/middleware/auth.mjs +2 -2
- package/dist/astro/middleware/redirect.mjs +4 -4
- package/dist/astro/middleware/request-context.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +66 -46
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +3 -3
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +3 -3
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +2 -2
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +3 -3
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +9 -9
- package/dist/astro/routes/api/admin/bylines/index.mjs +9 -9
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +7 -7
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +6 -6
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +6 -6
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +4 -4
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +3 -3
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/index.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +3 -3
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +27 -27
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +41 -28
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts +8 -0
- package/dist/astro/routes/api/admin/plugins/registry/artifact.d.mts.map +1 -0
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs +301 -0
- package/dist/astro/routes/api/admin/plugins/registry/artifact.mjs.map +1 -0
- package/dist/astro/routes/api/admin/plugins/registry/install.d.mts.map +1 -1
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +46 -28
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs.map +1 -1
- package/dist/astro/routes/api/admin/plugins/updates.mjs +27 -27
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +27 -27
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +3 -3
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +27 -27
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- package/dist/astro/routes/api/admin/users/index.mjs +3 -3
- package/dist/astro/routes/api/auth/dev-bypass.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
- package/dist/astro/routes/api/auth/invite/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/index.mjs +3 -3
- package/dist/astro/routes/api/auth/invite/register-options.mjs +3 -3
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +4 -4
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/index.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/options.mjs +4 -4
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/passkey/verify.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/complete.mjs +3 -3
- package/dist/astro/routes/api/auth/signup/request.mjs +4 -4
- package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +6 -6
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +8 -8
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +3 -3
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/index.mjs +4 -4
- package/dist/astro/routes/api/content/_collection_/trash.mjs +4 -4
- package/dist/astro/routes/api/dashboard.mjs +7 -7
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/import/probe.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress/media.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +6 -6
- package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts +11 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs +17 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-url-helpers.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts.map +1 -1
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +7 -7
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs.map +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +4 -4
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +5 -5
- package/dist/astro/routes/api/manifest.mjs +3 -3
- package/dist/astro/routes/api/mcp.mjs +26 -26
- package/dist/astro/routes/api/media/_id_/confirm.mjs +4 -4
- package/dist/astro/routes/api/media/_id_.mjs +4 -4
- package/dist/astro/routes/api/media/file/_...key_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +3 -3
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +3 -3
- package/dist/astro/routes/api/media/providers/index.mjs +3 -3
- package/dist/astro/routes/api/media/upload-url.mjs +4 -4
- package/dist/astro/routes/api/media.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/items.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_/translations.mjs +5 -5
- package/dist/astro/routes/api/menus/_name_.mjs +5 -5
- package/dist/astro/routes/api/menus/index.mjs +5 -5
- package/dist/astro/routes/api/oauth/device/authorize.mjs +3 -3
- package/dist/astro/routes/api/oauth/device/code.mjs +4 -4
- package/dist/astro/routes/api/oauth/device/token.mjs +4 -4
- package/dist/astro/routes/api/oauth/register.mjs +2 -2
- package/dist/astro/routes/api/oauth/token/refresh.mjs +3 -3
- package/dist/astro/routes/api/oauth/token/revoke.mjs +3 -3
- package/dist/astro/routes/api/oauth/token.mjs +2 -2
- package/dist/astro/routes/api/openapi.json.mjs +2 -2
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +3 -3
- package/dist/astro/routes/api/redirects/404s/index.mjs +6 -6
- package/dist/astro/routes/api/redirects/404s/summary.mjs +6 -6
- package/dist/astro/routes/api/redirects/_id_.mjs +7 -7
- package/dist/astro/routes/api/redirects/index.mjs +7 -7
- 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 +27 -27
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +27 -27
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +27 -27
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +27 -27
- package/dist/astro/routes/api/schema/collections/index.mjs +27 -27
- package/dist/astro/routes/api/schema/index.mjs +6 -6
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +27 -27
- package/dist/astro/routes/api/schema/orphans/index.mjs +27 -27
- package/dist/astro/routes/api/search/enable.mjs +7 -7
- package/dist/astro/routes/api/search/index.mjs +6 -6
- package/dist/astro/routes/api/search/rebuild.mjs +7 -7
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +6 -6
- package/dist/astro/routes/api/sections/_slug_.mjs +6 -6
- package/dist/astro/routes/api/sections/index.mjs +6 -6
- package/dist/astro/routes/api/settings/email.mjs +4 -4
- package/dist/astro/routes/api/settings.mjs +8 -8
- package/dist/astro/routes/api/setup/admin-verify.mjs +3 -3
- package/dist/astro/routes/api/setup/admin.mjs +3 -3
- package/dist/astro/routes/api/setup/dev-bypass.mjs +15 -15
- package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
- package/dist/astro/routes/api/setup/index.mjs +16 -16
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +4 -4
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +9 -9
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +9 -9
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +9 -9
- package/dist/astro/routes/api/taxonomies/index.mjs +9 -9
- package/dist/astro/routes/api/themes/preview.mjs +3 -3
- package/dist/astro/routes/api/typegen.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +4 -4
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +6 -6
- package/dist/astro/routes/api/widget-areas/_name_.mjs +5 -5
- package/dist/astro/routes/api/widget-areas/index.mjs +6 -6
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +4 -4
- package/dist/astro/routes/sitemap-_collection_.xml.d.mts.map +1 -1
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +58 -13
- package/dist/astro/routes/sitemap-_collection_.xml.mjs.map +1 -1
- package/dist/astro/routes/sitemap.xml.mjs +5 -5
- package/dist/astro/types.d.mts +10 -3
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{authorize-Bkwe8kuL.mjs → authorize-Bn4S4DUT.mjs} +2 -2
- package/dist/{authorize-Bkwe8kuL.mjs.map → authorize-Bn4S4DUT.mjs.map} +1 -1
- package/dist/{byline-CTaWkMh5.mjs → byline-BDylH_m4.mjs} +3 -3
- package/dist/{byline-CTaWkMh5.mjs.map → byline-BDylH_m4.mjs.map} +1 -1
- package/dist/{bylines-H0Xh5TMy.mjs → bylines-B7TFEvFf.mjs} +2 -2
- package/dist/{bylines-H0Xh5TMy.mjs.map → bylines-B7TFEvFf.mjs.map} +1 -1
- package/dist/{bylines-DtDRNF1n.d.mts → bylines-DWLnr6-k.d.mts} +17 -17
- package/dist/{bylines-DtDRNF1n.d.mts.map → bylines-DWLnr6-k.d.mts.map} +1 -1
- package/dist/{bylines-BYHWU3T7.mjs → bylines-n6nykUyI.mjs} +6 -6
- package/dist/{bylines-BYHWU3T7.mjs.map → bylines-n6nykUyI.mjs.map} +1 -1
- package/dist/{cache-CNk1jIxp.mjs → cache-BcI1yUjR.mjs} +2 -2
- package/dist/{cache-CNk1jIxp.mjs.map → cache-BcI1yUjR.mjs.map} +1 -1
- package/dist/{chunks-BkfVdD-3.mjs → chunks-cYG4SnIP.mjs} +2 -2
- package/dist/{chunks-BkfVdD-3.mjs.map → chunks-cYG4SnIP.mjs.map} +1 -1
- package/dist/cli/index.mjs +61 -15
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/{comment-_yzlBYPx.mjs → comment-C76G-9tz.mjs} +2 -2
- package/dist/{comment-_yzlBYPx.mjs.map → comment-C76G-9tz.mjs.map} +1 -1
- package/dist/{comments-DxID-rsd.mjs → comments-CCxFFGY1.mjs} +3 -3
- package/dist/{comments-DxID-rsd.mjs.map → comments-CCxFFGY1.mjs.map} +1 -1
- package/dist/{content-C0ooIs-f.mjs → content-8voQNTXX.mjs} +3 -3
- package/dist/{content-C0ooIs-f.mjs.map → content-8voQNTXX.mjs.map} +1 -1
- package/dist/{context-sAnCaUIR.mjs → context-B7qiYrz2.mjs} +7 -7
- package/dist/{context-sAnCaUIR.mjs.map → context-B7qiYrz2.mjs.map} +1 -1
- package/dist/{dashboard-Cqw3ay2X.mjs → dashboard-BeaFSPpx.mjs} +4 -4
- package/dist/{dashboard-Cqw3ay2X.mjs.map → dashboard-BeaFSPpx.mjs.map} +1 -1
- package/dist/db/index.d.mts +1 -1
- package/dist/db/index.mjs +1 -1
- package/dist/db/sqlite.mjs +1 -1
- package/dist/{db-errors-CGN9kJfo.mjs → db-errors-BiYqoX-n.mjs} +14 -2
- package/dist/db-errors-BiYqoX-n.mjs.map +1 -0
- package/dist/{error-CPh_8eLq.mjs → error-ChfADBuu.mjs} +5 -3
- package/dist/error-ChfADBuu.mjs.map +1 -0
- package/dist/errors-9P_FDrJ_.mjs +17 -0
- package/dist/errors-9P_FDrJ_.mjs.map +1 -0
- package/dist/{fts-manager-Mnrtn-r2.mjs → fts-manager-C_b-4x8u.mjs} +2 -2
- package/dist/{fts-manager-Mnrtn-r2.mjs.map → fts-manager-C_b-4x8u.mjs.map} +1 -1
- package/dist/{index-Bv1Wf1zB.d.mts → index-D_p_jIP1.d.mts} +153 -109
- package/dist/index-D_p_jIP1.d.mts.map +1 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +38 -38
- package/dist/{load-DmXNVhst.mjs → load-CLFRjk9r.mjs} +2 -2
- package/dist/{load-DmXNVhst.mjs.map → load-CLFRjk9r.mjs.map} +1 -1
- package/dist/{loader-Chm5h7Gr.mjs → loader-D-vIJjfY.mjs} +86 -46
- package/dist/loader-D-vIJjfY.mjs.map +1 -0
- package/dist/media/local-runtime.d.mts +3 -3
- package/dist/media/local-runtime.mjs +4 -4
- package/dist/{media-oqRcNiQf.mjs → media-CKQd8AYU.mjs} +2 -2
- package/dist/{media-oqRcNiQf.mjs.map → media-CKQd8AYU.mjs.map} +1 -1
- package/dist/{menus-C75SSmRy.mjs → menus-C-nWT5Tu.mjs} +17 -11
- package/dist/menus-C-nWT5Tu.mjs.map +1 -0
- package/dist/{menus-Bjf5R1Qq.mjs → menus-arUNspyU.mjs} +2 -2
- package/dist/{menus-Bjf5R1Qq.mjs.map → menus-arUNspyU.mjs.map} +1 -1
- package/dist/{parse-3-caTKgt.mjs → parse-DHbXfvxO.mjs} +2 -2
- package/dist/{parse-3-caTKgt.mjs.map → parse-DHbXfvxO.mjs.map} +1 -1
- package/dist/plugin-utils.d.mts +25 -10
- package/dist/plugin-utils.d.mts.map +1 -1
- package/dist/plugin-utils.mjs +11 -10
- package/dist/plugin-utils.mjs.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +3 -3
- package/dist/{query-BJn8TOPk.mjs → query-7m6-l0f_.mjs} +21 -14
- package/dist/query-7m6-l0f_.mjs.map +1 -0
- package/dist/{rate-limit-D_-gAeJ0.mjs → rate-limit-D8RAXN8b.mjs} +2 -2
- package/dist/{rate-limit-D_-gAeJ0.mjs.map → rate-limit-D8RAXN8b.mjs.map} +1 -1
- package/dist/{redirect-CNv4mHX2.mjs → redirect-CjfDGrTd.mjs} +2 -2
- package/dist/{redirect-CNv4mHX2.mjs.map → redirect-CjfDGrTd.mjs.map} +1 -1
- package/dist/{redirects-B-CUZ1Xh.mjs → redirects-CowoEHdE.mjs} +3 -3
- package/dist/{redirects-B-CUZ1Xh.mjs.map → redirects-CowoEHdE.mjs.map} +1 -1
- package/dist/{registry-DqrAQDXH.mjs → registry-Cyp-dx6J.mjs} +4 -4
- package/dist/{registry-DqrAQDXH.mjs.map → registry-Cyp-dx6J.mjs.map} +1 -1
- package/dist/resolve-D6sM-SgF.mjs +143 -0
- package/dist/resolve-D6sM-SgF.mjs.map +1 -0
- package/dist/{runner-CNHRo1mT.d.mts → runner-DSQBurMS.d.mts} +7 -4
- package/dist/runner-DSQBurMS.d.mts.map +1 -0
- package/dist/{runner-CGlojznK.mjs → runner-Drnvs96u.mjs} +20 -24
- package/dist/{runner-CGlojznK.mjs.map → runner-Drnvs96u.mjs.map} +1 -1
- package/dist/runtime.d.mts +3 -3
- package/dist/runtime.mjs +2 -2
- package/dist/{schema-Djdlfi5G.mjs → schema-CI9mYPX3.mjs} +4 -4
- package/dist/{schema-Djdlfi5G.mjs.map → schema-CI9mYPX3.mjs.map} +1 -1
- package/dist/{search-By-NN3da.mjs → search-DKz_mGBP.mjs} +4 -4
- package/dist/{search-By-NN3da.mjs.map → search-DKz_mGBP.mjs.map} +1 -1
- package/dist/{sections-DcBIlOq1.mjs → sections-DBbCDIAT.mjs} +3 -3
- package/dist/{sections-DcBIlOq1.mjs.map → sections-DBbCDIAT.mjs.map} +1 -1
- package/dist/seed/index.mjs +13 -13
- package/dist/{seo-bjDoq9Eg.mjs → seo-BGCyDlkb.mjs} +2 -2
- package/dist/{seo-bjDoq9Eg.mjs.map → seo-BGCyDlkb.mjs.map} +1 -1
- package/dist/{seo-BoR4wCUh.mjs → seo-Dq707mNQ.mjs} +5 -3
- package/dist/seo-Dq707mNQ.mjs.map +1 -0
- package/dist/{service-BuuTdGAT.mjs → service-B0H7U1Y9.mjs} +2 -2
- package/dist/{service-BuuTdGAT.mjs.map → service-B0H7U1Y9.mjs.map} +1 -1
- package/dist/{settings-hcubRfkr.mjs → settings-BSXRtTzk.mjs} +3 -3
- package/dist/{settings-hcubRfkr.mjs.map → settings-BSXRtTzk.mjs.map} +1 -1
- package/dist/{settings-CJnKiWuR.mjs → settings-DfwNyQkf.mjs} +3 -3
- package/dist/{settings-CJnKiWuR.mjs.map → settings-DfwNyQkf.mjs.map} +1 -1
- package/dist/{taxonomies-CLs9HPE2.mjs → taxonomies-4vx0nmMr.mjs} +4 -4
- package/dist/{taxonomies-CLs9HPE2.mjs.map → taxonomies-4vx0nmMr.mjs.map} +1 -1
- package/dist/{taxonomies-WamPVA2x.mjs → taxonomies-CcvrMLbR.mjs} +7 -7
- package/dist/{taxonomies-WamPVA2x.mjs.map → taxonomies-CcvrMLbR.mjs.map} +1 -1
- package/dist/{taxonomy-D4Uc2LsZ.mjs → taxonomy-zqGQUqgu.mjs} +3 -3
- package/dist/{taxonomy-D4Uc2LsZ.mjs.map → taxonomy-zqGQUqgu.mjs.map} +1 -1
- package/dist/{transport-DOxLfUir.d.mts → transport-C2MGqtL6.d.mts} +1 -1
- package/dist/{transport-DOxLfUir.d.mts.map → transport-C2MGqtL6.d.mts.map} +1 -1
- package/dist/{types-ByV5sgsv.mjs → types-B0bmgwMG.mjs} +2 -2
- package/dist/{types-ByV5sgsv.mjs.map → types-B0bmgwMG.mjs.map} +1 -1
- package/dist/{user-D3BD5zdT.mjs → user-hUSOaIJy.mjs} +2 -2
- package/dist/{user-D3BD5zdT.mjs.map → user-hUSOaIJy.mjs.map} +1 -1
- package/dist/{validate-mz87i8_1.mjs → validate-IGltez8n.mjs} +2 -2
- package/dist/{validate-mz87i8_1.mjs.map → validate-IGltez8n.mjs.map} +1 -1
- package/dist/{validation-DKHhXjPr.mjs → validation-Bmymau7y.mjs} +6 -6
- package/dist/{validation-DKHhXjPr.mjs.map → validation-Bmymau7y.mjs.map} +1 -1
- package/dist/version-ITD3PlQd.mjs +7 -0
- package/dist/{version-Ct7C6RSo.mjs.map → version-ITD3PlQd.mjs.map} +1 -1
- package/dist/{widgets-lShIQXU5.mjs → widgets-yHQa4c6c.mjs} +2 -2
- package/dist/{widgets-lShIQXU5.mjs.map → widgets-yHQa4c6c.mjs.map} +1 -1
- package/dist/{zod-generator-dvxgmd1M.mjs → zod-generator-B80aap1J.mjs} +2 -2
- package/dist/{zod-generator-dvxgmd1M.mjs.map → zod-generator-B80aap1J.mjs.map} +1 -1
- package/package.json +7 -7
- package/src/api/errors.ts +2 -0
- package/src/api/handlers/index.ts +2 -0
- package/src/api/handlers/registry.ts +69 -1
- package/src/api/handlers/seo.ts +16 -1
- package/src/api/handlers/snapshot.ts +1 -1
- package/src/astro/integration/index.ts +26 -0
- package/src/astro/integration/routes.ts +5 -0
- package/src/astro/integration/runtime.ts +8 -0
- package/src/astro/middleware.ts +4 -0
- package/src/astro/public-plugin-api-routes.ts +41 -0
- package/src/astro/routes/api/admin/plugins/registry/[id]/update.ts +4 -0
- package/src/astro/routes/api/admin/plugins/registry/artifact.ts +388 -0
- package/src/astro/routes/api/admin/plugins/registry/install.ts +7 -1
- package/src/astro/routes/api/import/wordpress/rewrite-url-helpers.ts +22 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +5 -2
- package/src/astro/routes/sitemap-[collection].xml.ts +114 -14
- package/src/astro/types.ts +14 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +35 -11
- package/src/database/connection.ts +3 -10
- package/src/database/errors.ts +14 -0
- package/src/database/index.ts +3 -1
- package/src/database/migrations/runner.ts +29 -21
- package/src/emdash-runtime.ts +1 -0
- package/src/i18n/resolve.ts +152 -0
- package/src/index.ts +2 -0
- package/src/loader.ts +133 -59
- package/src/plugin-utils.ts +23 -0
- package/src/query.ts +24 -5
- package/src/utils/db-errors.ts +24 -0
- package/dist/connection-2igzM-AT.mjs +0 -57
- package/dist/connection-2igzM-AT.mjs.map +0 -1
- package/dist/db-errors-CGN9kJfo.mjs.map +0 -1
- package/dist/error-CPh_8eLq.mjs.map +0 -1
- package/dist/index-Bv1Wf1zB.d.mts.map +0 -1
- package/dist/loader-Chm5h7Gr.mjs.map +0 -1
- package/dist/menus-C75SSmRy.mjs.map +0 -1
- package/dist/query-BJn8TOPk.mjs.map +0 -1
- package/dist/resolve-Cj98DuqN.mjs +0 -39
- package/dist/resolve-Cj98DuqN.mjs.map +0 -1
- package/dist/runner-CNHRo1mT.d.mts.map +0 -1
- package/dist/seo-BoR4wCUh.mjs.map +0 -1
- package/dist/version-Ct7C6RSo.mjs +0 -7
|
@@ -35,13 +35,23 @@ export function portableTextToProsemirror(blocks: PortableTextBlock[]): ProseMir
|
|
|
35
35
|
|
|
36
36
|
// Check for list items
|
|
37
37
|
if (isTextBlock(block) && block.listItem) {
|
|
38
|
-
// Collect
|
|
38
|
+
// Collect a list "run": the level=1 anchor plus everything that
|
|
39
|
+
// nests under it (level > 1, regardless of listItem type — a number
|
|
40
|
+
// child under a bullet parent is still part of the same tree). A
|
|
41
|
+
// level=1 block with a different listItem ends the run. Without the
|
|
42
|
+
// `level > 1` carve-out the run breaks on the first nested type
|
|
43
|
+
// switch and the descendant subtree leaks out as its own top-level
|
|
44
|
+
// list (e.g. `[bullet L1, number L2, bullet L1]` would render as
|
|
45
|
+
// three sibling lists instead of one bullet list with a numbered
|
|
46
|
+
// child).
|
|
39
47
|
const listBlocks: PortableTextTextBlock[] = [];
|
|
40
48
|
const listType = block.listItem;
|
|
41
49
|
|
|
42
50
|
while (i < blocks.length) {
|
|
43
51
|
const current = blocks[i];
|
|
44
|
-
if (isTextBlock(current)
|
|
52
|
+
if (!isTextBlock(current) || !current.listItem) break;
|
|
53
|
+
const level = current.level || 1;
|
|
54
|
+
if (level > 1 || current.listItem === listType) {
|
|
45
55
|
listBlocks.push(current);
|
|
46
56
|
i++;
|
|
47
57
|
} else {
|
|
@@ -230,20 +240,34 @@ function convertListItem(
|
|
|
230
240
|
|
|
231
241
|
// Handle nested items
|
|
232
242
|
if (nestedItems.length > 0) {
|
|
233
|
-
//
|
|
234
|
-
|
|
243
|
+
// The shallowest level in `nestedItems` is the effective root of this
|
|
244
|
+
// item's nested subtree. A new sub-list only starts when we hit
|
|
245
|
+
// another block at that root level with a different `listItem` type;
|
|
246
|
+
// deeper blocks (level > minLevel) belong to the current group as
|
|
247
|
+
// descendants regardless of their own `listItem`. The previous
|
|
248
|
+
// grouping broke on any type change at any depth, so a deep mixed
|
|
249
|
+
// tree like `bullet L1 → number L2 → bullet L3 → number L2` would
|
|
250
|
+
// emit C(L3) as a sibling list under A(L1) instead of nesting it
|
|
251
|
+
// under B(L2), then degrade C to L2 on round-trip.
|
|
252
|
+
let minLevel = Infinity;
|
|
253
|
+
for (const ni of nestedItems) {
|
|
254
|
+
const level = ni.level || 2;
|
|
255
|
+
if (level < minLevel) minLevel = level;
|
|
256
|
+
}
|
|
235
257
|
|
|
258
|
+
let j = 0;
|
|
236
259
|
while (j < nestedItems.length) {
|
|
237
|
-
const
|
|
260
|
+
const anchorType: "bullet" | "number" = nestedItems[j].listItem || parentListType;
|
|
238
261
|
const nestedGroup: PortableTextTextBlock[] = [];
|
|
239
262
|
|
|
240
|
-
|
|
241
|
-
j < nestedItems.length &&
|
|
242
|
-
(nestedItems[j].listItem || parentListType) === nestedListType
|
|
243
|
-
) {
|
|
263
|
+
do {
|
|
244
264
|
nestedGroup.push(nestedItems[j]);
|
|
245
265
|
j++;
|
|
246
|
-
}
|
|
266
|
+
} while (
|
|
267
|
+
j < nestedItems.length &&
|
|
268
|
+
((nestedItems[j].level || 2) > minLevel ||
|
|
269
|
+
(nestedItems[j].listItem || parentListType) === anchorType)
|
|
270
|
+
);
|
|
247
271
|
|
|
248
272
|
if (nestedGroup.length > 0) {
|
|
249
273
|
// Decrease level for nested conversion
|
|
@@ -251,7 +275,7 @@ function convertListItem(
|
|
|
251
275
|
...ni,
|
|
252
276
|
level: (ni.level || 2) - 1,
|
|
253
277
|
}));
|
|
254
|
-
content.push(convertList(adjustedGroup,
|
|
278
|
+
content.push(convertList(adjustedGroup, anchorType));
|
|
255
279
|
}
|
|
256
280
|
}
|
|
257
281
|
}
|
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
import BetterSqlite3 from "better-sqlite3";
|
|
2
2
|
import { Kysely, SqliteDialect } from "kysely";
|
|
3
3
|
|
|
4
|
+
import { EmDashDatabaseError } from "./errors.js";
|
|
4
5
|
import { kyselyLogOption } from "./instrumentation.js";
|
|
5
6
|
import type { Database } from "./types.js";
|
|
6
7
|
|
|
8
|
+
export { EmDashDatabaseError };
|
|
9
|
+
|
|
7
10
|
export interface DatabaseConfig {
|
|
8
11
|
url: string;
|
|
9
12
|
authToken?: string;
|
|
10
13
|
}
|
|
11
14
|
|
|
12
|
-
export class EmDashDatabaseError extends Error {
|
|
13
|
-
constructor(
|
|
14
|
-
message: string,
|
|
15
|
-
public override cause?: unknown,
|
|
16
|
-
) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.name = "EmDashDatabaseError";
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
15
|
/**
|
|
23
16
|
* Returns a helpful, actionable message when better-sqlite3's native binary
|
|
24
17
|
* was compiled against a different Node.js version than the one running. This
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database error types. Kept in their own module (no driver imports) so the
|
|
3
|
+
* public package barrel can re-export them without dragging native database
|
|
4
|
+
* drivers into the module graph of consumers that picked a different dialect.
|
|
5
|
+
*/
|
|
6
|
+
export class EmDashDatabaseError extends Error {
|
|
7
|
+
constructor(
|
|
8
|
+
message: string,
|
|
9
|
+
public override cause?: unknown,
|
|
10
|
+
) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "EmDashDatabaseError";
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/database/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// `createDatabase` is intentionally not re-exported here: it lives in
|
|
2
|
+
// `connection.ts`, which statically imports `better-sqlite3`. See #947.
|
|
3
|
+
export { EmDashDatabaseError } from "./errors.js";
|
|
2
4
|
export type { DatabaseConfig } from "./connection.js";
|
|
3
5
|
export { runMigrations, getMigrationStatus, rollbackMigration } from "./migrations/runner.js";
|
|
4
6
|
export type { MigrationStatus } from "./migrations/runner.js";
|
|
@@ -107,16 +107,28 @@ export interface MigrationStatus {
|
|
|
107
107
|
const MIGRATION_TABLE = "_emdash_migrations";
|
|
108
108
|
const MIGRATION_LOCK_TABLE = "_emdash_migrations_lock";
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
export interface MigrationOptions {
|
|
111
|
+
migrationTableSchema?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function createMigrator(db: Kysely<Database>, options?: MigrationOptions): Migrator {
|
|
115
|
+
return new Migrator({
|
|
115
116
|
db,
|
|
116
117
|
provider: new StaticMigrationProvider(),
|
|
117
118
|
migrationTableName: MIGRATION_TABLE,
|
|
118
119
|
migrationLockTableName: MIGRATION_LOCK_TABLE,
|
|
120
|
+
migrationTableSchema: options?.migrationTableSchema,
|
|
119
121
|
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get migration status
|
|
126
|
+
*/
|
|
127
|
+
export async function getMigrationStatus(
|
|
128
|
+
db: Kysely<Database>,
|
|
129
|
+
options?: MigrationOptions,
|
|
130
|
+
): Promise<MigrationStatus> {
|
|
131
|
+
const migrator = createMigrator(db, options);
|
|
120
132
|
|
|
121
133
|
const migrations = await migrator.getMigrations();
|
|
122
134
|
|
|
@@ -274,24 +286,24 @@ function deepErrorMessage(error: unknown): string {
|
|
|
274
286
|
* success. This matches the user-observable expectation that running
|
|
275
287
|
* migrations twice in a row is a no-op.
|
|
276
288
|
*/
|
|
277
|
-
export async function runMigrations(
|
|
289
|
+
export async function runMigrations(
|
|
290
|
+
db: Kysely<Database>,
|
|
291
|
+
options?: MigrationOptions,
|
|
292
|
+
): Promise<{ applied: string[] }> {
|
|
278
293
|
// Fast path: check if all migrations are already applied.
|
|
279
294
|
// A single cheap query vs the Migrator's full schema introspection.
|
|
280
295
|
// We use `>=` rather than `===` so a database with extra rows from a
|
|
281
296
|
// newer build (e.g. mid-deploy old isolate, or downgrade) still skips
|
|
282
297
|
// the migrator instead of falling through to the race-recovery path
|
|
283
298
|
// unnecessarily.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
299
|
+
if (!options?.migrationTableSchema) {
|
|
300
|
+
const initialCount = await getAppliedMigrationCount(db);
|
|
301
|
+
if (initialCount !== null && initialCount >= MIGRATION_COUNT) {
|
|
302
|
+
return { applied: [] };
|
|
303
|
+
}
|
|
287
304
|
}
|
|
288
305
|
|
|
289
|
-
const migrator =
|
|
290
|
-
db,
|
|
291
|
-
provider: new StaticMigrationProvider(),
|
|
292
|
-
migrationTableName: MIGRATION_TABLE,
|
|
293
|
-
migrationLockTableName: MIGRATION_LOCK_TABLE,
|
|
294
|
-
});
|
|
306
|
+
const migrator = createMigrator(db, options);
|
|
295
307
|
|
|
296
308
|
const { error, results } = await migrator.migrateToLatest();
|
|
297
309
|
|
|
@@ -325,13 +337,9 @@ export async function runMigrations(db: Kysely<Database>): Promise<{ applied: st
|
|
|
325
337
|
*/
|
|
326
338
|
export async function rollbackMigration(
|
|
327
339
|
db: Kysely<Database>,
|
|
340
|
+
options?: MigrationOptions,
|
|
328
341
|
): Promise<{ rolledBack: string | null }> {
|
|
329
|
-
const migrator =
|
|
330
|
-
db,
|
|
331
|
-
provider: new StaticMigrationProvider(),
|
|
332
|
-
migrationTableName: MIGRATION_TABLE,
|
|
333
|
-
migrationLockTableName: MIGRATION_LOCK_TABLE,
|
|
334
|
-
});
|
|
342
|
+
const migrator = createMigrator(db, options);
|
|
335
343
|
|
|
336
344
|
const { error, results } = await migrator.migrateDown();
|
|
337
345
|
|
package/src/emdash-runtime.ts
CHANGED
package/src/i18n/resolve.ts
CHANGED
|
@@ -35,3 +35,155 @@ export function resolveLocaleChain(explicit?: string): string[] {
|
|
|
35
35
|
if (!isI18nEnabled()) return [locale];
|
|
36
36
|
return getFallbackChain(locale);
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
const REPEATED_SLASHES = /\/{2,}/g;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Interpolate a collection `url_pattern` with a row's slug and id.
|
|
43
|
+
*
|
|
44
|
+
* Falls back to `/{collection}/{slug}` when no pattern is configured.
|
|
45
|
+
* Does NOT apply any locale prefix — pass the result through
|
|
46
|
+
* Astro's `getRelativeLocaleUrl` / `getAbsoluteLocaleUrl` (or the
|
|
47
|
+
* `localizePath` helper below) to add the locale segment.
|
|
48
|
+
*/
|
|
49
|
+
export function interpolateUrlPattern(options: {
|
|
50
|
+
pattern: string | null;
|
|
51
|
+
collection: string;
|
|
52
|
+
slug: string;
|
|
53
|
+
id: string;
|
|
54
|
+
}): string {
|
|
55
|
+
const { pattern, collection, slug, id } = options;
|
|
56
|
+
const basePattern = pattern ?? `/${encodeURIComponent(collection)}/{slug}`;
|
|
57
|
+
let path = basePattern
|
|
58
|
+
.replace("{slug}", encodeURIComponent(slug))
|
|
59
|
+
.replace("{id}", encodeURIComponent(id));
|
|
60
|
+
path = path.replace(REPEATED_SLASHES, "/");
|
|
61
|
+
if (path.length > 1 && path.endsWith("/")) path = path.slice(0, -1);
|
|
62
|
+
if (!path.startsWith("/")) path = `/${path}`;
|
|
63
|
+
return path;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Apply a locale prefix to a path, honouring the user's Astro `i18n`
|
|
68
|
+
* routing config (`prefixDefaultLocale`, custom `path`/`codes` mappings).
|
|
69
|
+
*
|
|
70
|
+
* Reads the resolved config from `astro:config/server`, which is always
|
|
71
|
+
* available regardless of whether i18n is enabled -- so this function
|
|
72
|
+
* works in both i18n and non-i18n builds without tripping Astro's
|
|
73
|
+
* `i18nNotEnabled` resolver (the case with importing `astro:i18n`).
|
|
74
|
+
*
|
|
75
|
+
* Returns:
|
|
76
|
+
* - The original `path` when i18n is not configured.
|
|
77
|
+
* - The original `path` for the default locale when
|
|
78
|
+
* `prefixDefaultLocale` is false.
|
|
79
|
+
* - `/{segment}{path}` for any other configured locale, where
|
|
80
|
+
* `{segment}` is the locale's custom `path` if one is set,
|
|
81
|
+
* otherwise the locale code.
|
|
82
|
+
* - `null` when the row's locale isn't in the configured list.
|
|
83
|
+
* Callers should drop the entry: a sitemap link to a route the
|
|
84
|
+
* site can't serve is worse than no link at all (search engines
|
|
85
|
+
* get a 404 / soft-404 and downrank the page).
|
|
86
|
+
*
|
|
87
|
+
* Falls back to `getI18nConfig()` (EmDash's mirror of the same config,
|
|
88
|
+
* populated at runtime startup) when `astro:config/server` is
|
|
89
|
+
* unavailable -- e.g. running outside an Astro build context, such as
|
|
90
|
+
* in vitest.
|
|
91
|
+
*/
|
|
92
|
+
export async function localizePath(path: string, locale: string): Promise<string | null> {
|
|
93
|
+
const segment = await resolveLocaleSegment(locale);
|
|
94
|
+
if (segment === undefined) return null;
|
|
95
|
+
if (segment === null || segment === "") return normalizePath(path);
|
|
96
|
+
return normalizePath(`/${segment}${path}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Resolve the URL segment to use for a locale.
|
|
101
|
+
*
|
|
102
|
+
* Returns:
|
|
103
|
+
* - `null` when i18n isn't configured (caller should not prefix).
|
|
104
|
+
* - `""` when the locale is the default locale and
|
|
105
|
+
* `prefixDefaultLocale` is false (caller should not prefix).
|
|
106
|
+
* - The locale's custom `path` value, or the locale string itself.
|
|
107
|
+
* - `undefined` when the locale isn't in the configured list --
|
|
108
|
+
* the row points at a route the site can't serve.
|
|
109
|
+
*/
|
|
110
|
+
async function resolveLocaleSegment(locale: string): Promise<string | null | undefined> {
|
|
111
|
+
const i18n = await readAstroI18nConfig();
|
|
112
|
+
if (!i18n || !i18n.locales || i18n.locales.length <= 1) return null;
|
|
113
|
+
|
|
114
|
+
const isDefault = locale === i18n.defaultLocale;
|
|
115
|
+
if (isDefault && !i18n.prefixDefaultLocale) return "";
|
|
116
|
+
|
|
117
|
+
// When the locale has a custom `path`/`codes` mapping, use the path
|
|
118
|
+
// for the URL segment. Otherwise use the locale code directly.
|
|
119
|
+
for (const entry of i18n.locales) {
|
|
120
|
+
if (typeof entry === "string") {
|
|
121
|
+
if (entry === locale) return entry;
|
|
122
|
+
} else if (entry.codes.includes(locale)) {
|
|
123
|
+
return entry.path;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface AstroI18nConfig {
|
|
131
|
+
defaultLocale: string;
|
|
132
|
+
locales: Array<string | { codes: readonly string[]; path: string }>;
|
|
133
|
+
prefixDefaultLocale?: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let astroI18nCache: AstroI18nConfig | null | undefined;
|
|
137
|
+
|
|
138
|
+
async function readAstroI18nConfig(): Promise<AstroI18nConfig | null> {
|
|
139
|
+
if (astroI18nCache !== undefined) return astroI18nCache;
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const mod = (await import("astro:config/server")) as {
|
|
143
|
+
i18n?: {
|
|
144
|
+
defaultLocale: string;
|
|
145
|
+
locales: Array<string | { codes: readonly string[]; path: string }>;
|
|
146
|
+
routing?: { prefixDefaultLocale?: boolean } | string;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
if (!mod.i18n) {
|
|
150
|
+
astroI18nCache = null;
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const routing = mod.i18n.routing;
|
|
154
|
+
astroI18nCache = {
|
|
155
|
+
defaultLocale: mod.i18n.defaultLocale,
|
|
156
|
+
locales: mod.i18n.locales,
|
|
157
|
+
prefixDefaultLocale:
|
|
158
|
+
typeof routing === "object" ? (routing.prefixDefaultLocale ?? false) : false,
|
|
159
|
+
};
|
|
160
|
+
return astroI18nCache;
|
|
161
|
+
} catch {
|
|
162
|
+
// `astro:config/server` isn't resolvable (e.g. running under vitest
|
|
163
|
+
// outside an Astro build). Fall back to EmDash's runtime config,
|
|
164
|
+
// which is populated at startup via the same astroConfig object.
|
|
165
|
+
const cfg = getI18nConfig();
|
|
166
|
+
if (!cfg || !isI18nEnabled()) {
|
|
167
|
+
astroI18nCache = null;
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
astroI18nCache = {
|
|
171
|
+
defaultLocale: cfg.defaultLocale,
|
|
172
|
+
locales: cfg.locales,
|
|
173
|
+
prefixDefaultLocale: cfg.prefixDefaultLocale,
|
|
174
|
+
};
|
|
175
|
+
return astroI18nCache;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** @internal -- exposed for tests to reset the module-level cache. */
|
|
180
|
+
export function _resetAstroI18nCacheForTests(): void {
|
|
181
|
+
astroI18nCache = undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizePath(path: string): string {
|
|
185
|
+
let p = path.replace(REPEATED_SLASHES, "/");
|
|
186
|
+
if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1);
|
|
187
|
+
if (!p.startsWith("/")) p = `/${p}`;
|
|
188
|
+
return p;
|
|
189
|
+
}
|
package/src/index.ts
CHANGED
package/src/loader.ts
CHANGED
|
@@ -20,7 +20,7 @@ import { decodeCursor, encodeCursor } from "./database/repositories/types.js";
|
|
|
20
20
|
import { validateIdentifier } from "./database/validate.js";
|
|
21
21
|
import type { Database } from "./index.js";
|
|
22
22
|
import { getRequestContext } from "./request-context.js";
|
|
23
|
-
import { isMissingTableError } from "./utils/db-errors.js";
|
|
23
|
+
import { isMissingColumnError, isMissingTableError } from "./utils/db-errors.js";
|
|
24
24
|
|
|
25
25
|
const FIELD_NAME_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
26
26
|
|
|
@@ -311,10 +311,12 @@ function buildStatusCondition(
|
|
|
311
311
|
const scheduledAtExpr = isPostgres(db)
|
|
312
312
|
? sql`${sql.ref(scheduledAtField)}::timestamptz`
|
|
313
313
|
: sql.ref(scheduledAtField);
|
|
314
|
-
|
|
314
|
+
const nowExpr = isPostgres(db)
|
|
315
|
+
? currentTimestampValue(db)
|
|
316
|
+
: sql`strftime('%Y-%m-%dT%H:%M:%fZ', 'now')`;
|
|
317
|
+
return sql`(${sql.ref(statusField)} = 'published' OR (${sql.ref(statusField)} = 'scheduled' AND ${scheduledAtExpr} <= ${nowExpr}))`;
|
|
315
318
|
}
|
|
316
319
|
|
|
317
|
-
// For other statuses (draft, archived), just match exactly
|
|
318
320
|
return sql`${sql.ref(statusField)} = ${status}`;
|
|
319
321
|
}
|
|
320
322
|
|
|
@@ -408,6 +410,65 @@ function buildCursorCondition(
|
|
|
408
410
|
return sql`(${sql.ref(primary.field)} > ${orderValue} OR (${sql.ref(primary.field)} = ${orderValue} AND ${sql.ref(idField)} > ${cursorId}))`;
|
|
409
411
|
}
|
|
410
412
|
|
|
413
|
+
/** Type guard: is the where value a range object (not a string or array)? */
|
|
414
|
+
function isWhereRange(value: WhereValue): value is WhereRange {
|
|
415
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Build AND conditions for non-taxonomy field filters.
|
|
420
|
+
* Returns an array of sql fragments; empty if no field filters apply.
|
|
421
|
+
* Field names are validated against FIELD_NAME_PATTERN to prevent injection.
|
|
422
|
+
*/
|
|
423
|
+
function buildFieldConditions(
|
|
424
|
+
fields: Record<string, WhereValue>,
|
|
425
|
+
tablePrefix?: string,
|
|
426
|
+
): ReturnType<typeof sql>[] {
|
|
427
|
+
const conditions: ReturnType<typeof sql>[] = [];
|
|
428
|
+
|
|
429
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
430
|
+
if (!FIELD_NAME_PATTERN.test(key)) {
|
|
431
|
+
console.warn(`[emdash] where filter: invalid field name "${key}" ignored`);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (value == null) continue;
|
|
435
|
+
const ref = tablePrefix ? sql.ref(`${tablePrefix}.${key}`) : sql.ref(key);
|
|
436
|
+
|
|
437
|
+
if (isWhereRange(value)) {
|
|
438
|
+
if (value.gt !== undefined) conditions.push(sql`${ref} > ${value.gt}`);
|
|
439
|
+
if (value.gte !== undefined) conditions.push(sql`${ref} >= ${value.gte}`);
|
|
440
|
+
if (value.lt !== undefined) conditions.push(sql`${ref} < ${value.lt}`);
|
|
441
|
+
if (value.lte !== undefined) conditions.push(sql`${ref} <= ${value.lte}`);
|
|
442
|
+
} else if (Array.isArray(value)) {
|
|
443
|
+
if (value.length > 0) {
|
|
444
|
+
conditions.push(sql`${ref} IN (${sql.join(value.map((v) => sql`${v}`))})`);
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
conditions.push(sql`${ref} = ${value}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return conditions;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Range filter for comparison operators on field values.
|
|
456
|
+
* Values are compared as strings in the database. This works correctly for
|
|
457
|
+
* ISO 8601 dates (e.g. "2024-01-01T00:00:00Z") because lexicographic ordering
|
|
458
|
+
* matches chronological ordering. Ensure date values use a consistent format.
|
|
459
|
+
*/
|
|
460
|
+
export interface WhereRange {
|
|
461
|
+
gt?: string;
|
|
462
|
+
gte?: string;
|
|
463
|
+
lt?: string;
|
|
464
|
+
lte?: string;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* A where clause value: exact match, multi-value match, or range comparison.
|
|
469
|
+
*/
|
|
470
|
+
export type WhereValue = string | string[] | WhereRange;
|
|
471
|
+
|
|
411
472
|
/**
|
|
412
473
|
* Filter for loadCollection - type is required
|
|
413
474
|
*/
|
|
@@ -421,9 +482,16 @@ export interface CollectionFilter {
|
|
|
421
482
|
*/
|
|
422
483
|
cursor?: string;
|
|
423
484
|
/**
|
|
424
|
-
* Filter by field values
|
|
485
|
+
* Filter by field values, taxonomy terms, or ranges.
|
|
486
|
+
*
|
|
487
|
+
* Taxonomy names are detected automatically and filtered via JOIN.
|
|
488
|
+
* Other keys are treated as column filters on the content table.
|
|
489
|
+
*
|
|
490
|
+
* @example { category: 'news' } - taxonomy term
|
|
491
|
+
* @example { series: 'main' } - exact match on a content field
|
|
492
|
+
* @example { published_at: { gte: '2024-01-01', lt: '2025-01-01' } } - date range
|
|
425
493
|
*/
|
|
426
|
-
where?: Record<string,
|
|
494
|
+
where?: Record<string, WhereValue>;
|
|
427
495
|
/**
|
|
428
496
|
* Order results by field(s)
|
|
429
497
|
* @default { created_at: "desc" }
|
|
@@ -551,79 +619,81 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
551
619
|
? buildCursorCondition(cursor, orderBy, tableName)
|
|
552
620
|
: null;
|
|
553
621
|
|
|
554
|
-
//
|
|
622
|
+
// Separate taxonomy filters from field filters
|
|
555
623
|
let result: { rows: Record<string, unknown>[] };
|
|
624
|
+
let taxonomyFilter: { name: string; slugs: string[] } | null = null;
|
|
625
|
+
const fieldFilters: Record<string, WhereValue> = {};
|
|
556
626
|
|
|
557
627
|
if (where && Object.keys(where).length > 0) {
|
|
558
|
-
// Get taxonomy names to detect taxonomy filters
|
|
559
628
|
const taxNames = await getTaxonomyNames(db);
|
|
560
|
-
const taxonomyFilters: Record<string, string | string[]> = {};
|
|
561
629
|
|
|
562
630
|
for (const [key, value] of Object.entries(where)) {
|
|
631
|
+
if (value == null) continue;
|
|
563
632
|
if (taxNames.has(key)) {
|
|
564
|
-
|
|
633
|
+
if (isWhereRange(value)) {
|
|
634
|
+
console.warn(
|
|
635
|
+
`[emdash] where filter: range operators are not supported on taxonomy "${key}", ignored`,
|
|
636
|
+
);
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
if (taxonomyFilter) {
|
|
640
|
+
console.warn(
|
|
641
|
+
`[emdash] where filter: only one taxonomy is supported per query, "${key}" ignored`,
|
|
642
|
+
);
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
const slugs = Array.isArray(value) ? value : [value];
|
|
646
|
+
taxonomyFilter = { name: key, slugs };
|
|
647
|
+
} else {
|
|
648
|
+
fieldFilters[key] = value;
|
|
565
649
|
}
|
|
566
650
|
}
|
|
651
|
+
}
|
|
567
652
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
AND ct.entry_id = ${sql.ref(tableName)}.id
|
|
588
|
-
INNER JOIN taxonomies t
|
|
589
|
-
ON t.id = ct.taxonomy_id
|
|
590
|
-
WHERE ${sql.ref(tableName)}.deleted_at IS NULL
|
|
591
|
-
AND ${statusCondition}
|
|
592
|
-
${localeCondition}
|
|
593
|
-
${cursorCond}
|
|
594
|
-
AND t.name = ${taxName}
|
|
595
|
-
AND t.slug IN (${sql.join(slugs.map((s) => sql`${s}`))})
|
|
596
|
-
${orderByClause}
|
|
597
|
-
${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}
|
|
598
|
-
`.execute(db);
|
|
599
|
-
} else {
|
|
600
|
-
// No taxonomy filters, use simple query
|
|
601
|
-
const orderByClause = buildOrderByClause(orderBy);
|
|
602
|
-
const statusCondition = buildStatusCondition(db, status);
|
|
603
|
-
const localeFilter = locale ? sql`AND locale = ${locale}` : sql``;
|
|
604
|
-
const cursorCond = cursorCondition ? sql`AND ${cursorCondition}` : sql``;
|
|
605
|
-
result = await sql<Record<string, unknown>>`
|
|
606
|
-
SELECT * FROM ${sql.ref(tableName)}
|
|
607
|
-
WHERE deleted_at IS NULL
|
|
653
|
+
if (taxonomyFilter) {
|
|
654
|
+
const orderByClause = buildOrderByClause(orderBy, tableName);
|
|
655
|
+
const statusCondition = buildStatusCondition(db, status, tableName);
|
|
656
|
+
const localeCondition = locale
|
|
657
|
+
? sql`AND ${sql.ref(tableName)}.locale = ${locale}`
|
|
658
|
+
: sql``;
|
|
659
|
+
const cursorCond = cursorConditionPrefixed ? sql`AND ${cursorConditionPrefixed}` : sql``;
|
|
660
|
+
const fieldConds = buildFieldConditions(fieldFilters, tableName);
|
|
661
|
+
const fieldCondsSQL =
|
|
662
|
+
fieldConds.length > 0 ? sql`${sql.join(fieldConds, sql` AND `)}` : null;
|
|
663
|
+
|
|
664
|
+
result = await sql<Record<string, unknown>>`
|
|
665
|
+
SELECT DISTINCT ${sql.ref(tableName)}.* FROM ${sql.ref(tableName)}
|
|
666
|
+
INNER JOIN content_taxonomies ct
|
|
667
|
+
ON ct.collection = ${type}
|
|
668
|
+
AND ct.entry_id = ${sql.ref(tableName)}.id
|
|
669
|
+
INNER JOIN taxonomies t
|
|
670
|
+
ON t.id = ct.taxonomy_id
|
|
671
|
+
WHERE ${sql.ref(tableName)}.deleted_at IS NULL
|
|
608
672
|
AND ${statusCondition}
|
|
609
|
-
${
|
|
673
|
+
${localeCondition}
|
|
610
674
|
${cursorCond}
|
|
611
|
-
${
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
675
|
+
AND t.name = ${taxonomyFilter.name}
|
|
676
|
+
AND t.slug IN (${sql.join(taxonomyFilter.slugs.map((s) => sql`${s}`))})
|
|
677
|
+
${fieldCondsSQL ? sql`AND ${fieldCondsSQL}` : sql``}
|
|
678
|
+
${orderByClause}
|
|
679
|
+
${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}
|
|
680
|
+
`.execute(db);
|
|
615
681
|
} else {
|
|
616
|
-
// No where clause, use simple query
|
|
617
682
|
const orderByClause = buildOrderByClause(orderBy);
|
|
618
683
|
const statusCondition = buildStatusCondition(db, status);
|
|
619
684
|
const localeFilter = locale ? sql`AND locale = ${locale}` : sql``;
|
|
620
685
|
const cursorCond = cursorCondition ? sql`AND ${cursorCondition}` : sql``;
|
|
686
|
+
const fieldConds = buildFieldConditions(fieldFilters);
|
|
687
|
+
const fieldCondsSQL =
|
|
688
|
+
fieldConds.length > 0 ? sql`${sql.join(fieldConds, sql` AND `)}` : null;
|
|
689
|
+
|
|
621
690
|
result = await sql<Record<string, unknown>>`
|
|
622
691
|
SELECT * FROM ${sql.ref(tableName)}
|
|
623
692
|
WHERE deleted_at IS NULL
|
|
624
693
|
AND ${statusCondition}
|
|
625
694
|
${localeFilter}
|
|
626
695
|
${cursorCond}
|
|
696
|
+
${fieldCondsSQL ? sql`AND ${fieldCondsSQL}` : sql``}
|
|
627
697
|
${orderByClause}
|
|
628
698
|
${fetchLimit ? sql`LIMIT ${fetchLimit}` : sql``}
|
|
629
699
|
`.execute(db);
|
|
@@ -693,13 +763,17 @@ export function emdashLoader(): LiveLoader<EntryData, EntryFilter, CollectionFil
|
|
|
693
763
|
},
|
|
694
764
|
};
|
|
695
765
|
} catch (error) {
|
|
696
|
-
// Handle missing table gracefully - return empty collection.
|
|
697
|
-
//
|
|
698
|
-
|
|
766
|
+
// Handle missing table/column gracefully - return empty collection.
|
|
767
|
+
// Missing table happens before migrations have run.
|
|
768
|
+
// Missing column happens when a where filter references a non-existent field.
|
|
769
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
770
|
+
if (isMissingTableError(error) || isMissingColumnError(error)) {
|
|
771
|
+
if (isMissingColumnError(error)) {
|
|
772
|
+
console.warn(`[emdash] where filter: ${message}`);
|
|
773
|
+
}
|
|
699
774
|
return { entries: [] };
|
|
700
775
|
}
|
|
701
776
|
|
|
702
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
703
777
|
return {
|
|
704
778
|
error: new Error(`Failed to load collection: ${message}`),
|
|
705
779
|
};
|