emdash 0.0.0-a → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -1
- package/dist/adapters-BLMa4JGD.d.mts +106 -0
- package/dist/adapters-BLMa4JGD.d.mts.map +1 -0
- package/dist/apply-Bjfq_b4-.mjs +1293 -0
- package/dist/apply-Bjfq_b4-.mjs.map +1 -0
- package/dist/astro/index.d.mts +51 -0
- package/dist/astro/index.d.mts.map +1 -0
- package/dist/astro/index.mjs +1333 -0
- package/dist/astro/index.mjs.map +1 -0
- package/dist/astro/middleware/auth.d.mts +31 -0
- package/dist/astro/middleware/auth.d.mts.map +1 -0
- package/dist/astro/middleware/auth.mjs +654 -0
- package/dist/astro/middleware/auth.mjs.map +1 -0
- package/dist/astro/middleware/redirect.d.mts +22 -0
- package/dist/astro/middleware/redirect.d.mts.map +1 -0
- package/dist/astro/middleware/redirect.mjs +63 -0
- package/dist/astro/middleware/redirect.mjs.map +1 -0
- package/dist/astro/middleware/request-context.d.mts +18 -0
- package/dist/astro/middleware/request-context.d.mts.map +1 -0
- package/dist/astro/middleware/request-context.mjs +1310 -0
- package/dist/astro/middleware/request-context.mjs.map +1 -0
- package/dist/astro/middleware/setup.d.mts +20 -0
- package/dist/astro/middleware/setup.d.mts.map +1 -0
- package/dist/astro/middleware/setup.mjs +47 -0
- package/dist/astro/middleware/setup.mjs.map +1 -0
- package/dist/astro/middleware.d.mts +13 -0
- package/dist/astro/middleware.d.mts.map +1 -0
- package/dist/astro/middleware.mjs +1613 -0
- package/dist/astro/middleware.mjs.map +1 -0
- package/dist/astro/types.d.mts +250 -0
- package/dist/astro/types.d.mts.map +1 -0
- package/dist/astro/types.mjs +1 -0
- package/dist/base64-MBPo9ozB.mjs +59 -0
- package/dist/base64-MBPo9ozB.mjs.map +1 -0
- package/dist/byline-CL847F26.mjs +213 -0
- package/dist/byline-CL847F26.mjs.map +1 -0
- package/dist/bylines-C2a-2TGt.mjs +136 -0
- package/dist/bylines-C2a-2TGt.mjs.map +1 -0
- package/dist/chunk-ClPoSABd.mjs +21 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +3909 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/client/cf-access.d.mts +60 -0
- package/dist/client/cf-access.d.mts.map +1 -0
- package/dist/client/cf-access.mjs +179 -0
- package/dist/client/cf-access.mjs.map +1 -0
- package/dist/client/index.d.mts +398 -0
- package/dist/client/index.d.mts.map +1 -0
- package/dist/client/index.mjs +346 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/config-CKE8p9xM.mjs +55 -0
- package/dist/config-CKE8p9xM.mjs.map +1 -0
- package/dist/connection-B4zVnQIa.mjs +40 -0
- package/dist/connection-B4zVnQIa.mjs.map +1 -0
- package/dist/content-D6C2WsZC.mjs +824 -0
- package/dist/content-D6C2WsZC.mjs.map +1 -0
- package/dist/db/index.d.mts +4 -0
- package/dist/db/index.mjs +62 -0
- package/dist/db/index.mjs.map +1 -0
- package/dist/db/libsql.d.mts +11 -0
- package/dist/db/libsql.d.mts.map +1 -0
- package/dist/db/libsql.mjs +17 -0
- package/dist/db/libsql.mjs.map +1 -0
- package/dist/db/postgres.d.mts +11 -0
- package/dist/db/postgres.d.mts.map +1 -0
- package/dist/db/postgres.mjs +30 -0
- package/dist/db/postgres.mjs.map +1 -0
- package/dist/db/sqlite.d.mts +11 -0
- package/dist/db/sqlite.d.mts.map +1 -0
- package/dist/db/sqlite.mjs +16 -0
- package/dist/db/sqlite.mjs.map +1 -0
- package/dist/default-Cyi4aAxu.mjs +81 -0
- package/dist/default-Cyi4aAxu.mjs.map +1 -0
- package/dist/dialect-helpers-B9uSp2GJ.mjs +90 -0
- package/dist/dialect-helpers-B9uSp2GJ.mjs.map +1 -0
- package/dist/error-Cxz0tQeO.mjs +27 -0
- package/dist/error-Cxz0tQeO.mjs.map +1 -0
- package/dist/index-C1xF3OGh.d.mts +4527 -0
- package/dist/index-C1xF3OGh.d.mts.map +1 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +30 -0
- package/dist/load-yOOlckBj.mjs +28 -0
- package/dist/load-yOOlckBj.mjs.map +1 -0
- package/dist/loader-fz8Q_3EO.mjs +447 -0
- package/dist/loader-fz8Q_3EO.mjs.map +1 -0
- package/dist/manifest-schema-Dcl0R6nM.mjs +184 -0
- package/dist/manifest-schema-Dcl0R6nM.mjs.map +1 -0
- package/dist/media/index.d.mts +26 -0
- package/dist/media/index.d.mts.map +1 -0
- package/dist/media/index.mjs +55 -0
- package/dist/media/index.mjs.map +1 -0
- package/dist/media/local-runtime.d.mts +39 -0
- package/dist/media/local-runtime.d.mts.map +1 -0
- package/dist/media/local-runtime.mjs +133 -0
- package/dist/media/local-runtime.mjs.map +1 -0
- package/dist/media-DqHVh136.mjs +200 -0
- package/dist/media-DqHVh136.mjs.map +1 -0
- package/dist/mode-C2EzN1uE.mjs +23 -0
- package/dist/mode-C2EzN1uE.mjs.map +1 -0
- package/dist/page/index.d.mts +140 -0
- package/dist/page/index.d.mts.map +1 -0
- package/dist/page/index.mjs +416 -0
- package/dist/page/index.mjs.map +1 -0
- package/dist/placeholder-CmGAmqeO.d.mts +276 -0
- package/dist/placeholder-CmGAmqeO.d.mts.map +1 -0
- package/dist/placeholder-SmpOx-_v.mjs +243 -0
- package/dist/placeholder-SmpOx-_v.mjs.map +1 -0
- package/dist/plugin-utils.d.mts +58 -0
- package/dist/plugin-utils.d.mts.map +1 -0
- package/dist/plugin-utils.mjs +78 -0
- package/dist/plugin-utils.mjs.map +1 -0
- package/dist/plugins/adapt-sandbox-entry.d.mts +22 -0
- package/dist/plugins/adapt-sandbox-entry.d.mts.map +1 -0
- package/dist/plugins/adapt-sandbox-entry.mjs +113 -0
- package/dist/plugins/adapt-sandbox-entry.mjs.map +1 -0
- package/dist/query-CS_iSj34.mjs +460 -0
- package/dist/query-CS_iSj34.mjs.map +1 -0
- package/dist/redirect-DIfIni3r.mjs +329 -0
- package/dist/redirect-DIfIni3r.mjs.map +1 -0
- package/dist/registry-D_w5HW4G.mjs +863 -0
- package/dist/registry-D_w5HW4G.mjs.map +1 -0
- package/dist/request-context.d.mts +49 -0
- package/dist/request-context.d.mts.map +1 -0
- package/dist/request-context.mjs +43 -0
- package/dist/request-context.mjs.map +1 -0
- package/dist/runner-B-u2F2b6.mjs +1412 -0
- package/dist/runner-B-u2F2b6.mjs.map +1 -0
- package/dist/runner-EAtf0ZIe.d.mts +27 -0
- package/dist/runner-EAtf0ZIe.d.mts.map +1 -0
- package/dist/runtime.d.mts +26 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +42 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/search-DG603UrT.mjs +9211 -0
- package/dist/search-DG603UrT.mjs.map +1 -0
- package/dist/seed/index.d.mts +3 -0
- package/dist/seed/index.mjs +15 -0
- package/dist/seo/index.d.mts +70 -0
- package/dist/seo/index.d.mts.map +1 -0
- package/dist/seo/index.mjs +70 -0
- package/dist/seo/index.mjs.map +1 -0
- package/dist/storage/local.d.mts +39 -0
- package/dist/storage/local.d.mts.map +1 -0
- package/dist/storage/local.mjs +166 -0
- package/dist/storage/local.mjs.map +1 -0
- package/dist/storage/s3.d.mts +32 -0
- package/dist/storage/s3.d.mts.map +1 -0
- package/dist/storage/s3.mjs +175 -0
- package/dist/storage/s3.mjs.map +1 -0
- package/dist/tokens-DpgrkrXK.mjs +171 -0
- package/dist/tokens-DpgrkrXK.mjs.map +1 -0
- package/dist/transport-BFGblqwG.d.mts +42 -0
- package/dist/transport-BFGblqwG.d.mts.map +1 -0
- package/dist/transport-yxiQsi8I.mjs +418 -0
- package/dist/transport-yxiQsi8I.mjs.map +1 -0
- package/dist/types-BRuPJGdV.d.mts +102 -0
- package/dist/types-BRuPJGdV.d.mts.map +1 -0
- package/dist/types-C4-fAxN3.d.mts +182 -0
- package/dist/types-C4-fAxN3.d.mts.map +1 -0
- package/dist/types-CMMN0pNg.mjs +31 -0
- package/dist/types-CMMN0pNg.mjs.map +1 -0
- package/dist/types-CUBbjgmP.mjs +16 -0
- package/dist/types-CUBbjgmP.mjs.map +1 -0
- package/dist/types-DRjfYOEv.d.mts +426 -0
- package/dist/types-DRjfYOEv.d.mts.map +1 -0
- package/dist/types-DY5zk5HN.mjs +73 -0
- package/dist/types-DY5zk5HN.mjs.map +1 -0
- package/dist/types-DaNLHo_T.d.mts +184 -0
- package/dist/types-DaNLHo_T.d.mts.map +1 -0
- package/dist/types-DvhsUmSJ.d.mts +1111 -0
- package/dist/types-DvhsUmSJ.d.mts.map +1 -0
- package/dist/validate-CpBtVMsD.d.mts +378 -0
- package/dist/validate-CpBtVMsD.d.mts.map +1 -0
- package/dist/validate-CqRJb_xU.mjs +97 -0
- package/dist/validate-CqRJb_xU.mjs.map +1 -0
- package/dist/validate-O7PWmlnq.mjs +328 -0
- package/dist/validate-O7PWmlnq.mjs.map +1 -0
- package/locals.d.ts +46 -0
- package/package.json +233 -19
- package/src/api/authorize.ts +63 -0
- package/src/api/csrf.ts +48 -0
- package/src/api/error.ts +99 -0
- package/src/api/errors.ts +445 -0
- package/src/api/escape.ts +9 -0
- package/src/api/handlers/api-tokens.ts +240 -0
- package/src/api/handlers/comments.ts +314 -0
- package/src/api/handlers/content.ts +1315 -0
- package/src/api/handlers/dashboard.ts +205 -0
- package/src/api/handlers/device-flow.ts +687 -0
- package/src/api/handlers/index.ts +163 -0
- package/src/api/handlers/manifest.ts +158 -0
- package/src/api/handlers/marketplace.ts +930 -0
- package/src/api/handlers/media.ts +207 -0
- package/src/api/handlers/menus.ts +493 -0
- package/src/api/handlers/oauth-authorization.ts +429 -0
- package/src/api/handlers/oauth-clients.ts +353 -0
- package/src/api/handlers/oauth-user-lookup.ts +39 -0
- package/src/api/handlers/plugins.ts +254 -0
- package/src/api/handlers/redirects.ts +360 -0
- package/src/api/handlers/revision.ts +145 -0
- package/src/api/handlers/schema.ts +534 -0
- package/src/api/handlers/sections.ts +289 -0
- package/src/api/handlers/seo.ts +115 -0
- package/src/api/handlers/settings.ts +49 -0
- package/src/api/handlers/snapshot.ts +350 -0
- package/src/api/handlers/taxonomies.ts +523 -0
- package/src/api/index.ts +6 -0
- package/src/api/openapi/document.ts +2368 -0
- package/src/api/openapi/index.ts +1 -0
- package/src/api/parse.ts +139 -0
- package/src/api/redirect.ts +14 -0
- package/src/api/rev.ts +67 -0
- package/src/api/schemas/auth.ts +112 -0
- package/src/api/schemas/bylines.ts +85 -0
- package/src/api/schemas/comments.ts +117 -0
- package/src/api/schemas/common.ts +89 -0
- package/src/api/schemas/content.ts +191 -0
- package/src/api/schemas/import.ts +52 -0
- package/src/api/schemas/index.ts +17 -0
- package/src/api/schemas/media.ts +116 -0
- package/src/api/schemas/menus.ts +111 -0
- package/src/api/schemas/redirects.ts +155 -0
- package/src/api/schemas/schema.ts +203 -0
- package/src/api/schemas/search.ts +63 -0
- package/src/api/schemas/sections.ts +67 -0
- package/src/api/schemas/settings.ts +63 -0
- package/src/api/schemas/setup.ts +37 -0
- package/src/api/schemas/taxonomies.ts +113 -0
- package/src/api/schemas/users.ts +96 -0
- package/src/api/schemas/widgets.ts +80 -0
- package/src/api/site-url.ts +25 -0
- package/src/api/types.ts +82 -0
- package/src/astro/index.ts +27 -0
- package/src/astro/integration/index.ts +303 -0
- package/src/astro/integration/routes.ts +834 -0
- package/src/astro/integration/runtime.ts +338 -0
- package/src/astro/integration/virtual-modules.ts +469 -0
- package/src/astro/integration/vite-config.ts +328 -0
- package/src/astro/middleware/auth.ts +743 -0
- package/src/astro/middleware/redirect.ts +89 -0
- package/src/astro/middleware/request-context.ts +129 -0
- package/src/astro/middleware/setup.ts +89 -0
- package/src/astro/middleware.ts +398 -0
- package/src/astro/routes/PluginRegistry.tsx +15 -0
- package/src/astro/routes/admin.astro +81 -0
- package/src/astro/routes/api/admin/allowed-domains/[domain].ts +112 -0
- package/src/astro/routes/api/admin/allowed-domains/index.ts +108 -0
- package/src/astro/routes/api/admin/api-tokens/[id].ts +40 -0
- package/src/astro/routes/api/admin/api-tokens/index.ts +68 -0
- package/src/astro/routes/api/admin/bylines/[id]/index.ts +87 -0
- package/src/astro/routes/api/admin/bylines/index.ts +72 -0
- package/src/astro/routes/api/admin/comments/[id]/status.ts +120 -0
- package/src/astro/routes/api/admin/comments/[id].ts +64 -0
- package/src/astro/routes/api/admin/comments/bulk.ts +42 -0
- package/src/astro/routes/api/admin/comments/counts.ts +30 -0
- package/src/astro/routes/api/admin/comments/index.ts +46 -0
- package/src/astro/routes/api/admin/hooks/exclusive/[hookName].ts +91 -0
- package/src/astro/routes/api/admin/hooks/exclusive/index.ts +51 -0
- package/src/astro/routes/api/admin/oauth-clients/[id].ts +110 -0
- package/src/astro/routes/api/admin/oauth-clients/index.ts +71 -0
- package/src/astro/routes/api/admin/plugins/[id]/disable.ts +39 -0
- package/src/astro/routes/api/admin/plugins/[id]/enable.ts +39 -0
- package/src/astro/routes/api/admin/plugins/[id]/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/[id]/uninstall.ts +48 -0
- package/src/astro/routes/api/admin/plugins/[id]/update.ts +59 -0
- package/src/astro/routes/api/admin/plugins/index.ts +32 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/icon.ts +61 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/plugins/marketplace/[id]/install.ts +62 -0
- package/src/astro/routes/api/admin/plugins/marketplace/index.ts +38 -0
- package/src/astro/routes/api/admin/plugins/updates.ts +28 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/index.ts +33 -0
- package/src/astro/routes/api/admin/themes/marketplace/[id]/thumbnail.ts +61 -0
- package/src/astro/routes/api/admin/themes/marketplace/index.ts +45 -0
- package/src/astro/routes/api/admin/users/[id]/disable.ts +69 -0
- package/src/astro/routes/api/admin/users/[id]/enable.ts +48 -0
- package/src/astro/routes/api/admin/users/[id]/index.ts +146 -0
- package/src/astro/routes/api/admin/users/[id]/send-recovery.ts +72 -0
- package/src/astro/routes/api/admin/users/index.ts +66 -0
- package/src/astro/routes/api/auth/dev-bypass.ts +139 -0
- package/src/astro/routes/api/auth/invite/accept.ts +52 -0
- package/src/astro/routes/api/auth/invite/complete.ts +84 -0
- package/src/astro/routes/api/auth/invite/index.ts +99 -0
- package/src/astro/routes/api/auth/logout.ts +40 -0
- package/src/astro/routes/api/auth/magic-link/send.ts +89 -0
- package/src/astro/routes/api/auth/magic-link/verify.ts +71 -0
- package/src/astro/routes/api/auth/me.ts +60 -0
- package/src/astro/routes/api/auth/oauth/[provider]/callback.ts +219 -0
- package/src/astro/routes/api/auth/oauth/[provider].ts +119 -0
- package/src/astro/routes/api/auth/passkey/[id].ts +124 -0
- package/src/astro/routes/api/auth/passkey/index.ts +54 -0
- package/src/astro/routes/api/auth/passkey/options.ts +82 -0
- package/src/astro/routes/api/auth/passkey/register/options.ts +86 -0
- package/src/astro/routes/api/auth/passkey/register/verify.ts +117 -0
- package/src/astro/routes/api/auth/passkey/verify.ts +66 -0
- package/src/astro/routes/api/auth/signup/complete.ts +85 -0
- package/src/astro/routes/api/auth/signup/request.ts +77 -0
- package/src/astro/routes/api/auth/signup/verify.ts +53 -0
- package/src/astro/routes/api/comments/[collection]/[contentId]/index.ts +312 -0
- package/src/astro/routes/api/content/[collection]/[id]/compare.ts +28 -0
- package/src/astro/routes/api/content/[collection]/[id]/discard-draft.ts +54 -0
- package/src/astro/routes/api/content/[collection]/[id]/duplicate.ts +61 -0
- package/src/astro/routes/api/content/[collection]/[id]/permanent.ts +33 -0
- package/src/astro/routes/api/content/[collection]/[id]/preview-url.ts +107 -0
- package/src/astro/routes/api/content/[collection]/[id]/publish.ts +56 -0
- package/src/astro/routes/api/content/[collection]/[id]/restore.ts +54 -0
- package/src/astro/routes/api/content/[collection]/[id]/revisions.ts +31 -0
- package/src/astro/routes/api/content/[collection]/[id]/schedule.ts +105 -0
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +140 -0
- package/src/astro/routes/api/content/[collection]/[id]/translations.ts +30 -0
- package/src/astro/routes/api/content/[collection]/[id]/unpublish.ts +56 -0
- package/src/astro/routes/api/content/[collection]/[id].ts +137 -0
- package/src/astro/routes/api/content/[collection]/index.ts +59 -0
- package/src/astro/routes/api/content/[collection]/trash.ts +33 -0
- package/src/astro/routes/api/dashboard.ts +32 -0
- package/src/astro/routes/api/dev/emails.ts +36 -0
- package/src/astro/routes/api/import/probe.ts +47 -0
- package/src/astro/routes/api/import/wordpress/analyze.ts +510 -0
- package/src/astro/routes/api/import/wordpress/execute.ts +283 -0
- package/src/astro/routes/api/import/wordpress/media.ts +338 -0
- package/src/astro/routes/api/import/wordpress/prepare.ts +181 -0
- package/src/astro/routes/api/import/wordpress/rewrite-urls.ts +393 -0
- package/src/astro/routes/api/import/wordpress-plugin/analyze.ts +111 -0
- package/src/astro/routes/api/import/wordpress-plugin/callback.ts +58 -0
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +347 -0
- package/src/astro/routes/api/manifest.ts +62 -0
- package/src/astro/routes/api/mcp.ts +124 -0
- package/src/astro/routes/api/media/[id]/confirm.ts +93 -0
- package/src/astro/routes/api/media/[id].ts +145 -0
- package/src/astro/routes/api/media/file/[key].ts +79 -0
- package/src/astro/routes/api/media/providers/[providerId]/[itemId].ts +86 -0
- package/src/astro/routes/api/media/providers/[providerId]/index.ts +111 -0
- package/src/astro/routes/api/media/providers/index.ts +30 -0
- package/src/astro/routes/api/media/upload-url.ts +137 -0
- package/src/astro/routes/api/media.ts +190 -0
- package/src/astro/routes/api/menus/[name]/items.ts +87 -0
- package/src/astro/routes/api/menus/[name]/reorder.ts +33 -0
- package/src/astro/routes/api/menus/[name].ts +65 -0
- package/src/astro/routes/api/menus/index.ts +47 -0
- package/src/astro/routes/api/oauth/authorize.ts +412 -0
- package/src/astro/routes/api/oauth/device/authorize.ts +45 -0
- package/src/astro/routes/api/oauth/device/code.ts +51 -0
- package/src/astro/routes/api/oauth/device/token.ts +69 -0
- package/src/astro/routes/api/oauth/token/refresh.ts +38 -0
- package/src/astro/routes/api/oauth/token/revoke.ts +38 -0
- package/src/astro/routes/api/oauth/token.ts +184 -0
- package/src/astro/routes/api/openapi.json.ts +32 -0
- package/src/astro/routes/api/plugins/[pluginId]/[...path].ts +92 -0
- package/src/astro/routes/api/redirects/404s/index.ts +72 -0
- package/src/astro/routes/api/redirects/404s/summary.ts +33 -0
- package/src/astro/routes/api/redirects/[id].ts +84 -0
- package/src/astro/routes/api/redirects/index.ts +52 -0
- package/src/astro/routes/api/revisions/[revisionId]/index.ts +29 -0
- package/src/astro/routes/api/revisions/[revisionId]/restore.ts +62 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +76 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +52 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +32 -0
- package/src/astro/routes/api/schema/collections/[slug]/index.ts +80 -0
- package/src/astro/routes/api/schema/collections/index.ts +47 -0
- package/src/astro/routes/api/schema/index.ts +109 -0
- package/src/astro/routes/api/schema/orphans/[slug].ts +36 -0
- package/src/astro/routes/api/schema/orphans/index.ts +26 -0
- package/src/astro/routes/api/search/enable.ts +64 -0
- package/src/astro/routes/api/search/index.ts +55 -0
- package/src/astro/routes/api/search/rebuild.ts +72 -0
- package/src/astro/routes/api/search/stats.ts +35 -0
- package/src/astro/routes/api/search/suggest.ts +53 -0
- package/src/astro/routes/api/sections/[slug].ts +84 -0
- package/src/astro/routes/api/sections/index.ts +52 -0
- package/src/astro/routes/api/settings/email.ts +150 -0
- package/src/astro/routes/api/settings.ts +67 -0
- package/src/astro/routes/api/setup/admin-verify.ts +100 -0
- package/src/astro/routes/api/setup/admin.ts +94 -0
- package/src/astro/routes/api/setup/dev-bypass.ts +199 -0
- package/src/astro/routes/api/setup/dev-reset.ts +40 -0
- package/src/astro/routes/api/setup/index.ts +126 -0
- package/src/astro/routes/api/setup/status.ts +122 -0
- package/src/astro/routes/api/snapshot.ts +75 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/[slug].ts +95 -0
- package/src/astro/routes/api/taxonomies/[name]/terms/index.ts +69 -0
- package/src/astro/routes/api/taxonomies/index.ts +59 -0
- package/src/astro/routes/api/themes/preview.ts +77 -0
- package/src/astro/routes/api/typegen.ts +114 -0
- package/src/astro/routes/api/well-known/auth.ts +68 -0
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +44 -0
- package/src/astro/routes/api/well-known/oauth-protected-resource.ts +37 -0
- package/src/astro/routes/api/widget-areas/[name]/reorder.ts +72 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets/[id].ts +127 -0
- package/src/astro/routes/api/widget-areas/[name]/widgets.ts +80 -0
- package/src/astro/routes/api/widget-areas/[name].ts +87 -0
- package/src/astro/routes/api/widget-areas/index.ts +99 -0
- package/src/astro/routes/api/widget-components.ts +22 -0
- package/src/astro/routes/robots.txt.ts +77 -0
- package/src/astro/routes/sitemap.xml.ts +97 -0
- package/src/astro/storage/adapters.ts +74 -0
- package/src/astro/storage/index.ts +19 -0
- package/src/astro/storage/types.ts +60 -0
- package/src/astro/types.ts +346 -0
- package/src/auth/api-tokens.ts +25 -0
- package/src/auth/challenge-store.ts +80 -0
- package/src/auth/mode.ts +96 -0
- package/src/auth/oauth-state-store.ts +96 -0
- package/src/auth/passkey-config.ts +27 -0
- package/src/auth/rate-limit.ts +158 -0
- package/src/auth/scopes.ts +33 -0
- package/src/auth/types.ts +104 -0
- package/src/aws-sdk.d.ts +100 -0
- package/src/bylines/index.ts +237 -0
- package/src/cleanup.ts +153 -0
- package/src/cli/client-factory.ts +100 -0
- package/src/cli/commands/auth.ts +46 -0
- package/src/cli/commands/bundle-utils.ts +247 -0
- package/src/cli/commands/bundle.ts +609 -0
- package/src/cli/commands/content.ts +442 -0
- package/src/cli/commands/dev.ts +191 -0
- package/src/cli/commands/doctor.ts +211 -0
- package/src/cli/commands/export-seed.ts +630 -0
- package/src/cli/commands/import/wordpress.ts +1056 -0
- package/src/cli/commands/init.ts +192 -0
- package/src/cli/commands/login.ts +547 -0
- package/src/cli/commands/media.ts +165 -0
- package/src/cli/commands/menu.ts +67 -0
- package/src/cli/commands/plugin-init.ts +291 -0
- package/src/cli/commands/plugin-validate.ts +31 -0
- package/src/cli/commands/plugin.ts +33 -0
- package/src/cli/commands/publish.ts +699 -0
- package/src/cli/commands/schema.ts +233 -0
- package/src/cli/commands/search-cmd.ts +54 -0
- package/src/cli/commands/seed.ts +288 -0
- package/src/cli/commands/taxonomy.ts +128 -0
- package/src/cli/commands/types.ts +68 -0
- package/src/cli/credentials.ts +236 -0
- package/src/cli/index.ts +70 -0
- package/src/cli/output.ts +75 -0
- package/src/cli/wxr/parser.ts +969 -0
- package/src/client/cf-access.ts +193 -0
- package/src/client/index.ts +854 -0
- package/src/client/portable-text.ts +413 -0
- package/src/client/transport.ts +200 -0
- package/src/comments/moderator.ts +46 -0
- package/src/comments/notifications.ts +144 -0
- package/src/comments/query.ts +105 -0
- package/src/comments/service.ts +213 -0
- package/src/components/Break.astro +45 -0
- package/src/components/Button.astro +71 -0
- package/src/components/Buttons.astro +49 -0
- package/src/components/Code.astro +59 -0
- package/src/components/Columns.astro +59 -0
- package/src/components/CommentForm.astro +315 -0
- package/src/components/Comments.astro +232 -0
- package/src/components/Cover.astro +128 -0
- package/src/components/EmDashBodyEnd.astro +32 -0
- package/src/components/EmDashBodyStart.astro +32 -0
- package/src/components/EmDashHead.astro +53 -0
- package/src/components/EmDashImage.astro +178 -0
- package/src/components/EmDashMedia.astro +167 -0
- package/src/components/Embed.astro +128 -0
- package/src/components/File.astro +122 -0
- package/src/components/Gallery.astro +93 -0
- package/src/components/HtmlBlock.astro +33 -0
- package/src/components/Image.astro +178 -0
- package/src/components/InlineEditor.astro +27 -0
- package/src/components/InlinePortableTextEditor.tsx +1905 -0
- package/src/components/LiveSearch.astro +614 -0
- package/src/components/PortableText.astro +51 -0
- package/src/components/Pullquote.astro +51 -0
- package/src/components/Table.astro +108 -0
- package/src/components/WidgetArea.astro +22 -0
- package/src/components/WidgetRenderer.astro +72 -0
- package/src/components/index.ts +116 -0
- package/src/components/marks/Link.astro +31 -0
- package/src/components/marks/StrikeThrough.astro +7 -0
- package/src/components/marks/Subscript.astro +7 -0
- package/src/components/marks/Superscript.astro +7 -0
- package/src/components/marks/Underline.astro +7 -0
- package/src/components/widgets/Archives.astro +65 -0
- package/src/components/widgets/Categories.astro +35 -0
- package/src/components/widgets/RecentPosts.astro +51 -0
- package/src/components/widgets/Search.astro +18 -0
- package/src/components/widgets/Tags.astro +38 -0
- package/src/content/converters/index.ts +9 -0
- package/src/content/converters/portable-text-to-prosemirror.ts +385 -0
- package/src/content/converters/prosemirror-to-portable-text.ts +413 -0
- package/src/content/converters/types.ts +120 -0
- package/src/content/index.ts +5 -0
- package/src/database/connection.ts +67 -0
- package/src/database/dialect-helpers.ts +138 -0
- package/src/database/index.ts +5 -0
- package/src/database/migrations/001_initial.ts +136 -0
- package/src/database/migrations/002_media_status.ts +26 -0
- package/src/database/migrations/003_schema_registry.ts +79 -0
- package/src/database/migrations/004_plugins.ts +62 -0
- package/src/database/migrations/005_menus.ts +67 -0
- package/src/database/migrations/006_taxonomy_defs.ts +51 -0
- package/src/database/migrations/007_widgets.ts +42 -0
- package/src/database/migrations/008_auth.ts +194 -0
- package/src/database/migrations/009_user_disabled.ts +27 -0
- package/src/database/migrations/011_sections.ts +65 -0
- package/src/database/migrations/012_search.ts +25 -0
- package/src/database/migrations/013_scheduled_publishing.ts +51 -0
- package/src/database/migrations/014_draft_revisions.ts +72 -0
- package/src/database/migrations/015_indexes.ts +82 -0
- package/src/database/migrations/016_api_tokens.ts +89 -0
- package/src/database/migrations/017_authorization_codes.ts +45 -0
- package/src/database/migrations/018_seo.ts +56 -0
- package/src/database/migrations/019_i18n.ts +618 -0
- package/src/database/migrations/020_collection_url_pattern.ts +23 -0
- package/src/database/migrations/021_remove_section_categories.ts +43 -0
- package/src/database/migrations/022_marketplace_plugin_state.ts +46 -0
- package/src/database/migrations/023_plugin_metadata.ts +33 -0
- package/src/database/migrations/024_media_placeholders.ts +32 -0
- package/src/database/migrations/025_oauth_clients.ts +28 -0
- package/src/database/migrations/026_cron_tasks.ts +49 -0
- package/src/database/migrations/027_comments.ts +87 -0
- package/src/database/migrations/028_drop_author_url.ts +9 -0
- package/src/database/migrations/029_redirects.ts +67 -0
- package/src/database/migrations/030_widen_scheduled_index.ts +48 -0
- package/src/database/migrations/031_bylines.ts +90 -0
- package/src/database/migrations/032_rate_limits.ts +42 -0
- package/src/database/migrations/runner.ts +170 -0
- package/src/database/repositories/audit.ts +294 -0
- package/src/database/repositories/byline.ts +387 -0
- package/src/database/repositories/comment.ts +458 -0
- package/src/database/repositories/content.ts +1144 -0
- package/src/database/repositories/index.ts +30 -0
- package/src/database/repositories/media.ts +347 -0
- package/src/database/repositories/options.ts +150 -0
- package/src/database/repositories/plugin-storage.ts +373 -0
- package/src/database/repositories/redirect.ts +480 -0
- package/src/database/repositories/revision.ts +200 -0
- package/src/database/repositories/seo.ts +176 -0
- package/src/database/repositories/taxonomy.ts +294 -0
- package/src/database/repositories/types.ts +132 -0
- package/src/database/repositories/user.ts +258 -0
- package/src/database/transaction.ts +54 -0
- package/src/database/types.ts +501 -0
- package/src/database/validate.ts +138 -0
- package/src/db/adapters.ts +125 -0
- package/src/db/index.ts +37 -0
- package/src/db/libsql.ts +23 -0
- package/src/db/postgres.ts +30 -0
- package/src/db/sqlite.ts +27 -0
- package/src/emdash-runtime.ts +2096 -0
- package/src/fields/boolean.ts +34 -0
- package/src/fields/datetime.ts +44 -0
- package/src/fields/file.ts +41 -0
- package/src/fields/image.ts +34 -0
- package/src/fields/index.ts +42 -0
- package/src/fields/integer.ts +50 -0
- package/src/fields/json.ts +37 -0
- package/src/fields/multiselect.ts +48 -0
- package/src/fields/number.ts +52 -0
- package/src/fields/portable-text.ts +33 -0
- package/src/fields/reference.ts +29 -0
- package/src/fields/richtext.ts +31 -0
- package/src/fields/select.ts +46 -0
- package/src/fields/slug.ts +38 -0
- package/src/fields/text.ts +55 -0
- package/src/fields/textarea.ts +52 -0
- package/src/fields/types.ts +64 -0
- package/src/i18n/config.ts +68 -0
- package/src/import/index.ts +90 -0
- package/src/import/menus.ts +436 -0
- package/src/import/registry.ts +111 -0
- package/src/import/sections.ts +103 -0
- package/src/import/settings.ts +281 -0
- package/src/import/sources/wordpress-plugin.ts +641 -0
- package/src/import/sources/wordpress-rest.ts +191 -0
- package/src/import/sources/wxr.ts +330 -0
- package/src/import/ssrf.ts +260 -0
- package/src/import/types.ts +418 -0
- package/src/import/utils.ts +412 -0
- package/src/index.ts +481 -0
- package/src/loader.ts +770 -0
- package/src/mcp/server.ts +1463 -0
- package/src/media/index.ts +32 -0
- package/src/media/local-runtime.ts +213 -0
- package/src/media/local.ts +46 -0
- package/src/media/normalize.ts +190 -0
- package/src/media/placeholder.ts +150 -0
- package/src/media/provider-loader.ts +78 -0
- package/src/media/types.ts +279 -0
- package/src/menus/index.ts +324 -0
- package/src/menus/types.ts +112 -0
- package/src/page/context.ts +93 -0
- package/src/page/fragments.ts +89 -0
- package/src/page/index.ts +58 -0
- package/src/page/jsonld.ts +94 -0
- package/src/page/metadata.ts +185 -0
- package/src/page/seo-contributions.ts +136 -0
- package/src/plugin-utils.ts +80 -0
- package/src/plugins/adapt-sandbox-entry.ts +207 -0
- package/src/plugins/context.ts +833 -0
- package/src/plugins/cron.ts +361 -0
- package/src/plugins/define-plugin.ts +259 -0
- package/src/plugins/email-console.ts +73 -0
- package/src/plugins/email.ts +209 -0
- package/src/plugins/hooks.ts +1273 -0
- package/src/plugins/index.ts +193 -0
- package/src/plugins/manager.ts +595 -0
- package/src/plugins/manifest-schema.ts +230 -0
- package/src/plugins/marketplace.ts +460 -0
- package/src/plugins/request-meta.ts +139 -0
- package/src/plugins/routes.ts +302 -0
- package/src/plugins/sandbox/index.ts +18 -0
- package/src/plugins/sandbox/noop.ts +76 -0
- package/src/plugins/sandbox/types.ts +173 -0
- package/src/plugins/scheduler/node.ts +122 -0
- package/src/plugins/scheduler/piggyback.ts +71 -0
- package/src/plugins/scheduler/types.ts +27 -0
- package/src/plugins/state.ts +208 -0
- package/src/plugins/storage-indexes.ts +326 -0
- package/src/plugins/storage-query.ts +240 -0
- package/src/plugins/types.ts +1284 -0
- package/src/preview/helpers.ts +27 -0
- package/src/preview/index.ts +40 -0
- package/src/preview/tokens.ts +279 -0
- package/src/preview/urls.ts +118 -0
- package/src/query.ts +674 -0
- package/src/redirects/patterns.ts +224 -0
- package/src/request-context.ts +67 -0
- package/src/runtime.ts +21 -0
- package/src/schema/index.ts +29 -0
- package/src/schema/query.ts +44 -0
- package/src/schema/registry.ts +965 -0
- package/src/schema/types.ts +276 -0
- package/src/schema/zod-generator.ts +413 -0
- package/src/search/fts-manager.ts +452 -0
- package/src/search/index.ts +26 -0
- package/src/search/query.ts +396 -0
- package/src/search/text-extraction.ts +162 -0
- package/src/search/types.ts +114 -0
- package/src/sections/index.ts +226 -0
- package/src/sections/types.ts +86 -0
- package/src/seed/apply.ts +1141 -0
- package/src/seed/default.ts +86 -0
- package/src/seed/index.ts +28 -0
- package/src/seed/load.ts +35 -0
- package/src/seed/types.ts +341 -0
- package/src/seed/validate.ts +642 -0
- package/src/seo/index.ts +179 -0
- package/src/settings/index.ts +203 -0
- package/src/settings/types.ts +58 -0
- package/src/storage/index.ts +28 -0
- package/src/storage/local.ts +253 -0
- package/src/storage/s3.ts +271 -0
- package/src/storage/types.ts +204 -0
- package/src/taxonomies/index.ts +309 -0
- package/src/taxonomies/types.ts +61 -0
- package/src/ui.ts +75 -0
- package/src/utils/base64.ts +73 -0
- package/src/utils/hash.ts +36 -0
- package/src/utils/sanitize.ts +20 -0
- package/src/utils/slugify.ts +29 -0
- package/src/utils/url.ts +48 -0
- package/src/virtual-modules.d.ts +111 -0
- package/src/visual-editing/editable.ts +108 -0
- package/src/visual-editing/toolbar.ts +1229 -0
- package/src/widgets/components.ts +105 -0
- package/src/widgets/index.ts +131 -0
- package/src/widgets/types.ts +81 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database-backed rate limiter for unauthenticated endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Uses a `_emdash_rate_limits` table with composite primary key (key, window).
|
|
5
|
+
* Each call to `checkRateLimit` atomically upserts a counter and returns
|
|
6
|
+
* whether the request is within the allowed limit.
|
|
7
|
+
*
|
|
8
|
+
* Key format: `{ip}:{endpoint}` — limits are per-IP, per-endpoint.
|
|
9
|
+
* Window format: ISO timestamp truncated to the window size.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Kysely } from "kysely";
|
|
13
|
+
import { sql } from "kysely";
|
|
14
|
+
|
|
15
|
+
import { apiError } from "../api/error.js";
|
|
16
|
+
import type { Database } from "../database/types.js";
|
|
17
|
+
|
|
18
|
+
/** Loose validation for IPv4 and IPv6 addresses. */
|
|
19
|
+
const IP_PATTERN = /^[\da-fA-F.:]+$/;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Result of a rate limit check.
|
|
23
|
+
*/
|
|
24
|
+
export interface RateLimitResult {
|
|
25
|
+
/** Whether the request is allowed (within limit). */
|
|
26
|
+
allowed: boolean;
|
|
27
|
+
/** Current request count in this window. */
|
|
28
|
+
count: number;
|
|
29
|
+
/** Maximum requests allowed in this window. */
|
|
30
|
+
limit: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check (and increment) the rate limit for a given IP + endpoint.
|
|
35
|
+
*
|
|
36
|
+
* If `ip` is null (no trusted IP available), rate limiting is skipped
|
|
37
|
+
* and the request is allowed. There's no meaningful key to rate limit
|
|
38
|
+
* on when the IP is unknown.
|
|
39
|
+
*
|
|
40
|
+
* Returns whether the request is allowed. The counter is always
|
|
41
|
+
* incremented — even when the limit is exceeded — so that repeated
|
|
42
|
+
* abuse doesn't reset the window.
|
|
43
|
+
*
|
|
44
|
+
* Piggybacks cleanup of expired entries with a 1% probability
|
|
45
|
+
* to prevent unbounded table growth.
|
|
46
|
+
*/
|
|
47
|
+
export async function checkRateLimit(
|
|
48
|
+
db: Kysely<Database>,
|
|
49
|
+
ip: string | null,
|
|
50
|
+
endpoint: string,
|
|
51
|
+
maxRequests: number,
|
|
52
|
+
windowSeconds: number,
|
|
53
|
+
): Promise<RateLimitResult> {
|
|
54
|
+
// No trusted IP — skip rate limiting entirely
|
|
55
|
+
if (!ip) {
|
|
56
|
+
return { allowed: true, count: 0, limit: maxRequests };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const windowStart = new Date(
|
|
60
|
+
Math.floor(Date.now() / (windowSeconds * 1000)) * windowSeconds * 1000,
|
|
61
|
+
).toISOString();
|
|
62
|
+
const key = `${ip}:${endpoint}`;
|
|
63
|
+
|
|
64
|
+
// Atomic upsert: insert or increment, return current count
|
|
65
|
+
const result = await sql<{ count: number }>`
|
|
66
|
+
INSERT INTO _emdash_rate_limits (key, window, count)
|
|
67
|
+
VALUES (${key}, ${windowStart}, 1)
|
|
68
|
+
ON CONFLICT (key, window)
|
|
69
|
+
DO UPDATE SET count = _emdash_rate_limits.count + 1
|
|
70
|
+
RETURNING count
|
|
71
|
+
`.execute(db);
|
|
72
|
+
|
|
73
|
+
const count = result.rows[0]?.count ?? 1;
|
|
74
|
+
|
|
75
|
+
// Piggyback cleanup: 1% chance per request to clean expired entries
|
|
76
|
+
if (Math.random() < 0.01) {
|
|
77
|
+
cleanupExpiredRateLimits(db).catch(() => {
|
|
78
|
+
// Swallow errors — cleanup is best-effort
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
allowed: count <= maxRequests,
|
|
84
|
+
count,
|
|
85
|
+
limit: maxRequests,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build a 429 Too Many Requests response with standard headers.
|
|
91
|
+
*/
|
|
92
|
+
export function rateLimitResponse(retryAfterSeconds: number): Response {
|
|
93
|
+
const response = apiError("RATE_LIMITED", "Too many requests. Please try again later.", 429);
|
|
94
|
+
response.headers.set("Retry-After", String(retryAfterSeconds));
|
|
95
|
+
return response;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Extract client IP from a Request.
|
|
100
|
+
*
|
|
101
|
+
* Resolution order:
|
|
102
|
+
* 1. `CF-Connecting-IP` — trusted only when the Cloudflare `cf` object is
|
|
103
|
+
* present (proving the request traversed Cloudflare's edge, which
|
|
104
|
+
* strips/overwrites client-supplied values).
|
|
105
|
+
* 2. `X-Forwarded-For` (first entry) — also trusted only on Cloudflare.
|
|
106
|
+
* Without a trusted reverse proxy the header is trivially spoofable,
|
|
107
|
+
* so we don't use it for standalone deployments.
|
|
108
|
+
* 3. `null` — no trusted IP available. Callers must handle this gracefully
|
|
109
|
+
* (e.g. skip rate limiting).
|
|
110
|
+
*
|
|
111
|
+
* Aligned with `extractRequestMeta` in `plugins/request-meta.ts`.
|
|
112
|
+
*/
|
|
113
|
+
export function getClientIp(request: Request): string | null {
|
|
114
|
+
const headers = request.headers;
|
|
115
|
+
// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- CF Workers runtime shape
|
|
116
|
+
const cf = (request as unknown as { cf?: Record<string, unknown> }).cf;
|
|
117
|
+
|
|
118
|
+
if (!cf) {
|
|
119
|
+
// Not on Cloudflare — no trusted source of client IP
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Trust CF-Connecting-IP when the cf object confirms Cloudflare
|
|
124
|
+
const cfIp = headers.get("cf-connecting-ip")?.trim();
|
|
125
|
+
if (cfIp && IP_PATTERN.test(cfIp)) {
|
|
126
|
+
return cfIp;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Fallback to XFF on Cloudflare (CF sets this reliably)
|
|
130
|
+
const xff = headers.get("x-forwarded-for");
|
|
131
|
+
if (xff) {
|
|
132
|
+
const first = xff.split(",")[0]?.trim();
|
|
133
|
+
if (first && IP_PATTERN.test(first)) {
|
|
134
|
+
return first;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Delete expired rate limit entries.
|
|
143
|
+
*
|
|
144
|
+
* Entries with a window timestamp older than `maxAgeSeconds` are removed.
|
|
145
|
+
* Safe to call periodically (e.g., from cron cleanup or on-request piggyback).
|
|
146
|
+
*/
|
|
147
|
+
export async function cleanupExpiredRateLimits(
|
|
148
|
+
db: Kysely<Database>,
|
|
149
|
+
maxAgeSeconds = 3600,
|
|
150
|
+
): Promise<number> {
|
|
151
|
+
const cutoff = new Date(Date.now() - maxAgeSeconds * 1000).toISOString();
|
|
152
|
+
|
|
153
|
+
const result = await sql`
|
|
154
|
+
DELETE FROM _emdash_rate_limits WHERE window < ${cutoff}
|
|
155
|
+
`.execute(db);
|
|
156
|
+
|
|
157
|
+
return Number(result.numAffectedRows ?? 0);
|
|
158
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope enforcement for API token authentication.
|
|
3
|
+
*
|
|
4
|
+
* Routes call `requireScope(locals, "content:write")` alongside role checks.
|
|
5
|
+
* Session-authenticated requests have no scope restrictions (implicit full access).
|
|
6
|
+
* Token-authenticated requests must have the required scope (or "admin").
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { hasScope } from "./api-tokens.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if the request has a required scope.
|
|
13
|
+
* Returns a 403 Response if the scope is missing, or null if OK.
|
|
14
|
+
*
|
|
15
|
+
* For session-authenticated users (no tokenScopes), always returns null
|
|
16
|
+
* since session auth has implicit full scope.
|
|
17
|
+
*/
|
|
18
|
+
export function requireScope(locals: { tokenScopes?: string[] }, scope: string): Response | null {
|
|
19
|
+
// Session auth = no scope restrictions
|
|
20
|
+
if (!locals.tokenScopes) return null;
|
|
21
|
+
|
|
22
|
+
if (hasScope(locals.tokenScopes, scope)) return null;
|
|
23
|
+
|
|
24
|
+
return new Response(
|
|
25
|
+
JSON.stringify({
|
|
26
|
+
error: {
|
|
27
|
+
code: "INSUFFICIENT_SCOPE",
|
|
28
|
+
message: `Token lacks required scope: ${scope}`,
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
{ status: 403, headers: { "Content-Type": "application/json" } },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Provider Types
|
|
3
|
+
*
|
|
4
|
+
* Defines the interfaces for pluggable authentication providers.
|
|
5
|
+
* Providers like Cloudflare Access implement these interfaces.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Result of authenticating a request via an external auth provider
|
|
10
|
+
*/
|
|
11
|
+
export interface AuthResult {
|
|
12
|
+
/** User's email address */
|
|
13
|
+
email: string;
|
|
14
|
+
/** User's display name */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Resolved role level (e.g., 50 for Admin, 30 for Editor) */
|
|
17
|
+
role: number;
|
|
18
|
+
/** Provider-specific subject ID */
|
|
19
|
+
subject?: string;
|
|
20
|
+
/** Additional provider-specific data */
|
|
21
|
+
metadata?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Auth descriptor - returned by auth adapter functions (e.g., access())
|
|
26
|
+
*
|
|
27
|
+
* Similar to DatabaseDescriptor and StorageDescriptor, this allows
|
|
28
|
+
* auth providers to be configured at build time and loaded at runtime.
|
|
29
|
+
*/
|
|
30
|
+
export interface AuthDescriptor {
|
|
31
|
+
/**
|
|
32
|
+
* Auth provider type identifier
|
|
33
|
+
* @example "cloudflare-access", "okta", "auth0"
|
|
34
|
+
*/
|
|
35
|
+
type: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Module specifier to import at runtime
|
|
39
|
+
* The module must export an `authenticate` function.
|
|
40
|
+
* @example "@emdash-cms/cloudflare/auth"
|
|
41
|
+
*/
|
|
42
|
+
entrypoint: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Provider-specific configuration (JSON-serializable)
|
|
46
|
+
*/
|
|
47
|
+
config: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Auth provider module interface
|
|
52
|
+
*
|
|
53
|
+
* Modules specified by AuthDescriptor.entrypoint must export
|
|
54
|
+
* an `authenticate` function matching this signature.
|
|
55
|
+
*/
|
|
56
|
+
export interface AuthProviderModule {
|
|
57
|
+
/**
|
|
58
|
+
* Authenticate a request using the provider
|
|
59
|
+
*
|
|
60
|
+
* @param request - The incoming HTTP request
|
|
61
|
+
* @param config - Provider-specific configuration from AuthDescriptor
|
|
62
|
+
* @returns Authentication result if valid, throws if invalid
|
|
63
|
+
*/
|
|
64
|
+
authenticate(request: Request, config: unknown): Promise<AuthResult>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Configuration options common to external auth providers
|
|
69
|
+
*/
|
|
70
|
+
export interface ExternalAuthConfig {
|
|
71
|
+
/**
|
|
72
|
+
* Automatically create EmDash users on first login
|
|
73
|
+
* @default true
|
|
74
|
+
*/
|
|
75
|
+
autoProvision?: boolean;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Role level for users not matching any group in roleMapping
|
|
79
|
+
* @default 30 (Editor)
|
|
80
|
+
*/
|
|
81
|
+
defaultRole?: number;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Update user's role on each login based on current IdP groups
|
|
85
|
+
* When false, role is only set on first provisioning
|
|
86
|
+
* @default false
|
|
87
|
+
*/
|
|
88
|
+
syncRoles?: boolean;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Map IdP group names to EmDash role levels
|
|
92
|
+
* First match wins if user is in multiple groups
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* roleMapping: {
|
|
97
|
+
* "Admins": 50, // Admin
|
|
98
|
+
* "Developers": 40, // Developer
|
|
99
|
+
* "Content Team": 30, // Editor
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
roleMapping?: Record<string, number>;
|
|
104
|
+
}
|
package/src/aws-sdk.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for optional AWS SDK dependencies
|
|
3
|
+
*
|
|
4
|
+
* The AWS SDK is only required when using S3-compatible storage.
|
|
5
|
+
* This file provides minimal type declarations to satisfy TypeScript
|
|
6
|
+
* without requiring the SDK to be installed.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare module "@aws-sdk/client-s3" {
|
|
10
|
+
export interface S3ClientConfig {
|
|
11
|
+
endpoint: string;
|
|
12
|
+
region: string;
|
|
13
|
+
credentials: {
|
|
14
|
+
accessKeyId: string;
|
|
15
|
+
secretAccessKey: string;
|
|
16
|
+
};
|
|
17
|
+
forcePathStyle?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SdkStreamMixin {
|
|
21
|
+
transformToWebStream(): ReadableStream;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface GetObjectResponse {
|
|
25
|
+
Body?: SdkStreamMixin;
|
|
26
|
+
ContentType?: string;
|
|
27
|
+
ContentLength?: number;
|
|
28
|
+
ETag?: string;
|
|
29
|
+
LastModified?: Date;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface HeadObjectResponse {
|
|
33
|
+
ContentType?: string;
|
|
34
|
+
ContentLength?: number;
|
|
35
|
+
ETag?: string;
|
|
36
|
+
LastModified?: Date;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ListObjectsV2Response {
|
|
40
|
+
Contents?: Array<{
|
|
41
|
+
Key?: string;
|
|
42
|
+
Size?: number;
|
|
43
|
+
LastModified?: Date;
|
|
44
|
+
ETag?: string;
|
|
45
|
+
}>;
|
|
46
|
+
NextContinuationToken?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class S3Client {
|
|
50
|
+
constructor(config: S3ClientConfig);
|
|
51
|
+
send(command: GetObjectCommand): Promise<GetObjectResponse>;
|
|
52
|
+
send(command: HeadObjectCommand): Promise<HeadObjectResponse>;
|
|
53
|
+
send(command: ListObjectsV2Command): Promise<ListObjectsV2Response>;
|
|
54
|
+
send(command: PutObjectCommand): Promise<void>;
|
|
55
|
+
send(command: DeleteObjectCommand): Promise<void>;
|
|
56
|
+
// Generic fallback
|
|
57
|
+
send(command: unknown): Promise<unknown>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class PutObjectCommand {
|
|
61
|
+
constructor(input: {
|
|
62
|
+
Bucket: string;
|
|
63
|
+
Key: string;
|
|
64
|
+
Body?: unknown;
|
|
65
|
+
ContentType?: string;
|
|
66
|
+
ContentLength?: number;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class GetObjectCommand {
|
|
71
|
+
constructor(input: { Bucket: string; Key: string });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class DeleteObjectCommand {
|
|
75
|
+
constructor(input: { Bucket: string; Key: string });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class HeadObjectCommand {
|
|
79
|
+
constructor(input: { Bucket: string; Key: string });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export class ListObjectsV2Command {
|
|
83
|
+
constructor(input: {
|
|
84
|
+
Bucket: string;
|
|
85
|
+
Prefix?: string;
|
|
86
|
+
MaxKeys?: number;
|
|
87
|
+
ContinuationToken?: string;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
declare module "@aws-sdk/s3-request-presigner" {
|
|
93
|
+
import type { S3Client } from "@aws-sdk/client-s3";
|
|
94
|
+
|
|
95
|
+
export function getSignedUrl(
|
|
96
|
+
client: S3Client,
|
|
97
|
+
command: unknown,
|
|
98
|
+
options: { expiresIn: number },
|
|
99
|
+
): Promise<string>;
|
|
100
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime API for bylines
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to query byline profiles and byline credits
|
|
5
|
+
* associated with content entries. Follows the same pattern as
|
|
6
|
+
* the taxonomies runtime API.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { sql } from "kysely";
|
|
10
|
+
|
|
11
|
+
import { BylineRepository } from "../database/repositories/byline.js";
|
|
12
|
+
import type { BylineSummary, ContentBylineCredit } from "../database/repositories/types.js";
|
|
13
|
+
import { validateIdentifier } from "../database/validate.js";
|
|
14
|
+
import { getDb } from "../loader.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get a byline by ID.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { getByline } from "emdash";
|
|
22
|
+
*
|
|
23
|
+
* const byline = await getByline("01HXYZ...");
|
|
24
|
+
* if (byline) {
|
|
25
|
+
* console.log(byline.displayName);
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export async function getByline(id: string): Promise<BylineSummary | null> {
|
|
30
|
+
const db = await getDb();
|
|
31
|
+
const repo = new BylineRepository(db);
|
|
32
|
+
return repo.findById(id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get a byline by slug.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { getBylineBySlug } from "emdash";
|
|
41
|
+
*
|
|
42
|
+
* const byline = await getBylineBySlug("jane-doe");
|
|
43
|
+
* if (byline) {
|
|
44
|
+
* console.log(byline.displayName); // "Jane Doe"
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export async function getBylineBySlug(slug: string): Promise<BylineSummary | null> {
|
|
49
|
+
const db = await getDb();
|
|
50
|
+
const repo = new BylineRepository(db);
|
|
51
|
+
return repo.findBySlug(slug);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get byline credits for a single content entry.
|
|
56
|
+
*
|
|
57
|
+
* Returns explicit byline credits from the junction table. If none exist
|
|
58
|
+
* but the entry has an `authorId`, falls back to the user-linked byline
|
|
59
|
+
* (marked as source: "inferred").
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { getEntryBylines } from "emdash";
|
|
64
|
+
*
|
|
65
|
+
* const bylines = await getEntryBylines("posts", post.data.id);
|
|
66
|
+
* for (const credit of bylines) {
|
|
67
|
+
* console.log(credit.byline.displayName, credit.roleLabel);
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export async function getEntryBylines(
|
|
72
|
+
collection: string,
|
|
73
|
+
entryId: string,
|
|
74
|
+
): Promise<ContentBylineCredit[]> {
|
|
75
|
+
validateIdentifier(collection, "collection");
|
|
76
|
+
const db = await getDb();
|
|
77
|
+
const repo = new BylineRepository(db);
|
|
78
|
+
|
|
79
|
+
const explicit = await repo.getContentBylines(collection, entryId);
|
|
80
|
+
if (explicit.length > 0) {
|
|
81
|
+
return explicit.map((c) => ({ ...c, source: "explicit" as const }));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Fallback: look up user-linked byline from author_id
|
|
85
|
+
const authorId = await getAuthorId(db, collection, entryId);
|
|
86
|
+
if (authorId) {
|
|
87
|
+
const fallback = await repo.findByUserId(authorId);
|
|
88
|
+
if (fallback) {
|
|
89
|
+
return [{ byline: fallback, sortOrder: 0, roleLabel: null, source: "inferred" }];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Batch-fetch byline credits for multiple content entries in a single query.
|
|
98
|
+
*
|
|
99
|
+
* This is more efficient than calling getEntryBylines for each entry
|
|
100
|
+
* when you need bylines for a list of entries (e.g., a blog index page).
|
|
101
|
+
*
|
|
102
|
+
* @param collection - The collection slug (e.g., "posts")
|
|
103
|
+
* @param entryIds - Array of entry IDs
|
|
104
|
+
* @returns Map from entry ID to array of byline credits
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { getBylinesForEntries, getEmDashCollection } from "emdash";
|
|
109
|
+
*
|
|
110
|
+
* const { entries } = await getEmDashCollection("posts");
|
|
111
|
+
* const ids = entries.map(e => e.data.id);
|
|
112
|
+
* const bylinesMap = await getBylinesForEntries("posts", ids);
|
|
113
|
+
*
|
|
114
|
+
* for (const entry of entries) {
|
|
115
|
+
* const bylines = bylinesMap.get(entry.data.id) ?? [];
|
|
116
|
+
* // render bylines
|
|
117
|
+
* }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export async function getBylinesForEntries(
|
|
121
|
+
collection: string,
|
|
122
|
+
entryIds: string[],
|
|
123
|
+
): Promise<Map<string, ContentBylineCredit[]>> {
|
|
124
|
+
validateIdentifier(collection, "collection");
|
|
125
|
+
const result = new Map<string, ContentBylineCredit[]>();
|
|
126
|
+
|
|
127
|
+
// Initialize all entry IDs with empty arrays
|
|
128
|
+
for (const id of entryIds) {
|
|
129
|
+
result.set(id, []);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (entryIds.length === 0) {
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const db = await getDb();
|
|
137
|
+
const repo = new BylineRepository(db);
|
|
138
|
+
|
|
139
|
+
// 1. Batch fetch all explicit byline credits
|
|
140
|
+
const bylinesMap = await repo.getContentBylinesMany(collection, entryIds);
|
|
141
|
+
|
|
142
|
+
// 2. Collect entry IDs that need fallback lookup
|
|
143
|
+
const fallbackEntryIds: string[] = [];
|
|
144
|
+
const needsFallback: Map<string, string> = new Map(); // entryId -> authorId
|
|
145
|
+
|
|
146
|
+
for (const id of entryIds) {
|
|
147
|
+
if (!bylinesMap.has(id)) {
|
|
148
|
+
// Need to check author_id for this entry — but we only have the IDs,
|
|
149
|
+
// so batch-fetch them from the content table
|
|
150
|
+
fallbackEntryIds.push(id);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Batch-fetch author_ids for entries that need fallback
|
|
155
|
+
if (fallbackEntryIds.length > 0) {
|
|
156
|
+
const authorMap = await getAuthorIds(db, collection, fallbackEntryIds);
|
|
157
|
+
for (const [entryId, authorId] of authorMap) {
|
|
158
|
+
needsFallback.set(entryId, authorId);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 3. Batch fetch user-linked bylines for fallback
|
|
163
|
+
const uniqueAuthorIds = [...new Set(needsFallback.values())];
|
|
164
|
+
const authorBylineMap = await repo.findByUserIds(uniqueAuthorIds);
|
|
165
|
+
|
|
166
|
+
// 4. Assign results
|
|
167
|
+
for (const id of entryIds) {
|
|
168
|
+
const explicit = bylinesMap.get(id);
|
|
169
|
+
if (explicit && explicit.length > 0) {
|
|
170
|
+
result.set(
|
|
171
|
+
id,
|
|
172
|
+
explicit.map((c) => ({ ...c, source: "explicit" as const })),
|
|
173
|
+
);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const authorId = needsFallback.get(id);
|
|
178
|
+
if (authorId) {
|
|
179
|
+
const fallback = authorBylineMap.get(authorId);
|
|
180
|
+
if (fallback) {
|
|
181
|
+
result.set(id, [{ byline: fallback, sortOrder: 0, roleLabel: null, source: "inferred" }]);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Already initialized with empty array
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Look up the author_id for a single content entry.
|
|
194
|
+
* Uses raw SQL since we need dynamic table names.
|
|
195
|
+
*/
|
|
196
|
+
async function getAuthorId(
|
|
197
|
+
db: Awaited<ReturnType<typeof getDb>>,
|
|
198
|
+
collection: string,
|
|
199
|
+
entryId: string,
|
|
200
|
+
): Promise<string | null> {
|
|
201
|
+
const tableName = `ec_${collection}`;
|
|
202
|
+
validateIdentifier(tableName, "content table");
|
|
203
|
+
|
|
204
|
+
const result = await sql<{ author_id: string | null }>`
|
|
205
|
+
SELECT author_id FROM ${sql.ref(tableName)}
|
|
206
|
+
WHERE id = ${entryId}
|
|
207
|
+
LIMIT 1
|
|
208
|
+
`.execute(db);
|
|
209
|
+
|
|
210
|
+
return result.rows[0]?.author_id ?? null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Batch-fetch author_ids for multiple content entries.
|
|
215
|
+
* Returns Map<entryId, authorId> (only entries with non-null author_id).
|
|
216
|
+
*/
|
|
217
|
+
async function getAuthorIds(
|
|
218
|
+
db: Awaited<ReturnType<typeof getDb>>,
|
|
219
|
+
collection: string,
|
|
220
|
+
entryIds: string[],
|
|
221
|
+
): Promise<Map<string, string>> {
|
|
222
|
+
const tableName = `ec_${collection}`;
|
|
223
|
+
validateIdentifier(tableName, "content table");
|
|
224
|
+
|
|
225
|
+
const result = await sql<{ id: string; author_id: string | null }>`
|
|
226
|
+
SELECT id, author_id FROM ${sql.ref(tableName)}
|
|
227
|
+
WHERE id IN (${sql.join(entryIds.map((id) => sql`${id}`))})
|
|
228
|
+
`.execute(db);
|
|
229
|
+
|
|
230
|
+
const map = new Map<string, string>();
|
|
231
|
+
for (const row of result.rows) {
|
|
232
|
+
if (row.author_id) {
|
|
233
|
+
map.set(row.id, row.author_id);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return map;
|
|
237
|
+
}
|