@fairu/sdk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +447 -0
- package/dist/FairuClient-BUObk5LJ.cjs +91 -0
- package/dist/FairuClient-BUObk5LJ.cjs.map +1 -0
- package/dist/FairuClient-CT-IPh8i.js +92 -0
- package/dist/FairuClient-CT-IPh8i.js.map +1 -0
- package/dist/FairuError-D8sSmRAa.js +214 -0
- package/dist/FairuError-D8sSmRAa.js.map +1 -0
- package/dist/FairuError-DWH_Nwk0.cjs +213 -0
- package/dist/FairuError-DWH_Nwk0.cjs.map +1 -0
- package/dist/FairuProvider-BRpRddCI.cjs +51 -0
- package/dist/FairuProvider-BRpRddCI.cjs.map +1 -0
- package/dist/FairuProvider-Tc0vFo5L.js +52 -0
- package/dist/FairuProvider-Tc0vFo5L.js.map +1 -0
- package/dist/FileProxyBuilder-D-jZpAtd.cjs +269 -0
- package/dist/FileProxyBuilder-D-jZpAtd.cjs.map +1 -0
- package/dist/FileProxyBuilder-vHw1zBpJ.js +270 -0
- package/dist/FileProxyBuilder-vHw1zBpJ.js.map +1 -0
- package/dist/FragmentBuilder-BinoxeVS.js +194 -0
- package/dist/FragmentBuilder-BinoxeVS.js.map +1 -0
- package/dist/FragmentBuilder-BuvIC0aT.cjs +193 -0
- package/dist/FragmentBuilder-BuvIC0aT.cjs.map +1 -0
- package/dist/UploadError-CHBJuChw.cjs +81 -0
- package/dist/UploadError-CHBJuChw.cjs.map +1 -0
- package/dist/UploadError-_gEcJqSS.js +82 -0
- package/dist/UploadError-_gEcJqSS.js.map +1 -0
- package/dist/client/FairuClient.d.ts +8 -0
- package/dist/client/FairuClient.d.ts.map +1 -0
- package/dist/client/FairuProvider.d.ts +78 -0
- package/dist/client/FairuProvider.d.ts.map +1 -0
- package/dist/client/cache.d.ts +6 -0
- package/dist/client/cache.d.ts.map +1 -0
- package/dist/client/config.d.ts +77 -0
- package/dist/client/config.d.ts.map +1 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/errors/FairuError.d.ts +107 -0
- package/dist/errors/FairuError.d.ts.map +1 -0
- package/dist/errors/UploadError.d.ts +54 -0
- package/dist/errors/UploadError.d.ts.map +1 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/fileproxy/FileProxyBuilder.d.ts +141 -0
- package/dist/fileproxy/FileProxyBuilder.d.ts.map +1 -0
- package/dist/fileproxy/index.d.ts +4 -0
- package/dist/fileproxy/index.d.ts.map +1 -0
- package/dist/fileproxy/types.d.ts +79 -0
- package/dist/fileproxy/types.d.ts.map +1 -0
- package/dist/fileproxy/useFileProxyUrl.d.ts +91 -0
- package/dist/fileproxy/useFileProxyUrl.d.ts.map +1 -0
- package/dist/fileproxy.cjs +10 -0
- package/dist/fileproxy.cjs.map +1 -0
- package/dist/fileproxy.d.ts +2 -0
- package/dist/fileproxy.js +10 -0
- package/dist/fileproxy.js.map +1 -0
- package/dist/fragments/FragmentBuilder.d.ts +107 -0
- package/dist/fragments/FragmentBuilder.d.ts.map +1 -0
- package/dist/fragments/FragmentRegistry.d.ts +93 -0
- package/dist/fragments/FragmentRegistry.d.ts.map +1 -0
- package/dist/fragments/index.d.ts +5 -0
- package/dist/fragments/index.d.ts.map +1 -0
- package/dist/fragments/predefined/assetFragments.d.ts +39 -0
- package/dist/fragments/predefined/assetFragments.d.ts.map +1 -0
- package/dist/fragments/predefined/copyrightFragments.d.ts +12 -0
- package/dist/fragments/predefined/copyrightFragments.d.ts.map +1 -0
- package/dist/fragments/predefined/folderFragments.d.ts +28 -0
- package/dist/fragments/predefined/folderFragments.d.ts.map +1 -0
- package/dist/fragments/predefined/galleryFragments.d.ts +24 -0
- package/dist/fragments/predefined/galleryFragments.d.ts.map +1 -0
- package/dist/fragments/predefined/index.d.ts +6 -0
- package/dist/fragments/predefined/index.d.ts.map +1 -0
- package/dist/fragments/predefined/licenseFragments.d.ts +12 -0
- package/dist/fragments/predefined/licenseFragments.d.ts.map +1 -0
- package/dist/fragments/types.d.ts +46 -0
- package/dist/fragments/types.d.ts.map +1 -0
- package/dist/fragments.cjs +403 -0
- package/dist/fragments.cjs.map +1 -0
- package/dist/fragments.d.ts +2 -0
- package/dist/fragments.js +403 -0
- package/dist/fragments.js.map +1 -0
- package/dist/generated/graphql.d.ts +3464 -0
- package/dist/generated/graphql.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/mutations/index.d.ts +5 -0
- package/dist/hooks/mutations/index.d.ts.map +1 -0
- package/dist/hooks/mutations/useAssetMutations.d.ts +66 -0
- package/dist/hooks/mutations/useAssetMutations.d.ts.map +1 -0
- package/dist/hooks/mutations/useFolderMutations.d.ts +69 -0
- package/dist/hooks/mutations/useFolderMutations.d.ts.map +1 -0
- package/dist/hooks/mutations/useGalleryMutations.d.ts +39 -0
- package/dist/hooks/mutations/useGalleryMutations.d.ts.map +1 -0
- package/dist/hooks/mutations/useUpdateAsset.d.ts +50 -0
- package/dist/hooks/mutations/useUpdateAsset.d.ts.map +1 -0
- package/dist/hooks/queries/index.d.ts +8 -0
- package/dist/hooks/queries/index.d.ts.map +1 -0
- package/dist/hooks/queries/useAsset.d.ts +46 -0
- package/dist/hooks/queries/useAsset.d.ts.map +1 -0
- package/dist/hooks/queries/useAssets.d.ts +58 -0
- package/dist/hooks/queries/useAssets.d.ts.map +1 -0
- package/dist/hooks/queries/useCopyright.d.ts +51 -0
- package/dist/hooks/queries/useCopyright.d.ts.map +1 -0
- package/dist/hooks/queries/useFolder.d.ts +93 -0
- package/dist/hooks/queries/useFolder.d.ts.map +1 -0
- package/dist/hooks/queries/useGallery.d.ts +101 -0
- package/dist/hooks/queries/useGallery.d.ts.map +1 -0
- package/dist/hooks/queries/useLicense.d.ts +63 -0
- package/dist/hooks/queries/useLicense.d.ts.map +1 -0
- package/dist/hooks/queries/useTenant.d.ts +89 -0
- package/dist/hooks/queries/useTenant.d.ts.map +1 -0
- package/dist/index.cjs +244 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +244 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +596 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.ts +22 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +596 -0
- package/dist/react.js.map +1 -0
- package/dist/upload/index.d.ts +4 -0
- package/dist/upload/index.d.ts.map +1 -0
- package/dist/upload/types.d.ts +96 -0
- package/dist/upload/types.d.ts.map +1 -0
- package/dist/upload/useMultipartUpload.d.ts +67 -0
- package/dist/upload/useMultipartUpload.d.ts.map +1 -0
- package/dist/upload/useUpload.d.ts +64 -0
- package/dist/upload/useUpload.d.ts.map +1 -0
- package/dist/upload.cjs +6 -0
- package/dist/upload.cjs.map +1 -0
- package/dist/upload.d.ts +2 -0
- package/dist/upload.js +6 -0
- package/dist/upload.js.map +1 -0
- package/dist/useFileProxyUrl-BCcux6re.cjs +87 -0
- package/dist/useFileProxyUrl-BCcux6re.cjs.map +1 -0
- package/dist/useFileProxyUrl-D_S1R_7O.js +88 -0
- package/dist/useFileProxyUrl-D_S1R_7O.js.map +1 -0
- package/dist/useMultipartUpload-BKnDbl8h.cjs +423 -0
- package/dist/useMultipartUpload-BKnDbl8h.cjs.map +1 -0
- package/dist/useMultipartUpload-CPK_PgUU.js +424 -0
- package/dist/useMultipartUpload-CPK_PgUU.js.map +1 -0
- package/dist/vanilla.cjs +219 -0
- package/dist/vanilla.cjs.map +1 -0
- package/dist/vanilla.d.ts +94 -0
- package/dist/vanilla.d.ts.map +1 -0
- package/dist/vanilla.js +220 -0
- package/dist/vanilla.js.map +1 -0
- package/package.json +177 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const react = require("react");
|
|
3
|
+
const urql = require("urql");
|
|
4
|
+
const UploadError = require("./UploadError-CHBJuChw.cjs");
|
|
5
|
+
const CREATE_UPLOAD_LINK_MUTATION = `
|
|
6
|
+
mutation CreateFairuUploadLink(
|
|
7
|
+
$type: FairuUploadType!
|
|
8
|
+
$filename: String!
|
|
9
|
+
$folder: ID
|
|
10
|
+
$alt: String
|
|
11
|
+
$caption: String
|
|
12
|
+
$description: String
|
|
13
|
+
$focal_point: String
|
|
14
|
+
$copyright: String
|
|
15
|
+
) {
|
|
16
|
+
createFairuUploadLink(
|
|
17
|
+
type: $type
|
|
18
|
+
filename: $filename
|
|
19
|
+
folder: $folder
|
|
20
|
+
alt: $alt
|
|
21
|
+
caption: $caption
|
|
22
|
+
description: $description
|
|
23
|
+
focal_point: $focal_point
|
|
24
|
+
copyright: $copyright
|
|
25
|
+
) {
|
|
26
|
+
id
|
|
27
|
+
url
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
const initialState$1 = {
|
|
32
|
+
status: "idle",
|
|
33
|
+
progress: { loaded: 0, total: 0, percentage: 0 },
|
|
34
|
+
error: null,
|
|
35
|
+
result: null
|
|
36
|
+
};
|
|
37
|
+
function useUpload() {
|
|
38
|
+
const [state, setState] = react.useState(initialState$1);
|
|
39
|
+
const abortControllerRef = react.useRef(null);
|
|
40
|
+
const [, createUploadLink] = urql.useMutation(CREATE_UPLOAD_LINK_MUTATION);
|
|
41
|
+
const upload = react.useCallback(
|
|
42
|
+
async (file, options = {}) => {
|
|
43
|
+
var _a, _b, _c, _d, _e;
|
|
44
|
+
abortControllerRef.current = new AbortController();
|
|
45
|
+
const { signal } = abortControllerRef.current;
|
|
46
|
+
try {
|
|
47
|
+
setState({
|
|
48
|
+
status: "preparing",
|
|
49
|
+
progress: { loaded: 0, total: file.size, percentage: 0 },
|
|
50
|
+
error: null,
|
|
51
|
+
result: null
|
|
52
|
+
});
|
|
53
|
+
(_a = options.onStatusChange) == null ? void 0 : _a.call(options, "preparing");
|
|
54
|
+
const linkResponse = await createUploadLink({
|
|
55
|
+
type: "STANDARD",
|
|
56
|
+
filename: file.name,
|
|
57
|
+
folder: options.folderId,
|
|
58
|
+
alt: options.alt,
|
|
59
|
+
caption: options.caption,
|
|
60
|
+
description: options.description,
|
|
61
|
+
focal_point: options.focalPoint,
|
|
62
|
+
copyright: options.copyright
|
|
63
|
+
});
|
|
64
|
+
if (linkResponse.error || !((_b = linkResponse.data) == null ? void 0 : _b.createFairuUploadLink)) {
|
|
65
|
+
throw new UploadError.UploadError("Failed to create upload link", "INIT_FAILED");
|
|
66
|
+
}
|
|
67
|
+
const { id, url } = linkResponse.data.createFairuUploadLink;
|
|
68
|
+
if (signal.aborted) {
|
|
69
|
+
throw new UploadError.UploadError("Upload aborted", "ABORTED");
|
|
70
|
+
}
|
|
71
|
+
setState((prev) => ({ ...prev, status: "uploading" }));
|
|
72
|
+
(_c = options.onStatusChange) == null ? void 0 : _c.call(options, "uploading");
|
|
73
|
+
await uploadWithProgress(url, file, signal, (progress) => {
|
|
74
|
+
var _a2;
|
|
75
|
+
setState((prev) => ({ ...prev, progress }));
|
|
76
|
+
(_a2 = options.onProgress) == null ? void 0 : _a2.call(options, progress);
|
|
77
|
+
});
|
|
78
|
+
if (signal.aborted) {
|
|
79
|
+
throw new UploadError.UploadError("Upload aborted", "ABORTED");
|
|
80
|
+
}
|
|
81
|
+
const result = { id, url };
|
|
82
|
+
setState({
|
|
83
|
+
status: "completed",
|
|
84
|
+
progress: { loaded: file.size, total: file.size, percentage: 100 },
|
|
85
|
+
error: null,
|
|
86
|
+
result
|
|
87
|
+
});
|
|
88
|
+
(_d = options.onStatusChange) == null ? void 0 : _d.call(options, "completed");
|
|
89
|
+
return result;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const uploadError = error instanceof UploadError.UploadError ? error : new UploadError.UploadError(
|
|
92
|
+
error instanceof Error ? error.message : "Upload failed"
|
|
93
|
+
);
|
|
94
|
+
setState((prev) => ({
|
|
95
|
+
...prev,
|
|
96
|
+
status: uploadError.isAborted() ? "aborted" : "error",
|
|
97
|
+
error: uploadError
|
|
98
|
+
}));
|
|
99
|
+
(_e = options.onStatusChange) == null ? void 0 : _e.call(
|
|
100
|
+
options,
|
|
101
|
+
uploadError.isAborted() ? "aborted" : "error"
|
|
102
|
+
);
|
|
103
|
+
throw uploadError;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
[createUploadLink]
|
|
107
|
+
);
|
|
108
|
+
const abort = react.useCallback(() => {
|
|
109
|
+
var _a;
|
|
110
|
+
(_a = abortControllerRef.current) == null ? void 0 : _a.abort();
|
|
111
|
+
}, []);
|
|
112
|
+
const reset = react.useCallback(() => {
|
|
113
|
+
var _a;
|
|
114
|
+
(_a = abortControllerRef.current) == null ? void 0 : _a.abort();
|
|
115
|
+
setState(initialState$1);
|
|
116
|
+
}, []);
|
|
117
|
+
return {
|
|
118
|
+
...state,
|
|
119
|
+
upload,
|
|
120
|
+
abort,
|
|
121
|
+
reset
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async function uploadWithProgress(url, file, signal, onProgress) {
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const xhr = new XMLHttpRequest();
|
|
127
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
128
|
+
if (event.lengthComputable) {
|
|
129
|
+
onProgress({
|
|
130
|
+
loaded: event.loaded,
|
|
131
|
+
total: event.total,
|
|
132
|
+
percentage: Math.round(event.loaded / event.total * 100)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
xhr.addEventListener("load", () => {
|
|
137
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
138
|
+
resolve();
|
|
139
|
+
} else {
|
|
140
|
+
reject(new UploadError.UploadError(`Upload failed with status ${xhr.status}`));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
xhr.addEventListener("error", () => {
|
|
144
|
+
reject(new UploadError.UploadError("Upload failed", "NETWORK_ERROR"));
|
|
145
|
+
});
|
|
146
|
+
xhr.addEventListener("abort", () => {
|
|
147
|
+
reject(new UploadError.UploadError("Upload aborted", "ABORTED"));
|
|
148
|
+
});
|
|
149
|
+
signal.addEventListener("abort", () => {
|
|
150
|
+
xhr.abort();
|
|
151
|
+
});
|
|
152
|
+
xhr.open("PUT", url);
|
|
153
|
+
xhr.setRequestHeader(
|
|
154
|
+
"Content-Type",
|
|
155
|
+
file.type || "application/octet-stream"
|
|
156
|
+
);
|
|
157
|
+
xhr.send(file);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const INIT_MULTIPART_MUTATION = `
|
|
161
|
+
mutation InitFairuMultipartUpload(
|
|
162
|
+
$filename: String!
|
|
163
|
+
$folder: ID
|
|
164
|
+
$fileSize: Int
|
|
165
|
+
$contentType: String
|
|
166
|
+
$alt: String
|
|
167
|
+
$caption: String
|
|
168
|
+
$description: String
|
|
169
|
+
$copyright: String
|
|
170
|
+
) {
|
|
171
|
+
initFairuMultipartUpload(
|
|
172
|
+
filename: $filename
|
|
173
|
+
folder: $folder
|
|
174
|
+
fileSize: $fileSize
|
|
175
|
+
contentType: $contentType
|
|
176
|
+
alt: $alt
|
|
177
|
+
caption: $caption
|
|
178
|
+
description: $description
|
|
179
|
+
copyright: $copyright
|
|
180
|
+
) {
|
|
181
|
+
id
|
|
182
|
+
upload_id
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
const GET_PART_URL_MUTATION = `
|
|
187
|
+
mutation GetFairuMultipartPartUrl($fileId: ID!, $uploadId: String!, $partNumber: Int!) {
|
|
188
|
+
getFairuMultipartPartUrl(fileId: $fileId, uploadId: $uploadId, partNumber: $partNumber) {
|
|
189
|
+
url
|
|
190
|
+
part_number
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
`;
|
|
194
|
+
const COMPLETE_MULTIPART_MUTATION = `
|
|
195
|
+
mutation CompleteFairuMultipartUpload($fileId: ID!, $uploadId: String!, $parts: [FairuMultipartPartInput!]!) {
|
|
196
|
+
completeFairuMultipartUpload(fileId: $fileId, uploadId: $uploadId, parts: $parts) {
|
|
197
|
+
id
|
|
198
|
+
url
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
`;
|
|
202
|
+
const ABORT_MULTIPART_MUTATION = `
|
|
203
|
+
mutation AbortFairuMultipartUpload($fileId: ID!, $uploadId: String!) {
|
|
204
|
+
abortFairuMultipartUpload(fileId: $fileId, uploadId: $uploadId)
|
|
205
|
+
}
|
|
206
|
+
`;
|
|
207
|
+
const DEFAULT_PART_SIZE = 5 * 1024 * 1024;
|
|
208
|
+
const DEFAULT_CONCURRENCY = 3;
|
|
209
|
+
const initialState = {
|
|
210
|
+
status: "idle",
|
|
211
|
+
progress: { loaded: 0, total: 0, percentage: 0 },
|
|
212
|
+
error: null,
|
|
213
|
+
result: null,
|
|
214
|
+
partsCompleted: 0,
|
|
215
|
+
totalParts: 0
|
|
216
|
+
};
|
|
217
|
+
function useMultipartUpload() {
|
|
218
|
+
const [state, setState] = react.useState(initialState);
|
|
219
|
+
const abortControllerRef = react.useRef(null);
|
|
220
|
+
const uploadStateRef = react.useRef(null);
|
|
221
|
+
const [, initMultipart] = urql.useMutation(INIT_MULTIPART_MUTATION);
|
|
222
|
+
const [, getPartUrl] = urql.useMutation(GET_PART_URL_MUTATION);
|
|
223
|
+
const [, completeMultipart] = urql.useMutation(COMPLETE_MULTIPART_MUTATION);
|
|
224
|
+
const [, abortMultipart] = urql.useMutation(ABORT_MULTIPART_MUTATION);
|
|
225
|
+
const upload = react.useCallback(
|
|
226
|
+
async (file, options = {}) => {
|
|
227
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
228
|
+
const partSize = options.partSize ?? DEFAULT_PART_SIZE;
|
|
229
|
+
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
230
|
+
abortControllerRef.current = new AbortController();
|
|
231
|
+
const { signal } = abortControllerRef.current;
|
|
232
|
+
try {
|
|
233
|
+
const totalParts = Math.ceil(file.size / partSize);
|
|
234
|
+
setState({
|
|
235
|
+
status: "preparing",
|
|
236
|
+
progress: { loaded: 0, total: file.size, percentage: 0 },
|
|
237
|
+
error: null,
|
|
238
|
+
result: null,
|
|
239
|
+
partsCompleted: 0,
|
|
240
|
+
totalParts
|
|
241
|
+
});
|
|
242
|
+
(_a = options.onStatusChange) == null ? void 0 : _a.call(options, "preparing");
|
|
243
|
+
const initResponse = await initMultipart({
|
|
244
|
+
filename: file.name,
|
|
245
|
+
folder: options.folderId,
|
|
246
|
+
fileSize: file.size,
|
|
247
|
+
contentType: file.type || "application/octet-stream",
|
|
248
|
+
alt: options.alt,
|
|
249
|
+
caption: options.caption,
|
|
250
|
+
description: options.description,
|
|
251
|
+
copyright: options.copyright
|
|
252
|
+
});
|
|
253
|
+
if (initResponse.error || !((_b = initResponse.data) == null ? void 0 : _b.initFairuMultipartUpload)) {
|
|
254
|
+
throw new UploadError.UploadError("Failed to initialize multipart upload", "INIT_FAILED");
|
|
255
|
+
}
|
|
256
|
+
const { id: fileId, upload_id: uploadId } = initResponse.data.initFairuMultipartUpload;
|
|
257
|
+
uploadStateRef.current = { fileId, uploadId };
|
|
258
|
+
if (signal.aborted) {
|
|
259
|
+
throw new UploadError.UploadError("Upload aborted", "ABORTED");
|
|
260
|
+
}
|
|
261
|
+
setState((prev) => ({ ...prev, status: "uploading" }));
|
|
262
|
+
(_c = options.onStatusChange) == null ? void 0 : _c.call(options, "uploading");
|
|
263
|
+
const completedParts = [];
|
|
264
|
+
let uploadedBytes = 0;
|
|
265
|
+
const partRanges = [];
|
|
266
|
+
for (let i = 0; i < totalParts; i++) {
|
|
267
|
+
partRanges.push({
|
|
268
|
+
partNumber: i + 1,
|
|
269
|
+
start: i * partSize,
|
|
270
|
+
end: Math.min((i + 1) * partSize, file.size)
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
const uploadPart = async (partInfo) => {
|
|
274
|
+
var _a2;
|
|
275
|
+
if (signal.aborted) {
|
|
276
|
+
throw new UploadError.UploadError("Upload aborted", "ABORTED");
|
|
277
|
+
}
|
|
278
|
+
const urlResponse = await getPartUrl({
|
|
279
|
+
fileId,
|
|
280
|
+
uploadId,
|
|
281
|
+
partNumber: partInfo.partNumber
|
|
282
|
+
});
|
|
283
|
+
if (urlResponse.error || !((_a2 = urlResponse.data) == null ? void 0 : _a2.getFairuMultipartPartUrl)) {
|
|
284
|
+
throw new UploadError.UploadError(
|
|
285
|
+
`Failed to get URL for part ${partInfo.partNumber}`,
|
|
286
|
+
"PART_FAILED",
|
|
287
|
+
partInfo.partNumber
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const { url } = urlResponse.data.getFairuMultipartPartUrl;
|
|
291
|
+
const partBlob = file.slice(partInfo.start, partInfo.end);
|
|
292
|
+
const etag = await uploadPartWithProgress(
|
|
293
|
+
url,
|
|
294
|
+
partBlob,
|
|
295
|
+
signal,
|
|
296
|
+
(partLoaded) => {
|
|
297
|
+
var _a3;
|
|
298
|
+
const totalLoaded = uploadedBytes + partLoaded;
|
|
299
|
+
const progress = {
|
|
300
|
+
loaded: totalLoaded,
|
|
301
|
+
total: file.size,
|
|
302
|
+
percentage: Math.round(totalLoaded / file.size * 100)
|
|
303
|
+
};
|
|
304
|
+
setState((prev) => ({ ...prev, progress }));
|
|
305
|
+
(_a3 = options.onProgress) == null ? void 0 : _a3.call(options, progress);
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
uploadedBytes += partInfo.end - partInfo.start;
|
|
309
|
+
setState((prev) => ({
|
|
310
|
+
...prev,
|
|
311
|
+
partsCompleted: prev.partsCompleted + 1
|
|
312
|
+
}));
|
|
313
|
+
return {
|
|
314
|
+
partNumber: partInfo.partNumber,
|
|
315
|
+
etag
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
for (let i = 0; i < partRanges.length; i += concurrency) {
|
|
319
|
+
const batch = partRanges.slice(i, i + concurrency);
|
|
320
|
+
const results = await Promise.all(batch.map(uploadPart));
|
|
321
|
+
completedParts.push(...results);
|
|
322
|
+
}
|
|
323
|
+
if (signal.aborted) {
|
|
324
|
+
throw new UploadError.UploadError("Upload aborted", "ABORTED");
|
|
325
|
+
}
|
|
326
|
+
setState((prev) => ({ ...prev, status: "processing" }));
|
|
327
|
+
(_d = options.onStatusChange) == null ? void 0 : _d.call(options, "processing");
|
|
328
|
+
const completeResponse = await completeMultipart({
|
|
329
|
+
fileId,
|
|
330
|
+
uploadId,
|
|
331
|
+
parts: completedParts.map((p) => ({
|
|
332
|
+
PartNumber: p.partNumber,
|
|
333
|
+
ETag: p.etag
|
|
334
|
+
}))
|
|
335
|
+
});
|
|
336
|
+
if (completeResponse.error || !((_e = completeResponse.data) == null ? void 0 : _e.completeFairuMultipartUpload)) {
|
|
337
|
+
throw new UploadError.UploadError("Failed to complete multipart upload", "COMPLETE_FAILED");
|
|
338
|
+
}
|
|
339
|
+
const result = {
|
|
340
|
+
id: completeResponse.data.completeFairuMultipartUpload.id,
|
|
341
|
+
url: completeResponse.data.completeFairuMultipartUpload.url
|
|
342
|
+
};
|
|
343
|
+
setState({
|
|
344
|
+
status: "completed",
|
|
345
|
+
progress: { loaded: file.size, total: file.size, percentage: 100 },
|
|
346
|
+
error: null,
|
|
347
|
+
result,
|
|
348
|
+
partsCompleted: totalParts,
|
|
349
|
+
totalParts
|
|
350
|
+
});
|
|
351
|
+
(_f = options.onStatusChange) == null ? void 0 : _f.call(options, "completed");
|
|
352
|
+
uploadStateRef.current = null;
|
|
353
|
+
return result;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
const uploadError = error instanceof UploadError.UploadError ? error : new UploadError.UploadError(
|
|
356
|
+
error instanceof Error ? error.message : "Upload failed"
|
|
357
|
+
);
|
|
358
|
+
setState((prev) => ({
|
|
359
|
+
...prev,
|
|
360
|
+
status: uploadError.isAborted() ? "aborted" : "error",
|
|
361
|
+
error: uploadError
|
|
362
|
+
}));
|
|
363
|
+
(_g = options.onStatusChange) == null ? void 0 : _g.call(
|
|
364
|
+
options,
|
|
365
|
+
uploadError.isAborted() ? "aborted" : "error"
|
|
366
|
+
);
|
|
367
|
+
throw uploadError;
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
[initMultipart, getPartUrl, completeMultipart]
|
|
371
|
+
);
|
|
372
|
+
const abort = react.useCallback(async () => {
|
|
373
|
+
var _a;
|
|
374
|
+
(_a = abortControllerRef.current) == null ? void 0 : _a.abort();
|
|
375
|
+
if (uploadStateRef.current) {
|
|
376
|
+
const { fileId, uploadId } = uploadStateRef.current;
|
|
377
|
+
await abortMultipart({ fileId, uploadId });
|
|
378
|
+
uploadStateRef.current = null;
|
|
379
|
+
}
|
|
380
|
+
}, [abortMultipart]);
|
|
381
|
+
const reset = react.useCallback(() => {
|
|
382
|
+
abort();
|
|
383
|
+
setState(initialState);
|
|
384
|
+
}, [abort]);
|
|
385
|
+
return {
|
|
386
|
+
...state,
|
|
387
|
+
upload,
|
|
388
|
+
abort,
|
|
389
|
+
reset
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
async function uploadPartWithProgress(url, blob, signal, onProgress) {
|
|
393
|
+
return new Promise((resolve, reject) => {
|
|
394
|
+
const xhr = new XMLHttpRequest();
|
|
395
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
396
|
+
if (event.lengthComputable) {
|
|
397
|
+
onProgress(event.loaded);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
xhr.addEventListener("load", () => {
|
|
401
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
402
|
+
const etag = xhr.getResponseHeader("ETag") ?? "";
|
|
403
|
+
resolve(etag.replace(/"/g, ""));
|
|
404
|
+
} else {
|
|
405
|
+
reject(new UploadError.UploadError(`Part upload failed with status ${xhr.status}`, "PART_FAILED"));
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
xhr.addEventListener("error", () => {
|
|
409
|
+
reject(new UploadError.UploadError("Part upload failed", "NETWORK_ERROR"));
|
|
410
|
+
});
|
|
411
|
+
xhr.addEventListener("abort", () => {
|
|
412
|
+
reject(new UploadError.UploadError("Upload aborted", "ABORTED"));
|
|
413
|
+
});
|
|
414
|
+
signal.addEventListener("abort", () => {
|
|
415
|
+
xhr.abort();
|
|
416
|
+
});
|
|
417
|
+
xhr.open("PUT", url);
|
|
418
|
+
xhr.send(blob);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
exports.useMultipartUpload = useMultipartUpload;
|
|
422
|
+
exports.useUpload = useUpload;
|
|
423
|
+
//# sourceMappingURL=useMultipartUpload-BKnDbl8h.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMultipartUpload-BKnDbl8h.cjs","sources":["../src/upload/useUpload.ts","../src/upload/useMultipartUpload.ts"],"sourcesContent":["import { useState, useCallback, useRef } from 'react';\nimport { useMutation } from 'urql';\nimport type {\n UploadStatus,\n UploadProgress,\n UploadOptions,\n UploadResult,\n} from './types';\nimport { UploadError } from '../errors';\n\nconst CREATE_UPLOAD_LINK_MUTATION = `\n mutation CreateFairuUploadLink(\n $type: FairuUploadType!\n $filename: String!\n $folder: ID\n $alt: String\n $caption: String\n $description: String\n $focal_point: String\n $copyright: String\n ) {\n createFairuUploadLink(\n type: $type\n filename: $filename\n folder: $folder\n alt: $alt\n caption: $caption\n description: $description\n focal_point: $focal_point\n copyright: $copyright\n ) {\n id\n url\n }\n }\n`;\n\n/**\n * State for the upload hook.\n */\nexport interface UseUploadState {\n status: UploadStatus;\n progress: UploadProgress;\n error: UploadError | null;\n result: UploadResult | null;\n}\n\n/**\n * Result of the useUpload hook.\n */\nexport interface UseUploadResult extends UseUploadState {\n /**\n * Upload a file.\n */\n upload: (file: File, options?: UploadOptions) => Promise<UploadResult>;\n\n /**\n * Abort the current upload.\n */\n abort: () => void;\n\n /**\n * Reset the upload state.\n */\n reset: () => void;\n}\n\nconst initialState: UseUploadState = {\n status: 'idle',\n progress: { loaded: 0, total: 0, percentage: 0 },\n error: null,\n result: null,\n};\n\n/**\n * Hook for uploading files to Fairu.\n *\n * @example\n * ```tsx\n * function FileUploader() {\n * const { upload, progress, status, error, result } = useUpload();\n *\n * const handleFileSelect = async (e) => {\n * const file = e.target.files?.[0];\n * if (!file) return;\n *\n * try {\n * const result = await upload(file, {\n * folderId: 'target-folder-id',\n * alt: 'Uploaded image',\n * });\n * console.log('Uploaded:', result);\n * } catch (err) {\n * console.error('Upload failed:', err);\n * }\n * };\n *\n * return (\n * <div>\n * <input type=\"file\" onChange={handleFileSelect} />\n * {status === 'uploading' && (\n * <progress value={progress.percentage} max={100} />\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useUpload(): UseUploadResult {\n const [state, setState] = useState<UseUploadState>(initialState);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const [, createUploadLink] = useMutation(CREATE_UPLOAD_LINK_MUTATION);\n\n const upload = useCallback(\n async (file: File, options: UploadOptions = {}): Promise<UploadResult> => {\n // Create new abort controller\n abortControllerRef.current = new AbortController();\n const { signal } = abortControllerRef.current;\n\n try {\n // Set initial state\n setState({\n status: 'preparing',\n progress: { loaded: 0, total: file.size, percentage: 0 },\n error: null,\n result: null,\n });\n options.onStatusChange?.('preparing');\n\n // Create upload link\n const linkResponse = await createUploadLink({\n type: 'STANDARD',\n filename: file.name,\n folder: options.folderId,\n alt: options.alt,\n caption: options.caption,\n description: options.description,\n focal_point: options.focalPoint,\n copyright: options.copyright,\n });\n\n if (linkResponse.error || !linkResponse.data?.createFairuUploadLink) {\n throw new UploadError('Failed to create upload link', 'INIT_FAILED');\n }\n\n const { id, url } = linkResponse.data.createFairuUploadLink;\n\n // Check if aborted\n if (signal.aborted) {\n throw new UploadError('Upload aborted', 'ABORTED');\n }\n\n // Start upload\n setState((prev) => ({ ...prev, status: 'uploading' }));\n options.onStatusChange?.('uploading');\n\n // Upload file with progress tracking\n await uploadWithProgress(url, file, signal, (progress) => {\n setState((prev) => ({ ...prev, progress }));\n options.onProgress?.(progress);\n });\n\n // Check if aborted\n if (signal.aborted) {\n throw new UploadError('Upload aborted', 'ABORTED');\n }\n\n // Completed\n const result: UploadResult = { id, url };\n setState({\n status: 'completed',\n progress: { loaded: file.size, total: file.size, percentage: 100 },\n error: null,\n result,\n });\n options.onStatusChange?.('completed');\n\n return result;\n } catch (error) {\n const uploadError =\n error instanceof UploadError\n ? error\n : new UploadError(\n error instanceof Error ? error.message : 'Upload failed'\n );\n\n setState((prev) => ({\n ...prev,\n status: uploadError.isAborted() ? 'aborted' : 'error',\n error: uploadError,\n }));\n options.onStatusChange?.(\n uploadError.isAborted() ? 'aborted' : 'error'\n );\n\n throw uploadError;\n }\n },\n [createUploadLink]\n );\n\n const abort = useCallback(() => {\n abortControllerRef.current?.abort();\n }, []);\n\n const reset = useCallback(() => {\n abortControllerRef.current?.abort();\n setState(initialState);\n }, []);\n\n return {\n ...state,\n upload,\n abort,\n reset,\n };\n}\n\n/**\n * Helper function for upload with progress tracking.\n */\nasync function uploadWithProgress(\n url: string,\n file: File,\n signal: AbortSignal,\n onProgress: (progress: UploadProgress) => void\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n onProgress({\n loaded: event.loaded,\n total: event.total,\n percentage: Math.round((event.loaded / event.total) * 100),\n });\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new UploadError(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new UploadError('Upload failed', 'NETWORK_ERROR'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new UploadError('Upload aborted', 'ABORTED'));\n });\n\n signal.addEventListener('abort', () => {\n xhr.abort();\n });\n\n xhr.open('PUT', url);\n xhr.setRequestHeader(\n 'Content-Type',\n file.type || 'application/octet-stream'\n );\n xhr.send(file);\n });\n}\n","import { useState, useCallback, useRef } from 'react';\nimport { useMutation } from 'urql';\nimport type {\n UploadStatus,\n UploadProgress,\n MultipartUploadOptions,\n UploadResult,\n UploadPart,\n} from './types';\nimport { UploadError } from '../errors';\n\nconst INIT_MULTIPART_MUTATION = `\n mutation InitFairuMultipartUpload(\n $filename: String!\n $folder: ID\n $fileSize: Int\n $contentType: String\n $alt: String\n $caption: String\n $description: String\n $copyright: String\n ) {\n initFairuMultipartUpload(\n filename: $filename\n folder: $folder\n fileSize: $fileSize\n contentType: $contentType\n alt: $alt\n caption: $caption\n description: $description\n copyright: $copyright\n ) {\n id\n upload_id\n }\n }\n`;\n\nconst GET_PART_URL_MUTATION = `\n mutation GetFairuMultipartPartUrl($fileId: ID!, $uploadId: String!, $partNumber: Int!) {\n getFairuMultipartPartUrl(fileId: $fileId, uploadId: $uploadId, partNumber: $partNumber) {\n url\n part_number\n }\n }\n`;\n\nconst COMPLETE_MULTIPART_MUTATION = `\n mutation CompleteFairuMultipartUpload($fileId: ID!, $uploadId: String!, $parts: [FairuMultipartPartInput!]!) {\n completeFairuMultipartUpload(fileId: $fileId, uploadId: $uploadId, parts: $parts) {\n id\n url\n }\n }\n`;\n\nconst ABORT_MULTIPART_MUTATION = `\n mutation AbortFairuMultipartUpload($fileId: ID!, $uploadId: String!) {\n abortFairuMultipartUpload(fileId: $fileId, uploadId: $uploadId)\n }\n`;\n\nconst DEFAULT_PART_SIZE = 5 * 1024 * 1024; // 5MB\nconst DEFAULT_CONCURRENCY = 3;\n\n/**\n * State for the multipart upload hook.\n */\nexport interface UseMultipartUploadState {\n status: UploadStatus;\n progress: UploadProgress;\n error: UploadError | null;\n result: UploadResult | null;\n partsCompleted: number;\n totalParts: number;\n}\n\n/**\n * Result of the useMultipartUpload hook.\n */\nexport interface UseMultipartUploadResult extends UseMultipartUploadState {\n upload: (file: File, options?: MultipartUploadOptions) => Promise<UploadResult>;\n abort: () => void;\n reset: () => void;\n}\n\nconst initialState: UseMultipartUploadState = {\n status: 'idle',\n progress: { loaded: 0, total: 0, percentage: 0 },\n error: null,\n result: null,\n partsCompleted: 0,\n totalParts: 0,\n};\n\n/**\n * Hook for multipart uploads of large files.\n *\n * @example\n * ```tsx\n * function LargeFileUploader() {\n * const {\n * upload,\n * progress,\n * status,\n * partsCompleted,\n * totalParts,\n * abort,\n * } = useMultipartUpload();\n *\n * const handleFileSelect = async (e) => {\n * const file = e.target.files?.[0];\n * if (!file) return;\n *\n * try {\n * await upload(file, {\n * partSize: 10 * 1024 * 1024, // 10MB parts\n * concurrency: 5,\n * });\n * } catch (err) {\n * console.error('Upload failed:', err);\n * }\n * };\n *\n * return (\n * <div>\n * <input type=\"file\" onChange={handleFileSelect} />\n * {status === 'uploading' && (\n * <div>\n * <progress value={progress.percentage} max={100} />\n * <span>Parts: {partsCompleted}/{totalParts}</span>\n * <button onClick={abort}>Cancel</button>\n * </div>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useMultipartUpload(): UseMultipartUploadResult {\n const [state, setState] = useState<UseMultipartUploadState>(initialState);\n const abortControllerRef = useRef<AbortController | null>(null);\n const uploadStateRef = useRef<{\n fileId: string;\n uploadId: string;\n } | null>(null);\n\n const [, initMultipart] = useMutation(INIT_MULTIPART_MUTATION);\n const [, getPartUrl] = useMutation(GET_PART_URL_MUTATION);\n const [, completeMultipart] = useMutation(COMPLETE_MULTIPART_MUTATION);\n const [, abortMultipart] = useMutation(ABORT_MULTIPART_MUTATION);\n\n const upload = useCallback(\n async (\n file: File,\n options: MultipartUploadOptions = {}\n ): Promise<UploadResult> => {\n const partSize = options.partSize ?? DEFAULT_PART_SIZE;\n const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;\n\n abortControllerRef.current = new AbortController();\n const { signal } = abortControllerRef.current;\n\n try {\n // Calculate parts\n const totalParts = Math.ceil(file.size / partSize);\n\n setState({\n status: 'preparing',\n progress: { loaded: 0, total: file.size, percentage: 0 },\n error: null,\n result: null,\n partsCompleted: 0,\n totalParts,\n });\n options.onStatusChange?.('preparing');\n\n // Initialize multipart upload\n const initResponse = await initMultipart({\n filename: file.name,\n folder: options.folderId,\n fileSize: file.size,\n contentType: file.type || 'application/octet-stream',\n alt: options.alt,\n caption: options.caption,\n description: options.description,\n copyright: options.copyright,\n });\n\n if (initResponse.error || !initResponse.data?.initFairuMultipartUpload) {\n throw new UploadError('Failed to initialize multipart upload', 'INIT_FAILED');\n }\n\n const { id: fileId, upload_id: uploadId } =\n initResponse.data.initFairuMultipartUpload;\n uploadStateRef.current = { fileId, uploadId };\n\n if (signal.aborted) {\n throw new UploadError('Upload aborted', 'ABORTED');\n }\n\n // Start uploading\n setState((prev) => ({ ...prev, status: 'uploading' }));\n options.onStatusChange?.('uploading');\n\n // Upload parts with concurrency control\n const completedParts: UploadPart[] = [];\n let uploadedBytes = 0;\n\n // Create part ranges\n const partRanges: Array<{ partNumber: number; start: number; end: number }> = [];\n for (let i = 0; i < totalParts; i++) {\n partRanges.push({\n partNumber: i + 1,\n start: i * partSize,\n end: Math.min((i + 1) * partSize, file.size),\n });\n }\n\n // Upload part function\n const uploadPart = async (partInfo: typeof partRanges[0]): Promise<UploadPart> => {\n if (signal.aborted) {\n throw new UploadError('Upload aborted', 'ABORTED');\n }\n\n // Get presigned URL for part\n const urlResponse = await getPartUrl({\n fileId,\n uploadId,\n partNumber: partInfo.partNumber,\n });\n\n if (urlResponse.error || !urlResponse.data?.getFairuMultipartPartUrl) {\n throw new UploadError(\n `Failed to get URL for part ${partInfo.partNumber}`,\n 'PART_FAILED',\n partInfo.partNumber\n );\n }\n\n const { url } = urlResponse.data.getFairuMultipartPartUrl;\n\n // Upload part\n const partBlob = file.slice(partInfo.start, partInfo.end);\n const etag = await uploadPartWithProgress(\n url,\n partBlob,\n signal,\n (partLoaded) => {\n const totalLoaded = uploadedBytes + partLoaded;\n const progress: UploadProgress = {\n loaded: totalLoaded,\n total: file.size,\n percentage: Math.round((totalLoaded / file.size) * 100),\n };\n setState((prev) => ({ ...prev, progress }));\n options.onProgress?.(progress);\n }\n );\n\n uploadedBytes += partInfo.end - partInfo.start;\n\n setState((prev) => ({\n ...prev,\n partsCompleted: prev.partsCompleted + 1,\n }));\n\n return {\n partNumber: partInfo.partNumber,\n etag,\n };\n };\n\n // Execute with concurrency limit\n for (let i = 0; i < partRanges.length; i += concurrency) {\n const batch = partRanges.slice(i, i + concurrency);\n const results = await Promise.all(batch.map(uploadPart));\n completedParts.push(...results);\n }\n\n if (signal.aborted) {\n throw new UploadError('Upload aborted', 'ABORTED');\n }\n\n // Complete multipart upload\n setState((prev) => ({ ...prev, status: 'processing' }));\n options.onStatusChange?.('processing');\n\n const completeResponse = await completeMultipart({\n fileId,\n uploadId,\n parts: completedParts.map((p) => ({\n PartNumber: p.partNumber,\n ETag: p.etag,\n })),\n });\n\n if (\n completeResponse.error ||\n !completeResponse.data?.completeFairuMultipartUpload\n ) {\n throw new UploadError('Failed to complete multipart upload', 'COMPLETE_FAILED');\n }\n\n const result: UploadResult = {\n id: completeResponse.data.completeFairuMultipartUpload.id,\n url: completeResponse.data.completeFairuMultipartUpload.url,\n };\n\n setState({\n status: 'completed',\n progress: { loaded: file.size, total: file.size, percentage: 100 },\n error: null,\n result,\n partsCompleted: totalParts,\n totalParts,\n });\n options.onStatusChange?.('completed');\n uploadStateRef.current = null;\n\n return result;\n } catch (error) {\n const uploadError =\n error instanceof UploadError\n ? error\n : new UploadError(\n error instanceof Error ? error.message : 'Upload failed'\n );\n\n setState((prev) => ({\n ...prev,\n status: uploadError.isAborted() ? 'aborted' : 'error',\n error: uploadError,\n }));\n options.onStatusChange?.(\n uploadError.isAborted() ? 'aborted' : 'error'\n );\n\n throw uploadError;\n }\n },\n [initMultipart, getPartUrl, completeMultipart]\n );\n\n const abort = useCallback(async () => {\n abortControllerRef.current?.abort();\n\n // Abort the multipart upload on the server\n if (uploadStateRef.current) {\n const { fileId, uploadId } = uploadStateRef.current;\n await abortMultipart({ fileId, uploadId });\n uploadStateRef.current = null;\n }\n }, [abortMultipart]);\n\n const reset = useCallback(() => {\n abort();\n setState(initialState);\n }, [abort]);\n\n return {\n ...state,\n upload,\n abort,\n reset,\n };\n}\n\n/**\n * Upload a part with progress tracking.\n */\nasync function uploadPartWithProgress(\n url: string,\n blob: Blob,\n signal: AbortSignal,\n onProgress: (loaded: number) => void\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n xhr.upload.addEventListener('progress', (event) => {\n if (event.lengthComputable) {\n onProgress(event.loaded);\n }\n });\n\n xhr.addEventListener('load', () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n const etag = xhr.getResponseHeader('ETag') ?? '';\n resolve(etag.replace(/\"/g, ''));\n } else {\n reject(new UploadError(`Part upload failed with status ${xhr.status}`, 'PART_FAILED'));\n }\n });\n\n xhr.addEventListener('error', () => {\n reject(new UploadError('Part upload failed', 'NETWORK_ERROR'));\n });\n\n xhr.addEventListener('abort', () => {\n reject(new UploadError('Upload aborted', 'ABORTED'));\n });\n\n signal.addEventListener('abort', () => {\n xhr.abort();\n });\n\n xhr.open('PUT', url);\n xhr.send(blob);\n });\n}\n"],"names":["initialState","useState","useRef","useMutation","useCallback","UploadError","_a"],"mappings":";;;;AAUA,MAAM,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDpC,MAAMA,iBAA+B;AAAA,EACnC,QAAQ;AAAA,EACR,UAAU,EAAE,QAAQ,GAAG,OAAO,GAAG,YAAY,EAAA;AAAA,EAC7C,OAAO;AAAA,EACP,QAAQ;AACV;AAoCO,SAAS,YAA6B;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAIC,MAAAA,SAAyBD,cAAY;AAC/D,QAAM,qBAAqBE,MAAAA,OAA+B,IAAI;AAE9D,QAAM,GAAG,gBAAgB,IAAIC,KAAAA,YAAY,2BAA2B;AAEpE,QAAM,SAASC,MAAAA;AAAAA,IACb,OAAO,MAAY,UAAyB,OAA8B;;AAExE,yBAAmB,UAAU,IAAI,gBAAA;AACjC,YAAM,EAAE,WAAW,mBAAmB;AAEtC,UAAI;AAEF,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,UAAU,EAAE,QAAQ,GAAG,OAAO,KAAK,MAAM,YAAY,EAAA;AAAA,UACrD,OAAO;AAAA,UACP,QAAQ;AAAA,QAAA,CACT;AACD,sBAAQ,mBAAR,iCAAyB;AAGzB,cAAM,eAAe,MAAM,iBAAiB;AAAA,UAC1C,MAAM;AAAA,UACN,UAAU,KAAK;AAAA,UACf,QAAQ,QAAQ;AAAA,UAChB,KAAK,QAAQ;AAAA,UACb,SAAS,QAAQ;AAAA,UACjB,aAAa,QAAQ;AAAA,UACrB,aAAa,QAAQ;AAAA,UACrB,WAAW,QAAQ;AAAA,QAAA,CACpB;AAED,YAAI,aAAa,SAAS,GAAC,kBAAa,SAAb,mBAAmB,wBAAuB;AACnE,gBAAM,IAAIC,YAAAA,YAAY,gCAAgC,aAAa;AAAA,QACrE;AAEA,cAAM,EAAE,IAAI,IAAA,IAAQ,aAAa,KAAK;AAGtC,YAAI,OAAO,SAAS;AAClB,gBAAM,IAAIA,YAAAA,YAAY,kBAAkB,SAAS;AAAA,QACnD;AAGA,iBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,QAAQ,cAAc;AACrD,sBAAQ,mBAAR,iCAAyB;AAGzB,cAAM,mBAAmB,KAAK,MAAM,QAAQ,CAAC,aAAa;;AACxD,mBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,WAAW;AAC1C,WAAAC,MAAA,QAAQ,eAAR,gBAAAA,IAAA,cAAqB;AAAA,QACvB,CAAC;AAGD,YAAI,OAAO,SAAS;AAClB,gBAAM,IAAID,YAAAA,YAAY,kBAAkB,SAAS;AAAA,QACnD;AAGA,cAAM,SAAuB,EAAE,IAAI,IAAA;AACnC,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,UAAU,EAAE,QAAQ,KAAK,MAAM,OAAO,KAAK,MAAM,YAAY,IAAA;AAAA,UAC7D,OAAO;AAAA,UACP;AAAA,QAAA,CACD;AACD,sBAAQ,mBAAR,iCAAyB;AAEzB,eAAO;AAAA,MACT,SAAS,OAAO;AACd,cAAM,cACJ,iBAAiBA,0BACb,QACA,IAAIA,YAAAA;AAAAA,UACF,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAAA;AAGjD,iBAAS,CAAC,UAAU;AAAA,UAClB,GAAG;AAAA,UACH,QAAQ,YAAY,UAAA,IAAc,YAAY;AAAA,UAC9C,OAAO;AAAA,QAAA,EACP;AACF,sBAAQ,mBAAR;AAAA;AAAA,UACE,YAAY,UAAA,IAAc,YAAY;AAAA;AAGxC,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,gBAAgB;AAAA,EAAA;AAGnB,QAAM,QAAQD,MAAAA,YAAY,MAAM;;AAC9B,6BAAmB,YAAnB,mBAA4B;AAAA,EAC9B,GAAG,CAAA,CAAE;AAEL,QAAM,QAAQA,MAAAA,YAAY,MAAM;;AAC9B,6BAAmB,YAAnB,mBAA4B;AAC5B,aAASJ,cAAY;AAAA,EACvB,GAAG,CAAA,CAAE;AAEL,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAKA,eAAe,mBACb,KACA,MACA,QACA,YACe;AACf,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,IAAI,eAAA;AAEhB,QAAI,OAAO,iBAAiB,YAAY,CAAC,UAAU;AACjD,UAAI,MAAM,kBAAkB;AAC1B,mBAAW;AAAA,UACT,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,UACb,YAAY,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,GAAG;AAAA,QAAA,CAC1D;AAAA,MACH;AAAA,IACF,CAAC;AAED,QAAI,iBAAiB,QAAQ,MAAM;AACjC,UAAI,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AACzC,gBAAA;AAAA,MACF,OAAO;AACL,eAAO,IAAIK,YAAAA,YAAY,6BAA6B,IAAI,MAAM,EAAE,CAAC;AAAA,MACnE;AAAA,IACF,CAAC;AAED,QAAI,iBAAiB,SAAS,MAAM;AAClC,aAAO,IAAIA,YAAAA,YAAY,iBAAiB,eAAe,CAAC;AAAA,IAC1D,CAAC;AAED,QAAI,iBAAiB,SAAS,MAAM;AAClC,aAAO,IAAIA,YAAAA,YAAY,kBAAkB,SAAS,CAAC;AAAA,IACrD,CAAC;AAED,WAAO,iBAAiB,SAAS,MAAM;AACrC,UAAI,MAAA;AAAA,IACN,CAAC;AAED,QAAI,KAAK,OAAO,GAAG;AACnB,QAAI;AAAA,MACF;AAAA,MACA,KAAK,QAAQ;AAAA,IAAA;AAEf,QAAI,KAAK,IAAI;AAAA,EACf,CAAC;AACH;ACjQA,MAAM,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BhC,MAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS9B,MAAM,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASpC,MAAM,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAMjC,MAAM,oBAAoB,IAAI,OAAO;AACrC,MAAM,sBAAsB;AAuB5B,MAAM,eAAwC;AAAA,EAC5C,QAAQ;AAAA,EACR,UAAU,EAAE,QAAQ,GAAG,OAAO,GAAG,YAAY,EAAA;AAAA,EAC7C,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,YAAY;AACd;AA8CO,SAAS,qBAA+C;AAC7D,QAAM,CAAC,OAAO,QAAQ,IAAIJ,MAAAA,SAAkC,YAAY;AACxE,QAAM,qBAAqBC,MAAAA,OAA+B,IAAI;AAC9D,QAAM,iBAAiBA,MAAAA,OAGb,IAAI;AAEd,QAAM,GAAG,aAAa,IAAIC,KAAAA,YAAY,uBAAuB;AAC7D,QAAM,GAAG,UAAU,IAAIA,KAAAA,YAAY,qBAAqB;AACxD,QAAM,GAAG,iBAAiB,IAAIA,KAAAA,YAAY,2BAA2B;AACrE,QAAM,GAAG,cAAc,IAAIA,KAAAA,YAAY,wBAAwB;AAE/D,QAAM,SAASC,MAAAA;AAAAA,IACb,OACE,MACA,UAAkC,OACR;;AAC1B,YAAM,WAAW,QAAQ,YAAY;AACrC,YAAM,cAAc,QAAQ,eAAe;AAE3C,yBAAmB,UAAU,IAAI,gBAAA;AACjC,YAAM,EAAE,WAAW,mBAAmB;AAEtC,UAAI;AAEF,cAAM,aAAa,KAAK,KAAK,KAAK,OAAO,QAAQ;AAEjD,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,UAAU,EAAE,QAAQ,GAAG,OAAO,KAAK,MAAM,YAAY,EAAA;AAAA,UACrD,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,gBAAgB;AAAA,UAChB;AAAA,QAAA,CACD;AACD,sBAAQ,mBAAR,iCAAyB;AAGzB,cAAM,eAAe,MAAM,cAAc;AAAA,UACvC,UAAU,KAAK;AAAA,UACf,QAAQ,QAAQ;AAAA,UAChB,UAAU,KAAK;AAAA,UACf,aAAa,KAAK,QAAQ;AAAA,UAC1B,KAAK,QAAQ;AAAA,UACb,SAAS,QAAQ;AAAA,UACjB,aAAa,QAAQ;AAAA,UACrB,WAAW,QAAQ;AAAA,QAAA,CACpB;AAED,YAAI,aAAa,SAAS,GAAC,kBAAa,SAAb,mBAAmB,2BAA0B;AACtE,gBAAM,IAAIC,YAAAA,YAAY,yCAAyC,aAAa;AAAA,QAC9E;AAEA,cAAM,EAAE,IAAI,QAAQ,WAAW,aAC7B,aAAa,KAAK;AACpB,uBAAe,UAAU,EAAE,QAAQ,SAAA;AAEnC,YAAI,OAAO,SAAS;AAClB,gBAAM,IAAIA,YAAAA,YAAY,kBAAkB,SAAS;AAAA,QACnD;AAGA,iBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,QAAQ,cAAc;AACrD,sBAAQ,mBAAR,iCAAyB;AAGzB,cAAM,iBAA+B,CAAA;AACrC,YAAI,gBAAgB;AAGpB,cAAM,aAAwE,CAAA;AAC9E,iBAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,qBAAW,KAAK;AAAA,YACd,YAAY,IAAI;AAAA,YAChB,OAAO,IAAI;AAAA,YACX,KAAK,KAAK,KAAK,IAAI,KAAK,UAAU,KAAK,IAAI;AAAA,UAAA,CAC5C;AAAA,QACH;AAGA,cAAM,aAAa,OAAO,aAAwD;;AAChF,cAAI,OAAO,SAAS;AAClB,kBAAM,IAAIA,YAAAA,YAAY,kBAAkB,SAAS;AAAA,UACnD;AAGA,gBAAM,cAAc,MAAM,WAAW;AAAA,YACnC;AAAA,YACA;AAAA,YACA,YAAY,SAAS;AAAA,UAAA,CACtB;AAED,cAAI,YAAY,SAAS,GAACC,MAAA,YAAY,SAAZ,gBAAAA,IAAkB,2BAA0B;AACpE,kBAAM,IAAID,YAAAA;AAAAA,cACR,8BAA8B,SAAS,UAAU;AAAA,cACjD;AAAA,cACA,SAAS;AAAA,YAAA;AAAA,UAEb;AAEA,gBAAM,EAAE,IAAA,IAAQ,YAAY,KAAK;AAGjC,gBAAM,WAAW,KAAK,MAAM,SAAS,OAAO,SAAS,GAAG;AACxD,gBAAM,OAAO,MAAM;AAAA,YACjB;AAAA,YACA;AAAA,YACA;AAAA,YACA,CAAC,eAAe;;AACd,oBAAM,cAAc,gBAAgB;AACpC,oBAAM,WAA2B;AAAA,gBAC/B,QAAQ;AAAA,gBACR,OAAO,KAAK;AAAA,gBACZ,YAAY,KAAK,MAAO,cAAc,KAAK,OAAQ,GAAG;AAAA,cAAA;AAExD,uBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,WAAW;AAC1C,eAAAC,MAAA,QAAQ,eAAR,gBAAAA,IAAA,cAAqB;AAAA,YACvB;AAAA,UAAA;AAGF,2BAAiB,SAAS,MAAM,SAAS;AAEzC,mBAAS,CAAC,UAAU;AAAA,YAClB,GAAG;AAAA,YACH,gBAAgB,KAAK,iBAAiB;AAAA,UAAA,EACtC;AAEF,iBAAO;AAAA,YACL,YAAY,SAAS;AAAA,YACrB;AAAA,UAAA;AAAA,QAEJ;AAGA,iBAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,aAAa;AACvD,gBAAM,QAAQ,WAAW,MAAM,GAAG,IAAI,WAAW;AACjD,gBAAM,UAAU,MAAM,QAAQ,IAAI,MAAM,IAAI,UAAU,CAAC;AACvD,yBAAe,KAAK,GAAG,OAAO;AAAA,QAChC;AAEA,YAAI,OAAO,SAAS;AAClB,gBAAM,IAAID,YAAAA,YAAY,kBAAkB,SAAS;AAAA,QACnD;AAGA,iBAAS,CAAC,UAAU,EAAE,GAAG,MAAM,QAAQ,eAAe;AACtD,sBAAQ,mBAAR,iCAAyB;AAEzB,cAAM,mBAAmB,MAAM,kBAAkB;AAAA,UAC/C;AAAA,UACA;AAAA,UACA,OAAO,eAAe,IAAI,CAAC,OAAO;AAAA,YAChC,YAAY,EAAE;AAAA,YACd,MAAM,EAAE;AAAA,UAAA,EACR;AAAA,QAAA,CACH;AAED,YACE,iBAAiB,SACjB,GAAC,sBAAiB,SAAjB,mBAAuB,+BACxB;AACA,gBAAM,IAAIA,YAAAA,YAAY,uCAAuC,iBAAiB;AAAA,QAChF;AAEA,cAAM,SAAuB;AAAA,UAC3B,IAAI,iBAAiB,KAAK,6BAA6B;AAAA,UACvD,KAAK,iBAAiB,KAAK,6BAA6B;AAAA,QAAA;AAG1D,iBAAS;AAAA,UACP,QAAQ;AAAA,UACR,UAAU,EAAE,QAAQ,KAAK,MAAM,OAAO,KAAK,MAAM,YAAY,IAAA;AAAA,UAC7D,OAAO;AAAA,UACP;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,QAAA,CACD;AACD,sBAAQ,mBAAR,iCAAyB;AACzB,uBAAe,UAAU;AAEzB,eAAO;AAAA,MACT,SAAS,OAAO;AACd,cAAM,cACJ,iBAAiBA,0BACb,QACA,IAAIA,YAAAA;AAAAA,UACF,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAAA;AAGjD,iBAAS,CAAC,UAAU;AAAA,UAClB,GAAG;AAAA,UACH,QAAQ,YAAY,UAAA,IAAc,YAAY;AAAA,UAC9C,OAAO;AAAA,QAAA,EACP;AACF,sBAAQ,mBAAR;AAAA;AAAA,UACE,YAAY,UAAA,IAAc,YAAY;AAAA;AAGxC,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,eAAe,YAAY,iBAAiB;AAAA,EAAA;AAG/C,QAAM,QAAQD,MAAAA,YAAY,YAAY;;AACpC,6BAAmB,YAAnB,mBAA4B;AAG5B,QAAI,eAAe,SAAS;AAC1B,YAAM,EAAE,QAAQ,SAAA,IAAa,eAAe;AAC5C,YAAM,eAAe,EAAE,QAAQ,UAAU;AACzC,qBAAe,UAAU;AAAA,IAC3B;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,QAAQA,MAAAA,YAAY,MAAM;AAC9B,UAAA;AACA,aAAS,YAAY;AAAA,EACvB,GAAG,CAAC,KAAK,CAAC;AAEV,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAKA,eAAe,uBACb,KACA,MACA,QACA,YACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,MAAM,IAAI,eAAA;AAEhB,QAAI,OAAO,iBAAiB,YAAY,CAAC,UAAU;AACjD,UAAI,MAAM,kBAAkB;AAC1B,mBAAW,MAAM,MAAM;AAAA,MACzB;AAAA,IACF,CAAC;AAED,QAAI,iBAAiB,QAAQ,MAAM;AACjC,UAAI,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AACzC,cAAM,OAAO,IAAI,kBAAkB,MAAM,KAAK;AAC9C,gBAAQ,KAAK,QAAQ,MAAM,EAAE,CAAC;AAAA,MAChC,OAAO;AACL,eAAO,IAAIC,YAAAA,YAAY,kCAAkC,IAAI,MAAM,IAAI,aAAa,CAAC;AAAA,MACvF;AAAA,IACF,CAAC;AAED,QAAI,iBAAiB,SAAS,MAAM;AAClC,aAAO,IAAIA,YAAAA,YAAY,sBAAsB,eAAe,CAAC;AAAA,IAC/D,CAAC;AAED,QAAI,iBAAiB,SAAS,MAAM;AAClC,aAAO,IAAIA,YAAAA,YAAY,kBAAkB,SAAS,CAAC;AAAA,IACrD,CAAC;AAED,WAAO,iBAAiB,SAAS,MAAM;AACrC,UAAI,MAAA;AAAA,IACN,CAAC;AAED,QAAI,KAAK,OAAO,GAAG;AACnB,QAAI,KAAK,IAAI;AAAA,EACf,CAAC;AACH;;;"}
|