@clipto/upload-browser 0.1.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/dist/index.js ADDED
@@ -0,0 +1,576 @@
1
+ // src/browser-upload-http.ts
2
+ import axios from "axios";
3
+ function normalizeTransportError(cause, request) {
4
+ if (axios.isCancel(cause)) return cause;
5
+ if (!axios.isAxiosError(cause)) return cause;
6
+ const axiosError = cause;
7
+ if (axiosError.response) return axiosError;
8
+ if (request.signal?.aborted || axiosError.code === "ERR_CANCELED") {
9
+ return new axios.CanceledError();
10
+ }
11
+ if (["ECONNABORTED", "ETIMEDOUT"].includes(axiosError.code || "")) {
12
+ return axiosError;
13
+ }
14
+ const error = new axios.AxiosError(
15
+ "Browser upload request failed without an HTTP response",
16
+ "ERR_NETWORK",
17
+ request,
18
+ axiosError.request
19
+ );
20
+ error.cause = axiosError;
21
+ return error;
22
+ }
23
+ function withNormalizedTransportErrors(adapter) {
24
+ return async (request) => {
25
+ try {
26
+ return await adapter(request);
27
+ } catch (cause) {
28
+ throw normalizeTransportError(cause, request);
29
+ }
30
+ };
31
+ }
32
+ function createBrowserUploadHttpClient(readRestoredParts = false) {
33
+ if (typeof XMLHttpRequest === "undefined") return void 0;
34
+ const client = axios.create({
35
+ adapter: "xhr",
36
+ timeout: 0,
37
+ maxBodyLength: Infinity,
38
+ maxContentLength: Infinity,
39
+ proxy: false
40
+ });
41
+ const nativeAdapter = withNormalizedTransportErrors(
42
+ axios.getAdapter(client.defaults.adapter)
43
+ );
44
+ const guardedRestore = readRestoredParts && typeof navigator !== "undefined" && /Safari/.test(navigator.userAgent) && !/Chrome|Chromium|CriOS|Edg|Android/.test(navigator.userAgent);
45
+ if (guardedRestore) {
46
+ let readFailure;
47
+ client.defaults.adapter = async (request) => {
48
+ if (request.signal?.aborted) throw new axios.CanceledError();
49
+ if (readFailure) throw readFailure;
50
+ if (request.method !== "put" || !(request.data instanceof Blob) || request.data.size === 0) {
51
+ return nativeAdapter(request);
52
+ }
53
+ if (request.data.size > 5 * 1024 * 1024) {
54
+ throw new axios.AxiosError(
55
+ "Restored part exceeds read budget",
56
+ "UPLOAD_RESTORE_READ_LIMIT"
57
+ );
58
+ }
59
+ const blob = request.data;
60
+ const failedRead = (cause) => {
61
+ const error = new axios.AxiosError(
62
+ "Cached upload part could not be read. Please select the file again.",
63
+ "UPLOAD_RESTORE_READ_FAILED"
64
+ );
65
+ error.cause = cause;
66
+ readFailure = error;
67
+ return error;
68
+ };
69
+ const buffer = await new Promise((resolve, reject) => {
70
+ const reader = new FileReader();
71
+ let done = false;
72
+ const finish = (error) => {
73
+ if (done) return;
74
+ done = true;
75
+ clearTimeout(timer);
76
+ request.signal?.removeEventListener?.("abort", onAbort);
77
+ if (error) {
78
+ reject(error);
79
+ if (reader.readyState === 1) reader.abort();
80
+ return;
81
+ }
82
+ const result = reader.result;
83
+ if (!(result instanceof ArrayBuffer) || result.byteLength !== blob.size) {
84
+ reject(
85
+ failedRead(
86
+ new DOMException("Incomplete part read", "NotReadableError")
87
+ )
88
+ );
89
+ return;
90
+ }
91
+ resolve(result);
92
+ };
93
+ const onAbort = () => finish(new axios.CanceledError());
94
+ const timer = setTimeout(
95
+ () => finish(
96
+ new axios.AxiosError(
97
+ "Cached part read timed out",
98
+ "UPLOAD_RESTORE_READ_TIMEOUT"
99
+ )
100
+ ),
101
+ 6e4
102
+ );
103
+ reader.onload = () => finish();
104
+ reader.onerror = () => finish(failedRead(reader.error));
105
+ reader.onabort = onAbort;
106
+ request.signal?.addEventListener?.("abort", onAbort);
107
+ if (request.signal?.aborted) return onAbort();
108
+ try {
109
+ reader.readAsArrayBuffer(blob);
110
+ } catch (error) {
111
+ finish(failedRead(error));
112
+ }
113
+ });
114
+ if (request.signal?.aborted) throw new axios.CanceledError();
115
+ if (readFailure) throw readFailure;
116
+ return nativeAdapter({ ...request, data: buffer });
117
+ };
118
+ } else {
119
+ client.defaults.adapter = nativeAdapter;
120
+ }
121
+ client.interceptors.request.use((config) => {
122
+ config.headers.delete("content-length");
123
+ return config;
124
+ });
125
+ return client;
126
+ }
127
+
128
+ // src/local-blob-upload.ts
129
+ import axios2 from "axios";
130
+ import {
131
+ CliptoUploader,
132
+ UploadCanceledError,
133
+ UploadError,
134
+ isUploadError
135
+ } from "@clipto/uploader";
136
+ var PUT_REQUEST_TIMEOUT_MS = 15 * 6e4;
137
+ var PART_SIGNATURE_TIMEOUT_MS = 3e4;
138
+ var SINGLE_SIGNATURE_TIMEOUT_MS = 3e4;
139
+ var PART_RETRY_DELAY_MS = 12e3;
140
+ var normalizeRetryMessage = (message) => message.replace(
141
+ /^(part .* attempt \d+ failed, retry in )\d+ms:/,
142
+ `$1${PART_RETRY_DELAY_MS}ms:`
143
+ );
144
+ async function withUploadWatchdog(signal, timeoutMs, code, execute) {
145
+ if (signal.aborted) throw new UploadCanceledError();
146
+ const controller = new AbortController();
147
+ let timer;
148
+ let onAbort = () => {
149
+ };
150
+ let reset = () => {
151
+ };
152
+ let finished = false;
153
+ const disarm = () => clearTimeout(timer);
154
+ const stopped = new Promise((_, reject) => {
155
+ onAbort = () => {
156
+ disarm();
157
+ reject(new UploadCanceledError());
158
+ controller.abort();
159
+ };
160
+ reset = () => {
161
+ if (finished || controller.signal.aborted) return;
162
+ disarm();
163
+ timer = setTimeout(() => {
164
+ reject(new axios2.AxiosError("Upload made no progress", code));
165
+ controller.abort();
166
+ }, timeoutMs);
167
+ };
168
+ signal.addEventListener("abort", onAbort, { once: true });
169
+ reset();
170
+ });
171
+ try {
172
+ return await Promise.race([
173
+ execute(controller.signal, reset, disarm),
174
+ stopped
175
+ ]);
176
+ } finally {
177
+ finished = true;
178
+ disarm();
179
+ signal.removeEventListener("abort", onAbort);
180
+ }
181
+ }
182
+ var isRecoverableUploadError = (error) => {
183
+ const cause = isUploadError(error) ? error.cause : error;
184
+ if (!axios2.isAxiosError(cause) || cause.code === "ERR_CANCELED") return false;
185
+ const status = cause.response?.status;
186
+ if (status) return status === 429 || status >= 500 && status <= 599;
187
+ return [
188
+ "ERR_NETWORK",
189
+ "ECONNABORTED",
190
+ "ETIMEDOUT",
191
+ "UPLOAD_PUT_TIMEOUT"
192
+ ].includes(cause.code || "");
193
+ };
194
+ var isTransientSingleNetworkError = (error) => isUploadError(error) && error.phase === "putSingle" && axios2.isAxiosError(error.cause) && error.cause.code === "ERR_NETWORK" && !error.cause.response;
195
+ var isSafariPartRef = (value) => Boolean(
196
+ value && typeof value === "object" && "kind" in value && value.kind === "safari-array-buffer-part"
197
+ );
198
+ function createLocalBlobUploader(config, options = {}) {
199
+ const uploader = new CliptoUploader({ ...config, maxPartRetries: 3 });
200
+ const internals = uploader;
201
+ if (typeof internals.blobToArrayBuffer !== "function") return uploader;
202
+ Object.defineProperty(uploader, "blobToArrayBuffer", {
203
+ value: async (blob) => blob,
204
+ configurable: false,
205
+ writable: false
206
+ });
207
+ const uploadSingle = typeof internals.uploadSingle === "function" ? internals.uploadSingle.bind(uploader) : void 0;
208
+ const putData = typeof internals.putData === "function" ? internals.putData.bind(uploader) : void 0;
209
+ if (!uploadSingle || !putData) return uploader;
210
+ const beforeRetryPut = /* @__PURE__ */ new WeakMap();
211
+ const beforePartPut = /* @__PURE__ */ new WeakMap();
212
+ if (typeof internals.uploadPartOnce === "function" && typeof internals.sleep === "function" && typeof internals.emitWarning === "function" && typeof internals.isRetryable === "function") {
213
+ const uploadPartOnce = internals.uploadPartOnce.bind(uploader);
214
+ const sleep = internals.sleep.bind(uploader);
215
+ const emitWarning = internals.emitWarning.bind(uploader);
216
+ const logger = config.logger || console;
217
+ Object.defineProperty(uploader, "logger", {
218
+ value: {
219
+ info: logger.info.bind(logger),
220
+ error: logger.error.bind(logger),
221
+ warn: (message, ...args) => logger.warn(
222
+ message === void 0 ? message : normalizeRetryMessage(message),
223
+ ...args
224
+ )
225
+ }
226
+ });
227
+ Object.defineProperty(uploader, "emitWarning", {
228
+ value: (onWarning, warning) => emitWarning(
229
+ onWarning,
230
+ warning.code === "PART_RETRY" ? { ...warning, message: normalizeRetryMessage(warning.message) } : warning
231
+ )
232
+ });
233
+ Object.defineProperty(uploader, "isRetryable", {
234
+ value: isRecoverableUploadError
235
+ });
236
+ Object.defineProperty(uploader, "sleep", {
237
+ value: (ms, signal) => sleep(Math.max(ms, PART_RETRY_DELAY_MS), signal)
238
+ });
239
+ Object.defineProperty(uploader, "uploadPartOnce", {
240
+ value: (...args) => withUploadWatchdog(
241
+ args[6].signal,
242
+ PART_SIGNATURE_TIMEOUT_MS,
243
+ "UPLOAD_PART_SIGNATURE_TIMEOUT",
244
+ async (signal, _reset, disarm) => {
245
+ beforePartPut.set(signal, disarm);
246
+ try {
247
+ const nextArgs = [...args];
248
+ if (isSafariPartRef(nextArgs[0])) {
249
+ try {
250
+ if (!options.safariCache)
251
+ throw new Error("Missing Safari cache");
252
+ nextArgs[0] = await options.safariCache.getPart(
253
+ nextArgs[0].descriptor,
254
+ nextArgs[3],
255
+ nextArgs[4]
256
+ );
257
+ } catch (error) {
258
+ throw new UploadError({
259
+ code: "INVALID_ARGUMENT",
260
+ message: "source_unreadable",
261
+ phase: "prepare",
262
+ cause: error,
263
+ objectKey: nextArgs[1],
264
+ uploadId: nextArgs[2],
265
+ partNumber: nextArgs[3],
266
+ resumable: false,
267
+ retryable: false
268
+ });
269
+ }
270
+ }
271
+ nextArgs[6] = { signal };
272
+ return await uploadPartOnce(...nextArgs);
273
+ } finally {
274
+ beforePartPut.delete(signal);
275
+ }
276
+ }
277
+ ).catch((error) => {
278
+ if (axios2.isCancel(error) || axios2.isAxiosError(error) && error.code === "ERR_CANCELED") {
279
+ throw new UploadCanceledError(args[1]);
280
+ }
281
+ if (axios2.isAxiosError(error) && error.code === "UPLOAD_PART_SIGNATURE_TIMEOUT") {
282
+ throw new UploadError({
283
+ code: "SIGNATURE_API_ERROR",
284
+ message: "Part signature timed out",
285
+ phase: "signPart",
286
+ cause: error,
287
+ objectKey: args[1],
288
+ uploadId: args[2],
289
+ partNumber: args[3],
290
+ resumable: true,
291
+ retryable: false
292
+ });
293
+ }
294
+ throw error;
295
+ })
296
+ });
297
+ }
298
+ Object.defineProperty(uploader, "putData", {
299
+ value: (...args) => {
300
+ if (args[5].aborted) throw new UploadCanceledError();
301
+ beforeRetryPut.get(args[5])?.();
302
+ beforePartPut.get(args[5])?.();
303
+ return withUploadWatchdog(
304
+ args[5],
305
+ PUT_REQUEST_TIMEOUT_MS,
306
+ "UPLOAD_PUT_TIMEOUT",
307
+ (signal) => {
308
+ const nextArgs = [...args];
309
+ nextArgs[5] = signal;
310
+ nextArgs[6] = (loaded) => {
311
+ if (signal.aborted) return;
312
+ args[6]?.(loaded);
313
+ };
314
+ return putData(...nextArgs);
315
+ }
316
+ );
317
+ }
318
+ });
319
+ Object.defineProperty(uploader, "uploadSingle", {
320
+ value: async (...args) => {
321
+ const signal = args[6].signal;
322
+ let deadline = 0;
323
+ let lastError;
324
+ for (let attempt = 0; attempt <= 2; attempt += 1) {
325
+ if (signal.aborted) throw new UploadCanceledError(args[1]);
326
+ if (attempt === 0) {
327
+ try {
328
+ return await withUploadWatchdog(
329
+ signal,
330
+ SINGLE_SIGNATURE_TIMEOUT_MS,
331
+ "UPLOAD_SINGLE_SIGNATURE_TIMEOUT",
332
+ async (attemptSignal, _reset, disarm) => {
333
+ beforeRetryPut.set(attemptSignal, disarm);
334
+ try {
335
+ const nextArgs = [...args];
336
+ nextArgs[6] = { signal: attemptSignal };
337
+ return await uploadSingle(...nextArgs);
338
+ } finally {
339
+ beforeRetryPut.delete(attemptSignal);
340
+ }
341
+ }
342
+ );
343
+ } catch (error) {
344
+ if (axios2.isAxiosError(error) && error.code === "UPLOAD_SINGLE_SIGNATURE_TIMEOUT") {
345
+ throw new UploadError({
346
+ code: "SIGNATURE_API_ERROR",
347
+ message: "Single upload signature timed out",
348
+ phase: "signSingle",
349
+ cause: error,
350
+ objectKey: args[1],
351
+ retryable: false
352
+ });
353
+ }
354
+ if (!isTransientSingleNetworkError(error)) throw error;
355
+ lastError = error;
356
+ deadline = Date.now() + 15e3;
357
+ }
358
+ continue;
359
+ }
360
+ if (Date.now() >= deadline) throw lastError;
361
+ const controller = new AbortController();
362
+ let timer;
363
+ let delayTimer;
364
+ let onAbort = () => {
365
+ };
366
+ try {
367
+ const stopped = new Promise((_, reject) => {
368
+ onAbort = () => {
369
+ controller.abort();
370
+ reject(new UploadCanceledError(args[1]));
371
+ };
372
+ signal.addEventListener("abort", onAbort, { once: true });
373
+ timer = setTimeout(
374
+ () => {
375
+ controller.abort();
376
+ reject(lastError);
377
+ },
378
+ Math.max(0, deadline - Date.now())
379
+ );
380
+ });
381
+ await Promise.race([
382
+ new Promise((resolve) => {
383
+ delayTimer = setTimeout(resolve, 500 * attempt);
384
+ }),
385
+ stopped
386
+ ]);
387
+ if (signal.aborted) throw new UploadCanceledError(args[1]);
388
+ if (Date.now() >= deadline) throw lastError;
389
+ args[5].reset();
390
+ args[5].report(0);
391
+ const retryArgs = [...args];
392
+ retryArgs[6] = { signal: controller.signal };
393
+ beforeRetryPut.set(controller.signal, () => {
394
+ if (signal.aborted) throw new UploadCanceledError(args[1]);
395
+ if (controller.signal.aborted || Date.now() >= deadline)
396
+ throw lastError;
397
+ clearTimeout(timer);
398
+ });
399
+ return await Promise.race([uploadSingle(...retryArgs), stopped]);
400
+ } catch (error) {
401
+ if (signal.aborted) throw new UploadCanceledError(args[1]);
402
+ if (!isTransientSingleNetworkError(error)) throw error;
403
+ lastError = error;
404
+ } finally {
405
+ beforeRetryPut.delete(controller.signal);
406
+ clearTimeout(timer);
407
+ clearTimeout(delayTimer);
408
+ signal.removeEventListener("abort", onAbort);
409
+ }
410
+ }
411
+ throw lastError;
412
+ }
413
+ });
414
+ return uploader;
415
+ }
416
+
417
+ // src/safari-upload-part-cache.ts
418
+ var DEFAULT_PART_SIZE = 5 * 1024 * 1024;
419
+ var CACHE_VERSION = 1;
420
+ var CACHE_PREFIX = "safari_upload_part_cache";
421
+ var SafariUploadSourceUnreadableError = class extends Error {
422
+ constructor() {
423
+ super("source_unreadable");
424
+ this.code = "SOURCE_UNREADABLE";
425
+ this.retryable = false;
426
+ this.name = "SafariUploadSourceUnreadableError";
427
+ }
428
+ };
429
+ var manifestKey = (cacheId) => `${CACHE_PREFIX}:${cacheId}:manifest`;
430
+ var partKey = (cacheId, partNumber) => `${CACHE_PREFIX}:${cacheId}:part:${partNumber}`;
431
+ function isSafariMultipartCacheEligible(size, userAgent = typeof navigator === "undefined" ? "" : navigator.userAgent) {
432
+ return size > DEFAULT_PART_SIZE && /Safari/.test(userAgent) && !/Chrome|Chromium|CriOS|Edg|Android/.test(userAgent);
433
+ }
434
+ function isSafariUploadCacheDescriptor(value) {
435
+ if (!value || typeof value !== "object") return false;
436
+ const descriptor = value;
437
+ return descriptor.kind === "safari-array-buffer-parts" && descriptor.version === CACHE_VERSION && typeof descriptor.cacheId === "string" && descriptor.cacheId.length > 0 && Number.isSafeInteger(descriptor.totalSize) && (descriptor.totalSize || 0) > 0 && Number.isSafeInteger(descriptor.partSize) && (descriptor.partSize || 0) > 0 && descriptor.partCount === Math.ceil((descriptor.totalSize || 0) / (descriptor.partSize || 1)) && Boolean(descriptor.file) && typeof descriptor.file?.name === "string" && typeof descriptor.file?.type === "string" && typeof descriptor.file?.lastModified === "number";
438
+ }
439
+ function createSafariUploadSource(descriptor) {
440
+ return {
441
+ kind: "safari-array-buffer-source",
442
+ descriptor,
443
+ size: descriptor.totalSize,
444
+ type: descriptor.file.type,
445
+ name: descriptor.file.name,
446
+ lastModified: descriptor.file.lastModified,
447
+ slice(start = 0, end = descriptor.totalSize) {
448
+ const partNumber = Math.floor(start / descriptor.partSize) + 1;
449
+ return createPartRef(descriptor, partNumber, end - start);
450
+ }
451
+ };
452
+ }
453
+ function isSafariUploadSource(value) {
454
+ return Boolean(
455
+ value && typeof value === "object" && value.kind === "safari-array-buffer-source"
456
+ );
457
+ }
458
+ function createSafariUploadCacheManager({
459
+ storage,
460
+ logger = console
461
+ }) {
462
+ const cleanup = async (descriptor) => {
463
+ await Promise.allSettled([
464
+ ...Array.from(
465
+ { length: descriptor.partCount },
466
+ (_, index) => storage.remove(partKey(descriptor.cacheId, index + 1))
467
+ ),
468
+ storage.remove(manifestKey(descriptor.cacheId))
469
+ ]);
470
+ };
471
+ const create = async (file, cacheId, options) => {
472
+ const partSize = options?.partSize ?? DEFAULT_PART_SIZE;
473
+ const descriptor = {
474
+ kind: "safari-array-buffer-parts",
475
+ version: CACHE_VERSION,
476
+ cacheId,
477
+ totalSize: file.size,
478
+ partSize,
479
+ partCount: Math.ceil(file.size / partSize),
480
+ file: options?.metadata ?? {
481
+ name: "name" in file && typeof file.name === "string" ? file.name : "upload",
482
+ type: file.type,
483
+ lastModified: "lastModified" in file && typeof file.lastModified === "number" ? file.lastModified : Date.now()
484
+ }
485
+ };
486
+ try {
487
+ for (let partNumber = 1; partNumber <= descriptor.partCount; partNumber++) {
488
+ const expected = expectedPartSize(descriptor, partNumber);
489
+ const start = (partNumber - 1) * partSize;
490
+ const bytes = await readBlobPart(file.slice(start, start + expected));
491
+ if (bytes.byteLength !== expected)
492
+ throw new Error("Invalid source part");
493
+ const key = partKey(cacheId, partNumber);
494
+ await storage.set(key, bytes);
495
+ const stored = await storage.get(key);
496
+ if (!(stored instanceof ArrayBuffer) || stored.byteLength !== expected) {
497
+ throw new Error("Invalid persisted part");
498
+ }
499
+ }
500
+ await storage.set(manifestKey(cacheId), descriptor);
501
+ const manifest = await storage.get(manifestKey(cacheId));
502
+ if (!isSafariUploadCacheDescriptor(manifest)) {
503
+ throw new Error("Invalid persisted manifest");
504
+ }
505
+ return descriptor;
506
+ } catch (error) {
507
+ await cleanup(descriptor);
508
+ logger.warn(
509
+ "Safari upload byte cache unavailable, falling back to Blob",
510
+ error
511
+ );
512
+ return null;
513
+ }
514
+ };
515
+ const validate = async (descriptor) => {
516
+ const stored = await storage.get(
517
+ manifestKey(descriptor.cacheId)
518
+ );
519
+ return isSafariUploadCacheDescriptor(stored) && stored.totalSize === descriptor.totalSize && stored.partSize === descriptor.partSize && stored.partCount === descriptor.partCount;
520
+ };
521
+ const getPart = async (descriptor, partNumber, expectedLength) => {
522
+ if (partNumber < 1 || partNumber > descriptor.partCount || expectedLength !== expectedPartSize(descriptor, partNumber)) {
523
+ throw new SafariUploadSourceUnreadableError();
524
+ }
525
+ try {
526
+ const bytes = await storage.get(
527
+ partKey(descriptor.cacheId, partNumber)
528
+ );
529
+ if (!(bytes instanceof ArrayBuffer) || bytes.byteLength !== expectedLength) {
530
+ throw new SafariUploadSourceUnreadableError();
531
+ }
532
+ return bytes;
533
+ } catch (error) {
534
+ if (error instanceof SafariUploadSourceUnreadableError) throw error;
535
+ throw new SafariUploadSourceUnreadableError();
536
+ }
537
+ };
538
+ return { create, validate, cleanup, getPart };
539
+ }
540
+ function readBlobPart(blob) {
541
+ if (typeof blob.arrayBuffer === "function") return blob.arrayBuffer();
542
+ return new Promise((resolve, reject) => {
543
+ const reader = new FileReader();
544
+ reader.onload = () => resolve(reader.result);
545
+ reader.onerror = () => reject(reader.error);
546
+ reader.readAsArrayBuffer(blob);
547
+ });
548
+ }
549
+ function expectedPartSize(descriptor, partNumber) {
550
+ return Math.min(
551
+ descriptor.partSize,
552
+ descriptor.totalSize - (partNumber - 1) * descriptor.partSize
553
+ );
554
+ }
555
+ function createPartRef(descriptor, partNumber, size) {
556
+ return {
557
+ kind: "safari-array-buffer-part",
558
+ descriptor,
559
+ partNumber,
560
+ size,
561
+ type: descriptor.file.type,
562
+ slice(start = 0, end = size) {
563
+ return createPartRef(descriptor, partNumber, end - start);
564
+ }
565
+ };
566
+ }
567
+ export {
568
+ createBrowserUploadHttpClient,
569
+ createLocalBlobUploader,
570
+ createSafariUploadCacheManager,
571
+ createSafariUploadSource,
572
+ isSafariMultipartCacheEligible,
573
+ isSafariUploadCacheDescriptor,
574
+ isSafariUploadSource
575
+ };
576
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser-upload-http.ts","../src/local-blob-upload.ts","../src/safari-upload-part-cache.ts"],"sourcesContent":["import axios, {\n type AxiosAdapter,\n type AxiosError,\n type InternalAxiosRequestConfig,\n} from \"axios\";\nimport type { IUploaderConfig } from \"@clipto/uploader\";\n\n/** 将浏览器 XHR 的模糊无响应异常归一成稳定网络错误,供上传层统一判断是否重试。 */\nfunction normalizeTransportError(\n cause: unknown,\n request: InternalAxiosRequestConfig,\n): unknown {\n if (axios.isCancel(cause)) return cause;\n if (!axios.isAxiosError(cause)) return cause;\n const axiosError = cause as AxiosError;\n if (axiosError.response) return axiosError;\n if (request.signal?.aborted || axiosError.code === \"ERR_CANCELED\") {\n return new axios.CanceledError();\n }\n if ([\"ECONNABORTED\", \"ETIMEDOUT\"].includes(axiosError.code || \"\")) {\n return axiosError;\n }\n // Safari 对部分 XHR 失败只提供 status=0 或无 code,稳定分类才能触发 SDK 分片重试。\n const error = new axios.AxiosError(\n \"Browser upload request failed without an HTTP response\",\n \"ERR_NETWORK\",\n request,\n axiosError.request,\n );\n error.cause = axiosError;\n return error;\n}\n\n/** 包装原生 XHR adapter,在错误离浏览器最近的位置保留并规范化传输语义。 */\nfunction withNormalizedTransportErrors(adapter: AxiosAdapter): AxiosAdapter {\n return async (request) => {\n try {\n return await adapter(request);\n } catch (cause) {\n throw normalizeTransportError(cause, request);\n }\n };\n}\n\n/** 创建浏览器上传 HTTP 客户端;恢复模式额外验证 Safari Blob 可读性并发送 ArrayBuffer。 */\nexport function createBrowserUploadHttpClient(\n readRestoredParts = false,\n): IUploaderConfig[\"httpClient\"] {\n if (typeof XMLHttpRequest === \"undefined\") return undefined;\n const client = axios.create({\n adapter: \"xhr\",\n timeout: 0,\n maxBodyLength: Infinity,\n maxContentLength: Infinity,\n proxy: false,\n });\n const nativeAdapter = withNormalizedTransportErrors(\n axios.getAdapter(client.defaults.adapter),\n );\n const guardedRestore =\n readRestoredParts &&\n typeof navigator !== \"undefined\" &&\n /Safari/.test(navigator.userAgent) &&\n !/Chrome|Chromium|CriOS|Edg|Android/.test(navigator.userAgent);\n\n if (guardedRestore) {\n let readFailure: Error | undefined;\n client.defaults.adapter = async (request) => {\n if (request.signal?.aborted) throw new axios.CanceledError();\n if (readFailure) throw readFailure;\n if (\n request.method !== \"put\" ||\n !(request.data instanceof Blob) ||\n request.data.size === 0\n ) {\n return nativeAdapter(request);\n }\n if (request.data.size > 5 * 1024 * 1024) {\n throw new axios.AxiosError(\n \"Restored part exceeds read budget\",\n \"UPLOAD_RESTORE_READ_LIMIT\",\n );\n }\n const blob = request.data;\n const failedRead = (cause: unknown) => {\n const error = new axios.AxiosError(\n \"Cached upload part could not be read. Please select the file again.\",\n \"UPLOAD_RESTORE_READ_FAILED\",\n );\n error.cause = cause as Error;\n readFailure = error;\n return error;\n };\n const buffer = await new Promise<ArrayBuffer>((resolve, reject) => {\n const reader = new FileReader();\n let done = false;\n const finish = (error?: Error) => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n request.signal?.removeEventListener?.(\"abort\", onAbort);\n if (error) {\n reject(error);\n if (reader.readyState === 1) reader.abort();\n return;\n }\n const result = reader.result;\n if (\n !(result instanceof ArrayBuffer) ||\n result.byteLength !== blob.size\n ) {\n reject(\n failedRead(\n new DOMException(\"Incomplete part read\", \"NotReadableError\"),\n ),\n );\n return;\n }\n resolve(result);\n };\n const onAbort = () => finish(new axios.CanceledError());\n const timer = setTimeout(\n () =>\n finish(\n new axios.AxiosError(\n \"Cached part read timed out\",\n \"UPLOAD_RESTORE_READ_TIMEOUT\",\n ),\n ),\n 60_000,\n );\n reader.onload = () => finish();\n reader.onerror = () => finish(failedRead(reader.error));\n reader.onabort = onAbort;\n request.signal?.addEventListener?.(\"abort\", onAbort);\n if (request.signal?.aborted) return onAbort();\n try {\n reader.readAsArrayBuffer(blob);\n } catch (error) {\n finish(failedRead(error));\n }\n });\n if (request.signal?.aborted) throw new axios.CanceledError();\n if (readFailure) throw readFailure;\n return nativeAdapter({ ...request, data: buffer });\n };\n } else {\n client.defaults.adapter = nativeAdapter;\n }\n\n client.interceptors.request.use((config) => {\n config.headers.delete(\"content-length\");\n return config;\n });\n return client as unknown as IUploaderConfig[\"httpClient\"];\n}\n","import axios from \"axios\";\nimport {\n CliptoUploader,\n UploadCanceledError,\n UploadError,\n isUploadError,\n type IUploaderConfig,\n type IUploadWarning,\n} from \"@clipto/uploader\";\nimport type {\n SafariUploadCacheDescriptor,\n SafariUploadCacheManager,\n} from \"./safari-upload-part-cache\";\n\ntype SingleUploadArgs = [\n Blob,\n string,\n number,\n string,\n string | undefined,\n { reset(): void; report(loaded: number): void },\n { signal: AbortSignal },\n];\n\ntype SafariPartRef = {\n kind: \"safari-array-buffer-part\";\n descriptor: SafariUploadCacheDescriptor;\n partNumber: number;\n size: number;\n};\n\ntype PartUploadArgs = [\n Blob | ArrayBuffer | SafariPartRef,\n string,\n string,\n number,\n number,\n (partNumber: number, loaded: number) => void,\n { signal: AbortSignal },\n];\n\nconst PUT_REQUEST_TIMEOUT_MS = 15 * 60_000;\nconst PART_SIGNATURE_TIMEOUT_MS = 30_000;\nconst SINGLE_SIGNATURE_TIMEOUT_MS = 30_000;\nconst PART_RETRY_DELAY_MS = 12_000;\n\nexport interface LocalBlobUploaderOptions {\n safariCache?: Pick<SafariUploadCacheManager, \"getPart\">;\n}\n\nconst normalizeRetryMessage = (message: string): string =>\n message.replace(\n /^(part .* attempt \\d+ failed, retry in )\\d+ms:/,\n `$1${PART_RETRY_DELAY_MS}ms:`,\n );\n\n/** 为签名和 PUT 建立可取消的硬超时,并允许进入 PUT 时解除签名计时。 */\nasync function withUploadWatchdog<T>(\n signal: AbortSignal,\n timeoutMs: number,\n code: string,\n execute: (\n signal: AbortSignal,\n reset: () => void,\n disarm: () => void,\n ) => Promise<T>,\n): Promise<T> {\n if (signal.aborted) throw new UploadCanceledError();\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort = () => {};\n let reset = () => {};\n let finished = false;\n const disarm = () => clearTimeout(timer);\n const stopped = new Promise<never>((_, reject) => {\n onAbort = () => {\n disarm();\n reject(new UploadCanceledError());\n controller.abort();\n };\n reset = () => {\n if (finished || controller.signal.aborted) return;\n disarm();\n timer = setTimeout(() => {\n reject(new axios.AxiosError(\"Upload made no progress\", code));\n controller.abort();\n }, timeoutMs);\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n reset();\n });\n try {\n return await Promise.race([\n execute(controller.signal, reset, disarm),\n stopped,\n ]);\n } finally {\n finished = true;\n disarm();\n signal.removeEventListener(\"abort\", onAbort);\n }\n}\n\n/** 仅将网络、超时、限流和服务端错误交给 multipart 重试。 */\nconst isRecoverableUploadError = (error: unknown): boolean => {\n const cause = isUploadError(error) ? error.cause : error;\n if (!axios.isAxiosError(cause) || cause.code === \"ERR_CANCELED\") return false;\n const status = cause.response?.status;\n if (status) return status === 429 || (status >= 500 && status <= 599);\n return [\n \"ERR_NETWORK\",\n \"ECONNABORTED\",\n \"ETIMEDOUT\",\n \"UPLOAD_PUT_TIMEOUT\",\n ].includes(cause.code || \"\");\n};\n\nconst isTransientSingleNetworkError = (error: unknown): boolean =>\n isUploadError(error) &&\n error.phase === \"putSingle\" &&\n axios.isAxiosError(error.cause) &&\n error.cause.code === \"ERR_NETWORK\" &&\n !error.cause.response;\n\nconst isSafariPartRef = (value: unknown): value is SafariPartRef =>\n Boolean(\n value &&\n typeof value === \"object\" &&\n \"kind\" in value &&\n value.kind === \"safari-array-buffer-part\",\n );\n\n/** 创建浏览器 Blob 上传实例,并以可选 seam 接入 Safari 缓存逐片读取。 */\nexport function createLocalBlobUploader(\n config: IUploaderConfig,\n options: LocalBlobUploaderOptions = {},\n): CliptoUploader {\n const uploader = new CliptoUploader({ ...config, maxPartRetries: 3 });\n const internals = uploader as unknown as {\n blobToArrayBuffer?: (blob: Blob) => Promise<ArrayBuffer>;\n uploadPartOnce?: (...args: PartUploadArgs) => Promise<unknown>;\n sleep?: (ms: number, signal: AbortSignal) => Promise<void>;\n emitWarning?: (\n onWarning: ((warning: IUploadWarning) => void) | undefined,\n warning: IUploadWarning,\n ) => void;\n isRetryable?: (error: unknown) => boolean;\n uploadSingle?: (...args: SingleUploadArgs) => Promise<unknown>;\n putData?: (\n ...args: [\n string,\n Blob,\n number,\n string,\n Record<string, string> | undefined,\n AbortSignal,\n ((loaded: number) => void)?,\n ]\n ) => Promise<unknown>;\n };\n\n // 私有 seam 按能力分层启用,缺少 multipart 接点不能影响 single PUT 的可靠性。\n if (typeof internals.blobToArrayBuffer !== \"function\") return uploader;\n Object.defineProperty(uploader, \"blobToArrayBuffer\", {\n value: async (blob: Blob) => blob,\n configurable: false,\n writable: false,\n });\n\n const uploadSingle =\n typeof internals.uploadSingle === \"function\"\n ? internals.uploadSingle.bind(uploader)\n : undefined;\n const putData =\n typeof internals.putData === \"function\"\n ? internals.putData.bind(uploader)\n : undefined;\n if (!uploadSingle || !putData) return uploader;\n\n const beforeRetryPut = new WeakMap<AbortSignal, () => void>();\n const beforePartPut = new WeakMap<AbortSignal, () => void>();\n if (\n typeof internals.uploadPartOnce === \"function\" &&\n typeof internals.sleep === \"function\" &&\n typeof internals.emitWarning === \"function\" &&\n typeof internals.isRetryable === \"function\"\n ) {\n const uploadPartOnce = internals.uploadPartOnce.bind(uploader);\n const sleep = internals.sleep.bind(uploader);\n const emitWarning = internals.emitWarning.bind(uploader);\n const logger = config.logger || console;\n Object.defineProperty(uploader, \"logger\", {\n value: {\n info: logger.info.bind(logger),\n error: logger.error.bind(logger),\n warn: (message?: string, ...args: unknown[]) =>\n logger.warn(\n message === undefined ? message : normalizeRetryMessage(message),\n ...args,\n ),\n },\n });\n Object.defineProperty(uploader, \"emitWarning\", {\n value: (\n onWarning: ((warning: IUploadWarning) => void) | undefined,\n warning: IUploadWarning,\n ) =>\n emitWarning(\n onWarning,\n warning.code === \"PART_RETRY\"\n ? { ...warning, message: normalizeRetryMessage(warning.message) }\n : warning,\n ),\n });\n Object.defineProperty(uploader, \"isRetryable\", {\n value: isRecoverableUploadError,\n });\n Object.defineProperty(uploader, \"sleep\", {\n value: (ms: number, signal: AbortSignal) =>\n sleep(Math.max(ms, PART_RETRY_DELAY_MS), signal),\n });\n Object.defineProperty(uploader, \"uploadPartOnce\", {\n value: (...args: PartUploadArgs) =>\n withUploadWatchdog(\n args[6].signal,\n PART_SIGNATURE_TIMEOUT_MS,\n \"UPLOAD_PART_SIGNATURE_TIMEOUT\",\n async (signal, _reset, disarm) => {\n beforePartPut.set(signal, disarm);\n try {\n const nextArgs: PartUploadArgs = [...args];\n if (isSafariPartRef(nextArgs[0])) {\n try {\n if (!options.safariCache)\n throw new Error(\"Missing Safari cache\");\n nextArgs[0] = await options.safariCache.getPart(\n nextArgs[0].descriptor,\n nextArgs[3],\n nextArgs[4],\n );\n } catch (error) {\n throw new UploadError({\n code: \"INVALID_ARGUMENT\",\n message: \"source_unreadable\",\n phase: \"prepare\",\n cause: error,\n objectKey: nextArgs[1],\n uploadId: nextArgs[2],\n partNumber: nextArgs[3],\n resumable: false,\n retryable: false,\n });\n }\n }\n nextArgs[6] = { signal };\n return await uploadPartOnce(...nextArgs);\n } finally {\n beforePartPut.delete(signal);\n }\n },\n ).catch((error: unknown) => {\n // SDK 会把未包装的 Axios 取消误归类为传输失败,先恢复公共取消语义。\n if (\n axios.isCancel(error) ||\n (axios.isAxiosError(error) && error.code === \"ERR_CANCELED\")\n ) {\n throw new UploadCanceledError(args[1]);\n }\n if (\n axios.isAxiosError(error) &&\n error.code === \"UPLOAD_PART_SIGNATURE_TIMEOUT\"\n ) {\n throw new UploadError({\n code: \"SIGNATURE_API_ERROR\",\n message: \"Part signature timed out\",\n phase: \"signPart\",\n cause: error,\n objectKey: args[1],\n uploadId: args[2],\n partNumber: args[3],\n resumable: true,\n retryable: false,\n });\n }\n throw error;\n }),\n });\n }\n\n Object.defineProperty(uploader, \"putData\", {\n value: (...args: Parameters<typeof putData>) => {\n if (args[5].aborted) throw new UploadCanceledError();\n beforeRetryPut.get(args[5])?.();\n beforePartPut.get(args[5])?.();\n return withUploadWatchdog(\n args[5],\n PUT_REQUEST_TIMEOUT_MS,\n \"UPLOAD_PUT_TIMEOUT\",\n (signal) => {\n const nextArgs: Parameters<typeof putData> = [...args];\n nextArgs[5] = signal;\n nextArgs[6] = (loaded) => {\n if (signal.aborted) return;\n args[6]?.(loaded);\n };\n return putData(...nextArgs);\n },\n );\n },\n });\n\n Object.defineProperty(uploader, \"uploadSingle\", {\n value: async (...args: SingleUploadArgs) => {\n const signal = args[6].signal;\n let deadline = 0;\n let lastError: unknown;\n for (let attempt = 0; attempt <= 2; attempt += 1) {\n if (signal.aborted) throw new UploadCanceledError(args[1]);\n if (attempt === 0) {\n try {\n return await withUploadWatchdog(\n signal,\n SINGLE_SIGNATURE_TIMEOUT_MS,\n \"UPLOAD_SINGLE_SIGNATURE_TIMEOUT\",\n async (attemptSignal, _reset, disarm) => {\n beforeRetryPut.set(attemptSignal, disarm);\n try {\n const nextArgs: SingleUploadArgs = [...args];\n nextArgs[6] = { signal: attemptSignal };\n return await uploadSingle(...nextArgs);\n } finally {\n beforeRetryPut.delete(attemptSignal);\n }\n },\n );\n } catch (error) {\n if (\n axios.isAxiosError(error) &&\n error.code === \"UPLOAD_SINGLE_SIGNATURE_TIMEOUT\"\n ) {\n throw new UploadError({\n code: \"SIGNATURE_API_ERROR\",\n message: \"Single upload signature timed out\",\n phase: \"signSingle\",\n cause: error,\n objectKey: args[1],\n retryable: false,\n });\n }\n if (!isTransientSingleNetworkError(error)) throw error;\n lastError = error;\n deadline = Date.now() + 15_000;\n }\n continue;\n }\n if (Date.now() >= deadline) throw lastError;\n const controller = new AbortController();\n let timer: ReturnType<typeof setTimeout> | undefined;\n let delayTimer: ReturnType<typeof setTimeout> | undefined;\n let onAbort = () => {};\n try {\n const stopped = new Promise<never>((_, reject) => {\n onAbort = () => {\n controller.abort();\n reject(new UploadCanceledError(args[1]));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n timer = setTimeout(\n () => {\n controller.abort();\n reject(lastError);\n },\n Math.max(0, deadline - Date.now()),\n );\n });\n await Promise.race([\n new Promise<void>((resolve) => {\n delayTimer = setTimeout(resolve, 500 * attempt);\n }),\n stopped,\n ]);\n if (signal.aborted) throw new UploadCanceledError(args[1]);\n if (Date.now() >= deadline) throw lastError;\n args[5].reset();\n args[5].report(0);\n const retryArgs: SingleUploadArgs = [...args];\n retryArgs[6] = { signal: controller.signal };\n beforeRetryPut.set(controller.signal, () => {\n if (signal.aborted) throw new UploadCanceledError(args[1]);\n if (controller.signal.aborted || Date.now() >= deadline)\n throw lastError;\n clearTimeout(timer);\n });\n return await Promise.race([uploadSingle(...retryArgs), stopped]);\n } catch (error) {\n if (signal.aborted) throw new UploadCanceledError(args[1]);\n if (!isTransientSingleNetworkError(error)) throw error;\n lastError = error;\n } finally {\n beforeRetryPut.delete(controller.signal);\n clearTimeout(timer);\n clearTimeout(delayTimer);\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n throw lastError;\n },\n });\n\n return uploader;\n}\n","const DEFAULT_PART_SIZE = 5 * 1024 * 1024;\nconst CACHE_VERSION = 1 as const;\nconst CACHE_PREFIX = \"safari_upload_part_cache\";\n\nexport interface UploadPartStorage {\n get<T>(key: string): Promise<T | null>;\n set<T>(key: string, value: T): Promise<void>;\n remove(key: string): Promise<void>;\n}\n\nexport interface SafariUploadCacheDescriptor {\n kind: \"safari-array-buffer-parts\";\n version: typeof CACHE_VERSION;\n cacheId: string;\n totalSize: number;\n partSize: number;\n partCount: number;\n file: {\n name: string;\n type: string;\n lastModified: number;\n };\n}\n\ninterface SafariUploadPartRef {\n kind: \"safari-array-buffer-part\";\n descriptor: SafariUploadCacheDescriptor;\n partNumber: number;\n size: number;\n type: string;\n slice(start?: number, end?: number): SafariUploadPartRef;\n}\n\nexport interface SafariUploadSource {\n kind: \"safari-array-buffer-source\";\n descriptor: SafariUploadCacheDescriptor;\n size: number;\n type: string;\n name: string;\n lastModified: number;\n slice(start?: number, end?: number): SafariUploadPartRef;\n}\n\nexport interface SafariUploadCacheManager {\n create(\n file: Blob,\n cacheId: string,\n options?: {\n partSize?: number;\n metadata?: { name: string; type: string; lastModified: number };\n },\n ): Promise<SafariUploadCacheDescriptor | null>;\n validate(descriptor: SafariUploadCacheDescriptor): Promise<boolean>;\n cleanup(\n descriptor: Pick<SafariUploadCacheDescriptor, \"cacheId\" | \"partCount\">,\n ): Promise<void>;\n getPart(\n descriptor: SafariUploadCacheDescriptor,\n partNumber: number,\n expectedLength: number,\n ): Promise<ArrayBuffer>;\n}\n\nclass SafariUploadSourceUnreadableError extends Error {\n readonly code = \"SOURCE_UNREADABLE\";\n readonly retryable = false;\n\n constructor() {\n super(\"source_unreadable\");\n this.name = \"SafariUploadSourceUnreadableError\";\n }\n}\n\nconst manifestKey = (cacheId: string) => `${CACHE_PREFIX}:${cacheId}:manifest`;\nconst partKey = (cacheId: string, partNumber: number) =>\n `${CACHE_PREFIX}:${cacheId}:part:${partNumber}`;\n\n/** Safari 刷新恢复 Blob 不可靠,仅为超过 SDK multipart 阈值的文件启用字节缓存。 */\nexport function isSafariMultipartCacheEligible(\n size: number,\n userAgent = typeof navigator === \"undefined\" ? \"\" : navigator.userAgent,\n): boolean {\n return (\n size > DEFAULT_PART_SIZE &&\n /Safari/.test(userAgent) &&\n !/Chrome|Chromium|CriOS|Edg|Android/.test(userAgent)\n );\n}\n\n/** 校验可持久化 descriptor 的版本和分片布局,保持与主站 version=1 wire shape 兼容。 */\nexport function isSafariUploadCacheDescriptor(\n value: unknown,\n): value is SafariUploadCacheDescriptor {\n if (!value || typeof value !== \"object\") return false;\n const descriptor = value as Partial<SafariUploadCacheDescriptor>;\n return (\n descriptor.kind === \"safari-array-buffer-parts\" &&\n descriptor.version === CACHE_VERSION &&\n typeof descriptor.cacheId === \"string\" &&\n descriptor.cacheId.length > 0 &&\n Number.isSafeInteger(descriptor.totalSize) &&\n (descriptor.totalSize || 0) > 0 &&\n Number.isSafeInteger(descriptor.partSize) &&\n (descriptor.partSize || 0) > 0 &&\n descriptor.partCount ===\n Math.ceil((descriptor.totalSize || 0) / (descriptor.partSize || 1)) &&\n Boolean(descriptor.file) &&\n typeof descriptor.file?.name === \"string\" &&\n typeof descriptor.file?.type === \"string\" &&\n typeof descriptor.file?.lastModified === \"number\"\n );\n}\n\n/** 构造只携带元信息和分片引用的 Blob-like 源,避免刷新恢复时拼接整文件。 */\nexport function createSafariUploadSource(\n descriptor: SafariUploadCacheDescriptor,\n): SafariUploadSource {\n return {\n kind: \"safari-array-buffer-source\",\n descriptor,\n size: descriptor.totalSize,\n type: descriptor.file.type,\n name: descriptor.file.name,\n lastModified: descriptor.file.lastModified,\n slice(start = 0, end = descriptor.totalSize) {\n const partNumber = Math.floor(start / descriptor.partSize) + 1;\n return createPartRef(descriptor, partNumber, end - start);\n },\n };\n}\n\n/** 判断上传源是否为 Safari 缓存生成的虚拟 Blob-like 源。 */\nexport function isSafariUploadSource(\n value: unknown,\n): value is SafariUploadSource {\n return Boolean(\n value &&\n typeof value === \"object\" &&\n (value as SafariUploadSource).kind === \"safari-array-buffer-source\",\n );\n}\n\n/** 创建注入存储的 Safari 分片缓存管理器,将 IndexedDB 等具体实现留给宿主。 */\nexport function createSafariUploadCacheManager({\n storage,\n logger = console,\n}: {\n storage: UploadPartStorage;\n logger?: Pick<Console, \"warn\">;\n}): SafariUploadCacheManager {\n const cleanup: SafariUploadCacheManager[\"cleanup\"] = async (descriptor) => {\n await Promise.allSettled([\n ...Array.from({ length: descriptor.partCount }, (_, index) =>\n storage.remove(partKey(descriptor.cacheId, index + 1)),\n ),\n storage.remove(manifestKey(descriptor.cacheId)),\n ]);\n };\n\n const create: SafariUploadCacheManager[\"create\"] = async (\n file,\n cacheId,\n options,\n ) => {\n const partSize = options?.partSize ?? DEFAULT_PART_SIZE;\n const descriptor: SafariUploadCacheDescriptor = {\n kind: \"safari-array-buffer-parts\",\n version: CACHE_VERSION,\n cacheId,\n totalSize: file.size,\n partSize,\n partCount: Math.ceil(file.size / partSize),\n file: options?.metadata ?? {\n name:\n \"name\" in file && typeof file.name === \"string\"\n ? file.name\n : \"upload\",\n type: file.type,\n lastModified:\n \"lastModified\" in file && typeof file.lastModified === \"number\"\n ? file.lastModified\n : Date.now(),\n },\n };\n\n try {\n for (\n let partNumber = 1;\n partNumber <= descriptor.partCount;\n partNumber++\n ) {\n const expected = expectedPartSize(descriptor, partNumber);\n const start = (partNumber - 1) * partSize;\n const bytes = await readBlobPart(file.slice(start, start + expected));\n if (bytes.byteLength !== expected)\n throw new Error(\"Invalid source part\");\n const key = partKey(cacheId, partNumber);\n await storage.set(key, bytes);\n const stored = await storage.get<ArrayBuffer>(key);\n if (\n !(stored instanceof ArrayBuffer) ||\n stored.byteLength !== expected\n ) {\n throw new Error(\"Invalid persisted part\");\n }\n }\n await storage.set(manifestKey(cacheId), descriptor);\n const manifest = await storage.get(manifestKey(cacheId));\n if (!isSafariUploadCacheDescriptor(manifest)) {\n throw new Error(\"Invalid persisted manifest\");\n }\n return descriptor;\n } catch (error) {\n // 任一写入或回读失败都清理完整 key 空间,避免留下可被误判为可恢复的半成品。\n await cleanup(descriptor);\n logger.warn(\n \"Safari upload byte cache unavailable, falling back to Blob\",\n error,\n );\n return null;\n }\n };\n\n const validate: SafariUploadCacheManager[\"validate\"] = async (descriptor) => {\n const stored = await storage.get<SafariUploadCacheDescriptor>(\n manifestKey(descriptor.cacheId),\n );\n return (\n isSafariUploadCacheDescriptor(stored) &&\n stored.totalSize === descriptor.totalSize &&\n stored.partSize === descriptor.partSize &&\n stored.partCount === descriptor.partCount\n );\n };\n\n const getPart: SafariUploadCacheManager[\"getPart\"] = async (\n descriptor,\n partNumber,\n expectedLength,\n ) => {\n if (\n partNumber < 1 ||\n partNumber > descriptor.partCount ||\n expectedLength !== expectedPartSize(descriptor, partNumber)\n ) {\n throw new SafariUploadSourceUnreadableError();\n }\n try {\n const bytes = await storage.get<ArrayBuffer>(\n partKey(descriptor.cacheId, partNumber),\n );\n if (\n !(bytes instanceof ArrayBuffer) ||\n bytes.byteLength !== expectedLength\n ) {\n throw new SafariUploadSourceUnreadableError();\n }\n return bytes;\n } catch (error) {\n if (error instanceof SafariUploadSourceUnreadableError) throw error;\n throw new SafariUploadSourceUnreadableError();\n }\n };\n\n return { create, validate, cleanup, getPart };\n}\n\n/** 读取 Blob 分片,并兼容缺少 Blob.arrayBuffer 的旧浏览器。 */\nfunction readBlobPart(blob: Blob): Promise<ArrayBuffer> {\n if (typeof blob.arrayBuffer === \"function\") return blob.arrayBuffer();\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error);\n reader.readAsArrayBuffer(blob);\n });\n}\n\n/** 计算指定分片应有长度,最后一片允许小于标准 partSize。 */\nfunction expectedPartSize(\n descriptor: SafariUploadCacheDescriptor,\n partNumber: number,\n): number {\n return Math.min(\n descriptor.partSize,\n descriptor.totalSize - (partNumber - 1) * descriptor.partSize,\n );\n}\n\n/** 创建 SDK 切片流程使用的内部引用,不将该私有 seam 暴露为包导出。 */\nfunction createPartRef(\n descriptor: SafariUploadCacheDescriptor,\n partNumber: number,\n size: number,\n): SafariUploadPartRef {\n return {\n kind: \"safari-array-buffer-part\",\n descriptor,\n partNumber,\n size,\n type: descriptor.file.type,\n slice(start = 0, end = size) {\n return createPartRef(descriptor, partNumber, end - start);\n },\n };\n}\n"],"mappings":"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","names":["axios"]}