firebase-storage-kit 1.1.0 → 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 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,16 +5,18 @@ 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;
12
+ path: string;
12
13
  progress: number;
13
14
  bytesTransferred: number;
14
15
  totalBytes: number;
15
16
  status: UploadStatus;
16
17
  downloadURL?: string;
17
18
  error?: Error;
19
+ retryAttempt?: number;
18
20
  batchId?: string;
19
21
  }
20
22
  interface UploadBatch {
@@ -41,12 +43,31 @@ declare class Emitter<TEvents extends Record<string, unknown>> {
41
43
  protected emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void;
42
44
  }
43
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
+ }
44
64
  type UploadHandleEvents = {
45
65
  progress: UploadItem;
46
66
  success: UploadItem;
47
67
  error: UploadItem;
48
68
  canceled: UploadItem;
49
69
  statusChange: UploadItem;
70
+ retry: UploadRetryEvent;
50
71
  };
51
72
  /** Controls file upload and tracks its progress.
52
73
  *
@@ -59,6 +80,8 @@ declare class UploadHandle extends Emitter<UploadHandleEvents> {
59
80
  private task;
60
81
  private onChange;
61
82
  private terminated;
83
+ private controlHooks;
84
+ private pausedDuringRetry;
62
85
  constructor(upload: UploadItem, onChange: () => void);
63
86
  cancel(): void;
64
87
  pause(): void;
@@ -107,6 +130,8 @@ declare class BatchHandle extends Emitter<BatchHandleEvents> {
107
130
  private onChange;
108
131
  private running;
109
132
  private nextIdx;
133
+ /** Indices that were started via {@link fillSlots} and still hold a concurrency slot. */
134
+ private activeSlots;
110
135
  private settledCount;
111
136
  private succeededCount;
112
137
  private failedCount;
@@ -140,10 +165,41 @@ interface ProviderUploadCallbacks {
140
165
  onError: (error: Error) => void;
141
166
  onSuccess: (downloadURL: string) => void;
142
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
+ }
143
180
  interface UploadOptions {
144
181
  /** Object path in the bucket (Firebase), e.g. `uploads/photo.jpg`. */
145
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;
146
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;
147
203
 
148
204
  interface FileMetadata {
149
205
  path: string;
@@ -175,6 +231,7 @@ declare class StorageManager$1 {
175
231
  private batches;
176
232
  private changeListeners;
177
233
  private cachedState;
234
+ private uploadOptionsByHandleId;
178
235
  constructor(provider: StorageProvider);
179
236
  /** Current `uploads` and `batches` state. */
180
237
  getState: () => StorageState;
@@ -214,4 +271,4 @@ declare class StorageManager extends StorageManager$1 {
214
271
  constructor(storage: FirebaseStorage);
215
272
  }
216
273
 
217
- 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
@@ -42,6 +42,8 @@ var BatchHandle = class extends Emitter {
42
42
  onChange;
43
43
  running = 0;
44
44
  nextIdx = 0;
45
+ /** Indices that were started via {@link fillSlots} and still hold a concurrency slot. */
46
+ activeSlots = /* @__PURE__ */ new Set();
45
47
  settledCount = 0;
46
48
  succeededCount = 0;
47
49
  failedCount = 0;
@@ -93,7 +95,7 @@ var BatchHandle = class extends Emitter {
93
95
  this.canceled = true;
94
96
  for (const h of this.uploads) {
95
97
  const s = h.upload.status;
96
- if (s === "queued" || s === "uploading" || s === "paused") {
98
+ if (s === "queued" || s === "uploading" || s === "retrying" || s === "paused") {
97
99
  h.cancel();
98
100
  }
99
101
  }
@@ -103,7 +105,9 @@ var BatchHandle = class extends Emitter {
103
105
  if (this.paused || this.canceled || this.terminalEmitted) return;
104
106
  this.paused = true;
105
107
  for (const h of this.uploads) {
106
- if (h.upload.status === "uploading") h.pause();
108
+ if (h.upload.status === "uploading" || h.upload.status === "retrying") {
109
+ h.pause();
110
+ }
107
111
  }
108
112
  this.onChange();
109
113
  }
@@ -136,8 +140,10 @@ var BatchHandle = class extends Emitter {
136
140
  }
137
141
  fillSlots() {
138
142
  while (!this.canceled && !this.paused && !this.failedFast && this.running < this.concurrency && this.nextIdx < this.uploads.length) {
139
- const handle = this.uploads[this.nextIdx++];
140
- if (!handle) continue;
143
+ const idx = this.nextIdx++;
144
+ const handle = this.uploads[idx];
145
+ if (!handle || handle.upload.status !== "queued") continue;
146
+ this.activeSlots.add(idx);
141
147
  this.running++;
142
148
  this.startNext(handle);
143
149
  }
@@ -150,7 +156,9 @@ var BatchHandle = class extends Emitter {
150
156
  }
151
157
  handleChildSettled(idx, upload, kind) {
152
158
  this.updateAggregate(idx, upload);
153
- this.running = Math.max(0, this.running - 1);
159
+ if (this.activeSlots.delete(idx)) {
160
+ this.running = Math.max(0, this.running - 1);
161
+ }
154
162
  this.settledCount++;
155
163
  if (kind === "success") {
156
164
  this.succeededCount++;
@@ -166,7 +174,7 @@ var BatchHandle = class extends Emitter {
166
174
  for (const h of this.uploads) {
167
175
  if (h.upload.id === upload.id) continue;
168
176
  const s = h.upload.status;
169
- if (s === "queued" || s === "uploading" || s === "paused") {
177
+ if (s === "queued" || s === "uploading" || s === "retrying" || s === "paused") {
170
178
  h.cancel();
171
179
  }
172
180
  }
@@ -188,12 +196,79 @@ var BatchHandle = class extends Emitter {
188
196
  }
189
197
  };
190
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
+
191
264
  // src/core/upload-handle.ts
192
265
  var UploadHandle = class extends Emitter {
193
266
  upload;
194
267
  task = null;
195
268
  onChange;
196
269
  terminated = false;
270
+ controlHooks = null;
271
+ pausedDuringRetry = false;
197
272
  constructor(upload, onChange) {
198
273
  super();
199
274
  this.upload = upload;
@@ -204,6 +279,14 @@ var UploadHandle = class extends Emitter {
204
279
  this.task = task;
205
280
  }
206
281
  /** @internal */
282
+ _registerControlHooks(hooks) {
283
+ this.controlHooks = hooks;
284
+ }
285
+ /** @internal */
286
+ _clearControlHooks() {
287
+ this.controlHooks = null;
288
+ }
289
+ /** @internal */
207
290
  _setStatus(status) {
208
291
  if (this.upload.status === status) return;
209
292
  this.upload.status = status;
@@ -222,11 +305,39 @@ var UploadHandle = class extends Emitter {
222
305
  this.onChange();
223
306
  }
224
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 */
225
335
  _reportSuccess(downloadURL) {
226
336
  if (this.terminated) return;
227
337
  this.terminated = true;
228
338
  this.upload.downloadURL = downloadURL;
229
339
  this.upload.progress = 100;
340
+ this._clearControlHooks();
230
341
  this._setStatus("success");
231
342
  this.emit("success", this.upload);
232
343
  this.onChange();
@@ -236,6 +347,7 @@ var UploadHandle = class extends Emitter {
236
347
  if (this.terminated) return;
237
348
  this.terminated = true;
238
349
  this.upload.error = error;
350
+ this._clearControlHooks();
239
351
  this._setStatus("error");
240
352
  this.emit("error", this.upload);
241
353
  this.onChange();
@@ -243,21 +355,37 @@ var UploadHandle = class extends Emitter {
243
355
  cancel() {
244
356
  if (this.terminated) return;
245
357
  this.terminated = true;
358
+ this.controlHooks?.abort();
246
359
  this.task?.cancel();
360
+ this._clearControlHooks();
247
361
  this._setStatus("canceled");
248
362
  this.emit("canceled", this.upload);
249
363
  this.onChange();
250
364
  }
251
365
  pause() {
252
366
  if (this.terminated) return;
253
- if (this.upload.status !== "uploading") return;
254
- this.task?.pause?.();
255
- this._setStatus("paused");
256
- this.onChange();
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
+ }
257
379
  }
258
380
  resume() {
259
381
  if (this.terminated) return;
260
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
+ }
261
389
  this.task?.resume?.();
262
390
  this._setStatus("uploading");
263
391
  this.onChange();
@@ -271,6 +399,7 @@ var StorageManager = class {
271
399
  batches = [];
272
400
  changeListeners = /* @__PURE__ */ new Set();
273
401
  cachedState = null;
402
+ uploadOptionsByHandleId = /* @__PURE__ */ new Map();
274
403
  constructor(provider) {
275
404
  this.provider = provider;
276
405
  }
@@ -312,7 +441,8 @@ var StorageManager = class {
312
441
  * @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.
313
442
  */
314
443
  uploadFile(file, options) {
315
- const handle = this.createHandle(file);
444
+ const handle = this.createHandle(file, { path: options.path });
445
+ this.uploadOptionsByHandleId.set(handle.upload.id, options);
316
446
  this.uploadHandles.push(handle);
317
447
  this.notifyChange();
318
448
  this.startUpload(handle, options);
@@ -330,12 +460,14 @@ var StorageManager = class {
330
460
  */
331
461
  uploadFiles(files, optionsFor, batchOptions = {}) {
332
462
  const id = crypto.randomUUID();
333
- const handles = files.map((file) => this.createHandle(file, id));
334
- const optionsByUploadId = /* @__PURE__ */ new Map();
335
- handles.forEach((h, i) => {
336
- const file = files[i];
337
- if (!file) return;
338
- optionsByUploadId.set(h.upload.id, optionsFor(file, i));
463
+ const handles = files.map((file, i) => {
464
+ const options = optionsFor(file, i);
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;
339
471
  });
340
472
  this.uploadHandles.push(...handles);
341
473
  const batch = new BatchHandle({
@@ -343,9 +475,10 @@ var StorageManager = class {
343
475
  uploads: handles,
344
476
  options: batchOptions,
345
477
  startNext: (handle) => {
346
- const opts = optionsByUploadId.get(handle.upload.id);
347
- if (!opts) return;
348
- this.startUpload(handle, opts);
478
+ const options = this.uploadOptionsByHandleId.get(handle.upload.id) ?? {
479
+ path: handle.upload.path
480
+ };
481
+ this.startUpload(handle, options);
349
482
  },
350
483
  onChange: () => this.notifyChange()
351
484
  });
@@ -354,27 +487,96 @@ var StorageManager = class {
354
487
  batch._start();
355
488
  return batch;
356
489
  }
357
- createHandle(file, batchId) {
490
+ createHandle(file, options) {
358
491
  const upload = {
359
492
  id: crypto.randomUUID(),
360
493
  file,
494
+ path: options.path,
361
495
  progress: 0,
362
496
  bytesTransferred: 0,
363
497
  totalBytes: file.size,
364
498
  status: "queued",
365
- ...batchId !== void 0 ? { batchId } : {}
499
+ ...options.batchId !== void 0 ? { batchId: options.batchId } : {}
366
500
  };
367
501
  return new UploadHandle(upload, () => this.notifyChange());
368
502
  }
369
503
  startUpload(handle, options) {
370
- handle._setStatus("uploading");
371
- const task = this.provider.upload(handle.upload.file, options, {
372
- onProgress: (bytesTransferred, totalBytes) => handle._reportProgress(bytesTransferred, totalBytes),
373
- onError: (error) => handle._reportError(error),
374
- onSuccess: (downloadURL) => handle._reportSuccess(downloadURL)
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
+ }
375
578
  });
376
- handle._attachTask(task);
377
- this.notifyChange();
579
+ runAttempt();
378
580
  }
379
581
  notifyChange() {
380
582
  this.cachedState = null;
@@ -461,6 +663,6 @@ var StorageManager2 = class extends StorageManager {
461
663
  }
462
664
  };
463
665
 
464
- export { BatchHandle, StorageManager2 as StorageManager, UploadHandle };
666
+ export { BatchHandle, DEFAULT_RETRY_OPTIONS, StorageManager2 as StorageManager, UploadHandle, computeRetryDelay, getStorageErrorCode, isRetryableStorageError, resolveRetryOptions };
465
667
  //# sourceMappingURL=index.js.map
466
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 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 handle = this.uploads[this.nextIdx++];\n if (!handle) continue;\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 this.running = Math.max(0, this.running - 1);\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);\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) => this.createHandle(file, id));\n const optionsByUploadId = new Map<string, UploadOptions>();\n handles.forEach((h, i) => {\n const file = files[i];\n if (!file) return;\n optionsByUploadId.set(h.upload.id, optionsFor(file, i));\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 opts = optionsByUploadId.get(handle.upload.id);\n if (!opts) return;\n this.startUpload(handle, opts);\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(file: File, batchId?: string): UploadHandle {\n const upload: UploadItem = {\n id: crypto.randomUUID(),\n file,\n progress: 0,\n bytesTransferred: 0,\n totalBytes: file.size,\n status: \"queued\",\n ...(batchId !== undefined ? { 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 {\n ProviderUploadCallbacks,\n ProviderUploadTask,\n UploadOptions,\n} from \"../types/provider\";\nimport type { FileMetadata } from \"../types/metadata\";\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"]}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "firebase-storage-kit",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Storage manager for Firebase Storage with uploads, progress tracking, batch uploads, and file query helpers.",
5
5
  "keywords": [
6
6
  "firebase",
7
- "firebase-storage",
7
+ "firebase storage",
8
8
  "storage",
9
9
  "upload",
10
10
  "resumable",
@@ -40,6 +40,8 @@
40
40
  "dev": "tsup --watch",
41
41
  "clean": "rm -rf dist",
42
42
  "typecheck": "tsc --noEmit",
43
+ "test": "bun test",
44
+ "test:watch": "bun test --watch",
43
45
  "prepublishOnly": "npm run build"
44
46
  },
45
47
  "peerDependencies": {