emdash 0.7.0 → 0.9.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-Di31kZ28.d.mts → adapters-DoNJiveC.d.mts} +1 -1
- package/dist/{adapters-Di31kZ28.d.mts.map → adapters-DoNJiveC.d.mts.map} +1 -1
- package/dist/{apply-5uslYdUu.mjs → apply-BzltprvY.mjs} +90 -139
- package/dist/apply-BzltprvY.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +194 -17
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +6 -7
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +34 -57
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.d.mts.map +1 -1
- package/dist/astro/middleware/redirect.mjs +17 -12
- package/dist/astro/middleware/redirect.mjs.map +1 -1
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +9 -6
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +301 -165
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +34 -10
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{base64-MBPo9ozB.mjs → base64-BRICGH2l.mjs} +1 -1
- package/dist/{base64-MBPo9ozB.mjs.map → base64-BRICGH2l.mjs.map} +1 -1
- package/dist/{byline-C4OVd8b3.mjs → byline-BSaNL1w7.mjs} +5 -5
- package/dist/byline-BSaNL1w7.mjs.map +1 -0
- package/dist/bylines-CvJ3PYz2.mjs +113 -0
- package/dist/bylines-CvJ3PYz2.mjs.map +1 -0
- package/dist/cache-C6N_hhN7.mjs +65 -0
- package/dist/cache-C6N_hhN7.mjs.map +1 -0
- package/dist/{chunks-HGz06Soa.mjs → chunks-NBQVDOci.mjs} +8 -2
- package/dist/{chunks-HGz06Soa.mjs.map → chunks-NBQVDOci.mjs.map} +1 -1
- package/dist/cli/index.mjs +229 -31
- 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 +3 -3
- package/dist/client/index.mjs.map +1 -1
- package/dist/{config-BXwuX8Bx.mjs → config-BI0V3ICQ.mjs} +1 -1
- package/dist/{config-BXwuX8Bx.mjs.map → config-BI0V3ICQ.mjs.map} +1 -1
- package/dist/{content-D7J5y73J.mjs → content-8lOYF0pr.mjs} +43 -28
- package/dist/content-8lOYF0pr.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +2 -2
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/libsql.d.mts.map +1 -1
- package/dist/db/libsql.mjs +7 -2
- package/dist/db/libsql.mjs.map +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db/sqlite.d.mts.map +1 -1
- package/dist/db/sqlite.mjs +8 -3
- package/dist/db/sqlite.mjs.map +1 -1
- package/dist/{db-errors-D0UT85nC.mjs → db-errors-WRezodiz.mjs} +1 -1
- package/dist/{db-errors-D0UT85nC.mjs.map → db-errors-WRezodiz.mjs.map} +1 -1
- package/dist/{default-CME5YdZ3.mjs → default-D8ksjWhO.mjs} +1 -1
- package/dist/{default-CME5YdZ3.mjs.map → default-D8ksjWhO.mjs.map} +1 -1
- package/dist/{dialect-helpers-DhTzaUxP.mjs → dialect-helpers-BKCvISIQ.mjs} +19 -2
- package/dist/dialect-helpers-BKCvISIQ.mjs.map +1 -0
- package/dist/{error-CiYn9yDu.mjs → error-D_-tqP-I.mjs} +1 -1
- package/dist/error-D_-tqP-I.mjs.map +1 -0
- package/dist/{index-De6_Xv3v.d.mts → index-BFRaVcD6.d.mts} +243 -40
- package/dist/index-BFRaVcD6.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +29 -25
- package/dist/{load-CBcmDIot.mjs → load-DDqMMvZL.mjs} +2 -2
- package/dist/{load-CBcmDIot.mjs.map → load-DDqMMvZL.mjs.map} +1 -1
- package/dist/{loader-DeiBJEMe.mjs → loader-CKLbBnhK.mjs} +32 -10
- package/dist/loader-CKLbBnhK.mjs.map +1 -0
- package/dist/{manifest-schema-V30qsMft.mjs → manifest-schema-DqWNC3lM.mjs} +45 -3
- package/dist/manifest-schema-DqWNC3lM.mjs.map +1 -0
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/media/local-runtime.mjs +3 -3
- package/dist/{media-DqHVh136.mjs → media-BW32b4gi.mjs} +4 -7
- package/dist/media-BW32b4gi.mjs.map +1 -0
- package/dist/{mode-CpNnGkPz.mjs → mode-ier8jbBk.mjs} +1 -1
- package/dist/mode-ier8jbBk.mjs.map +1 -0
- package/dist/options-BVp3UsTS.mjs +117 -0
- package/dist/options-BVp3UsTS.mjs.map +1 -0
- package/dist/page/index.d.mts +2 -2
- package/dist/{placeholder-tzpqGWII.d.mts → placeholder-BE4o_2dc.d.mts} +1 -1
- package/dist/{placeholder-tzpqGWII.d.mts.map → placeholder-BE4o_2dc.d.mts.map} +1 -1
- package/dist/{placeholder-C-fk5hYI.mjs → placeholder-CIJejMlK.mjs} +1 -1
- package/dist/placeholder-CIJejMlK.mjs.map +1 -0
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -1
- package/dist/plugins/adapt-sandbox-entry.mjs +6 -5
- package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -1
- package/dist/public-url-DByxYjUw.mjs +51 -0
- package/dist/public-url-DByxYjUw.mjs.map +1 -0
- package/dist/{query-g4Ug-9j9.mjs → query-Cg9ZKRQ0.mjs} +114 -16
- package/dist/query-Cg9ZKRQ0.mjs.map +1 -0
- package/dist/{redirect-CN0Rt9Ob.mjs → redirect-BhUBKRc1.mjs} +13 -8
- package/dist/redirect-BhUBKRc1.mjs.map +1 -0
- package/dist/{registry-Ci3WxVAr.mjs → registry-Dw70ChxB.mjs} +69 -11
- package/dist/registry-Dw70ChxB.mjs.map +1 -0
- package/dist/{request-cache-DiR961CV.mjs → request-cache-B-bmkipQ.mjs} +1 -1
- package/dist/request-cache-B-bmkipQ.mjs.map +1 -0
- package/dist/runner-Bnoj7vjK.d.mts +44 -0
- package/dist/runner-Bnoj7vjK.d.mts.map +1 -0
- package/dist/{runner-tQ7BJ4T7.mjs → runner-C7ADox5q.mjs} +185 -55
- package/dist/{runner-tQ7BJ4T7.mjs.map → runner-C7ADox5q.mjs.map} +1 -1
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +4 -4
- package/dist/{search-B0effn3j.mjs → search-dOGEccMa.mjs} +341 -152
- package/dist/search-dOGEccMa.mjs.map +1 -0
- package/dist/secrets-CW3reAnU.mjs +314 -0
- package/dist/secrets-CW3reAnU.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +15 -14
- package/dist/seo/index.d.mts +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.d.mts.map +1 -1
- package/dist/storage/s3.mjs +4 -4
- package/dist/storage/s3.mjs.map +1 -1
- package/dist/{taxonomies-K2z0Uhnj.mjs → taxonomies-ZlRtD6AG.mjs} +14 -7
- package/dist/taxonomies-ZlRtD6AG.mjs.map +1 -0
- package/dist/{tokens-BFPFx3CA.mjs → tokens-D7zMmWi2.mjs} +2 -2
- package/dist/{tokens-BFPFx3CA.mjs.map → tokens-D7zMmWi2.mjs.map} +1 -1
- package/dist/{transport-BykRfpyy.mjs → transport-BeMCmin1.mjs} +6 -5
- package/dist/{transport-BykRfpyy.mjs.map → transport-BeMCmin1.mjs.map} +1 -1
- package/dist/{transport-H4Iwx7tC.d.mts → transport-DNEfeMaU.d.mts} +1 -1
- package/dist/{transport-H4Iwx7tC.d.mts.map → transport-DNEfeMaU.d.mts.map} +1 -1
- package/dist/types-4fVtCIm0.mjs +68 -0
- package/dist/types-4fVtCIm0.mjs.map +1 -0
- package/dist/{types-CnZYHyLW.d.mts → types-BSyXeCFW.d.mts} +24 -2
- package/dist/{types-CnZYHyLW.d.mts.map → types-BSyXeCFW.d.mts.map} +1 -1
- package/dist/{types-DgrIP0tF.d.mts → types-BuBIptGk.d.mts} +80 -106
- package/dist/types-BuBIptGk.d.mts.map +1 -0
- package/dist/{types-BH2L167P.mjs → types-CDbKp7ND.mjs} +1 -1
- package/dist/{types-BH2L167P.mjs.map → types-CDbKp7ND.mjs.map} +1 -1
- package/dist/{types-DDS4MxsT.mjs → types-CIOg5AR8.mjs} +1 -1
- package/dist/{types-DDS4MxsT.mjs.map → types-CIOg5AR8.mjs.map} +1 -1
- package/dist/{types-6CUZRrZP.d.mts → types-CJsYGpco.d.mts} +24 -2
- package/dist/{types-6CUZRrZP.d.mts.map → types-CJsYGpco.d.mts.map} +1 -1
- package/dist/types-CRxNbK-Z.mjs +68 -0
- package/dist/types-CRxNbK-Z.mjs.map +1 -0
- package/dist/{types-C2v0c34j.d.mts → types-CrtWgIvl.d.mts} +1 -1
- package/dist/{types-C2v0c34j.d.mts.map → types-CrtWgIvl.d.mts.map} +1 -1
- package/dist/{types-CFWjXmus.d.mts → types-M78DQ1lx.d.mts} +1 -1
- package/dist/{types-CFWjXmus.d.mts.map → types-M78DQ1lx.d.mts.map} +1 -1
- package/dist/{validate-CqsNItbt.mjs → validate-Baqf0slj.mjs} +3 -3
- package/dist/{validate-CqsNItbt.mjs.map → validate-Baqf0slj.mjs.map} +1 -1
- package/dist/{validate-kM8Pjuf7.d.mts → validate-BfQh_C_y.d.mts} +4 -4
- package/dist/{validate-kM8Pjuf7.d.mts.map → validate-BfQh_C_y.d.mts.map} +1 -1
- package/dist/validation-BfEI7tNe.mjs +144 -0
- package/dist/validation-BfEI7tNe.mjs.map +1 -0
- package/dist/version-DoxrVdYf.mjs +7 -0
- package/dist/{version-BnTKdfam.mjs.map → version-DoxrVdYf.mjs.map} +1 -1
- package/dist/zod-generator-CC0xNe_K.mjs +132 -0
- package/dist/zod-generator-CC0xNe_K.mjs.map +1 -0
- package/locals.d.ts +1 -6
- package/package.json +21 -7
- package/src/api/auth-storage.ts +37 -0
- package/src/api/error.ts +6 -0
- package/src/api/errors.ts +8 -0
- package/src/api/handlers/comments.ts +19 -4
- package/src/api/handlers/content.ts +151 -4
- package/src/api/handlers/device-flow.ts +5 -0
- package/src/api/handlers/index.ts +2 -0
- package/src/api/handlers/marketplace.ts +11 -4
- package/src/api/handlers/media.ts +8 -1
- package/src/api/handlers/menus.ts +160 -21
- package/src/api/handlers/oauth-authorization.ts +72 -33
- package/src/api/handlers/redirects.ts +16 -3
- package/src/api/handlers/revision.ts +23 -14
- package/src/api/handlers/sections.ts +8 -1
- package/src/api/handlers/taxonomies.ts +131 -22
- package/src/api/handlers/validation.ts +212 -0
- package/src/api/openapi/document.ts +4 -1
- package/src/api/public-url.ts +54 -5
- package/src/api/route-utils.ts +14 -0
- package/src/api/schemas/comments.ts +2 -2
- package/src/api/schemas/common.ts +1 -1
- package/src/api/schemas/content.ts +17 -0
- package/src/api/schemas/sections.ts +3 -3
- package/src/api/schemas/setup.ts +8 -0
- package/src/api/schemas/users.ts +1 -1
- package/src/api/schemas/widgets.ts +12 -10
- package/src/api/setup-complete.ts +40 -0
- package/src/api/types.ts +5 -1
- package/src/astro/integration/index.ts +30 -2
- package/src/astro/integration/routes.ts +28 -0
- package/src/astro/integration/runtime.ts +49 -1
- package/src/astro/integration/virtual-modules.ts +73 -2
- package/src/astro/integration/vite-config.ts +49 -13
- package/src/astro/middleware/auth.ts +34 -6
- package/src/astro/middleware/redirect.ts +29 -16
- package/src/astro/middleware/request-context.ts +15 -5
- package/src/astro/middleware.ts +41 -10
- package/src/astro/routes/PluginRegistry.tsx +10 -1
- package/src/astro/routes/api/auth/invite/complete.ts +6 -1
- package/src/astro/routes/api/auth/mode.ts +57 -0
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +23 -3
- package/src/astro/routes/api/auth/oauth/[provider].ts +10 -4
- package/src/astro/routes/api/auth/passkey/register/verify.ts +6 -1
- package/src/astro/routes/api/auth/passkey/verify.ts +6 -1
- package/src/astro/routes/api/auth/signup/complete.ts +6 -1
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +2 -2
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +34 -12
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +32 -2
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +4 -2
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +3 -2
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +8 -4
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +1 -1
- package/src/astro/routes/api/content/[collection]/[id].ts +12 -0
- package/src/astro/routes/api/content/[collection]/index.ts +1 -9
- package/src/astro/routes/api/import/wordpress/execute.ts +3 -1
- package/src/astro/routes/api/import/wordpress/media.ts +2 -7
- package/src/astro/routes/api/import/wordpress/prepare.ts +9 -0
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +3 -1
- package/src/astro/routes/api/manifest.ts +62 -45
- package/src/astro/routes/api/media/[id]/confirm.ts +10 -1
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +12 -3
- package/src/astro/routes/api/openapi.json.ts +27 -10
- package/src/astro/routes/api/redirects/404s/index.ts +10 -4
- package/src/astro/routes/api/redirects/404s/summary.ts +4 -2
- package/src/astro/routes/api/redirects/[id].ts +10 -4
- package/src/astro/routes/api/redirects/index.ts +7 -3
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +1 -1
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +0 -2
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +0 -1
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +0 -1
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +2 -2
- package/src/astro/routes/api/schema/collections/index.ts +1 -1
- package/src/astro/routes/api/search/index.ts +10 -2
- package/src/astro/routes/api/sections/[slug].ts +10 -4
- package/src/astro/routes/api/sections/index.ts +7 -3
- package/src/astro/routes/api/settings/email.ts +4 -9
- package/src/astro/routes/api/setup/admin-verify.ts +6 -1
- package/src/astro/routes/api/setup/admin.ts +8 -2
- package/src/astro/routes/api/setup/index.ts +2 -2
- package/src/astro/routes/api/setup/status.ts +3 -1
- package/src/astro/routes/api/snapshot.ts +44 -18
- package/src/astro/routes/api/taxonomies/index.ts +0 -1
- package/src/astro/routes/api/themes/preview.ts +11 -5
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +4 -1
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +4 -1
- package/src/astro/routes/api/widget-areas/[name].ts +4 -1
- package/src/astro/routes/api/widget-areas/index.ts +4 -1
- package/src/astro/types.ts +32 -3
- package/src/auth/allowed-origins.ts +168 -0
- package/src/auth/mode.ts +15 -3
- package/src/auth/passkey-config.ts +35 -13
- package/src/auth/providers/github-admin.tsx +29 -0
- package/src/auth/providers/github.ts +31 -0
- package/src/auth/providers/google-admin.tsx +44 -0
- package/src/auth/providers/google.ts +31 -0
- package/src/auth/types.ts +114 -4
- package/src/bylines/index.ts +37 -88
- package/src/cli/commands/auth.ts +28 -6
- package/src/cli/commands/bundle-utils.ts +11 -2
- package/src/cli/commands/bundle.ts +31 -9
- package/src/cli/commands/content.ts +13 -0
- package/src/cli/commands/login.ts +8 -1
- package/src/cli/commands/publish.ts +24 -0
- package/src/cli/commands/secrets.ts +183 -0
- package/src/cli/credentials.ts +1 -1
- package/src/cli/index.ts +5 -1
- package/src/client/index.ts +4 -4
- package/src/client/transport.ts +17 -7
- package/src/components/Break.astro +2 -2
- package/src/components/EmDashHead.astro +18 -13
- package/src/components/EmDashImage.astro +7 -6
- package/src/components/Embed.astro +1 -1
- package/src/components/Gallery.astro +6 -4
- package/src/components/Image.astro +9 -4
- package/src/components/InlinePortableTextEditor.tsx +106 -19
- package/src/components/LiveSearch.astro +5 -14
- package/src/config/secrets.ts +528 -0
- package/src/database/dialect-helpers.ts +50 -0
- package/src/database/migrations/034_published_at_index.ts +1 -1
- package/src/database/migrations/035_bounded_404_log.ts +56 -39
- package/src/database/migrations/runner.ts +156 -23
- package/src/database/repositories/audit.ts +6 -8
- package/src/database/repositories/byline.ts +6 -8
- package/src/database/repositories/comment.ts +12 -16
- package/src/database/repositories/content.ts +76 -52
- package/src/database/repositories/index.ts +1 -1
- package/src/database/repositories/media.ts +10 -13
- package/src/database/repositories/plugin-storage.ts +4 -6
- package/src/database/repositories/redirect.ts +26 -19
- package/src/database/repositories/taxonomy.ts +40 -3
- package/src/database/repositories/types.ts +57 -8
- package/src/database/repositories/user.ts +6 -8
- package/src/db/libsql.ts +1 -3
- package/src/db/sqlite.ts +2 -5
- package/src/emdash-runtime.ts +388 -247
- package/src/index.ts +14 -1
- package/src/loader.ts +30 -6
- package/src/mcp/server.ts +781 -141
- package/src/media/normalize.ts +1 -1
- package/src/media/url.ts +78 -0
- package/src/page/site-identity.ts +58 -0
- package/src/plugins/adapt-sandbox-entry.ts +22 -10
- package/src/plugins/context.ts +13 -10
- package/src/plugins/define-plugin.ts +40 -12
- package/src/plugins/email-console.ts +10 -3
- package/src/plugins/hooks.ts +34 -19
- package/src/plugins/index.ts +9 -0
- package/src/plugins/manifest-schema.ts +49 -2
- package/src/plugins/types.ts +174 -13
- package/src/preview/urls.ts +23 -3
- package/src/query.ts +149 -6
- package/src/redirects/cache.ts +38 -18
- package/src/request-cache.ts +3 -0
- package/src/schema/registry.ts +97 -5
- package/src/schema/zod-generator.ts +27 -5
- package/src/search/fts-manager.ts +0 -2
- package/src/search/query.ts +111 -26
- package/src/search/types.ts +8 -1
- package/src/sections/index.ts +7 -9
- package/src/seed/apply.ts +2 -0
- package/src/settings/index.ts +80 -6
- package/src/settings/types.ts +23 -1
- package/src/storage/s3.ts +12 -6
- package/src/taxonomies/index.ts +11 -1
- package/src/virtual-modules.d.ts +21 -1
- package/src/widgets/index.ts +1 -1
- package/dist/apply-5uslYdUu.mjs.map +0 -1
- package/dist/byline-C4OVd8b3.mjs.map +0 -1
- package/dist/bylines-hPTW79hw.mjs +0 -157
- package/dist/bylines-hPTW79hw.mjs.map +0 -1
- package/dist/cache-BkKBuIvS.mjs +0 -56
- package/dist/cache-BkKBuIvS.mjs.map +0 -1
- package/dist/chunk-ClPoSABd.mjs +0 -21
- package/dist/content-D7J5y73J.mjs.map +0 -1
- package/dist/dialect-helpers-DhTzaUxP.mjs.map +0 -1
- package/dist/error-CiYn9yDu.mjs.map +0 -1
- package/dist/index-De6_Xv3v.d.mts.map +0 -1
- package/dist/loader-DeiBJEMe.mjs.map +0 -1
- package/dist/manifest-schema-V30qsMft.mjs.map +0 -1
- package/dist/media-DqHVh136.mjs.map +0 -1
- package/dist/mode-CpNnGkPz.mjs.map +0 -1
- package/dist/placeholder-C-fk5hYI.mjs.map +0 -1
- package/dist/query-g4Ug-9j9.mjs.map +0 -1
- package/dist/redirect-CN0Rt9Ob.mjs.map +0 -1
- package/dist/registry-Ci3WxVAr.mjs.map +0 -1
- package/dist/request-cache-DiR961CV.mjs.map +0 -1
- package/dist/runner-BR2xKwhn.d.mts +0 -34
- package/dist/runner-BR2xKwhn.d.mts.map +0 -1
- package/dist/search-B0effn3j.mjs.map +0 -1
- package/dist/taxonomies-K2z0Uhnj.mjs.map +0 -1
- package/dist/types-CMMN0pNg.mjs +0 -31
- package/dist/types-CMMN0pNg.mjs.map +0 -1
- package/dist/types-DgrIP0tF.d.mts.map +0 -1
- package/dist/version-BnTKdfam.mjs +0 -7
|
@@ -226,14 +226,12 @@ export class PluginStorageRepository<T = unknown> implements StorageCollection<T
|
|
|
226
226
|
query = query.where(({ eb }) => eb(sql.join(whereSqlParts, sql.raw("")), "=", sql.raw("1")));
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
// Handle cursor-based pagination
|
|
229
|
+
// Handle cursor-based pagination — throws on invalid cursor.
|
|
230
230
|
if (cursor) {
|
|
231
231
|
const decoded = decodeCursor(cursor);
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
);
|
|
236
|
-
}
|
|
232
|
+
query = query.where(({ eb }) =>
|
|
233
|
+
eb(sql`(created_at, id)`, ">", sql`(${decoded.orderValue}, ${decoded.id})`),
|
|
234
|
+
);
|
|
237
235
|
}
|
|
238
236
|
|
|
239
237
|
// Build ORDER BY using sql template
|
|
@@ -28,6 +28,9 @@ export const REFERRER_MAX_LENGTH = 512;
|
|
|
28
28
|
/** Max stored length for the `User-Agent` header — truncated on insert. */
|
|
29
29
|
export const USER_AGENT_MAX_LENGTH = 256;
|
|
30
30
|
|
|
31
|
+
/** Pattern to escape LIKE wildcards: %, _, and backslash */
|
|
32
|
+
const LIKE_ESCAPE_RE = /[\\%_]/g;
|
|
33
|
+
|
|
31
34
|
/**
|
|
32
35
|
* Truncate a header-derived string to `max` chars, preserving `null`/`undefined`
|
|
33
36
|
* as `null`. Empty strings stay empty (the caller decides whether to coerce).
|
|
@@ -162,9 +165,15 @@ export class RedirectRepository {
|
|
|
162
165
|
.limit(limit + 1);
|
|
163
166
|
|
|
164
167
|
if (opts.search) {
|
|
165
|
-
|
|
168
|
+
// Escape LIKE wildcards in the search term to prevent injection.
|
|
169
|
+
// Must include ESCAPE clause for SQLite to recognize backslash as escape char.
|
|
170
|
+
const escaped = opts.search.replace(LIKE_ESCAPE_RE, (c) => `\\${c}`);
|
|
171
|
+
const term = `%${escaped}%`;
|
|
166
172
|
query = query.where((eb) =>
|
|
167
|
-
eb.or([
|
|
173
|
+
eb.or([
|
|
174
|
+
sql<boolean>`source LIKE ${term} ESCAPE '\\'`,
|
|
175
|
+
sql<boolean>`destination LIKE ${term} ESCAPE '\\'`,
|
|
176
|
+
]),
|
|
168
177
|
);
|
|
169
178
|
}
|
|
170
179
|
|
|
@@ -182,14 +191,12 @@ export class RedirectRepository {
|
|
|
182
191
|
|
|
183
192
|
if (opts.cursor) {
|
|
184
193
|
const decoded = decodeCursor(opts.cursor);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
eb.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
);
|
|
192
|
-
}
|
|
194
|
+
query = query.where((eb) =>
|
|
195
|
+
eb.or([
|
|
196
|
+
eb("created_at", "<", decoded.orderValue),
|
|
197
|
+
eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)]),
|
|
198
|
+
]),
|
|
199
|
+
);
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
const rows = await query.execute();
|
|
@@ -504,19 +511,19 @@ export class RedirectRepository {
|
|
|
504
511
|
.limit(limit + 1);
|
|
505
512
|
|
|
506
513
|
if (opts.search) {
|
|
507
|
-
|
|
514
|
+
const escaped = opts.search.replace(LIKE_ESCAPE_RE, (c) => `\\${c}`);
|
|
515
|
+
const term = `%${escaped}%`;
|
|
516
|
+
query = query.where(sql<boolean>`path LIKE ${term} ESCAPE '\\'`);
|
|
508
517
|
}
|
|
509
518
|
|
|
510
519
|
if (opts.cursor) {
|
|
511
520
|
const decoded = decodeCursor(opts.cursor);
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
eb.
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
);
|
|
519
|
-
}
|
|
521
|
+
query = query.where((eb) =>
|
|
522
|
+
eb.or([
|
|
523
|
+
eb("created_at", "<", decoded.orderValue),
|
|
524
|
+
eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)]),
|
|
525
|
+
]),
|
|
526
|
+
);
|
|
520
527
|
}
|
|
521
528
|
|
|
522
529
|
const rows = await query.execute();
|
|
@@ -41,12 +41,15 @@ export class TaxonomyRepository {
|
|
|
41
41
|
async create(input: CreateTaxonomyInput): Promise<Taxonomy> {
|
|
42
42
|
const id = ulid();
|
|
43
43
|
|
|
44
|
+
// Empty-string parentId is coerced to null defensively. Higher layers
|
|
45
|
+
// also normalize this — see handleTermCreate / handleTermUpdate.
|
|
46
|
+
const parentId = input.parentId === undefined || input.parentId === "" ? null : input.parentId;
|
|
44
47
|
const row: TaxonomyTable = {
|
|
45
48
|
id,
|
|
46
49
|
name: input.name,
|
|
47
50
|
slug: input.slug,
|
|
48
51
|
label: input.label,
|
|
49
|
-
parent_id:
|
|
52
|
+
parent_id: parentId,
|
|
50
53
|
data: input.data ? JSON.stringify(input.data) : null,
|
|
51
54
|
};
|
|
52
55
|
|
|
@@ -90,11 +93,15 @@ export class TaxonomyRepository {
|
|
|
90
93
|
* Get all terms for a taxonomy (e.g., all categories)
|
|
91
94
|
*/
|
|
92
95
|
async findByName(name: string, options: { parentId?: string | null } = {}): Promise<Taxonomy[]> {
|
|
96
|
+
// `id asc` is a stable tiebreaker for terms that share a label.
|
|
97
|
+
// Without it the SQL ordering is implementation-defined when labels
|
|
98
|
+
// match, which breaks keyset pagination over `(label, id)`.
|
|
93
99
|
let query = this.db
|
|
94
100
|
.selectFrom("taxonomies")
|
|
95
101
|
.selectAll()
|
|
96
102
|
.where("name", "=", name)
|
|
97
|
-
.orderBy("label", "asc")
|
|
103
|
+
.orderBy("label", "asc")
|
|
104
|
+
.orderBy("id", "asc");
|
|
98
105
|
|
|
99
106
|
if (options.parentId !== undefined) {
|
|
100
107
|
if (options.parentId === null) {
|
|
@@ -117,6 +124,7 @@ export class TaxonomyRepository {
|
|
|
117
124
|
.selectAll()
|
|
118
125
|
.where("parent_id", "=", parentId)
|
|
119
126
|
.orderBy("label", "asc")
|
|
127
|
+
.orderBy("id", "asc")
|
|
120
128
|
.execute();
|
|
121
129
|
|
|
122
130
|
return rows.map((row) => this.rowToTaxonomy(row));
|
|
@@ -132,7 +140,10 @@ export class TaxonomyRepository {
|
|
|
132
140
|
const updates: Partial<TaxonomyTable> = {};
|
|
133
141
|
if (input.slug !== undefined) updates.slug = input.slug;
|
|
134
142
|
if (input.label !== undefined) updates.label = input.label;
|
|
135
|
-
if (input.parentId !== undefined)
|
|
143
|
+
if (input.parentId !== undefined) {
|
|
144
|
+
// Defense in depth: empty-string parentId means null (no parent).
|
|
145
|
+
updates.parent_id = input.parentId === "" ? null : input.parentId;
|
|
146
|
+
}
|
|
136
147
|
if (input.data !== undefined) updates.data = JSON.stringify(input.data);
|
|
137
148
|
|
|
138
149
|
if (Object.keys(updates).length > 0) {
|
|
@@ -278,6 +289,32 @@ export class TaxonomyRepository {
|
|
|
278
289
|
return Number(result?.count || 0);
|
|
279
290
|
}
|
|
280
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Batch count entries for multiple taxonomy term IDs.
|
|
294
|
+
* Chunks the query at SQL_BATCH_SIZE to stay below D1's bind-parameter limit.
|
|
295
|
+
* Returns a Map from term ID to count.
|
|
296
|
+
*/
|
|
297
|
+
async countEntriesForTerms(termIds: string[]): Promise<Map<string, number>> {
|
|
298
|
+
if (termIds.length === 0) return new Map();
|
|
299
|
+
|
|
300
|
+
const { chunks, SQL_BATCH_SIZE } = await import("../../utils/chunks.js");
|
|
301
|
+
|
|
302
|
+
const counts = new Map<string, number>();
|
|
303
|
+
for (const chunk of chunks(termIds, SQL_BATCH_SIZE)) {
|
|
304
|
+
const rows = await this.db
|
|
305
|
+
.selectFrom("content_taxonomies")
|
|
306
|
+
.select(["taxonomy_id", (eb) => eb.fn.count("entry_id").as("count")])
|
|
307
|
+
.where("taxonomy_id", "in", chunk)
|
|
308
|
+
.groupBy("taxonomy_id")
|
|
309
|
+
.execute();
|
|
310
|
+
|
|
311
|
+
for (const row of rows) {
|
|
312
|
+
counts.set(row.taxonomy_id, Number(row.count || 0));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return counts;
|
|
316
|
+
}
|
|
317
|
+
|
|
281
318
|
/**
|
|
282
319
|
* Convert database row to Taxonomy object
|
|
283
320
|
*/
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { encodeBase64, decodeBase64 } from "../../utils/base64.js";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Hard cap on cursor length. Cursors we issue are short JSON-in-base64
|
|
5
|
+
* blobs; a real cursor is well under 200 chars. This guards against
|
|
6
|
+
* malicious callers passing megabyte-sized strings to force the base64
|
|
7
|
+
* decoder to allocate (decodeBase64 is O(N) in input size). The MCP and
|
|
8
|
+
* REST schemas also clamp at 2048 — this 4096 cap is a defense-in-depth
|
|
9
|
+
* floor inside the repository helpers.
|
|
10
|
+
*/
|
|
11
|
+
const MAX_CURSOR_LENGTH = 4096;
|
|
12
|
+
|
|
3
13
|
export interface CreateContentInput {
|
|
4
14
|
type: string;
|
|
5
15
|
slug?: string | null;
|
|
@@ -87,17 +97,45 @@ export function encodeCursor(orderValue: string, id: string): string {
|
|
|
87
97
|
return encodeBase64(JSON.stringify({ orderValue, id }));
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
/**
|
|
91
|
-
|
|
100
|
+
/**
|
|
101
|
+
* Thrown when a pagination cursor cannot be decoded.
|
|
102
|
+
*
|
|
103
|
+
* Repository callers should let this propagate; handler catch blocks
|
|
104
|
+
* map it to a structured `INVALID_CURSOR` error so client pagination
|
|
105
|
+
* bugs surface immediately rather than silently re-fetching the first
|
|
106
|
+
* page.
|
|
107
|
+
*/
|
|
108
|
+
export class InvalidCursorError extends Error {
|
|
109
|
+
constructor(cursor: string) {
|
|
110
|
+
const display = cursor.length > 50 ? `${cursor.slice(0, 47)}...` : cursor;
|
|
111
|
+
super(`Invalid pagination cursor: ${display}`);
|
|
112
|
+
this.name = "InvalidCursorError";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Decode a cursor to order value + id.
|
|
118
|
+
*
|
|
119
|
+
* Throws `InvalidCursorError` if the cursor is empty, not valid base64,
|
|
120
|
+
* not valid JSON, or doesn't contain string `orderValue` and `id` fields.
|
|
121
|
+
*/
|
|
122
|
+
export function decodeCursor(cursor: string): { orderValue: string; id: string } {
|
|
123
|
+
if (!cursor) throw new InvalidCursorError(cursor);
|
|
124
|
+
if (cursor.length > MAX_CURSOR_LENGTH) throw new InvalidCursorError(cursor);
|
|
125
|
+
let parsed: unknown;
|
|
92
126
|
try {
|
|
93
|
-
|
|
94
|
-
if (typeof parsed.orderValue === "string" && typeof parsed.id === "string") {
|
|
95
|
-
return parsed;
|
|
96
|
-
}
|
|
97
|
-
return null;
|
|
127
|
+
parsed = JSON.parse(decodeBase64(cursor));
|
|
98
128
|
} catch {
|
|
99
|
-
|
|
129
|
+
throw new InvalidCursorError(cursor);
|
|
130
|
+
}
|
|
131
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
132
|
+
throw new InvalidCursorError(cursor);
|
|
133
|
+
}
|
|
134
|
+
const candidate = parsed as { orderValue?: unknown; id?: unknown };
|
|
135
|
+
if (typeof candidate.orderValue !== "string" || typeof candidate.id !== "string") {
|
|
136
|
+
throw new InvalidCursorError(cursor);
|
|
100
137
|
}
|
|
138
|
+
return { orderValue: candidate.orderValue, id: candidate.id };
|
|
101
139
|
}
|
|
102
140
|
|
|
103
141
|
export interface ContentItem {
|
|
@@ -121,6 +159,17 @@ export interface ContentItem {
|
|
|
121
159
|
translationGroup: string | null;
|
|
122
160
|
/** SEO metadata — only populated for collections with `has_seo` enabled */
|
|
123
161
|
seo?: ContentSeo;
|
|
162
|
+
/**
|
|
163
|
+
* For collections that support `revisions`: when a draft revision exists,
|
|
164
|
+
* `data` reflects the unsaved draft and `liveData` carries the currently-
|
|
165
|
+
* published values. When no draft exists, `liveData` is undefined.
|
|
166
|
+
*
|
|
167
|
+
* Hydrated by `EmDashRuntime.hydrateDraftData()` — repositories themselves
|
|
168
|
+
* never set this field; it's purely a runtime-overlay concept that gives
|
|
169
|
+
* agents a clear picture of "draft vs. live" without re-fetching the
|
|
170
|
+
* revision history.
|
|
171
|
+
*/
|
|
172
|
+
liveData?: Record<string, unknown>;
|
|
124
173
|
}
|
|
125
174
|
|
|
126
175
|
export class EmDashValidationError extends Error {
|
|
@@ -123,14 +123,12 @@ export class UserRepository {
|
|
|
123
123
|
|
|
124
124
|
if (options.cursor) {
|
|
125
125
|
const decoded = decodeCursor(options.cursor);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
eb.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
133
|
-
}
|
|
126
|
+
query = query.where((eb) =>
|
|
127
|
+
eb.or([
|
|
128
|
+
eb("created_at", "<", decoded.orderValue),
|
|
129
|
+
eb.and([eb("created_at", "=", decoded.orderValue), eb("id", "<", decoded.id)]),
|
|
130
|
+
]),
|
|
131
|
+
);
|
|
134
132
|
}
|
|
135
133
|
|
|
136
134
|
const rows = await query.execute();
|
package/src/db/libsql.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Loaded at runtime via virtual module.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { LibsqlDialect } from "@libsql/kysely-libsql";
|
|
8
9
|
import type { Dialect } from "kysely";
|
|
9
10
|
|
|
10
11
|
import type { LibsqlConfig } from "./adapters.js";
|
|
@@ -13,9 +14,6 @@ import type { LibsqlConfig } from "./adapters.js";
|
|
|
13
14
|
* Create a libSQL dialect from config
|
|
14
15
|
*/
|
|
15
16
|
export function createDialect(config: LibsqlConfig): Dialect {
|
|
16
|
-
// Dynamic import to avoid loading @libsql/kysely-libsql at config time
|
|
17
|
-
const { LibsqlDialect } = require("@libsql/kysely-libsql");
|
|
18
|
-
|
|
19
17
|
return new LibsqlDialect({
|
|
20
18
|
url: config.url,
|
|
21
19
|
authToken: config.authToken,
|
package/src/db/sqlite.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Loaded at runtime via virtual module.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
9
|
+
import { type Dialect, SqliteDialect } from "kysely";
|
|
9
10
|
|
|
10
11
|
import type { SqliteConfig } from "./adapters.js";
|
|
11
12
|
|
|
@@ -13,10 +14,6 @@ import type { SqliteConfig } from "./adapters.js";
|
|
|
13
14
|
* Create a SQLite dialect from config
|
|
14
15
|
*/
|
|
15
16
|
export function createDialect(config: SqliteConfig): Dialect {
|
|
16
|
-
// Dynamic import to avoid loading better-sqlite3 at config time
|
|
17
|
-
const BetterSqlite3 = require("better-sqlite3");
|
|
18
|
-
const { SqliteDialect } = require("kysely");
|
|
19
|
-
|
|
20
17
|
// Parse URL to get file path
|
|
21
18
|
const url = config.url;
|
|
22
19
|
const filePath = url.startsWith("file:") ? url.slice(5) : url;
|