@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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { FileKeyEncoded, FileKeyParts } from "../keys.js";
|
|
2
1
|
import { UploadChecksum } from "../storage/types.js";
|
|
3
2
|
import { FileMetadata, FileVisibility, UploadStrategy } from "../types.js";
|
|
4
3
|
|
|
@@ -10,8 +9,8 @@ type UploadProgress = {
|
|
|
10
9
|
totalParts?: number;
|
|
11
10
|
};
|
|
12
11
|
type CreateUploadAndTransferOptions = {
|
|
13
|
-
|
|
14
|
-
fileKey
|
|
12
|
+
provider: string;
|
|
13
|
+
fileKey: string;
|
|
15
14
|
filename?: string;
|
|
16
15
|
contentType?: string;
|
|
17
16
|
checksum?: UploadChecksum | null;
|
|
@@ -21,10 +20,16 @@ type CreateUploadAndTransferOptions = {
|
|
|
21
20
|
metadata?: Record<string, unknown>;
|
|
22
21
|
onProgress?: (progress: UploadProgress) => void;
|
|
23
22
|
};
|
|
23
|
+
type DownloadMethod = "signed-url" | "content";
|
|
24
|
+
type DownloadFileOptions = {
|
|
25
|
+
provider: string;
|
|
26
|
+
method: DownloadMethod;
|
|
27
|
+
};
|
|
24
28
|
type UploadCreateResponse = {
|
|
25
29
|
uploadId: string;
|
|
26
30
|
fileKey: string;
|
|
27
|
-
|
|
31
|
+
provider: string;
|
|
32
|
+
status: "created" | "in_progress";
|
|
28
33
|
strategy: UploadStrategy;
|
|
29
34
|
expiresAt: string;
|
|
30
35
|
upload: {
|
|
@@ -34,8 +39,12 @@ type UploadCreateResponse = {
|
|
|
34
39
|
uploadHeaders?: Record<string, string>;
|
|
35
40
|
partSizeBytes?: number;
|
|
36
41
|
maxParts?: number;
|
|
42
|
+
statusEndpoint: string;
|
|
43
|
+
progressEndpoint: string;
|
|
37
44
|
partsEndpoint?: string;
|
|
45
|
+
partsCompleteEndpoint?: string;
|
|
38
46
|
completeEndpoint: string;
|
|
47
|
+
abortEndpoint: string;
|
|
39
48
|
contentEndpoint?: string;
|
|
40
49
|
};
|
|
41
50
|
};
|
|
@@ -44,8 +53,8 @@ type UploadHelpers = {
|
|
|
44
53
|
upload: UploadCreateResponse;
|
|
45
54
|
file: FileMetadata;
|
|
46
55
|
}>;
|
|
47
|
-
downloadFile: (
|
|
56
|
+
downloadFile: (fileKey: string, options: DownloadFileOptions) => Promise<Response>;
|
|
48
57
|
};
|
|
49
58
|
//#endregion
|
|
50
|
-
export { CreateUploadAndTransferOptions, UploadCreateResponse, UploadHelpers, UploadProgress };
|
|
59
|
+
export { CreateUploadAndTransferOptions, DownloadFileOptions, DownloadMethod, UploadCreateResponse, UploadHelpers, UploadProgress };
|
|
51
60
|
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","names":[],"sources":["../../../src/client/helpers.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","names":[],"sources":["../../../src/client/helpers.ts"],"sourcesContent":[],"mappings":";;;;KAGY,cAAA;;EAAA,UAAA,EAAA,MAAc;EAOd,aAAA,EAAA,MAAA;EAA8B,UAAA,CAAA,EAAA,MAAA;;AAO3B,KAPH,8BAAA,GAOG;UAEF,EAAA,MAAA;SACa,EAAA,MAAA;EAAc,QAAA,CAAA,EAAA,MAAA;EAG5B,WAAA,CAAA,EAAA,MAAc;EAEd,QAAA,CAAA,EAVC,cAUkB,GAAA,IAAA;EAKnB,IAAA,CAAA,EAAA,MAAA,EAAA;EAAoB,UAAA,CAAA,EAbjB,cAaiB;YAKpB,CAAA,EAAA,MAAA;UAMQ,CAAA,EAtBP,MAsBO,CAAA,MAAA,EAAA,OAAA,CAAA;EAAM,UAAA,CAAA,EAAA,CAAA,QAAA,EArBA,cAqBA,EAAA,GAAA,IAAA;AAa1B,CAAA;AAAyB,KA/Bb,cAAA,GA+Ba,YAAA,GAAA,SAAA;AAEf,KA/BE,mBAAA,GA+BF;UACG,EAAA,MAAA;QACY,EA/Bf,cA+Be;;AAAlB,KA5BK,oBAAA,GA4BL;UACoC,EAAA,MAAA;SAAgC,EAAA,MAAA;UAAR,EAAA,MAAA;EAAO,MAAA,EAAA,SAAA,GAAA,aAAA;YAxB9D;;;;;;oBAMQ;;;;;;;;;;;;KAaR,aAAA;kCAEF,eACG,mCACN;YAAkB;UAA4B;;2CACV,wBAAwB,QAAQ"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { encodeFileKey } from "../keys.js";
|
|
2
|
-
|
|
3
1
|
//#region src/client/helpers.ts
|
|
4
2
|
const DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
3
|
+
const PROXY_UPLOAD_STRATEGY_HINT = "Server selected proxy upload strategy for this file (no direct upload URL was returned). If you expected direct-to-storage upload, your active provider/config does not support direct upload for this request.";
|
|
4
|
+
const PROXY_UPLOAD_RECOVERY_HINT = "Verify the /uploads/:uploadId/content endpoint is reachable from the client, or switch provider/config so /uploads returns a direct strategy.";
|
|
5
|
+
const DOWNLOAD_METHOD_HINT = "Pick the download method explicitly: use 'signed-url' for GET /files/by-key/download-url, otherwise use 'content' for GET /files/by-key/content.";
|
|
5
6
|
const mergeHeaders = (base, next) => {
|
|
6
7
|
const merged = new Headers(base ?? void 0);
|
|
7
8
|
if (!next) return merged;
|
|
@@ -25,6 +26,74 @@ const readJsonSafely = async (response) => {
|
|
|
25
26
|
return null;
|
|
26
27
|
}
|
|
27
28
|
};
|
|
29
|
+
const toErrorMessage = (error) => {
|
|
30
|
+
if (error instanceof Error && error.message) return error.message;
|
|
31
|
+
if (typeof error === "string" && error.length > 0) return error;
|
|
32
|
+
return "Unknown network error";
|
|
33
|
+
};
|
|
34
|
+
const readMessageFromPayload = (payload) => {
|
|
35
|
+
if (payload && typeof payload === "object" && "message" in payload && typeof payload.message === "string" && payload.message.length > 0) return payload.message;
|
|
36
|
+
return null;
|
|
37
|
+
};
|
|
38
|
+
const toAbsoluteUrl = (url) => {
|
|
39
|
+
try {
|
|
40
|
+
if (typeof window !== "undefined" && window.location?.origin) return new URL(url, window.location.origin);
|
|
41
|
+
return new URL(url, "http://fragno.local");
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const validateProxyContentUrl = (url) => {
|
|
47
|
+
const parsed = toAbsoluteUrl(url);
|
|
48
|
+
if (!parsed) return `Proxy upload endpoint '${url}' is not a valid URL.`;
|
|
49
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return `Proxy upload endpoint '${url}' must use http:// or https://.`;
|
|
50
|
+
if (typeof window !== "undefined" && window.location?.protocol === "https:" && parsed.protocol === "http:") return `Proxy upload endpoint '${url}' uses http:// on a https:// page.`;
|
|
51
|
+
return null;
|
|
52
|
+
};
|
|
53
|
+
const buildProxyUploadErrorMessage = (input) => {
|
|
54
|
+
const detailLines = [];
|
|
55
|
+
if (typeof input.status === "number") detailLines.push(`response status ${input.status}`);
|
|
56
|
+
if (input.streamError !== void 0) detailLines.push(`streamed upload error: ${toErrorMessage(input.streamError)}`);
|
|
57
|
+
if (input.fallbackError !== void 0) detailLines.push(`buffered upload error: ${toErrorMessage(input.fallbackError)}`);
|
|
58
|
+
const details = detailLines.length > 0 ? ` Details: ${detailLines.join(" | ")}.` : "";
|
|
59
|
+
return `${PROXY_UPLOAD_STRATEGY_HINT} Proxy upload to '${input.endpointUrl}' failed.${details} ${PROXY_UPLOAD_RECOVERY_HINT}`;
|
|
60
|
+
};
|
|
61
|
+
const buildDownloadRequestErrorMessage = (input) => {
|
|
62
|
+
const detailLines = [];
|
|
63
|
+
if (typeof input.status === "number") detailLines.push(`response status ${input.status}`);
|
|
64
|
+
const payloadMessage = readMessageFromPayload(input.payload);
|
|
65
|
+
if (payloadMessage) detailLines.push(`message: ${payloadMessage}`);
|
|
66
|
+
if (input.requestError !== void 0) detailLines.push(`request error: ${toErrorMessage(input.requestError)}`);
|
|
67
|
+
const details = detailLines.length > 0 ? ` Details: ${detailLines.join(" | ")}.` : "";
|
|
68
|
+
const hint = input.hint ? ` ${input.hint}` : "";
|
|
69
|
+
return `Download request to '${input.endpointUrl}' failed.${details}${hint}`;
|
|
70
|
+
};
|
|
71
|
+
const sanitizeSignedDownloadUrl = (url) => {
|
|
72
|
+
try {
|
|
73
|
+
const parsed = new URL(url);
|
|
74
|
+
parsed.search = "";
|
|
75
|
+
parsed.hash = "";
|
|
76
|
+
return parsed.toString();
|
|
77
|
+
} catch {
|
|
78
|
+
return "<redacted-signed-url>";
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const readErrorCodeFromPayload = (payload) => {
|
|
82
|
+
if (payload && typeof payload === "object" && "code" in payload) {
|
|
83
|
+
const code = payload.code;
|
|
84
|
+
if (typeof code === "string" && code.length > 0) return code;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
88
|
+
const hasText = (value) => typeof value === "string" && value.trim().length > 0;
|
|
89
|
+
const requireUploadEndpoint = (endpoint, label) => {
|
|
90
|
+
if (!hasText(endpoint)) throw new Error(`Missing ${label} endpoint for upload`);
|
|
91
|
+
return endpoint;
|
|
92
|
+
};
|
|
93
|
+
const buildByKeyQuery = (provider, fileKey) => new URLSearchParams({
|
|
94
|
+
provider,
|
|
95
|
+
key: fileKey
|
|
96
|
+
}).toString();
|
|
28
97
|
const createUploadHelpers = (input) => {
|
|
29
98
|
const { buildUrl, fetcher, defaultOptions } = input;
|
|
30
99
|
const fetchJson = async (path, init, expectedErrorCode) => {
|
|
@@ -38,9 +107,9 @@ const createUploadHelpers = (input) => {
|
|
|
38
107
|
}
|
|
39
108
|
return await response.json();
|
|
40
109
|
};
|
|
41
|
-
const reportProgress = async (
|
|
110
|
+
const reportProgress = async (progressEndpoint, progress, onProgress) => {
|
|
42
111
|
onProgress?.(progress);
|
|
43
|
-
await fetchJson(
|
|
112
|
+
await fetchJson(progressEndpoint, {
|
|
44
113
|
method: "POST",
|
|
45
114
|
headers: { "Content-Type": "application/json" },
|
|
46
115
|
body: JSON.stringify({
|
|
@@ -53,12 +122,13 @@ const createUploadHelpers = (input) => {
|
|
|
53
122
|
const filename = options.filename ?? (typeof File !== "undefined" && file instanceof File && file.name ? file.name : "upload");
|
|
54
123
|
const contentType = options.contentType ?? (file.type && file.type.length > 0 ? file.type : void 0) ?? DEFAULT_CONTENT_TYPE;
|
|
55
124
|
const sizeBytes = file.size;
|
|
56
|
-
if (!options.
|
|
125
|
+
if (!hasText(options.provider)) throw new Error("Provider is required");
|
|
126
|
+
if (!hasText(options.fileKey)) throw new Error("File key is required");
|
|
57
127
|
const upload = await fetchJson("/uploads", {
|
|
58
128
|
method: "POST",
|
|
59
129
|
headers: { "Content-Type": "application/json" },
|
|
60
130
|
body: JSON.stringify({
|
|
61
|
-
|
|
131
|
+
provider: options.provider,
|
|
62
132
|
fileKey: options.fileKey,
|
|
63
133
|
filename,
|
|
64
134
|
sizeBytes,
|
|
@@ -73,6 +143,7 @@ const createUploadHelpers = (input) => {
|
|
|
73
143
|
const totalBytes = sizeBytes;
|
|
74
144
|
let bytesUploaded = 0;
|
|
75
145
|
let partsUploaded = 0;
|
|
146
|
+
const progressEndpoint = requireUploadEndpoint(upload.upload.progressEndpoint, "progress");
|
|
76
147
|
if (upload.strategy === "direct-single") {
|
|
77
148
|
if (!upload.upload.uploadUrl) throw new Error("Missing upload URL for direct upload");
|
|
78
149
|
const uploadResponse = await fetcher(upload.upload.uploadUrl, {
|
|
@@ -83,7 +154,7 @@ const createUploadHelpers = (input) => {
|
|
|
83
154
|
if (!uploadResponse.ok) throw new Error(`Direct upload failed (${uploadResponse.status})`);
|
|
84
155
|
bytesUploaded = totalBytes;
|
|
85
156
|
partsUploaded = 1;
|
|
86
|
-
await reportProgress(
|
|
157
|
+
await reportProgress(progressEndpoint, {
|
|
87
158
|
bytesUploaded,
|
|
88
159
|
totalBytes,
|
|
89
160
|
partsUploaded,
|
|
@@ -101,6 +172,7 @@ const createUploadHelpers = (input) => {
|
|
|
101
172
|
if (upload.strategy === "direct-multipart") {
|
|
102
173
|
const partSizeBytes = upload.upload.partSizeBytes;
|
|
103
174
|
if (!partSizeBytes || !upload.upload.partsEndpoint) throw new Error("Missing multipart configuration for upload");
|
|
175
|
+
const partsCompleteEndpoint = requireUploadEndpoint(upload.upload.partsCompleteEndpoint, "multipart completion");
|
|
104
176
|
const totalParts = Math.ceil(totalBytes / partSizeBytes);
|
|
105
177
|
if (upload.upload.maxParts && totalParts > upload.upload.maxParts) throw new Error("Multipart upload exceeds maximum parts");
|
|
106
178
|
const partNumbers = Array.from({ length: totalParts }, (_, i) => i + 1);
|
|
@@ -135,14 +207,14 @@ const createUploadHelpers = (input) => {
|
|
|
135
207
|
});
|
|
136
208
|
bytesUploaded += partSize;
|
|
137
209
|
partsUploaded += 1;
|
|
138
|
-
await reportProgress(
|
|
210
|
+
await reportProgress(progressEndpoint, {
|
|
139
211
|
bytesUploaded,
|
|
140
212
|
totalBytes,
|
|
141
213
|
partsUploaded,
|
|
142
214
|
totalParts
|
|
143
215
|
}, options.onProgress);
|
|
144
216
|
}
|
|
145
|
-
await fetchJson(
|
|
217
|
+
await fetchJson(partsCompleteEndpoint, {
|
|
146
218
|
method: "POST",
|
|
147
219
|
headers: { "Content-Type": "application/json" },
|
|
148
220
|
body: JSON.stringify({ parts: completedParts })
|
|
@@ -157,6 +229,9 @@ const createUploadHelpers = (input) => {
|
|
|
157
229
|
};
|
|
158
230
|
}
|
|
159
231
|
if (!upload.upload.contentEndpoint) throw new Error("Missing proxy content endpoint for upload");
|
|
232
|
+
const proxyContentUrl = buildUrl(upload.upload.contentEndpoint);
|
|
233
|
+
const proxyUrlValidationError = validateProxyContentUrl(proxyContentUrl);
|
|
234
|
+
if (proxyUrlValidationError) throw new Error(`${proxyUrlValidationError} ${PROXY_UPLOAD_RECOVERY_HINT}`);
|
|
160
235
|
const source = file.stream();
|
|
161
236
|
const stream = new ReadableStream({ start(controller) {
|
|
162
237
|
const reader = source.getReader();
|
|
@@ -190,14 +265,27 @@ const createUploadHelpers = (input) => {
|
|
|
190
265
|
requestInit.duplex = "half";
|
|
191
266
|
let proxyResponse;
|
|
192
267
|
try {
|
|
193
|
-
proxyResponse = await fetcher(
|
|
194
|
-
} catch (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
268
|
+
proxyResponse = await fetcher(proxyContentUrl, requestInit);
|
|
269
|
+
} catch (streamError) {
|
|
270
|
+
let fallbackResponse;
|
|
271
|
+
try {
|
|
272
|
+
fallbackResponse = await fetcher(proxyContentUrl, buildRequestInit(defaultOptions, {
|
|
273
|
+
method: "PUT",
|
|
274
|
+
headers: { "Content-Type": DEFAULT_CONTENT_TYPE },
|
|
275
|
+
body: file
|
|
276
|
+
}));
|
|
277
|
+
} catch (fallbackError) {
|
|
278
|
+
throw new Error(buildProxyUploadErrorMessage({
|
|
279
|
+
endpointUrl: proxyContentUrl,
|
|
280
|
+
streamError,
|
|
281
|
+
fallbackError
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
if (!fallbackResponse.ok) throw new Error(buildProxyUploadErrorMessage({
|
|
285
|
+
endpointUrl: proxyContentUrl,
|
|
286
|
+
status: fallbackResponse.status,
|
|
287
|
+
streamError
|
|
199
288
|
}));
|
|
200
|
-
if (!fallbackResponse.ok) throw new Error(`Proxy upload failed (${fallbackResponse.status})`);
|
|
201
289
|
options.onProgress?.({
|
|
202
290
|
bytesUploaded: totalBytes,
|
|
203
291
|
totalBytes,
|
|
@@ -208,28 +296,86 @@ const createUploadHelpers = (input) => {
|
|
|
208
296
|
file: await fallbackResponse.json()
|
|
209
297
|
};
|
|
210
298
|
}
|
|
211
|
-
if (!proxyResponse.ok) throw new Error(
|
|
299
|
+
if (!proxyResponse.ok) throw new Error(buildProxyUploadErrorMessage({
|
|
300
|
+
endpointUrl: proxyContentUrl,
|
|
301
|
+
status: proxyResponse.status
|
|
302
|
+
}));
|
|
212
303
|
return {
|
|
213
304
|
upload,
|
|
214
305
|
file: await proxyResponse.json()
|
|
215
306
|
};
|
|
216
307
|
};
|
|
217
|
-
const downloadFile = async (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (
|
|
308
|
+
const downloadFile = async (fileKey, options) => {
|
|
309
|
+
if (!hasText(fileKey)) throw new Error("File key is required");
|
|
310
|
+
if (!options || !hasText(options.provider)) throw new Error("Download provider is required");
|
|
311
|
+
if (!options || options.method !== "signed-url" && options.method !== "content") throw new Error(`Download method is required. ${DOWNLOAD_METHOD_HINT}`);
|
|
312
|
+
const byKeyQuery = buildByKeyQuery(options.provider, fileKey);
|
|
313
|
+
if (options.method === "signed-url") {
|
|
314
|
+
const downloadUrlEndpoint = buildUrl(`/files/by-key/download-url?${byKeyQuery}`);
|
|
315
|
+
let downloadUrlResponse;
|
|
316
|
+
try {
|
|
317
|
+
downloadUrlResponse = await fetcher(downloadUrlEndpoint, buildRequestInit(defaultOptions, { method: "GET" }));
|
|
318
|
+
} catch (error) {
|
|
319
|
+
throw new Error(buildDownloadRequestErrorMessage({
|
|
320
|
+
endpointUrl: downloadUrlEndpoint,
|
|
321
|
+
requestError: error
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
324
|
+
if (!downloadUrlResponse.ok) {
|
|
325
|
+
const errorPayload = await readJsonSafely(downloadUrlResponse);
|
|
326
|
+
const hint = readErrorCodeFromPayload(errorPayload) === "SIGNED_URL_UNSUPPORTED" ? "Requested method 'signed-url' is unsupported by this storage adapter. This is a programming error. Use method 'content' when streaming downloads are available." : void 0;
|
|
327
|
+
throw new Error(buildDownloadRequestErrorMessage({
|
|
328
|
+
endpointUrl: downloadUrlEndpoint,
|
|
329
|
+
status: downloadUrlResponse.status,
|
|
330
|
+
payload: errorPayload,
|
|
331
|
+
hint
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
221
334
|
const payload = await downloadUrlResponse.json();
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
335
|
+
const sanitizedUrl = sanitizeSignedDownloadUrl(payload.url);
|
|
336
|
+
let response;
|
|
337
|
+
try {
|
|
338
|
+
response = await fetcher(payload.url, {
|
|
339
|
+
method: "GET",
|
|
340
|
+
headers: payload.headers
|
|
341
|
+
});
|
|
342
|
+
} catch (error) {
|
|
343
|
+
throw new Error(buildDownloadRequestErrorMessage({
|
|
344
|
+
endpointUrl: sanitizedUrl,
|
|
345
|
+
requestError: error
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
if (!response.ok) {
|
|
349
|
+
const payloadError = await readJsonSafely(response);
|
|
350
|
+
throw new Error(buildDownloadRequestErrorMessage({
|
|
351
|
+
endpointUrl: sanitizedUrl,
|
|
352
|
+
status: response.status,
|
|
353
|
+
payload: payloadError
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
return response;
|
|
226
357
|
}
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
358
|
+
const contentEndpoint = buildUrl(`/files/by-key/content?${byKeyQuery}`);
|
|
359
|
+
let contentResponse;
|
|
360
|
+
try {
|
|
361
|
+
contentResponse = await fetcher(contentEndpoint, buildRequestInit(defaultOptions, { method: "GET" }));
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw new Error(buildDownloadRequestErrorMessage({
|
|
364
|
+
endpointUrl: contentEndpoint,
|
|
365
|
+
requestError: error
|
|
366
|
+
}));
|
|
367
|
+
}
|
|
368
|
+
if (!contentResponse.ok) {
|
|
369
|
+
const contentError = await readJsonSafely(contentResponse);
|
|
370
|
+
const hint = readErrorCodeFromPayload(contentError) === "SIGNED_URL_UNSUPPORTED" ? "The 'content' download endpoint is unsupported by this storage adapter. This request used method 'content'. Use method 'signed-url' when signed downloads are available." : void 0;
|
|
371
|
+
throw new Error(buildDownloadRequestErrorMessage({
|
|
372
|
+
endpointUrl: contentEndpoint,
|
|
373
|
+
status: contentResponse.status,
|
|
374
|
+
payload: contentError,
|
|
375
|
+
hint
|
|
376
|
+
}));
|
|
231
377
|
}
|
|
232
|
-
return
|
|
378
|
+
return contentResponse;
|
|
233
379
|
};
|
|
234
380
|
return {
|
|
235
381
|
createUploadAndTransfer,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","names":["createUploadAndTransfer: UploadHelpers[\"createUploadAndTransfer\"]","completedParts: { partNumber: number; etag: string; sizeBytes: number }[]","completionParts: { partNumber: number; etag: string }[]","proxyResponse: Response","downloadFile: UploadHelpers[\"downloadFile\"]"],"sources":["../../../src/client/helpers.ts"],"sourcesContent":["import { encodeFileKey, type FileKeyEncoded, type FileKeyParts } from \"../keys\";\nimport type { UploadChecksum } from \"../storage/types\";\nimport type { FileMetadata, FileVisibility, UploadStrategy } from \"../types\";\n\nexport type UploadProgress = {\n bytesUploaded: number;\n totalBytes: number;\n partsUploaded: number;\n totalParts?: number;\n};\n\nexport type CreateUploadAndTransferOptions = {\n keyParts?: FileKeyParts;\n fileKey?: FileKeyEncoded;\n filename?: string;\n contentType?: string;\n checksum?: UploadChecksum | null;\n tags?: string[];\n visibility?: FileVisibility;\n uploaderId?: string;\n metadata?: Record<string, unknown>;\n onProgress?: (progress: UploadProgress) => void;\n};\n\nexport type UploadCreateResponse = {\n uploadId: string;\n fileKey: string;\n status: \"created\";\n strategy: UploadStrategy;\n expiresAt: string;\n upload: {\n mode: \"single\" | \"multipart\";\n transport: \"direct\" | \"proxy\";\n uploadUrl?: string;\n uploadHeaders?: Record<string, string>;\n partSizeBytes?: number;\n maxParts?: number;\n partsEndpoint?: string;\n completeEndpoint: string;\n contentEndpoint?: string;\n };\n};\n\nexport type UploadHelpers = {\n createUploadAndTransfer: (\n file: Blob,\n options: CreateUploadAndTransferOptions,\n ) => Promise<{ upload: UploadCreateResponse; file: FileMetadata }>;\n downloadFile: (fileKeyOrParts: FileKeyEncoded | FileKeyParts) => Promise<Response>;\n};\n\nconst DEFAULT_CONTENT_TYPE = \"application/octet-stream\";\n\nconst mergeHeaders = (base?: HeadersInit, next?: HeadersInit) => {\n const merged = new Headers(base ?? undefined);\n\n if (!next) {\n return merged;\n }\n\n if (next instanceof Headers) {\n for (const [key, value] of next.entries()) {\n merged.set(key, value);\n }\n } else if (Array.isArray(next)) {\n for (const [key, value] of next) {\n merged.set(key, value);\n }\n } else {\n for (const [key, value] of Object.entries(next)) {\n merged.set(key, value);\n }\n }\n\n return merged;\n};\n\nconst buildRequestInit = (defaults: RequestInit | undefined, init: RequestInit): RequestInit => {\n if (!defaults) {\n return init;\n }\n\n return {\n ...defaults,\n ...init,\n headers: mergeHeaders(defaults.headers, init.headers),\n };\n};\n\nconst readJsonSafely = async (response: Response) => {\n try {\n return (await response.json()) as unknown;\n } catch {\n return null;\n }\n};\n\nexport const createUploadHelpers = (input: {\n buildUrl: (path: string) => string;\n fetcher: typeof fetch;\n defaultOptions?: RequestInit;\n}): UploadHelpers => {\n const { buildUrl, fetcher, defaultOptions } = input;\n\n const fetchJson = async <T>(\n path: string,\n init: RequestInit,\n expectedErrorCode?: string,\n ): Promise<T> => {\n const response = await fetcher(buildUrl(path), buildRequestInit(defaultOptions, init));\n\n if (!response.ok) {\n const payload = await readJsonSafely(response);\n const code =\n typeof payload === \"object\" && payload ? (payload as { code?: string }).code : undefined;\n if (expectedErrorCode && code === expectedErrorCode) {\n throw new Error(expectedErrorCode);\n }\n const message =\n typeof payload === \"object\" && payload && \"message\" in payload\n ? String((payload as { message?: string }).message)\n : `Request failed (${response.status})`;\n throw new Error(message);\n }\n\n return (await response.json()) as T;\n };\n\n const reportProgress = async (\n uploadId: string,\n progress: UploadProgress,\n onProgress?: (progress: UploadProgress) => void,\n ) => {\n onProgress?.(progress);\n await fetchJson<{ bytesUploaded: number; partsUploaded: number }>(\n `/uploads/${uploadId}/progress`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n bytesUploaded: progress.bytesUploaded,\n partsUploaded: progress.partsUploaded,\n }),\n },\n );\n };\n\n const createUploadAndTransfer: UploadHelpers[\"createUploadAndTransfer\"] = async (\n file,\n options,\n ) => {\n const filename =\n options.filename ??\n (typeof File !== \"undefined\" && file instanceof File && file.name ? file.name : \"upload\");\n const contentType =\n options.contentType ??\n (file.type && file.type.length > 0 ? file.type : undefined) ??\n DEFAULT_CONTENT_TYPE;\n const sizeBytes = file.size;\n\n if (!options.keyParts && !options.fileKey) {\n throw new Error(\"File key parts or file key is required\");\n }\n\n const upload = await fetchJson<UploadCreateResponse>(\"/uploads\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n keyParts: options.keyParts,\n fileKey: options.fileKey,\n filename,\n sizeBytes,\n contentType,\n checksum: options.checksum ?? null,\n tags: options.tags,\n visibility: options.visibility,\n uploaderId: options.uploaderId,\n metadata: options.metadata,\n }),\n });\n\n const totalBytes = sizeBytes;\n let bytesUploaded = 0;\n let partsUploaded = 0;\n\n if (upload.strategy === \"direct-single\") {\n if (!upload.upload.uploadUrl) {\n throw new Error(\"Missing upload URL for direct upload\");\n }\n\n const uploadResponse = await fetcher(upload.upload.uploadUrl, {\n method: \"PUT\",\n headers: upload.upload.uploadHeaders,\n body: file,\n });\n\n if (!uploadResponse.ok) {\n throw new Error(`Direct upload failed (${uploadResponse.status})`);\n }\n\n bytesUploaded = totalBytes;\n partsUploaded = 1;\n await reportProgress(\n upload.uploadId,\n {\n bytesUploaded,\n totalBytes,\n partsUploaded,\n totalParts: 1,\n },\n options.onProgress,\n );\n\n const fileMetadata = await fetchJson<FileMetadata>(upload.upload.completeEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({}),\n });\n\n return { upload, file: fileMetadata };\n }\n\n if (upload.strategy === \"direct-multipart\") {\n const partSizeBytes = upload.upload.partSizeBytes;\n if (!partSizeBytes || !upload.upload.partsEndpoint) {\n throw new Error(\"Missing multipart configuration for upload\");\n }\n\n const totalParts = Math.ceil(totalBytes / partSizeBytes);\n if (upload.upload.maxParts && totalParts > upload.upload.maxParts) {\n throw new Error(\"Multipart upload exceeds maximum parts\");\n }\n\n const partNumbers = Array.from({ length: totalParts }, (_, i) => i + 1);\n const partUrls = await fetchJson<{\n parts: { partNumber: number; url: string; headers?: Record<string, string> }[];\n }>(upload.upload.partsEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ partNumbers }),\n });\n\n const parts = partUrls.parts.sort((a, b) => a.partNumber - b.partNumber);\n const completedParts: { partNumber: number; etag: string; sizeBytes: number }[] = [];\n const completionParts: { partNumber: number; etag: string }[] = [];\n\n for (const part of parts) {\n const start = (part.partNumber - 1) * partSizeBytes;\n const end = Math.min(start + partSizeBytes, totalBytes);\n const partBlob = file.slice(start, end);\n\n const response = await fetcher(part.url, {\n method: \"PUT\",\n headers: part.headers,\n body: partBlob,\n });\n\n if (!response.ok) {\n throw new Error(`Multipart upload failed (${response.status})`);\n }\n\n const etag = response.headers.get(\"ETag\") ?? response.headers.get(\"etag\");\n if (!etag) {\n throw new Error(\"Missing ETag for uploaded part\");\n }\n\n const partSize = partBlob.size;\n completedParts.push({ partNumber: part.partNumber, etag, sizeBytes: partSize });\n completionParts.push({ partNumber: part.partNumber, etag });\n\n bytesUploaded += partSize;\n partsUploaded += 1;\n await reportProgress(\n upload.uploadId,\n {\n bytesUploaded,\n totalBytes,\n partsUploaded,\n totalParts,\n },\n options.onProgress,\n );\n }\n\n await fetchJson<{ bytesUploaded: number; partsUploaded: number }>(\n `/uploads/${upload.uploadId}/parts/complete`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ parts: completedParts }),\n },\n );\n\n const fileMetadata = await fetchJson<FileMetadata>(upload.upload.completeEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ parts: completionParts }),\n });\n\n return { upload, file: fileMetadata };\n }\n\n if (!upload.upload.contentEndpoint) {\n throw new Error(\"Missing proxy content endpoint for upload\");\n }\n\n const source = file.stream();\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const reader = source.getReader();\n\n const pump = (): void => {\n reader.read().then(\n ({ done, value }) => {\n if (done) {\n controller.close();\n return;\n }\n\n if (value) {\n bytesUploaded += value.byteLength;\n options.onProgress?.({\n bytesUploaded,\n totalBytes,\n partsUploaded: 0,\n });\n controller.enqueue(value);\n }\n\n pump();\n },\n (error) => {\n controller.error(error);\n },\n );\n };\n\n pump();\n },\n });\n\n const requestInit = buildRequestInit(defaultOptions, {\n method: \"PUT\",\n headers: { \"Content-Type\": DEFAULT_CONTENT_TYPE },\n body: stream,\n }) as RequestInit & { duplex?: \"half\" };\n requestInit.duplex = \"half\";\n\n let proxyResponse: Response;\n try {\n proxyResponse = await fetcher(buildUrl(upload.upload.contentEndpoint), requestInit);\n } catch (_error) {\n const fallbackResponse = await fetcher(\n buildUrl(upload.upload.contentEndpoint),\n buildRequestInit(defaultOptions, {\n method: \"PUT\",\n headers: { \"Content-Type\": DEFAULT_CONTENT_TYPE },\n body: file,\n }),\n );\n\n if (!fallbackResponse.ok) {\n throw new Error(`Proxy upload failed (${fallbackResponse.status})`);\n }\n\n options.onProgress?.({\n bytesUploaded: totalBytes,\n totalBytes,\n partsUploaded: 0,\n });\n\n const fallbackMetadata = (await fallbackResponse.json()) as FileMetadata;\n return { upload, file: fallbackMetadata };\n }\n\n if (!proxyResponse.ok) {\n throw new Error(`Proxy upload failed (${proxyResponse.status})`);\n }\n\n const proxyMetadata = (await proxyResponse.json()) as FileMetadata;\n\n return { upload, file: proxyMetadata };\n };\n\n const downloadFile: UploadHelpers[\"downloadFile\"] = async (fileKeyOrParts) => {\n const fileKey = Array.isArray(fileKeyOrParts) ? encodeFileKey(fileKeyOrParts) : fileKeyOrParts;\n\n const downloadUrlResponse = await fetcher(\n buildUrl(`/files/${fileKey}/download-url`),\n buildRequestInit(defaultOptions, { method: \"GET\" }),\n );\n\n if (downloadUrlResponse.ok) {\n const payload = (await downloadUrlResponse.json()) as {\n url: string;\n headers?: Record<string, string>;\n };\n\n return fetcher(payload.url, {\n method: \"GET\",\n headers: payload.headers,\n });\n }\n\n const errorPayload = await readJsonSafely(downloadUrlResponse);\n const errorCode =\n typeof errorPayload === \"object\" && errorPayload\n ? (errorPayload as { code?: string }).code\n : undefined;\n\n if (errorCode !== \"SIGNED_URL_UNSUPPORTED\") {\n const message =\n typeof errorPayload === \"object\" && errorPayload && \"message\" in errorPayload\n ? String((errorPayload as { message?: string }).message)\n : `Download failed (${downloadUrlResponse.status})`;\n throw new Error(message);\n }\n\n return fetcher(\n buildUrl(`/files/${fileKey}/content`),\n buildRequestInit(defaultOptions, { method: \"GET\" }),\n );\n };\n\n return {\n createUploadAndTransfer,\n downloadFile,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"helpers.js","names":["detailLines: string[]","createUploadAndTransfer: UploadHelpers[\"createUploadAndTransfer\"]","completedParts: {\n partNumber: number;\n etag: string;\n sizeBytes: number;\n }[]","completionParts: { partNumber: number; etag: string }[]","proxyResponse: Response","fallbackResponse: Response","downloadFile: UploadHelpers[\"downloadFile\"]","downloadUrlResponse: Response","response: Response","contentResponse: Response"],"sources":["../../../src/client/helpers.ts"],"sourcesContent":["import type { UploadChecksum } from \"../storage/types\";\nimport type { FileMetadata, FileVisibility, UploadStrategy } from \"../types\";\n\nexport type UploadProgress = {\n bytesUploaded: number;\n totalBytes: number;\n partsUploaded: number;\n totalParts?: number;\n};\n\nexport type CreateUploadAndTransferOptions = {\n provider: string;\n fileKey: string;\n filename?: string;\n contentType?: string;\n checksum?: UploadChecksum | null;\n tags?: string[];\n visibility?: FileVisibility;\n uploaderId?: string;\n metadata?: Record<string, unknown>;\n onProgress?: (progress: UploadProgress) => void;\n};\n\nexport type DownloadMethod = \"signed-url\" | \"content\";\n\nexport type DownloadFileOptions = {\n provider: string;\n method: DownloadMethod;\n};\n\nexport type UploadCreateResponse = {\n uploadId: string;\n fileKey: string;\n provider: string;\n status: \"created\" | \"in_progress\";\n strategy: UploadStrategy;\n expiresAt: string;\n upload: {\n mode: \"single\" | \"multipart\";\n transport: \"direct\" | \"proxy\";\n uploadUrl?: string;\n uploadHeaders?: Record<string, string>;\n partSizeBytes?: number;\n maxParts?: number;\n statusEndpoint: string;\n progressEndpoint: string;\n partsEndpoint?: string;\n partsCompleteEndpoint?: string;\n completeEndpoint: string;\n abortEndpoint: string;\n contentEndpoint?: string;\n };\n};\n\nexport type UploadHelpers = {\n createUploadAndTransfer: (\n file: Blob,\n options: CreateUploadAndTransferOptions,\n ) => Promise<{ upload: UploadCreateResponse; file: FileMetadata }>;\n downloadFile: (fileKey: string, options: DownloadFileOptions) => Promise<Response>;\n};\n\nconst DEFAULT_CONTENT_TYPE = \"application/octet-stream\";\nconst PROXY_UPLOAD_STRATEGY_HINT =\n \"Server selected proxy upload strategy for this file (no direct upload URL was returned). If you expected direct-to-storage upload, your active provider/config does not support direct upload for this request.\";\nconst PROXY_UPLOAD_RECOVERY_HINT =\n \"Verify the /uploads/:uploadId/content endpoint is reachable from the client, or switch provider/config so /uploads returns a direct strategy.\";\nconst DOWNLOAD_METHOD_HINT =\n \"Pick the download method explicitly: use 'signed-url' for GET /files/by-key/download-url, otherwise use 'content' for GET /files/by-key/content.\";\n\nconst mergeHeaders = (base?: HeadersInit, next?: HeadersInit) => {\n const merged = new Headers(base ?? undefined);\n\n if (!next) {\n return merged;\n }\n\n if (next instanceof Headers) {\n for (const [key, value] of next.entries()) {\n merged.set(key, value);\n }\n } else if (Array.isArray(next)) {\n for (const [key, value] of next) {\n merged.set(key, value);\n }\n } else {\n for (const [key, value] of Object.entries(next)) {\n merged.set(key, value);\n }\n }\n\n return merged;\n};\n\nconst buildRequestInit = (defaults: RequestInit | undefined, init: RequestInit): RequestInit => {\n if (!defaults) {\n return init;\n }\n\n return {\n ...defaults,\n ...init,\n headers: mergeHeaders(defaults.headers, init.headers),\n };\n};\n\nconst readJsonSafely = async (response: Response) => {\n try {\n return (await response.json()) as unknown;\n } catch {\n return null;\n }\n};\n\nconst toErrorMessage = (error: unknown) => {\n if (error instanceof Error && error.message) {\n return error.message;\n }\n\n if (typeof error === \"string\" && error.length > 0) {\n return error;\n }\n\n return \"Unknown network error\";\n};\n\nconst readMessageFromPayload = (payload: unknown): string | null => {\n if (\n payload &&\n typeof payload === \"object\" &&\n \"message\" in payload &&\n typeof payload.message === \"string\" &&\n payload.message.length > 0\n ) {\n return payload.message;\n }\n\n return null;\n};\n\nconst toAbsoluteUrl = (url: string): URL | null => {\n try {\n if (typeof window !== \"undefined\" && window.location?.origin) {\n return new URL(url, window.location.origin);\n }\n\n return new URL(url, \"http://fragno.local\");\n } catch {\n return null;\n }\n};\n\nconst validateProxyContentUrl = (url: string): string | null => {\n const parsed = toAbsoluteUrl(url);\n if (!parsed) {\n return `Proxy upload endpoint '${url}' is not a valid URL.`;\n }\n\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return `Proxy upload endpoint '${url}' must use http:// or https://.`;\n }\n\n if (\n typeof window !== \"undefined\" &&\n window.location?.protocol === \"https:\" &&\n parsed.protocol === \"http:\"\n ) {\n return `Proxy upload endpoint '${url}' uses http:// on a https:// page.`;\n }\n\n return null;\n};\n\nconst buildProxyUploadErrorMessage = (input: {\n endpointUrl: string;\n status?: number;\n streamError?: unknown;\n fallbackError?: unknown;\n}) => {\n const detailLines: string[] = [];\n if (typeof input.status === \"number\") {\n detailLines.push(`response status ${input.status}`);\n }\n if (input.streamError !== undefined) {\n detailLines.push(`streamed upload error: ${toErrorMessage(input.streamError)}`);\n }\n if (input.fallbackError !== undefined) {\n detailLines.push(`buffered upload error: ${toErrorMessage(input.fallbackError)}`);\n }\n\n const details = detailLines.length > 0 ? ` Details: ${detailLines.join(\" | \")}.` : \"\";\n return `${PROXY_UPLOAD_STRATEGY_HINT} Proxy upload to '${input.endpointUrl}' failed.${details} ${PROXY_UPLOAD_RECOVERY_HINT}`;\n};\n\nconst buildDownloadRequestErrorMessage = (input: {\n endpointUrl: string;\n status?: number;\n payload?: unknown;\n requestError?: unknown;\n hint?: string;\n}) => {\n const detailLines: string[] = [];\n if (typeof input.status === \"number\") {\n detailLines.push(`response status ${input.status}`);\n }\n\n const payloadMessage = readMessageFromPayload(input.payload);\n if (payloadMessage) {\n detailLines.push(`message: ${payloadMessage}`);\n }\n\n if (input.requestError !== undefined) {\n detailLines.push(`request error: ${toErrorMessage(input.requestError)}`);\n }\n\n const details = detailLines.length > 0 ? ` Details: ${detailLines.join(\" | \")}.` : \"\";\n const hint = input.hint ? ` ${input.hint}` : \"\";\n return `Download request to '${input.endpointUrl}' failed.${details}${hint}`;\n};\n\nconst sanitizeSignedDownloadUrl = (url: string) => {\n try {\n const parsed = new URL(url);\n parsed.search = \"\";\n parsed.hash = \"\";\n return parsed.toString();\n } catch {\n return \"<redacted-signed-url>\";\n }\n};\n\nconst readErrorCodeFromPayload = (payload: unknown): string | null => {\n if (payload && typeof payload === \"object\" && \"code\" in payload) {\n const code = (payload as { code?: unknown }).code;\n if (typeof code === \"string\" && code.length > 0) {\n return code;\n }\n }\n\n return null;\n};\n\nconst hasText = (value: string | undefined | null): value is string =>\n typeof value === \"string\" && value.trim().length > 0;\n\nconst requireUploadEndpoint = (endpoint: string | undefined, label: string) => {\n if (!hasText(endpoint)) {\n throw new Error(`Missing ${label} endpoint for upload`);\n }\n\n return endpoint;\n};\n\nconst buildByKeyQuery = (provider: string, fileKey: string) =>\n new URLSearchParams({ provider, key: fileKey }).toString();\n\nexport const createUploadHelpers = (input: {\n buildUrl: (path: string) => string;\n fetcher: typeof fetch;\n defaultOptions?: RequestInit;\n}): UploadHelpers => {\n const { buildUrl, fetcher, defaultOptions } = input;\n\n const fetchJson = async <T>(\n path: string,\n init: RequestInit,\n expectedErrorCode?: string,\n ): Promise<T> => {\n const response = await fetcher(buildUrl(path), buildRequestInit(defaultOptions, init));\n\n if (!response.ok) {\n const payload = await readJsonSafely(response);\n const code =\n typeof payload === \"object\" && payload ? (payload as { code?: string }).code : undefined;\n if (expectedErrorCode && code === expectedErrorCode) {\n throw new Error(expectedErrorCode);\n }\n const message =\n typeof payload === \"object\" && payload && \"message\" in payload\n ? String((payload as { message?: string }).message)\n : `Request failed (${response.status})`;\n throw new Error(message);\n }\n\n return (await response.json()) as T;\n };\n\n const reportProgress = async (\n progressEndpoint: string,\n progress: UploadProgress,\n onProgress?: (progress: UploadProgress) => void,\n ) => {\n onProgress?.(progress);\n await fetchJson<{ bytesUploaded: number; partsUploaded: number }>(progressEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n bytesUploaded: progress.bytesUploaded,\n partsUploaded: progress.partsUploaded,\n }),\n });\n };\n\n const createUploadAndTransfer: UploadHelpers[\"createUploadAndTransfer\"] = async (\n file,\n options,\n ) => {\n const filename =\n options.filename ??\n (typeof File !== \"undefined\" && file instanceof File && file.name ? file.name : \"upload\");\n const contentType =\n options.contentType ??\n (file.type && file.type.length > 0 ? file.type : undefined) ??\n DEFAULT_CONTENT_TYPE;\n const sizeBytes = file.size;\n\n if (!hasText(options.provider)) {\n throw new Error(\"Provider is required\");\n }\n\n if (!hasText(options.fileKey)) {\n throw new Error(\"File key is required\");\n }\n\n const upload = await fetchJson<UploadCreateResponse>(\"/uploads\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n provider: options.provider,\n fileKey: options.fileKey,\n filename,\n sizeBytes,\n contentType,\n checksum: options.checksum ?? null,\n tags: options.tags,\n visibility: options.visibility,\n uploaderId: options.uploaderId,\n metadata: options.metadata,\n }),\n });\n\n const totalBytes = sizeBytes;\n let bytesUploaded = 0;\n let partsUploaded = 0;\n const progressEndpoint = requireUploadEndpoint(upload.upload.progressEndpoint, \"progress\");\n\n if (upload.strategy === \"direct-single\") {\n if (!upload.upload.uploadUrl) {\n throw new Error(\"Missing upload URL for direct upload\");\n }\n\n const uploadResponse = await fetcher(upload.upload.uploadUrl, {\n method: \"PUT\",\n headers: upload.upload.uploadHeaders,\n body: file,\n });\n\n if (!uploadResponse.ok) {\n throw new Error(`Direct upload failed (${uploadResponse.status})`);\n }\n\n bytesUploaded = totalBytes;\n partsUploaded = 1;\n await reportProgress(\n progressEndpoint,\n {\n bytesUploaded,\n totalBytes,\n partsUploaded,\n totalParts: 1,\n },\n options.onProgress,\n );\n\n const fileMetadata = await fetchJson<FileMetadata>(upload.upload.completeEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({}),\n });\n\n return { upload, file: fileMetadata };\n }\n\n if (upload.strategy === \"direct-multipart\") {\n const partSizeBytes = upload.upload.partSizeBytes;\n if (!partSizeBytes || !upload.upload.partsEndpoint) {\n throw new Error(\"Missing multipart configuration for upload\");\n }\n const partsCompleteEndpoint = requireUploadEndpoint(\n upload.upload.partsCompleteEndpoint,\n \"multipart completion\",\n );\n\n const totalParts = Math.ceil(totalBytes / partSizeBytes);\n if (upload.upload.maxParts && totalParts > upload.upload.maxParts) {\n throw new Error(\"Multipart upload exceeds maximum parts\");\n }\n\n const partNumbers = Array.from({ length: totalParts }, (_, i) => i + 1);\n const partUrls = await fetchJson<{\n parts: {\n partNumber: number;\n url: string;\n headers?: Record<string, string>;\n }[];\n }>(upload.upload.partsEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ partNumbers }),\n });\n\n const parts = partUrls.parts.sort((a, b) => a.partNumber - b.partNumber);\n const completedParts: {\n partNumber: number;\n etag: string;\n sizeBytes: number;\n }[] = [];\n const completionParts: { partNumber: number; etag: string }[] = [];\n\n for (const part of parts) {\n const start = (part.partNumber - 1) * partSizeBytes;\n const end = Math.min(start + partSizeBytes, totalBytes);\n const partBlob = file.slice(start, end);\n\n const response = await fetcher(part.url, {\n method: \"PUT\",\n headers: part.headers,\n body: partBlob,\n });\n\n if (!response.ok) {\n throw new Error(`Multipart upload failed (${response.status})`);\n }\n\n const etag = response.headers.get(\"ETag\") ?? response.headers.get(\"etag\");\n if (!etag) {\n throw new Error(\"Missing ETag for uploaded part\");\n }\n\n const partSize = partBlob.size;\n completedParts.push({\n partNumber: part.partNumber,\n etag,\n sizeBytes: partSize,\n });\n completionParts.push({ partNumber: part.partNumber, etag });\n\n bytesUploaded += partSize;\n partsUploaded += 1;\n await reportProgress(\n progressEndpoint,\n {\n bytesUploaded,\n totalBytes,\n partsUploaded,\n totalParts,\n },\n options.onProgress,\n );\n }\n\n await fetchJson<{ bytesUploaded: number; partsUploaded: number }>(partsCompleteEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ parts: completedParts }),\n });\n\n const fileMetadata = await fetchJson<FileMetadata>(upload.upload.completeEndpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ parts: completionParts }),\n });\n\n return { upload, file: fileMetadata };\n }\n\n if (!upload.upload.contentEndpoint) {\n throw new Error(\"Missing proxy content endpoint for upload\");\n }\n\n const proxyContentUrl = buildUrl(upload.upload.contentEndpoint);\n const proxyUrlValidationError = validateProxyContentUrl(proxyContentUrl);\n if (proxyUrlValidationError) {\n throw new Error(`${proxyUrlValidationError} ${PROXY_UPLOAD_RECOVERY_HINT}`);\n }\n\n const source = file.stream();\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n const reader = source.getReader();\n\n const pump = (): void => {\n reader.read().then(\n ({ done, value }) => {\n if (done) {\n controller.close();\n return;\n }\n\n if (value) {\n bytesUploaded += value.byteLength;\n options.onProgress?.({\n bytesUploaded,\n totalBytes,\n partsUploaded: 0,\n });\n controller.enqueue(value);\n }\n\n pump();\n },\n (error) => {\n controller.error(error);\n },\n );\n };\n\n pump();\n },\n });\n\n const requestInit = buildRequestInit(defaultOptions, {\n method: \"PUT\",\n headers: { \"Content-Type\": DEFAULT_CONTENT_TYPE },\n body: stream,\n }) as RequestInit & { duplex?: \"half\" };\n requestInit.duplex = \"half\";\n\n let proxyResponse: Response;\n try {\n proxyResponse = await fetcher(proxyContentUrl, requestInit);\n } catch (streamError) {\n let fallbackResponse: Response;\n try {\n fallbackResponse = await fetcher(\n proxyContentUrl,\n buildRequestInit(defaultOptions, {\n method: \"PUT\",\n headers: { \"Content-Type\": DEFAULT_CONTENT_TYPE },\n body: file,\n }),\n );\n } catch (fallbackError) {\n throw new Error(\n buildProxyUploadErrorMessage({\n endpointUrl: proxyContentUrl,\n streamError,\n fallbackError,\n }),\n );\n }\n\n if (!fallbackResponse.ok) {\n throw new Error(\n buildProxyUploadErrorMessage({\n endpointUrl: proxyContentUrl,\n status: fallbackResponse.status,\n streamError,\n }),\n );\n }\n\n options.onProgress?.({\n bytesUploaded: totalBytes,\n totalBytes,\n partsUploaded: 0,\n });\n\n const fallbackMetadata = (await fallbackResponse.json()) as FileMetadata;\n return { upload, file: fallbackMetadata };\n }\n\n if (!proxyResponse.ok) {\n throw new Error(\n buildProxyUploadErrorMessage({\n endpointUrl: proxyContentUrl,\n status: proxyResponse.status,\n }),\n );\n }\n\n const proxyMetadata = (await proxyResponse.json()) as FileMetadata;\n\n return { upload, file: proxyMetadata };\n };\n\n const downloadFile: UploadHelpers[\"downloadFile\"] = async (fileKey, options) => {\n if (!hasText(fileKey)) {\n throw new Error(\"File key is required\");\n }\n\n if (!options || !hasText(options.provider)) {\n throw new Error(\"Download provider is required\");\n }\n\n if (!options || (options.method !== \"signed-url\" && options.method !== \"content\")) {\n throw new Error(`Download method is required. ${DOWNLOAD_METHOD_HINT}`);\n }\n\n const byKeyQuery = buildByKeyQuery(options.provider, fileKey);\n\n if (options.method === \"signed-url\") {\n const downloadUrlEndpoint = buildUrl(`/files/by-key/download-url?${byKeyQuery}`);\n\n let downloadUrlResponse: Response;\n try {\n downloadUrlResponse = await fetcher(\n downloadUrlEndpoint,\n buildRequestInit(defaultOptions, { method: \"GET\" }),\n );\n } catch (error) {\n throw new Error(\n buildDownloadRequestErrorMessage({\n endpointUrl: downloadUrlEndpoint,\n requestError: error,\n }),\n );\n }\n\n if (!downloadUrlResponse.ok) {\n const errorPayload = await readJsonSafely(downloadUrlResponse);\n const errorCode = readErrorCodeFromPayload(errorPayload);\n const hint =\n errorCode === \"SIGNED_URL_UNSUPPORTED\"\n ? \"Requested method 'signed-url' is unsupported by this storage adapter. This is a programming error. Use method 'content' when streaming downloads are available.\"\n : undefined;\n throw new Error(\n buildDownloadRequestErrorMessage({\n endpointUrl: downloadUrlEndpoint,\n status: downloadUrlResponse.status,\n payload: errorPayload,\n hint,\n }),\n );\n }\n\n const payload = (await downloadUrlResponse.json()) as {\n url: string;\n headers?: Record<string, string>;\n };\n const sanitizedUrl = sanitizeSignedDownloadUrl(payload.url);\n\n let response: Response;\n try {\n response = await fetcher(payload.url, {\n method: \"GET\",\n headers: payload.headers,\n });\n } catch (error) {\n throw new Error(\n buildDownloadRequestErrorMessage({\n endpointUrl: sanitizedUrl,\n requestError: error,\n }),\n );\n }\n\n if (!response.ok) {\n const payloadError = await readJsonSafely(response);\n throw new Error(\n buildDownloadRequestErrorMessage({\n endpointUrl: sanitizedUrl,\n status: response.status,\n payload: payloadError,\n }),\n );\n }\n\n return response;\n }\n\n const contentEndpoint = buildUrl(`/files/by-key/content?${byKeyQuery}`);\n let contentResponse: Response;\n try {\n contentResponse = await fetcher(\n contentEndpoint,\n buildRequestInit(defaultOptions, { method: \"GET\" }),\n );\n } catch (error) {\n throw new Error(\n buildDownloadRequestErrorMessage({\n endpointUrl: contentEndpoint,\n requestError: error,\n }),\n );\n }\n\n if (!contentResponse.ok) {\n const contentError = await readJsonSafely(contentResponse);\n const errorCode = readErrorCodeFromPayload(contentError);\n const hint =\n errorCode === \"SIGNED_URL_UNSUPPORTED\"\n ? \"The 'content' download endpoint is unsupported by this storage adapter. This request used method 'content'. Use method 'signed-url' when signed downloads are available.\"\n : undefined;\n throw new Error(\n buildDownloadRequestErrorMessage({\n endpointUrl: contentEndpoint,\n status: contentResponse.status,\n payload: contentError,\n hint,\n }),\n );\n }\n\n return contentResponse;\n };\n\n return {\n createUploadAndTransfer,\n downloadFile,\n };\n};\n"],"mappings":";AA8DA,MAAM,uBAAuB;AAC7B,MAAM,6BACJ;AACF,MAAM,6BACJ;AACF,MAAM,uBACJ;AAEF,MAAM,gBAAgB,MAAoB,SAAuB;CAC/D,MAAM,SAAS,IAAI,QAAQ,QAAQ,OAAU;AAE7C,KAAI,CAAC,KACH,QAAO;AAGT,KAAI,gBAAgB,QAClB,MAAK,MAAM,CAAC,KAAK,UAAU,KAAK,SAAS,CACvC,QAAO,IAAI,KAAK,MAAM;UAEf,MAAM,QAAQ,KAAK,CAC5B,MAAK,MAAM,CAAC,KAAK,UAAU,KACzB,QAAO,IAAI,KAAK,MAAM;KAGxB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,CAC7C,QAAO,IAAI,KAAK,MAAM;AAI1B,QAAO;;AAGT,MAAM,oBAAoB,UAAmC,SAAmC;AAC9F,KAAI,CAAC,SACH,QAAO;AAGT,QAAO;EACL,GAAG;EACH,GAAG;EACH,SAAS,aAAa,SAAS,SAAS,KAAK,QAAQ;EACtD;;AAGH,MAAM,iBAAiB,OAAO,aAAuB;AACnD,KAAI;AACF,SAAQ,MAAM,SAAS,MAAM;SACvB;AACN,SAAO;;;AAIX,MAAM,kBAAkB,UAAmB;AACzC,KAAI,iBAAiB,SAAS,MAAM,QAClC,QAAO,MAAM;AAGf,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAC9C,QAAO;AAGT,QAAO;;AAGT,MAAM,0BAA0B,YAAoC;AAClE,KACE,WACA,OAAO,YAAY,YACnB,aAAa,WACb,OAAO,QAAQ,YAAY,YAC3B,QAAQ,QAAQ,SAAS,EAEzB,QAAO,QAAQ;AAGjB,QAAO;;AAGT,MAAM,iBAAiB,QAA4B;AACjD,KAAI;AACF,MAAI,OAAO,WAAW,eAAe,OAAO,UAAU,OACpD,QAAO,IAAI,IAAI,KAAK,OAAO,SAAS,OAAO;AAG7C,SAAO,IAAI,IAAI,KAAK,sBAAsB;SACpC;AACN,SAAO;;;AAIX,MAAM,2BAA2B,QAA+B;CAC9D,MAAM,SAAS,cAAc,IAAI;AACjC,KAAI,CAAC,OACH,QAAO,0BAA0B,IAAI;AAGvC,KAAI,OAAO,aAAa,WAAW,OAAO,aAAa,SACrD,QAAO,0BAA0B,IAAI;AAGvC,KACE,OAAO,WAAW,eAClB,OAAO,UAAU,aAAa,YAC9B,OAAO,aAAa,QAEpB,QAAO,0BAA0B,IAAI;AAGvC,QAAO;;AAGT,MAAM,gCAAgC,UAKhC;CACJ,MAAMA,cAAwB,EAAE;AAChC,KAAI,OAAO,MAAM,WAAW,SAC1B,aAAY,KAAK,mBAAmB,MAAM,SAAS;AAErD,KAAI,MAAM,gBAAgB,OACxB,aAAY,KAAK,0BAA0B,eAAe,MAAM,YAAY,GAAG;AAEjF,KAAI,MAAM,kBAAkB,OAC1B,aAAY,KAAK,0BAA0B,eAAe,MAAM,cAAc,GAAG;CAGnF,MAAM,UAAU,YAAY,SAAS,IAAI,aAAa,YAAY,KAAK,MAAM,CAAC,KAAK;AACnF,QAAO,GAAG,2BAA2B,oBAAoB,MAAM,YAAY,WAAW,QAAQ,GAAG;;AAGnG,MAAM,oCAAoC,UAMpC;CACJ,MAAMA,cAAwB,EAAE;AAChC,KAAI,OAAO,MAAM,WAAW,SAC1B,aAAY,KAAK,mBAAmB,MAAM,SAAS;CAGrD,MAAM,iBAAiB,uBAAuB,MAAM,QAAQ;AAC5D,KAAI,eACF,aAAY,KAAK,YAAY,iBAAiB;AAGhD,KAAI,MAAM,iBAAiB,OACzB,aAAY,KAAK,kBAAkB,eAAe,MAAM,aAAa,GAAG;CAG1E,MAAM,UAAU,YAAY,SAAS,IAAI,aAAa,YAAY,KAAK,MAAM,CAAC,KAAK;CACnF,MAAM,OAAO,MAAM,OAAO,IAAI,MAAM,SAAS;AAC7C,QAAO,wBAAwB,MAAM,YAAY,WAAW,UAAU;;AAGxE,MAAM,6BAA6B,QAAgB;AACjD,KAAI;EACF,MAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,SAAO,SAAS;AAChB,SAAO,OAAO;AACd,SAAO,OAAO,UAAU;SAClB;AACN,SAAO;;;AAIX,MAAM,4BAA4B,YAAoC;AACpE,KAAI,WAAW,OAAO,YAAY,YAAY,UAAU,SAAS;EAC/D,MAAM,OAAQ,QAA+B;AAC7C,MAAI,OAAO,SAAS,YAAY,KAAK,SAAS,EAC5C,QAAO;;AAIX,QAAO;;AAGT,MAAM,WAAW,UACf,OAAO,UAAU,YAAY,MAAM,MAAM,CAAC,SAAS;AAErD,MAAM,yBAAyB,UAA8B,UAAkB;AAC7E,KAAI,CAAC,QAAQ,SAAS,CACpB,OAAM,IAAI,MAAM,WAAW,MAAM,sBAAsB;AAGzD,QAAO;;AAGT,MAAM,mBAAmB,UAAkB,YACzC,IAAI,gBAAgB;CAAE;CAAU,KAAK;CAAS,CAAC,CAAC,UAAU;AAE5D,MAAa,uBAAuB,UAIf;CACnB,MAAM,EAAE,UAAU,SAAS,mBAAmB;CAE9C,MAAM,YAAY,OAChB,MACA,MACA,sBACe;EACf,MAAM,WAAW,MAAM,QAAQ,SAAS,KAAK,EAAE,iBAAiB,gBAAgB,KAAK,CAAC;AAEtF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,UAAU,MAAM,eAAe,SAAS;GAC9C,MAAM,OACJ,OAAO,YAAY,YAAY,UAAW,QAA8B,OAAO;AACjF,OAAI,qBAAqB,SAAS,kBAChC,OAAM,IAAI,MAAM,kBAAkB;GAEpC,MAAM,UACJ,OAAO,YAAY,YAAY,WAAW,aAAa,UACnD,OAAQ,QAAiC,QAAQ,GACjD,mBAAmB,SAAS,OAAO;AACzC,SAAM,IAAI,MAAM,QAAQ;;AAG1B,SAAQ,MAAM,SAAS,MAAM;;CAG/B,MAAM,iBAAiB,OACrB,kBACA,UACA,eACG;AACH,eAAa,SAAS;AACtB,QAAM,UAA4D,kBAAkB;GAClF,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU;IACnB,eAAe,SAAS;IACxB,eAAe,SAAS;IACzB,CAAC;GACH,CAAC;;CAGJ,MAAMC,0BAAoE,OACxE,MACA,YACG;EACH,MAAM,WACJ,QAAQ,aACP,OAAO,SAAS,eAAe,gBAAgB,QAAQ,KAAK,OAAO,KAAK,OAAO;EAClF,MAAM,cACJ,QAAQ,gBACP,KAAK,QAAQ,KAAK,KAAK,SAAS,IAAI,KAAK,OAAO,WACjD;EACF,MAAM,YAAY,KAAK;AAEvB,MAAI,CAAC,QAAQ,QAAQ,SAAS,CAC5B,OAAM,IAAI,MAAM,uBAAuB;AAGzC,MAAI,CAAC,QAAQ,QAAQ,QAAQ,CAC3B,OAAM,IAAI,MAAM,uBAAuB;EAGzC,MAAM,SAAS,MAAM,UAAgC,YAAY;GAC/D,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAC/C,MAAM,KAAK,UAAU;IACnB,UAAU,QAAQ;IAClB,SAAS,QAAQ;IACjB;IACA;IACA;IACA,UAAU,QAAQ,YAAY;IAC9B,MAAM,QAAQ;IACd,YAAY,QAAQ;IACpB,YAAY,QAAQ;IACpB,UAAU,QAAQ;IACnB,CAAC;GACH,CAAC;EAEF,MAAM,aAAa;EACnB,IAAI,gBAAgB;EACpB,IAAI,gBAAgB;EACpB,MAAM,mBAAmB,sBAAsB,OAAO,OAAO,kBAAkB,WAAW;AAE1F,MAAI,OAAO,aAAa,iBAAiB;AACvC,OAAI,CAAC,OAAO,OAAO,UACjB,OAAM,IAAI,MAAM,uCAAuC;GAGzD,MAAM,iBAAiB,MAAM,QAAQ,OAAO,OAAO,WAAW;IAC5D,QAAQ;IACR,SAAS,OAAO,OAAO;IACvB,MAAM;IACP,CAAC;AAEF,OAAI,CAAC,eAAe,GAClB,OAAM,IAAI,MAAM,yBAAyB,eAAe,OAAO,GAAG;AAGpE,mBAAgB;AAChB,mBAAgB;AAChB,SAAM,eACJ,kBACA;IACE;IACA;IACA;IACA,YAAY;IACb,EACD,QAAQ,WACT;AAQD,UAAO;IAAE;IAAQ,MANI,MAAM,UAAwB,OAAO,OAAO,kBAAkB;KACjF,QAAQ;KACR,SAAS,EAAE,gBAAgB,oBAAoB;KAC/C,MAAM,KAAK,UAAU,EAAE,CAAC;KACzB,CAAC;IAEmC;;AAGvC,MAAI,OAAO,aAAa,oBAAoB;GAC1C,MAAM,gBAAgB,OAAO,OAAO;AACpC,OAAI,CAAC,iBAAiB,CAAC,OAAO,OAAO,cACnC,OAAM,IAAI,MAAM,6CAA6C;GAE/D,MAAM,wBAAwB,sBAC5B,OAAO,OAAO,uBACd,uBACD;GAED,MAAM,aAAa,KAAK,KAAK,aAAa,cAAc;AACxD,OAAI,OAAO,OAAO,YAAY,aAAa,OAAO,OAAO,SACvD,OAAM,IAAI,MAAM,yCAAyC;GAG3D,MAAM,cAAc,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,GAAG,MAAM,IAAI,EAAE;GAavE,MAAM,SAZW,MAAM,UAMpB,OAAO,OAAO,eAAe;IAC9B,QAAQ;IACR,SAAS,EAAE,gBAAgB,oBAAoB;IAC/C,MAAM,KAAK,UAAU,EAAE,aAAa,CAAC;IACtC,CAAC,EAEqB,MAAM,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,WAAW;GACxE,MAAMC,iBAIA,EAAE;GACR,MAAMC,kBAA0D,EAAE;AAElE,QAAK,MAAM,QAAQ,OAAO;IACxB,MAAM,SAAS,KAAK,aAAa,KAAK;IACtC,MAAM,MAAM,KAAK,IAAI,QAAQ,eAAe,WAAW;IACvD,MAAM,WAAW,KAAK,MAAM,OAAO,IAAI;IAEvC,MAAM,WAAW,MAAM,QAAQ,KAAK,KAAK;KACvC,QAAQ;KACR,SAAS,KAAK;KACd,MAAM;KACP,CAAC;AAEF,QAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,4BAA4B,SAAS,OAAO,GAAG;IAGjE,MAAM,OAAO,SAAS,QAAQ,IAAI,OAAO,IAAI,SAAS,QAAQ,IAAI,OAAO;AACzE,QAAI,CAAC,KACH,OAAM,IAAI,MAAM,iCAAiC;IAGnD,MAAM,WAAW,SAAS;AAC1B,mBAAe,KAAK;KAClB,YAAY,KAAK;KACjB;KACA,WAAW;KACZ,CAAC;AACF,oBAAgB,KAAK;KAAE,YAAY,KAAK;KAAY;KAAM,CAAC;AAE3D,qBAAiB;AACjB,qBAAiB;AACjB,UAAM,eACJ,kBACA;KACE;KACA;KACA;KACA;KACD,EACD,QAAQ,WACT;;AAGH,SAAM,UAA4D,uBAAuB;IACvF,QAAQ;IACR,SAAS,EAAE,gBAAgB,oBAAoB;IAC/C,MAAM,KAAK,UAAU,EAAE,OAAO,gBAAgB,CAAC;IAChD,CAAC;AAQF,UAAO;IAAE;IAAQ,MANI,MAAM,UAAwB,OAAO,OAAO,kBAAkB;KACjF,QAAQ;KACR,SAAS,EAAE,gBAAgB,oBAAoB;KAC/C,MAAM,KAAK,UAAU,EAAE,OAAO,iBAAiB,CAAC;KACjD,CAAC;IAEmC;;AAGvC,MAAI,CAAC,OAAO,OAAO,gBACjB,OAAM,IAAI,MAAM,4CAA4C;EAG9D,MAAM,kBAAkB,SAAS,OAAO,OAAO,gBAAgB;EAC/D,MAAM,0BAA0B,wBAAwB,gBAAgB;AACxE,MAAI,wBACF,OAAM,IAAI,MAAM,GAAG,wBAAwB,GAAG,6BAA6B;EAG7E,MAAM,SAAS,KAAK,QAAQ;EAC5B,MAAM,SAAS,IAAI,eAA2B,EAC5C,MAAM,YAAY;GAChB,MAAM,SAAS,OAAO,WAAW;GAEjC,MAAM,aAAmB;AACvB,WAAO,MAAM,CAAC,MACX,EAAE,MAAM,YAAY;AACnB,SAAI,MAAM;AACR,iBAAW,OAAO;AAClB;;AAGF,SAAI,OAAO;AACT,uBAAiB,MAAM;AACvB,cAAQ,aAAa;OACnB;OACA;OACA,eAAe;OAChB,CAAC;AACF,iBAAW,QAAQ,MAAM;;AAG3B,WAAM;QAEP,UAAU;AACT,gBAAW,MAAM,MAAM;MAE1B;;AAGH,SAAM;KAET,CAAC;EAEF,MAAM,cAAc,iBAAiB,gBAAgB;GACnD,QAAQ;GACR,SAAS,EAAE,gBAAgB,sBAAsB;GACjD,MAAM;GACP,CAAC;AACF,cAAY,SAAS;EAErB,IAAIC;AACJ,MAAI;AACF,mBAAgB,MAAM,QAAQ,iBAAiB,YAAY;WACpD,aAAa;GACpB,IAAIC;AACJ,OAAI;AACF,uBAAmB,MAAM,QACvB,iBACA,iBAAiB,gBAAgB;KAC/B,QAAQ;KACR,SAAS,EAAE,gBAAgB,sBAAsB;KACjD,MAAM;KACP,CAAC,CACH;YACM,eAAe;AACtB,UAAM,IAAI,MACR,6BAA6B;KAC3B,aAAa;KACb;KACA;KACD,CAAC,CACH;;AAGH,OAAI,CAAC,iBAAiB,GACpB,OAAM,IAAI,MACR,6BAA6B;IAC3B,aAAa;IACb,QAAQ,iBAAiB;IACzB;IACD,CAAC,CACH;AAGH,WAAQ,aAAa;IACnB,eAAe;IACf;IACA,eAAe;IAChB,CAAC;AAGF,UAAO;IAAE;IAAQ,MADS,MAAM,iBAAiB,MAAM;IACd;;AAG3C,MAAI,CAAC,cAAc,GACjB,OAAM,IAAI,MACR,6BAA6B;GAC3B,aAAa;GACb,QAAQ,cAAc;GACvB,CAAC,CACH;AAKH,SAAO;GAAE;GAAQ,MAFM,MAAM,cAAc,MAAM;GAEX;;CAGxC,MAAMC,eAA8C,OAAO,SAAS,YAAY;AAC9E,MAAI,CAAC,QAAQ,QAAQ,CACnB,OAAM,IAAI,MAAM,uBAAuB;AAGzC,MAAI,CAAC,WAAW,CAAC,QAAQ,QAAQ,SAAS,CACxC,OAAM,IAAI,MAAM,gCAAgC;AAGlD,MAAI,CAAC,WAAY,QAAQ,WAAW,gBAAgB,QAAQ,WAAW,UACrE,OAAM,IAAI,MAAM,gCAAgC,uBAAuB;EAGzE,MAAM,aAAa,gBAAgB,QAAQ,UAAU,QAAQ;AAE7D,MAAI,QAAQ,WAAW,cAAc;GACnC,MAAM,sBAAsB,SAAS,8BAA8B,aAAa;GAEhF,IAAIC;AACJ,OAAI;AACF,0BAAsB,MAAM,QAC1B,qBACA,iBAAiB,gBAAgB,EAAE,QAAQ,OAAO,CAAC,CACpD;YACM,OAAO;AACd,UAAM,IAAI,MACR,iCAAiC;KAC/B,aAAa;KACb,cAAc;KACf,CAAC,CACH;;AAGH,OAAI,CAAC,oBAAoB,IAAI;IAC3B,MAAM,eAAe,MAAM,eAAe,oBAAoB;IAE9D,MAAM,OADY,yBAAyB,aAAa,KAExC,2BACV,oKACA;AACN,UAAM,IAAI,MACR,iCAAiC;KAC/B,aAAa;KACb,QAAQ,oBAAoB;KAC5B,SAAS;KACT;KACD,CAAC,CACH;;GAGH,MAAM,UAAW,MAAM,oBAAoB,MAAM;GAIjD,MAAM,eAAe,0BAA0B,QAAQ,IAAI;GAE3D,IAAIC;AACJ,OAAI;AACF,eAAW,MAAM,QAAQ,QAAQ,KAAK;KACpC,QAAQ;KACR,SAAS,QAAQ;KAClB,CAAC;YACK,OAAO;AACd,UAAM,IAAI,MACR,iCAAiC;KAC/B,aAAa;KACb,cAAc;KACf,CAAC,CACH;;AAGH,OAAI,CAAC,SAAS,IAAI;IAChB,MAAM,eAAe,MAAM,eAAe,SAAS;AACnD,UAAM,IAAI,MACR,iCAAiC;KAC/B,aAAa;KACb,QAAQ,SAAS;KACjB,SAAS;KACV,CAAC,CACH;;AAGH,UAAO;;EAGT,MAAM,kBAAkB,SAAS,yBAAyB,aAAa;EACvE,IAAIC;AACJ,MAAI;AACF,qBAAkB,MAAM,QACtB,iBACA,iBAAiB,gBAAgB,EAAE,QAAQ,OAAO,CAAC,CACpD;WACM,OAAO;AACd,SAAM,IAAI,MACR,iCAAiC;IAC/B,aAAa;IACb,cAAc;IACf,CAAC,CACH;;AAGH,MAAI,CAAC,gBAAgB,IAAI;GACvB,MAAM,eAAe,MAAM,eAAe,gBAAgB;GAE1D,MAAM,OADY,yBAAyB,aAAa,KAExC,2BACV,6KACA;AACN,SAAM,IAAI,MACR,iCAAiC;IAC/B,aAAa;IACb,QAAQ,gBAAgB;IACxB,SAAS;IACT;IACD,CAAC,CACH;;AAGH,SAAO;;AAGT,QAAO;EACL;EACA;EACD"}
|