@better-s3/react 3.1048.0 → 3.1049.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @better-s3/react
2
2
 
3
- Headless React hooks for S3 file uploads, downloads, and deletes — state management, progress tracking, and cancellation built in.
3
+ Headless React hooks for S3 upload, download, and delete — state, progress, and cancellation built in.
4
4
 
5
5
  Requires [`@better-s3/server`](../better-s3-server) on the backend. For ready-made components, see [`@better-s3/ui`](../better-s3-ui).
6
6
 
@@ -13,85 +13,65 @@ pnpm add @better-s3/react @better-s3/server
13
13
  ## Setup
14
14
 
15
15
  ```ts
16
- import { createS3Api } from "@better-s3/server";
16
+ import { createS3Api } from "@better-s3/core";
17
17
 
18
18
  const api = createS3Api();
19
19
  ```
20
20
 
21
21
  ## Hooks
22
22
 
23
- ### `useUpload` Single file upload
23
+ | Hook | Use case |
24
+ | ------------------------ | --------------------------------------- |
25
+ | `useUploadControls` | Single file — picker + drag-and-drop |
26
+ | `useMultiUploadControls` | Multiple files — picker + drag-and-drop |
27
+ | `useUpload` | Single file — call `upload()` yourself |
28
+ | `useMultiUpload` | Batch — call `upload()` yourself |
29
+ | `useDownload` | Presigned URL → native browser download |
30
+ | `useFetchDownload` | Fetch with progress and cancel |
31
+ | `useDelete` | Two-step delete with confirmation |
24
32
 
25
- ```tsx
26
- const { phase, progress, error, upload, cancel, reset } = useUpload({
27
- api,
28
- accept: ["image/*", ".pdf"],
29
- maxFileSize: 10 * 1024 * 1024, // client-side pre-validation (UX)
30
- onSuccess: (_file, result) => console.log("Uploaded:", result.key),
31
- onError: (_file, error) => {
32
- // error.message for size violations:
33
- // Simple: S3 returns 403 — "Upload failed: 403 Forbidden"
34
- // Multipart: server returns 422 — "File size (X bytes) exceeds …"
35
- },
36
- });
37
-
38
- await upload(file, `uploads/${file.name}`, { metadata: { source: "web" } });
39
- ```
40
-
41
- **Phases:** `idle → validating → uploading → success | error`
42
-
43
- > `maxFileSize` performs a client-side check before the upload starts (good UX). The real enforcement is server-side:
44
- >
45
- > - **Simple uploads** — S3 enforces exact file size via a signed `content-length-range` policy. The client cannot upload a different-sized file with that URL.
46
- > - **Multipart uploads** — Server verifies via `HeadObject` after `CompleteMultipartUpload` and deletes the object if the limit is exceeded.
47
-
48
- ### `useUploadControls` — Upload with file picker & drag-drop
33
+ ### Upload — single file
49
34
 
50
35
  ```tsx
51
- const { mode, phase, progress, openFilePicker, inputProps, dropHandlers } =
36
+ const { openFilePicker, inputProps, dropHandlers, progress, isUploading } =
52
37
  useUploadControls({
53
38
  api,
54
39
  objectKey: (file) => `uploads/${file.name}`,
55
- maxFiles: 5, // > 1 → switches to multi-upload mode automatically
40
+ accept: ["image/*"],
41
+ maxFileSize: 10 * 1024 * 1024,
56
42
  });
43
+
44
+ // <input {...inputProps} />
45
+ // <div {...dropHandlers} onClick={openFilePicker}>…</div>
57
46
  ```
58
47
 
59
- ### `useMultiUpload`Batch upload
48
+ ### Uploadmultiple files
60
49
 
61
50
  ```tsx
62
- const { phase, files, totalProgress, upload, cancel } = useMultiUpload({
63
- api,
64
- maxFiles: 10,
65
- concurrentFiles: 3,
66
- });
67
-
68
- await upload(selectedFiles, (file) => `uploads/${file.name}`);
51
+ const { openFilePicker, inputProps, files, totalProgress } =
52
+ useMultiUploadControls({
53
+ api,
54
+ objectKey: (file) => `uploads/${file.name}`,
55
+ maxFiles: 5,
56
+ concurrentFiles: 3,
57
+ });
69
58
  ```
70
59
 
71
- ### `useDownload` — File download
60
+ ### Download
72
61
 
73
62
  ```tsx
74
- const { phase, error, download, reset } = useDownload({ api });
63
+ const { download } = useDownload({ api });
75
64
  download("uploads/photo.jpg", "photo.jpg");
76
65
  ```
77
66
 
78
- ### `useDelete` — File deletion with confirmation
67
+ ### Delete
79
68
 
80
69
  ```tsx
81
- const { phase, pendingKey, requestDelete, confirmDelete, cancelDelete } =
82
- useDelete({ api });
83
-
84
- requestDelete("uploads/photo.jpg"); // phase → "confirming"
85
- confirmDelete(); // phase → "deleting" → "success"
70
+ const { requestDelete, confirmDelete } = useDelete({ api });
71
+ requestDelete("uploads/photo.jpg");
72
+ await confirmDelete();
86
73
  ```
87
74
 
88
- ## Upload modes
89
-
90
- | Mode | When used | Size enforcement |
91
- | --------- | ------------------------------------------------ | --------------------------------------------------------- |
92
- | Simple | `file.size < multipartThreshold` (default 50 MB) | S3 presigned POST policy — exact `content-length-range` |
93
- | Multipart | `multipart: true` or file ≥ threshold | Server `HeadObject` check after `CompleteMultipartUpload` |
94
-
95
75
  ## License
96
76
 
97
77
  MIT
package/dist/api.d.ts CHANGED
@@ -5,8 +5,8 @@
5
5
  * endpoint, tRPC mutation, GraphQL query, or anything else. The object is
6
6
  * returned as-is; `createS3Client` just enforces the type contract.
7
7
  *
8
- * If you are using `@better-s3/server`, use `createS3Api` from that package
9
- * instead — it builds a standard fetch client from a base path.
8
+ * If you are using the default better-s3 route layout, use `createS3Api` from
9
+ * `@better-s3/core` — it builds a standard fetch client from a base path.
10
10
  *
11
11
  * @example
12
12
  * ```ts
@@ -24,7 +24,5 @@
24
24
  * });
25
25
  * ```
26
26
  */
27
- import type { S3Api } from "./types/s3-api";
27
+ import type { S3Api } from "@better-s3/core";
28
28
  export declare function createS3Client(input: S3Api): S3Api;
29
- /** @deprecated Use {@link createS3Client} instead. */
30
- export declare const createS3Api: typeof createS3Client;
@@ -1,10 +1,4 @@
1
- export { formatFileSize } from "./format-file-size";
2
- export { validateFile } from "./validate-file";
3
- export { parseContentDispositionFilename } from "./parse-content-disposition";
4
1
  export { formatUploadProgress } from "./format-upload-progress";
5
2
  export { formatSpeed } from "./format-speed";
6
3
  export { formatEta } from "./format-eta";
7
- export { buildObjectKey } from "./build-object-key";
8
- export { getFileExtension } from "./get-file-extension";
9
- export { truncateFilename } from "./truncate-filename";
10
4
  export { createSpeedTracker, type SpeedTracker } from "./speed-tracker";
@@ -1,20 +1,28 @@
1
- import type { S3Api } from "../types/s3-api";
1
+ import type { S3Api } from "@better-s3/core";
2
2
  import type { DeletePhase, DeleteHooks } from "../types";
3
+ /** Options for {@link useDelete}. */
3
4
  export type UseDeleteOptions = DeleteHooks & {
4
5
  /** S3Api client. Optional when an `<S3Provider>` is present in the tree. */
5
6
  api?: S3Api;
6
- /** Target bucket (overrides server default) */
7
+ /** Target bucket (overrides server default). */
7
8
  bucket?: string;
8
9
  };
9
10
  export type UseDeleteState = {
11
+ /** Current delete phase. */
10
12
  phase: DeletePhase;
13
+ /** Error message, or `null`. */
11
14
  error: string | null;
12
15
  };
13
16
  export type UseDeleteReturn = UseDeleteState & {
17
+ /** Key awaiting confirmation, or `null`. */
18
+ pendingKey: string | null;
19
+ /** Move to the `confirming` phase for the given key. */
14
20
  requestDelete: (key: string) => void;
21
+ /** Send the delete request for the pending key. */
15
22
  confirmDelete: () => Promise<void>;
23
+ /** Cancel confirmation and return to `idle`. */
16
24
  cancelDelete: () => void;
25
+ /** Reset state to `idle`. */
17
26
  reset: () => void;
18
- pendingKey: string | null;
19
27
  };
20
28
  export declare function useDelete(options: UseDeleteOptions): UseDeleteReturn;
@@ -1,27 +1,32 @@
1
- import type { S3Api } from "../types/s3-api";
1
+ import type { S3Api } from "@better-s3/core";
2
2
  export type { DownloadPhase, DownloadHooks } from "../types/download";
3
3
  import type { DownloadPhase, DownloadHooks } from "../types/download";
4
+ /** Options for {@link useDownload}. */
4
5
  export type UseDownloadOptions = DownloadHooks & {
5
6
  /** S3Api client. Optional when an `<S3Provider>` is present in the tree. */
6
7
  api?: S3Api;
7
- /** Target bucket (overrides server default) */
8
+ /** Target bucket (overrides server default). */
8
9
  bucket?: string;
9
10
  };
10
11
  export type UseDownloadState = {
12
+ /** Current download phase. */
11
13
  phase: DownloadPhase;
14
+ /** Error message, or `null`. */
12
15
  error: string | null;
13
- /** Presigned URL — set after a successful presign, cleared on reset */
16
+ /** Presigned URL — set after a successful presign, cleared on reset. */
14
17
  url: string | null;
15
- /** Validity window in seconds for the presigned URL */
18
+ /** Validity window in seconds for the presigned URL. */
16
19
  expiresIn: number | null;
17
20
  };
18
21
  export type UseDownloadReturn = UseDownloadState & {
22
+ /** Presign and trigger a native browser download. */
19
23
  download: (key: string, downloadName?: string) => Promise<void>;
20
- /** Fetch the presigned URL without triggering a browser download — for headless use */
24
+ /** Fetch the presigned URL without triggering a browser download. */
21
25
  presign: (key: string, downloadName?: string) => Promise<{
22
26
  url: string;
23
27
  expiresIn: number;
24
28
  } | null>;
29
+ /** Reset state to `idle`. */
25
30
  reset: () => void;
26
31
  };
27
32
  export declare function useDownload(options: UseDownloadOptions): UseDownloadReturn;
@@ -1,23 +1,32 @@
1
- import type { S3Api } from "../types/s3-api";
1
+ import type { S3Api } from "@better-s3/core";
2
2
  import type { FetchDownloadPhase, FetchDownloadHooks } from "../types/download";
3
3
  import type { UploadProgress } from "../types/upload";
4
4
  export type { FetchDownloadPhase, FetchDownloadProgress, FetchDownloadHooks, } from "../types/download";
5
+ /** Options for {@link useFetchDownload}. */
5
6
  export type UseFetchDownloadOptions = FetchDownloadHooks & {
6
7
  /** S3Api client. Optional when an `<S3Provider>` is present in the tree. */
7
8
  api?: S3Api;
8
- /** Target bucket (overrides server default) */
9
+ /** Target bucket (overrides server default). */
9
10
  bucket?: string;
10
11
  };
11
12
  export type UseFetchDownloadState = {
13
+ /** Current download phase. */
12
14
  phase: FetchDownloadPhase;
15
+ /** Byte transfer progress. */
13
16
  progress: UploadProgress;
17
+ /** Error message, or `null`. */
14
18
  error: string | null;
19
+ /** Resolved download filename. */
15
20
  fileName: string | null;
21
+ /** Total file size in bytes. */
16
22
  fileSize: number | null;
17
23
  };
18
24
  export type UseFetchDownloadReturn = UseFetchDownloadState & {
25
+ /** Presign, fetch bytes, and save via the browser. */
19
26
  download: (key: string, downloadName?: string) => Promise<void>;
27
+ /** Abort the active download. */
20
28
  cancel: () => void;
29
+ /** Reset state to `idle`. */
21
30
  reset: () => void;
22
31
  };
23
32
  export declare function useFetchDownload(options: UseFetchDownloadOptions): UseFetchDownloadReturn;
@@ -1,19 +1,28 @@
1
1
  import type { UploadProgress, MultiUploadFileState, MultiUploadPhase } from "../types";
2
2
  import { type UseMultiUploadOptions } from "./use-multi-upload";
3
+ /** Options for {@link useMultiUploadControls}. */
3
4
  export type UseMultiUploadControlsOptions = UseMultiUploadOptions & {
5
+ /** S3 object key, or a function that derives it from each file. */
4
6
  objectKey: string | ((file: File) => string);
5
7
  };
6
8
  export type UseMultiUploadControlsReturn = {
9
+ /** Current batch upload phase. */
7
10
  phase: MultiUploadPhase;
8
11
  /** Per-file upload states. */
9
12
  files: MultiUploadFileState[];
10
13
  /** Aggregated progress across all files. */
11
14
  totalProgress: UploadProgress;
15
+ /** Batch-level error message, or `null`. */
12
16
  error: string | null;
17
+ /** `true` while uploading. */
13
18
  isUploading: boolean;
19
+ /** Handle files from drag-and-drop or a file input. */
14
20
  handleFiles: (files: FileList | null) => void;
21
+ /** Open the hidden file picker. */
15
22
  openFilePicker: () => void;
23
+ /** Abort all in-flight uploads. */
16
24
  cancel: () => void;
25
+ /** Reset state to `idle`. */
17
26
  reset: () => void;
18
27
  /** Spread on a hidden `<input>` element. */
19
28
  inputProps: {
@@ -1,22 +1,30 @@
1
- import type { S3Api } from "../types/s3-api";
1
+ import type { S3Api } from "@better-s3/core";
2
2
  import type { UploadConfig, UploadProgress, UploadRequestOptions, MultiUploadPhase, MultiUploadFileState, MultiUploadHooks } from "../types";
3
+ /** Options for {@link useMultiUpload}. */
3
4
  export type UseMultiUploadOptions = UploadConfig & MultiUploadHooks & {
4
5
  /** S3Api client. Optional when an `<S3Provider>` is present in the tree. */
5
6
  api?: S3Api;
6
- /** Static request options applied to all files */
7
+ /** Static request options applied to all files. */
7
8
  uploadOptions?: UploadRequestOptions;
8
- /** Per-file request options (overrides uploadOptions) */
9
+ /** Per-file request options (overrides `uploadOptions`). */
9
10
  getUploadOptions?: (file: File) => UploadRequestOptions;
10
11
  };
11
12
  export type UseMultiUploadState = {
13
+ /** Current batch upload phase. */
12
14
  phase: MultiUploadPhase;
15
+ /** Per-file upload states. */
13
16
  files: MultiUploadFileState[];
17
+ /** Aggregated progress across all files. */
14
18
  totalProgress: UploadProgress;
19
+ /** Batch-level error message, or `null`. */
15
20
  error: string | null;
16
21
  };
17
22
  export type UseMultiUploadReturn = UseMultiUploadState & {
23
+ /** Upload multiple files. */
18
24
  upload: (files: File[], resolveKey: (file: File) => string) => Promise<void>;
25
+ /** Abort all in-flight uploads. */
19
26
  cancel: () => void;
27
+ /** Reset state to `idle`. */
20
28
  reset: () => void;
21
29
  };
22
30
  export declare function useMultiUpload(options: UseMultiUploadOptions): UseMultiUploadReturn;
@@ -1,6 +1,8 @@
1
1
  import type { UploadPhase, UploadProgress, UploadRequestOptions } from "../types";
2
2
  import { type UseUploadOptions } from "./use-upload";
3
+ /** Options for {@link useUploadControls}. */
3
4
  export type UseUploadControlsOptions = UseUploadOptions & {
5
+ /** S3 object key, or a function that derives it from the file. */
4
6
  objectKey: string | ((file: File) => string);
5
7
  /** Static request options applied to the upload. */
6
8
  uploadOptions?: UploadRequestOptions;
@@ -8,17 +10,24 @@ export type UseUploadControlsOptions = UseUploadOptions & {
8
10
  getUploadOptions?: (file: File) => UploadRequestOptions;
9
11
  };
10
12
  export type UseUploadControlsReturn = {
13
+ /** Current upload phase. */
11
14
  phase: UploadPhase;
12
15
  /** Info about the selected file. */
13
16
  fileInfo: {
14
17
  name: string;
15
18
  size: number;
16
19
  } | null;
20
+ /** Byte transfer progress. */
17
21
  progress: UploadProgress;
22
+ /** Error message, or `null`. */
18
23
  error: string | null;
24
+ /** `true` while uploading. */
19
25
  isUploading: boolean;
26
+ /** Handle files from drag-and-drop or a file input. */
20
27
  handleFiles: (files: FileList | null) => void;
28
+ /** Open the hidden file picker. */
21
29
  openFilePicker: () => void;
30
+ /** Abort and reset to idle. */
22
31
  cancel: () => void;
23
32
  /**
24
33
  * Soft-stop: preserves S3 parts and store entry so a future `upload()` can
@@ -1,15 +1,22 @@
1
- import type { S3Api } from "../types/s3-api";
1
+ import type { S3Api } from "@better-s3/core";
2
2
  import type { UploadConfig, UploadHooks, UploadPhase, UploadProgress, UploadResult, UploadRequestOptions } from "../types";
3
+ /** Options for {@link useUpload}. */
3
4
  export type UseUploadOptions = UploadConfig & UploadHooks & {
4
5
  /** S3Api client. Optional when an `<S3Provider>` is present in the tree. */
5
6
  api?: S3Api;
6
7
  };
7
8
  export type UseUploadState = {
9
+ /** Current upload phase. */
8
10
  phase: UploadPhase;
11
+ /** Byte transfer progress. */
9
12
  progress: UploadProgress;
13
+ /** Error message, or `null`. */
10
14
  error: string | null;
15
+ /** Result after success, or `null`. */
11
16
  result: UploadResult | null;
17
+ /** Name of the file being uploaded. */
12
18
  fileName: string | null;
19
+ /** Size of the file being uploaded in bytes. */
13
20
  fileSize: number | null;
14
21
  };
15
22
  export type UseUploadReturn = UseUploadState & {
package/dist/index.d.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  export * from "./types";
2
- export { createS3Client, createS3Api } from "./api";
3
- /** @deprecated Use {@link createS3Client} instead. */
4
- export { createS3Api as createPresignApi } from "./api";
2
+ export { createS3Client } from "./api";
5
3
  export { S3Provider, useS3Client, S3Context } from "./s3-provider";
6
4
  export { createLocalStorageStore, createMemoryStore } from "./store";
7
5
  export { uploadFile, uploadFiles, type UploadEngineCallbacks, type FileItem, type FileItemStatus, type MultiUploadCallbacks, } from "./upload";
8
- export { formatFileSize, validateFile, parseContentDispositionFilename, formatUploadProgress, formatSpeed, formatEta, buildObjectKey, getFileExtension, truncateFilename, createSpeedTracker, type SpeedTracker, } from "./helpers";
6
+ export { formatUploadProgress, formatSpeed, formatEta, createSpeedTracker, type SpeedTracker, } from "./helpers";
9
7
  export { useUpload, type UseUploadOptions, type UseUploadState, type UseUploadReturn, } from "./hooks/use-upload";
10
8
  export { useMultiUpload, type UseMultiUploadOptions, type UseMultiUploadState, type UseMultiUploadReturn, } from "./hooks/use-multi-upload";
11
9
  export { useUploadControls, type UseUploadControlsOptions, type UseUploadControlsReturn, } from "./hooks/use-upload-controls";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createContext, useContext, useState, useRef, useCallback } from 'react';
2
2
  import { jsx } from 'react/jsx-runtime';
3
+ import { formatFileSize, validateFile, parseFileName } from '@better-s3/core';
3
4
 
4
5
  // src/types/error.ts
5
6
  var S3UploadError = class extends Error {
@@ -16,7 +17,6 @@ var S3UploadError = class extends Error {
16
17
  function createS3Client(input) {
17
18
  return input;
18
19
  }
19
- var createS3Api = createS3Client;
20
20
  var S3Context = createContext(null);
21
21
  function S3Provider({
22
22
  client,
@@ -517,66 +517,11 @@ async function uploadFiles(api, items, config = {}, callbacks = {}, signal, getR
517
517
  await Promise.all(workers);
518
518
  return results;
519
519
  }
520
-
521
- // src/helpers/format-file-size.ts
522
- function formatFileSize(bytes) {
523
- if (bytes === 0) return "0 B";
524
- const units = ["B", "KB", "MB", "GB", "TB"];
525
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
526
- const size = bytes / Math.pow(1024, i);
527
- return `${size.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
528
- }
529
-
530
- // src/helpers/validate-file.ts
531
- function validateFile(file, options) {
532
- if (options.accept?.length) {
533
- const allowed = options.accept.some((type) => {
534
- if (type.startsWith(".")) {
535
- return file.name.toLowerCase().endsWith(type.toLowerCase());
536
- }
537
- if (type.endsWith("/*")) {
538
- return file.type.startsWith(type.replace("/*", "/"));
539
- }
540
- return file.type === type;
541
- });
542
- if (!allowed) {
543
- const ext = file.name.includes(".") ? file.name.split(".").pop() : null;
544
- return `File type "${ext ? `.${ext}` : file.type || "unknown"}" is not allowed`;
545
- }
546
- }
547
- if (file.size === 0) {
548
- return "File is empty";
549
- }
550
- if (options.maxFileSize && file.size > options.maxFileSize) {
551
- const maxMB = (options.maxFileSize / (1024 * 1024)).toFixed(1);
552
- return `File size exceeds ${maxMB} MB limit`;
553
- }
554
- return null;
555
- }
556
-
557
- // src/helpers/parse-content-disposition.ts
558
- function parseContentDispositionFilename(header, fallback) {
559
- if (!header) return fallback;
560
- const starMatch = header.match(/filename\*=UTF-8''([^;,\s]+)/i);
561
- if (starMatch) {
562
- try {
563
- return decodeURIComponent(starMatch[1]);
564
- } catch {
565
- }
566
- }
567
- const match = header.match(/filename="([^"]+)"/i);
568
- if (match) return match[1];
569
- return fallback;
570
- }
571
-
572
- // src/helpers/format-upload-progress.ts
573
520
  function formatUploadProgress(loaded, total, percent) {
574
521
  const loadedStr = formatFileSize(loaded);
575
522
  if (!total) return loadedStr;
576
523
  return `${loadedStr} / ${formatFileSize(total)} (${Math.round(percent)}%)`;
577
524
  }
578
-
579
- // src/helpers/format-speed.ts
580
525
  function formatSpeed(bytesPerSecond) {
581
526
  return `${formatFileSize(bytesPerSecond)}/s`;
582
527
  }
@@ -593,33 +538,6 @@ function formatEta(remainingBytes, bytesPerSecond) {
593
538
  return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
594
539
  }
595
540
 
596
- // src/helpers/build-object-key.ts
597
- function buildObjectKey(...parts) {
598
- return parts.map((p) => p.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
599
- }
600
-
601
- // src/helpers/get-file-extension.ts
602
- function getFileExtension(filename) {
603
- const dotIndex = filename.lastIndexOf(".");
604
- if (dotIndex < 1) return "";
605
- return filename.slice(dotIndex + 1).toLowerCase();
606
- }
607
-
608
- // src/helpers/truncate-filename.ts
609
- function truncateFilename(name, maxChars = 26) {
610
- if (name.length <= maxChars) return name;
611
- const dotIndex = name.lastIndexOf(".");
612
- if (dotIndex <= 0) {
613
- return name.slice(0, maxChars - 1) + "\u2026";
614
- }
615
- const ext = name.slice(dotIndex);
616
- const available = maxChars - ext.length - 1;
617
- if (available <= 0) {
618
- return name.slice(0, maxChars - 1) + "\u2026";
619
- }
620
- return name.slice(0, available) + "\u2026 " + ext;
621
- }
622
-
623
541
  // src/helpers/speed-tracker.ts
624
542
  function createSpeedTracker(windowMs = 3e3) {
625
543
  const samples = [];
@@ -1270,10 +1188,7 @@ function useFetchDownload(options) {
1270
1188
  );
1271
1189
  }
1272
1190
  const contentLength = Number(res.headers.get("content-length") || 0);
1273
- const name = downloadName ?? parseContentDispositionFilename(
1274
- res.headers.get("content-disposition"),
1275
- fallback
1276
- );
1191
+ const name = downloadName ?? parseFileName(res.headers.get("content-disposition")) ?? fallback;
1277
1192
  setState((s) => ({
1278
1193
  ...s,
1279
1194
  fileName: name,
@@ -1413,6 +1328,6 @@ function useDelete(options) {
1413
1328
  };
1414
1329
  }
1415
1330
 
1416
- export { S3Context, S3Provider, S3UploadError, buildObjectKey, createLocalStorageStore, createMemoryStore, createS3Api as createPresignApi, createS3Api, createS3Client, createSpeedTracker, formatEta, formatFileSize, formatSpeed, formatUploadProgress, getFileExtension, parseContentDispositionFilename, truncateFilename, uploadFile, uploadFiles, useDelete, useDownload, useFetchDownload, useMultiUpload, useMultiUploadControls, useS3Client, useUpload, useUploadControls, validateFile };
1331
+ export { S3Context, S3Provider, S3UploadError, createLocalStorageStore, createMemoryStore, createS3Client, createSpeedTracker, formatEta, formatSpeed, formatUploadProgress, uploadFile, uploadFiles, useDelete, useDownload, useFetchDownload, useMultiUpload, useMultiUploadControls, useS3Client, useUpload, useUploadControls };
1417
1332
  //# sourceMappingURL=index.js.map
1418
1333
  //# sourceMappingURL=index.js.map