firebase-storage-kit 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/index.d.ts +56 -2
- package/dist/index.js +217 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,6 +60,21 @@ const unsubscribe = manager.subscribe((state) => {
|
|
|
60
60
|
});
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
### Upload retries
|
|
64
|
+
|
|
65
|
+
Uploads retry transient failures automatically (3 retries by default, exponential backoff with jitter). Pass `retry: false` to disable, or customize:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const handle = manager.uploadFile(file, {
|
|
69
|
+
path: `uploads/${file.name}`,
|
|
70
|
+
retry: { maxRetries: 5, initialDelayMs: 700 },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
handle.on("retry", ({ attempt, maxAttempts, delayMs, error }) => {
|
|
74
|
+
console.log(`Retry ${attempt}/${maxAttempts} in ${delayMs}ms`, error.message);
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
63
78
|
### Querying files
|
|
64
79
|
|
|
65
80
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ interface StorageState {
|
|
|
5
5
|
uploads: UploadItem[];
|
|
6
6
|
batches: UploadBatch[];
|
|
7
7
|
}
|
|
8
|
-
type UploadStatus = "queued" | "uploading" | "paused" | "success" | "error" | "canceled";
|
|
8
|
+
type UploadStatus = "queued" | "uploading" | "retrying" | "paused" | "success" | "error" | "canceled";
|
|
9
9
|
interface UploadItem {
|
|
10
10
|
id: string;
|
|
11
11
|
file: File;
|
|
@@ -16,6 +16,7 @@ interface UploadItem {
|
|
|
16
16
|
status: UploadStatus;
|
|
17
17
|
downloadURL?: string;
|
|
18
18
|
error?: Error;
|
|
19
|
+
retryAttempt?: number;
|
|
19
20
|
batchId?: string;
|
|
20
21
|
}
|
|
21
22
|
interface UploadBatch {
|
|
@@ -42,12 +43,31 @@ declare class Emitter<TEvents extends Record<string, unknown>> {
|
|
|
42
43
|
protected emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
interface UploadRetryEvent {
|
|
47
|
+
upload: UploadItem;
|
|
48
|
+
error: Error;
|
|
49
|
+
attempt: number;
|
|
50
|
+
maxAttempts: number;
|
|
51
|
+
delayMs: number;
|
|
52
|
+
}
|
|
53
|
+
interface UploadRetryBackoffDetails {
|
|
54
|
+
attempt: number;
|
|
55
|
+
maxAttempts: number;
|
|
56
|
+
delayMs: number;
|
|
57
|
+
error: Error;
|
|
58
|
+
}
|
|
59
|
+
interface UploadControlHooks {
|
|
60
|
+
abort: () => void;
|
|
61
|
+
pauseDuringRetry?: () => void;
|
|
62
|
+
resumeDuringRetry?: () => void;
|
|
63
|
+
}
|
|
45
64
|
type UploadHandleEvents = {
|
|
46
65
|
progress: UploadItem;
|
|
47
66
|
success: UploadItem;
|
|
48
67
|
error: UploadItem;
|
|
49
68
|
canceled: UploadItem;
|
|
50
69
|
statusChange: UploadItem;
|
|
70
|
+
retry: UploadRetryEvent;
|
|
51
71
|
};
|
|
52
72
|
/** Controls file upload and tracks its progress.
|
|
53
73
|
*
|
|
@@ -60,6 +80,8 @@ declare class UploadHandle extends Emitter<UploadHandleEvents> {
|
|
|
60
80
|
private task;
|
|
61
81
|
private onChange;
|
|
62
82
|
private terminated;
|
|
83
|
+
private controlHooks;
|
|
84
|
+
private pausedDuringRetry;
|
|
63
85
|
constructor(upload: UploadItem, onChange: () => void);
|
|
64
86
|
cancel(): void;
|
|
65
87
|
pause(): void;
|
|
@@ -143,10 +165,41 @@ interface ProviderUploadCallbacks {
|
|
|
143
165
|
onError: (error: Error) => void;
|
|
144
166
|
onSuccess: (downloadURL: string) => void;
|
|
145
167
|
}
|
|
168
|
+
interface RetryOptions {
|
|
169
|
+
/** Extra attempts after the first failure. Default: 3 (4 total tries). */
|
|
170
|
+
maxRetries?: number;
|
|
171
|
+
/** Initial backoff in ms. Default: 1000. */
|
|
172
|
+
initialDelayMs?: number;
|
|
173
|
+
/** Backoff cap in ms. Default: 30000. */
|
|
174
|
+
maxDelayMs?: number;
|
|
175
|
+
/** Apply jitter to backoff delays. Default: true. */
|
|
176
|
+
jitter?: boolean;
|
|
177
|
+
/** Override retry classification. */
|
|
178
|
+
isRetryable?: (error: Error) => boolean;
|
|
179
|
+
}
|
|
146
180
|
interface UploadOptions {
|
|
147
181
|
/** Object path in the bucket (Firebase), e.g. `uploads/photo.jpg`. */
|
|
148
182
|
path: string;
|
|
183
|
+
/** Retries are enabled by default. Pass `false` to disable, or an object to customize. */
|
|
184
|
+
retry?: RetryOptions | false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
interface ResolvedRetryOptions {
|
|
188
|
+
maxRetries: number;
|
|
189
|
+
initialDelayMs: number;
|
|
190
|
+
maxDelayMs: number;
|
|
191
|
+
jitter: boolean;
|
|
192
|
+
isRetryable?: (error: Error) => boolean;
|
|
149
193
|
}
|
|
194
|
+
declare const DEFAULT_RETRY_OPTIONS: ResolvedRetryOptions;
|
|
195
|
+
/** Resolves upload retry settings. Returns `null` when retries are disabled. */
|
|
196
|
+
declare function resolveRetryOptions(retry?: RetryOptions | false): ResolvedRetryOptions | null;
|
|
197
|
+
/** Reads a Firebase-style `code` from an error without importing Firebase types. */
|
|
198
|
+
declare function getStorageErrorCode(error: Error): string | undefined;
|
|
199
|
+
/** Whether an upload error should trigger another attempt. */
|
|
200
|
+
declare function isRetryableStorageError(error: Error, options: ResolvedRetryOptions | null): boolean;
|
|
201
|
+
/** Computes backoff delay after a failed attempt (1-based). */
|
|
202
|
+
declare function computeRetryDelay(failedAttempt: number, options: ResolvedRetryOptions): number;
|
|
150
203
|
|
|
151
204
|
interface FileMetadata {
|
|
152
205
|
path: string;
|
|
@@ -178,6 +231,7 @@ declare class StorageManager$1 {
|
|
|
178
231
|
private batches;
|
|
179
232
|
private changeListeners;
|
|
180
233
|
private cachedState;
|
|
234
|
+
private uploadOptionsByHandleId;
|
|
181
235
|
constructor(provider: StorageProvider);
|
|
182
236
|
/** Current `uploads` and `batches` state. */
|
|
183
237
|
getState: () => StorageState;
|
|
@@ -217,4 +271,4 @@ declare class StorageManager extends StorageManager$1 {
|
|
|
217
271
|
constructor(storage: FirebaseStorage);
|
|
218
272
|
}
|
|
219
273
|
|
|
220
|
-
export { BatchHandle, type BatchHandleEvents, type BatchHandleInit, type BatchOptions, type FileMetadata, type ProviderUploadCallbacks, type ProviderUploadTask, StorageManager, type StorageState, type UploadBatch, UploadHandle, type UploadHandleEvents, type UploadItem, type UploadOptions, type UploadStatus };
|
|
274
|
+
export { BatchHandle, type BatchHandleEvents, type BatchHandleInit, type BatchOptions, DEFAULT_RETRY_OPTIONS, type FileMetadata, type ProviderUploadCallbacks, type ProviderUploadTask, type RetryOptions, StorageManager, type StorageState, type UploadBatch, type UploadControlHooks, UploadHandle, type UploadHandleEvents, type UploadItem, type UploadOptions, type UploadRetryBackoffDetails, type UploadRetryEvent, type UploadStatus, computeRetryDelay, getStorageErrorCode, isRetryableStorageError, resolveRetryOptions };
|
package/dist/index.js
CHANGED
|
@@ -95,7 +95,7 @@ var BatchHandle = class extends Emitter {
|
|
|
95
95
|
this.canceled = true;
|
|
96
96
|
for (const h of this.uploads) {
|
|
97
97
|
const s = h.upload.status;
|
|
98
|
-
if (s === "queued" || s === "uploading" || s === "paused") {
|
|
98
|
+
if (s === "queued" || s === "uploading" || s === "retrying" || s === "paused") {
|
|
99
99
|
h.cancel();
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -105,7 +105,9 @@ var BatchHandle = class extends Emitter {
|
|
|
105
105
|
if (this.paused || this.canceled || this.terminalEmitted) return;
|
|
106
106
|
this.paused = true;
|
|
107
107
|
for (const h of this.uploads) {
|
|
108
|
-
if (h.upload.status === "uploading"
|
|
108
|
+
if (h.upload.status === "uploading" || h.upload.status === "retrying") {
|
|
109
|
+
h.pause();
|
|
110
|
+
}
|
|
109
111
|
}
|
|
110
112
|
this.onChange();
|
|
111
113
|
}
|
|
@@ -172,7 +174,7 @@ var BatchHandle = class extends Emitter {
|
|
|
172
174
|
for (const h of this.uploads) {
|
|
173
175
|
if (h.upload.id === upload.id) continue;
|
|
174
176
|
const s = h.upload.status;
|
|
175
|
-
if (s === "queued" || s === "uploading" || s === "paused") {
|
|
177
|
+
if (s === "queued" || s === "uploading" || s === "retrying" || s === "paused") {
|
|
176
178
|
h.cancel();
|
|
177
179
|
}
|
|
178
180
|
}
|
|
@@ -194,12 +196,79 @@ var BatchHandle = class extends Emitter {
|
|
|
194
196
|
}
|
|
195
197
|
};
|
|
196
198
|
|
|
199
|
+
// src/core/retry.ts
|
|
200
|
+
var DEFAULT_RETRY_OPTIONS = {
|
|
201
|
+
maxRetries: 3,
|
|
202
|
+
initialDelayMs: 1e3,
|
|
203
|
+
maxDelayMs: 3e4,
|
|
204
|
+
jitter: true
|
|
205
|
+
};
|
|
206
|
+
var NON_RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
207
|
+
"storage/canceled",
|
|
208
|
+
"storage/unauthorized",
|
|
209
|
+
"storage/unauthenticated",
|
|
210
|
+
"storage/quota-exceeded",
|
|
211
|
+
"storage/invalid-argument",
|
|
212
|
+
"storage/invalid-argument-count",
|
|
213
|
+
"storage/invalid-url",
|
|
214
|
+
"storage/invalid-default-bucket",
|
|
215
|
+
"storage/no-default-bucket",
|
|
216
|
+
"storage/bucket-not-found",
|
|
217
|
+
"storage/project-not-found",
|
|
218
|
+
"storage/invalid-root-operation",
|
|
219
|
+
"storage/invalid-event-name",
|
|
220
|
+
"storage/invalid-format",
|
|
221
|
+
"storage/no-download-url",
|
|
222
|
+
"storage/unauthorized-app",
|
|
223
|
+
"storage/unsupported-environment"
|
|
224
|
+
]);
|
|
225
|
+
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
226
|
+
"storage/retry-limit-exceeded",
|
|
227
|
+
"storage/unknown",
|
|
228
|
+
"storage/invalid-checksum",
|
|
229
|
+
"storage/cannot-slice-blob",
|
|
230
|
+
"storage/server-file-wrong-size"
|
|
231
|
+
]);
|
|
232
|
+
function resolveRetryOptions(retry) {
|
|
233
|
+
if (retry === false) return null;
|
|
234
|
+
return {
|
|
235
|
+
...DEFAULT_RETRY_OPTIONS,
|
|
236
|
+
...retry
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function getStorageErrorCode(error) {
|
|
240
|
+
return error.code;
|
|
241
|
+
}
|
|
242
|
+
function isRetryableStorageError(error, options) {
|
|
243
|
+
if (!options) return false;
|
|
244
|
+
if (options.isRetryable) {
|
|
245
|
+
return options.isRetryable(error);
|
|
246
|
+
}
|
|
247
|
+
const code = getStorageErrorCode(error);
|
|
248
|
+
if (code) {
|
|
249
|
+
if (NON_RETRYABLE_CODES.has(code)) return false;
|
|
250
|
+
if (RETRYABLE_CODES.has(code)) return true;
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
function computeRetryDelay(failedAttempt, options) {
|
|
256
|
+
const base = Math.min(
|
|
257
|
+
options.maxDelayMs,
|
|
258
|
+
options.initialDelayMs * 2 ** (failedAttempt - 1)
|
|
259
|
+
);
|
|
260
|
+
if (!options.jitter) return base;
|
|
261
|
+
return Math.round(base * (0.5 + Math.random() * 0.5));
|
|
262
|
+
}
|
|
263
|
+
|
|
197
264
|
// src/core/upload-handle.ts
|
|
198
265
|
var UploadHandle = class extends Emitter {
|
|
199
266
|
upload;
|
|
200
267
|
task = null;
|
|
201
268
|
onChange;
|
|
202
269
|
terminated = false;
|
|
270
|
+
controlHooks = null;
|
|
271
|
+
pausedDuringRetry = false;
|
|
203
272
|
constructor(upload, onChange) {
|
|
204
273
|
super();
|
|
205
274
|
this.upload = upload;
|
|
@@ -210,6 +279,14 @@ var UploadHandle = class extends Emitter {
|
|
|
210
279
|
this.task = task;
|
|
211
280
|
}
|
|
212
281
|
/** @internal */
|
|
282
|
+
_registerControlHooks(hooks) {
|
|
283
|
+
this.controlHooks = hooks;
|
|
284
|
+
}
|
|
285
|
+
/** @internal */
|
|
286
|
+
_clearControlHooks() {
|
|
287
|
+
this.controlHooks = null;
|
|
288
|
+
}
|
|
289
|
+
/** @internal */
|
|
213
290
|
_setStatus(status) {
|
|
214
291
|
if (this.upload.status === status) return;
|
|
215
292
|
this.upload.status = status;
|
|
@@ -228,11 +305,39 @@ var UploadHandle = class extends Emitter {
|
|
|
228
305
|
this.onChange();
|
|
229
306
|
}
|
|
230
307
|
/** @internal */
|
|
308
|
+
_prepareRetryAttempt(attempt) {
|
|
309
|
+
if (this.terminated) return;
|
|
310
|
+
this.upload.retryAttempt = attempt;
|
|
311
|
+
this.upload.bytesTransferred = 0;
|
|
312
|
+
this.upload.progress = 0;
|
|
313
|
+
this.upload.error = void 0;
|
|
314
|
+
this.pausedDuringRetry = false;
|
|
315
|
+
this._setStatus("uploading");
|
|
316
|
+
this.onChange();
|
|
317
|
+
}
|
|
318
|
+
/** @internal */
|
|
319
|
+
_enterRetryBackoff(details) {
|
|
320
|
+
if (this.terminated) return;
|
|
321
|
+
this.upload.retryAttempt = details.attempt;
|
|
322
|
+
this.upload.error = details.error;
|
|
323
|
+
this.task = null;
|
|
324
|
+
this._setStatus("retrying");
|
|
325
|
+
this.emit("retry", {
|
|
326
|
+
upload: this.upload,
|
|
327
|
+
error: details.error,
|
|
328
|
+
attempt: details.attempt,
|
|
329
|
+
maxAttempts: details.maxAttempts,
|
|
330
|
+
delayMs: details.delayMs
|
|
331
|
+
});
|
|
332
|
+
this.onChange();
|
|
333
|
+
}
|
|
334
|
+
/** @internal */
|
|
231
335
|
_reportSuccess(downloadURL) {
|
|
232
336
|
if (this.terminated) return;
|
|
233
337
|
this.terminated = true;
|
|
234
338
|
this.upload.downloadURL = downloadURL;
|
|
235
339
|
this.upload.progress = 100;
|
|
340
|
+
this._clearControlHooks();
|
|
236
341
|
this._setStatus("success");
|
|
237
342
|
this.emit("success", this.upload);
|
|
238
343
|
this.onChange();
|
|
@@ -242,6 +347,7 @@ var UploadHandle = class extends Emitter {
|
|
|
242
347
|
if (this.terminated) return;
|
|
243
348
|
this.terminated = true;
|
|
244
349
|
this.upload.error = error;
|
|
350
|
+
this._clearControlHooks();
|
|
245
351
|
this._setStatus("error");
|
|
246
352
|
this.emit("error", this.upload);
|
|
247
353
|
this.onChange();
|
|
@@ -249,21 +355,37 @@ var UploadHandle = class extends Emitter {
|
|
|
249
355
|
cancel() {
|
|
250
356
|
if (this.terminated) return;
|
|
251
357
|
this.terminated = true;
|
|
358
|
+
this.controlHooks?.abort();
|
|
252
359
|
this.task?.cancel();
|
|
360
|
+
this._clearControlHooks();
|
|
253
361
|
this._setStatus("canceled");
|
|
254
362
|
this.emit("canceled", this.upload);
|
|
255
363
|
this.onChange();
|
|
256
364
|
}
|
|
257
365
|
pause() {
|
|
258
366
|
if (this.terminated) return;
|
|
259
|
-
if (this.upload.status
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
367
|
+
if (this.upload.status === "uploading") {
|
|
368
|
+
this.task?.pause?.();
|
|
369
|
+
this._setStatus("paused");
|
|
370
|
+
this.onChange();
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (this.upload.status === "retrying") {
|
|
374
|
+
this.pausedDuringRetry = true;
|
|
375
|
+
this.controlHooks?.pauseDuringRetry?.();
|
|
376
|
+
this._setStatus("paused");
|
|
377
|
+
this.onChange();
|
|
378
|
+
}
|
|
263
379
|
}
|
|
264
380
|
resume() {
|
|
265
381
|
if (this.terminated) return;
|
|
266
382
|
if (this.upload.status !== "paused") return;
|
|
383
|
+
if (this.pausedDuringRetry) {
|
|
384
|
+
this.pausedDuringRetry = false;
|
|
385
|
+
this.controlHooks?.resumeDuringRetry?.();
|
|
386
|
+
this.onChange();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
267
389
|
this.task?.resume?.();
|
|
268
390
|
this._setStatus("uploading");
|
|
269
391
|
this.onChange();
|
|
@@ -277,6 +399,7 @@ var StorageManager = class {
|
|
|
277
399
|
batches = [];
|
|
278
400
|
changeListeners = /* @__PURE__ */ new Set();
|
|
279
401
|
cachedState = null;
|
|
402
|
+
uploadOptionsByHandleId = /* @__PURE__ */ new Map();
|
|
280
403
|
constructor(provider) {
|
|
281
404
|
this.provider = provider;
|
|
282
405
|
}
|
|
@@ -319,6 +442,7 @@ var StorageManager = class {
|
|
|
319
442
|
*/
|
|
320
443
|
uploadFile(file, options) {
|
|
321
444
|
const handle = this.createHandle(file, { path: options.path });
|
|
445
|
+
this.uploadOptionsByHandleId.set(handle.upload.id, options);
|
|
322
446
|
this.uploadHandles.push(handle);
|
|
323
447
|
this.notifyChange();
|
|
324
448
|
this.startUpload(handle, options);
|
|
@@ -338,7 +462,12 @@ var StorageManager = class {
|
|
|
338
462
|
const id = crypto.randomUUID();
|
|
339
463
|
const handles = files.map((file, i) => {
|
|
340
464
|
const options = optionsFor(file, i);
|
|
341
|
-
|
|
465
|
+
const handle = this.createHandle(file, {
|
|
466
|
+
batchId: id,
|
|
467
|
+
path: options.path
|
|
468
|
+
});
|
|
469
|
+
this.uploadOptionsByHandleId.set(handle.upload.id, options);
|
|
470
|
+
return handle;
|
|
342
471
|
});
|
|
343
472
|
this.uploadHandles.push(...handles);
|
|
344
473
|
const batch = new BatchHandle({
|
|
@@ -346,7 +475,10 @@ var StorageManager = class {
|
|
|
346
475
|
uploads: handles,
|
|
347
476
|
options: batchOptions,
|
|
348
477
|
startNext: (handle) => {
|
|
349
|
-
this.
|
|
478
|
+
const options = this.uploadOptionsByHandleId.get(handle.upload.id) ?? {
|
|
479
|
+
path: handle.upload.path
|
|
480
|
+
};
|
|
481
|
+
this.startUpload(handle, options);
|
|
350
482
|
},
|
|
351
483
|
onChange: () => this.notifyChange()
|
|
352
484
|
});
|
|
@@ -369,14 +501,82 @@ var StorageManager = class {
|
|
|
369
501
|
return new UploadHandle(upload, () => this.notifyChange());
|
|
370
502
|
}
|
|
371
503
|
startUpload(handle, options) {
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
504
|
+
const retryOptions = resolveRetryOptions(options.retry);
|
|
505
|
+
const maxAttempts = retryOptions ? retryOptions.maxRetries + 1 : 1;
|
|
506
|
+
let attempt = 0;
|
|
507
|
+
let retryTimeout = null;
|
|
508
|
+
let currentTask = null;
|
|
509
|
+
let aborted = false;
|
|
510
|
+
let pausedDuringRetry = false;
|
|
511
|
+
let pendingRetryError = null;
|
|
512
|
+
const clearRetryTimeout = () => {
|
|
513
|
+
if (retryTimeout !== null) {
|
|
514
|
+
clearTimeout(retryTimeout);
|
|
515
|
+
retryTimeout = null;
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
const abort = () => {
|
|
519
|
+
aborted = true;
|
|
520
|
+
clearRetryTimeout();
|
|
521
|
+
currentTask?.cancel();
|
|
522
|
+
currentTask = null;
|
|
523
|
+
pendingRetryError = null;
|
|
524
|
+
};
|
|
525
|
+
const scheduleRetry = (error, delayMs) => {
|
|
526
|
+
if (aborted) return;
|
|
527
|
+
pendingRetryError = error;
|
|
528
|
+
handle._enterRetryBackoff({
|
|
529
|
+
attempt: attempt + 1,
|
|
530
|
+
maxAttempts,
|
|
531
|
+
delayMs,
|
|
532
|
+
error
|
|
533
|
+
});
|
|
534
|
+
retryTimeout = setTimeout(() => {
|
|
535
|
+
retryTimeout = null;
|
|
536
|
+
if (aborted || pausedDuringRetry) return;
|
|
537
|
+
pendingRetryError = null;
|
|
538
|
+
runAttempt();
|
|
539
|
+
}, delayMs);
|
|
540
|
+
};
|
|
541
|
+
const runAttempt = () => {
|
|
542
|
+
if (aborted) return;
|
|
543
|
+
attempt += 1;
|
|
544
|
+
handle._prepareRetryAttempt(attempt);
|
|
545
|
+
currentTask = this.provider.upload(handle.upload.file, options, {
|
|
546
|
+
onProgress: (bytesTransferred, totalBytes) => handle._reportProgress(bytesTransferred, totalBytes),
|
|
547
|
+
onError: (error) => {
|
|
548
|
+
currentTask = null;
|
|
549
|
+
if (aborted) return;
|
|
550
|
+
if (attempt < maxAttempts && isRetryableStorageError(error, retryOptions)) {
|
|
551
|
+
const delayMs = retryOptions ? computeRetryDelay(attempt, retryOptions) : 0;
|
|
552
|
+
scheduleRetry(error, delayMs);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
handle._reportError(error);
|
|
556
|
+
},
|
|
557
|
+
onSuccess: (downloadURL) => {
|
|
558
|
+
currentTask = null;
|
|
559
|
+
if (aborted) return;
|
|
560
|
+
handle._reportSuccess(downloadURL);
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
handle._attachTask(currentTask);
|
|
564
|
+
};
|
|
565
|
+
handle._registerControlHooks({
|
|
566
|
+
abort,
|
|
567
|
+
pauseDuringRetry: () => {
|
|
568
|
+
pausedDuringRetry = true;
|
|
569
|
+
clearRetryTimeout();
|
|
570
|
+
},
|
|
571
|
+
resumeDuringRetry: () => {
|
|
572
|
+
if (aborted) return;
|
|
573
|
+
pausedDuringRetry = false;
|
|
574
|
+
if (pendingRetryError) {
|
|
575
|
+
scheduleRetry(pendingRetryError, 0);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
377
578
|
});
|
|
378
|
-
|
|
379
|
-
this.notifyChange();
|
|
579
|
+
runAttempt();
|
|
380
580
|
}
|
|
381
581
|
notifyChange() {
|
|
382
582
|
this.cachedState = null;
|
|
@@ -463,6 +663,6 @@ var StorageManager2 = class extends StorageManager {
|
|
|
463
663
|
}
|
|
464
664
|
};
|
|
465
665
|
|
|
466
|
-
export { BatchHandle, StorageManager2 as StorageManager, UploadHandle };
|
|
666
|
+
export { BatchHandle, DEFAULT_RETRY_OPTIONS, StorageManager2 as StorageManager, UploadHandle, computeRetryDelay, getStorageErrorCode, isRetryableStorageError, resolveRetryOptions };
|
|
467
667
|
//# sourceMappingURL=index.js.map
|
|
468
668
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/emitter.ts","../src/core/batch-handle.ts","../src/core/upload-handle.ts","../src/core/storage-manager.ts","../src/providers/firebase-provider.ts","../src/firebase-storage-manager.ts"],"names":["StorageManager"],"mappings":"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","file":"index.js","sourcesContent":["type Listener<TPayload> = (payload: TPayload) => void;\n\nexport class Emitter<TEvents extends Record<string, unknown>> {\n private listeners: { [K in keyof TEvents]?: Set<Listener<TEvents[K]>> } = {};\n\n /**\n * @param event The event to listen for.\n * @param listener The function to call when the event is emitted.\n * @returns A function to unsubscribe from the event.\n */\n on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>) {\n if (!this.listeners[event]) {\n this.listeners[event] = new Set();\n }\n\n this.listeners[event]!.add(listener);\n\n return () => {\n this.listeners[event]?.delete(listener);\n };\n }\n\n /**\n * @param event The event to emit.\n * @param payload The payload to emit.\n */\n protected emit<K extends keyof TEvents>(event: K, payload: TEvents[K]) {\n const listeners = this.listeners[event];\n\n if (!listeners) {\n return;\n }\n\n for (const listener of listeners) {\n listener(payload);\n }\n }\n}\n","import type { UploadBatch, UploadItem } from \"../types/upload\";\nimport { Emitter } from \"./emitter\";\nimport type { UploadHandle } from \"./upload-handle\";\n\n/**\n * Events on a batch from {@link StorageManager.uploadFiles}. `progress` / `change` carry a {@link UploadBatch} snapshot.\n * `uploadSuccess` and `uploadError` fire once per child. When all children are done: `success` (normal end) or `error` (only if `continueOnError` was `false` and one failed).\n */\nexport type BatchHandleEvents = {\n progress: UploadBatch;\n uploadSuccess: UploadItem;\n uploadError: UploadItem;\n success: UploadBatch;\n error: UploadBatch;\n change: UploadBatch;\n};\n\n/** Settings for {@link StorageManager.uploadFiles} (third argument). */\nexport interface BatchOptions {\n /** How many files upload at the same time. Default is 3. */\n concurrency?: number;\n /**\n * `true` (default): if one file fails, the others keep going. When every file has finished, the batch fires `success` — look at each upload for errors.\n *\n * `false`: the first failure stops the other files and the batch fires `error`.\n */\n continueOnError?: boolean;\n}\n\nexport interface BatchHandleInit {\n id: string;\n uploads: UploadHandle[];\n options: BatchOptions;\n startNext: (handle: UploadHandle) => void;\n onChange: () => void;\n}\n\n/**\n * A group of files from {@link StorageManager.uploadFiles}. Use `.on(...)` like a single {@link UploadHandle}, plus `snapshot()` for totals.\n */\nexport class BatchHandle extends Emitter<BatchHandleEvents> {\n public readonly id: string;\n public readonly uploads: UploadHandle[];\n\n private concurrency: number;\n private continueOnError: boolean;\n private startNext: (handle: UploadHandle) => void;\n private onChange: () => void;\n\n private running = 0;\n private nextIdx = 0;\n /** Indices that were started via {@link fillSlots} and still hold a concurrency slot. */\n private activeSlots = new Set<number>();\n private settledCount = 0;\n private succeededCount = 0;\n private failedCount = 0;\n private failedFast = false;\n private canceled = false;\n private paused = false;\n private terminalEmitted = false;\n\n // Stable view of UploadItems; the items themselves mutate in place, but the\n // array reference never changes, so we allocate it once instead of rebuilding\n // it on every snapshot.\n private readonly uploadsView: UploadItem[];\n\n // Running aggregates updated incrementally on each child event so snapshot()\n // is O(1) instead of O(n) per progress tick.\n private aggregateBytesTransferred = 0;\n private aggregateTotalBytes = 0;\n private readonly lastBytes: number[];\n private readonly lastTotal: number[];\n\n constructor(init: BatchHandleInit) {\n super();\n this.id = init.id;\n this.uploads = init.uploads;\n this.concurrency = Math.max(1, init.options.concurrency ?? 3);\n this.continueOnError = init.options.continueOnError ?? true;\n this.startNext = init.startNext;\n this.onChange = init.onChange;\n\n this.uploadsView = init.uploads.map((h) => h.upload);\n this.lastBytes = new Array(init.uploads.length).fill(0);\n this.lastTotal = new Array(init.uploads.length).fill(0);\n for (let i = 0; i < this.uploadsView.length; i++) {\n const u = this.uploadsView[i]!;\n this.lastBytes[i] = u.bytesTransferred;\n this.lastTotal[i] = u.totalBytes;\n this.aggregateBytesTransferred += u.bytesTransferred;\n this.aggregateTotalBytes += u.totalBytes;\n }\n\n this.uploads.forEach((child, idx) => {\n child.on(\"progress\", (u) => this.handleChildProgress(idx, u));\n child.on(\"success\", (u) => this.handleChildSettled(idx, u, \"success\"));\n child.on(\"error\", (u) => this.handleChildSettled(idx, u, \"error\"));\n child.on(\"canceled\", (u) => this.handleChildSettled(idx, u, \"canceled\"));\n });\n }\n\n /** @internal */\n _start(): void {\n this.fillSlots();\n }\n\n cancel(): void {\n if (this.canceled || this.terminalEmitted) return;\n this.canceled = true;\n for (const h of this.uploads) {\n const s = h.upload.status;\n if (s === \"queued\" || s === \"uploading\" || s === \"paused\") {\n h.cancel();\n }\n }\n this.onChange();\n }\n\n pause(): void {\n if (this.paused || this.canceled || this.terminalEmitted) return;\n this.paused = true;\n for (const h of this.uploads) {\n if (h.upload.status === \"uploading\") h.pause();\n }\n this.onChange();\n }\n\n resume(): void {\n if (!this.paused || this.canceled || this.terminalEmitted) return;\n this.paused = false;\n for (const h of this.uploads) {\n if (h.upload.status === \"paused\") h.resume();\n }\n this.fillSlots();\n this.onChange();\n }\n\n snapshot(): UploadBatch {\n const totalProgress =\n this.aggregateTotalBytes > 0\n ? (this.aggregateBytesTransferred / this.aggregateTotalBytes) * 100\n : 0;\n return {\n id: this.id,\n uploads: this.uploadsView,\n totalProgress,\n completedCount: this.succeededCount,\n failedCount: this.failedCount,\n };\n }\n\n private updateAggregate(idx: number, u: UploadItem): void {\n const prevBytes = this.lastBytes[idx] ?? 0;\n const prevTotal = this.lastTotal[idx] ?? 0;\n this.aggregateBytesTransferred += u.bytesTransferred - prevBytes;\n this.aggregateTotalBytes += u.totalBytes - prevTotal;\n this.lastBytes[idx] = u.bytesTransferred;\n this.lastTotal[idx] = u.totalBytes;\n }\n\n private fillSlots(): void {\n while (\n !this.canceled &&\n !this.paused &&\n !this.failedFast &&\n this.running < this.concurrency &&\n this.nextIdx < this.uploads.length\n ) {\n const idx = this.nextIdx++;\n const handle = this.uploads[idx];\n if (!handle || handle.upload.status !== \"queued\") continue;\n this.activeSlots.add(idx);\n this.running++;\n this.startNext(handle);\n }\n }\n\n private handleChildProgress(idx: number, upload: UploadItem): void {\n this.updateAggregate(idx, upload);\n const snap = this.snapshot();\n this.emit(\"progress\", snap);\n this.emit(\"change\", snap);\n }\n\n private handleChildSettled(\n idx: number,\n upload: UploadItem,\n kind: \"success\" | \"error\" | \"canceled\",\n ): void {\n this.updateAggregate(idx, upload);\n if (this.activeSlots.delete(idx)) {\n this.running = Math.max(0, this.running - 1);\n }\n this.settledCount++;\n\n if (kind === \"success\") {\n this.succeededCount++;\n this.emit(\"uploadSuccess\", upload);\n } else if (kind === \"error\") {\n this.failedCount++;\n this.emit(\"uploadError\", upload);\n }\n\n const snap = this.snapshot();\n this.emit(\"change\", snap);\n\n if (\n kind === \"error\" &&\n !this.continueOnError &&\n !this.failedFast &&\n !this.canceled\n ) {\n this.failedFast = true;\n for (const h of this.uploads) {\n if (h.upload.id === upload.id) continue;\n const s = h.upload.status;\n if (s === \"queued\" || s === \"uploading\" || s === \"paused\") {\n h.cancel();\n }\n }\n this.terminalEmitted = true;\n this.emit(\"error\", snap);\n this.onChange();\n return;\n }\n\n if (this.failedFast || this.canceled) {\n // we call this because a task can canceled as seen above\n // by h.cancel(). Which then triggers the handelchildSettled call above.\n this.onChange();\n return;\n }\n\n this.fillSlots();\n\n if (this.settledCount >= this.uploads.length && !this.terminalEmitted) {\n this.terminalEmitted = true;\n this.emit(\"success\", snap);\n }\n\n this.onChange();\n }\n}\n","import { Emitter } from \"./emitter\";\n\nimport type { ProviderUploadTask } from \"../types/provider\";\nimport type { UploadItem, UploadStatus } from \"../types/upload\";\n\nexport type UploadHandleEvents = {\n progress: UploadItem;\n success: UploadItem;\n error: UploadItem;\n canceled: UploadItem;\n statusChange: UploadItem;\n};\n\n/** Controls file upload and tracks its progress.\n *\n * @param upload The upload item to control.\n * @param onChange A function to call when the upload status changes.\n * @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.\n */\nexport class UploadHandle extends Emitter<UploadHandleEvents> {\n public readonly upload: UploadItem;\n\n private task: ProviderUploadTask | null = null;\n private onChange: () => void;\n private terminated = false;\n\n constructor(upload: UploadItem, onChange: () => void) {\n super();\n this.upload = upload;\n this.onChange = onChange;\n }\n\n /** @internal */\n _attachTask(task: ProviderUploadTask): void {\n this.task = task;\n }\n\n /** @internal */\n _setStatus(status: UploadStatus): void {\n if (this.upload.status === status) return;\n this.upload.status = status;\n this.emit(\"statusChange\", this.upload);\n }\n\n /** @internal */\n _reportProgress(bytesTransferred: number, totalBytes: number): void {\n if (this.terminated) return;\n this.upload.bytesTransferred = bytesTransferred;\n this.upload.totalBytes = totalBytes;\n this.upload.progress =\n totalBytes > 0 ? (bytesTransferred / totalBytes) * 100 : 0;\n if (this.upload.status !== \"uploading\") {\n this._setStatus(\"uploading\");\n }\n this.emit(\"progress\", this.upload);\n this.onChange();\n }\n\n /** @internal */\n _reportSuccess(downloadURL: string): void {\n if (this.terminated) return;\n this.terminated = true;\n this.upload.downloadURL = downloadURL;\n this.upload.progress = 100;\n this._setStatus(\"success\");\n this.emit(\"success\", this.upload);\n this.onChange();\n }\n\n /** @internal */\n _reportError(error: Error): void {\n if (this.terminated) return;\n this.terminated = true;\n this.upload.error = error;\n this._setStatus(\"error\");\n this.emit(\"error\", this.upload);\n this.onChange();\n }\n\n cancel(): void {\n if (this.terminated) return;\n this.terminated = true;\n this.task?.cancel();\n this._setStatus(\"canceled\");\n this.emit(\"canceled\", this.upload);\n this.onChange();\n }\n\n pause(): void {\n if (this.terminated) return;\n if (this.upload.status !== \"uploading\") return;\n this.task?.pause?.();\n this._setStatus(\"paused\");\n this.onChange();\n }\n\n resume(): void {\n if (this.terminated) return;\n if (this.upload.status !== \"paused\") return;\n this.task?.resume?.();\n this._setStatus(\"uploading\");\n this.onChange();\n }\n}\n","import { BatchHandle, type BatchOptions } from \"./batch-handle\";\nimport { UploadHandle } from \"./upload-handle\";\n\nimport type { StorageProvider } from \"../providers/provider\";\nimport type { FileMetadata } from \"../types/metadata\";\nimport type { UploadOptions } from \"../types/provider\";\nimport type { StorageState, UploadItem } from \"../types/upload\";\n\n/**\n * Manages file uploads and storage queries.\n *\n * You can react in two ways: call `.on(...)` on each {@link UploadHandle} / {@link BatchHandle}, **or** call `subscribe` to get the same lists\n * that {@link StorageManager.getState} returns whenever anything changes (call the returned function to stop listening).\n *\n */\nexport class StorageManager {\n private provider: StorageProvider;\n private uploadHandles: UploadHandle[] = [];\n private batches: BatchHandle[] = [];\n private changeListeners = new Set<(state: StorageState) => void>();\n private cachedState: StorageState | null = null;\n\n constructor(provider: StorageProvider) {\n this.provider = provider;\n }\n\n /** Current `uploads` and `batches` state. */\n getState = (): StorageState => {\n if (this.cachedState === null) {\n this.cachedState = {\n uploads: this.uploadHandles.map((h) => h.upload),\n batches: this.batches.map((b) => b.snapshot()),\n };\n }\n return this.cachedState;\n };\n\n /** Runs `listener` after each change; returns a function — call it to unsubscribe. */\n subscribe = (listener: (state: StorageState) => void): (() => void) => {\n this.changeListeners.add(listener);\n return () => {\n this.changeListeners.delete(listener);\n };\n };\n\n /** Returns `true` if the object exists. Returns `false` only for not-found; other errors are thrown. */\n exists(path: string): Promise<boolean> {\n return this.provider.exists(path);\n }\n\n /** Returns metadata for the object at `path`. Throws if the object does not exist. */\n getMetadata(path: string): Promise<FileMetadata> {\n return this.provider.getMetadata(path);\n }\n\n /** Returns a download URL for the object at `path`. */\n getDownloadURL(path: string): Promise<string> {\n return this.provider.getDownloadURL(path);\n }\n\n /** Deletes the object at `path`. */\n delete(path: string): Promise<void> {\n return this.provider.delete(path);\n }\n\n /** Starts the file upload.`options.path` is the object path in storage (see {@link UploadOptions}).\n *\n * @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.\n */\n uploadFile(file: File, options: UploadOptions): UploadHandle {\n const handle = this.createHandle(file, { path: options.path });\n this.uploadHandles.push(handle);\n this.notifyChange();\n this.startUpload(handle, options);\n return handle;\n }\n\n /**\n * Upload several files as one batch. The optional third argument sets how many run at once\n * and what happens when one file fails — see {@link BatchOptions}.\n *\n * @remarks\n * `optionsFor` picks storage options per file (same as `uploadFile`); index matches `files` order.\n * If `continueOnError` is `false`, the first failure cancels the other files and the batch fires `error`.\n * If it stays `true` (default), other files keep going; when every file has finished, the batch fires `success`.\n *\n */\n uploadFiles(\n files: File[],\n optionsFor: (file: File, index: number) => UploadOptions,\n batchOptions: BatchOptions = {},\n ): BatchHandle {\n const id = crypto.randomUUID();\n const handles = files.map((file, i) => {\n const options = optionsFor(file, i);\n return this.createHandle(file, { batchId: id, path: options.path });\n });\n\n this.uploadHandles.push(...handles);\n\n const batch = new BatchHandle({\n id,\n uploads: handles,\n options: batchOptions,\n startNext: (handle) => {\n this.startUpload(handle, { path: handle.upload.path });\n },\n onChange: () => this.notifyChange(),\n });\n\n this.batches.push(batch);\n this.notifyChange();\n batch._start();\n return batch;\n }\n\n private createHandle(\n file: File,\n options: { path: string; batchId?: string },\n ): UploadHandle {\n const upload: UploadItem = {\n id: crypto.randomUUID(),\n file,\n path: options.path,\n progress: 0,\n bytesTransferred: 0,\n totalBytes: file.size,\n status: \"queued\",\n ...(options.batchId !== undefined ? { batchId: options.batchId } : {}),\n };\n return new UploadHandle(upload, () => this.notifyChange());\n }\n\n private startUpload(handle: UploadHandle, options: UploadOptions): void {\n handle._setStatus(\"uploading\");\n const task = this.provider.upload(handle.upload.file, options, {\n onProgress: (bytesTransferred, totalBytes) =>\n handle._reportProgress(bytesTransferred, totalBytes),\n onError: (error) => handle._reportError(error),\n onSuccess: (downloadURL) => handle._reportSuccess(downloadURL),\n });\n handle._attachTask(task);\n this.notifyChange();\n }\n\n private notifyChange(): void {\n this.cachedState = null;\n const state = this.getState();\n for (const listener of this.changeListeners) {\n listener(state);\n }\n }\n}\n","import {\n deleteObject,\n getDownloadURL,\n getMetadata,\n ref,\n uploadBytesResumable,\n type FirebaseStorage,\n type StorageError,\n} from \"firebase/storage\";\n\nimport type { FileMetadata } from \"../types/metadata\";\nimport type {\n ProviderUploadCallbacks,\n ProviderUploadTask,\n UploadOptions,\n} from \"../types/provider\";\nimport type { StorageProvider } from \"./provider\";\n\nexport class FirebaseStorageProvider implements StorageProvider {\n constructor(private storage: FirebaseStorage) {}\n\n upload(\n file: File,\n\n options: UploadOptions,\n\n callbacks: ProviderUploadCallbacks,\n ): ProviderUploadTask {\n const storageRef = ref(this.storage, options.path);\n\n const task = uploadBytesResumable(storageRef, file);\n\n task.on(\n \"state_changed\",\n\n (snapshot) => {\n callbacks.onProgress(\n snapshot.bytesTransferred,\n\n snapshot.totalBytes,\n );\n },\n\n (error) => {\n callbacks.onError(error);\n },\n\n async () => {\n try {\n const downloadURL = await getDownloadURL(storageRef);\n\n callbacks.onSuccess(downloadURL);\n } catch (error) {\n callbacks.onError(error as Error);\n }\n },\n );\n\n return {\n cancel: () => {\n task.cancel();\n },\n\n pause: () => {\n task.pause();\n },\n\n resume: () => {\n task.resume();\n },\n };\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n await getMetadata(ref(this.storage, path));\n return true;\n } catch (err) {\n if ((err as StorageError).code === \"storage/object-not-found\") {\n return false;\n }\n throw err;\n }\n }\n\n async getMetadata(path: string): Promise<FileMetadata> {\n const meta = await getMetadata(ref(this.storage, path));\n return {\n path,\n size: meta.size,\n contentType: meta.contentType,\n createdAt: new Date(meta.timeCreated),\n updatedAt: new Date(meta.updated),\n customMetadata: meta.customMetadata,\n };\n }\n\n getDownloadURL(path: string): Promise<string> {\n return getDownloadURL(ref(this.storage, path));\n }\n\n async delete(path: string): Promise<void> {\n await deleteObject(ref(this.storage, path));\n }\n}\n","import type { FirebaseStorage } from \"firebase/storage\";\n\nimport { StorageManager as BaseStorageManager } from \"./core/storage-manager\";\nimport { FirebaseStorageProvider } from \"./providers/firebase-provider\";\n\n/** Firebase Storage uploads and file helpers. Pass a `FirebaseStorage` instance from `getStorage(app)`. */\nexport class StorageManager extends BaseStorageManager {\n constructor(storage: FirebaseStorage) {\n super(new FirebaseStorageProvider(storage));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/emitter.ts","../src/core/batch-handle.ts","../src/core/retry.ts","../src/core/upload-handle.ts","../src/core/storage-manager.ts","../src/providers/firebase-provider.ts","../src/firebase-storage-manager.ts"],"names":["StorageManager"],"mappings":"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","file":"index.js","sourcesContent":["type Listener<TPayload> = (payload: TPayload) => void;\n\nexport class Emitter<TEvents extends Record<string, unknown>> {\n private listeners: { [K in keyof TEvents]?: Set<Listener<TEvents[K]>> } = {};\n\n /**\n * @param event The event to listen for.\n * @param listener The function to call when the event is emitted.\n * @returns A function to unsubscribe from the event.\n */\n on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>) {\n if (!this.listeners[event]) {\n this.listeners[event] = new Set();\n }\n\n this.listeners[event]!.add(listener);\n\n return () => {\n this.listeners[event]?.delete(listener);\n };\n }\n\n /**\n * @param event The event to emit.\n * @param payload The payload to emit.\n */\n protected emit<K extends keyof TEvents>(event: K, payload: TEvents[K]) {\n const listeners = this.listeners[event];\n\n if (!listeners) {\n return;\n }\n\n for (const listener of listeners) {\n listener(payload);\n }\n }\n}\n","import type { UploadBatch, UploadItem } from \"../types/upload\";\nimport { Emitter } from \"./emitter\";\nimport type { UploadHandle } from \"./upload-handle\";\n\n/**\n * Events on a batch from {@link StorageManager.uploadFiles}. `progress` / `change` carry a {@link UploadBatch} snapshot.\n * `uploadSuccess` and `uploadError` fire once per child. When all children are done: `success` (normal end) or `error` (only if `continueOnError` was `false` and one failed).\n */\nexport type BatchHandleEvents = {\n progress: UploadBatch;\n uploadSuccess: UploadItem;\n uploadError: UploadItem;\n success: UploadBatch;\n error: UploadBatch;\n change: UploadBatch;\n};\n\n/** Settings for {@link StorageManager.uploadFiles} (third argument). */\nexport interface BatchOptions {\n /** How many files upload at the same time. Default is 3. */\n concurrency?: number;\n /**\n * `true` (default): if one file fails, the others keep going. When every file has finished, the batch fires `success` — look at each upload for errors.\n *\n * `false`: the first failure stops the other files and the batch fires `error`.\n */\n continueOnError?: boolean;\n}\n\nexport interface BatchHandleInit {\n id: string;\n uploads: UploadHandle[];\n options: BatchOptions;\n startNext: (handle: UploadHandle) => void;\n onChange: () => void;\n}\n\n/**\n * A group of files from {@link StorageManager.uploadFiles}. Use `.on(...)` like a single {@link UploadHandle}, plus `snapshot()` for totals.\n */\nexport class BatchHandle extends Emitter<BatchHandleEvents> {\n public readonly id: string;\n public readonly uploads: UploadHandle[];\n\n private concurrency: number;\n private continueOnError: boolean;\n private startNext: (handle: UploadHandle) => void;\n private onChange: () => void;\n\n private running = 0;\n private nextIdx = 0;\n /** Indices that were started via {@link fillSlots} and still hold a concurrency slot. */\n private activeSlots = new Set<number>();\n private settledCount = 0;\n private succeededCount = 0;\n private failedCount = 0;\n private failedFast = false;\n private canceled = false;\n private paused = false;\n private terminalEmitted = false;\n\n // Stable view of UploadItems; the items themselves mutate in place, but the\n // array reference never changes, so we allocate it once instead of rebuilding\n // it on every snapshot.\n private readonly uploadsView: UploadItem[];\n\n // Running aggregates updated incrementally on each child event so snapshot()\n // is O(1) instead of O(n) per progress tick.\n private aggregateBytesTransferred = 0;\n private aggregateTotalBytes = 0;\n private readonly lastBytes: number[];\n private readonly lastTotal: number[];\n\n constructor(init: BatchHandleInit) {\n super();\n this.id = init.id;\n this.uploads = init.uploads;\n this.concurrency = Math.max(1, init.options.concurrency ?? 3);\n this.continueOnError = init.options.continueOnError ?? true;\n this.startNext = init.startNext;\n this.onChange = init.onChange;\n\n this.uploadsView = init.uploads.map((h) => h.upload);\n this.lastBytes = new Array(init.uploads.length).fill(0);\n this.lastTotal = new Array(init.uploads.length).fill(0);\n for (let i = 0; i < this.uploadsView.length; i++) {\n const u = this.uploadsView[i]!;\n this.lastBytes[i] = u.bytesTransferred;\n this.lastTotal[i] = u.totalBytes;\n this.aggregateBytesTransferred += u.bytesTransferred;\n this.aggregateTotalBytes += u.totalBytes;\n }\n\n this.uploads.forEach((child, idx) => {\n child.on(\"progress\", (u) => this.handleChildProgress(idx, u));\n child.on(\"success\", (u) => this.handleChildSettled(idx, u, \"success\"));\n child.on(\"error\", (u) => this.handleChildSettled(idx, u, \"error\"));\n child.on(\"canceled\", (u) => this.handleChildSettled(idx, u, \"canceled\"));\n });\n }\n\n /** @internal */\n _start(): void {\n this.fillSlots();\n }\n\n cancel(): void {\n if (this.canceled || this.terminalEmitted) return;\n this.canceled = true;\n for (const h of this.uploads) {\n const s = h.upload.status;\n if (\n s === \"queued\" ||\n s === \"uploading\" ||\n s === \"retrying\" ||\n s === \"paused\"\n ) {\n h.cancel();\n }\n }\n this.onChange();\n }\n\n pause(): void {\n if (this.paused || this.canceled || this.terminalEmitted) return;\n this.paused = true;\n for (const h of this.uploads) {\n if (h.upload.status === \"uploading\" || h.upload.status === \"retrying\") {\n h.pause();\n }\n }\n this.onChange();\n }\n\n resume(): void {\n if (!this.paused || this.canceled || this.terminalEmitted) return;\n this.paused = false;\n for (const h of this.uploads) {\n if (h.upload.status === \"paused\") h.resume();\n }\n this.fillSlots();\n this.onChange();\n }\n\n snapshot(): UploadBatch {\n const totalProgress =\n this.aggregateTotalBytes > 0\n ? (this.aggregateBytesTransferred / this.aggregateTotalBytes) * 100\n : 0;\n return {\n id: this.id,\n uploads: this.uploadsView,\n totalProgress,\n completedCount: this.succeededCount,\n failedCount: this.failedCount,\n };\n }\n\n private updateAggregate(idx: number, u: UploadItem): void {\n const prevBytes = this.lastBytes[idx] ?? 0;\n const prevTotal = this.lastTotal[idx] ?? 0;\n this.aggregateBytesTransferred += u.bytesTransferred - prevBytes;\n this.aggregateTotalBytes += u.totalBytes - prevTotal;\n this.lastBytes[idx] = u.bytesTransferred;\n this.lastTotal[idx] = u.totalBytes;\n }\n\n private fillSlots(): void {\n while (\n !this.canceled &&\n !this.paused &&\n !this.failedFast &&\n this.running < this.concurrency &&\n this.nextIdx < this.uploads.length\n ) {\n const idx = this.nextIdx++;\n const handle = this.uploads[idx];\n if (!handle || handle.upload.status !== \"queued\") continue;\n this.activeSlots.add(idx);\n this.running++;\n this.startNext(handle);\n }\n }\n\n private handleChildProgress(idx: number, upload: UploadItem): void {\n this.updateAggregate(idx, upload);\n const snap = this.snapshot();\n this.emit(\"progress\", snap);\n this.emit(\"change\", snap);\n }\n\n private handleChildSettled(\n idx: number,\n upload: UploadItem,\n kind: \"success\" | \"error\" | \"canceled\",\n ): void {\n this.updateAggregate(idx, upload);\n if (this.activeSlots.delete(idx)) {\n this.running = Math.max(0, this.running - 1);\n }\n this.settledCount++;\n\n if (kind === \"success\") {\n this.succeededCount++;\n this.emit(\"uploadSuccess\", upload);\n } else if (kind === \"error\") {\n this.failedCount++;\n this.emit(\"uploadError\", upload);\n }\n\n const snap = this.snapshot();\n this.emit(\"change\", snap);\n\n if (\n kind === \"error\" &&\n !this.continueOnError &&\n !this.failedFast &&\n !this.canceled\n ) {\n this.failedFast = true;\n for (const h of this.uploads) {\n if (h.upload.id === upload.id) continue;\n const s = h.upload.status;\n if (\n s === \"queued\" ||\n s === \"uploading\" ||\n s === \"retrying\" ||\n s === \"paused\"\n ) {\n h.cancel();\n }\n }\n this.terminalEmitted = true;\n this.emit(\"error\", snap);\n this.onChange();\n return;\n }\n\n if (this.failedFast || this.canceled) {\n // we call this because a task can canceled as seen above\n // by h.cancel(). Which then triggers the handelchildSettled call above.\n this.onChange();\n return;\n }\n\n this.fillSlots();\n\n if (this.settledCount >= this.uploads.length && !this.terminalEmitted) {\n this.terminalEmitted = true;\n this.emit(\"success\", snap);\n }\n\n this.onChange();\n }\n}\n","import type { RetryOptions } from \"../types/provider\";\n\nexport interface ResolvedRetryOptions {\n maxRetries: number;\n initialDelayMs: number;\n maxDelayMs: number;\n jitter: boolean;\n isRetryable?: (error: Error) => boolean;\n}\n\nexport const DEFAULT_RETRY_OPTIONS: ResolvedRetryOptions = {\n maxRetries: 3,\n initialDelayMs: 1000,\n maxDelayMs: 30000,\n jitter: true,\n};\n\nconst NON_RETRYABLE_CODES = new Set([\n \"storage/canceled\",\n \"storage/unauthorized\",\n \"storage/unauthenticated\",\n \"storage/quota-exceeded\",\n \"storage/invalid-argument\",\n \"storage/invalid-argument-count\",\n \"storage/invalid-url\",\n \"storage/invalid-default-bucket\",\n \"storage/no-default-bucket\",\n \"storage/bucket-not-found\",\n \"storage/project-not-found\",\n \"storage/invalid-root-operation\",\n \"storage/invalid-event-name\",\n \"storage/invalid-format\",\n \"storage/no-download-url\",\n \"storage/unauthorized-app\",\n \"storage/unsupported-environment\",\n]);\n\nconst RETRYABLE_CODES = new Set([\n \"storage/retry-limit-exceeded\",\n \"storage/unknown\",\n \"storage/invalid-checksum\",\n \"storage/cannot-slice-blob\",\n \"storage/server-file-wrong-size\",\n]);\n\n/** Resolves upload retry settings. Returns `null` when retries are disabled. */\nexport function resolveRetryOptions(\n retry?: RetryOptions | false,\n): ResolvedRetryOptions | null {\n if (retry === false) return null;\n return {\n ...DEFAULT_RETRY_OPTIONS,\n ...retry,\n };\n}\n\n/** Reads a Firebase-style `code` from an error without importing Firebase types. */\nexport function getStorageErrorCode(error: Error): string | undefined {\n return (error as { code?: string }).code;\n}\n\n/** Whether an upload error should trigger another attempt. */\nexport function isRetryableStorageError(\n error: Error,\n options: ResolvedRetryOptions | null,\n): boolean {\n if (!options) return false;\n\n if (options.isRetryable) {\n return options.isRetryable(error);\n }\n\n const code = getStorageErrorCode(error);\n if (code) {\n if (NON_RETRYABLE_CODES.has(code)) return false;\n if (RETRYABLE_CODES.has(code)) return true;\n return false;\n }\n\n return true;\n}\n\n/** Computes backoff delay after a failed attempt (1-based). */\nexport function computeRetryDelay(\n failedAttempt: number,\n options: ResolvedRetryOptions,\n): number {\n const base = Math.min(\n options.maxDelayMs,\n options.initialDelayMs * 2 ** (failedAttempt - 1),\n );\n if (!options.jitter) return base;\n return Math.round(base * (0.5 + Math.random() * 0.5));\n}\n","import { Emitter } from \"./emitter\";\n\nimport type { ProviderUploadTask } from \"../types/provider\";\nimport type { UploadItem, UploadStatus } from \"../types/upload\";\n\nexport interface UploadRetryEvent {\n upload: UploadItem;\n error: Error;\n attempt: number;\n maxAttempts: number;\n delayMs: number;\n}\n\nexport interface UploadRetryBackoffDetails {\n attempt: number;\n maxAttempts: number;\n delayMs: number;\n error: Error;\n}\n\nexport interface UploadControlHooks {\n abort: () => void;\n pauseDuringRetry?: () => void;\n resumeDuringRetry?: () => void;\n}\n\nexport type UploadHandleEvents = {\n progress: UploadItem;\n success: UploadItem;\n error: UploadItem;\n canceled: UploadItem;\n statusChange: UploadItem;\n retry: UploadRetryEvent;\n};\n\n/** Controls file upload and tracks its progress.\n *\n * @param upload The upload item to control.\n * @param onChange A function to call when the upload status changes.\n * @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.\n */\nexport class UploadHandle extends Emitter<UploadHandleEvents> {\n public readonly upload: UploadItem;\n\n private task: ProviderUploadTask | null = null;\n private onChange: () => void;\n private terminated = false;\n private controlHooks: UploadControlHooks | null = null;\n private pausedDuringRetry = false;\n\n constructor(upload: UploadItem, onChange: () => void) {\n super();\n this.upload = upload;\n this.onChange = onChange;\n }\n\n /** @internal */\n _attachTask(task: ProviderUploadTask): void {\n this.task = task;\n }\n\n /** @internal */\n _registerControlHooks(hooks: UploadControlHooks): void {\n this.controlHooks = hooks;\n }\n\n /** @internal */\n _clearControlHooks(): void {\n this.controlHooks = null;\n }\n\n /** @internal */\n _setStatus(status: UploadStatus): void {\n if (this.upload.status === status) return;\n this.upload.status = status;\n this.emit(\"statusChange\", this.upload);\n }\n\n /** @internal */\n _reportProgress(bytesTransferred: number, totalBytes: number): void {\n if (this.terminated) return;\n this.upload.bytesTransferred = bytesTransferred;\n this.upload.totalBytes = totalBytes;\n this.upload.progress =\n totalBytes > 0 ? (bytesTransferred / totalBytes) * 100 : 0;\n if (this.upload.status !== \"uploading\") {\n this._setStatus(\"uploading\");\n }\n this.emit(\"progress\", this.upload);\n this.onChange();\n }\n\n /** @internal */\n _prepareRetryAttempt(attempt: number): void {\n if (this.terminated) return;\n this.upload.retryAttempt = attempt;\n this.upload.bytesTransferred = 0;\n this.upload.progress = 0;\n this.upload.error = undefined;\n this.pausedDuringRetry = false;\n this._setStatus(\"uploading\");\n this.onChange();\n }\n\n /** @internal */\n _enterRetryBackoff(details: UploadRetryBackoffDetails): void {\n if (this.terminated) return;\n this.upload.retryAttempt = details.attempt;\n this.upload.error = details.error;\n this.task = null;\n this._setStatus(\"retrying\");\n this.emit(\"retry\", {\n upload: this.upload,\n error: details.error,\n attempt: details.attempt,\n maxAttempts: details.maxAttempts,\n delayMs: details.delayMs,\n });\n this.onChange();\n }\n\n /** @internal */\n _reportSuccess(downloadURL: string): void {\n if (this.terminated) return;\n this.terminated = true;\n this.upload.downloadURL = downloadURL;\n this.upload.progress = 100;\n this._clearControlHooks();\n this._setStatus(\"success\");\n this.emit(\"success\", this.upload);\n this.onChange();\n }\n\n /** @internal */\n _reportError(error: Error): void {\n if (this.terminated) return;\n this.terminated = true;\n this.upload.error = error;\n this._clearControlHooks();\n this._setStatus(\"error\");\n this.emit(\"error\", this.upload);\n this.onChange();\n }\n\n cancel(): void {\n if (this.terminated) return;\n this.terminated = true;\n this.controlHooks?.abort();\n this.task?.cancel();\n this._clearControlHooks();\n this._setStatus(\"canceled\");\n this.emit(\"canceled\", this.upload);\n this.onChange();\n }\n\n pause(): void {\n if (this.terminated) return;\n if (this.upload.status === \"uploading\") {\n this.task?.pause?.();\n this._setStatus(\"paused\");\n this.onChange();\n return;\n }\n if (this.upload.status === \"retrying\") {\n this.pausedDuringRetry = true;\n this.controlHooks?.pauseDuringRetry?.();\n this._setStatus(\"paused\");\n this.onChange();\n }\n }\n\n resume(): void {\n if (this.terminated) return;\n if (this.upload.status !== \"paused\") return;\n\n if (this.pausedDuringRetry) {\n this.pausedDuringRetry = false;\n this.controlHooks?.resumeDuringRetry?.();\n this.onChange();\n return;\n }\n\n this.task?.resume?.();\n this._setStatus(\"uploading\");\n this.onChange();\n }\n}\n","import { BatchHandle, type BatchOptions } from \"./batch-handle\";\nimport {\n computeRetryDelay,\n isRetryableStorageError,\n resolveRetryOptions,\n} from \"./retry\";\nimport { UploadHandle } from \"./upload-handle\";\n\nimport type { StorageProvider } from \"../providers/provider\";\nimport type { FileMetadata } from \"../types/metadata\";\nimport type { ProviderUploadTask, UploadOptions } from \"../types/provider\";\nimport type { StorageState, UploadItem } from \"../types/upload\";\n\n/**\n * Manages file uploads and storage queries.\n *\n * You can react in two ways: call `.on(...)` on each {@link UploadHandle} / {@link BatchHandle}, **or** call `subscribe` to get the same lists\n * that {@link StorageManager.getState} returns whenever anything changes (call the returned function to stop listening).\n *\n */\nexport class StorageManager {\n private provider: StorageProvider;\n private uploadHandles: UploadHandle[] = [];\n private batches: BatchHandle[] = [];\n private changeListeners = new Set<(state: StorageState) => void>();\n private cachedState: StorageState | null = null;\n private uploadOptionsByHandleId = new Map<string, UploadOptions>();\n\n constructor(provider: StorageProvider) {\n this.provider = provider;\n }\n\n /** Current `uploads` and `batches` state. */\n getState = (): StorageState => {\n if (this.cachedState === null) {\n this.cachedState = {\n uploads: this.uploadHandles.map((h) => h.upload),\n batches: this.batches.map((b) => b.snapshot()),\n };\n }\n return this.cachedState;\n };\n\n /** Runs `listener` after each change; returns a function — call it to unsubscribe. */\n subscribe = (listener: (state: StorageState) => void): (() => void) => {\n this.changeListeners.add(listener);\n return () => {\n this.changeListeners.delete(listener);\n };\n };\n\n /** Returns `true` if the object exists. Returns `false` only for not-found; other errors are thrown. */\n exists(path: string): Promise<boolean> {\n return this.provider.exists(path);\n }\n\n /** Returns metadata for the object at `path`. Throws if the object does not exist. */\n getMetadata(path: string): Promise<FileMetadata> {\n return this.provider.getMetadata(path);\n }\n\n /** Returns a download URL for the object at `path`. */\n getDownloadURL(path: string): Promise<string> {\n return this.provider.getDownloadURL(path);\n }\n\n /** Deletes the object at `path`. */\n delete(path: string): Promise<void> {\n return this.provider.delete(path);\n }\n\n /** Starts the file upload.`options.path` is the object path in storage (see {@link UploadOptions}).\n *\n * @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.\n */\n uploadFile(file: File, options: UploadOptions): UploadHandle {\n const handle = this.createHandle(file, { path: options.path });\n this.uploadOptionsByHandleId.set(handle.upload.id, options);\n this.uploadHandles.push(handle);\n this.notifyChange();\n this.startUpload(handle, options);\n return handle;\n }\n\n /**\n * Upload several files as one batch. The optional third argument sets how many run at once\n * and what happens when one file fails — see {@link BatchOptions}.\n *\n * @remarks\n * `optionsFor` picks storage options per file (same as `uploadFile`); index matches `files` order.\n * If `continueOnError` is `false`, the first failure cancels the other files and the batch fires `error`.\n * If it stays `true` (default), other files keep going; when every file has finished, the batch fires `success`.\n *\n */\n uploadFiles(\n files: File[],\n optionsFor: (file: File, index: number) => UploadOptions,\n batchOptions: BatchOptions = {},\n ): BatchHandle {\n const id = crypto.randomUUID();\n const handles = files.map((file, i) => {\n const options = optionsFor(file, i);\n const handle = this.createHandle(file, {\n batchId: id,\n path: options.path,\n });\n this.uploadOptionsByHandleId.set(handle.upload.id, options);\n return handle;\n });\n\n this.uploadHandles.push(...handles);\n\n const batch = new BatchHandle({\n id,\n uploads: handles,\n options: batchOptions,\n startNext: (handle) => {\n const options = this.uploadOptionsByHandleId.get(handle.upload.id) ?? {\n path: handle.upload.path,\n };\n this.startUpload(handle, options);\n },\n onChange: () => this.notifyChange(),\n });\n\n this.batches.push(batch);\n this.notifyChange();\n batch._start();\n return batch;\n }\n\n private createHandle(\n file: File,\n options: { path: string; batchId?: string },\n ): UploadHandle {\n const upload: UploadItem = {\n id: crypto.randomUUID(),\n file,\n path: options.path,\n progress: 0,\n bytesTransferred: 0,\n totalBytes: file.size,\n status: \"queued\",\n ...(options.batchId !== undefined ? { batchId: options.batchId } : {}),\n };\n return new UploadHandle(upload, () => this.notifyChange());\n }\n\n private startUpload(handle: UploadHandle, options: UploadOptions): void {\n const retryOptions = resolveRetryOptions(options.retry);\n const maxAttempts = retryOptions ? retryOptions.maxRetries + 1 : 1;\n\n let attempt = 0;\n let retryTimeout: ReturnType<typeof setTimeout> | null = null;\n let currentTask: ProviderUploadTask | null = null;\n let aborted = false;\n let pausedDuringRetry = false;\n let pendingRetryError: Error | null = null;\n\n const clearRetryTimeout = (): void => {\n if (retryTimeout !== null) {\n clearTimeout(retryTimeout);\n retryTimeout = null;\n }\n };\n\n const abort = (): void => {\n aborted = true;\n clearRetryTimeout();\n currentTask?.cancel();\n currentTask = null;\n pendingRetryError = null;\n };\n\n const scheduleRetry = (error: Error, delayMs: number): void => {\n if (aborted) return;\n\n pendingRetryError = error;\n handle._enterRetryBackoff({\n attempt: attempt + 1,\n maxAttempts,\n delayMs,\n error,\n });\n\n retryTimeout = setTimeout(() => {\n retryTimeout = null;\n if (aborted || pausedDuringRetry) return;\n pendingRetryError = null;\n runAttempt();\n }, delayMs);\n };\n\n const runAttempt = (): void => {\n if (aborted) return;\n\n attempt += 1;\n handle._prepareRetryAttempt(attempt);\n\n currentTask = this.provider.upload(handle.upload.file, options, {\n onProgress: (bytesTransferred, totalBytes) =>\n handle._reportProgress(bytesTransferred, totalBytes),\n onError: (error) => {\n currentTask = null;\n if (aborted) return;\n\n if (\n attempt < maxAttempts &&\n isRetryableStorageError(error, retryOptions)\n ) {\n const delayMs = retryOptions\n ? computeRetryDelay(attempt, retryOptions)\n : 0;\n scheduleRetry(error, delayMs);\n return;\n }\n\n handle._reportError(error);\n },\n onSuccess: (downloadURL) => {\n currentTask = null;\n if (aborted) return;\n handle._reportSuccess(downloadURL);\n },\n });\n handle._attachTask(currentTask);\n };\n\n handle._registerControlHooks({\n abort,\n pauseDuringRetry: () => {\n pausedDuringRetry = true;\n clearRetryTimeout();\n },\n resumeDuringRetry: () => {\n if (aborted) return;\n pausedDuringRetry = false;\n if (pendingRetryError) {\n scheduleRetry(pendingRetryError, 0);\n }\n },\n });\n\n runAttempt();\n }\n\n private notifyChange(): void {\n this.cachedState = null;\n const state = this.getState();\n for (const listener of this.changeListeners) {\n listener(state);\n }\n }\n}\n","import {\n deleteObject,\n getDownloadURL,\n getMetadata,\n ref,\n uploadBytesResumable,\n type FirebaseStorage,\n type StorageError,\n} from \"firebase/storage\";\n\nimport type { FileMetadata } from \"../types/metadata\";\nimport type {\n ProviderUploadCallbacks,\n ProviderUploadTask,\n UploadOptions,\n} from \"../types/provider\";\nimport type { StorageProvider } from \"./provider\";\n\nexport class FirebaseStorageProvider implements StorageProvider {\n constructor(private storage: FirebaseStorage) {}\n\n upload(\n file: File,\n\n options: UploadOptions,\n\n callbacks: ProviderUploadCallbacks,\n ): ProviderUploadTask {\n const storageRef = ref(this.storage, options.path);\n\n const task = uploadBytesResumable(storageRef, file);\n\n task.on(\n \"state_changed\",\n\n (snapshot) => {\n callbacks.onProgress(\n snapshot.bytesTransferred,\n\n snapshot.totalBytes,\n );\n },\n\n (error) => {\n callbacks.onError(error);\n },\n\n async () => {\n try {\n const downloadURL = await getDownloadURL(storageRef);\n\n callbacks.onSuccess(downloadURL);\n } catch (error) {\n callbacks.onError(error as Error);\n }\n },\n );\n\n return {\n cancel: () => {\n task.cancel();\n },\n\n pause: () => {\n task.pause();\n },\n\n resume: () => {\n task.resume();\n },\n };\n }\n\n async exists(path: string): Promise<boolean> {\n try {\n await getMetadata(ref(this.storage, path));\n return true;\n } catch (err) {\n if ((err as StorageError).code === \"storage/object-not-found\") {\n return false;\n }\n throw err;\n }\n }\n\n async getMetadata(path: string): Promise<FileMetadata> {\n const meta = await getMetadata(ref(this.storage, path));\n return {\n path,\n size: meta.size,\n contentType: meta.contentType,\n createdAt: new Date(meta.timeCreated),\n updatedAt: new Date(meta.updated),\n customMetadata: meta.customMetadata,\n };\n }\n\n getDownloadURL(path: string): Promise<string> {\n return getDownloadURL(ref(this.storage, path));\n }\n\n async delete(path: string): Promise<void> {\n await deleteObject(ref(this.storage, path));\n }\n}\n","import type { FirebaseStorage } from \"firebase/storage\";\n\nimport { StorageManager as BaseStorageManager } from \"./core/storage-manager\";\nimport { FirebaseStorageProvider } from \"./providers/firebase-provider\";\n\n/** Firebase Storage uploads and file helpers. Pass a `FirebaseStorage` instance from `getStorage(app)`. */\nexport class StorageManager extends BaseStorageManager {\n constructor(storage: FirebaseStorage) {\n super(new FirebaseStorageProvider(storage));\n }\n}\n"]}
|