emdash 0.0.0-b → 0.0.2
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 -43
- 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 +1336 -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-C0hCbYnD.mjs +1412 -0
- package/dist/runner-C0hCbYnD.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 +684 -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 +349 -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 +335 -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 +116 -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 +115 -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 +101 -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 +58 -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 +68 -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 +697 -0
- package/src/cli/commands/schema.ts +233 -0
- package/src/cli/commands/search-cmd.ts +54 -0
- package/src/cli/commands/seed.ts +286 -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 +170 -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 +39 -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 +249 -0
- package/src/storage/s3.ts +263 -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,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Filesystem Storage Implementation
|
|
3
|
+
*
|
|
4
|
+
* For development and testing. Stores files in a local directory.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createReadStream, existsSync } from "node:fs";
|
|
8
|
+
import * as fs from "node:fs/promises";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import { Readable } from "node:stream";
|
|
11
|
+
|
|
12
|
+
import mime from "mime/lite";
|
|
13
|
+
|
|
14
|
+
/** Type guard for Node.js ErrnoException */
|
|
15
|
+
function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
|
16
|
+
return error instanceof Error && "code" in error;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
Storage,
|
|
21
|
+
LocalStorageConfig,
|
|
22
|
+
UploadResult,
|
|
23
|
+
DownloadResult,
|
|
24
|
+
ListResult,
|
|
25
|
+
ListOptions,
|
|
26
|
+
SignedUploadUrl,
|
|
27
|
+
SignedUploadOptions,
|
|
28
|
+
} from "./types.js";
|
|
29
|
+
import { EmDashStorageError } from "./types.js";
|
|
30
|
+
|
|
31
|
+
/** Pattern to remove leading slashes */
|
|
32
|
+
const LEADING_SLASH_PATTERN = /^\//;
|
|
33
|
+
|
|
34
|
+
/** Pattern to remove trailing slashes */
|
|
35
|
+
const TRAILING_SLASH_PATTERN = /\/$/;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Local filesystem storage implementation
|
|
39
|
+
*/
|
|
40
|
+
export class LocalStorage implements Storage {
|
|
41
|
+
/** Resolved absolute base directory for all stored files */
|
|
42
|
+
private directory: string;
|
|
43
|
+
private baseUrl: string;
|
|
44
|
+
|
|
45
|
+
constructor(config: LocalStorageConfig) {
|
|
46
|
+
this.directory = path.resolve(config.directory);
|
|
47
|
+
this.baseUrl = config.baseUrl.replace(TRAILING_SLASH_PATTERN, "");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve a storage key to an absolute file path, ensuring it stays
|
|
52
|
+
* within the configured storage directory. Uses path.resolve() for
|
|
53
|
+
* canonical resolution rather than regex stripping.
|
|
54
|
+
*
|
|
55
|
+
* @throws EmDashStorageError if the resolved path escapes the base directory
|
|
56
|
+
*/
|
|
57
|
+
private getFilePath(key: string): string {
|
|
58
|
+
const normalizedKey = key.replace(LEADING_SLASH_PATTERN, "");
|
|
59
|
+
const resolved = path.resolve(this.directory, normalizedKey);
|
|
60
|
+
|
|
61
|
+
// Verify the resolved path is within the base directory
|
|
62
|
+
if (!resolved.startsWith(this.directory + path.sep) && resolved !== this.directory) {
|
|
63
|
+
throw new EmDashStorageError("Invalid file path", "INVALID_PATH");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return resolved;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async upload(options: {
|
|
70
|
+
key: string;
|
|
71
|
+
body: Buffer | Uint8Array | ReadableStream<Uint8Array>;
|
|
72
|
+
contentType: string;
|
|
73
|
+
}): Promise<UploadResult> {
|
|
74
|
+
try {
|
|
75
|
+
const filePath = this.getFilePath(options.key);
|
|
76
|
+
const dir = path.dirname(filePath);
|
|
77
|
+
|
|
78
|
+
// Ensure directory exists
|
|
79
|
+
await fs.mkdir(dir, { recursive: true });
|
|
80
|
+
|
|
81
|
+
// Convert body to buffer
|
|
82
|
+
let buffer: Buffer;
|
|
83
|
+
if (options.body instanceof ReadableStream) {
|
|
84
|
+
const chunks: Uint8Array[] = [];
|
|
85
|
+
const reader = options.body.getReader();
|
|
86
|
+
while (true) {
|
|
87
|
+
const { done, value } = await reader.read();
|
|
88
|
+
if (done) break;
|
|
89
|
+
chunks.push(value);
|
|
90
|
+
}
|
|
91
|
+
buffer = Buffer.concat(chunks);
|
|
92
|
+
} else if (options.body instanceof Uint8Array) {
|
|
93
|
+
buffer = Buffer.from(options.body);
|
|
94
|
+
} else {
|
|
95
|
+
buffer = options.body;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await fs.writeFile(filePath, buffer);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
key: options.key,
|
|
102
|
+
url: this.getPublicUrl(options.key),
|
|
103
|
+
size: buffer.length,
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new EmDashStorageError(`Failed to upload file: ${options.key}`, "UPLOAD_FAILED", error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async download(key: string): Promise<DownloadResult> {
|
|
111
|
+
try {
|
|
112
|
+
const filePath = this.getFilePath(key);
|
|
113
|
+
|
|
114
|
+
if (!existsSync(filePath)) {
|
|
115
|
+
throw new EmDashStorageError(`File not found: ${key}`, "NOT_FOUND");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const stat = await fs.stat(filePath);
|
|
119
|
+
const nodeStream = createReadStream(filePath);
|
|
120
|
+
|
|
121
|
+
// Convert Node.js stream to web ReadableStream
|
|
122
|
+
// Readable.toWeb returns ReadableStream (which is ReadableStream<unknown>),
|
|
123
|
+
// but Node ReadStreams produce Buffer/Uint8Array chunks
|
|
124
|
+
// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- Readable.toWeb returns ReadableStream<unknown>; Node ReadStreams produce Uint8Array chunks
|
|
125
|
+
const webStream: ReadableStream<Uint8Array> = Readable.toWeb(
|
|
126
|
+
nodeStream,
|
|
127
|
+
) as ReadableStream<Uint8Array>;
|
|
128
|
+
|
|
129
|
+
// Infer content type from extension
|
|
130
|
+
const ext = path.extname(key).toLowerCase();
|
|
131
|
+
const contentType = getContentType(ext);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
body: webStream,
|
|
135
|
+
contentType,
|
|
136
|
+
size: stat.size,
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof EmDashStorageError) throw error;
|
|
140
|
+
throw new EmDashStorageError(`Failed to download file: ${key}`, "DOWNLOAD_FAILED", error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async delete(key: string): Promise<void> {
|
|
145
|
+
try {
|
|
146
|
+
const filePath = this.getFilePath(key);
|
|
147
|
+
await fs.unlink(filePath);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
// Ignore "file not found" errors (idempotent delete)
|
|
150
|
+
if (!isNodeError(error) || error.code !== "ENOENT") {
|
|
151
|
+
throw new EmDashStorageError(`Failed to delete file: ${key}`, "DELETE_FAILED", error);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async exists(key: string): Promise<boolean> {
|
|
157
|
+
try {
|
|
158
|
+
const filePath = this.getFilePath(key);
|
|
159
|
+
await fs.access(filePath);
|
|
160
|
+
return true;
|
|
161
|
+
} catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async list(options: ListOptions = {}): Promise<ListResult> {
|
|
167
|
+
try {
|
|
168
|
+
const prefix = options.prefix || "";
|
|
169
|
+
const searchDir = path.resolve(this.directory, path.dirname(prefix));
|
|
170
|
+
|
|
171
|
+
// Validate the search directory stays within the base directory
|
|
172
|
+
if (!searchDir.startsWith(this.directory + path.sep) && searchDir !== this.directory) {
|
|
173
|
+
throw new EmDashStorageError("Invalid list prefix", "INVALID_PATH");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const prefixBase = path.basename(prefix);
|
|
177
|
+
|
|
178
|
+
// Ensure directory exists
|
|
179
|
+
try {
|
|
180
|
+
await fs.access(searchDir);
|
|
181
|
+
} catch {
|
|
182
|
+
return { files: [] };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const entries = await fs.readdir(searchDir, { withFileTypes: true });
|
|
186
|
+
const files: ListResult["files"] = [];
|
|
187
|
+
|
|
188
|
+
for (const entry of entries) {
|
|
189
|
+
if (entry.isFile() && entry.name.startsWith(prefixBase)) {
|
|
190
|
+
const key = path.join(path.dirname(prefix), entry.name);
|
|
191
|
+
const filePath = path.join(searchDir, entry.name);
|
|
192
|
+
const stat = await fs.stat(filePath);
|
|
193
|
+
|
|
194
|
+
files.push({
|
|
195
|
+
key,
|
|
196
|
+
size: stat.size,
|
|
197
|
+
lastModified: stat.mtime,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Sort by last modified (newest first)
|
|
203
|
+
files.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
|
|
204
|
+
|
|
205
|
+
// Apply limit and cursor (simple implementation)
|
|
206
|
+
const startIndex = options.cursor ? parseInt(options.cursor, 10) : 0;
|
|
207
|
+
const limit = options.limit || 1000;
|
|
208
|
+
const paginatedFiles = files.slice(startIndex, startIndex + limit);
|
|
209
|
+
const hasMore = startIndex + limit < files.length;
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
files: paginatedFiles,
|
|
213
|
+
nextCursor: hasMore ? String(startIndex + limit) : undefined,
|
|
214
|
+
};
|
|
215
|
+
} catch (error) {
|
|
216
|
+
throw new EmDashStorageError("Failed to list files", "LIST_FAILED", error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async getSignedUploadUrl(_options: SignedUploadOptions): Promise<SignedUploadUrl> {
|
|
221
|
+
// Local storage doesn't support signed URLs
|
|
222
|
+
throw new EmDashStorageError(
|
|
223
|
+
"Local storage does not support signed upload URLs. " +
|
|
224
|
+
"Upload files directly through the API.",
|
|
225
|
+
"NOT_SUPPORTED",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
getPublicUrl(key: string): string {
|
|
230
|
+
return `${this.baseUrl}/${key}`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get content type from file extension
|
|
236
|
+
*/
|
|
237
|
+
function getContentType(ext: string): string {
|
|
238
|
+
return mime.getType(ext) ?? "application/octet-stream";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Create local storage adapter
|
|
243
|
+
* This is the factory function called at runtime
|
|
244
|
+
*/
|
|
245
|
+
export function createStorage(config: Record<string, unknown>): Storage {
|
|
246
|
+
const directory = typeof config.directory === "string" ? config.directory : "";
|
|
247
|
+
const baseUrl = typeof config.baseUrl === "string" ? config.baseUrl : "";
|
|
248
|
+
return new LocalStorage({ directory, baseUrl });
|
|
249
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3-Compatible Storage Implementation
|
|
3
|
+
*
|
|
4
|
+
* Uses the AWS SDK v3 for S3 operations.
|
|
5
|
+
* Works with AWS S3, Cloudflare R2, Minio, and other S3-compatible services.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
S3Client,
|
|
10
|
+
PutObjectCommand,
|
|
11
|
+
GetObjectCommand,
|
|
12
|
+
DeleteObjectCommand,
|
|
13
|
+
HeadObjectCommand,
|
|
14
|
+
ListObjectsV2Command,
|
|
15
|
+
type ListObjectsV2Response,
|
|
16
|
+
} from "@aws-sdk/client-s3";
|
|
17
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
Storage,
|
|
21
|
+
S3StorageConfig,
|
|
22
|
+
UploadResult,
|
|
23
|
+
DownloadResult,
|
|
24
|
+
ListResult,
|
|
25
|
+
ListOptions,
|
|
26
|
+
SignedUploadUrl,
|
|
27
|
+
SignedUploadOptions,
|
|
28
|
+
} from "./types.js";
|
|
29
|
+
import { EmDashStorageError } from "./types.js";
|
|
30
|
+
|
|
31
|
+
const TRAILING_SLASH_PATTERN = /\/$/;
|
|
32
|
+
|
|
33
|
+
/** Type guard for AWS SDK errors (have a `name` property) */
|
|
34
|
+
function hasErrorName(error: unknown): error is Error & { name: string } {
|
|
35
|
+
return error instanceof Error && typeof error.name === "string";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* S3-compatible storage implementation
|
|
40
|
+
*/
|
|
41
|
+
export class S3Storage implements Storage {
|
|
42
|
+
private client: S3Client;
|
|
43
|
+
private bucket: string;
|
|
44
|
+
private publicUrl?: string;
|
|
45
|
+
private endpoint: string;
|
|
46
|
+
|
|
47
|
+
constructor(config: S3StorageConfig) {
|
|
48
|
+
this.bucket = config.bucket;
|
|
49
|
+
this.publicUrl = config.publicUrl;
|
|
50
|
+
this.endpoint = config.endpoint;
|
|
51
|
+
|
|
52
|
+
this.client = new S3Client({
|
|
53
|
+
endpoint: config.endpoint,
|
|
54
|
+
region: config.region || "auto",
|
|
55
|
+
credentials: {
|
|
56
|
+
accessKeyId: config.accessKeyId,
|
|
57
|
+
secretAccessKey: config.secretAccessKey,
|
|
58
|
+
},
|
|
59
|
+
// Required for R2 and some S3-compatible services
|
|
60
|
+
forcePathStyle: true,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async upload(options: {
|
|
65
|
+
key: string;
|
|
66
|
+
body: Buffer | Uint8Array | ReadableStream<Uint8Array>;
|
|
67
|
+
contentType: string;
|
|
68
|
+
}): Promise<UploadResult> {
|
|
69
|
+
try {
|
|
70
|
+
// Convert ReadableStream to Buffer if needed
|
|
71
|
+
let body: Buffer | Uint8Array;
|
|
72
|
+
if (options.body instanceof ReadableStream) {
|
|
73
|
+
const chunks: Uint8Array[] = [];
|
|
74
|
+
const reader = options.body.getReader();
|
|
75
|
+
while (true) {
|
|
76
|
+
const { done, value } = await reader.read();
|
|
77
|
+
if (done) break;
|
|
78
|
+
chunks.push(value);
|
|
79
|
+
}
|
|
80
|
+
body = Buffer.concat(chunks);
|
|
81
|
+
} else {
|
|
82
|
+
body = options.body;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await this.client.send(
|
|
86
|
+
new PutObjectCommand({
|
|
87
|
+
Bucket: this.bucket,
|
|
88
|
+
Key: options.key,
|
|
89
|
+
Body: body,
|
|
90
|
+
ContentType: options.contentType,
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
key: options.key,
|
|
96
|
+
url: this.getPublicUrl(options.key),
|
|
97
|
+
size: body.length,
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new EmDashStorageError(`Failed to upload file: ${options.key}`, "UPLOAD_FAILED", error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async download(key: string): Promise<DownloadResult> {
|
|
105
|
+
try {
|
|
106
|
+
const response = await this.client.send(
|
|
107
|
+
new GetObjectCommand({
|
|
108
|
+
Bucket: this.bucket,
|
|
109
|
+
Key: key,
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (!response.Body) {
|
|
114
|
+
throw new EmDashStorageError(`File not found: ${key}`, "NOT_FOUND");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Convert SDK stream to web ReadableStream
|
|
118
|
+
const body = response.Body.transformToWebStream();
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
body,
|
|
122
|
+
contentType: response.ContentType || "application/octet-stream",
|
|
123
|
+
size: response.ContentLength || 0,
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (
|
|
127
|
+
error instanceof EmDashStorageError ||
|
|
128
|
+
(hasErrorName(error) && error.name === "NoSuchKey")
|
|
129
|
+
) {
|
|
130
|
+
throw new EmDashStorageError(`File not found: ${key}`, "NOT_FOUND", error);
|
|
131
|
+
}
|
|
132
|
+
throw new EmDashStorageError(`Failed to download file: ${key}`, "DOWNLOAD_FAILED", error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async delete(key: string): Promise<void> {
|
|
137
|
+
try {
|
|
138
|
+
await this.client.send(
|
|
139
|
+
new DeleteObjectCommand({
|
|
140
|
+
Bucket: this.bucket,
|
|
141
|
+
Key: key,
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
// S3 delete is idempotent, so we ignore "not found" errors
|
|
146
|
+
if (!hasErrorName(error) || error.name !== "NoSuchKey") {
|
|
147
|
+
throw new EmDashStorageError(`Failed to delete file: ${key}`, "DELETE_FAILED", error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async exists(key: string): Promise<boolean> {
|
|
153
|
+
try {
|
|
154
|
+
await this.client.send(
|
|
155
|
+
new HeadObjectCommand({
|
|
156
|
+
Bucket: this.bucket,
|
|
157
|
+
Key: key,
|
|
158
|
+
}),
|
|
159
|
+
);
|
|
160
|
+
return true;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
if (hasErrorName(error) && error.name === "NotFound") {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
throw new EmDashStorageError(`Failed to check file existence: ${key}`, "HEAD_FAILED", error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async list(options: ListOptions = {}): Promise<ListResult> {
|
|
170
|
+
try {
|
|
171
|
+
// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- S3 client.send returns generic output; narrowing to ListObjectsV2Response
|
|
172
|
+
const response = (await this.client.send(
|
|
173
|
+
new ListObjectsV2Command({
|
|
174
|
+
Bucket: this.bucket,
|
|
175
|
+
Prefix: options.prefix,
|
|
176
|
+
MaxKeys: options.limit,
|
|
177
|
+
ContinuationToken: options.cursor,
|
|
178
|
+
}),
|
|
179
|
+
)) as ListObjectsV2Response;
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
files: (response.Contents || []).map(
|
|
183
|
+
(item: { Key?: string; Size?: number; LastModified?: Date; ETag?: string }) => ({
|
|
184
|
+
key: item.Key!,
|
|
185
|
+
size: item.Size || 0,
|
|
186
|
+
lastModified: item.LastModified || new Date(),
|
|
187
|
+
etag: item.ETag,
|
|
188
|
+
}),
|
|
189
|
+
),
|
|
190
|
+
nextCursor: response.NextContinuationToken,
|
|
191
|
+
};
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw new EmDashStorageError("Failed to list files", "LIST_FAILED", error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getSignedUploadUrl(options: SignedUploadOptions): Promise<SignedUploadUrl> {
|
|
198
|
+
try {
|
|
199
|
+
const expiresIn = options.expiresIn || 3600; // 1 hour default
|
|
200
|
+
|
|
201
|
+
const command = new PutObjectCommand({
|
|
202
|
+
Bucket: this.bucket,
|
|
203
|
+
Key: options.key,
|
|
204
|
+
ContentType: options.contentType,
|
|
205
|
+
ContentLength: options.size,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const url = await getSignedUrl(this.client, command, { expiresIn });
|
|
209
|
+
|
|
210
|
+
const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString();
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
url,
|
|
214
|
+
method: "PUT",
|
|
215
|
+
headers: {
|
|
216
|
+
"Content-Type": options.contentType,
|
|
217
|
+
...(options.size ? { "Content-Length": String(options.size) } : {}),
|
|
218
|
+
},
|
|
219
|
+
expiresAt,
|
|
220
|
+
};
|
|
221
|
+
} catch (error) {
|
|
222
|
+
throw new EmDashStorageError(
|
|
223
|
+
`Failed to generate signed URL for: ${options.key}`,
|
|
224
|
+
"SIGNED_URL_FAILED",
|
|
225
|
+
error,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
getPublicUrl(key: string): string {
|
|
231
|
+
if (this.publicUrl) {
|
|
232
|
+
return `${this.publicUrl.replace(TRAILING_SLASH_PATTERN, "")}/${key}`;
|
|
233
|
+
}
|
|
234
|
+
// Default to endpoint + bucket + key
|
|
235
|
+
return `${this.endpoint.replace(TRAILING_SLASH_PATTERN, "")}/${this.bucket}/${key}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Create S3 storage adapter
|
|
241
|
+
* This is the factory function called at runtime
|
|
242
|
+
*/
|
|
243
|
+
export function createStorage(config: Record<string, unknown>): Storage {
|
|
244
|
+
const { endpoint, bucket, accessKeyId, secretAccessKey, region, publicUrl } = config;
|
|
245
|
+
if (
|
|
246
|
+
typeof endpoint !== "string" ||
|
|
247
|
+
typeof bucket !== "string" ||
|
|
248
|
+
typeof accessKeyId !== "string" ||
|
|
249
|
+
typeof secretAccessKey !== "string"
|
|
250
|
+
) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
"S3Storage requires 'endpoint', 'bucket', 'accessKeyId', and 'secretAccessKey' string config values",
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return new S3Storage({
|
|
256
|
+
endpoint,
|
|
257
|
+
bucket,
|
|
258
|
+
accessKeyId,
|
|
259
|
+
secretAccessKey,
|
|
260
|
+
region: typeof region === "string" ? region : undefined,
|
|
261
|
+
publicUrl: typeof publicUrl === "string" ? publicUrl : undefined,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage Layer Types
|
|
3
|
+
*
|
|
4
|
+
* Defines the interface for S3-compatible storage backends.
|
|
5
|
+
* Works with R2, AWS S3, Minio, and other S3-compatible services.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Storage configuration for S3-compatible backends
|
|
10
|
+
*/
|
|
11
|
+
export interface S3StorageConfig {
|
|
12
|
+
/** S3 endpoint URL (e.g., "https://xxx.r2.cloudflarestorage.com") */
|
|
13
|
+
endpoint: string;
|
|
14
|
+
/** Bucket name */
|
|
15
|
+
bucket: string;
|
|
16
|
+
/** AWS access key ID */
|
|
17
|
+
accessKeyId: string;
|
|
18
|
+
/** AWS secret access key */
|
|
19
|
+
secretAccessKey: string;
|
|
20
|
+
/** Optional region (defaults to "auto" for R2) */
|
|
21
|
+
region?: string;
|
|
22
|
+
/** Optional public URL prefix for generated URLs (e.g., CDN URL) */
|
|
23
|
+
publicUrl?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Local filesystem storage for development
|
|
28
|
+
*/
|
|
29
|
+
export interface LocalStorageConfig {
|
|
30
|
+
/** Directory path for storing files */
|
|
31
|
+
directory: string;
|
|
32
|
+
/** Base URL for serving files */
|
|
33
|
+
baseUrl: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Storage adapter descriptor (serializable config)
|
|
38
|
+
*/
|
|
39
|
+
export interface StorageDescriptor {
|
|
40
|
+
/** Module path exporting createStorage function */
|
|
41
|
+
entrypoint: string;
|
|
42
|
+
/** Serializable config passed to createStorage at runtime */
|
|
43
|
+
config: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Factory function signature for storage adapters
|
|
48
|
+
*
|
|
49
|
+
* Each adapter accesses its own bindings directly:
|
|
50
|
+
* - R2: imports from cloudflare:workers
|
|
51
|
+
* - S3: uses credentials from config
|
|
52
|
+
* - Local: uses filesystem path from config
|
|
53
|
+
*/
|
|
54
|
+
export type CreateStorageFn = (config: Record<string, unknown>) => Storage;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Upload result
|
|
58
|
+
*/
|
|
59
|
+
export interface UploadResult {
|
|
60
|
+
/** Storage key (path within bucket) */
|
|
61
|
+
key: string;
|
|
62
|
+
/** Public URL to access the file */
|
|
63
|
+
url: string;
|
|
64
|
+
/** File size in bytes */
|
|
65
|
+
size: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Download result
|
|
70
|
+
*/
|
|
71
|
+
export interface DownloadResult {
|
|
72
|
+
/** File content as readable stream */
|
|
73
|
+
body: ReadableStream<Uint8Array>;
|
|
74
|
+
/** MIME type */
|
|
75
|
+
contentType: string;
|
|
76
|
+
/** File size in bytes */
|
|
77
|
+
size: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Signed URL for direct upload
|
|
82
|
+
*/
|
|
83
|
+
export interface SignedUploadUrl {
|
|
84
|
+
/** Signed URL for PUT request */
|
|
85
|
+
url: string;
|
|
86
|
+
/** HTTP method (always PUT) */
|
|
87
|
+
method: "PUT";
|
|
88
|
+
/** Headers to include in the upload request */
|
|
89
|
+
headers: Record<string, string>;
|
|
90
|
+
/** URL expiration time (ISO string) */
|
|
91
|
+
expiresAt: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Options for generating signed upload URL
|
|
96
|
+
*/
|
|
97
|
+
export interface SignedUploadOptions {
|
|
98
|
+
/** Storage key (path within bucket) */
|
|
99
|
+
key: string;
|
|
100
|
+
/** MIME type of the file */
|
|
101
|
+
contentType: string;
|
|
102
|
+
/** File size in bytes (for content-length validation) */
|
|
103
|
+
size?: number;
|
|
104
|
+
/** URL expiration in seconds (default: 3600) */
|
|
105
|
+
expiresIn?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* File listing result
|
|
110
|
+
*/
|
|
111
|
+
export interface ListResult {
|
|
112
|
+
/** List of files */
|
|
113
|
+
files: FileInfo[];
|
|
114
|
+
/** Cursor for next page (if more results) */
|
|
115
|
+
nextCursor?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* File info from listing
|
|
120
|
+
*/
|
|
121
|
+
export interface FileInfo {
|
|
122
|
+
/** Storage key */
|
|
123
|
+
key: string;
|
|
124
|
+
/** File size in bytes */
|
|
125
|
+
size: number;
|
|
126
|
+
/** Last modified date */
|
|
127
|
+
lastModified: Date;
|
|
128
|
+
/** ETag (content hash) */
|
|
129
|
+
etag?: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Options for listing files
|
|
134
|
+
*/
|
|
135
|
+
export interface ListOptions {
|
|
136
|
+
/** Filter by key prefix */
|
|
137
|
+
prefix?: string;
|
|
138
|
+
/** Maximum results per page */
|
|
139
|
+
limit?: number;
|
|
140
|
+
/** Cursor from previous list call */
|
|
141
|
+
cursor?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Storage interface
|
|
146
|
+
*
|
|
147
|
+
* All storage backends must implement this interface.
|
|
148
|
+
*/
|
|
149
|
+
export interface Storage {
|
|
150
|
+
/**
|
|
151
|
+
* Upload a file to storage
|
|
152
|
+
*/
|
|
153
|
+
upload(options: {
|
|
154
|
+
key: string;
|
|
155
|
+
body: Buffer | Uint8Array | ReadableStream<Uint8Array>;
|
|
156
|
+
contentType: string;
|
|
157
|
+
}): Promise<UploadResult>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Download a file from storage
|
|
161
|
+
*/
|
|
162
|
+
download(key: string): Promise<DownloadResult>;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Delete a file from storage
|
|
166
|
+
* Idempotent - does not throw if file doesn't exist
|
|
167
|
+
*/
|
|
168
|
+
delete(key: string): Promise<void>;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if a file exists
|
|
172
|
+
*/
|
|
173
|
+
exists(key: string): Promise<boolean>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* List files in storage
|
|
177
|
+
*/
|
|
178
|
+
list(options?: ListOptions): Promise<ListResult>;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate a signed URL for direct upload
|
|
182
|
+
* Client uploads directly to storage, bypassing the server
|
|
183
|
+
*/
|
|
184
|
+
getSignedUploadUrl(options: SignedUploadOptions): Promise<SignedUploadUrl>;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get public URL for a file
|
|
188
|
+
*/
|
|
189
|
+
getPublicUrl(key: string): string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Storage error with additional context
|
|
194
|
+
*/
|
|
195
|
+
export class EmDashStorageError extends Error {
|
|
196
|
+
constructor(
|
|
197
|
+
message: string,
|
|
198
|
+
public code: string,
|
|
199
|
+
public override cause?: unknown,
|
|
200
|
+
) {
|
|
201
|
+
super(message);
|
|
202
|
+
this.name = "EmDashStorageError";
|
|
203
|
+
}
|
|
204
|
+
}
|