@fragno-dev/upload 0.1.1 → 0.1.3
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 +148 -12
- package/dist/browser/client/clients.js +17 -9
- package/dist/browser/client/clients.js.map +1 -1
- package/dist/browser/client/helpers.d.ts +15 -6
- package/dist/browser/client/helpers.d.ts.map +1 -1
- package/dist/browser/client/helpers.js +176 -30
- package/dist/browser/client/helpers.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/{@nanostores_query@0.3.4_nanostores@1.1.0 → @nanostores_query@0.3.4_nanostores@1.2.0}/node_modules/@nanostores/query/dist/nanoquery.js +6 -6
- package/dist/browser/client/node_modules/.pnpm/{@nanostores_query@0.3.4_nanostores@1.1.0 → @nanostores_query@0.3.4_nanostores@1.2.0}/node_modules/@nanostores/query/dist/nanoquery.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/{@nanostores_solid@1.1.1_nanostores@1.1.0_solid-js@1.9.10 → @nanostores_solid@1.1.1_nanostores@1.2.0_solid-js@1.9.10}/node_modules/@nanostores/solid/dist/index.js +2 -2
- package/dist/browser/client/node_modules/.pnpm/{@nanostores_solid@1.1.1_nanostores@1.1.0_solid-js@1.9.10 → @nanostores_solid@1.1.1_nanostores@1.2.0_solid-js@1.9.10}/node_modules/@nanostores/solid/dist/index.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/atom/index.js +2 -1
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/atom/index.js.map +1 -0
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/clean-stores/index.js +6 -0
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/clean-stores/index.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/computed/index.js +8 -5
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/computed/index.js.map +1 -0
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/lifecycle/index.js +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/lifecycle/index.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/listen-keys/index.js +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/listen-keys/index.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/map/index.js +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/map/index.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/task/index.js +1 -1
- package/dist/browser/client/node_modules/.pnpm/{nanostores@1.1.0 → nanostores@1.2.0}/node_modules/nanostores/task/index.js.map +1 -1
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/warn/index.js +16 -0
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.2.0/node_modules/nanostores/warn/index.js.map +1 -0
- package/dist/browser/client/packages/fragment-upload/src/definition.js +1 -42
- package/dist/browser/client/packages/fragment-upload/src/definition.js.map +1 -1
- package/dist/browser/client/packages/fragment-upload/src/routes/files.js +12 -5
- package/dist/browser/client/packages/fragment-upload/src/routes/files.js.map +1 -1
- package/dist/browser/client/packages/fragment-upload/src/routes/shared.js +3 -4
- package/dist/browser/client/packages/fragment-upload/src/routes/shared.js.map +1 -1
- package/dist/browser/client/packages/fragment-upload/src/routes/uploads.js +32 -21
- package/dist/browser/client/packages/fragment-upload/src/routes/uploads.js.map +1 -1
- package/dist/browser/client/packages/fragment-upload/src/schema.js +33 -3
- package/dist/browser/client/packages/fragment-upload/src/schema.js.map +1 -1
- package/dist/browser/client/packages/fragment-upload/src/types.d.ts +1 -2
- package/dist/browser/client/packages/fragment-upload/src/types.d.ts.map +1 -1
- package/dist/browser/client/packages/fragno/dist/client/client.js +28 -12
- package/dist/browser/client/packages/fragno/dist/client/client.js.map +1 -1
- package/dist/browser/client/packages/fragno/dist/client/client.svelte.js +11 -3
- package/dist/browser/client/packages/fragno/dist/client/client.svelte.js.map +1 -1
- package/dist/browser/client/packages/fragno/dist/client/react.js +104 -12
- package/dist/browser/client/packages/fragno/dist/client/react.js.map +1 -1
- package/dist/browser/client/packages/fragno/dist/client/solid.js +25 -11
- package/dist/browser/client/packages/fragno/dist/client/solid.js.map +1 -1
- package/dist/browser/client/packages/fragno/dist/client/vanilla.js +21 -1
- package/dist/browser/client/packages/fragno/dist/client/vanilla.js.map +1 -1
- package/dist/browser/client/packages/fragno/dist/client/vue.js +19 -11
- package/dist/browser/client/packages/fragno/dist/client/vue.js.map +1 -1
- package/dist/browser/client/react.d.ts +215 -192
- package/dist/browser/client/react.d.ts.map +1 -1
- package/dist/browser/client/react.js.map +1 -1
- package/dist/browser/client/solid.d.ts +218 -196
- package/dist/browser/client/solid.d.ts.map +1 -1
- package/dist/browser/client/solid.js.map +1 -1
- package/dist/browser/client/svelte.d.ts +216 -193
- package/dist/browser/client/svelte.d.ts.map +1 -1
- package/dist/browser/client/svelte.js.map +1 -1
- package/dist/browser/client/vanilla.d.ts +217 -195
- package/dist/browser/client/vanilla.d.ts.map +1 -1
- package/dist/browser/client/vanilla.js.map +1 -1
- package/dist/browser/client/vue.d.ts +217 -194
- package/dist/browser/client/vue.d.ts.map +1 -1
- package/dist/browser/client/vue.js.map +1 -1
- package/dist/cli/commands/files/delete.d.ts +4 -4
- package/dist/cli/commands/files/delete.d.ts.map +1 -1
- package/dist/cli/commands/files/delete.js +8 -10
- package/dist/cli/commands/files/delete.js.map +1 -1
- package/dist/cli/commands/files/download-url.d.ts +4 -4
- package/dist/cli/commands/files/download-url.d.ts.map +1 -1
- package/dist/cli/commands/files/download-url.js +8 -10
- package/dist/cli/commands/files/download-url.js.map +1 -1
- package/dist/cli/commands/files/download.d.ts +4 -4
- package/dist/cli/commands/files/download.d.ts.map +1 -1
- package/dist/cli/commands/files/download.js +10 -12
- package/dist/cli/commands/files/download.js.map +1 -1
- package/dist/cli/commands/files/get.d.ts +4 -4
- package/dist/cli/commands/files/get.d.ts.map +1 -1
- package/dist/cli/commands/files/get.js +8 -10
- package/dist/cli/commands/files/get.js.map +1 -1
- package/dist/cli/commands/files/list.d.ts +4 -4
- package/dist/cli/commands/files/list.d.ts.map +1 -1
- package/dist/cli/commands/files/list.js +6 -8
- package/dist/cli/commands/files/list.js.map +1 -1
- package/dist/cli/commands/files/update.d.ts +4 -4
- package/dist/cli/commands/files/update.d.ts.map +1 -1
- package/dist/cli/commands/files/update.js +8 -10
- package/dist/cli/commands/files/update.js.map +1 -1
- package/dist/cli/commands/files/upload.d.ts +4 -4
- package/dist/cli/commands/files/upload.d.ts.map +1 -1
- package/dist/cli/commands/files/upload.js +10 -12
- package/dist/cli/commands/files/upload.js.map +1 -1
- package/dist/cli/commands/uploads/abort.d.ts +2 -2
- package/dist/cli/commands/uploads/abort.d.ts.map +1 -1
- package/dist/cli/commands/uploads/abort.js.map +1 -1
- package/dist/cli/commands/uploads/complete.d.ts +2 -2
- package/dist/cli/commands/uploads/complete.d.ts.map +1 -1
- package/dist/cli/commands/uploads/complete.js.map +1 -1
- package/dist/cli/commands/uploads/content.d.ts +2 -2
- package/dist/cli/commands/uploads/content.d.ts.map +1 -1
- package/dist/cli/commands/uploads/content.js +1 -1
- package/dist/cli/commands/uploads/content.js.map +1 -1
- package/dist/cli/commands/uploads/create.d.ts +4 -4
- package/dist/cli/commands/uploads/create.d.ts.map +1 -1
- package/dist/cli/commands/uploads/create.js +8 -11
- package/dist/cli/commands/uploads/create.js.map +1 -1
- package/dist/cli/commands/uploads/get.d.ts +2 -2
- package/dist/cli/commands/uploads/get.d.ts.map +1 -1
- package/dist/cli/commands/uploads/get.js.map +1 -1
- package/dist/cli/commands/uploads/parts-complete.d.ts +2 -2
- package/dist/cli/commands/uploads/parts-complete.d.ts.map +1 -1
- package/dist/cli/commands/uploads/parts-complete.js.map +1 -1
- package/dist/cli/commands/uploads/parts-list.d.ts +2 -2
- package/dist/cli/commands/uploads/parts-list.d.ts.map +1 -1
- package/dist/cli/commands/uploads/parts-list.js.map +1 -1
- package/dist/cli/commands/uploads/parts-urls.d.ts +2 -2
- package/dist/cli/commands/uploads/parts-urls.d.ts.map +1 -1
- package/dist/cli/commands/uploads/parts-urls.js.map +1 -1
- package/dist/cli/commands/uploads/progress.d.ts +2 -2
- package/dist/cli/commands/uploads/progress.d.ts.map +1 -1
- package/dist/cli/commands/uploads/progress.js.map +1 -1
- package/dist/cli/commands/uploads/transfer.d.ts +4 -4
- package/dist/cli/commands/uploads/transfer.d.ts.map +1 -1
- package/dist/cli/commands/uploads/transfer.js +9 -12
- package/dist/cli/commands/uploads/transfer.js.map +1 -1
- package/dist/cli/index.d.ts +13 -13
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +14 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/client.js +22 -5
- package/dist/cli/utils/client.js.map +1 -1
- package/dist/cli/utils/options.js +7 -43
- package/dist/cli/utils/options.js.map +1 -1
- package/dist/node/cli/commands/files/delete.d.ts +4 -4
- package/dist/node/cli/commands/files/delete.d.ts.map +1 -1
- package/dist/node/cli/commands/files/delete.js +8 -10
- package/dist/node/cli/commands/files/delete.js.map +1 -1
- package/dist/node/cli/commands/files/download-url.d.ts +4 -4
- package/dist/node/cli/commands/files/download-url.d.ts.map +1 -1
- package/dist/node/cli/commands/files/download-url.js +8 -10
- package/dist/node/cli/commands/files/download-url.js.map +1 -1
- package/dist/node/cli/commands/files/download.d.ts +4 -4
- package/dist/node/cli/commands/files/download.d.ts.map +1 -1
- package/dist/node/cli/commands/files/download.js +9 -11
- package/dist/node/cli/commands/files/download.js.map +1 -1
- package/dist/node/cli/commands/files/get.d.ts +4 -4
- package/dist/node/cli/commands/files/get.d.ts.map +1 -1
- package/dist/node/cli/commands/files/get.js +8 -10
- package/dist/node/cli/commands/files/get.js.map +1 -1
- package/dist/node/cli/commands/files/list.d.ts +4 -4
- package/dist/node/cli/commands/files/list.d.ts.map +1 -1
- package/dist/node/cli/commands/files/list.js +6 -8
- package/dist/node/cli/commands/files/list.js.map +1 -1
- package/dist/node/cli/commands/files/update.d.ts +4 -4
- package/dist/node/cli/commands/files/update.d.ts.map +1 -1
- package/dist/node/cli/commands/files/update.js +8 -10
- package/dist/node/cli/commands/files/update.js.map +1 -1
- package/dist/node/cli/commands/files/upload.d.ts +4 -4
- package/dist/node/cli/commands/files/upload.d.ts.map +1 -1
- package/dist/node/cli/commands/files/upload.js +9 -11
- package/dist/node/cli/commands/files/upload.js.map +1 -1
- package/dist/node/cli/commands/uploads/abort.d.ts +2 -2
- package/dist/node/cli/commands/uploads/abort.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/abort.js.map +1 -1
- package/dist/node/cli/commands/uploads/complete.d.ts +2 -2
- package/dist/node/cli/commands/uploads/complete.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/complete.js.map +1 -1
- package/dist/node/cli/commands/uploads/content.d.ts +2 -2
- package/dist/node/cli/commands/uploads/content.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/content.js.map +1 -1
- package/dist/node/cli/commands/uploads/create.d.ts +4 -4
- package/dist/node/cli/commands/uploads/create.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/create.js +8 -11
- package/dist/node/cli/commands/uploads/create.js.map +1 -1
- package/dist/node/cli/commands/uploads/get.d.ts +2 -2
- package/dist/node/cli/commands/uploads/get.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/get.js.map +1 -1
- package/dist/node/cli/commands/uploads/parts-complete.d.ts +2 -2
- package/dist/node/cli/commands/uploads/parts-complete.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/parts-complete.js.map +1 -1
- package/dist/node/cli/commands/uploads/parts-list.d.ts +2 -2
- package/dist/node/cli/commands/uploads/parts-list.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/parts-list.js.map +1 -1
- package/dist/node/cli/commands/uploads/parts-urls.d.ts +2 -2
- package/dist/node/cli/commands/uploads/parts-urls.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/parts-urls.js.map +1 -1
- package/dist/node/cli/commands/uploads/progress.d.ts +2 -2
- package/dist/node/cli/commands/uploads/progress.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/progress.js.map +1 -1
- package/dist/node/cli/commands/uploads/transfer.d.ts +4 -4
- package/dist/node/cli/commands/uploads/transfer.d.ts.map +1 -1
- package/dist/node/cli/commands/uploads/transfer.js +8 -11
- package/dist/node/cli/commands/uploads/transfer.js.map +1 -1
- package/dist/node/cli/index.d.ts +13 -13
- package/dist/node/cli/index.d.ts.map +1 -1
- package/dist/node/cli/index.js +14 -14
- package/dist/node/cli/index.js.map +1 -1
- package/dist/node/cli/utils/client.js +22 -5
- package/dist/node/cli/utils/client.js.map +1 -1
- package/dist/node/cli/utils/options.js +7 -43
- package/dist/node/cli/utils/options.js.map +1 -1
- package/dist/node/client/clients.d.ts +217 -194
- package/dist/node/client/clients.d.ts.map +1 -1
- package/dist/node/client/clients.js +17 -9
- package/dist/node/client/clients.js.map +1 -1
- package/dist/node/client/helpers.d.ts +15 -6
- package/dist/node/client/helpers.d.ts.map +1 -1
- package/dist/node/client/helpers.js +176 -30
- package/dist/node/client/helpers.js.map +1 -1
- package/dist/node/client/react.d.ts +217 -194
- package/dist/node/client/react.d.ts.map +1 -1
- package/dist/node/client/react.js.map +1 -1
- package/dist/node/client/solid.d.ts +218 -196
- package/dist/node/client/solid.d.ts.map +1 -1
- package/dist/node/client/solid.js.map +1 -1
- package/dist/node/client/svelte.d.ts +216 -193
- package/dist/node/client/svelte.d.ts.map +1 -1
- package/dist/node/client/svelte.js.map +1 -1
- package/dist/node/client/vanilla.d.ts +217 -195
- package/dist/node/client/vanilla.d.ts.map +1 -1
- package/dist/node/client/vanilla.js.map +1 -1
- package/dist/node/client/vue.d.ts +217 -194
- package/dist/node/client/vue.d.ts.map +1 -1
- package/dist/node/client/vue.js.map +1 -1
- package/dist/node/config.d.ts +6 -6
- package/dist/node/config.d.ts.map +1 -1
- package/dist/node/config.js.map +1 -1
- package/dist/node/definition.d.ts +588 -219
- package/dist/node/definition.d.ts.map +1 -1
- package/dist/node/definition.js +27 -3
- package/dist/node/definition.js.map +1 -1
- package/dist/node/file-key.d.ts +19 -0
- package/dist/node/file-key.d.ts.map +1 -0
- package/dist/node/file-key.js +47 -0
- package/dist/node/file-key.js.map +1 -0
- package/dist/node/index.d.ts +582 -175
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +3 -2
- package/dist/node/index.js.map +1 -1
- package/dist/node/routes/files.js +99 -64
- package/dist/node/routes/files.js.map +1 -1
- package/dist/node/routes/index.d.ts +1497 -721
- package/dist/node/routes/index.d.ts.map +1 -1
- package/dist/node/routes/shared.js +5 -9
- package/dist/node/routes/shared.js.map +1 -1
- package/dist/node/routes/uploads.js +105 -47
- package/dist/node/routes/uploads.js.map +1 -1
- package/dist/node/schema.d.ts +6 -6
- package/dist/node/schema.d.ts.map +1 -1
- package/dist/node/schema.js +12 -3
- package/dist/node/schema.js.map +1 -1
- package/dist/node/services/files.d.ts +6 -2
- package/dist/node/services/files.d.ts.map +1 -1
- package/dist/node/services/files.js +22 -20
- package/dist/node/services/files.js.map +1 -1
- package/dist/node/services/helpers.js +37 -15
- package/dist/node/services/helpers.js.map +1 -1
- package/dist/node/services/uploads.d.ts +10 -5
- package/dist/node/services/uploads.d.ts.map +1 -1
- package/dist/node/services/uploads.js +340 -63
- package/dist/node/services/uploads.js.map +1 -1
- package/dist/node/storage/fs.d.ts.map +1 -1
- package/dist/node/storage/fs.js +16 -10
- package/dist/node/storage/fs.js.map +1 -1
- package/dist/node/storage/object-key.js +36 -0
- package/dist/node/storage/object-key.js.map +1 -0
- package/dist/node/storage/r2-binding.d.ts +59 -0
- package/dist/node/storage/r2-binding.d.ts.map +1 -0
- package/dist/node/storage/r2-binding.js +245 -0
- package/dist/node/storage/r2-binding.js.map +1 -0
- package/dist/node/storage/r2.d.ts +6 -5
- package/dist/node/storage/r2.d.ts.map +1 -1
- package/dist/node/storage/s3.d.ts.map +1 -1
- package/dist/node/storage/s3.js +16 -10
- package/dist/node/storage/s3.js.map +1 -1
- package/dist/node/storage/types.d.ts +6 -5
- package/dist/node/storage/types.d.ts.map +1 -1
- package/dist/node/types.d.ts +1 -2
- package/dist/node/types.d.ts.map +1 -1
- package/package.json +26 -46
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/atom/index.js.map +0 -1
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/clean-stores/index.js +0 -6
- package/dist/browser/client/node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/computed/index.js.map +0 -1
- package/dist/browser/client/packages/fragment-upload/src/keys.d.ts +0 -7
- package/dist/browser/client/packages/fragment-upload/src/keys.d.ts.map +0 -1
- package/dist/browser/client/packages/fragment-upload/src/keys.js +0 -28
- package/dist/browser/client/packages/fragment-upload/src/keys.js.map +0 -1
- package/dist/browser/index-BdjKPO4J.d.ts +0 -177
- package/dist/browser/index-BdjKPO4J.d.ts.map +0 -1
- package/dist/browser/index.js +0 -3
- package/dist/browser/src-vdNJUbjT.js +0 -1982
- package/dist/browser/src-vdNJUbjT.js.map +0 -1
- package/dist/cli/keys.js +0 -32
- package/dist/cli/keys.js.map +0 -1
- package/dist/node/keys.d.ts +0 -12
- package/dist/node/keys.d.ts.map +0 -1
- package/dist/node/keys.js +0 -63
- package/dist/node/keys.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { assertFileKey } from "../file-key.js";
|
|
2
|
+
import { appendStorageObjectKeyVersionSegment } from "./object-key.js";
|
|
3
|
+
|
|
4
|
+
//#region src/storage/r2-binding.ts
|
|
5
|
+
const BYTES_IN_MIB = 1024 * 1024;
|
|
6
|
+
const BYTES_IN_GIB = 1024 * BYTES_IN_MIB;
|
|
7
|
+
const DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES = 1024;
|
|
8
|
+
const DEFAULT_UPLOAD_EXPIRES_IN_SECONDS = 3600;
|
|
9
|
+
const DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
10
|
+
const DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_MIB;
|
|
11
|
+
const DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_GIB;
|
|
12
|
+
const DEFAULT_MAX_MULTIPART_PARTS = 1e4;
|
|
13
|
+
const DEFAULT_MULTIPART_PART_SIZE_BYTES = 8 * BYTES_IN_MIB;
|
|
14
|
+
const normalizePrefix = (prefix) => {
|
|
15
|
+
if (!prefix) return "";
|
|
16
|
+
const segments = prefix.split("/").filter(Boolean);
|
|
17
|
+
for (const segment of segments) if (segment === "." || segment === "..") throw new Error("Storage key prefix cannot include '.' or '..' segments");
|
|
18
|
+
return segments.join("/");
|
|
19
|
+
};
|
|
20
|
+
const normalizeProvider = (provider) => {
|
|
21
|
+
const normalized = provider.trim();
|
|
22
|
+
if (normalized.length === 0 || normalized === "." || normalized === ".." || normalized.includes("/") || normalized.includes("\\")) throw new Error("Invalid provider");
|
|
23
|
+
return normalized;
|
|
24
|
+
};
|
|
25
|
+
const resolveMetadataSize = (metadata) => {
|
|
26
|
+
if (!metadata) return 0;
|
|
27
|
+
const serialized = JSON.stringify(metadata);
|
|
28
|
+
return new TextEncoder().encode(serialized).byteLength;
|
|
29
|
+
};
|
|
30
|
+
const divideCeil = (numerator, denominator) => (numerator + denominator - 1n) / denominator;
|
|
31
|
+
const resolveEtag = (result) => result.httpEtag ?? result.etag;
|
|
32
|
+
const assertSizeMatches = (input) => {
|
|
33
|
+
if (input.expected === void 0 || input.expected === null) return;
|
|
34
|
+
if (input.actual === void 0 || input.actual === null) return;
|
|
35
|
+
if (input.expected !== input.actual) throw new Error("Upload size mismatch");
|
|
36
|
+
};
|
|
37
|
+
const normalizeUploadedPart = (uploadedPart, fallbackPartNumber) => {
|
|
38
|
+
if (!uploadedPart || typeof uploadedPart !== "object") throw new Error("STORAGE_ERROR");
|
|
39
|
+
const part = uploadedPart;
|
|
40
|
+
if (typeof part.etag !== "string" || part.etag.length === 0) throw new Error("STORAGE_ERROR");
|
|
41
|
+
return {
|
|
42
|
+
etag: part.etag,
|
|
43
|
+
partNumber: typeof part.partNumber === "number" ? part.partNumber : fallbackPartNumber
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const takeBytesFromChunks = (chunks, size) => {
|
|
47
|
+
const part = new Uint8Array(size);
|
|
48
|
+
let offset = 0;
|
|
49
|
+
while (offset < size) {
|
|
50
|
+
const chunk = chunks[0];
|
|
51
|
+
if (!chunk) throw new Error("STORAGE_ERROR");
|
|
52
|
+
const remaining = size - offset;
|
|
53
|
+
if (chunk.byteLength <= remaining) {
|
|
54
|
+
part.set(chunk, offset);
|
|
55
|
+
offset += chunk.byteLength;
|
|
56
|
+
chunks.shift();
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
part.set(chunk.subarray(0, remaining), offset);
|
|
60
|
+
chunks[0] = chunk.subarray(remaining);
|
|
61
|
+
offset += remaining;
|
|
62
|
+
}
|
|
63
|
+
return part;
|
|
64
|
+
};
|
|
65
|
+
function createR2BindingStorageAdapter(options) {
|
|
66
|
+
const storageKeyPrefix = normalizePrefix(options.storageKeyPrefix ?? "");
|
|
67
|
+
const maxStorageKeyLengthBytes = options.maxStorageKeyLengthBytes ?? DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES;
|
|
68
|
+
const uploadExpiresInSeconds = options.uploadExpiresInSeconds ?? DEFAULT_UPLOAD_EXPIRES_IN_SECONDS;
|
|
69
|
+
const maxSingleUploadBytes = options.maxSingleUploadBytes;
|
|
70
|
+
const maxMultipartUploadBytes = options.maxMultipartUploadBytes;
|
|
71
|
+
const multipartThresholdBytes = options.multipartThresholdBytes ?? maxSingleUploadBytes;
|
|
72
|
+
const multipartPartSizeBytes = options.multipartPartSizeBytes ?? DEFAULT_MULTIPART_PART_SIZE_BYTES;
|
|
73
|
+
const minMultipartPartSizeBytes = DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES;
|
|
74
|
+
const maxMultipartPartSizeBytes = DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES;
|
|
75
|
+
const maxMultipartParts = DEFAULT_MAX_MULTIPART_PARTS;
|
|
76
|
+
const maxUploadBytes = options.maxMultipartUploadBytes ?? options.maxSingleUploadBytes;
|
|
77
|
+
const maxMetadataBytes = options.maxMetadataBytes;
|
|
78
|
+
const defaultContentType = options.defaultContentType ?? DEFAULT_CONTENT_TYPE;
|
|
79
|
+
if (multipartPartSizeBytes < minMultipartPartSizeBytes) throw new Error(`Multipart part size must be at least ${minMultipartPartSizeBytes} bytes for R2 uploads`);
|
|
80
|
+
if (multipartPartSizeBytes > maxMultipartPartSizeBytes) throw new Error(`Multipart part size must be at most ${maxMultipartPartSizeBytes} bytes for R2 uploads`);
|
|
81
|
+
const resolveStorageKey = (input) => {
|
|
82
|
+
const storageKey = [
|
|
83
|
+
storageKeyPrefix,
|
|
84
|
+
normalizeProvider(input.provider),
|
|
85
|
+
assertFileKey(input.fileKey)
|
|
86
|
+
].filter(Boolean).join("/");
|
|
87
|
+
if (storageKey.length === 0) throw new Error("Storage key cannot be empty");
|
|
88
|
+
if (new TextEncoder().encode(storageKey).byteLength > maxStorageKeyLengthBytes) throw new Error("Storage key exceeds maximum length");
|
|
89
|
+
return storageKey;
|
|
90
|
+
};
|
|
91
|
+
const resolveMultipartPartSize = (sizeBytes) => {
|
|
92
|
+
let partSize = multipartPartSizeBytes;
|
|
93
|
+
if (sizeBytes !== void 0 && sizeBytes !== null && sizeBytes > 0n) {
|
|
94
|
+
const minPartSizeForLimit = divideCeil(sizeBytes, BigInt(maxMultipartParts));
|
|
95
|
+
if (minPartSizeForLimit > BigInt(maxMultipartPartSizeBytes)) throw new Error("Multipart upload exceeds maximum number of parts");
|
|
96
|
+
if (minPartSizeForLimit > BigInt(partSize)) partSize = Number(minPartSizeForLimit);
|
|
97
|
+
}
|
|
98
|
+
if (partSize < minMultipartPartSizeBytes) throw new Error("Multipart part size is smaller than minimum allowed size");
|
|
99
|
+
if (partSize > maxMultipartPartSizeBytes) throw new Error("Multipart part size exceeds maximum allowed size");
|
|
100
|
+
return partSize;
|
|
101
|
+
};
|
|
102
|
+
const shouldUseMultipartUpload = (sizeBytes) => {
|
|
103
|
+
if (sizeBytes === void 0 || sizeBytes === null || sizeBytes <= 0n) return false;
|
|
104
|
+
if (multipartThresholdBytes !== void 0 && sizeBytes >= BigInt(multipartThresholdBytes)) return true;
|
|
105
|
+
if (maxSingleUploadBytes !== void 0 && sizeBytes > BigInt(maxSingleUploadBytes)) return true;
|
|
106
|
+
return false;
|
|
107
|
+
};
|
|
108
|
+
const writeMultipartStream = async (input) => {
|
|
109
|
+
const multipartUpload = await options.bucket.createMultipartUpload(input.storageKey, { httpMetadata: input.contentType ? { contentType: input.contentType } : void 0 });
|
|
110
|
+
if (!multipartUpload.uploadId) throw new Error("STORAGE_ERROR");
|
|
111
|
+
const uploadedParts = [];
|
|
112
|
+
const reader = input.body.getReader();
|
|
113
|
+
const bufferedChunks = [];
|
|
114
|
+
let bufferedBytes = 0;
|
|
115
|
+
let partNumber = 1;
|
|
116
|
+
let uploadedBytes = 0n;
|
|
117
|
+
try {
|
|
118
|
+
while (true) {
|
|
119
|
+
const { done, value } = await reader.read();
|
|
120
|
+
if (done) break;
|
|
121
|
+
if (!value || value.byteLength === 0) continue;
|
|
122
|
+
uploadedBytes += BigInt(value.byteLength);
|
|
123
|
+
bufferedChunks.push(value);
|
|
124
|
+
bufferedBytes += value.byteLength;
|
|
125
|
+
while (bufferedBytes >= input.partSizeBytes) {
|
|
126
|
+
if (partNumber > maxMultipartParts) throw new Error("Multipart upload exceeds maximum number of parts");
|
|
127
|
+
const part = takeBytesFromChunks(bufferedChunks, input.partSizeBytes);
|
|
128
|
+
bufferedBytes -= input.partSizeBytes;
|
|
129
|
+
const uploadedPart = await multipartUpload.uploadPart(partNumber, part);
|
|
130
|
+
uploadedParts.push(normalizeUploadedPart(uploadedPart, partNumber));
|
|
131
|
+
partNumber += 1;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
assertSizeMatches({
|
|
135
|
+
expected: input.sizeBytes,
|
|
136
|
+
actual: uploadedBytes
|
|
137
|
+
});
|
|
138
|
+
if (bufferedBytes > 0 || uploadedParts.length === 0) {
|
|
139
|
+
if (partNumber > maxMultipartParts) throw new Error("Multipart upload exceeds maximum number of parts");
|
|
140
|
+
const finalPart = bufferedBytes > 0 ? takeBytesFromChunks(bufferedChunks, bufferedBytes) : new Uint8Array(0);
|
|
141
|
+
bufferedBytes = 0;
|
|
142
|
+
const uploadedPart = await multipartUpload.uploadPart(partNumber, finalPart);
|
|
143
|
+
uploadedParts.push(normalizeUploadedPart(uploadedPart, partNumber));
|
|
144
|
+
}
|
|
145
|
+
const completed = await multipartUpload.complete(uploadedParts);
|
|
146
|
+
return {
|
|
147
|
+
...resolveEtag(completed) ? { etag: resolveEtag(completed) } : {},
|
|
148
|
+
sizeBytes: uploadedBytes
|
|
149
|
+
};
|
|
150
|
+
} catch (error) {
|
|
151
|
+
try {
|
|
152
|
+
await multipartUpload.abort();
|
|
153
|
+
} catch {}
|
|
154
|
+
throw error;
|
|
155
|
+
} finally {
|
|
156
|
+
reader.releaseLock();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
name: "r2-binding",
|
|
161
|
+
capabilities: {
|
|
162
|
+
directUpload: false,
|
|
163
|
+
multipartUpload: true,
|
|
164
|
+
signedDownload: false,
|
|
165
|
+
proxyUpload: true
|
|
166
|
+
},
|
|
167
|
+
limits: {
|
|
168
|
+
maxSingleUploadBytes,
|
|
169
|
+
maxMultipartUploadBytes,
|
|
170
|
+
minMultipartPartSizeBytes,
|
|
171
|
+
maxMultipartPartSizeBytes,
|
|
172
|
+
maxMultipartParts,
|
|
173
|
+
maxStorageKeyLengthBytes,
|
|
174
|
+
maxMetadataBytes
|
|
175
|
+
},
|
|
176
|
+
recommendations: {
|
|
177
|
+
uploadExpiresInSeconds,
|
|
178
|
+
signedUrlExpiresInSeconds: options.signedUrlExpiresInSeconds,
|
|
179
|
+
directUploadThresholdBytes: options.directUploadThresholdBytes,
|
|
180
|
+
multipartThresholdBytes,
|
|
181
|
+
multipartPartSizeBytes
|
|
182
|
+
},
|
|
183
|
+
resolveStorageKey,
|
|
184
|
+
initUpload: async ({ provider, fileKey, sizeBytes, metadata, objectKeyVersionSegment }) => {
|
|
185
|
+
const storageKey = appendStorageObjectKeyVersionSegment(resolveStorageKey({
|
|
186
|
+
provider,
|
|
187
|
+
fileKey
|
|
188
|
+
}), objectKeyVersionSegment, maxStorageKeyLengthBytes);
|
|
189
|
+
if (maxUploadBytes !== void 0 && sizeBytes > BigInt(maxUploadBytes)) throw new Error("Upload exceeds maximum upload size");
|
|
190
|
+
if (maxMetadataBytes !== void 0 && resolveMetadataSize(metadata) > maxMetadataBytes) throw new Error("Metadata exceeds maximum size");
|
|
191
|
+
return {
|
|
192
|
+
strategy: "proxy",
|
|
193
|
+
storageKey,
|
|
194
|
+
expiresAt: new Date(Date.now() + uploadExpiresInSeconds * 1e3)
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
writeStream: async ({ storageKey, body, contentType, sizeBytes }) => {
|
|
198
|
+
if (maxUploadBytes !== void 0 && sizeBytes !== void 0 && sizeBytes !== null && sizeBytes > BigInt(maxUploadBytes)) throw new Error("Upload exceeds maximum upload size");
|
|
199
|
+
if (shouldUseMultipartUpload(sizeBytes)) return writeMultipartStream({
|
|
200
|
+
storageKey,
|
|
201
|
+
body,
|
|
202
|
+
contentType,
|
|
203
|
+
sizeBytes,
|
|
204
|
+
partSizeBytes: resolveMultipartPartSize(sizeBytes)
|
|
205
|
+
});
|
|
206
|
+
const result = await options.bucket.put(storageKey, body, { httpMetadata: contentType ? { contentType } : void 0 });
|
|
207
|
+
const size = typeof result.size === "number" ? BigInt(result.size) : sizeBytes ?? void 0;
|
|
208
|
+
assertSizeMatches({
|
|
209
|
+
expected: sizeBytes,
|
|
210
|
+
actual: size
|
|
211
|
+
});
|
|
212
|
+
const etag = resolveEtag(result);
|
|
213
|
+
return {
|
|
214
|
+
...etag ? { etag } : {},
|
|
215
|
+
...size !== void 0 ? { sizeBytes: size } : {}
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
deleteObject: async ({ storageKey }) => {
|
|
219
|
+
await options.bucket.delete(storageKey);
|
|
220
|
+
},
|
|
221
|
+
getDownloadStream: async ({ storageKey }) => {
|
|
222
|
+
const object = await options.bucket.get(storageKey);
|
|
223
|
+
if (!object || !object.body) throw new Error("STORAGE_ERROR");
|
|
224
|
+
const headers = new Headers();
|
|
225
|
+
headers.set("Content-Type", object.httpMetadata?.contentType || defaultContentType);
|
|
226
|
+
headers.set("ETag", object.httpEtag);
|
|
227
|
+
headers.set("Content-Length", object.size.toString());
|
|
228
|
+
return new Response(object.body, { headers });
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const isR2BindingBucket = (candidate) => {
|
|
233
|
+
if (!candidate || typeof candidate !== "object") return false;
|
|
234
|
+
const bucket = candidate;
|
|
235
|
+
return typeof bucket.put === "function" && typeof bucket.get === "function" && typeof bucket.delete === "function" && typeof bucket.createMultipartUpload === "function";
|
|
236
|
+
};
|
|
237
|
+
const resolveR2BindingBucket = (bindings, bindingName) => {
|
|
238
|
+
const candidate = bindings[bindingName];
|
|
239
|
+
if (!isR2BindingBucket(candidate)) throw new Error(`Upload R2 bucket binding '${bindingName}' is not configured.`);
|
|
240
|
+
return candidate;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
//#endregion
|
|
244
|
+
export { createR2BindingStorageAdapter, resolveR2BindingBucket };
|
|
245
|
+
//# sourceMappingURL=r2-binding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-binding.js","names":["uploadedParts: R2BindingUploadedPart[]","bufferedChunks: Uint8Array[]"],"sources":["../../../src/storage/r2-binding.ts"],"sourcesContent":["import { assertFileKey } from \"../file-key\";\nimport { appendStorageObjectKeyVersionSegment } from \"./object-key\";\nimport type { StorageAdapter } from \"./types\";\n\nconst BYTES_IN_MIB = 1024 * 1024;\nconst BYTES_IN_GIB = 1024 * BYTES_IN_MIB;\nconst DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES = 1024;\nconst DEFAULT_UPLOAD_EXPIRES_IN_SECONDS = 60 * 60;\nconst DEFAULT_CONTENT_TYPE = \"application/octet-stream\";\nconst DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_MIB;\nconst DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_GIB;\nconst DEFAULT_MAX_MULTIPART_PARTS = 10_000;\nconst DEFAULT_MULTIPART_PART_SIZE_BYTES = 8 * BYTES_IN_MIB;\n\ntype R2BindingPutResult = {\n httpEtag?: string;\n etag?: string;\n size?: number;\n};\n\ntype R2BindingGetResult = {\n body: ReadableStream<Uint8Array> | null;\n size: number;\n httpEtag: string;\n httpMetadata?: {\n contentType?: string;\n };\n};\n\ntype R2BindingUploadedPart = {\n etag: string;\n partNumber: number;\n};\n\ntype R2BindingMultipartUpload = {\n uploadId: string;\n uploadPart: (partNumber: number, value: unknown) => Promise<R2BindingUploadedPart>;\n complete: (parts: R2BindingUploadedPart[]) => Promise<R2BindingPutResult>;\n abort: () => Promise<void>;\n};\n\nexport type R2BindingBucket = {\n put: (\n key: string,\n value: unknown,\n options?: {\n httpMetadata?: {\n contentType?: string;\n };\n },\n ) => Promise<R2BindingPutResult>;\n get: (key: string) => Promise<R2BindingGetResult | null>;\n delete: (key: string) => Promise<void>;\n createMultipartUpload: (\n key: string,\n options?: {\n httpMetadata?: {\n contentType?: string;\n };\n },\n ) => Promise<R2BindingMultipartUpload>;\n};\n\nexport type R2BindingStorageAdapterOptions = {\n bucket: R2BindingBucket;\n storageKeyPrefix?: string;\n directUploadThresholdBytes?: number;\n multipartThresholdBytes?: number;\n multipartPartSizeBytes?: number;\n uploadExpiresInSeconds?: number;\n signedUrlExpiresInSeconds?: number;\n maxSingleUploadBytes?: number;\n maxMultipartUploadBytes?: number;\n maxStorageKeyLengthBytes?: number;\n maxMetadataBytes?: number;\n defaultContentType?: string;\n};\n\nconst normalizePrefix = (prefix: string): string => {\n if (!prefix) {\n return \"\";\n }\n\n const segments = prefix.split(\"/\").filter(Boolean);\n for (const segment of segments) {\n if (segment === \".\" || segment === \"..\") {\n throw new Error(\"Storage key prefix cannot include '.' or '..' segments\");\n }\n }\n\n return segments.join(\"/\");\n};\n\nconst normalizeProvider = (provider: string): string => {\n const normalized = provider.trim();\n if (\n normalized.length === 0 ||\n normalized === \".\" ||\n normalized === \"..\" ||\n normalized.includes(\"/\") ||\n normalized.includes(\"\\\\\")\n ) {\n throw new Error(\"Invalid provider\");\n }\n\n return normalized;\n};\n\nconst resolveMetadataSize = (metadata?: Record<string, unknown> | null) => {\n if (!metadata) {\n return 0;\n }\n\n const serialized = JSON.stringify(metadata);\n return new TextEncoder().encode(serialized).byteLength;\n};\n\nconst divideCeil = (numerator: bigint, denominator: bigint) =>\n (numerator + denominator - 1n) / denominator;\n\nconst resolveEtag = (result: { httpEtag?: string; etag?: string }) =>\n result.httpEtag ?? result.etag;\n\nconst assertSizeMatches = (input: { expected?: bigint | null; actual?: bigint | null }) => {\n if (input.expected === undefined || input.expected === null) {\n return;\n }\n if (input.actual === undefined || input.actual === null) {\n return;\n }\n if (input.expected !== input.actual) {\n throw new Error(\"Upload size mismatch\");\n }\n};\n\nconst normalizeUploadedPart = (\n uploadedPart: unknown,\n fallbackPartNumber: number,\n): R2BindingUploadedPart => {\n if (!uploadedPart || typeof uploadedPart !== \"object\") {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const part = uploadedPart as Partial<R2BindingUploadedPart>;\n if (typeof part.etag !== \"string\" || part.etag.length === 0) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n return {\n etag: part.etag,\n partNumber: typeof part.partNumber === \"number\" ? part.partNumber : fallbackPartNumber,\n };\n};\n\nconst takeBytesFromChunks = (chunks: Uint8Array[], size: number) => {\n const part = new Uint8Array(size);\n let offset = 0;\n\n while (offset < size) {\n const chunk = chunks[0];\n if (!chunk) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const remaining = size - offset;\n if (chunk.byteLength <= remaining) {\n part.set(chunk, offset);\n offset += chunk.byteLength;\n chunks.shift();\n continue;\n }\n\n part.set(chunk.subarray(0, remaining), offset);\n chunks[0] = chunk.subarray(remaining);\n offset += remaining;\n }\n\n return part;\n};\n\nexport function createR2BindingStorageAdapter(\n options: R2BindingStorageAdapterOptions,\n): StorageAdapter {\n const storageKeyPrefix = normalizePrefix(options.storageKeyPrefix ?? \"\");\n const maxStorageKeyLengthBytes =\n options.maxStorageKeyLengthBytes ?? DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES;\n const uploadExpiresInSeconds =\n options.uploadExpiresInSeconds ?? DEFAULT_UPLOAD_EXPIRES_IN_SECONDS;\n const maxSingleUploadBytes = options.maxSingleUploadBytes;\n const maxMultipartUploadBytes = options.maxMultipartUploadBytes;\n const multipartThresholdBytes = options.multipartThresholdBytes ?? maxSingleUploadBytes;\n const multipartPartSizeBytes =\n options.multipartPartSizeBytes ?? DEFAULT_MULTIPART_PART_SIZE_BYTES;\n const minMultipartPartSizeBytes = DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES;\n const maxMultipartPartSizeBytes = DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES;\n const maxMultipartParts = DEFAULT_MAX_MULTIPART_PARTS;\n const maxUploadBytes = options.maxMultipartUploadBytes ?? options.maxSingleUploadBytes;\n const maxMetadataBytes = options.maxMetadataBytes;\n const defaultContentType = options.defaultContentType ?? DEFAULT_CONTENT_TYPE;\n\n if (multipartPartSizeBytes < minMultipartPartSizeBytes) {\n throw new Error(\n `Multipart part size must be at least ${minMultipartPartSizeBytes} bytes for R2 uploads`,\n );\n }\n if (multipartPartSizeBytes > maxMultipartPartSizeBytes) {\n throw new Error(\n `Multipart part size must be at most ${maxMultipartPartSizeBytes} bytes for R2 uploads`,\n );\n }\n\n const resolveStorageKey = (input: { provider: string; fileKey: string }) => {\n const provider = normalizeProvider(input.provider);\n const fileKey = assertFileKey(input.fileKey);\n const storageKey = [storageKeyPrefix, provider, fileKey].filter(Boolean).join(\"/\");\n\n if (storageKey.length === 0) {\n throw new Error(\"Storage key cannot be empty\");\n }\n\n if (new TextEncoder().encode(storageKey).byteLength > maxStorageKeyLengthBytes) {\n throw new Error(\"Storage key exceeds maximum length\");\n }\n\n return storageKey;\n };\n\n const resolveMultipartPartSize = (sizeBytes?: bigint | null) => {\n let partSize = multipartPartSizeBytes;\n\n if (sizeBytes !== undefined && sizeBytes !== null && sizeBytes > 0n) {\n const minPartSizeForLimit = divideCeil(sizeBytes, BigInt(maxMultipartParts));\n if (minPartSizeForLimit > BigInt(maxMultipartPartSizeBytes)) {\n throw new Error(\"Multipart upload exceeds maximum number of parts\");\n }\n if (minPartSizeForLimit > BigInt(partSize)) {\n partSize = Number(minPartSizeForLimit);\n }\n }\n\n if (partSize < minMultipartPartSizeBytes) {\n throw new Error(\"Multipart part size is smaller than minimum allowed size\");\n }\n if (partSize > maxMultipartPartSizeBytes) {\n throw new Error(\"Multipart part size exceeds maximum allowed size\");\n }\n\n return partSize;\n };\n\n // FIXME: This decision currently trusts caller-provided sizeBytes. To enforce\n // maxSingleUploadBytes/maxMultipartUploadBytes for unknown or underreported streams,\n // the proxy path needs real byte counting while reading the body.\n const shouldUseMultipartUpload = (sizeBytes?: bigint | null) => {\n if (sizeBytes === undefined || sizeBytes === null || sizeBytes <= 0n) {\n return false;\n }\n\n if (multipartThresholdBytes !== undefined && sizeBytes >= BigInt(multipartThresholdBytes)) {\n return true;\n }\n\n if (maxSingleUploadBytes !== undefined && sizeBytes > BigInt(maxSingleUploadBytes)) {\n return true;\n }\n\n return false;\n };\n\n const writeMultipartStream = async (input: {\n storageKey: string;\n body: ReadableStream<Uint8Array>;\n contentType?: string | null;\n sizeBytes?: bigint | null;\n partSizeBytes: number;\n }) => {\n const multipartUpload = await options.bucket.createMultipartUpload(input.storageKey, {\n httpMetadata: input.contentType ? { contentType: input.contentType } : undefined,\n });\n\n if (!multipartUpload.uploadId) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const uploadedParts: R2BindingUploadedPart[] = [];\n const reader = input.body.getReader();\n const bufferedChunks: Uint8Array[] = [];\n let bufferedBytes = 0;\n let partNumber = 1;\n let uploadedBytes = 0n;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n if (!value || value.byteLength === 0) {\n continue;\n }\n\n uploadedBytes += BigInt(value.byteLength);\n // TODO: Enforce the effective max upload bytes from uploadedBytes while streaming,\n // instead of relying on input.sizeBytes to reject oversized proxy uploads.\n bufferedChunks.push(value);\n bufferedBytes += value.byteLength;\n\n while (bufferedBytes >= input.partSizeBytes) {\n if (partNumber > maxMultipartParts) {\n throw new Error(\"Multipart upload exceeds maximum number of parts\");\n }\n\n const part = takeBytesFromChunks(bufferedChunks, input.partSizeBytes);\n bufferedBytes -= input.partSizeBytes;\n\n const uploadedPart = await multipartUpload.uploadPart(partNumber, part);\n uploadedParts.push(normalizeUploadedPart(uploadedPart, partNumber));\n partNumber += 1;\n }\n }\n\n assertSizeMatches({ expected: input.sizeBytes, actual: uploadedBytes });\n\n if (bufferedBytes > 0 || uploadedParts.length === 0) {\n if (partNumber > maxMultipartParts) {\n throw new Error(\"Multipart upload exceeds maximum number of parts\");\n }\n\n const finalPart =\n bufferedBytes > 0\n ? takeBytesFromChunks(bufferedChunks, bufferedBytes)\n : new Uint8Array(0);\n bufferedBytes = 0;\n\n const uploadedPart = await multipartUpload.uploadPart(partNumber, finalPart);\n uploadedParts.push(normalizeUploadedPart(uploadedPart, partNumber));\n }\n\n const completed = await multipartUpload.complete(uploadedParts);\n return {\n ...(resolveEtag(completed) ? { etag: resolveEtag(completed) } : {}),\n sizeBytes: uploadedBytes,\n };\n } catch (error) {\n try {\n await multipartUpload.abort();\n } catch {\n // Ignore abort failures to preserve the original storage error.\n }\n throw error;\n } finally {\n reader.releaseLock();\n }\n };\n\n return {\n name: \"r2-binding\",\n capabilities: {\n directUpload: false,\n multipartUpload: true,\n signedDownload: false,\n proxyUpload: true,\n },\n limits: {\n maxSingleUploadBytes,\n maxMultipartUploadBytes,\n minMultipartPartSizeBytes,\n maxMultipartPartSizeBytes,\n maxMultipartParts,\n maxStorageKeyLengthBytes,\n maxMetadataBytes,\n },\n recommendations: {\n uploadExpiresInSeconds,\n signedUrlExpiresInSeconds: options.signedUrlExpiresInSeconds,\n directUploadThresholdBytes: options.directUploadThresholdBytes,\n multipartThresholdBytes,\n multipartPartSizeBytes,\n },\n resolveStorageKey,\n initUpload: async ({ provider, fileKey, sizeBytes, metadata, objectKeyVersionSegment }) => {\n const storageKey = appendStorageObjectKeyVersionSegment(\n resolveStorageKey({ provider, fileKey }),\n objectKeyVersionSegment,\n maxStorageKeyLengthBytes,\n );\n\n if (maxUploadBytes !== undefined && sizeBytes > BigInt(maxUploadBytes)) {\n throw new Error(\"Upload exceeds maximum upload size\");\n }\n\n if (maxMetadataBytes !== undefined && resolveMetadataSize(metadata) > maxMetadataBytes) {\n throw new Error(\"Metadata exceeds maximum size\");\n }\n\n return {\n strategy: \"proxy\",\n storageKey,\n expiresAt: new Date(Date.now() + uploadExpiresInSeconds * 1000),\n };\n },\n writeStream: async ({ storageKey, body, contentType, sizeBytes }) => {\n if (\n maxUploadBytes !== undefined &&\n sizeBytes !== undefined &&\n sizeBytes !== null &&\n sizeBytes > BigInt(maxUploadBytes)\n ) {\n throw new Error(\"Upload exceeds maximum upload size\");\n }\n\n if (shouldUseMultipartUpload(sizeBytes)) {\n const partSizeBytes = resolveMultipartPartSize(sizeBytes);\n return writeMultipartStream({\n storageKey,\n body,\n contentType,\n sizeBytes,\n partSizeBytes,\n });\n }\n\n // TODO: Wrap the single-put body in a counting/limiting stream so unknown or\n // underreported sizeBytes cannot bypass configured max upload limits.\n const result = await options.bucket.put(storageKey, body, {\n httpMetadata: contentType ? { contentType } : undefined,\n });\n\n const size = typeof result.size === \"number\" ? BigInt(result.size) : (sizeBytes ?? undefined);\n assertSizeMatches({ expected: sizeBytes, actual: size });\n const etag = resolveEtag(result);\n\n return {\n ...(etag ? { etag } : {}),\n ...(size !== undefined ? { sizeBytes: size } : {}),\n };\n },\n deleteObject: async ({ storageKey }) => {\n await options.bucket.delete(storageKey);\n },\n getDownloadStream: async ({ storageKey }) => {\n const object = await options.bucket.get(storageKey);\n if (!object || !object.body) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const headers = new Headers();\n headers.set(\"Content-Type\", object.httpMetadata?.contentType || defaultContentType);\n headers.set(\"ETag\", object.httpEtag);\n headers.set(\"Content-Length\", object.size.toString());\n\n return new Response(object.body, { headers });\n },\n };\n}\n\nconst isR2BindingBucket = (candidate: unknown): candidate is R2BindingBucket => {\n if (!candidate || typeof candidate !== \"object\") {\n return false;\n }\n\n const bucket = candidate as Partial<R2BindingBucket>;\n return (\n typeof bucket.put === \"function\" &&\n typeof bucket.get === \"function\" &&\n typeof bucket.delete === \"function\" &&\n typeof bucket.createMultipartUpload === \"function\"\n );\n};\n\nexport const resolveR2BindingBucket = (\n bindings: Record<string, unknown>,\n bindingName: string,\n): R2BindingBucket => {\n const candidate = bindings[bindingName];\n if (!isR2BindingBucket(candidate)) {\n throw new Error(`Upload R2 bucket binding '${bindingName}' is not configured.`);\n }\n return candidate;\n};\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FileKey } from "../file-key.js";
|
|
2
2
|
import { StorageAdapterCapabilities, StorageAdapterRecommendations, UploadChecksum } from "./types.js";
|
|
3
3
|
import { S3CompatibleStorageAdapterOptions } from "./s3.js";
|
|
4
4
|
|
|
@@ -20,16 +20,17 @@ declare function createR2StorageAdapter(options: R2StorageAdapterOptions): {
|
|
|
20
20
|
capabilities: StorageAdapterCapabilities;
|
|
21
21
|
recommendations?: StorageAdapterRecommendations;
|
|
22
22
|
resolveStorageKey(input: {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
provider: string;
|
|
24
|
+
fileKey: FileKey;
|
|
25
25
|
}): string;
|
|
26
26
|
initUpload(input: {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
provider: string;
|
|
28
|
+
fileKey: FileKey;
|
|
29
29
|
sizeBytes: bigint;
|
|
30
30
|
contentType: string;
|
|
31
31
|
checksum?: UploadChecksum | null;
|
|
32
32
|
metadata?: Record<string, unknown> | null;
|
|
33
|
+
objectKeyVersionSegment?: string;
|
|
33
34
|
}): Promise<{
|
|
34
35
|
strategy: "direct-single" | "direct-multipart" | "proxy";
|
|
35
36
|
storageKey: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r2.d.ts","names":[],"sources":["../../../src/storage/r2.ts"],"sourcesContent":[],"mappings":";;;;;KAIY,uBAAA,GAA0B,KACpC;;;iBAMc,sBAAA,UAAgC;EAPpC,IAAA,EAAA,MAAA;EAAuB,MAAA,EAAA;IACjC,gBAAA,EAAA,MAAA;IADoC,oBAAA,CAAA,EAAA,MAAA;IAAI,uBAAA,CAAA,EAAA,MAAA;IAO1B,yBAAsB,CAAA,EAAA,MAAA;IAAA,yBAAA,CAAA,EAAA,MAAA;IAAU,iBAAA,CAAA,EAAA,MAAA;IAAuB,wBAAA,CAAA,EAAA,MAAA;;gBAAA
|
|
1
|
+
{"version":3,"file":"r2.d.ts","names":[],"sources":["../../../src/storage/r2.ts"],"sourcesContent":[],"mappings":";;;;;KAIY,uBAAA,GAA0B,KACpC;;;iBAMc,sBAAA,UAAgC;EAPpC,IAAA,EAAA,MAAA;EAAuB,MAAA,EAAA;IACjC,gBAAA,EAAA,MAAA;IADoC,oBAAA,CAAA,EAAA,MAAA;IAAI,uBAAA,CAAA,EAAA,MAAA;IAO1B,yBAAsB,CAAA,EAAA,MAAA;IAAA,yBAAA,CAAA,EAAA,MAAA;IAAU,iBAAA,CAAA,EAAA,MAAA;IAAuB,wBAAA,CAAA,EAAA,MAAA;;gBAAA;;;IAgBq0B,QAAA,EAAA,MAAA;;MAAuS,MAAA;;IAAsR,QAAA,EAAA,MAAA;;;;;eAA7jB;;;;;IAAqqD,eAAA,CAAA,EAAA,MAAA;;;;oBAA93C;;;;;;;;;;cAAsR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAwmC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3.d.ts","names":[],"sources":["../../../src/storage/s3.ts"],"sourcesContent":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"s3.d.ts","names":[],"sources":["../../../src/storage/s3.ts"],"sourcesContent":[],"mappings":";;;KAqBY,aAAA;;EAAA,GAAA,EAAA,MAAA;EAAa,OAAA,CAAA,EAGb,MAHa,CAAA,MAAA,EAAA,MAAA,CAAA;MAGb,CAAA,EAAA,MAAA,GACM,UADN,GAAA,IAAA;kBACM,CAAA,EAAA,MAAA;CAAU;AAIhB,KAAA,QAAA,GAAQ;EAAA,IAAA,EAAA,CAAA,KAAA,EACJ,aADI,EAAA,GACc,OADd,CAAA;IACJ,GAAA,EAAA,MAAA;IAAkD,OAAA,EAAA,MAAA,CAAA,MAAA,EAAA,MAAA,CAAA;;SAC/C,EAAA,CAAA,KAAA,EAAA,aAAA,EAAA,GAAkB,OAAlB,CAAA;IAAmD,GAAA,EAAA,MAAA;IAAjC,OAAA,CAAA,EAAiC,MAAjC,CAAA,MAAA,EAAA,MAAA,CAAA;EAAO,CAAA,CAAA;AAG5C,CAAA;AA4RgB,KA5RJ,iCAAA,GA4RoC;EAAA,MAAA,EAAA,MAAA;UACrC,EAAA,MAAA;QACR,EA3RO,QA2RP;EAAc,gBAAA,CAAA,EAAA,MAAA;;;;;;;;;;;;;;;iBAFD,gCAAA,UACL,oCACR"}
|
package/dist/node/storage/s3.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assertFileKey } from "../file-key.js";
|
|
2
|
+
import { appendStorageObjectKeyVersionSegment } from "./object-key.js";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
|
|
4
5
|
//#region src/storage/s3.ts
|
|
@@ -21,9 +22,10 @@ const normalizePrefix = (prefix) => {
|
|
|
21
22
|
for (const segment of segments) if (segment === "." || segment === "..") throw new Error("Storage key prefix cannot include '.' or '..' segments");
|
|
22
23
|
return segments.join("/");
|
|
23
24
|
};
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
25
|
+
const normalizeProvider = (provider) => {
|
|
26
|
+
const normalized = provider.trim();
|
|
27
|
+
if (normalized.length === 0 || normalized === "." || normalized === ".." || normalized.includes("/") || normalized.includes("\\")) throw new Error("Invalid provider");
|
|
28
|
+
return normalized;
|
|
27
29
|
};
|
|
28
30
|
const encodePathSegments = (value) => value.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
29
31
|
const joinPathSegments = (...segments) => {
|
|
@@ -149,7 +151,11 @@ function createS3CompatibleStorageAdapter(options) {
|
|
|
149
151
|
assertExpiresInSeconds(uploadExpiresInSeconds);
|
|
150
152
|
assertExpiresInSeconds(signedUrlExpiresInSeconds);
|
|
151
153
|
const resolveStorageKey = (input) => {
|
|
152
|
-
const storageKey = [
|
|
154
|
+
const storageKey = [
|
|
155
|
+
storageKeyPrefix,
|
|
156
|
+
normalizeProvider(input.provider),
|
|
157
|
+
assertFileKey(input.fileKey)
|
|
158
|
+
].filter(Boolean).join("/");
|
|
153
159
|
if (storageKey.length === 0) throw new Error("Storage key cannot be empty");
|
|
154
160
|
if (Buffer.byteLength(storageKey, "utf8") > maxStorageKeyLengthBytes) throw new Error("Storage key exceeds maximum length");
|
|
155
161
|
return storageKey;
|
|
@@ -197,12 +203,12 @@ function createS3CompatibleStorageAdapter(options) {
|
|
|
197
203
|
multipartPartSizeBytes
|
|
198
204
|
},
|
|
199
205
|
resolveStorageKey,
|
|
200
|
-
initUpload: async ({
|
|
206
|
+
initUpload: async ({ provider, fileKey, sizeBytes, contentType, metadata, checksum, objectKeyVersionSegment }) => {
|
|
201
207
|
validateMetadata(metadata);
|
|
202
|
-
const storageKey = resolveStorageKey({
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
});
|
|
208
|
+
const storageKey = appendStorageObjectKeyVersionSegment(resolveStorageKey({
|
|
209
|
+
provider,
|
|
210
|
+
fileKey
|
|
211
|
+
}), objectKeyVersionSegment, maxStorageKeyLengthBytes);
|
|
206
212
|
const expiresAt = new Date(Date.now() + uploadExpiresInSeconds * 1e3);
|
|
207
213
|
const sizeNumber = Number(sizeBytes);
|
|
208
214
|
if (Number.isNaN(sizeNumber) || sizeNumber < 0) throw new Error("Upload size must be a non-negative number");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3.js","names":["path","signed"],"sources":["../../../src/storage/s3.ts"],"sourcesContent":["import { encodeFileKey, type FileKeyEncoded, type FileKeyParts } from \"../keys\";\nimport { createHash } from \"node:crypto\";\nimport type { StorageAdapter, UploadChecksum } from \"./types\";\n\nconst BYTES_IN_MIB = 1024 * 1024;\nconst BYTES_IN_GIB = 1024 * BYTES_IN_MIB;\nconst BYTES_IN_TIB = 1024 * BYTES_IN_GIB;\n\nconst DEFAULT_MAX_SINGLE_UPLOAD_BYTES = 5 * BYTES_IN_GIB;\nconst DEFAULT_MAX_MULTIPART_UPLOAD_BYTES = 5 * BYTES_IN_TIB;\nconst DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_MIB;\nconst DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_GIB;\nconst DEFAULT_MAX_MULTIPART_PARTS = 10_000;\nconst DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES = 1024;\nconst DEFAULT_UPLOAD_EXPIRES_IN_SECONDS = 60 * 60;\nconst DEFAULT_SIGNED_URL_EXPIRES_IN_SECONDS = 60 * 60;\nconst DEFAULT_MULTIPART_PART_SIZE_BYTES = 8 * BYTES_IN_MIB;\nconst MAX_PRESIGNED_EXPIRES_IN_SECONDS = 60 * 60 * 24 * 7;\n\nexport type S3SignerInput = {\n method: string;\n url: string;\n headers?: Record<string, string>;\n body?: string | Uint8Array | null;\n expiresInSeconds?: number;\n};\n\nexport type S3Signer = {\n sign: (input: S3SignerInput) => Promise<{ url: string; headers: Record<string, string> }>;\n presign: (input: S3SignerInput) => Promise<{ url: string; headers?: Record<string, string> }>;\n};\n\nexport type S3CompatibleStorageAdapterOptions = {\n bucket: string;\n endpoint: string;\n signer: S3Signer;\n storageKeyPrefix?: string;\n pathStyle?: boolean;\n uploadExpiresInSeconds?: number;\n signedUrlExpiresInSeconds?: number;\n directUploadThresholdBytes?: number;\n multipartThresholdBytes?: number;\n multipartPartSizeBytes?: number;\n maxSingleUploadBytes?: number;\n maxMultipartUploadBytes?: number;\n minMultipartPartSizeBytes?: number;\n maxMultipartPartSizeBytes?: number;\n maxMultipartParts?: number;\n maxStorageKeyLengthBytes?: number;\n maxMetadataBytes?: number;\n};\n\nconst normalizePrefix = (prefix: string): string => {\n if (!prefix) {\n return \"\";\n }\n\n const segments = prefix.split(\"/\").filter(Boolean);\n for (const segment of segments) {\n if (segment === \".\" || segment === \"..\") {\n throw new Error(\"Storage key prefix cannot include '.' or '..' segments\");\n }\n }\n\n return segments.join(\"/\");\n};\n\nconst resolveStorageKeyFromParts = (input: {\n fileKey: FileKeyEncoded;\n fileKeyParts: FileKeyParts;\n}): string => {\n const encoded = input.fileKeyParts.length > 0 ? encodeFileKey(input.fileKeyParts) : input.fileKey;\n return encoded.length > 0 ? encoded.split(\".\").join(\"/\") : \"\";\n};\n\nconst encodePathSegments = (value: string) =>\n value\n .split(\"/\")\n .map((segment) => encodeURIComponent(segment))\n .join(\"/\");\n\nconst joinPathSegments = (...segments: (string | undefined)[]) => {\n const trimmed = segments.filter(Boolean).map((segment) => segment!.replace(/^\\/+|\\/+$/g, \"\"));\n return trimmed.join(\"/\");\n};\n\nconst parseHex = (value: string, expectedBytes: number) => {\n const trimmed = value.trim();\n if (!/^[0-9a-f]+$/i.test(trimmed) || trimmed.length !== expectedBytes * 2) {\n return null;\n }\n return Buffer.from(trimmed, \"hex\");\n};\n\nconst parseBase64 = (value: string, expectedBytes: number) => {\n const trimmed = value.trim();\n if (!/^[A-Za-z0-9+/=]+$/.test(trimmed)) {\n return null;\n }\n const buf = Buffer.from(trimmed, \"base64\");\n if (buf.length !== expectedBytes) {\n return null;\n }\n return buf;\n};\n\nconst normalizeChecksum = (checksum?: UploadChecksum | null) => {\n if (!checksum) {\n return null;\n }\n\n const expectedBytes = checksum.algo === \"md5\" ? 16 : 32;\n const raw = checksum.value ?? \"\";\n const buf = parseHex(raw, expectedBytes) ?? parseBase64(raw, expectedBytes);\n if (!buf) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n\n return {\n algo: checksum.algo,\n hex: buf.toString(\"hex\"),\n base64: buf.toString(\"base64\"),\n };\n};\n\nconst resolveChecksumHeaders = (checksum?: UploadChecksum | null): Record<string, string> => {\n const normalized = normalizeChecksum(checksum);\n if (!normalized) {\n return {};\n }\n\n if (normalized.algo === \"md5\") {\n return { \"Content-MD5\": normalized.base64 };\n }\n\n return { \"x-amz-checksum-sha256\": normalized.base64 };\n};\n\nconst normalizeEtag = (etag: string | null) => {\n if (!etag) {\n return null;\n }\n\n const trimmed = etag.trim().replace(/^W\\//, \"\").replace(/^\"|\"$/g, \"\");\n if (!/^[0-9a-f]{32}$/i.test(trimmed)) {\n return null;\n }\n\n return trimmed.toLowerCase();\n};\n\nconst computeChecksumFromResponse = async (\n response: Response,\n algo: \"md5\" | \"sha256\",\n): Promise<string> => {\n if (!response.body) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const hash = createHash(algo);\n const reader = response.body.getReader();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n if (value) {\n hash.update(Buffer.from(value));\n }\n }\n\n return hash.digest(\"hex\");\n};\n\nconst parseContentLength = (value: string | null) => {\n if (!value) {\n return null;\n }\n if (!/^\\d+$/.test(value)) {\n return null;\n }\n return BigInt(value);\n};\n\nconst getHeaderValue = (headers: Headers, name: string) => {\n const direct = headers.get(name);\n if (direct !== null) {\n return direct;\n }\n const target = name.toLowerCase();\n for (const [key, value] of headers.entries()) {\n if (key.toLowerCase() === target) {\n return value;\n }\n }\n return null;\n};\n\nconst buildObjectUrl = (input: {\n endpoint: string;\n bucket: string;\n storageKey: string;\n pathStyle: boolean;\n}) => {\n const url = new URL(input.endpoint);\n const encodedKey = encodePathSegments(input.storageKey);\n const basePath = url.pathname === \"/\" ? \"\" : url.pathname.replace(/\\/+$/g, \"\");\n\n if (input.pathStyle) {\n const path = joinPathSegments(basePath, input.bucket, encodedKey);\n url.pathname = path ? `/${path}` : \"/\";\n return url;\n }\n\n url.hostname = `${input.bucket}.${url.hostname}`;\n const path = joinPathSegments(basePath, encodedKey);\n url.pathname = path ? `/${path}` : \"/\";\n return url;\n};\n\nconst assertExpiresInSeconds = (value: number) => {\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(\"Expiration must be a positive number of seconds\");\n }\n if (value > MAX_PRESIGNED_EXPIRES_IN_SECONDS) {\n throw new Error(\"Expiration exceeds SigV4 maximum of 7 days\");\n }\n};\n\nconst resolveMetadataSize = (metadata?: Record<string, unknown> | null) => {\n if (!metadata) {\n return 0;\n }\n\n const serialized = JSON.stringify(metadata);\n return Buffer.byteLength(serialized, \"utf8\");\n};\n\nconst parseUploadId = (payload: string) => {\n const match = payload.match(/<UploadId>([^<]+)<\\/UploadId>/);\n if (!match) {\n return null;\n }\n\n return match[1];\n};\n\nconst escapeXml = (value: string) =>\n value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n\nconst buildCompleteMultipartBody = (parts: { partNumber: number; etag: string }[]) => {\n const sorted = [...parts].sort((a, b) => a.partNumber - b.partNumber);\n const entries = sorted\n .map(\n (part) =>\n `<Part><PartNumber>${part.partNumber}</PartNumber><ETag>${escapeXml(\n part.etag,\n )}</ETag></Part>`,\n )\n .join(\"\");\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?><CompleteMultipartUpload>${entries}</CompleteMultipartUpload>`;\n};\n\nconst resolveMultipartPartSize = (input: {\n sizeBytes: bigint;\n desiredPartSizeBytes: number;\n minPartSizeBytes: number;\n maxPartSizeBytes: number;\n maxParts: number;\n}) => {\n let partSize = input.desiredPartSizeBytes;\n\n if (partSize < input.minPartSizeBytes) {\n partSize = input.minPartSizeBytes;\n }\n\n if (partSize > input.maxPartSizeBytes) {\n throw new Error(\"Multipart part size exceeds maximum allowed size\");\n }\n\n const sizeNumber = Number(input.sizeBytes);\n const minimumPartSizeForLimit = Math.ceil(sizeNumber / input.maxParts);\n\n if (minimumPartSizeForLimit > partSize) {\n partSize = minimumPartSizeForLimit;\n }\n\n if (partSize < input.minPartSizeBytes) {\n partSize = input.minPartSizeBytes;\n }\n\n if (partSize > input.maxPartSizeBytes) {\n throw new Error(\"Multipart part size exceeds maximum allowed size\");\n }\n\n const partCount = Math.ceil(sizeNumber / partSize);\n if (partCount > input.maxParts) {\n throw new Error(\"Multipart upload exceeds maximum number of parts\");\n }\n\n return partSize;\n};\n\nexport function createS3CompatibleStorageAdapter(\n options: S3CompatibleStorageAdapterOptions,\n): StorageAdapter {\n const storageKeyPrefix = normalizePrefix(options.storageKeyPrefix ?? \"\");\n const pathStyle = options.pathStyle ?? false;\n const maxStorageKeyLengthBytes =\n options.maxStorageKeyLengthBytes ?? DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES;\n const maxSingleUploadBytes = options.maxSingleUploadBytes ?? DEFAULT_MAX_SINGLE_UPLOAD_BYTES;\n const maxMultipartUploadBytes =\n options.maxMultipartUploadBytes ?? DEFAULT_MAX_MULTIPART_UPLOAD_BYTES;\n const minMultipartPartSizeBytes =\n options.minMultipartPartSizeBytes ?? DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES;\n const maxMultipartPartSizeBytes =\n options.maxMultipartPartSizeBytes ?? DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES;\n const maxMultipartParts = options.maxMultipartParts ?? DEFAULT_MAX_MULTIPART_PARTS;\n const uploadExpiresInSeconds =\n options.uploadExpiresInSeconds ?? DEFAULT_UPLOAD_EXPIRES_IN_SECONDS;\n const signedUrlExpiresInSeconds =\n options.signedUrlExpiresInSeconds ?? DEFAULT_SIGNED_URL_EXPIRES_IN_SECONDS;\n const directUploadThresholdBytes = options.directUploadThresholdBytes ?? maxSingleUploadBytes;\n const multipartThresholdBytes = options.multipartThresholdBytes ?? maxSingleUploadBytes;\n const multipartPartSizeBytes =\n options.multipartPartSizeBytes ?? DEFAULT_MULTIPART_PART_SIZE_BYTES;\n\n assertExpiresInSeconds(uploadExpiresInSeconds);\n assertExpiresInSeconds(signedUrlExpiresInSeconds);\n\n const resolveStorageKey = (input: { fileKey: FileKeyEncoded; fileKeyParts: FileKeyParts }) => {\n const baseKey = resolveStorageKeyFromParts(input);\n const storageKey = [storageKeyPrefix, baseKey].filter(Boolean).join(\"/\");\n\n if (storageKey.length === 0) {\n throw new Error(\"Storage key cannot be empty\");\n }\n\n if (Buffer.byteLength(storageKey, \"utf8\") > maxStorageKeyLengthBytes) {\n throw new Error(\"Storage key exceeds maximum length\");\n }\n\n return storageKey;\n };\n\n const buildUrl = (storageKey: string, queryParams: Record<string, string | undefined> = {}) => {\n const url = buildObjectUrl({\n endpoint: options.endpoint,\n bucket: options.bucket,\n storageKey,\n pathStyle,\n });\n\n for (const [key, value] of Object.entries(queryParams)) {\n if (value === undefined) {\n continue;\n }\n url.searchParams.set(key, value);\n }\n\n return url;\n };\n\n const validateMetadata = (metadata?: Record<string, unknown> | null) => {\n if (!metadata) {\n return;\n }\n\n if (options.maxMetadataBytes === undefined) {\n return;\n }\n\n if (resolveMetadataSize(metadata) > options.maxMetadataBytes) {\n throw new Error(\"Metadata exceeds maximum size\");\n }\n };\n\n return {\n name: \"s3\",\n capabilities: {\n directUpload: true,\n multipartUpload: true,\n signedDownload: true,\n proxyUpload: false,\n },\n limits: {\n maxSingleUploadBytes,\n maxMultipartUploadBytes,\n minMultipartPartSizeBytes,\n maxMultipartPartSizeBytes,\n maxMultipartParts,\n maxStorageKeyLengthBytes,\n maxMetadataBytes: options.maxMetadataBytes,\n },\n recommendations: {\n uploadExpiresInSeconds,\n signedUrlExpiresInSeconds,\n directUploadThresholdBytes,\n multipartThresholdBytes,\n multipartPartSizeBytes,\n },\n resolveStorageKey,\n initUpload: async ({ fileKey, fileKeyParts, sizeBytes, contentType, metadata, checksum }) => {\n validateMetadata(metadata);\n const storageKey = resolveStorageKey({ fileKey, fileKeyParts });\n\n const expiresAt = new Date(Date.now() + uploadExpiresInSeconds * 1000);\n const sizeNumber = Number(sizeBytes);\n\n if (Number.isNaN(sizeNumber) || sizeNumber < 0) {\n throw new Error(\"Upload size must be a non-negative number\");\n }\n\n const canMultipart = sizeNumber <= maxMultipartUploadBytes;\n const canSingle = sizeNumber <= maxSingleUploadBytes;\n\n if (!canMultipart) {\n throw new Error(\"Upload exceeds maximum multipart size\");\n }\n\n const prefersMultipart = sizeNumber >= multipartThresholdBytes;\n const canUseSingle = canSingle && sizeNumber <= directUploadThresholdBytes;\n\n if (prefersMultipart || !canUseSingle) {\n if (!canMultipart) {\n throw new Error(\"Upload exceeds maximum multipart size\");\n }\n\n const partSizeBytes = resolveMultipartPartSize({\n sizeBytes,\n desiredPartSizeBytes: multipartPartSizeBytes,\n minPartSizeBytes: minMultipartPartSizeBytes,\n maxPartSizeBytes: maxMultipartPartSizeBytes,\n maxParts: maxMultipartParts,\n });\n\n const createUrl = buildUrl(storageKey, { uploads: \"\" });\n const signed = await options.signer.sign({\n method: \"POST\",\n url: createUrl.toString(),\n headers: {\n \"Content-Type\": contentType,\n },\n });\n\n const response = await fetch(signed.url, {\n method: \"POST\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create multipart upload (${response.status})`);\n }\n\n const payload = await response.text();\n const uploadId = parseUploadId(payload);\n if (!uploadId) {\n throw new Error(\"Failed to parse multipart upload id\");\n }\n\n return {\n strategy: \"direct-multipart\",\n storageKey,\n storageUploadId: uploadId,\n partSizeBytes,\n expiresAt,\n };\n }\n\n if (!canSingle) {\n throw new Error(\"Upload exceeds maximum single upload size\");\n }\n\n const uploadUrl = buildUrl(storageKey);\n const checksumHeaders = resolveChecksumHeaders(checksum);\n const signed = await options.signer.presign({\n method: \"PUT\",\n url: uploadUrl.toString(),\n headers: {\n \"Content-Type\": contentType,\n ...checksumHeaders,\n },\n expiresInSeconds: uploadExpiresInSeconds,\n });\n\n return {\n strategy: \"direct-single\",\n storageKey,\n expiresAt,\n uploadUrl: signed.url,\n uploadHeaders: signed.headers,\n };\n },\n getPartUploadUrls: async ({ storageKey, storageUploadId, partNumbers }) => {\n for (const partNumber of partNumbers) {\n if (partNumber < 1 || partNumber > maxMultipartParts) {\n throw new Error(\"INVALID_REQUEST\");\n }\n }\n\n const urls = await Promise.all(\n partNumbers.map(async (partNumber) => {\n const url = buildUrl(storageKey, {\n partNumber: String(partNumber),\n uploadId: storageUploadId,\n });\n const signed = await options.signer.presign({\n method: \"PUT\",\n url: url.toString(),\n expiresInSeconds: uploadExpiresInSeconds,\n });\n\n return {\n partNumber,\n url: signed.url,\n headers: signed.headers,\n };\n }),\n );\n\n return urls;\n },\n completeMultipartUpload: async ({ storageKey, storageUploadId, parts }) => {\n const url = buildUrl(storageKey, { uploadId: storageUploadId });\n const body = buildCompleteMultipartBody(parts);\n const signed = await options.signer.sign({\n method: \"POST\",\n url: url.toString(),\n headers: {\n \"Content-Type\": \"application/xml\",\n },\n body,\n });\n\n const response = await fetch(signed.url, {\n method: \"POST\",\n headers: signed.headers,\n body,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to complete multipart upload (${response.status})`);\n }\n\n const etag = response.headers.get(\"ETag\") ?? undefined;\n return { etag };\n },\n abortMultipartUpload: async ({ storageKey, storageUploadId }) => {\n const url = buildUrl(storageKey, { uploadId: storageUploadId });\n const signed = await options.signer.sign({\n method: \"DELETE\",\n url: url.toString(),\n });\n\n const response = await fetch(signed.url, {\n method: \"DELETE\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to abort multipart upload (${response.status})`);\n }\n },\n finalizeUpload: async ({ storageKey, expectedSizeBytes, checksum }) => {\n const url = buildUrl(storageKey);\n const signed = await options.signer.sign({\n method: \"HEAD\",\n url: url.toString(),\n });\n\n const response = await fetch(signed.url, {\n method: \"HEAD\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const sizeHeader = getHeaderValue(response.headers, \"content-length\");\n const sizeBytes = parseContentLength(sizeHeader);\n if (sizeBytes === null) {\n throw new Error(\"STORAGE_ERROR\");\n }\n if (sizeBytes !== expectedSizeBytes) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n\n const normalized = normalizeChecksum(checksum);\n if (normalized) {\n if (normalized.algo === \"md5\") {\n const etag = normalizeEtag(getHeaderValue(response.headers, \"etag\"));\n if (etag) {\n if (etag !== normalized.hex) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n } else {\n const signedGet = await options.signer.sign({\n method: \"GET\",\n url: buildUrl(storageKey).toString(),\n });\n const getResponse = await fetch(signedGet.url, {\n method: \"GET\",\n headers: signedGet.headers,\n });\n if (!getResponse.ok) {\n throw new Error(\"STORAGE_ERROR\");\n }\n const computed = await computeChecksumFromResponse(getResponse, \"md5\");\n if (computed !== normalized.hex) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n }\n } else {\n const checksumHeader = getHeaderValue(response.headers, \"x-amz-checksum-sha256\");\n if (checksumHeader) {\n const remote = parseBase64(checksumHeader, 32);\n if (!remote || remote.toString(\"base64\") !== normalized.base64) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n } else {\n const signedGet = await options.signer.sign({\n method: \"GET\",\n url: buildUrl(storageKey).toString(),\n });\n const getResponse = await fetch(signedGet.url, {\n method: \"GET\",\n headers: signedGet.headers,\n });\n if (!getResponse.ok) {\n throw new Error(\"STORAGE_ERROR\");\n }\n const computed = await computeChecksumFromResponse(getResponse, \"sha256\");\n if (computed !== normalized.hex) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n }\n }\n }\n\n const etag = getHeaderValue(response.headers, \"etag\");\n return { sizeBytes, etag: etag ?? undefined };\n },\n deleteObject: async ({ storageKey }) => {\n const url = buildUrl(storageKey);\n const signed = await options.signer.sign({\n method: \"DELETE\",\n url: url.toString(),\n });\n\n const response = await fetch(signed.url, {\n method: \"DELETE\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete object (${response.status})`);\n }\n },\n getDownloadUrl: async ({ storageKey, expiresInSeconds, contentDisposition, contentType }) => {\n assertExpiresInSeconds(expiresInSeconds);\n\n const url = buildUrl(storageKey, {\n \"response-content-disposition\": contentDisposition,\n \"response-content-type\": contentType,\n });\n\n const signed = await options.signer.presign({\n method: \"GET\",\n url: url.toString(),\n expiresInSeconds,\n });\n\n return {\n url: signed.url,\n headers: signed.headers,\n expiresAt: new Date(Date.now() + expiresInSeconds * 1000),\n };\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"s3.js","names":["path","signed"],"sources":["../../../src/storage/s3.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\nimport { assertFileKey } from \"../file-key\";\nimport { appendStorageObjectKeyVersionSegment } from \"./object-key\";\nimport type { StorageAdapter, UploadChecksum } from \"./types\";\n\nconst BYTES_IN_MIB = 1024 * 1024;\nconst BYTES_IN_GIB = 1024 * BYTES_IN_MIB;\nconst BYTES_IN_TIB = 1024 * BYTES_IN_GIB;\n\nconst DEFAULT_MAX_SINGLE_UPLOAD_BYTES = 5 * BYTES_IN_GIB;\nconst DEFAULT_MAX_MULTIPART_UPLOAD_BYTES = 5 * BYTES_IN_TIB;\nconst DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_MIB;\nconst DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES = 5 * BYTES_IN_GIB;\nconst DEFAULT_MAX_MULTIPART_PARTS = 10_000;\nconst DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES = 1024;\nconst DEFAULT_UPLOAD_EXPIRES_IN_SECONDS = 60 * 60;\nconst DEFAULT_SIGNED_URL_EXPIRES_IN_SECONDS = 60 * 60;\nconst DEFAULT_MULTIPART_PART_SIZE_BYTES = 8 * BYTES_IN_MIB;\nconst MAX_PRESIGNED_EXPIRES_IN_SECONDS = 60 * 60 * 24 * 7;\n\nexport type S3SignerInput = {\n method: string;\n url: string;\n headers?: Record<string, string>;\n body?: string | Uint8Array | null;\n expiresInSeconds?: number;\n};\n\nexport type S3Signer = {\n sign: (input: S3SignerInput) => Promise<{ url: string; headers: Record<string, string> }>;\n presign: (input: S3SignerInput) => Promise<{ url: string; headers?: Record<string, string> }>;\n};\n\nexport type S3CompatibleStorageAdapterOptions = {\n bucket: string;\n endpoint: string;\n signer: S3Signer;\n storageKeyPrefix?: string;\n pathStyle?: boolean;\n uploadExpiresInSeconds?: number;\n signedUrlExpiresInSeconds?: number;\n directUploadThresholdBytes?: number;\n multipartThresholdBytes?: number;\n multipartPartSizeBytes?: number;\n maxSingleUploadBytes?: number;\n maxMultipartUploadBytes?: number;\n minMultipartPartSizeBytes?: number;\n maxMultipartPartSizeBytes?: number;\n maxMultipartParts?: number;\n maxStorageKeyLengthBytes?: number;\n maxMetadataBytes?: number;\n};\n\nconst normalizePrefix = (prefix: string): string => {\n if (!prefix) {\n return \"\";\n }\n\n const segments = prefix.split(\"/\").filter(Boolean);\n for (const segment of segments) {\n if (segment === \".\" || segment === \"..\") {\n throw new Error(\"Storage key prefix cannot include '.' or '..' segments\");\n }\n }\n\n return segments.join(\"/\");\n};\n\nconst normalizeProvider = (provider: string): string => {\n const normalized = provider.trim();\n if (\n normalized.length === 0 ||\n normalized === \".\" ||\n normalized === \"..\" ||\n normalized.includes(\"/\") ||\n normalized.includes(\"\\\\\")\n ) {\n throw new Error(\"Invalid provider\");\n }\n\n return normalized;\n};\n\nconst encodePathSegments = (value: string) =>\n value\n .split(\"/\")\n .map((segment) => encodeURIComponent(segment))\n .join(\"/\");\n\nconst joinPathSegments = (...segments: (string | undefined)[]) => {\n const trimmed = segments.filter(Boolean).map((segment) => segment!.replace(/^\\/+|\\/+$/g, \"\"));\n return trimmed.join(\"/\");\n};\n\nconst parseHex = (value: string, expectedBytes: number) => {\n const trimmed = value.trim();\n if (!/^[0-9a-f]+$/i.test(trimmed) || trimmed.length !== expectedBytes * 2) {\n return null;\n }\n return Buffer.from(trimmed, \"hex\");\n};\n\nconst parseBase64 = (value: string, expectedBytes: number) => {\n const trimmed = value.trim();\n if (!/^[A-Za-z0-9+/=]+$/.test(trimmed)) {\n return null;\n }\n const buf = Buffer.from(trimmed, \"base64\");\n if (buf.length !== expectedBytes) {\n return null;\n }\n return buf;\n};\n\nconst normalizeChecksum = (checksum?: UploadChecksum | null) => {\n if (!checksum) {\n return null;\n }\n\n const expectedBytes = checksum.algo === \"md5\" ? 16 : 32;\n const raw = checksum.value ?? \"\";\n const buf = parseHex(raw, expectedBytes) ?? parseBase64(raw, expectedBytes);\n if (!buf) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n\n return {\n algo: checksum.algo,\n hex: buf.toString(\"hex\"),\n base64: buf.toString(\"base64\"),\n };\n};\n\nconst resolveChecksumHeaders = (checksum?: UploadChecksum | null): Record<string, string> => {\n const normalized = normalizeChecksum(checksum);\n if (!normalized) {\n return {};\n }\n\n if (normalized.algo === \"md5\") {\n return { \"Content-MD5\": normalized.base64 };\n }\n\n return { \"x-amz-checksum-sha256\": normalized.base64 };\n};\n\nconst normalizeEtag = (etag: string | null) => {\n if (!etag) {\n return null;\n }\n\n const trimmed = etag.trim().replace(/^W\\//, \"\").replace(/^\"|\"$/g, \"\");\n if (!/^[0-9a-f]{32}$/i.test(trimmed)) {\n return null;\n }\n\n return trimmed.toLowerCase();\n};\n\nconst computeChecksumFromResponse = async (\n response: Response,\n algo: \"md5\" | \"sha256\",\n): Promise<string> => {\n if (!response.body) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const hash = createHash(algo);\n const reader = response.body.getReader();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n if (value) {\n hash.update(Buffer.from(value));\n }\n }\n\n return hash.digest(\"hex\");\n};\n\nconst parseContentLength = (value: string | null) => {\n if (!value) {\n return null;\n }\n if (!/^\\d+$/.test(value)) {\n return null;\n }\n return BigInt(value);\n};\n\nconst getHeaderValue = (headers: Headers, name: string) => {\n const direct = headers.get(name);\n if (direct !== null) {\n return direct;\n }\n const target = name.toLowerCase();\n for (const [key, value] of headers.entries()) {\n if (key.toLowerCase() === target) {\n return value;\n }\n }\n return null;\n};\n\nconst buildObjectUrl = (input: {\n endpoint: string;\n bucket: string;\n storageKey: string;\n pathStyle: boolean;\n}) => {\n const url = new URL(input.endpoint);\n const encodedKey = encodePathSegments(input.storageKey);\n const basePath = url.pathname === \"/\" ? \"\" : url.pathname.replace(/\\/+$/g, \"\");\n\n if (input.pathStyle) {\n const path = joinPathSegments(basePath, input.bucket, encodedKey);\n url.pathname = path ? `/${path}` : \"/\";\n return url;\n }\n\n url.hostname = `${input.bucket}.${url.hostname}`;\n const path = joinPathSegments(basePath, encodedKey);\n url.pathname = path ? `/${path}` : \"/\";\n return url;\n};\n\nconst assertExpiresInSeconds = (value: number) => {\n if (!Number.isFinite(value) || value <= 0) {\n throw new Error(\"Expiration must be a positive number of seconds\");\n }\n if (value > MAX_PRESIGNED_EXPIRES_IN_SECONDS) {\n throw new Error(\"Expiration exceeds SigV4 maximum of 7 days\");\n }\n};\n\nconst resolveMetadataSize = (metadata?: Record<string, unknown> | null) => {\n if (!metadata) {\n return 0;\n }\n\n const serialized = JSON.stringify(metadata);\n return Buffer.byteLength(serialized, \"utf8\");\n};\n\nconst parseUploadId = (payload: string) => {\n const match = payload.match(/<UploadId>([^<]+)<\\/UploadId>/);\n if (!match) {\n return null;\n }\n\n return match[1];\n};\n\nconst escapeXml = (value: string) =>\n value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n\nconst buildCompleteMultipartBody = (parts: { partNumber: number; etag: string }[]) => {\n const sorted = [...parts].sort((a, b) => a.partNumber - b.partNumber);\n const entries = sorted\n .map(\n (part) =>\n `<Part><PartNumber>${part.partNumber}</PartNumber><ETag>${escapeXml(\n part.etag,\n )}</ETag></Part>`,\n )\n .join(\"\");\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?><CompleteMultipartUpload>${entries}</CompleteMultipartUpload>`;\n};\n\nconst resolveMultipartPartSize = (input: {\n sizeBytes: bigint;\n desiredPartSizeBytes: number;\n minPartSizeBytes: number;\n maxPartSizeBytes: number;\n maxParts: number;\n}) => {\n let partSize = input.desiredPartSizeBytes;\n\n if (partSize < input.minPartSizeBytes) {\n partSize = input.minPartSizeBytes;\n }\n\n if (partSize > input.maxPartSizeBytes) {\n throw new Error(\"Multipart part size exceeds maximum allowed size\");\n }\n\n const sizeNumber = Number(input.sizeBytes);\n const minimumPartSizeForLimit = Math.ceil(sizeNumber / input.maxParts);\n\n if (minimumPartSizeForLimit > partSize) {\n partSize = minimumPartSizeForLimit;\n }\n\n if (partSize < input.minPartSizeBytes) {\n partSize = input.minPartSizeBytes;\n }\n\n if (partSize > input.maxPartSizeBytes) {\n throw new Error(\"Multipart part size exceeds maximum allowed size\");\n }\n\n const partCount = Math.ceil(sizeNumber / partSize);\n if (partCount > input.maxParts) {\n throw new Error(\"Multipart upload exceeds maximum number of parts\");\n }\n\n return partSize;\n};\n\nexport function createS3CompatibleStorageAdapter(\n options: S3CompatibleStorageAdapterOptions,\n): StorageAdapter {\n const storageKeyPrefix = normalizePrefix(options.storageKeyPrefix ?? \"\");\n const pathStyle = options.pathStyle ?? false;\n const maxStorageKeyLengthBytes =\n options.maxStorageKeyLengthBytes ?? DEFAULT_MAX_STORAGE_KEY_LENGTH_BYTES;\n const maxSingleUploadBytes = options.maxSingleUploadBytes ?? DEFAULT_MAX_SINGLE_UPLOAD_BYTES;\n const maxMultipartUploadBytes =\n options.maxMultipartUploadBytes ?? DEFAULT_MAX_MULTIPART_UPLOAD_BYTES;\n const minMultipartPartSizeBytes =\n options.minMultipartPartSizeBytes ?? DEFAULT_MIN_MULTIPART_PART_SIZE_BYTES;\n const maxMultipartPartSizeBytes =\n options.maxMultipartPartSizeBytes ?? DEFAULT_MAX_MULTIPART_PART_SIZE_BYTES;\n const maxMultipartParts = options.maxMultipartParts ?? DEFAULT_MAX_MULTIPART_PARTS;\n const uploadExpiresInSeconds =\n options.uploadExpiresInSeconds ?? DEFAULT_UPLOAD_EXPIRES_IN_SECONDS;\n const signedUrlExpiresInSeconds =\n options.signedUrlExpiresInSeconds ?? DEFAULT_SIGNED_URL_EXPIRES_IN_SECONDS;\n const directUploadThresholdBytes = options.directUploadThresholdBytes ?? maxSingleUploadBytes;\n const multipartThresholdBytes = options.multipartThresholdBytes ?? maxSingleUploadBytes;\n const multipartPartSizeBytes =\n options.multipartPartSizeBytes ?? DEFAULT_MULTIPART_PART_SIZE_BYTES;\n\n assertExpiresInSeconds(uploadExpiresInSeconds);\n assertExpiresInSeconds(signedUrlExpiresInSeconds);\n\n const resolveStorageKey = (input: { provider: string; fileKey: string }) => {\n const provider = normalizeProvider(input.provider);\n const fileKey = assertFileKey(input.fileKey);\n const storageKey = [storageKeyPrefix, provider, fileKey].filter(Boolean).join(\"/\");\n\n if (storageKey.length === 0) {\n throw new Error(\"Storage key cannot be empty\");\n }\n\n if (Buffer.byteLength(storageKey, \"utf8\") > maxStorageKeyLengthBytes) {\n throw new Error(\"Storage key exceeds maximum length\");\n }\n\n return storageKey;\n };\n\n const buildUrl = (storageKey: string, queryParams: Record<string, string | undefined> = {}) => {\n const url = buildObjectUrl({\n endpoint: options.endpoint,\n bucket: options.bucket,\n storageKey,\n pathStyle,\n });\n\n for (const [key, value] of Object.entries(queryParams)) {\n if (value === undefined) {\n continue;\n }\n url.searchParams.set(key, value);\n }\n\n return url;\n };\n\n const validateMetadata = (metadata?: Record<string, unknown> | null) => {\n if (!metadata) {\n return;\n }\n\n if (options.maxMetadataBytes === undefined) {\n return;\n }\n\n if (resolveMetadataSize(metadata) > options.maxMetadataBytes) {\n throw new Error(\"Metadata exceeds maximum size\");\n }\n };\n\n return {\n name: \"s3\",\n capabilities: {\n directUpload: true,\n multipartUpload: true,\n signedDownload: true,\n proxyUpload: false,\n },\n limits: {\n maxSingleUploadBytes,\n maxMultipartUploadBytes,\n minMultipartPartSizeBytes,\n maxMultipartPartSizeBytes,\n maxMultipartParts,\n maxStorageKeyLengthBytes,\n maxMetadataBytes: options.maxMetadataBytes,\n },\n recommendations: {\n uploadExpiresInSeconds,\n signedUrlExpiresInSeconds,\n directUploadThresholdBytes,\n multipartThresholdBytes,\n multipartPartSizeBytes,\n },\n resolveStorageKey,\n initUpload: async ({\n provider,\n fileKey,\n sizeBytes,\n contentType,\n metadata,\n checksum,\n objectKeyVersionSegment,\n }) => {\n validateMetadata(metadata);\n const storageKey = appendStorageObjectKeyVersionSegment(\n resolveStorageKey({ provider, fileKey }),\n objectKeyVersionSegment,\n maxStorageKeyLengthBytes,\n );\n\n const expiresAt = new Date(Date.now() + uploadExpiresInSeconds * 1000);\n const sizeNumber = Number(sizeBytes);\n\n if (Number.isNaN(sizeNumber) || sizeNumber < 0) {\n throw new Error(\"Upload size must be a non-negative number\");\n }\n\n const canMultipart = sizeNumber <= maxMultipartUploadBytes;\n const canSingle = sizeNumber <= maxSingleUploadBytes;\n\n if (!canMultipart) {\n throw new Error(\"Upload exceeds maximum multipart size\");\n }\n\n const prefersMultipart = sizeNumber >= multipartThresholdBytes;\n const canUseSingle = canSingle && sizeNumber <= directUploadThresholdBytes;\n\n if (prefersMultipart || !canUseSingle) {\n if (!canMultipart) {\n throw new Error(\"Upload exceeds maximum multipart size\");\n }\n\n const partSizeBytes = resolveMultipartPartSize({\n sizeBytes,\n desiredPartSizeBytes: multipartPartSizeBytes,\n minPartSizeBytes: minMultipartPartSizeBytes,\n maxPartSizeBytes: maxMultipartPartSizeBytes,\n maxParts: maxMultipartParts,\n });\n\n const createUrl = buildUrl(storageKey, { uploads: \"\" });\n const signed = await options.signer.sign({\n method: \"POST\",\n url: createUrl.toString(),\n headers: {\n \"Content-Type\": contentType,\n },\n });\n\n const response = await fetch(signed.url, {\n method: \"POST\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to create multipart upload (${response.status})`);\n }\n\n const payload = await response.text();\n const uploadId = parseUploadId(payload);\n if (!uploadId) {\n throw new Error(\"Failed to parse multipart upload id\");\n }\n\n return {\n strategy: \"direct-multipart\",\n storageKey,\n storageUploadId: uploadId,\n partSizeBytes,\n expiresAt,\n };\n }\n\n if (!canSingle) {\n throw new Error(\"Upload exceeds maximum single upload size\");\n }\n\n const uploadUrl = buildUrl(storageKey);\n const checksumHeaders = resolveChecksumHeaders(checksum);\n const signed = await options.signer.presign({\n method: \"PUT\",\n url: uploadUrl.toString(),\n headers: {\n \"Content-Type\": contentType,\n ...checksumHeaders,\n },\n expiresInSeconds: uploadExpiresInSeconds,\n });\n\n return {\n strategy: \"direct-single\",\n storageKey,\n expiresAt,\n uploadUrl: signed.url,\n uploadHeaders: signed.headers,\n };\n },\n getPartUploadUrls: async ({ storageKey, storageUploadId, partNumbers }) => {\n for (const partNumber of partNumbers) {\n if (partNumber < 1 || partNumber > maxMultipartParts) {\n throw new Error(\"INVALID_REQUEST\");\n }\n }\n\n const urls = await Promise.all(\n partNumbers.map(async (partNumber) => {\n const url = buildUrl(storageKey, {\n partNumber: String(partNumber),\n uploadId: storageUploadId,\n });\n const signed = await options.signer.presign({\n method: \"PUT\",\n url: url.toString(),\n expiresInSeconds: uploadExpiresInSeconds,\n });\n\n return {\n partNumber,\n url: signed.url,\n headers: signed.headers,\n };\n }),\n );\n\n return urls;\n },\n completeMultipartUpload: async ({ storageKey, storageUploadId, parts }) => {\n const url = buildUrl(storageKey, { uploadId: storageUploadId });\n const body = buildCompleteMultipartBody(parts);\n const signed = await options.signer.sign({\n method: \"POST\",\n url: url.toString(),\n headers: {\n \"Content-Type\": \"application/xml\",\n },\n body,\n });\n\n const response = await fetch(signed.url, {\n method: \"POST\",\n headers: signed.headers,\n body,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to complete multipart upload (${response.status})`);\n }\n\n const etag = response.headers.get(\"ETag\") ?? undefined;\n return { etag };\n },\n abortMultipartUpload: async ({ storageKey, storageUploadId }) => {\n const url = buildUrl(storageKey, { uploadId: storageUploadId });\n const signed = await options.signer.sign({\n method: \"DELETE\",\n url: url.toString(),\n });\n\n const response = await fetch(signed.url, {\n method: \"DELETE\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to abort multipart upload (${response.status})`);\n }\n },\n finalizeUpload: async ({ storageKey, expectedSizeBytes, checksum }) => {\n const url = buildUrl(storageKey);\n const signed = await options.signer.sign({\n method: \"HEAD\",\n url: url.toString(),\n });\n\n const response = await fetch(signed.url, {\n method: \"HEAD\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(\"STORAGE_ERROR\");\n }\n\n const sizeHeader = getHeaderValue(response.headers, \"content-length\");\n const sizeBytes = parseContentLength(sizeHeader);\n if (sizeBytes === null) {\n throw new Error(\"STORAGE_ERROR\");\n }\n if (sizeBytes !== expectedSizeBytes) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n\n const normalized = normalizeChecksum(checksum);\n if (normalized) {\n if (normalized.algo === \"md5\") {\n const etag = normalizeEtag(getHeaderValue(response.headers, \"etag\"));\n if (etag) {\n if (etag !== normalized.hex) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n } else {\n const signedGet = await options.signer.sign({\n method: \"GET\",\n url: buildUrl(storageKey).toString(),\n });\n const getResponse = await fetch(signedGet.url, {\n method: \"GET\",\n headers: signedGet.headers,\n });\n if (!getResponse.ok) {\n throw new Error(\"STORAGE_ERROR\");\n }\n const computed = await computeChecksumFromResponse(getResponse, \"md5\");\n if (computed !== normalized.hex) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n }\n } else {\n const checksumHeader = getHeaderValue(response.headers, \"x-amz-checksum-sha256\");\n if (checksumHeader) {\n const remote = parseBase64(checksumHeader, 32);\n if (!remote || remote.toString(\"base64\") !== normalized.base64) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n } else {\n const signedGet = await options.signer.sign({\n method: \"GET\",\n url: buildUrl(storageKey).toString(),\n });\n const getResponse = await fetch(signedGet.url, {\n method: \"GET\",\n headers: signedGet.headers,\n });\n if (!getResponse.ok) {\n throw new Error(\"STORAGE_ERROR\");\n }\n const computed = await computeChecksumFromResponse(getResponse, \"sha256\");\n if (computed !== normalized.hex) {\n throw new Error(\"INVALID_CHECKSUM\");\n }\n }\n }\n }\n\n const etag = getHeaderValue(response.headers, \"etag\");\n return { sizeBytes, etag: etag ?? undefined };\n },\n deleteObject: async ({ storageKey }) => {\n const url = buildUrl(storageKey);\n const signed = await options.signer.sign({\n method: \"DELETE\",\n url: url.toString(),\n });\n\n const response = await fetch(signed.url, {\n method: \"DELETE\",\n headers: signed.headers,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete object (${response.status})`);\n }\n },\n getDownloadUrl: async ({ storageKey, expiresInSeconds, contentDisposition, contentType }) => {\n assertExpiresInSeconds(expiresInSeconds);\n\n const url = buildUrl(storageKey, {\n \"response-content-disposition\": contentDisposition,\n \"response-content-type\": contentType,\n });\n\n const signed = await options.signer.presign({\n method: \"GET\",\n url: url.toString(),\n expiresInSeconds,\n });\n\n return {\n url: signed.url,\n headers: signed.headers,\n expiresAt: new Date(Date.now() + expiresInSeconds * 1000),\n };\n },\n };\n}\n"],"mappings":";;;;;AAMA,MAAM,eAAe,OAAO;AAC5B,MAAM,eAAe,OAAO;AAC5B,MAAM,eAAe,OAAO;AAE5B,MAAM,kCAAkC,IAAI;AAC5C,MAAM,qCAAqC,IAAI;AAC/C,MAAM,wCAAwC,IAAI;AAClD,MAAM,wCAAwC,IAAI;AAClD,MAAM,8BAA8B;AACpC,MAAM,uCAAuC;AAC7C,MAAM,oCAAoC;AAC1C,MAAM,wCAAwC;AAC9C,MAAM,oCAAoC,IAAI;AAC9C,MAAM,mCAAmC,OAAU,KAAK;AAmCxD,MAAM,mBAAmB,WAA2B;AAClD,KAAI,CAAC,OACH,QAAO;CAGT,MAAM,WAAW,OAAO,MAAM,IAAI,CAAC,OAAO,QAAQ;AAClD,MAAK,MAAM,WAAW,SACpB,KAAI,YAAY,OAAO,YAAY,KACjC,OAAM,IAAI,MAAM,yDAAyD;AAI7E,QAAO,SAAS,KAAK,IAAI;;AAG3B,MAAM,qBAAqB,aAA6B;CACtD,MAAM,aAAa,SAAS,MAAM;AAClC,KACE,WAAW,WAAW,KACtB,eAAe,OACf,eAAe,QACf,WAAW,SAAS,IAAI,IACxB,WAAW,SAAS,KAAK,CAEzB,OAAM,IAAI,MAAM,mBAAmB;AAGrC,QAAO;;AAGT,MAAM,sBAAsB,UAC1B,MACG,MAAM,IAAI,CACV,KAAK,YAAY,mBAAmB,QAAQ,CAAC,CAC7C,KAAK,IAAI;AAEd,MAAM,oBAAoB,GAAG,aAAqC;AAEhE,QADgB,SAAS,OAAO,QAAQ,CAAC,KAAK,YAAY,QAAS,QAAQ,cAAc,GAAG,CAAC,CAC9E,KAAK,IAAI;;AAG1B,MAAM,YAAY,OAAe,kBAA0B;CACzD,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,eAAe,KAAK,QAAQ,IAAI,QAAQ,WAAW,gBAAgB,EACtE,QAAO;AAET,QAAO,OAAO,KAAK,SAAS,MAAM;;AAGpC,MAAM,eAAe,OAAe,kBAA0B;CAC5D,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,oBAAoB,KAAK,QAAQ,CACpC,QAAO;CAET,MAAM,MAAM,OAAO,KAAK,SAAS,SAAS;AAC1C,KAAI,IAAI,WAAW,cACjB,QAAO;AAET,QAAO;;AAGT,MAAM,qBAAqB,aAAqC;AAC9D,KAAI,CAAC,SACH,QAAO;CAGT,MAAM,gBAAgB,SAAS,SAAS,QAAQ,KAAK;CACrD,MAAM,MAAM,SAAS,SAAS;CAC9B,MAAM,MAAM,SAAS,KAAK,cAAc,IAAI,YAAY,KAAK,cAAc;AAC3E,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,mBAAmB;AAGrC,QAAO;EACL,MAAM,SAAS;EACf,KAAK,IAAI,SAAS,MAAM;EACxB,QAAQ,IAAI,SAAS,SAAS;EAC/B;;AAGH,MAAM,0BAA0B,aAA6D;CAC3F,MAAM,aAAa,kBAAkB,SAAS;AAC9C,KAAI,CAAC,WACH,QAAO,EAAE;AAGX,KAAI,WAAW,SAAS,MACtB,QAAO,EAAE,eAAe,WAAW,QAAQ;AAG7C,QAAO,EAAE,yBAAyB,WAAW,QAAQ;;AAGvD,MAAM,iBAAiB,SAAwB;AAC7C,KAAI,CAAC,KACH,QAAO;CAGT,MAAM,UAAU,KAAK,MAAM,CAAC,QAAQ,QAAQ,GAAG,CAAC,QAAQ,UAAU,GAAG;AACrE,KAAI,CAAC,kBAAkB,KAAK,QAAQ,CAClC,QAAO;AAGT,QAAO,QAAQ,aAAa;;AAG9B,MAAM,8BAA8B,OAClC,UACA,SACoB;AACpB,KAAI,CAAC,SAAS,KACZ,OAAM,IAAI,MAAM,gBAAgB;CAGlC,MAAM,OAAO,WAAW,KAAK;CAC7B,MAAM,SAAS,SAAS,KAAK,WAAW;AAExC,QAAO,MAAM;EACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,MAAI,KACF;AAEF,MAAI,MACF,MAAK,OAAO,OAAO,KAAK,MAAM,CAAC;;AAInC,QAAO,KAAK,OAAO,MAAM;;AAG3B,MAAM,sBAAsB,UAAyB;AACnD,KAAI,CAAC,MACH,QAAO;AAET,KAAI,CAAC,QAAQ,KAAK,MAAM,CACtB,QAAO;AAET,QAAO,OAAO,MAAM;;AAGtB,MAAM,kBAAkB,SAAkB,SAAiB;CACzD,MAAM,SAAS,QAAQ,IAAI,KAAK;AAChC,KAAI,WAAW,KACb,QAAO;CAET,MAAM,SAAS,KAAK,aAAa;AACjC,MAAK,MAAM,CAAC,KAAK,UAAU,QAAQ,SAAS,CAC1C,KAAI,IAAI,aAAa,KAAK,OACxB,QAAO;AAGX,QAAO;;AAGT,MAAM,kBAAkB,UAKlB;CACJ,MAAM,MAAM,IAAI,IAAI,MAAM,SAAS;CACnC,MAAM,aAAa,mBAAmB,MAAM,WAAW;CACvD,MAAM,WAAW,IAAI,aAAa,MAAM,KAAK,IAAI,SAAS,QAAQ,SAAS,GAAG;AAE9E,KAAI,MAAM,WAAW;EACnB,MAAMA,SAAO,iBAAiB,UAAU,MAAM,QAAQ,WAAW;AACjE,MAAI,WAAWA,SAAO,IAAIA,WAAS;AACnC,SAAO;;AAGT,KAAI,WAAW,GAAG,MAAM,OAAO,GAAG,IAAI;CACtC,MAAM,OAAO,iBAAiB,UAAU,WAAW;AACnD,KAAI,WAAW,OAAO,IAAI,SAAS;AACnC,QAAO;;AAGT,MAAM,0BAA0B,UAAkB;AAChD,KAAI,CAAC,OAAO,SAAS,MAAM,IAAI,SAAS,EACtC,OAAM,IAAI,MAAM,kDAAkD;AAEpE,KAAI,QAAQ,iCACV,OAAM,IAAI,MAAM,6CAA6C;;AAIjE,MAAM,uBAAuB,aAA8C;AACzE,KAAI,CAAC,SACH,QAAO;CAGT,MAAM,aAAa,KAAK,UAAU,SAAS;AAC3C,QAAO,OAAO,WAAW,YAAY,OAAO;;AAG9C,MAAM,iBAAiB,YAAoB;CACzC,MAAM,QAAQ,QAAQ,MAAM,gCAAgC;AAC5D,KAAI,CAAC,MACH,QAAO;AAGT,QAAO,MAAM;;AAGf,MAAM,aAAa,UACjB,MACG,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,SAAS,CACvB,QAAQ,MAAM,SAAS;AAE5B,MAAM,8BAA8B,UAAkD;AAUpF,QAAO,kEATQ,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,WAAW,CAElE,KACE,SACC,qBAAqB,KAAK,WAAW,qBAAqB,UACxD,KAAK,KACN,CAAC,gBACL,CACA,KAAK,GAAG,CACsE;;AAGnF,MAAM,4BAA4B,UAM5B;CACJ,IAAI,WAAW,MAAM;AAErB,KAAI,WAAW,MAAM,iBACnB,YAAW,MAAM;AAGnB,KAAI,WAAW,MAAM,iBACnB,OAAM,IAAI,MAAM,mDAAmD;CAGrE,MAAM,aAAa,OAAO,MAAM,UAAU;CAC1C,MAAM,0BAA0B,KAAK,KAAK,aAAa,MAAM,SAAS;AAEtE,KAAI,0BAA0B,SAC5B,YAAW;AAGb,KAAI,WAAW,MAAM,iBACnB,YAAW,MAAM;AAGnB,KAAI,WAAW,MAAM,iBACnB,OAAM,IAAI,MAAM,mDAAmD;AAIrE,KADkB,KAAK,KAAK,aAAa,SAAS,GAClC,MAAM,SACpB,OAAM,IAAI,MAAM,mDAAmD;AAGrE,QAAO;;AAGT,SAAgB,iCACd,SACgB;CAChB,MAAM,mBAAmB,gBAAgB,QAAQ,oBAAoB,GAAG;CACxE,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,2BACJ,QAAQ,4BAA4B;CACtC,MAAM,uBAAuB,QAAQ,wBAAwB;CAC7D,MAAM,0BACJ,QAAQ,2BAA2B;CACrC,MAAM,4BACJ,QAAQ,6BAA6B;CACvC,MAAM,4BACJ,QAAQ,6BAA6B;CACvC,MAAM,oBAAoB,QAAQ,qBAAqB;CACvD,MAAM,yBACJ,QAAQ,0BAA0B;CACpC,MAAM,4BACJ,QAAQ,6BAA6B;CACvC,MAAM,6BAA6B,QAAQ,8BAA8B;CACzE,MAAM,0BAA0B,QAAQ,2BAA2B;CACnE,MAAM,yBACJ,QAAQ,0BAA0B;AAEpC,wBAAuB,uBAAuB;AAC9C,wBAAuB,0BAA0B;CAEjD,MAAM,qBAAqB,UAAiD;EAG1E,MAAM,aAAa;GAAC;GAFH,kBAAkB,MAAM,SAAS;GAClC,cAAc,MAAM,QAAQ;GACY,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;AAElF,MAAI,WAAW,WAAW,EACxB,OAAM,IAAI,MAAM,8BAA8B;AAGhD,MAAI,OAAO,WAAW,YAAY,OAAO,GAAG,yBAC1C,OAAM,IAAI,MAAM,qCAAqC;AAGvD,SAAO;;CAGT,MAAM,YAAY,YAAoB,cAAkD,EAAE,KAAK;EAC7F,MAAM,MAAM,eAAe;GACzB,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB;GACA;GACD,CAAC;AAEF,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,YAAY,EAAE;AACtD,OAAI,UAAU,OACZ;AAEF,OAAI,aAAa,IAAI,KAAK,MAAM;;AAGlC,SAAO;;CAGT,MAAM,oBAAoB,aAA8C;AACtE,MAAI,CAAC,SACH;AAGF,MAAI,QAAQ,qBAAqB,OAC/B;AAGF,MAAI,oBAAoB,SAAS,GAAG,QAAQ,iBAC1C,OAAM,IAAI,MAAM,gCAAgC;;AAIpD,QAAO;EACL,MAAM;EACN,cAAc;GACZ,cAAc;GACd,iBAAiB;GACjB,gBAAgB;GAChB,aAAa;GACd;EACD,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA,kBAAkB,QAAQ;GAC3B;EACD,iBAAiB;GACf;GACA;GACA;GACA;GACA;GACD;EACD;EACA,YAAY,OAAO,EACjB,UACA,SACA,WACA,aACA,UACA,UACA,8BACI;AACJ,oBAAiB,SAAS;GAC1B,MAAM,aAAa,qCACjB,kBAAkB;IAAE;IAAU;IAAS,CAAC,EACxC,yBACA,yBACD;GAED,MAAM,YAAY,IAAI,KAAK,KAAK,KAAK,GAAG,yBAAyB,IAAK;GACtE,MAAM,aAAa,OAAO,UAAU;AAEpC,OAAI,OAAO,MAAM,WAAW,IAAI,aAAa,EAC3C,OAAM,IAAI,MAAM,4CAA4C;GAG9D,MAAM,eAAe,cAAc;GACnC,MAAM,YAAY,cAAc;AAEhC,OAAI,CAAC,aACH,OAAM,IAAI,MAAM,wCAAwC;AAM1D,OAHyB,cAAc,2BAGf,EAFH,aAAa,cAAc,6BAET;AACrC,QAAI,CAAC,aACH,OAAM,IAAI,MAAM,wCAAwC;IAG1D,MAAM,gBAAgB,yBAAyB;KAC7C;KACA,sBAAsB;KACtB,kBAAkB;KAClB,kBAAkB;KAClB,UAAU;KACX,CAAC;IAEF,MAAM,YAAY,SAAS,YAAY,EAAE,SAAS,IAAI,CAAC;IACvD,MAAMC,WAAS,MAAM,QAAQ,OAAO,KAAK;KACvC,QAAQ;KACR,KAAK,UAAU,UAAU;KACzB,SAAS,EACP,gBAAgB,aACjB;KACF,CAAC;IAEF,MAAM,WAAW,MAAM,MAAMA,SAAO,KAAK;KACvC,QAAQ;KACR,SAASA,SAAO;KACjB,CAAC;AAEF,QAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,sCAAsC,SAAS,OAAO,GAAG;IAI3E,MAAM,WAAW,cADD,MAAM,SAAS,MAAM,CACE;AACvC,QAAI,CAAC,SACH,OAAM,IAAI,MAAM,sCAAsC;AAGxD,WAAO;KACL,UAAU;KACV;KACA,iBAAiB;KACjB;KACA;KACD;;AAGH,OAAI,CAAC,UACH,OAAM,IAAI,MAAM,4CAA4C;GAG9D,MAAM,YAAY,SAAS,WAAW;GACtC,MAAM,kBAAkB,uBAAuB,SAAS;GACxD,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ;IAC1C,QAAQ;IACR,KAAK,UAAU,UAAU;IACzB,SAAS;KACP,gBAAgB;KAChB,GAAG;KACJ;IACD,kBAAkB;IACnB,CAAC;AAEF,UAAO;IACL,UAAU;IACV;IACA;IACA,WAAW,OAAO;IAClB,eAAe,OAAO;IACvB;;EAEH,mBAAmB,OAAO,EAAE,YAAY,iBAAiB,kBAAkB;AACzE,QAAK,MAAM,cAAc,YACvB,KAAI,aAAa,KAAK,aAAa,kBACjC,OAAM,IAAI,MAAM,kBAAkB;AAwBtC,UApBa,MAAM,QAAQ,IACzB,YAAY,IAAI,OAAO,eAAe;IACpC,MAAM,MAAM,SAAS,YAAY;KAC/B,YAAY,OAAO,WAAW;KAC9B,UAAU;KACX,CAAC;IACF,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ;KAC1C,QAAQ;KACR,KAAK,IAAI,UAAU;KACnB,kBAAkB;KACnB,CAAC;AAEF,WAAO;KACL;KACA,KAAK,OAAO;KACZ,SAAS,OAAO;KACjB;KACD,CACH;;EAIH,yBAAyB,OAAO,EAAE,YAAY,iBAAiB,YAAY;GACzE,MAAM,MAAM,SAAS,YAAY,EAAE,UAAU,iBAAiB,CAAC;GAC/D,MAAM,OAAO,2BAA2B,MAAM;GAC9C,MAAM,SAAS,MAAM,QAAQ,OAAO,KAAK;IACvC,QAAQ;IACR,KAAK,IAAI,UAAU;IACnB,SAAS,EACP,gBAAgB,mBACjB;IACD;IACD,CAAC;GAEF,MAAM,WAAW,MAAM,MAAM,OAAO,KAAK;IACvC,QAAQ;IACR,SAAS,OAAO;IAChB;IACD,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,wCAAwC,SAAS,OAAO,GAAG;AAI7E,UAAO,EAAE,MADI,SAAS,QAAQ,IAAI,OAAO,IAAI,QAC9B;;EAEjB,sBAAsB,OAAO,EAAE,YAAY,sBAAsB;GAC/D,MAAM,MAAM,SAAS,YAAY,EAAE,UAAU,iBAAiB,CAAC;GAC/D,MAAM,SAAS,MAAM,QAAQ,OAAO,KAAK;IACvC,QAAQ;IACR,KAAK,IAAI,UAAU;IACpB,CAAC;GAEF,MAAM,WAAW,MAAM,MAAM,OAAO,KAAK;IACvC,QAAQ;IACR,SAAS,OAAO;IACjB,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,qCAAqC,SAAS,OAAO,GAAG;;EAG5E,gBAAgB,OAAO,EAAE,YAAY,mBAAmB,eAAe;GACrE,MAAM,MAAM,SAAS,WAAW;GAChC,MAAM,SAAS,MAAM,QAAQ,OAAO,KAAK;IACvC,QAAQ;IACR,KAAK,IAAI,UAAU;IACpB,CAAC;GAEF,MAAM,WAAW,MAAM,MAAM,OAAO,KAAK;IACvC,QAAQ;IACR,SAAS,OAAO;IACjB,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,gBAAgB;GAIlC,MAAM,YAAY,mBADC,eAAe,SAAS,SAAS,iBAAiB,CACrB;AAChD,OAAI,cAAc,KAChB,OAAM,IAAI,MAAM,gBAAgB;AAElC,OAAI,cAAc,kBAChB,OAAM,IAAI,MAAM,mBAAmB;GAGrC,MAAM,aAAa,kBAAkB,SAAS;AAC9C,OAAI,WACF,KAAI,WAAW,SAAS,OAAO;IAC7B,MAAM,OAAO,cAAc,eAAe,SAAS,SAAS,OAAO,CAAC;AACpE,QAAI,MACF;SAAI,SAAS,WAAW,IACtB,OAAM,IAAI,MAAM,mBAAmB;WAEhC;KACL,MAAM,YAAY,MAAM,QAAQ,OAAO,KAAK;MAC1C,QAAQ;MACR,KAAK,SAAS,WAAW,CAAC,UAAU;MACrC,CAAC;KACF,MAAM,cAAc,MAAM,MAAM,UAAU,KAAK;MAC7C,QAAQ;MACR,SAAS,UAAU;MACpB,CAAC;AACF,SAAI,CAAC,YAAY,GACf,OAAM,IAAI,MAAM,gBAAgB;AAGlC,SADiB,MAAM,4BAA4B,aAAa,MAAM,KACrD,WAAW,IAC1B,OAAM,IAAI,MAAM,mBAAmB;;UAGlC;IACL,MAAM,iBAAiB,eAAe,SAAS,SAAS,wBAAwB;AAChF,QAAI,gBAAgB;KAClB,MAAM,SAAS,YAAY,gBAAgB,GAAG;AAC9C,SAAI,CAAC,UAAU,OAAO,SAAS,SAAS,KAAK,WAAW,OACtD,OAAM,IAAI,MAAM,mBAAmB;WAEhC;KACL,MAAM,YAAY,MAAM,QAAQ,OAAO,KAAK;MAC1C,QAAQ;MACR,KAAK,SAAS,WAAW,CAAC,UAAU;MACrC,CAAC;KACF,MAAM,cAAc,MAAM,MAAM,UAAU,KAAK;MAC7C,QAAQ;MACR,SAAS,UAAU;MACpB,CAAC;AACF,SAAI,CAAC,YAAY,GACf,OAAM,IAAI,MAAM,gBAAgB;AAGlC,SADiB,MAAM,4BAA4B,aAAa,SAAS,KACxD,WAAW,IAC1B,OAAM,IAAI,MAAM,mBAAmB;;;AAO3C,UAAO;IAAE;IAAW,MADP,eAAe,SAAS,SAAS,OAAO,IACnB;IAAW;;EAE/C,cAAc,OAAO,EAAE,iBAAiB;GACtC,MAAM,MAAM,SAAS,WAAW;GAChC,MAAM,SAAS,MAAM,QAAQ,OAAO,KAAK;IACvC,QAAQ;IACR,KAAK,IAAI,UAAU;IACpB,CAAC;GAEF,MAAM,WAAW,MAAM,MAAM,OAAO,KAAK;IACvC,QAAQ;IACR,SAAS,OAAO;IACjB,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,4BAA4B,SAAS,OAAO,GAAG;;EAGnE,gBAAgB,OAAO,EAAE,YAAY,kBAAkB,oBAAoB,kBAAkB;AAC3F,0BAAuB,iBAAiB;GAExC,MAAM,MAAM,SAAS,YAAY;IAC/B,gCAAgC;IAChC,yBAAyB;IAC1B,CAAC;GAEF,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ;IAC1C,QAAQ;IACR,KAAK,IAAI,UAAU;IACnB;IACD,CAAC;AAEF,UAAO;IACL,KAAK,OAAO;IACZ,SAAS,OAAO;IAChB,WAAW,IAAI,KAAK,KAAK,KAAK,GAAG,mBAAmB,IAAK;IAC1D;;EAEJ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FileKey } from "../file-key.js";
|
|
2
2
|
|
|
3
3
|
//#region src/storage/types.d.ts
|
|
4
4
|
type UploadTransport = "direct" | "proxy";
|
|
@@ -35,16 +35,17 @@ interface StorageAdapter {
|
|
|
35
35
|
limits?: StorageAdapterLimits;
|
|
36
36
|
recommendations?: StorageAdapterRecommendations;
|
|
37
37
|
resolveStorageKey(input: {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
provider: string;
|
|
39
|
+
fileKey: FileKey;
|
|
40
40
|
}): string;
|
|
41
41
|
initUpload(input: {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
provider: string;
|
|
43
|
+
fileKey: FileKey;
|
|
44
44
|
sizeBytes: bigint;
|
|
45
45
|
contentType: string;
|
|
46
46
|
checksum?: UploadChecksum | null;
|
|
47
47
|
metadata?: Record<string, unknown> | null;
|
|
48
|
+
objectKeyVersionSegment?: string;
|
|
48
49
|
}): Promise<{
|
|
49
50
|
strategy: "direct-single" | "direct-multipart" | "proxy";
|
|
50
51
|
storageKey: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/storage/types.ts"],"sourcesContent":[],"mappings":";;;KAEY,eAAA;KACA,UAAA;AADA,UAGK,0BAAA,CAHU;EACf,YAAA,EAAU,OAAA;EAEL,eAAA,EAAA,OAAA;EAOL,cAAA,EAAA,OAAc;EAET,WAAA,EAAA,OAAA;AAUjB;AAQiB,KApBL,cAAA,GAoBmB;EAAA,IAAA,EAAA,QAAA,GAAA,KAAA;OAEf,EAAA,MAAA;;AAEI,UAtBH,oBAAA,CAsBG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../src/storage/types.ts"],"sourcesContent":[],"mappings":";;;KAEY,eAAA;KACA,UAAA;AADA,UAGK,0BAAA,CAHU;EACf,YAAA,EAAU,OAAA;EAEL,eAAA,EAAA,OAAA;EAOL,cAAA,EAAA,OAAc;EAET,WAAA,EAAA,OAAA;AAUjB;AAQiB,KApBL,cAAA,GAoBmB;EAAA,IAAA,EAAA,QAAA,GAAA,KAAA;OAEf,EAAA,MAAA;;AAEI,UAtBH,oBAAA,CAsBG;sBAGoC,CAAA,EAAA,MAAA;yBAM3C,CAAA,EAAA,MAAA;2BAGE,CAAA,EAAA,MAAA;2BACA,CAAA,EAAA,MAAA;mBAOA,CAAA,EAAA,MAAA;0BAEK,CAAA,EAAA,MAAA;kBAPd,CAAA,EAAA,MAAA;;AAgBA,UA3CW,6BAAA,CA2CX;wBAOA,CAAA,EAAA,MAAA;2BAG2E,CAAA,EAAA,MAAA;4BAKxD,CAAA,EAAA,MAAA;yBAAf,CAAA,EAAA,MAAA;wBAGJ,CAAA,EAAA,MAAA;;AAOA,UA5DW,cAAA,CA4DX;MAGyC,EAAA,MAAA;cAUjC,EAvEE,0BAuEF;QACC,CAAA,EAvEJ,oBAuEI;iBAHT,CAAA,EAnEc,6BAmEd;mBAMuD,CAAA,KAAA,EAAA;IAAR,QAAA,EAAA,MAAA;IAAO,OAAA,EAtEJ,OAsEI;;;;aAhE/C;;;eAGE;eACA;;MAET;;;;;eAKS;;oBAEK;;;;;;;MASd;;;cAAqD;;;;;;;;;MAOrD;;;;;;MAG2E;;;UAKvE,eAAe;;;MAGnB;;;;;;;eAMS;MACT;;;;;;MAGyC;;;;;;MAQzC;;cAEQ;eACC;;;;MAGsC,QAAQ"}
|
package/dist/node/types.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ type UploadStatus = "created" | "in_progress" | "completed" | "aborted" | "faile
|
|
|
5
5
|
type UploadStrategy = "direct-single" | "direct-multipart" | "proxy";
|
|
6
6
|
type FileMetadata = {
|
|
7
7
|
fileKey: string;
|
|
8
|
-
fileKeyParts: (string | number)[];
|
|
9
8
|
uploaderId: string | null;
|
|
10
9
|
filename: string;
|
|
11
10
|
sizeBytes: number;
|
|
@@ -18,7 +17,7 @@ type FileMetadata = {
|
|
|
18
17
|
tags: string[] | null;
|
|
19
18
|
metadata: Record<string, unknown> | null;
|
|
20
19
|
status: FileStatus;
|
|
21
|
-
|
|
20
|
+
provider: string;
|
|
22
21
|
createdAt: string;
|
|
23
22
|
updatedAt: string;
|
|
24
23
|
completedAt: string | null;
|
package/dist/node/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types.ts"],"sourcesContent":[],"mappings":";KAAY,cAAA;AAAA,KAEA,UAAA,GAFc,OAAA,GAAA,SAAA;AAEd,KAEA,YAAA,GAFU,SAAA,GAAA,aAAA,GAAA,WAAA,GAAA,SAAA,GAAA,QAAA,GAAA,SAAA;AAEV,KAQA,cAAA,GARY,eAAA,GAAA,kBAAA,GAAA,OAAA;AAQZ,KAEA,YAAA,GAFc;EAEd,OAAA,EAAA,MAAY;EAAA,
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types.ts"],"sourcesContent":[],"mappings":";KAAY,cAAA;AAAA,KAEA,UAAA,GAFc,OAAA,GAAA,SAAA;AAEd,KAEA,YAAA,GAFU,SAAA,GAAA,aAAA,GAAA,WAAA,GAAA,SAAA,GAAA,QAAA,GAAA,SAAA;AAEV,KAQA,cAAA,GARY,eAAA,GAAA,kBAAA,GAAA,OAAA;AAQZ,KAEA,YAAA,GAFc;EAEd,OAAA,EAAA,MAAY;EAAA,UAAA,EAAA,MAAA,GAAA,IAAA;UAOV,EAAA,MAAA;WAEF,EAAA,MAAA;aACF,EAAA,MAAA;EAAU,QAAA,EAAA;;;;cAHN;;YAEF;UACF"}
|