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,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema for PluginManifest validation
|
|
3
|
+
*
|
|
4
|
+
* Used to validate manifest.json from plugin bundles at every parse site:
|
|
5
|
+
* - Client-side download (marketplace.ts extractBundle)
|
|
6
|
+
* - R2 load (api/handlers/marketplace.ts loadBundleFromR2)
|
|
7
|
+
* - CLI publish preview (cli/commands/publish.ts readManifestFromTarball)
|
|
8
|
+
* - Marketplace ingest extends this with publishing-specific fields
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
// ── Enum values (must stay in sync with types.ts) ───────────────
|
|
14
|
+
|
|
15
|
+
export const PLUGIN_CAPABILITIES = [
|
|
16
|
+
"network:fetch",
|
|
17
|
+
"network:fetch:any",
|
|
18
|
+
"read:content",
|
|
19
|
+
"write:content",
|
|
20
|
+
"read:media",
|
|
21
|
+
"write:media",
|
|
22
|
+
"read:users",
|
|
23
|
+
"email:send",
|
|
24
|
+
"email:provide",
|
|
25
|
+
"email:intercept",
|
|
26
|
+
"page:inject",
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
/** Must stay in sync with FieldType in schema/types.ts */
|
|
30
|
+
const FIELD_TYPES = [
|
|
31
|
+
"string",
|
|
32
|
+
"text",
|
|
33
|
+
"number",
|
|
34
|
+
"integer",
|
|
35
|
+
"boolean",
|
|
36
|
+
"datetime",
|
|
37
|
+
"select",
|
|
38
|
+
"multiSelect",
|
|
39
|
+
"portableText",
|
|
40
|
+
"image",
|
|
41
|
+
"file",
|
|
42
|
+
"reference",
|
|
43
|
+
"json",
|
|
44
|
+
"slug",
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
47
|
+
export const HOOK_NAMES = [
|
|
48
|
+
"plugin:install",
|
|
49
|
+
"plugin:activate",
|
|
50
|
+
"plugin:deactivate",
|
|
51
|
+
"plugin:uninstall",
|
|
52
|
+
"content:beforeSave",
|
|
53
|
+
"content:afterSave",
|
|
54
|
+
"content:beforeDelete",
|
|
55
|
+
"content:afterDelete",
|
|
56
|
+
"media:beforeUpload",
|
|
57
|
+
"media:afterUpload",
|
|
58
|
+
"cron",
|
|
59
|
+
"email:beforeSend",
|
|
60
|
+
"email:deliver",
|
|
61
|
+
"email:afterSend",
|
|
62
|
+
"comment:beforeCreate",
|
|
63
|
+
"comment:moderate",
|
|
64
|
+
"comment:afterCreate",
|
|
65
|
+
"comment:afterModerate",
|
|
66
|
+
"page:metadata",
|
|
67
|
+
"page:fragments",
|
|
68
|
+
] as const;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Structured hook entry for manifest — name plus optional metadata.
|
|
72
|
+
* During a transition period, both plain strings and objects are accepted.
|
|
73
|
+
*/
|
|
74
|
+
const manifestHookEntrySchema = z.object({
|
|
75
|
+
name: z.enum(HOOK_NAMES),
|
|
76
|
+
exclusive: z.boolean().optional(),
|
|
77
|
+
priority: z.number().int().optional(),
|
|
78
|
+
timeout: z.number().int().positive().optional(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Structured route entry for manifest — name plus optional metadata.
|
|
83
|
+
* Both plain strings and objects are accepted; strings are normalized
|
|
84
|
+
* to `{ name }` objects via `normalizeManifestRoute()`.
|
|
85
|
+
*/
|
|
86
|
+
/** Route names must be safe path segments — alphanumeric, hyphens, underscores, forward slashes */
|
|
87
|
+
const routeNamePattern = /^[a-zA-Z0-9][a-zA-Z0-9_\-/]*$/;
|
|
88
|
+
|
|
89
|
+
const manifestRouteEntrySchema = z.object({
|
|
90
|
+
name: z.string().min(1).regex(routeNamePattern, "Route name must be a safe path segment"),
|
|
91
|
+
public: z.boolean().optional(),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ── Sub-schemas ─────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
/** Index field names must be valid identifiers to prevent SQL injection via JSON path expressions */
|
|
97
|
+
const indexFieldName = z.string().regex(/^[a-zA-Z][a-zA-Z0-9_]*$/);
|
|
98
|
+
|
|
99
|
+
const storageCollectionSchema = z.object({
|
|
100
|
+
indexes: z.array(z.union([indexFieldName, z.array(indexFieldName)])),
|
|
101
|
+
uniqueIndexes: z.array(z.union([indexFieldName, z.array(indexFieldName)])).optional(),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const baseSettingFields = {
|
|
105
|
+
label: z.string(),
|
|
106
|
+
description: z.string().optional(),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const settingFieldSchema = z.discriminatedUnion("type", [
|
|
110
|
+
z.object({
|
|
111
|
+
...baseSettingFields,
|
|
112
|
+
type: z.literal("string"),
|
|
113
|
+
default: z.string().optional(),
|
|
114
|
+
multiline: z.boolean().optional(),
|
|
115
|
+
}),
|
|
116
|
+
z.object({
|
|
117
|
+
...baseSettingFields,
|
|
118
|
+
type: z.literal("number"),
|
|
119
|
+
default: z.number().optional(),
|
|
120
|
+
min: z.number().optional(),
|
|
121
|
+
max: z.number().optional(),
|
|
122
|
+
}),
|
|
123
|
+
z.object({ ...baseSettingFields, type: z.literal("boolean"), default: z.boolean().optional() }),
|
|
124
|
+
z.object({
|
|
125
|
+
...baseSettingFields,
|
|
126
|
+
type: z.literal("select"),
|
|
127
|
+
options: z.array(z.object({ value: z.string(), label: z.string() })),
|
|
128
|
+
default: z.string().optional(),
|
|
129
|
+
}),
|
|
130
|
+
z.object({ ...baseSettingFields, type: z.literal("secret") }),
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
const adminPageSchema = z.object({
|
|
134
|
+
path: z.string(),
|
|
135
|
+
label: z.string(),
|
|
136
|
+
icon: z.string().optional(),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const dashboardWidgetSchema = z.object({
|
|
140
|
+
id: z.string(),
|
|
141
|
+
size: z.enum(["full", "half", "third"]).optional(),
|
|
142
|
+
title: z.string().optional(),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const pluginAdminConfigSchema = z.object({
|
|
146
|
+
entry: z.string().optional(),
|
|
147
|
+
settingsSchema: z.record(z.string(), settingFieldSchema).optional(),
|
|
148
|
+
pages: z.array(adminPageSchema).optional(),
|
|
149
|
+
widgets: z.array(dashboardWidgetSchema).optional(),
|
|
150
|
+
fieldWidgets: z
|
|
151
|
+
.array(
|
|
152
|
+
z.object({
|
|
153
|
+
name: z.string().min(1),
|
|
154
|
+
label: z.string().min(1),
|
|
155
|
+
fieldTypes: z.array(z.enum(FIELD_TYPES)),
|
|
156
|
+
elements: z
|
|
157
|
+
.array(
|
|
158
|
+
z
|
|
159
|
+
.object({
|
|
160
|
+
type: z.string(),
|
|
161
|
+
action_id: z.string(),
|
|
162
|
+
label: z.string().optional(),
|
|
163
|
+
})
|
|
164
|
+
.passthrough(),
|
|
165
|
+
)
|
|
166
|
+
.optional(),
|
|
167
|
+
}),
|
|
168
|
+
)
|
|
169
|
+
.optional(),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// ── Main schema ─────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Zod schema matching the PluginManifest interface from types.ts.
|
|
176
|
+
*
|
|
177
|
+
* Every JSON.parse of a manifest.json should validate through this.
|
|
178
|
+
*/
|
|
179
|
+
export const pluginManifestSchema = z.object({
|
|
180
|
+
id: z.string().min(1),
|
|
181
|
+
version: z.string().min(1),
|
|
182
|
+
capabilities: z.array(z.enum(PLUGIN_CAPABILITIES)),
|
|
183
|
+
allowedHosts: z.array(z.string()),
|
|
184
|
+
storage: z.record(z.string(), storageCollectionSchema),
|
|
185
|
+
/**
|
|
186
|
+
* Hook declarations — accepts both plain name strings (legacy) and
|
|
187
|
+
* structured objects with exclusive/priority/timeout metadata.
|
|
188
|
+
* Plain strings are normalized to `{ name }` objects after parsing.
|
|
189
|
+
*/
|
|
190
|
+
hooks: z.array(z.union([z.enum(HOOK_NAMES), manifestHookEntrySchema])),
|
|
191
|
+
/**
|
|
192
|
+
* Route declarations — accepts both plain name strings and
|
|
193
|
+
* structured objects with public metadata.
|
|
194
|
+
* Plain strings are normalized to `{ name }` objects after parsing.
|
|
195
|
+
*/
|
|
196
|
+
routes: z.array(
|
|
197
|
+
z.union([
|
|
198
|
+
z.string().min(1).regex(routeNamePattern, "Route name must be a safe path segment"),
|
|
199
|
+
manifestRouteEntrySchema,
|
|
200
|
+
]),
|
|
201
|
+
),
|
|
202
|
+
admin: pluginAdminConfigSchema,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
export type ValidatedPluginManifest = z.infer<typeof pluginManifestSchema>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Normalize a manifest hook entry — plain strings become `{ name }` objects.
|
|
209
|
+
*/
|
|
210
|
+
export function normalizeManifestHook(
|
|
211
|
+
entry: string | { name: string; exclusive?: boolean; priority?: number; timeout?: number },
|
|
212
|
+
): { name: string; exclusive?: boolean; priority?: number; timeout?: number } {
|
|
213
|
+
if (typeof entry === "string") {
|
|
214
|
+
return { name: entry };
|
|
215
|
+
}
|
|
216
|
+
return entry;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Normalize a manifest route entry — plain strings become `{ name }` objects.
|
|
221
|
+
*/
|
|
222
|
+
export function normalizeManifestRoute(entry: string | { name: string; public?: boolean }): {
|
|
223
|
+
name: string;
|
|
224
|
+
public?: boolean;
|
|
225
|
+
} {
|
|
226
|
+
if (typeof entry === "string") {
|
|
227
|
+
return { name: entry };
|
|
228
|
+
}
|
|
229
|
+
return entry;
|
|
230
|
+
}
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarketplaceClient — HTTP client for the EmDash Plugin Marketplace
|
|
3
|
+
*
|
|
4
|
+
* Used by the install/update/proxy endpoints in EmDash core to communicate
|
|
5
|
+
* with the marketplace Worker. The marketplace is a distribution channel,
|
|
6
|
+
* not a runtime dependency — bundles are copied to site-local R2 at install time.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createGzipDecoder, unpackTar } from "modern-tar";
|
|
10
|
+
|
|
11
|
+
import { pluginManifestSchema } from "./manifest-schema.js";
|
|
12
|
+
import type { PluginManifest } from "./types.js";
|
|
13
|
+
|
|
14
|
+
// ── Module-level regex patterns ───────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const TRAILING_SLASHES = /\/+$/;
|
|
17
|
+
const LEADING_DOT_SLASH = /^\.\//;
|
|
18
|
+
|
|
19
|
+
// ── Types ──────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
export interface MarketplacePluginSummary {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
description: string | null;
|
|
25
|
+
author: {
|
|
26
|
+
name: string;
|
|
27
|
+
verified: boolean;
|
|
28
|
+
avatarUrl: string | null;
|
|
29
|
+
};
|
|
30
|
+
capabilities: string[];
|
|
31
|
+
keywords: string[];
|
|
32
|
+
installCount: number;
|
|
33
|
+
hasIcon: boolean;
|
|
34
|
+
iconUrl: string;
|
|
35
|
+
latestVersion?: {
|
|
36
|
+
version: string;
|
|
37
|
+
audit?: {
|
|
38
|
+
verdict: string;
|
|
39
|
+
riskScore: number;
|
|
40
|
+
};
|
|
41
|
+
imageAudit?: {
|
|
42
|
+
verdict: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
createdAt: string;
|
|
46
|
+
updatedAt: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface MarketplaceVersionSummary {
|
|
50
|
+
version: string;
|
|
51
|
+
minEmDashVersion: string | null;
|
|
52
|
+
bundleSize: number;
|
|
53
|
+
checksum: string;
|
|
54
|
+
changelog: string | null;
|
|
55
|
+
capabilities: string[];
|
|
56
|
+
status: string;
|
|
57
|
+
auditVerdict: string | null;
|
|
58
|
+
imageAuditVerdict: string | null;
|
|
59
|
+
publishedAt: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface MarketplacePluginDetail extends MarketplacePluginSummary {
|
|
63
|
+
repositoryUrl: string | null;
|
|
64
|
+
homepageUrl: string | null;
|
|
65
|
+
license: string | null;
|
|
66
|
+
latestVersion?: {
|
|
67
|
+
version: string;
|
|
68
|
+
minEmDashVersion: string | null;
|
|
69
|
+
bundleSize: number;
|
|
70
|
+
checksum: string;
|
|
71
|
+
changelog: string | null;
|
|
72
|
+
readme: string | null;
|
|
73
|
+
hasIcon: boolean;
|
|
74
|
+
screenshotCount: number;
|
|
75
|
+
screenshotUrls: string[];
|
|
76
|
+
capabilities: string[];
|
|
77
|
+
status: string;
|
|
78
|
+
audit?: {
|
|
79
|
+
verdict: string;
|
|
80
|
+
riskScore: number;
|
|
81
|
+
};
|
|
82
|
+
imageAudit?: {
|
|
83
|
+
verdict: string;
|
|
84
|
+
};
|
|
85
|
+
publishedAt: string;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface MarketplaceSearchOpts {
|
|
90
|
+
category?: string;
|
|
91
|
+
capability?: string;
|
|
92
|
+
sort?: "installs" | "updated" | "created" | "name";
|
|
93
|
+
cursor?: string;
|
|
94
|
+
limit?: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface MarketplaceSearchResult {
|
|
98
|
+
items: MarketplacePluginSummary[];
|
|
99
|
+
nextCursor?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Theme types ───────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
export interface MarketplaceThemeSummary {
|
|
105
|
+
id: string;
|
|
106
|
+
name: string;
|
|
107
|
+
description: string | null;
|
|
108
|
+
author: {
|
|
109
|
+
name: string;
|
|
110
|
+
verified: boolean;
|
|
111
|
+
avatarUrl: string | null;
|
|
112
|
+
};
|
|
113
|
+
keywords: string[];
|
|
114
|
+
previewUrl: string;
|
|
115
|
+
demoUrl: string | null;
|
|
116
|
+
hasThumbnail: boolean;
|
|
117
|
+
thumbnailUrl: string | null;
|
|
118
|
+
createdAt: string;
|
|
119
|
+
updatedAt: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface MarketplaceThemeDetail extends MarketplaceThemeSummary {
|
|
123
|
+
author: {
|
|
124
|
+
id: string;
|
|
125
|
+
name: string;
|
|
126
|
+
verified: boolean;
|
|
127
|
+
avatarUrl: string | null;
|
|
128
|
+
};
|
|
129
|
+
repositoryUrl: string | null;
|
|
130
|
+
homepageUrl: string | null;
|
|
131
|
+
license: string | null;
|
|
132
|
+
screenshotCount: number;
|
|
133
|
+
screenshotUrls: string[];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface MarketplaceThemeSearchOpts {
|
|
137
|
+
keyword?: string;
|
|
138
|
+
sort?: "name" | "created" | "updated";
|
|
139
|
+
cursor?: string;
|
|
140
|
+
limit?: number;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface MarketplaceThemeSearchResult {
|
|
144
|
+
items: MarketplaceThemeSummary[];
|
|
145
|
+
nextCursor?: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface PluginBundle {
|
|
149
|
+
manifest: PluginManifest;
|
|
150
|
+
backendCode: string;
|
|
151
|
+
adminCode?: string;
|
|
152
|
+
checksum: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ── Interface ──────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
export interface MarketplaceClient {
|
|
158
|
+
/** Search the marketplace catalog */
|
|
159
|
+
search(query?: string, opts?: MarketplaceSearchOpts): Promise<MarketplaceSearchResult>;
|
|
160
|
+
|
|
161
|
+
/** Get full plugin detail */
|
|
162
|
+
getPlugin(id: string): Promise<MarketplacePluginDetail>;
|
|
163
|
+
|
|
164
|
+
/** Get version history for a plugin */
|
|
165
|
+
getVersions(id: string): Promise<MarketplaceVersionSummary[]>;
|
|
166
|
+
|
|
167
|
+
/** Download and extract a plugin bundle */
|
|
168
|
+
downloadBundle(id: string, version: string): Promise<PluginBundle>;
|
|
169
|
+
|
|
170
|
+
/** Fire-and-forget install stat (never throws) */
|
|
171
|
+
reportInstall(id: string, version: string): Promise<void>;
|
|
172
|
+
|
|
173
|
+
/** Search theme listings */
|
|
174
|
+
searchThemes(
|
|
175
|
+
query?: string,
|
|
176
|
+
opts?: MarketplaceThemeSearchOpts,
|
|
177
|
+
): Promise<MarketplaceThemeSearchResult>;
|
|
178
|
+
|
|
179
|
+
/** Get full theme detail */
|
|
180
|
+
getTheme(id: string): Promise<MarketplaceThemeDetail>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ── Errors ─────────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
export class MarketplaceError extends Error {
|
|
186
|
+
constructor(
|
|
187
|
+
message: string,
|
|
188
|
+
public readonly status?: number,
|
|
189
|
+
public readonly code?: string,
|
|
190
|
+
) {
|
|
191
|
+
super(message);
|
|
192
|
+
this.name = "MarketplaceError";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export class MarketplaceUnavailableError extends MarketplaceError {
|
|
197
|
+
constructor(cause?: unknown) {
|
|
198
|
+
super("Plugin marketplace is unavailable", undefined, "MARKETPLACE_UNAVAILABLE");
|
|
199
|
+
if (cause) this.cause = cause;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── Implementation ─────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
class MarketplaceClientImpl implements MarketplaceClient {
|
|
206
|
+
private readonly baseUrl: string;
|
|
207
|
+
|
|
208
|
+
constructor(baseUrl: string) {
|
|
209
|
+
// Strip trailing slash
|
|
210
|
+
this.baseUrl = baseUrl.replace(TRAILING_SLASHES, "");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async search(query?: string, opts?: MarketplaceSearchOpts): Promise<MarketplaceSearchResult> {
|
|
214
|
+
const params = new URLSearchParams();
|
|
215
|
+
if (query) params.set("q", query);
|
|
216
|
+
if (opts?.category) params.set("category", opts.category);
|
|
217
|
+
if (opts?.capability) params.set("capability", opts.capability);
|
|
218
|
+
if (opts?.sort) params.set("sort", opts.sort);
|
|
219
|
+
if (opts?.cursor) params.set("cursor", opts.cursor);
|
|
220
|
+
if (opts?.limit) params.set("limit", String(opts.limit));
|
|
221
|
+
|
|
222
|
+
const qs = params.toString();
|
|
223
|
+
const url = `${this.baseUrl}/api/v1/plugins${qs ? `?${qs}` : ""}`;
|
|
224
|
+
const data = await this.fetchJson<MarketplaceSearchResult>(url);
|
|
225
|
+
return data;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async getPlugin(id: string): Promise<MarketplacePluginDetail> {
|
|
229
|
+
const url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}`;
|
|
230
|
+
return this.fetchJson<MarketplacePluginDetail>(url);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async getVersions(id: string): Promise<MarketplaceVersionSummary[]> {
|
|
234
|
+
const url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/versions`;
|
|
235
|
+
const data = await this.fetchJson<{ items: MarketplaceVersionSummary[] }>(url);
|
|
236
|
+
return data.items;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async downloadBundle(id: string, version: string): Promise<PluginBundle> {
|
|
240
|
+
const bundleUrl = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/versions/${encodeURIComponent(version)}/bundle`;
|
|
241
|
+
|
|
242
|
+
let response: Response;
|
|
243
|
+
try {
|
|
244
|
+
response = await fetch(bundleUrl, {
|
|
245
|
+
redirect: "follow",
|
|
246
|
+
});
|
|
247
|
+
} catch (err) {
|
|
248
|
+
throw new MarketplaceUnavailableError(err);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (!response.ok) {
|
|
252
|
+
throw new MarketplaceError(
|
|
253
|
+
`Failed to download bundle: ${response.status} ${response.statusText}`,
|
|
254
|
+
response.status,
|
|
255
|
+
"BUNDLE_DOWNLOAD_FAILED",
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const tarballBytes = new Uint8Array(await response.arrayBuffer());
|
|
260
|
+
try {
|
|
261
|
+
return await extractBundle(tarballBytes);
|
|
262
|
+
} catch (err) {
|
|
263
|
+
if (err instanceof MarketplaceError) throw err;
|
|
264
|
+
throw new MarketplaceError(
|
|
265
|
+
"Failed to extract plugin bundle",
|
|
266
|
+
undefined,
|
|
267
|
+
"BUNDLE_EXTRACT_FAILED",
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async reportInstall(id: string, version: string): Promise<void> {
|
|
273
|
+
// Generate a stable site hash (best-effort, non-identifying)
|
|
274
|
+
const siteHash = await generateSiteHash();
|
|
275
|
+
const url = `${this.baseUrl}/api/v1/plugins/${encodeURIComponent(id)}/installs`;
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
await fetch(url, {
|
|
279
|
+
method: "POST",
|
|
280
|
+
headers: { "Content-Type": "application/json" },
|
|
281
|
+
body: JSON.stringify({ siteHash, version }),
|
|
282
|
+
});
|
|
283
|
+
} catch {
|
|
284
|
+
// Fire-and-forget — never throw
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async searchThemes(
|
|
289
|
+
query?: string,
|
|
290
|
+
opts?: MarketplaceThemeSearchOpts,
|
|
291
|
+
): Promise<MarketplaceThemeSearchResult> {
|
|
292
|
+
const params = new URLSearchParams();
|
|
293
|
+
if (query) params.set("q", query);
|
|
294
|
+
if (opts?.keyword) params.set("keyword", opts.keyword);
|
|
295
|
+
if (opts?.sort) params.set("sort", opts.sort);
|
|
296
|
+
if (opts?.cursor) params.set("cursor", opts.cursor);
|
|
297
|
+
if (opts?.limit) params.set("limit", String(opts.limit));
|
|
298
|
+
|
|
299
|
+
const qs = params.toString();
|
|
300
|
+
const url = `${this.baseUrl}/api/v1/themes${qs ? `?${qs}` : ""}`;
|
|
301
|
+
return this.fetchJson<MarketplaceThemeSearchResult>(url);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async getTheme(id: string): Promise<MarketplaceThemeDetail> {
|
|
305
|
+
const url = `${this.baseUrl}/api/v1/themes/${encodeURIComponent(id)}`;
|
|
306
|
+
return this.fetchJson<MarketplaceThemeDetail>(url);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private async fetchJson<T>(url: string): Promise<T> {
|
|
310
|
+
let response: Response;
|
|
311
|
+
try {
|
|
312
|
+
response = await fetch(url, {
|
|
313
|
+
headers: { Accept: "application/json" },
|
|
314
|
+
});
|
|
315
|
+
} catch (err) {
|
|
316
|
+
throw new MarketplaceUnavailableError(err);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!response.ok) {
|
|
320
|
+
let errorMessage = `Marketplace request failed: ${response.status}`;
|
|
321
|
+
try {
|
|
322
|
+
const body: { error?: string } = await response.json();
|
|
323
|
+
if (body.error) errorMessage = body.error;
|
|
324
|
+
} catch {
|
|
325
|
+
// use default message
|
|
326
|
+
}
|
|
327
|
+
throw new MarketplaceError(errorMessage, response.status);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const data: T = await response.json();
|
|
331
|
+
return data;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ── Bundle extraction ──────────────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Extract manifest + code files from a tarball.
|
|
339
|
+
*
|
|
340
|
+
* The tarball is a gzipped tar archive containing:
|
|
341
|
+
* - manifest.json
|
|
342
|
+
* - backend.js
|
|
343
|
+
* - admin.js (optional)
|
|
344
|
+
*
|
|
345
|
+
* We use a minimal tar parser since we only need to read a few small files.
|
|
346
|
+
*/
|
|
347
|
+
async function extractBundle(tarballBytes: Uint8Array): Promise<PluginBundle> {
|
|
348
|
+
// Decompress fully into memory first, then parse the tar.
|
|
349
|
+
// Passing a pipeThrough() stream directly to unpackTar causes a backpressure
|
|
350
|
+
// deadlock in workerd: the tar decoder's body-stream pull() needs more
|
|
351
|
+
// decompressed data, but the upstream pipe is stalled waiting for the
|
|
352
|
+
// decoder's writable side to drain — a circular dependency.
|
|
353
|
+
const decompressedStream = new ReadableStream<Uint8Array>({
|
|
354
|
+
start(controller) {
|
|
355
|
+
controller.enqueue(tarballBytes);
|
|
356
|
+
controller.close();
|
|
357
|
+
},
|
|
358
|
+
}).pipeThrough(createGzipDecoder());
|
|
359
|
+
|
|
360
|
+
// Collect decompressed bytes fully before parsing
|
|
361
|
+
const decompressedBuf = await new Response(decompressedStream).arrayBuffer();
|
|
362
|
+
const decompressedBytes = new Uint8Array(decompressedBuf);
|
|
363
|
+
const decompressed = new ReadableStream<Uint8Array>({
|
|
364
|
+
start(controller) {
|
|
365
|
+
controller.enqueue(decompressedBytes);
|
|
366
|
+
controller.close();
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const entries = await unpackTar(decompressed);
|
|
371
|
+
|
|
372
|
+
const decoder = new TextDecoder();
|
|
373
|
+
const files = new Map<string, string>();
|
|
374
|
+
for (const entry of entries) {
|
|
375
|
+
if (entry.data && entry.header.type === "file") {
|
|
376
|
+
// Strip leading ./ prefix that tar tools commonly add
|
|
377
|
+
const name = entry.header.name.replace(LEADING_DOT_SLASH, "");
|
|
378
|
+
files.set(name, decoder.decode(entry.data));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const manifestJson = files.get("manifest.json");
|
|
383
|
+
const backendCode = files.get("backend.js");
|
|
384
|
+
|
|
385
|
+
if (!manifestJson) {
|
|
386
|
+
throw new MarketplaceError(
|
|
387
|
+
"Invalid bundle: missing manifest.json",
|
|
388
|
+
undefined,
|
|
389
|
+
"INVALID_BUNDLE",
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
if (!backendCode) {
|
|
393
|
+
throw new MarketplaceError("Invalid bundle: missing backend.js", undefined, "INVALID_BUNDLE");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
let manifest: PluginManifest;
|
|
397
|
+
try {
|
|
398
|
+
const parsed: unknown = JSON.parse(manifestJson);
|
|
399
|
+
const result = pluginManifestSchema.safeParse(parsed);
|
|
400
|
+
if (!result.success) {
|
|
401
|
+
throw new MarketplaceError(
|
|
402
|
+
"Invalid bundle: manifest.json failed validation",
|
|
403
|
+
undefined,
|
|
404
|
+
"INVALID_BUNDLE",
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
// Elements are validated as unknown[] by Zod; cast to PluginManifest
|
|
408
|
+
// for the Element[] type (Block Kit validation happens at render time).
|
|
409
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Zod types elements as unknown[]; Element type validated at render time
|
|
410
|
+
manifest = result.data as unknown as PluginManifest;
|
|
411
|
+
} catch (err) {
|
|
412
|
+
if (err instanceof MarketplaceError) throw err;
|
|
413
|
+
throw new MarketplaceError(
|
|
414
|
+
"Invalid bundle: malformed manifest.json",
|
|
415
|
+
undefined,
|
|
416
|
+
"INVALID_BUNDLE",
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Compute SHA-256 checksum of the tarball for verification
|
|
421
|
+
// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Uint8Array is a valid BufferSource at runtime; TS lib mismatch
|
|
422
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", tarballBytes as unknown as BufferSource);
|
|
423
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
424
|
+
const checksum = Array.from(hashArray, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
manifest,
|
|
428
|
+
backendCode,
|
|
429
|
+
adminCode: files.get("admin.js"),
|
|
430
|
+
checksum,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ── Helpers ────────────────────────────────────────────────────────
|
|
435
|
+
|
|
436
|
+
/** Generate a stable non-identifying site hash (best-effort) */
|
|
437
|
+
async function generateSiteHash(): Promise<string> {
|
|
438
|
+
// Use a timestamp-based approach since we can't reliably get the origin
|
|
439
|
+
// in all contexts (Workers, Node, etc.)
|
|
440
|
+
const seed = `emdash-${Date.now()}`;
|
|
441
|
+
try {
|
|
442
|
+
const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(seed));
|
|
443
|
+
const arr = new Uint8Array(hash);
|
|
444
|
+
return Array.from(arr.slice(0, 8), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
445
|
+
} catch {
|
|
446
|
+
// Fallback for environments without crypto.subtle
|
|
447
|
+
return Math.random().toString(36).slice(2, 18);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ── Factory ────────────────────────────────────────────────────────
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Create a MarketplaceClient for the given marketplace URL.
|
|
455
|
+
*
|
|
456
|
+
* @param baseUrl - The marketplace API base URL (e.g. "https://marketplace.emdashcms.com")
|
|
457
|
+
*/
|
|
458
|
+
export function createMarketplaceClient(baseUrl: string): MarketplaceClient {
|
|
459
|
+
return new MarketplaceClientImpl(baseUrl);
|
|
460
|
+
}
|