@uploadista/client-core 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +972 -33
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -4
- package/src/index.ts +2 -0
- package/src/managers/__tests__/event-subscription-manager.test.ts +566 -0
- package/src/managers/__tests__/upload-manager.test.ts +588 -0
- package/src/managers/event-subscription-manager.ts +280 -0
- package/src/managers/flow-manager.ts +614 -0
- package/src/managers/index.ts +28 -0
- package/src/managers/upload-manager.ts +353 -0
- package/src/services/service-container.ts +213 -1
- package/src/testing/index.ts +16 -0
- package/src/testing/mock-service-container.ts +629 -0
- package/src/types/flow-upload-options.ts +29 -4
- package/src/types/index.ts +1 -0
- package/src/types/upload-metrics.ts +130 -0
- package/src/types/upload-options.ts +17 -1
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import type { UploadFile } from "@uploadista/core/types";
|
|
2
|
+
import type { UploadOptions } from "../types/upload-options";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Upload status representing the current state of an upload
|
|
6
|
+
*/
|
|
7
|
+
export type UploadStatus =
|
|
8
|
+
| "idle"
|
|
9
|
+
| "uploading"
|
|
10
|
+
| "success"
|
|
11
|
+
| "error"
|
|
12
|
+
| "aborted";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Complete upload state
|
|
16
|
+
*/
|
|
17
|
+
export interface UploadState {
|
|
18
|
+
/** Current status of the upload */
|
|
19
|
+
status: UploadStatus;
|
|
20
|
+
/** Upload progress percentage (0-100) */
|
|
21
|
+
progress: number;
|
|
22
|
+
/** Number of bytes uploaded */
|
|
23
|
+
bytesUploaded: number;
|
|
24
|
+
/** Total bytes to upload, null if unknown/deferred */
|
|
25
|
+
totalBytes: number | null;
|
|
26
|
+
/** Error if upload failed */
|
|
27
|
+
error: Error | null;
|
|
28
|
+
/** Result if upload succeeded */
|
|
29
|
+
result: UploadFile | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Callbacks that UploadManager invokes during the upload lifecycle
|
|
34
|
+
*/
|
|
35
|
+
export interface UploadManagerCallbacks {
|
|
36
|
+
/**
|
|
37
|
+
* Called when the upload state changes
|
|
38
|
+
*/
|
|
39
|
+
onStateChange: (state: UploadState) => void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Called when upload progress updates
|
|
43
|
+
* @param uploadId - The unique identifier for this upload
|
|
44
|
+
* @param bytesUploaded - Number of bytes uploaded
|
|
45
|
+
* @param totalBytes - Total bytes to upload, null if unknown
|
|
46
|
+
*/
|
|
47
|
+
onProgress?: (
|
|
48
|
+
uploadId: string,
|
|
49
|
+
bytesUploaded: number,
|
|
50
|
+
totalBytes: number | null,
|
|
51
|
+
) => void;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Called when a chunk completes
|
|
55
|
+
* @param chunkSize - Size of the completed chunk
|
|
56
|
+
* @param bytesAccepted - Total bytes accepted so far
|
|
57
|
+
* @param bytesTotal - Total bytes to upload, null if unknown
|
|
58
|
+
*/
|
|
59
|
+
onChunkComplete?: (
|
|
60
|
+
chunkSize: number,
|
|
61
|
+
bytesAccepted: number,
|
|
62
|
+
bytesTotal: number | null,
|
|
63
|
+
) => void;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Called when upload completes successfully
|
|
67
|
+
* @param result - The uploaded file result
|
|
68
|
+
*/
|
|
69
|
+
onSuccess?: (result: UploadFile) => void;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Called when upload fails with an error
|
|
73
|
+
* @param error - The error that occurred
|
|
74
|
+
*/
|
|
75
|
+
onError?: (error: Error) => void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Called when upload is aborted
|
|
79
|
+
*/
|
|
80
|
+
onAbort?: () => void;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generic upload input type - can be any value that the upload client accepts
|
|
85
|
+
*/
|
|
86
|
+
export type UploadInput = unknown;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Abort controller interface for canceling uploads
|
|
90
|
+
*/
|
|
91
|
+
export interface UploadAbortController {
|
|
92
|
+
abort: () => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Upload function that performs the actual upload.
|
|
97
|
+
* Returns a promise that resolves to an abort controller.
|
|
98
|
+
*/
|
|
99
|
+
export type UploadFunction<
|
|
100
|
+
TInput = UploadInput,
|
|
101
|
+
TOptions extends UploadOptions = UploadOptions,
|
|
102
|
+
> = (input: TInput, options: TOptions) => Promise<UploadAbortController>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Initial state for a new upload
|
|
106
|
+
*/
|
|
107
|
+
const initialState: UploadState = {
|
|
108
|
+
status: "idle",
|
|
109
|
+
progress: 0,
|
|
110
|
+
bytesUploaded: 0,
|
|
111
|
+
totalBytes: null,
|
|
112
|
+
error: null,
|
|
113
|
+
result: null,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Platform-agnostic upload manager that handles upload state machine,
|
|
118
|
+
* progress tracking, error handling, abort, reset, and retry logic.
|
|
119
|
+
*
|
|
120
|
+
* Framework packages (React, Vue, React Native) should wrap this manager
|
|
121
|
+
* with framework-specific hooks/composables.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* const uploadFn = (input, options) => client.upload(input, options);
|
|
126
|
+
* const manager = new UploadManager(uploadFn, {
|
|
127
|
+
* onStateChange: (state) => setState(state),
|
|
128
|
+
* onProgress: (progress) => console.log(`${progress}%`),
|
|
129
|
+
* onSuccess: (result) => console.log('Upload complete:', result),
|
|
130
|
+
* onError: (error) => console.error('Upload failed:', error),
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* await manager.upload(file);
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export class UploadManager<
|
|
137
|
+
TInput = UploadInput,
|
|
138
|
+
TOptions extends UploadOptions = UploadOptions,
|
|
139
|
+
> {
|
|
140
|
+
private state: UploadState;
|
|
141
|
+
private abortController: UploadAbortController | null = null;
|
|
142
|
+
private lastInput: TInput | null = null;
|
|
143
|
+
private uploadId: string | null = null;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Create a new UploadManager
|
|
147
|
+
*
|
|
148
|
+
* @param uploadFn - Upload function to use for uploads
|
|
149
|
+
* @param callbacks - Callbacks to invoke during upload lifecycle
|
|
150
|
+
* @param options - Upload configuration options
|
|
151
|
+
*/
|
|
152
|
+
constructor(
|
|
153
|
+
private readonly uploadFn: UploadFunction<TInput, TOptions>,
|
|
154
|
+
private readonly callbacks: UploadManagerCallbacks,
|
|
155
|
+
private readonly options?: TOptions,
|
|
156
|
+
) {
|
|
157
|
+
this.state = { ...initialState };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get the current upload state
|
|
162
|
+
*/
|
|
163
|
+
getState(): UploadState {
|
|
164
|
+
return { ...this.state };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check if an upload is currently active
|
|
169
|
+
*/
|
|
170
|
+
isUploading(): boolean {
|
|
171
|
+
return this.state.status === "uploading";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if the upload can be retried
|
|
176
|
+
*/
|
|
177
|
+
canRetry(): boolean {
|
|
178
|
+
return (
|
|
179
|
+
(this.state.status === "error" || this.state.status === "aborted") &&
|
|
180
|
+
this.lastInput !== null
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Update the internal state and notify callbacks
|
|
186
|
+
*/
|
|
187
|
+
private updateState(update: Partial<UploadState>): void {
|
|
188
|
+
this.state = { ...this.state, ...update };
|
|
189
|
+
this.callbacks.onStateChange(this.state);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Start uploading a file or input
|
|
194
|
+
*
|
|
195
|
+
* @param input - File or input to upload (type depends on platform)
|
|
196
|
+
*/
|
|
197
|
+
async upload(input: TInput): Promise<void> {
|
|
198
|
+
// Determine totalBytes from input if possible (File/Blob on browser platforms)
|
|
199
|
+
let totalBytes: number | null = null;
|
|
200
|
+
if (input && typeof input === "object") {
|
|
201
|
+
if ("size" in input && typeof input.size === "number") {
|
|
202
|
+
totalBytes = input.size;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Reset state but keep reference for retries
|
|
207
|
+
this.updateState({
|
|
208
|
+
status: "uploading",
|
|
209
|
+
progress: 0,
|
|
210
|
+
bytesUploaded: 0,
|
|
211
|
+
totalBytes,
|
|
212
|
+
error: null,
|
|
213
|
+
result: null,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
this.lastInput = input;
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
// Build complete options with our callbacks
|
|
220
|
+
const uploadOptions = {
|
|
221
|
+
...this.options,
|
|
222
|
+
onProgress: (
|
|
223
|
+
uploadId: string,
|
|
224
|
+
bytesUploaded: number,
|
|
225
|
+
bytes: number | null,
|
|
226
|
+
) => {
|
|
227
|
+
// Store uploadId on first progress callback
|
|
228
|
+
if (!this.uploadId) {
|
|
229
|
+
this.uploadId = uploadId;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const progressPercent = bytes
|
|
233
|
+
? Math.round((bytesUploaded / bytes) * 100)
|
|
234
|
+
: 0;
|
|
235
|
+
|
|
236
|
+
this.updateState({
|
|
237
|
+
progress: progressPercent,
|
|
238
|
+
bytesUploaded,
|
|
239
|
+
totalBytes: bytes,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
this.callbacks.onProgress?.(uploadId, bytesUploaded, bytes);
|
|
243
|
+
this.options?.onProgress?.(uploadId, bytesUploaded, bytes);
|
|
244
|
+
},
|
|
245
|
+
onChunkComplete: (
|
|
246
|
+
chunkSize: number,
|
|
247
|
+
bytesAccepted: number,
|
|
248
|
+
bytesTotal: number | null,
|
|
249
|
+
) => {
|
|
250
|
+
this.callbacks.onChunkComplete?.(
|
|
251
|
+
chunkSize,
|
|
252
|
+
bytesAccepted,
|
|
253
|
+
bytesTotal,
|
|
254
|
+
);
|
|
255
|
+
this.options?.onChunkComplete?.(chunkSize, bytesAccepted, bytesTotal);
|
|
256
|
+
},
|
|
257
|
+
onSuccess: (result: UploadFile) => {
|
|
258
|
+
this.updateState({
|
|
259
|
+
status: "success",
|
|
260
|
+
result,
|
|
261
|
+
progress: 100,
|
|
262
|
+
bytesUploaded: result.size || 0,
|
|
263
|
+
totalBytes: result.size || null,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
this.callbacks.onSuccess?.(result);
|
|
267
|
+
this.options?.onSuccess?.(result);
|
|
268
|
+
this.abortController = null;
|
|
269
|
+
},
|
|
270
|
+
onError: (error: Error) => {
|
|
271
|
+
this.updateState({
|
|
272
|
+
status: "error",
|
|
273
|
+
error,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
this.callbacks.onError?.(error);
|
|
277
|
+
this.options?.onError?.(error);
|
|
278
|
+
this.abortController = null;
|
|
279
|
+
},
|
|
280
|
+
onAbort: () => {
|
|
281
|
+
this.updateState({
|
|
282
|
+
status: "aborted",
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
this.callbacks.onAbort?.();
|
|
286
|
+
this.options?.onAbort?.();
|
|
287
|
+
this.abortController = null;
|
|
288
|
+
},
|
|
289
|
+
onShouldRetry: this.options?.onShouldRetry,
|
|
290
|
+
} as TOptions;
|
|
291
|
+
|
|
292
|
+
// Start the upload
|
|
293
|
+
this.abortController = await this.uploadFn(input, uploadOptions);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
// Handle errors from upload initiation
|
|
296
|
+
const uploadError =
|
|
297
|
+
error instanceof Error ? error : new Error(String(error));
|
|
298
|
+
this.updateState({
|
|
299
|
+
status: "error",
|
|
300
|
+
error: uploadError,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
this.callbacks.onError?.(uploadError);
|
|
304
|
+
this.options?.onError?.(uploadError);
|
|
305
|
+
this.abortController = null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Abort the current upload
|
|
311
|
+
*/
|
|
312
|
+
abort(): void {
|
|
313
|
+
if (this.abortController) {
|
|
314
|
+
this.abortController.abort();
|
|
315
|
+
// Note: State update happens in onAbort callback
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Reset the upload state to idle
|
|
321
|
+
*/
|
|
322
|
+
reset(): void {
|
|
323
|
+
if (this.abortController) {
|
|
324
|
+
this.abortController.abort();
|
|
325
|
+
this.abortController = null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
this.state = { ...initialState };
|
|
329
|
+
this.lastInput = null;
|
|
330
|
+
this.uploadId = null;
|
|
331
|
+
this.callbacks.onStateChange(this.state);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Retry the last failed or aborted upload
|
|
336
|
+
*/
|
|
337
|
+
retry(): void {
|
|
338
|
+
if (this.canRetry() && this.lastInput !== null) {
|
|
339
|
+
this.upload(this.lastInput);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Clean up resources (call when disposing the manager)
|
|
345
|
+
*/
|
|
346
|
+
cleanup(): void {
|
|
347
|
+
if (this.abortController) {
|
|
348
|
+
this.abortController.abort();
|
|
349
|
+
this.abortController = null;
|
|
350
|
+
}
|
|
351
|
+
this.uploadId = null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -7,18 +7,230 @@ import type { IdGenerationService } from "./id-generation-service";
|
|
|
7
7
|
import type { PlatformService } from "./platform-service";
|
|
8
8
|
import type { StorageService } from "./storage-service";
|
|
9
9
|
import type { WebSocketFactory } from "./websocket-service";
|
|
10
|
+
|
|
10
11
|
/**
|
|
11
|
-
* Service container for dependency injection
|
|
12
|
+
* Service container for dependency injection in the Uploadista client.
|
|
13
|
+
*
|
|
14
|
+
* This container provides all platform-specific services needed by the upload client.
|
|
15
|
+
* Different platforms (browser, React Native, Node.js) provide their own implementations
|
|
16
|
+
* of these services to handle platform-specific APIs and behaviors.
|
|
17
|
+
*
|
|
18
|
+
* @template UploadInput - The type of input accepted by the file reader (e.g., File, Blob, string path)
|
|
19
|
+
*
|
|
20
|
+
* @example Browser implementation
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const services: ServiceContainer<File | Blob> = {
|
|
23
|
+
* storage: new LocalStorageService(),
|
|
24
|
+
* idGeneration: new BrowserIdGenerationService(),
|
|
25
|
+
* httpClient: new FetchHttpClient(),
|
|
26
|
+
* fileReader: new BrowserFileReaderService(),
|
|
27
|
+
* base64: new BrowserBase64Service(),
|
|
28
|
+
* websocket: new BrowserWebSocketFactory(),
|
|
29
|
+
* abortController: new BrowserAbortControllerFactory(),
|
|
30
|
+
* platform: new BrowserPlatformService(),
|
|
31
|
+
* checksumService: new WebCryptoChecksumService(),
|
|
32
|
+
* fingerprintService: new BrowserFingerprintService(),
|
|
33
|
+
* };
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example React Native implementation
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const services: ServiceContainer<FilePickResult> = {
|
|
39
|
+
* storage: new AsyncStorageService(),
|
|
40
|
+
* idGeneration: new ReactNativeIdGenerationService(),
|
|
41
|
+
* httpClient: new FetchHttpClient(),
|
|
42
|
+
* fileReader: new ReactNativeFileReaderService(),
|
|
43
|
+
* websocket: new ReactNativeWebSocketFactory(),
|
|
44
|
+
* abortController: new ReactNativeAbortControllerFactory(),
|
|
45
|
+
* platform: new ReactNativePlatformService(),
|
|
46
|
+
* checksumService: new ReactNativeChecksumService(),
|
|
47
|
+
* fingerprintService: new ReactNativeFingerprintService(),
|
|
48
|
+
* };
|
|
49
|
+
* ```
|
|
12
50
|
*/
|
|
13
51
|
export interface ServiceContainer<UploadInput> {
|
|
52
|
+
/**
|
|
53
|
+
* Storage service for persisting upload state and metadata.
|
|
54
|
+
*
|
|
55
|
+
* **Required**: Yes
|
|
56
|
+
*
|
|
57
|
+
* **Used for**:
|
|
58
|
+
* - Storing upload progress for resumption
|
|
59
|
+
* - Caching upload fingerprints
|
|
60
|
+
* - Persisting partial upload state across sessions
|
|
61
|
+
*
|
|
62
|
+
* **Platform implementations**:
|
|
63
|
+
* - Browser: `localStorage`, `IndexedDB`
|
|
64
|
+
* - React Native: `AsyncStorage`, `MMKV`
|
|
65
|
+
* - Node.js: File system, Redis
|
|
66
|
+
*/
|
|
14
67
|
storage: StorageService;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* ID generation service for creating unique identifiers.
|
|
71
|
+
*
|
|
72
|
+
* **Required**: Yes
|
|
73
|
+
*
|
|
74
|
+
* **Used for**:
|
|
75
|
+
* - Generating upload IDs
|
|
76
|
+
* - Creating request correlation IDs
|
|
77
|
+
* - Generating chunk identifiers
|
|
78
|
+
*
|
|
79
|
+
* **Platform implementations**:
|
|
80
|
+
* - Browser: `crypto.randomUUID()` or fallback
|
|
81
|
+
* - React Native: UUID libraries
|
|
82
|
+
* - Node.js: `crypto.randomUUID()`
|
|
83
|
+
*/
|
|
15
84
|
idGeneration: IdGenerationService;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* HTTP client for making upload requests.
|
|
88
|
+
*
|
|
89
|
+
* **Required**: Yes
|
|
90
|
+
*
|
|
91
|
+
* **Used for**:
|
|
92
|
+
* - Uploading file chunks
|
|
93
|
+
* - Making API calls to the upload server
|
|
94
|
+
* - Fetching upload metadata
|
|
95
|
+
*
|
|
96
|
+
* **Platform implementations**:
|
|
97
|
+
* - Browser: `fetch()` with connection pooling
|
|
98
|
+
* - React Native: `fetch()` or `XMLHttpRequest`
|
|
99
|
+
* - Node.js: `node:http`, `node:https`, or libraries like `undici`
|
|
100
|
+
*
|
|
101
|
+
* **Important**: Should support connection pooling for optimal performance
|
|
102
|
+
*/
|
|
16
103
|
httpClient: HttpClient;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* File reader service for reading file contents.
|
|
107
|
+
*
|
|
108
|
+
* **Required**: Yes
|
|
109
|
+
*
|
|
110
|
+
* **Used for**:
|
|
111
|
+
* - Reading file data for upload
|
|
112
|
+
* - Slicing files into chunks
|
|
113
|
+
* - Computing file checksums and fingerprints
|
|
114
|
+
*
|
|
115
|
+
* **Platform implementations**:
|
|
116
|
+
* - Browser: `FileReader` API, `Blob.slice()`
|
|
117
|
+
* - React Native: Native file system modules, `react-native-fs`
|
|
118
|
+
* - Node.js: `fs.createReadStream()`, `fs.promises.open()`
|
|
119
|
+
*
|
|
120
|
+
* **Generic type**: Accepts platform-specific input types (File, Blob, path string)
|
|
121
|
+
*/
|
|
17
122
|
fileReader: FileReaderService<UploadInput>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Base64 encoding/decoding service.
|
|
126
|
+
*
|
|
127
|
+
* **Required**: No (optional)
|
|
128
|
+
*
|
|
129
|
+
* **Used for**:
|
|
130
|
+
* - Encoding binary data for transport
|
|
131
|
+
* - Decoding server responses
|
|
132
|
+
* - Optional data transformations
|
|
133
|
+
*
|
|
134
|
+
* **Platform implementations**:
|
|
135
|
+
* - Browser: `btoa()`, `atob()`
|
|
136
|
+
* - React Native: `base64-js` or built-in
|
|
137
|
+
* - Node.js: `Buffer.from().toString('base64')`
|
|
138
|
+
*
|
|
139
|
+
* **Note**: Only needed for specific upload protocols that require base64 encoding
|
|
140
|
+
*/
|
|
18
141
|
base64?: Base64Service;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* WebSocket factory for creating WebSocket connections.
|
|
145
|
+
*
|
|
146
|
+
* **Required**: Yes
|
|
147
|
+
*
|
|
148
|
+
* **Used for**:
|
|
149
|
+
* - Real-time upload progress updates
|
|
150
|
+
* - Server-side event notifications
|
|
151
|
+
* - Live upload status from server
|
|
152
|
+
*
|
|
153
|
+
* **Platform implementations**:
|
|
154
|
+
* - Browser: Native `WebSocket` API
|
|
155
|
+
* - React Native: `react-native-websocket` or polyfills
|
|
156
|
+
* - Node.js: `ws` library
|
|
157
|
+
*
|
|
158
|
+
* **Important**: Must support standard WebSocket protocol
|
|
159
|
+
*/
|
|
19
160
|
websocket: WebSocketFactory;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Abort controller factory for creating cancellation tokens.
|
|
164
|
+
*
|
|
165
|
+
* **Required**: Yes
|
|
166
|
+
*
|
|
167
|
+
* **Used for**:
|
|
168
|
+
* - Aborting in-flight upload requests
|
|
169
|
+
* - Cancelling file read operations
|
|
170
|
+
* - Implementing upload timeout logic
|
|
171
|
+
*
|
|
172
|
+
* **Platform implementations**:
|
|
173
|
+
* - Browser: Native `AbortController` API
|
|
174
|
+
* - React Native: `AbortController` polyfill
|
|
175
|
+
* - Node.js: Native `AbortController` (Node 15+)
|
|
176
|
+
*/
|
|
20
177
|
abortController: AbortControllerFactory;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Platform detection service.
|
|
181
|
+
*
|
|
182
|
+
* **Required**: Yes
|
|
183
|
+
*
|
|
184
|
+
* **Used for**:
|
|
185
|
+
* - Detecting runtime environment (browser, React Native, Node.js)
|
|
186
|
+
* - Applying platform-specific optimizations
|
|
187
|
+
* - Conditional feature availability
|
|
188
|
+
*
|
|
189
|
+
* **Platform implementations**:
|
|
190
|
+
* - Browser: Checks `window`, `document` availability
|
|
191
|
+
* - React Native: Checks `navigator.product === 'ReactNative'`
|
|
192
|
+
* - Node.js: Checks `process` availability
|
|
193
|
+
*/
|
|
21
194
|
platform: PlatformService;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Checksum service for computing file checksums.
|
|
198
|
+
*
|
|
199
|
+
* **Required**: Yes
|
|
200
|
+
*
|
|
201
|
+
* **Used for**:
|
|
202
|
+
* - Verifying upload integrity
|
|
203
|
+
* - Detecting corrupted uploads
|
|
204
|
+
* - Implementing upload deduplication
|
|
205
|
+
*
|
|
206
|
+
* **Platform implementations**:
|
|
207
|
+
* - Browser: `crypto.subtle` Web Crypto API
|
|
208
|
+
* - React Native: Native crypto modules or JavaScript implementations
|
|
209
|
+
* - Node.js: `crypto` module
|
|
210
|
+
*
|
|
211
|
+
* **Common algorithms**: SHA-256, MD5, CRC32
|
|
212
|
+
*/
|
|
22
213
|
checksumService: ChecksumService;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Fingerprint service for generating unique file identifiers.
|
|
217
|
+
*
|
|
218
|
+
* **Required**: Yes
|
|
219
|
+
*
|
|
220
|
+
* **Used for**:
|
|
221
|
+
* - Upload resumption (matching partial uploads)
|
|
222
|
+
* - Deduplication (detecting duplicate files)
|
|
223
|
+
* - Cache key generation
|
|
224
|
+
*
|
|
225
|
+
* **Platform implementations**:
|
|
226
|
+
* - Browser: Combines file metadata (size, name, modified date, first/last bytes)
|
|
227
|
+
* - React Native: Similar to browser but uses platform-specific file APIs
|
|
228
|
+
* - Node.js: File stat info + content sampling
|
|
229
|
+
*
|
|
230
|
+
* **Generic type**: Accepts platform-specific input types
|
|
231
|
+
*
|
|
232
|
+
* **Note**: Fingerprints should be stable (same file = same fingerprint) but
|
|
233
|
+
* fast to compute (avoid reading entire file if possible)
|
|
234
|
+
*/
|
|
23
235
|
fingerprintService: FingerprintService<UploadInput>;
|
|
24
236
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
MockAbortController,
|
|
3
|
+
MockAbortControllerFactory,
|
|
4
|
+
MockBase64Service,
|
|
5
|
+
MockChecksumService,
|
|
6
|
+
MockFileReaderService,
|
|
7
|
+
MockFingerprintService,
|
|
8
|
+
MockHttpClient,
|
|
9
|
+
type MockHttpResponseConfig,
|
|
10
|
+
MockIdGenerationService,
|
|
11
|
+
MockPlatformService,
|
|
12
|
+
MockStorageService,
|
|
13
|
+
MockWebSocket,
|
|
14
|
+
MockWebSocketFactory,
|
|
15
|
+
createMockServiceContainer,
|
|
16
|
+
} from "./mock-service-container";
|