firebase-storage-kit 0.0.1 → 0.0.2
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 +11 -8
- package/dist/firebase.d.ts +1 -1
- package/dist/firebase.js.map +1 -1
- package/dist/index.d.ts +52 -8
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/{provider-xU7lonvB.d.ts → provider-DXcRr9uA.d.ts} +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
# firebase-storage-kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Upload manager for Firebase Storage with progress, cancellation, batch uploads, and configurable concurrency.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
+
With npm:
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
10
|
npm install firebase-storage-kit
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
Or with Yarn, pnpm, or Bun:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn add firebase-storage-kit
|
|
17
|
+
pnpm add firebase-storage-kit
|
|
18
|
+
bun add firebase-storage-kit
|
|
19
|
+
```
|
|
20
|
+
|
|
11
21
|
## Quick start
|
|
12
22
|
|
|
13
23
|
```ts
|
|
@@ -51,13 +61,6 @@ const unsubscribe = manager.subscribe((state) => {
|
|
|
51
61
|
});
|
|
52
62
|
```
|
|
53
63
|
|
|
54
|
-
## Entry points
|
|
55
|
-
|
|
56
|
-
| Import | Use |
|
|
57
|
-
| ------------------------------- | ----------------------------------------------------- |
|
|
58
|
-
| `firebase-storage-kit` | `UploadManager`, `UploadHandle`, `BatchHandle`, types |
|
|
59
|
-
| `firebase-storage-kit/firebase` | `FirebaseStorageProvider` |
|
|
60
|
-
|
|
61
64
|
## License
|
|
62
65
|
|
|
63
66
|
MIT
|
package/dist/firebase.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FirebaseStorage } from 'firebase/storage';
|
|
2
|
-
import { S as StorageProvider, U as UploadOptions, P as ProviderUploadCallbacks, a as ProviderUploadTask } from './provider-
|
|
2
|
+
import { S as StorageProvider, U as UploadOptions, P as ProviderUploadCallbacks, a as ProviderUploadTask } from './provider-DXcRr9uA.js';
|
|
3
3
|
|
|
4
4
|
declare class FirebaseStorageProvider implements StorageProvider {
|
|
5
5
|
private storage;
|
package/dist/firebase.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/firebase-provider.ts"],"names":[],"mappings":";;;AAcO,IAAM,0BAAN,MAAyD;AAAA,EAC9D,YAAoB,OAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAA2B;AAAA,EAA3B,OAAA;AAAA,EAEpB,MAAA,CACE,IAAA,EAEA,OAAA,EAEA,SAAA,EACoB;AACpB,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS,QAAQ,IAAI,CAAA;AAEjD,IAAA,MAAM,IAAA,GAAO,oBAAA,CAAqB,UAAA,EAAY,IAAI,CAAA;AAElD,IAAA,IAAA,CAAK,EAAA;AAAA,MACH,eAAA;AAAA,MAEA,CAAC,QAAA,KAAa;AACZ,QAAA,SAAA,CAAU,UAAA;AAAA,UACR,QAAA,CAAS,gBAAA;AAAA,UAET,QAAA,CAAS;AAAA,SACX;AAAA,MACF,CAAA;AAAA,MAEA,CAAC,KAAA,KAAU;AACT,QAAA,SAAA,CAAU,QAAQ,KAAK,CAAA;AAAA,MACzB,CAAA;AAAA,MAEA,YAAY;AACV,QAAA,IAAI;AACF,UAAA,MAAM,WAAA,GAAc,MAAM,cAAA,CAAe,UAAU,CAAA;AAEnD,UAAA,SAAA,CAAU,UAAU,WAAW,CAAA;AAAA,QACjC,SAAS,KAAA,EAAO;AACd,UAAA,SAAA,CAAU,QAAQ,KAAc,CAAA;AAAA,QAClC;AAAA,MACF;AAAA,KACF;AAEA,IAAA,OAAO;AAAA,MACL,QAAQ,MAAM;AACZ,QAAA,IAAA,CAAK,MAAA,EAAO;AAAA,MACd,CAAA;AAAA,MAEA,OAAO,MAAM;AACX,QAAA,IAAA,CAAK,KAAA,EAAM;AAAA,MACb,CAAA;AAAA,MAEA,QAAQ,MAAM;AACZ,QAAA,IAAA,CAAK,MAAA,EAAO;AAAA,MACd;AAAA,KACF;AAAA,EACF;AACF","file":"firebase.js","sourcesContent":["import {\n getDownloadURL,\n ref,\n uploadBytesResumable,\n type FirebaseStorage,\n} from \"firebase/storage\";\n\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/providers/firebase-provider.ts"],"names":[],"mappings":";;;AAcO,IAAM,0BAAN,MAAyD;AAAA,EAC9D,YAAoB,OAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAA2B;AAAA,EAA3B,OAAA;AAAA,EAEpB,MAAA,CACE,IAAA,EAEA,OAAA,EAEA,SAAA,EACoB;AACpB,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS,QAAQ,IAAI,CAAA;AAEjD,IAAA,MAAM,IAAA,GAAO,oBAAA,CAAqB,UAAA,EAAY,IAAI,CAAA;AAElD,IAAA,IAAA,CAAK,EAAA;AAAA,MACH,eAAA;AAAA,MAEA,CAAC,QAAA,KAAa;AACZ,QAAA,SAAA,CAAU,UAAA;AAAA,UACR,QAAA,CAAS,gBAAA;AAAA,UAET,QAAA,CAAS;AAAA,SACX;AAAA,MACF,CAAA;AAAA,MAEA,CAAC,KAAA,KAAU;AACT,QAAA,SAAA,CAAU,QAAQ,KAAK,CAAA;AAAA,MACzB,CAAA;AAAA,MAEA,YAAY;AACV,QAAA,IAAI;AACF,UAAA,MAAM,WAAA,GAAc,MAAM,cAAA,CAAe,UAAU,CAAA;AAEnD,UAAA,SAAA,CAAU,UAAU,WAAW,CAAA;AAAA,QACjC,SAAS,KAAA,EAAO;AACd,UAAA,SAAA,CAAU,QAAQ,KAAc,CAAA;AAAA,QAClC;AAAA,MACF;AAAA,KACF;AAEA,IAAA,OAAO;AAAA,MACL,QAAQ,MAAM;AACZ,QAAA,IAAA,CAAK,MAAA,EAAO;AAAA,MACd,CAAA;AAAA,MAEA,OAAO,MAAM;AACX,QAAA,IAAA,CAAK,KAAA,EAAM;AAAA,MACb,CAAA;AAAA,MAEA,QAAQ,MAAM;AACZ,QAAA,IAAA,CAAK,MAAA,EAAO;AAAA,MACd;AAAA,KACF;AAAA,EACF;AACF","file":"firebase.js","sourcesContent":["import {\n getDownloadURL,\n ref,\n uploadBytesResumable,\n type FirebaseStorage,\n} from \"firebase/storage\";\n\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"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { S as StorageProvider, U as UploadOptions } from './provider-
|
|
2
|
-
export { P as ProviderUploadCallbacks, a as ProviderUploadTask } from './provider-
|
|
1
|
+
import { S as StorageProvider, U as UploadOptions } from './provider-DXcRr9uA.js';
|
|
2
|
+
export { P as ProviderUploadCallbacks, a as ProviderUploadTask } from './provider-DXcRr9uA.js';
|
|
3
3
|
|
|
4
|
+
/** The current state of the upload manager. */
|
|
4
5
|
interface UploadState {
|
|
5
6
|
uploads: UploadItem[];
|
|
6
7
|
batches: UploadBatch[];
|
|
@@ -28,7 +29,16 @@ interface UploadBatch {
|
|
|
28
29
|
type Listener<TPayload> = (payload: TPayload) => void;
|
|
29
30
|
declare class Emitter<TEvents extends Record<string, unknown>> {
|
|
30
31
|
private listeners;
|
|
32
|
+
/**
|
|
33
|
+
* @param event The event to listen for.
|
|
34
|
+
* @param listener The function to call when the event is emitted.
|
|
35
|
+
* @returns A function to unsubscribe from the event.
|
|
36
|
+
*/
|
|
31
37
|
on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>): () => void;
|
|
38
|
+
/**
|
|
39
|
+
* @param event The event to emit.
|
|
40
|
+
* @param payload The payload to emit.
|
|
41
|
+
*/
|
|
32
42
|
protected emit<K extends keyof TEvents>(event: K, payload: TEvents[K]): void;
|
|
33
43
|
}
|
|
34
44
|
|
|
@@ -39,6 +49,12 @@ type UploadHandleEvents = {
|
|
|
39
49
|
canceled: UploadItem;
|
|
40
50
|
statusChange: UploadItem;
|
|
41
51
|
};
|
|
52
|
+
/** Controls file upload and tracks its progress.
|
|
53
|
+
*
|
|
54
|
+
* @param upload The upload item to control.
|
|
55
|
+
* @param onChange A function to call when the upload status changes.
|
|
56
|
+
* @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.
|
|
57
|
+
*/
|
|
42
58
|
declare class UploadHandle extends Emitter<UploadHandleEvents> {
|
|
43
59
|
readonly upload: UploadItem;
|
|
44
60
|
private task;
|
|
@@ -50,6 +66,10 @@ declare class UploadHandle extends Emitter<UploadHandleEvents> {
|
|
|
50
66
|
resume(): void;
|
|
51
67
|
}
|
|
52
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Events on a batch from {@link UploadManager.uploadFiles}. `progress` / `change` carry a {@link UploadBatch} snapshot.
|
|
71
|
+
* `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).
|
|
72
|
+
*/
|
|
53
73
|
type BatchHandleEvents = {
|
|
54
74
|
progress: UploadBatch;
|
|
55
75
|
uploadSuccess: UploadItem;
|
|
@@ -58,16 +78,14 @@ type BatchHandleEvents = {
|
|
|
58
78
|
error: UploadBatch;
|
|
59
79
|
change: UploadBatch;
|
|
60
80
|
};
|
|
81
|
+
/** Settings for {@link UploadManager.uploadFiles} (third argument). */
|
|
61
82
|
interface BatchOptions {
|
|
62
|
-
/**
|
|
83
|
+
/** How many files upload at the same time. Default is 3. */
|
|
63
84
|
concurrency?: number;
|
|
64
85
|
/**
|
|
65
|
-
*
|
|
66
|
-
* still emits `success` once all children have settled and callers inspect
|
|
67
|
-
* individual `UploadItem` errors.
|
|
86
|
+
* `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.
|
|
68
87
|
*
|
|
69
|
-
*
|
|
70
|
-
* and the batch emits `error`.
|
|
88
|
+
* `false`: the first failure stops the other files and the batch fires `error`.
|
|
71
89
|
*/
|
|
72
90
|
continueOnError?: boolean;
|
|
73
91
|
}
|
|
@@ -78,6 +96,9 @@ interface BatchHandleInit {
|
|
|
78
96
|
startNext: (handle: UploadHandle) => void;
|
|
79
97
|
onChange: () => void;
|
|
80
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* A group of files from {@link UploadManager.uploadFiles}. Use `.on(...)` like a single {@link UploadHandle}, plus `snapshot()` for totals.
|
|
101
|
+
*/
|
|
81
102
|
declare class BatchHandle extends Emitter<BatchHandleEvents> {
|
|
82
103
|
readonly id: string;
|
|
83
104
|
readonly uploads: UploadHandle[];
|
|
@@ -110,6 +131,13 @@ declare class BatchHandle extends Emitter<BatchHandleEvents> {
|
|
|
110
131
|
private handleChildSettled;
|
|
111
132
|
}
|
|
112
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Uploads files and tracks each upload or batch.
|
|
136
|
+
*
|
|
137
|
+
* You can react in two ways: call `.on(...)` on each {@link UploadHandle} / {@link BatchHandle}, **or** call `subscribe` to get the same lists
|
|
138
|
+
* that {@link UploadManager.getState} returns whenever anything changes (call the returned function to stop listening).
|
|
139
|
+
*
|
|
140
|
+
*/
|
|
113
141
|
declare class UploadManager {
|
|
114
142
|
private provider;
|
|
115
143
|
private uploadHandles;
|
|
@@ -117,9 +145,25 @@ declare class UploadManager {
|
|
|
117
145
|
private changeListeners;
|
|
118
146
|
private cachedState;
|
|
119
147
|
constructor(provider: StorageProvider);
|
|
148
|
+
/** Current `uploads` and `batches` state. */
|
|
120
149
|
getState: () => UploadState;
|
|
150
|
+
/** Runs `listener` after each change; returns a function — call it to unsubscribe. */
|
|
121
151
|
subscribe: (listener: (state: UploadState) => void) => (() => void);
|
|
152
|
+
/** Starts the file upload.`options.path` is the object path in storage (see {@link UploadOptions}).
|
|
153
|
+
*
|
|
154
|
+
* @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.
|
|
155
|
+
*/
|
|
122
156
|
uploadFile(file: File, options: UploadOptions): UploadHandle;
|
|
157
|
+
/**
|
|
158
|
+
* Upload several files as one batch. The optional third argument sets how many run at once
|
|
159
|
+
* and what happens when one file fails — see {@link BatchOptions}.
|
|
160
|
+
*
|
|
161
|
+
* @remarks
|
|
162
|
+
* `optionsFor` picks storage options per file (same as `uploadFile`); index matches `files` order.
|
|
163
|
+
* If `continueOnError` is `false`, the first failure cancels the other files and the batch fires `error`.
|
|
164
|
+
* If it stays `true` (default), other files keep going; when every file has finished, the batch fires `success`.
|
|
165
|
+
*
|
|
166
|
+
*/
|
|
123
167
|
uploadFiles(files: File[], optionsFor: (file: File, index: number) => UploadOptions, batchOptions?: BatchOptions): BatchHandle;
|
|
124
168
|
private createHandle;
|
|
125
169
|
private startUpload;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// src/core/emitter.ts
|
|
2
2
|
var Emitter = class {
|
|
3
3
|
listeners = /* @__PURE__ */ new Map();
|
|
4
|
+
/**
|
|
5
|
+
* @param event The event to listen for.
|
|
6
|
+
* @param listener The function to call when the event is emitted.
|
|
7
|
+
* @returns A function to unsubscribe from the event.
|
|
8
|
+
*/
|
|
4
9
|
on(event, listener) {
|
|
5
10
|
if (!this.listeners.has(event)) {
|
|
6
11
|
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
@@ -10,6 +15,10 @@ var Emitter = class {
|
|
|
10
15
|
this.listeners.get(event)?.delete(listener);
|
|
11
16
|
};
|
|
12
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* @param event The event to emit.
|
|
20
|
+
* @param payload The payload to emit.
|
|
21
|
+
*/
|
|
13
22
|
emit(event, payload) {
|
|
14
23
|
const listeners = this.listeners.get(event);
|
|
15
24
|
if (!listeners) {
|
|
@@ -263,6 +272,7 @@ var UploadManager = class {
|
|
|
263
272
|
constructor(provider) {
|
|
264
273
|
this.provider = provider;
|
|
265
274
|
}
|
|
275
|
+
/** Current `uploads` and `batches` state. */
|
|
266
276
|
getState = () => {
|
|
267
277
|
if (this.cachedState === null) {
|
|
268
278
|
this.cachedState = {
|
|
@@ -272,12 +282,17 @@ var UploadManager = class {
|
|
|
272
282
|
}
|
|
273
283
|
return this.cachedState;
|
|
274
284
|
};
|
|
285
|
+
/** Runs `listener` after each change; returns a function — call it to unsubscribe. */
|
|
275
286
|
subscribe = (listener) => {
|
|
276
287
|
this.changeListeners.add(listener);
|
|
277
288
|
return () => {
|
|
278
289
|
this.changeListeners.delete(listener);
|
|
279
290
|
};
|
|
280
291
|
};
|
|
292
|
+
/** Starts the file upload.`options.path` is the object path in storage (see {@link UploadOptions}).
|
|
293
|
+
*
|
|
294
|
+
* @returns An {@link UploadHandle} to control the upload: `pause`, `resume`, `cancel`, and listen for progress with `.on`.
|
|
295
|
+
*/
|
|
281
296
|
uploadFile(file, options) {
|
|
282
297
|
const handle = this.createHandle(file);
|
|
283
298
|
this.uploadHandles.push(handle);
|
|
@@ -285,6 +300,16 @@ var UploadManager = class {
|
|
|
285
300
|
this.startUpload(handle, options);
|
|
286
301
|
return handle;
|
|
287
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Upload several files as one batch. The optional third argument sets how many run at once
|
|
305
|
+
* and what happens when one file fails — see {@link BatchOptions}.
|
|
306
|
+
*
|
|
307
|
+
* @remarks
|
|
308
|
+
* `optionsFor` picks storage options per file (same as `uploadFile`); index matches `files` order.
|
|
309
|
+
* If `continueOnError` is `false`, the first failure cancels the other files and the batch fires `error`.
|
|
310
|
+
* If it stays `true` (default), other files keep going; when every file has finished, the batch fires `success`.
|
|
311
|
+
*
|
|
312
|
+
*/
|
|
288
313
|
uploadFiles(files, optionsFor, batchOptions = {}) {
|
|
289
314
|
const id = crypto.randomUUID();
|
|
290
315
|
const handles = files.map((file) => this.createHandle(file, id));
|
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/upload-manager.ts"],"names":[],"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 = new Map<keyof TEvents, Set<Listener<any>>>();\n\n on<K extends keyof TEvents>(event: K, listener: Listener<TEvents[K]>) {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n\n this.listeners.get(event)?.add(listener);\n\n return () => {\n this.listeners.get(event)?.delete(listener);\n };\n }\n\n protected emit<K extends keyof TEvents>(event: K, payload: TEvents[K]) {\n const listeners = this.listeners.get(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\nexport type BatchHandleEvents = {\n progress: UploadBatch;\n uploadSuccess: UploadItem;\n uploadError: UploadItem;\n success: UploadBatch;\n error: UploadBatch;\n change: UploadBatch;\n};\n\nexport interface BatchOptions {\n /** Maximum number of uploads in-flight at the same time. Defaults to 3. */\n concurrency?: number;\n /**\n * When true (default), a failing upload does not affect siblings; the batch\n * still emits `success` once all children have settled and callers inspect\n * individual `UploadItem` errors.\n *\n * When false, the first failure cancels all queued and in-flight siblings\n * and the batch emits `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\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\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 { UploadOptions } from \"../types/provider\";\nimport type { UploadItem, UploadState } from \"../types/upload\";\n\nexport class UploadManager {\n private provider: StorageProvider;\n private uploadHandles: UploadHandle[] = [];\n private batches: BatchHandle[] = [];\n private changeListeners = new Set<(state: UploadState) => void>();\n private cachedState: UploadState | null = null;\n\n constructor(provider: StorageProvider) {\n this.provider = provider;\n }\n\n getState = (): UploadState => {\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 subscribe = (listener: (state: UploadState) => void): (() => void) => {\n this.changeListeners.add(listener);\n return () => {\n this.changeListeners.delete(listener);\n };\n };\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 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"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/emitter.ts","../src/core/batch-handle.ts","../src/core/upload-handle.ts","../src/core/upload-manager.ts"],"names":[],"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 = new Map<keyof TEvents, Set<Listener<any>>>();\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.has(event)) {\n this.listeners.set(event, new Set());\n }\n\n this.listeners.get(event)?.add(listener);\n\n return () => {\n this.listeners.get(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.get(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 UploadManager.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 UploadManager.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 UploadManager.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 { UploadOptions } from \"../types/provider\";\nimport type { UploadItem, UploadState } from \"../types/upload\";\n\n/**\n * Uploads files and tracks each upload or batch.\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 UploadManager.getState} returns whenever anything changes (call the returned function to stop listening).\n *\n */\nexport class UploadManager {\n private provider: StorageProvider;\n private uploadHandles: UploadHandle[] = [];\n private batches: BatchHandle[] = [];\n private changeListeners = new Set<(state: UploadState) => void>();\n private cachedState: UploadState | null = null;\n\n constructor(provider: StorageProvider) {\n this.provider = provider;\n }\n\n /** Current `uploads` and `batches` state. */\n getState = (): UploadState => {\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: UploadState) => void): (() => void) => {\n this.changeListeners.add(listener);\n return () => {\n this.changeListeners.delete(listener);\n };\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-storage-kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Browser upload manager for Firebase Storage with batches, concurrency, and pause/resume.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"firebase",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"bugs": {
|
|
21
21
|
"url": "https://github.com/marknesh/firebase-storage-kit/issues"
|
|
22
22
|
},
|
|
23
|
-
"homepage": "https://github.com/marknesh/firebase-storage-kit
|
|
23
|
+
"homepage": "https://github.com/marknesh/firebase-storage-kit#readme",
|
|
24
24
|
"type": "module",
|
|
25
25
|
"sideEffects": false,
|
|
26
26
|
"main": "./dist/index.js",
|