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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emdash",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Astro-native CMS with WordPress migration support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -45,6 +45,12 @@
|
|
|
45
45
|
"default": "./dist/cli/index.mjs"
|
|
46
46
|
},
|
|
47
47
|
"./routes/*": "./src/astro/routes/*",
|
|
48
|
+
"./api/route-utils": "./src/api/route-utils.ts",
|
|
49
|
+
"./api/schemas": "./src/api/schemas/index.ts",
|
|
50
|
+
"./auth/providers/github": "./src/auth/providers/github.ts",
|
|
51
|
+
"./auth/providers/github-admin": "./src/auth/providers/github-admin.tsx",
|
|
52
|
+
"./auth/providers/google": "./src/auth/providers/google.ts",
|
|
53
|
+
"./auth/providers/google-admin": "./src/auth/providers/google-admin.tsx",
|
|
48
54
|
"./db": {
|
|
49
55
|
"types": "./dist/db/index.d.mts",
|
|
50
56
|
"default": "./dist/db/index.mjs"
|
|
@@ -128,6 +134,7 @@
|
|
|
128
134
|
"imports": {
|
|
129
135
|
"#api/schemas.js": "./src/api/schemas/index.js",
|
|
130
136
|
"#api/*": "./src/api/*",
|
|
137
|
+
"#config/*": "./src/config/*",
|
|
131
138
|
"#db/*": "./src/database/*",
|
|
132
139
|
"#auth/*": "./src/auth/*",
|
|
133
140
|
"#schema/*": "./src/schema/*",
|
|
@@ -153,6 +160,8 @@
|
|
|
153
160
|
"dependencies": {
|
|
154
161
|
"@floating-ui/react": "^0.27.16",
|
|
155
162
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
163
|
+
"@oslojs/crypto": "^1.0.1",
|
|
164
|
+
"@oslojs/encoding": "^1.1.0",
|
|
156
165
|
"@portabletext/toolkit": "^5.0.1",
|
|
157
166
|
"@tiptap/core": "^3.20.0",
|
|
158
167
|
"@tiptap/extension-focus": "^3.20.0",
|
|
@@ -185,9 +194,9 @@
|
|
|
185
194
|
"ulidx": "^2.4.1",
|
|
186
195
|
"upng-js": "^2.1.0",
|
|
187
196
|
"zod": "^4.3.5",
|
|
188
|
-
"@emdash-cms/admin": "0.
|
|
189
|
-
"@emdash-cms/auth": "0.
|
|
190
|
-
"@emdash-cms/gutenberg-to-portable-text": "0.
|
|
197
|
+
"@emdash-cms/admin": "0.9.0",
|
|
198
|
+
"@emdash-cms/auth": "0.9.0",
|
|
199
|
+
"@emdash-cms/gutenberg-to-portable-text": "0.9.0"
|
|
191
200
|
},
|
|
192
201
|
"optionalDependencies": {
|
|
193
202
|
"@libsql/kysely-libsql": "^0.4.0",
|
|
@@ -195,16 +204,21 @@
|
|
|
195
204
|
},
|
|
196
205
|
"peerDependencies": {
|
|
197
206
|
"@astrojs/react": ">=5.0.0-beta.0",
|
|
198
|
-
"@
|
|
199
|
-
"@tanstack/react-router": ">=1.100.0",
|
|
207
|
+
"@emdash-cms/auth-atproto": ">=0.2.1",
|
|
200
208
|
"astro": ">=6.0.0-beta.0",
|
|
201
209
|
"react": ">=18.0.0",
|
|
202
210
|
"react-dom": ">=18.0.0"
|
|
203
211
|
},
|
|
212
|
+
"peerDependenciesMeta": {
|
|
213
|
+
"@emdash-cms/auth-atproto": {
|
|
214
|
+
"optional": true
|
|
215
|
+
}
|
|
216
|
+
},
|
|
204
217
|
"devDependencies": {
|
|
205
218
|
"@apidevtools/swagger-parser": "^12.1.0",
|
|
206
219
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
207
220
|
"@types/better-sqlite3": "^7.6.12",
|
|
221
|
+
"@types/react": "19.2.14",
|
|
208
222
|
"@types/pg": "^8.16.0",
|
|
209
223
|
"@types/sanitize-html": "^2.16.0",
|
|
210
224
|
"@types/sax": "^1.2.7",
|
|
@@ -215,7 +229,7 @@
|
|
|
215
229
|
"vite": "^6.0.0",
|
|
216
230
|
"vitest": "^4.0.18",
|
|
217
231
|
"zod-openapi": "^5.4.6",
|
|
218
|
-
"@emdash-cms/blocks": "0.
|
|
232
|
+
"@emdash-cms/blocks": "0.9.0"
|
|
219
233
|
},
|
|
220
234
|
"repository": {
|
|
221
235
|
"type": "git",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth provider storage helper.
|
|
3
|
+
*
|
|
4
|
+
* Gives auth provider routes access to plugin-style storage collections
|
|
5
|
+
* namespaced under `auth:<providerId>`. Reuses the existing `_plugin_storage`
|
|
6
|
+
* table and `PluginStorageRepository` infrastructure.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Kysely } from "kysely";
|
|
10
|
+
|
|
11
|
+
import type { Database } from "../database/types.js";
|
|
12
|
+
import { createStorageAccess } from "../plugins/context.js";
|
|
13
|
+
import type { StorageCollection, StorageCollectionConfig } from "../plugins/types.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get storage collections for an auth provider.
|
|
17
|
+
*
|
|
18
|
+
* Returns a record of `StorageCollection` instances, one per declared
|
|
19
|
+
* collection in the provider's `storage` config. Data is stored in the
|
|
20
|
+
* shared `_plugin_storage` table under the namespace `auth:<providerId>`.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const storage = getAuthProviderStorage(emdash.db, "atproto", {
|
|
25
|
+
* states: { indexes: [] },
|
|
26
|
+
* sessions: { indexes: [] },
|
|
27
|
+
* });
|
|
28
|
+
* const session = await storage.sessions.get(sessionId);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export function getAuthProviderStorage(
|
|
32
|
+
db: Kysely<Database>,
|
|
33
|
+
providerId: string,
|
|
34
|
+
storageConfig: Record<string, StorageCollectionConfig>,
|
|
35
|
+
): Record<string, StorageCollection> {
|
|
36
|
+
return createStorageAccess(db, `auth:${providerId}`, storageConfig);
|
|
37
|
+
}
|
package/src/api/error.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* `new Response(JSON.stringify({ error: ... }), ...)` patterns.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { InvalidCursorError } from "../database/repositories/types.js";
|
|
8
9
|
import { mapErrorStatus } from "./errors.js";
|
|
9
10
|
import type { ApiResult } from "./types.js";
|
|
10
11
|
|
|
@@ -54,6 +55,11 @@ export function handleError(
|
|
|
54
55
|
fallbackMessage: string,
|
|
55
56
|
fallbackCode: string,
|
|
56
57
|
): Response {
|
|
58
|
+
// Bubble malformed-cursor errors as a structured 400 instead of a
|
|
59
|
+
// generic 500.
|
|
60
|
+
if (error instanceof InvalidCursorError) {
|
|
61
|
+
return apiError("INVALID_CURSOR", error.message, 400);
|
|
62
|
+
}
|
|
57
63
|
console.error(`[${fallbackCode}]`, error);
|
|
58
64
|
return apiError(fallbackCode, fallbackMessage, 500);
|
|
59
65
|
}
|
package/src/api/errors.ts
CHANGED
|
@@ -12,7 +12,9 @@ export const ErrorCode = {
|
|
|
12
12
|
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
13
13
|
INVALID_INPUT: "INVALID_INPUT",
|
|
14
14
|
INVALID_JSON: "INVALID_JSON",
|
|
15
|
+
INVALID_CURSOR: "INVALID_CURSOR",
|
|
15
16
|
CONFLICT: "CONFLICT",
|
|
17
|
+
SLUG_CONFLICT: "SLUG_CONFLICT",
|
|
16
18
|
NOT_CONFIGURED: "NOT_CONFIGURED",
|
|
17
19
|
UNAUTHORIZED: "UNAUTHORIZED",
|
|
18
20
|
FORBIDDEN: "FORBIDDEN",
|
|
@@ -152,6 +154,8 @@ export const ErrorCode = {
|
|
|
152
154
|
INVALID_CODE: "INVALID_CODE",
|
|
153
155
|
EXPIRED_CODE: "EXPIRED_CODE",
|
|
154
156
|
INSUFFICIENT_ROLE: "INSUFFICIENT_ROLE",
|
|
157
|
+
INSUFFICIENT_SCOPE: "INSUFFICIENT_SCOPE",
|
|
158
|
+
INSUFFICIENT_PERMISSIONS: "INSUFFICIENT_PERMISSIONS",
|
|
155
159
|
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
156
160
|
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
157
161
|
TOKEN_REVOKE_ERROR: "TOKEN_REVOKE_ERROR",
|
|
@@ -335,6 +339,7 @@ export function mapErrorStatus(code: string | undefined): number {
|
|
|
335
339
|
case ErrorCode.VALIDATION_ERROR:
|
|
336
340
|
case ErrorCode.INVALID_INPUT:
|
|
337
341
|
case ErrorCode.INVALID_JSON:
|
|
342
|
+
case ErrorCode.INVALID_CURSOR:
|
|
338
343
|
case ErrorCode.MISSING_PARAM:
|
|
339
344
|
case ErrorCode.INVALID_REQUEST:
|
|
340
345
|
case ErrorCode.NOT_SUPPORTED:
|
|
@@ -373,6 +378,8 @@ export function mapErrorStatus(code: string | undefined): number {
|
|
|
373
378
|
case ErrorCode.COMMENT_REJECTED:
|
|
374
379
|
case ErrorCode.DOMAIN_NOT_ALLOWED:
|
|
375
380
|
case ErrorCode.INSUFFICIENT_ROLE:
|
|
381
|
+
case ErrorCode.INSUFFICIENT_SCOPE:
|
|
382
|
+
case ErrorCode.INSUFFICIENT_PERMISSIONS:
|
|
376
383
|
case ErrorCode.CAPABILITY_ESCALATION:
|
|
377
384
|
case ErrorCode.ROUTE_VISIBILITY_ESCALATION:
|
|
378
385
|
case ErrorCode.AUDIT_FAILED:
|
|
@@ -388,6 +395,7 @@ export function mapErrorStatus(code: string | undefined): number {
|
|
|
388
395
|
|
|
389
396
|
// 409 Conflict
|
|
390
397
|
case ErrorCode.CONFLICT:
|
|
398
|
+
case ErrorCode.SLUG_CONFLICT:
|
|
391
399
|
case ErrorCode.COLLECTION_EXISTS:
|
|
392
400
|
case ErrorCode.FIELD_EXISTS:
|
|
393
401
|
case ErrorCode.CREDENTIAL_EXISTS:
|
|
@@ -8,6 +8,7 @@ import type { Kysely } from "kysely";
|
|
|
8
8
|
|
|
9
9
|
import { CommentRepository } from "../../database/repositories/comment.js";
|
|
10
10
|
import type { Comment, CommentStatus, PublicComment } from "../../database/repositories/comment.js";
|
|
11
|
+
import { InvalidCursorError } from "../../database/repositories/types.js";
|
|
11
12
|
import type { Database } from "../../database/types.js";
|
|
12
13
|
import type { ApiResult } from "../types.js";
|
|
13
14
|
|
|
@@ -60,6 +61,12 @@ export async function handleCommentList(
|
|
|
60
61
|
},
|
|
61
62
|
};
|
|
62
63
|
} catch (error) {
|
|
64
|
+
if (error instanceof InvalidCursorError) {
|
|
65
|
+
return {
|
|
66
|
+
success: false,
|
|
67
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
63
70
|
console.error("Comment list error:", error);
|
|
64
71
|
return {
|
|
65
72
|
success: false,
|
|
@@ -104,6 +111,12 @@ export async function handleCommentInbox(
|
|
|
104
111
|
},
|
|
105
112
|
};
|
|
106
113
|
} catch (error) {
|
|
114
|
+
if (error instanceof InvalidCursorError) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
118
|
+
};
|
|
119
|
+
}
|
|
107
120
|
console.error("Comment inbox error:", error);
|
|
108
121
|
return {
|
|
109
122
|
success: false,
|
|
@@ -303,11 +316,13 @@ export async function checkRateLimit(
|
|
|
303
316
|
/**
|
|
304
317
|
* Hash an IP address for storage (never store cleartext IPs).
|
|
305
318
|
*
|
|
306
|
-
* Uses full SHA-256 with
|
|
307
|
-
* recovery of IPs. The
|
|
308
|
-
*
|
|
319
|
+
* Uses full SHA-256 with a site-specific salt to prevent rainbow-table
|
|
320
|
+
* recovery of IPs. The salt must be provided by the caller — typically
|
|
321
|
+
* via `resolveSecretsCached(db).ipSalt` from `#config/secrets.js`. The
|
|
322
|
+
* salt is generated and persisted on first need so it's stable across
|
|
323
|
+
* requests within a deployment but unique per install.
|
|
309
324
|
*/
|
|
310
|
-
export async function hashIp(ip: string, salt: string
|
|
325
|
+
export async function hashIp(ip: string, salt: string): Promise<string> {
|
|
311
326
|
const data = `ip:${salt}:${ip}`;
|
|
312
327
|
const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(data));
|
|
313
328
|
return Array.from(new Uint8Array(buf), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
@@ -14,6 +14,7 @@ import { RevisionRepository } from "../../database/repositories/revision.js";
|
|
|
14
14
|
import { SeoRepository } from "../../database/repositories/seo.js";
|
|
15
15
|
import {
|
|
16
16
|
EmDashValidationError,
|
|
17
|
+
InvalidCursorError,
|
|
17
18
|
type ContentItem,
|
|
18
19
|
type ContentSeo,
|
|
19
20
|
type ContentSeoInput,
|
|
@@ -23,9 +24,26 @@ import type { Database } from "../../database/types.js";
|
|
|
23
24
|
import { validateIdentifier } from "../../database/validate.js";
|
|
24
25
|
import { isI18nEnabled } from "../../i18n/config.js";
|
|
25
26
|
import { invalidateRedirectCache } from "../../redirects/cache.js";
|
|
27
|
+
import { isMissingTableError } from "../../utils/db-errors.js";
|
|
26
28
|
import { encodeRev, validateRev } from "../rev.js";
|
|
27
29
|
import type { ApiResult, ContentListResponse, ContentResponse } from "../types.js";
|
|
28
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Narrow a caught error to one carrying a structured `apiError` discriminant.
|
|
33
|
+
* Used by transaction callbacks that want to surface a specific error code
|
|
34
|
+
* through the standard Error throwing path.
|
|
35
|
+
*/
|
|
36
|
+
function hasApiError(error: unknown): error is Error & { apiError: { code: string } } {
|
|
37
|
+
if (!(error instanceof Error) || !("apiError" in error)) return false;
|
|
38
|
+
const { apiError } = error;
|
|
39
|
+
return (
|
|
40
|
+
typeof apiError === "object" &&
|
|
41
|
+
apiError !== null &&
|
|
42
|
+
"code" in apiError &&
|
|
43
|
+
typeof apiError.code === "string"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
/**
|
|
30
48
|
* Extract a slug source (title or name) from content data.
|
|
31
49
|
* Returns null if no suitable string field is found.
|
|
@@ -267,6 +285,28 @@ export async function handleContentList(
|
|
|
267
285
|
},
|
|
268
286
|
};
|
|
269
287
|
} catch (error) {
|
|
288
|
+
if (error instanceof InvalidCursorError) {
|
|
289
|
+
return {
|
|
290
|
+
success: false,
|
|
291
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (isMissingTableError(error)) {
|
|
295
|
+
return {
|
|
296
|
+
success: false,
|
|
297
|
+
error: {
|
|
298
|
+
code: "COLLECTION_NOT_FOUND",
|
|
299
|
+
message: `Collection '${collection}' not found`,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (error instanceof EmDashValidationError) {
|
|
304
|
+
// e.g. invalid orderBy field
|
|
305
|
+
return {
|
|
306
|
+
success: false,
|
|
307
|
+
error: { code: "VALIDATION_ERROR", message: error.message },
|
|
308
|
+
};
|
|
309
|
+
}
|
|
270
310
|
console.error("Content list error:", error);
|
|
271
311
|
return {
|
|
272
312
|
success: false,
|
|
@@ -453,6 +493,46 @@ export async function handleContentCreate(
|
|
|
453
493
|
data: { item, _rev: encodeRev(item) },
|
|
454
494
|
};
|
|
455
495
|
} catch (error) {
|
|
496
|
+
if (isMissingTableError(error)) {
|
|
497
|
+
return {
|
|
498
|
+
success: false,
|
|
499
|
+
error: {
|
|
500
|
+
code: "COLLECTION_NOT_FOUND",
|
|
501
|
+
message: `Collection '${collection}' not found`,
|
|
502
|
+
},
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
if (error instanceof EmDashValidationError) {
|
|
506
|
+
return {
|
|
507
|
+
success: false,
|
|
508
|
+
error: { code: "VALIDATION_ERROR", message: error.message },
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
// SQLite UNIQUE constraint OR Postgres unique_violation — slug
|
|
512
|
+
// collisions and any other unique violations land here. Match
|
|
513
|
+
// specifically on "unique constraint failed" / "duplicate key" so we
|
|
514
|
+
// don't false-positive on NOT NULL or CHECK violations whose
|
|
515
|
+
// messages also contain "constraint failed".
|
|
516
|
+
const message = error instanceof Error ? error.message.toLowerCase() : "";
|
|
517
|
+
if (message.includes("unique constraint failed") || message.includes("duplicate key")) {
|
|
518
|
+
// Detect slug-specific collisions by message fingerprint
|
|
519
|
+
if (message.includes("slug")) {
|
|
520
|
+
return {
|
|
521
|
+
success: false,
|
|
522
|
+
error: {
|
|
523
|
+
code: "SLUG_CONFLICT",
|
|
524
|
+
message: `Slug '${body.slug ?? "(auto-generated)"}' already exists in collection '${collection}'`,
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
success: false,
|
|
530
|
+
error: {
|
|
531
|
+
code: "CONFLICT",
|
|
532
|
+
message: "Unique constraint violation",
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
}
|
|
456
536
|
console.error("Content create error:", error);
|
|
457
537
|
return {
|
|
458
538
|
success: false,
|
|
@@ -604,11 +684,44 @@ export async function handleContentUpdate(
|
|
|
604
684
|
} catch (error) {
|
|
605
685
|
// Handle structured errors thrown from inside the transaction
|
|
606
686
|
// (rev check failures, not-found)
|
|
607
|
-
if (error
|
|
608
|
-
const { code } = (error as Error & { apiError: { code: string } }).apiError;
|
|
687
|
+
if (hasApiError(error)) {
|
|
609
688
|
return {
|
|
610
689
|
success: false,
|
|
611
|
-
error: { code, message: error.message },
|
|
690
|
+
error: { code: error.apiError.code, message: error.message },
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
if (isMissingTableError(error)) {
|
|
694
|
+
return {
|
|
695
|
+
success: false,
|
|
696
|
+
error: {
|
|
697
|
+
code: "COLLECTION_NOT_FOUND",
|
|
698
|
+
message: `Collection '${collection}' not found`,
|
|
699
|
+
},
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
if (error instanceof EmDashValidationError) {
|
|
703
|
+
return {
|
|
704
|
+
success: false,
|
|
705
|
+
error: { code: "VALIDATION_ERROR", message: error.message },
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
const message = error instanceof Error ? error.message.toLowerCase() : "";
|
|
709
|
+
if (message.includes("unique constraint failed") || message.includes("duplicate key")) {
|
|
710
|
+
if (message.includes("slug")) {
|
|
711
|
+
return {
|
|
712
|
+
success: false,
|
|
713
|
+
error: {
|
|
714
|
+
code: "SLUG_CONFLICT",
|
|
715
|
+
message: `Slug '${body.slug ?? id}' already exists in collection '${collection}'`,
|
|
716
|
+
},
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
success: false,
|
|
721
|
+
error: {
|
|
722
|
+
code: "CONFLICT",
|
|
723
|
+
message: "Unique constraint violation",
|
|
724
|
+
},
|
|
612
725
|
};
|
|
613
726
|
}
|
|
614
727
|
console.error("Content update error:", error);
|
|
@@ -869,6 +982,12 @@ export async function handleContentListTrashed(
|
|
|
869
982
|
},
|
|
870
983
|
};
|
|
871
984
|
} catch (error) {
|
|
985
|
+
if (error instanceof InvalidCursorError) {
|
|
986
|
+
return {
|
|
987
|
+
success: false,
|
|
988
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
989
|
+
};
|
|
990
|
+
}
|
|
872
991
|
console.error("Content list trashed error:", error);
|
|
873
992
|
return {
|
|
874
993
|
success: false,
|
|
@@ -974,6 +1093,15 @@ export async function handleContentUnschedule(
|
|
|
974
1093
|
data: { item },
|
|
975
1094
|
};
|
|
976
1095
|
} catch (error) {
|
|
1096
|
+
if (error instanceof EmDashValidationError) {
|
|
1097
|
+
return {
|
|
1098
|
+
success: false,
|
|
1099
|
+
error: {
|
|
1100
|
+
code: "VALIDATION_ERROR",
|
|
1101
|
+
message: error.message,
|
|
1102
|
+
},
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
977
1105
|
console.error("Content unschedule error:", error);
|
|
978
1106
|
return {
|
|
979
1107
|
success: false,
|
|
@@ -996,12 +1124,13 @@ export async function handleContentPublish(
|
|
|
996
1124
|
db: Kysely<Database>,
|
|
997
1125
|
collection: string,
|
|
998
1126
|
id: string,
|
|
1127
|
+
options: { publishedAt?: string } = {},
|
|
999
1128
|
): Promise<ApiResult<ContentResponse>> {
|
|
1000
1129
|
try {
|
|
1001
1130
|
const item = await withTransaction(db, async (trx) => {
|
|
1002
1131
|
const repo = new ContentRepository(trx);
|
|
1003
1132
|
const resolvedId = (await resolveId(repo, collection, id)) ?? id;
|
|
1004
|
-
return repo.publish(collection, resolvedId);
|
|
1133
|
+
return repo.publish(collection, resolvedId, options.publishedAt);
|
|
1005
1134
|
});
|
|
1006
1135
|
|
|
1007
1136
|
const hasSeo = await collectionHasSeo(db, collection);
|
|
@@ -1012,6 +1141,15 @@ export async function handleContentPublish(
|
|
|
1012
1141
|
data: { item },
|
|
1013
1142
|
};
|
|
1014
1143
|
} catch (error) {
|
|
1144
|
+
if (error instanceof EmDashValidationError) {
|
|
1145
|
+
return {
|
|
1146
|
+
success: false,
|
|
1147
|
+
error: {
|
|
1148
|
+
code: "VALIDATION_ERROR",
|
|
1149
|
+
message: error.message,
|
|
1150
|
+
},
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1015
1153
|
console.error("Content publish error:", error);
|
|
1016
1154
|
return {
|
|
1017
1155
|
success: false,
|
|
@@ -1049,6 +1187,15 @@ export async function handleContentUnpublish(
|
|
|
1049
1187
|
data: { item },
|
|
1050
1188
|
};
|
|
1051
1189
|
} catch (error) {
|
|
1190
|
+
if (error instanceof EmDashValidationError) {
|
|
1191
|
+
return {
|
|
1192
|
+
success: false,
|
|
1193
|
+
error: {
|
|
1194
|
+
code: "VALIDATION_ERROR",
|
|
1195
|
+
message: error.message,
|
|
1196
|
+
},
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1052
1199
|
console.error("Content unpublish error:", error);
|
|
1053
1200
|
return {
|
|
1054
1201
|
success: false,
|
|
@@ -135,6 +135,11 @@ export async function handleDeviceCodeRequest(
|
|
|
135
135
|
verificationUri: string,
|
|
136
136
|
): Promise<ApiResult<DeviceCodeResponse>> {
|
|
137
137
|
try {
|
|
138
|
+
// Note: client_id is accepted but not validated against _emdash_oauth_clients
|
|
139
|
+
// because the CLI uses a well-known built-in client ID ("emdash-cli") that
|
|
140
|
+
// isn't stored in the DB. Full client_id validation + scope clamping for the
|
|
141
|
+
// device flow is tracked as a follow-up.
|
|
142
|
+
|
|
138
143
|
// Parse and validate scopes
|
|
139
144
|
const requestedScopes = input.scope ? input.scope.split(" ").filter(Boolean) : [];
|
|
140
145
|
const scopes = normalizeScopes(requestedScopes);
|
|
@@ -113,11 +113,13 @@ export {
|
|
|
113
113
|
handleMenuItemUpdate,
|
|
114
114
|
handleMenuItemDelete,
|
|
115
115
|
handleMenuItemReorder,
|
|
116
|
+
handleMenuSetItems,
|
|
116
117
|
type MenuListItem,
|
|
117
118
|
type MenuWithItems,
|
|
118
119
|
type CreateMenuItemInput,
|
|
119
120
|
type UpdateMenuItemInput,
|
|
120
121
|
type ReorderItem,
|
|
122
|
+
type MenuSetItemsInput,
|
|
121
123
|
} from "./menus.js";
|
|
122
124
|
|
|
123
125
|
// Section handlers
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "../../plugins/marketplace.js";
|
|
25
25
|
import type { SandboxRunner } from "../../plugins/sandbox/types.js";
|
|
26
26
|
import { PluginStateRepository } from "../../plugins/state.js";
|
|
27
|
+
import { normalizeCapabilities } from "../../plugins/types.js";
|
|
27
28
|
import type { PluginManifest } from "../../plugins/types.js";
|
|
28
29
|
import { EmDashStorageError } from "../../storage/types.js";
|
|
29
30
|
import type { Storage } from "../../storage/types.js";
|
|
@@ -95,11 +96,17 @@ function diffCapabilities(
|
|
|
95
96
|
oldCaps: string[],
|
|
96
97
|
newCaps: string[],
|
|
97
98
|
): { added: string[]; removed: string[] } {
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
// Normalize both sides before diffing so that an installed v1 manifest
|
|
100
|
+
// declaring `read:content` and an upgrade v2 manifest declaring
|
|
101
|
+
// `content:read` produces an empty diff — users should not see a
|
|
102
|
+
// spurious "capability changed" prompt for a pure rename.
|
|
103
|
+
const oldNorm = normalizeCapabilities(oldCaps);
|
|
104
|
+
const newNorm = normalizeCapabilities(newCaps);
|
|
105
|
+
const oldSet = new Set(oldNorm);
|
|
106
|
+
const newSet = new Set(newNorm);
|
|
100
107
|
return {
|
|
101
|
-
added:
|
|
102
|
-
removed:
|
|
108
|
+
added: newNorm.filter((c) => !oldSet.has(c)),
|
|
109
|
+
removed: oldNorm.filter((c) => !newSet.has(c)),
|
|
103
110
|
};
|
|
104
111
|
}
|
|
105
112
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { Kysely } from "kysely";
|
|
6
6
|
|
|
7
7
|
import { MediaRepository, type MediaItem } from "../../database/repositories/media.js";
|
|
8
|
+
import { InvalidCursorError } from "../../database/repositories/types.js";
|
|
8
9
|
import type { Database } from "../../database/types.js";
|
|
9
10
|
import type { ApiResult } from "../types.js";
|
|
10
11
|
|
|
@@ -43,7 +44,13 @@ export async function handleMediaList(
|
|
|
43
44
|
nextCursor: result.nextCursor,
|
|
44
45
|
},
|
|
45
46
|
};
|
|
46
|
-
} catch {
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (error instanceof InvalidCursorError) {
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: { code: "INVALID_CURSOR", message: error.message },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
47
54
|
return {
|
|
48
55
|
success: false,
|
|
49
56
|
error: {
|