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
|
@@ -42,26 +42,33 @@ export interface MenuWithItems extends MenuRow {
|
|
|
42
42
|
*/
|
|
43
43
|
export async function handleMenuList(db: Kysely<Database>): Promise<ApiResult<MenuListItem[]>> {
|
|
44
44
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
45
|
+
// Single query: LEFT JOIN + GROUP BY for the per-menu item count.
|
|
46
|
+
// Avoids the N+1 of one count query per menu.
|
|
47
|
+
const rows = await db
|
|
48
|
+
.selectFrom("_emdash_menus as m")
|
|
49
|
+
.leftJoin("_emdash_menu_items as i", "i.menu_id", "m.id")
|
|
50
|
+
.select(({ fn }) => [
|
|
51
|
+
"m.id",
|
|
52
|
+
"m.name",
|
|
53
|
+
"m.label",
|
|
54
|
+
"m.created_at",
|
|
55
|
+
"m.updated_at",
|
|
56
|
+
fn.count<number>("i.id").as("itemCount"),
|
|
57
|
+
])
|
|
58
|
+
.groupBy(["m.id", "m.name", "m.label", "m.created_at", "m.updated_at"])
|
|
59
|
+
.orderBy("m.name", "asc")
|
|
49
60
|
.execute();
|
|
50
61
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
itemCount: count,
|
|
62
|
-
};
|
|
63
|
-
}),
|
|
64
|
-
);
|
|
62
|
+
// SQLite returns count as `number`, but some dialects (Postgres)
|
|
63
|
+
// return `string` from a count() aggregate. Normalize to number.
|
|
64
|
+
const menusWithCounts: MenuListItem[] = rows.map((row) => ({
|
|
65
|
+
id: row.id,
|
|
66
|
+
name: row.name,
|
|
67
|
+
label: row.label,
|
|
68
|
+
created_at: row.created_at,
|
|
69
|
+
updated_at: row.updated_at,
|
|
70
|
+
itemCount: typeof row.itemCount === "string" ? Number(row.itemCount) : row.itemCount,
|
|
71
|
+
}));
|
|
65
72
|
|
|
66
73
|
return { success: true, data: menusWithCounts };
|
|
67
74
|
} catch {
|
|
@@ -135,7 +142,7 @@ export async function handleMenuGet(
|
|
|
135
142
|
if (!menu) {
|
|
136
143
|
return {
|
|
137
144
|
success: false,
|
|
138
|
-
error: { code: "NOT_FOUND", message:
|
|
145
|
+
error: { code: "NOT_FOUND", message: `Menu '${name}' not found` },
|
|
139
146
|
};
|
|
140
147
|
}
|
|
141
148
|
|
|
@@ -173,7 +180,7 @@ export async function handleMenuUpdate(
|
|
|
173
180
|
if (!menu) {
|
|
174
181
|
return {
|
|
175
182
|
success: false,
|
|
176
|
-
error: { code: "NOT_FOUND", message:
|
|
183
|
+
error: { code: "NOT_FOUND", message: `Menu '${name}' not found` },
|
|
177
184
|
};
|
|
178
185
|
}
|
|
179
186
|
|
|
@@ -217,10 +224,14 @@ export async function handleMenuDelete(
|
|
|
217
224
|
if (!menu) {
|
|
218
225
|
return {
|
|
219
226
|
success: false,
|
|
220
|
-
error: { code: "NOT_FOUND", message:
|
|
227
|
+
error: { code: "NOT_FOUND", message: `Menu '${name}' not found` },
|
|
221
228
|
};
|
|
222
229
|
}
|
|
223
230
|
|
|
231
|
+
// D1 has FOREIGN KEYS off by default, so the migration's `ON DELETE
|
|
232
|
+
// CASCADE` won't fire there. Delete items explicitly first — this is
|
|
233
|
+
// idempotent on SQLite/Postgres where the cascade also fires.
|
|
234
|
+
await db.deleteFrom("_emdash_menu_items").where("menu_id", "=", menu.id).execute();
|
|
224
235
|
await db.deleteFrom("_emdash_menus").where("id", "=", menu.id).execute();
|
|
225
236
|
|
|
226
237
|
return { success: true, data: { deleted: true } };
|
|
@@ -443,6 +454,134 @@ export interface ReorderItem {
|
|
|
443
454
|
sortOrder: number;
|
|
444
455
|
}
|
|
445
456
|
|
|
457
|
+
// ---------------------------------------------------------------------------
|
|
458
|
+
// Atomic-replace menu items (used by the MCP `menu_set_items` tool)
|
|
459
|
+
// ---------------------------------------------------------------------------
|
|
460
|
+
|
|
461
|
+
export interface MenuSetItemsInput {
|
|
462
|
+
label: string;
|
|
463
|
+
type: "custom" | "page" | "post" | "taxonomy" | "collection";
|
|
464
|
+
customUrl?: string;
|
|
465
|
+
referenceCollection?: string;
|
|
466
|
+
referenceId?: string;
|
|
467
|
+
titleAttr?: string;
|
|
468
|
+
target?: string;
|
|
469
|
+
cssClasses?: string;
|
|
470
|
+
/**
|
|
471
|
+
* Index of the parent item in this same array. Must be strictly less
|
|
472
|
+
* than the current item's index so the insert order resolves parents
|
|
473
|
+
* before children. `undefined` makes the item top-level.
|
|
474
|
+
*/
|
|
475
|
+
parentIndex?: number;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Replace the entire set of items for a menu in one atomic transaction.
|
|
480
|
+
*
|
|
481
|
+
* Existing items are deleted and the new list is inserted in the order
|
|
482
|
+
* provided. `parentIndex` references resolve to actual parent IDs as the
|
|
483
|
+
* insert proceeds.
|
|
484
|
+
*/
|
|
485
|
+
export async function handleMenuSetItems(
|
|
486
|
+
db: Kysely<Database>,
|
|
487
|
+
menuName: string,
|
|
488
|
+
items: MenuSetItemsInput[],
|
|
489
|
+
): Promise<ApiResult<{ name: string; itemCount: number }>> {
|
|
490
|
+
// Validate parentIndex references — must be strictly earlier so
|
|
491
|
+
// the array can be inserted in order with parents resolved first.
|
|
492
|
+
// Negative indices are out of range; only Zod's `.nonnegative()` at
|
|
493
|
+
// the MCP boundary catches them today, so guard explicitly here for
|
|
494
|
+
// any caller that bypasses Zod (REST routes, direct handler use).
|
|
495
|
+
for (let i = 0; i < items.length; i++) {
|
|
496
|
+
const item = items[i];
|
|
497
|
+
if (item?.parentIndex !== undefined) {
|
|
498
|
+
if (item.parentIndex < 0 || item.parentIndex >= i) {
|
|
499
|
+
return {
|
|
500
|
+
success: false,
|
|
501
|
+
error: {
|
|
502
|
+
code: "VALIDATION_ERROR",
|
|
503
|
+
message: `item[${i}].parentIndex (${item.parentIndex}) must reference an earlier item`,
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
// Sentinel for "menu not found" thrown from inside the transaction
|
|
512
|
+
// so the rollback fires before we return the structured error.
|
|
513
|
+
const notFoundSentinel = Symbol("menu-not-found");
|
|
514
|
+
|
|
515
|
+
try {
|
|
516
|
+
await withTransaction(db, async (trx) => {
|
|
517
|
+
// Existence check INSIDE the transaction so a concurrent
|
|
518
|
+
// menu_delete between lookup and write can't leave orphan
|
|
519
|
+
// items on D1 (FKs disabled by default).
|
|
520
|
+
const menu = await trx
|
|
521
|
+
.selectFrom("_emdash_menus")
|
|
522
|
+
.select("id")
|
|
523
|
+
.where("name", "=", menuName)
|
|
524
|
+
.executeTakeFirst();
|
|
525
|
+
|
|
526
|
+
if (!menu) {
|
|
527
|
+
throw notFoundSentinel;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
await trx.deleteFrom("_emdash_menu_items").where("menu_id", "=", menu.id).execute();
|
|
531
|
+
|
|
532
|
+
const insertedIds: string[] = [];
|
|
533
|
+
for (let i = 0; i < items.length; i++) {
|
|
534
|
+
const item = items[i];
|
|
535
|
+
if (!item) continue;
|
|
536
|
+
const id = ulid();
|
|
537
|
+
const parentId =
|
|
538
|
+
item.parentIndex !== undefined ? (insertedIds[item.parentIndex] ?? null) : null;
|
|
539
|
+
await trx
|
|
540
|
+
.insertInto("_emdash_menu_items")
|
|
541
|
+
.values({
|
|
542
|
+
id,
|
|
543
|
+
menu_id: menu.id,
|
|
544
|
+
parent_id: parentId,
|
|
545
|
+
sort_order: i,
|
|
546
|
+
type: item.type,
|
|
547
|
+
reference_collection: item.referenceCollection ?? null,
|
|
548
|
+
reference_id: item.referenceId ?? null,
|
|
549
|
+
custom_url: item.customUrl ?? null,
|
|
550
|
+
label: item.label,
|
|
551
|
+
title_attr: item.titleAttr ?? null,
|
|
552
|
+
target: item.target ?? null,
|
|
553
|
+
css_classes: item.cssClasses ?? null,
|
|
554
|
+
})
|
|
555
|
+
.execute();
|
|
556
|
+
insertedIds.push(id);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
await trx
|
|
560
|
+
.updateTable("_emdash_menus")
|
|
561
|
+
.set({ updated_at: new Date().toISOString() })
|
|
562
|
+
.where("id", "=", menu.id)
|
|
563
|
+
.execute();
|
|
564
|
+
});
|
|
565
|
+
} catch (error) {
|
|
566
|
+
if (error === notFoundSentinel) {
|
|
567
|
+
return {
|
|
568
|
+
success: false,
|
|
569
|
+
error: { code: "NOT_FOUND", message: `Menu '${menuName}' not found` },
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
throw error;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return { success: true, data: { name: menuName, itemCount: items.length } };
|
|
576
|
+
} catch (error) {
|
|
577
|
+
console.error("[emdash] handleMenuSetItems failed:", error);
|
|
578
|
+
return {
|
|
579
|
+
success: false,
|
|
580
|
+
error: { code: "MENU_SET_ITEMS_ERROR", message: "Failed to set menu items" },
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
446
585
|
/**
|
|
447
586
|
* Batch reorder menu items.
|
|
448
587
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* utilities. Token infrastructure is shared with the device flow.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { clampScopes, computeS256Challenge } from "@emdash-cms/auth";
|
|
11
|
+
import { clampScopes, computeS256Challenge, secureCompare } from "@emdash-cms/auth";
|
|
12
12
|
import type { RoleLevel } from "@emdash-cms/auth";
|
|
13
13
|
import { generateCodeVerifier } from "arctic";
|
|
14
14
|
import type { Kysely } from "kysely";
|
|
@@ -19,10 +19,12 @@ import {
|
|
|
19
19
|
TOKEN_PREFIXES,
|
|
20
20
|
VALID_SCOPES,
|
|
21
21
|
} from "../../auth/api-tokens.js";
|
|
22
|
+
import { withTransaction } from "../../database/transaction.js";
|
|
22
23
|
import type { Database } from "../../database/types.js";
|
|
23
24
|
import { validateRedirectUri } from "../oauth/redirect-uri.js";
|
|
24
25
|
import type { ApiResult } from "../types.js";
|
|
25
26
|
import { lookupOAuthClient, validateClientRedirectUri } from "./oauth-clients.js";
|
|
27
|
+
import { lookupUserRoleAndStatus } from "./oauth-user-lookup.js";
|
|
26
28
|
|
|
27
29
|
// ---------------------------------------------------------------------------
|
|
28
30
|
// Constants
|
|
@@ -296,8 +298,9 @@ export async function handleAuthorizationCodeExchange(
|
|
|
296
298
|
}
|
|
297
299
|
|
|
298
300
|
// PKCE verification: SHA256(code_verifier) must match stored code_challenge
|
|
301
|
+
// Use constant-time comparison to prevent timing side-channels
|
|
299
302
|
const derivedChallenge = computeS256Challenge(params.code_verifier);
|
|
300
|
-
if (derivedChallenge
|
|
303
|
+
if (!secureCompare(derivedChallenge, row.code_challenge)) {
|
|
301
304
|
return {
|
|
302
305
|
success: false,
|
|
303
306
|
error: { code: "invalid_grant", message: "PKCE verification failed" },
|
|
@@ -312,44 +315,80 @@ export async function handleAuthorizationCodeExchange(
|
|
|
312
315
|
};
|
|
313
316
|
}
|
|
314
317
|
|
|
315
|
-
//
|
|
316
|
-
|
|
318
|
+
// Revalidate user role before issuing tokens (same pattern as handleTokenRefresh).
|
|
319
|
+
// The user's role may have changed since the authorization code was issued.
|
|
320
|
+
const userInfo = await lookupUserRoleAndStatus(db, row.user_id);
|
|
321
|
+
if (!userInfo) {
|
|
322
|
+
return {
|
|
323
|
+
success: false,
|
|
324
|
+
error: { code: "invalid_grant", message: "User not found" },
|
|
325
|
+
};
|
|
326
|
+
}
|
|
317
327
|
|
|
328
|
+
if (userInfo.disabled) {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
error: { code: "invalid_grant", message: "User account is disabled" },
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Re-clamp scopes against the user's current role
|
|
336
|
+
const storedScopes = JSON.parse(row.scopes) as string[];
|
|
337
|
+
let scopes = clampScopes(storedScopes, userInfo.role);
|
|
338
|
+
|
|
339
|
+
// Intersect with client's registered scopes (if restricted)
|
|
340
|
+
const client = await lookupOAuthClient(db, row.client_id);
|
|
341
|
+
if (client?.scopes?.length) {
|
|
342
|
+
scopes = scopes.filter((s: string) => client.scopes!.includes(s));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (scopes.length === 0) {
|
|
346
|
+
return {
|
|
347
|
+
success: false,
|
|
348
|
+
error: {
|
|
349
|
+
code: "invalid_grant",
|
|
350
|
+
message: "User role no longer supports any of the requested scopes",
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Issue tokens (same as device flow)
|
|
318
356
|
const accessToken = generatePrefixedToken(TOKEN_PREFIXES.OAUTH_ACCESS);
|
|
319
357
|
const accessExpires = expiresAt(ACCESS_TOKEN_TTL_SECONDS);
|
|
320
358
|
|
|
321
359
|
const refreshToken = generatePrefixedToken(TOKEN_PREFIXES.OAUTH_REFRESH);
|
|
322
360
|
const refreshExpires = expiresAt(REFRESH_TOKEN_TTL_SECONDS);
|
|
323
361
|
|
|
324
|
-
//
|
|
325
|
-
await db
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
362
|
+
// Atomically store both tokens in a transaction
|
|
363
|
+
await withTransaction(db, async (trx) => {
|
|
364
|
+
await trx
|
|
365
|
+
.insertInto("_emdash_oauth_tokens")
|
|
366
|
+
.values({
|
|
367
|
+
token_hash: accessToken.hash,
|
|
368
|
+
token_type: "access",
|
|
369
|
+
user_id: row.user_id,
|
|
370
|
+
scopes: JSON.stringify(scopes),
|
|
371
|
+
client_type: "mcp",
|
|
372
|
+
expires_at: accessExpires,
|
|
373
|
+
refresh_token_hash: refreshToken.hash,
|
|
374
|
+
client_id: row.client_id,
|
|
375
|
+
})
|
|
376
|
+
.execute();
|
|
377
|
+
|
|
378
|
+
await trx
|
|
379
|
+
.insertInto("_emdash_oauth_tokens")
|
|
380
|
+
.values({
|
|
381
|
+
token_hash: refreshToken.hash,
|
|
382
|
+
token_type: "refresh",
|
|
383
|
+
user_id: row.user_id,
|
|
384
|
+
scopes: JSON.stringify(scopes),
|
|
385
|
+
client_type: "mcp",
|
|
386
|
+
expires_at: refreshExpires,
|
|
387
|
+
refresh_token_hash: null,
|
|
388
|
+
client_id: row.client_id,
|
|
389
|
+
})
|
|
390
|
+
.execute();
|
|
391
|
+
});
|
|
353
392
|
|
|
354
393
|
return {
|
|
355
394
|
success: true,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type NotFoundEntry,
|
|
12
12
|
type NotFoundSummary,
|
|
13
13
|
} from "../../database/repositories/redirect.js";
|
|
14
|
+
import { InvalidCursorError } from "../../database/repositories/types.js";
|
|
14
15
|
import type { FindManyResult } from "../../database/repositories/types.js";
|
|
15
16
|
import type { Database } from "../../database/types.js";
|
|
16
17
|
import { wouldCreateLoop, detectLoops, type RedirectEdge } from "../../redirects/loops.js";
|
|
@@ -48,7 +49,13 @@ export async function handleRedirectList(
|
|
|
48
49
|
...(loopRedirectIds.length > 0 ? { loopRedirectIds } : {}),
|
|
49
50
|
},
|
|
50
51
|
};
|
|
51
|
-
} catch {
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof InvalidCursorError) {
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
52
59
|
return {
|
|
53
60
|
success: false,
|
|
54
61
|
error: { code: "REDIRECT_LIST_ERROR", message: "Failed to fetch redirects" },
|
|
@@ -318,7 +325,7 @@ export async function handleRedirectDelete(
|
|
|
318
325
|
function loopError(loopPath: string[]): ApiResult<never> {
|
|
319
326
|
const hops = loopPath
|
|
320
327
|
.slice(0, -1)
|
|
321
|
-
.map((p, i) => `${p} \u2192 ${loopPath[i + 1]
|
|
328
|
+
.map((p, i) => `${p} \u2192 ${loopPath[i + 1]}`)
|
|
322
329
|
.join("\n");
|
|
323
330
|
return {
|
|
324
331
|
success: false,
|
|
@@ -387,7 +394,13 @@ export async function handleNotFoundList(
|
|
|
387
394
|
const repo = new RedirectRepository(db);
|
|
388
395
|
const result = await repo.find404s(params);
|
|
389
396
|
return { success: true, data: result };
|
|
390
|
-
} catch {
|
|
397
|
+
} catch (error) {
|
|
398
|
+
if (error instanceof InvalidCursorError) {
|
|
399
|
+
return {
|
|
400
|
+
success: false,
|
|
401
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
402
|
+
};
|
|
403
|
+
}
|
|
391
404
|
return {
|
|
392
405
|
success: false,
|
|
393
406
|
error: { code: "NOT_FOUND_LIST_ERROR", message: "Failed to fetch 404 log" },
|
|
@@ -6,6 +6,7 @@ import type { Kysely } from "kysely";
|
|
|
6
6
|
|
|
7
7
|
import { ContentRepository } from "../../database/repositories/content.js";
|
|
8
8
|
import { RevisionRepository, type Revision } from "../../database/repositories/revision.js";
|
|
9
|
+
import { withTransaction } from "../../database/transaction.js";
|
|
9
10
|
import type { Database } from "../../database/types.js";
|
|
10
11
|
import type { ApiResult, ContentResponse } from "../types.js";
|
|
11
12
|
|
|
@@ -95,7 +96,6 @@ export async function handleRevisionRestore(
|
|
|
95
96
|
): Promise<ApiResult<ContentResponse>> {
|
|
96
97
|
try {
|
|
97
98
|
const revisionRepo = new RevisionRepository(db);
|
|
98
|
-
const contentRepo = new ContentRepository(db);
|
|
99
99
|
|
|
100
100
|
// Get the revision
|
|
101
101
|
const revision = await revisionRepo.findById(revisionId);
|
|
@@ -112,22 +112,31 @@ export async function handleRevisionRestore(
|
|
|
112
112
|
// Extract _slug from revision data (stored as metadata, not a real column)
|
|
113
113
|
const { _slug, ...fieldData } = revision.data;
|
|
114
114
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
115
|
+
// Atomically update content and create a new revision to record the restore.
|
|
116
|
+
// If either operation fails, neither is committed (on engines that support
|
|
117
|
+
// transactions; on D1, withTransaction falls back to sequential execution).
|
|
118
|
+
const item = await withTransaction(db, async (trx) => {
|
|
119
|
+
const trxContentRepo = new ContentRepository(trx);
|
|
120
|
+
const trxRevisionRepo = new RevisionRepository(trx);
|
|
121
|
+
|
|
122
|
+
const updated = await trxContentRepo.update(revision.collection, revision.entryId, {
|
|
123
|
+
data: fieldData,
|
|
124
|
+
slug: typeof _slug === "string" ? _slug : undefined,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await trxRevisionRepo.create({
|
|
128
|
+
collection: revision.collection,
|
|
129
|
+
entryId: revision.entryId,
|
|
130
|
+
data: revision.data,
|
|
131
|
+
authorId: callerUserId,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return updated;
|
|
127
135
|
});
|
|
128
136
|
|
|
129
137
|
// Fire-and-forget: prune old revisions to prevent unbounded growth
|
|
130
|
-
|
|
138
|
+
const pruneRepo = new RevisionRepository(db);
|
|
139
|
+
void pruneRepo.pruneOldRevisions(revision.collection, revision.entryId, 50).catch(() => {});
|
|
131
140
|
|
|
132
141
|
return {
|
|
133
142
|
success: true,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { Kysely } from "kysely";
|
|
6
6
|
import { ulid } from "ulidx";
|
|
7
7
|
|
|
8
|
+
import { InvalidCursorError } from "../../database/repositories/types.js";
|
|
8
9
|
import type { FindManyResult } from "../../database/repositories/types.js";
|
|
9
10
|
import type { Database } from "../../database/types.js";
|
|
10
11
|
import {
|
|
@@ -36,7 +37,13 @@ export async function handleSectionList(
|
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
return { success: true, data: result };
|
|
39
|
-
} catch {
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof InvalidCursorError) {
|
|
42
|
+
return {
|
|
43
|
+
success: false,
|
|
44
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
40
47
|
return {
|
|
41
48
|
success: false,
|
|
42
49
|
error: { code: "SECTION_LIST_ERROR", message: "Failed to fetch sections" },
|