@uploadista/client-browser 0.0.3
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/.turbo/turbo-build.log +5 -0
- package/.turbo/turbo-check.log +130 -0
- package/AUTO_CAPABILITIES.md +98 -0
- package/FRAMEWORK_INTEGRATION.md +407 -0
- package/LICENSE +21 -0
- package/README.md +795 -0
- package/SMART_CHUNKING.md +140 -0
- package/dist/client/create-uploadista-client.d.ts +182 -0
- package/dist/client/create-uploadista-client.d.ts.map +1 -0
- package/dist/client/create-uploadista-client.js +76 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +1 -0
- package/dist/framework-utils.d.ts +201 -0
- package/dist/framework-utils.d.ts.map +1 -0
- package/dist/framework-utils.js +282 -0
- package/dist/http-client.d.ts +44 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +489 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/services/abort-controller-factory.d.ts +30 -0
- package/dist/services/abort-controller-factory.d.ts.map +1 -0
- package/dist/services/abort-controller-factory.js +98 -0
- package/dist/services/checksum-service.d.ts +30 -0
- package/dist/services/checksum-service.d.ts.map +1 -0
- package/dist/services/checksum-service.js +44 -0
- package/dist/services/create-browser-services.d.ts +36 -0
- package/dist/services/create-browser-services.d.ts.map +1 -0
- package/dist/services/create-browser-services.js +56 -0
- package/dist/services/file-reader.d.ts +91 -0
- package/dist/services/file-reader.d.ts.map +1 -0
- package/dist/services/file-reader.js +251 -0
- package/dist/services/fingerprint-service.d.ts +41 -0
- package/dist/services/fingerprint-service.d.ts.map +1 -0
- package/dist/services/fingerprint-service.js +64 -0
- package/dist/services/id-generation/id-generation.d.ts +40 -0
- package/dist/services/id-generation/id-generation.d.ts.map +1 -0
- package/dist/services/id-generation/id-generation.js +58 -0
- package/dist/services/platform-service.d.ts +38 -0
- package/dist/services/platform-service.d.ts.map +1 -0
- package/dist/services/platform-service.js +221 -0
- package/dist/services/storage/local-storage-service.d.ts +55 -0
- package/dist/services/storage/local-storage-service.d.ts.map +1 -0
- package/dist/services/storage/local-storage-service.js +178 -0
- package/dist/services/storage/session-storage-service.d.ts +55 -0
- package/dist/services/storage/session-storage-service.d.ts.map +1 -0
- package/dist/services/storage/session-storage-service.js +179 -0
- package/dist/services/websocket-factory.d.ts +46 -0
- package/dist/services/websocket-factory.d.ts.map +1 -0
- package/dist/services/websocket-factory.js +196 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/upload-input.d.ts +26 -0
- package/dist/types/upload-input.d.ts.map +1 -0
- package/dist/types/upload-input.js +1 -0
- package/dist/utils/hash-util.d.ts +60 -0
- package/dist/utils/hash-util.d.ts.map +1 -0
- package/dist/utils/hash-util.js +75 -0
- package/package.json +32 -0
- package/src/client/create-uploadista-client.ts +150 -0
- package/src/client/index.ts +1 -0
- package/src/framework-utils.ts +446 -0
- package/src/http-client.ts +546 -0
- package/src/index.ts +8 -0
- package/src/services/abort-controller-factory.ts +108 -0
- package/src/services/checksum-service.ts +46 -0
- package/src/services/create-browser-services.ts +81 -0
- package/src/services/file-reader.ts +344 -0
- package/src/services/fingerprint-service.ts +67 -0
- package/src/services/id-generation/id-generation.ts +60 -0
- package/src/services/platform-service.ts +231 -0
- package/src/services/storage/local-storage-service.ts +187 -0
- package/src/services/storage/session-storage-service.ts +188 -0
- package/src/services/websocket-factory.ts +212 -0
- package/src/types/index.ts +1 -0
- package/src/types/upload-input.ts +25 -0
- package/src/utils/hash-util.ts +79 -0
- package/tsconfig.json +22 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
# @uploadista/client-browser
|
|
2
|
+
|
|
3
|
+
Browser-optimized upload client for Uploadista with native Web APIs.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@uploadista/client-browser` is the browser-specific implementation of the Uploadista upload client. It provides a complete file upload solution optimized for modern web browsers, leveraging native browser APIs for optimal performance and compatibility.
|
|
8
|
+
|
|
9
|
+
This package extends `@uploadista/client-core` with browser-specific implementations:
|
|
10
|
+
- **Fetch API** for HTTP requests with connection pooling
|
|
11
|
+
- **File API** for reading File and Blob objects
|
|
12
|
+
- **Web Crypto API** for checksums and fingerprints
|
|
13
|
+
- **WebSocket API** for real-time progress updates
|
|
14
|
+
- **LocalStorage API** for upload state persistence
|
|
15
|
+
|
|
16
|
+
The client supports resumable uploads, parallel chunk uploads, progress tracking, and automatic retry with exponential backoff. It's designed to handle files of any size by reading and uploading them in configurable chunks, keeping memory usage low even for multi-gigabyte files.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @uploadista/client-browser
|
|
22
|
+
# or
|
|
23
|
+
pnpm add @uploadista/client-browser
|
|
24
|
+
# or
|
|
25
|
+
yarn add @uploadista/client-browser
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
32
|
+
|
|
33
|
+
// Create the client
|
|
34
|
+
const client = createUploadistaClient({
|
|
35
|
+
endpoint: 'https://api.uploadista.com/upload'
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Get file from input
|
|
39
|
+
const fileInput = document.querySelector<HTMLInputElement>('input[type="file"]');
|
|
40
|
+
const file = fileInput.files[0];
|
|
41
|
+
|
|
42
|
+
// Upload with progress tracking
|
|
43
|
+
const upload = await client.upload(file, {
|
|
44
|
+
onProgress: (event) => {
|
|
45
|
+
console.log(`Progress: ${event.progress}%`);
|
|
46
|
+
console.log(`Speed: ${event.bytesPerSecond / 1024} KB/s`);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
console.log('Upload complete:', upload.id);
|
|
51
|
+
console.log('File URL:', upload.url);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- **Browser-Native APIs** - Uses Fetch, File API, WebSocket, and Web Crypto for optimal performance
|
|
57
|
+
- **Resumable Uploads** - Automatically resume interrupted uploads using fingerprinting
|
|
58
|
+
- **Chunked Uploads** - Upload files in configurable chunks to handle large files efficiently
|
|
59
|
+
- **Connection Pooling** - Optimized HTTP/2 connection reuse with keep-alive
|
|
60
|
+
- **Progress Tracking** - Real-time progress updates with upload speed and ETA
|
|
61
|
+
- **WebSocket Support** - Real-time events for upload and flow processing updates
|
|
62
|
+
- **Automatic Retry** - Configurable retry logic with exponential backoff
|
|
63
|
+
- **LocalStorage Persistence** - Save upload state for resumption across page reloads
|
|
64
|
+
- **TypeScript Support** - Full type safety with TypeScript definitions
|
|
65
|
+
- **Framework Utilities** - Helper types and utilities for React, Vue, and other frameworks
|
|
66
|
+
|
|
67
|
+
## API Reference
|
|
68
|
+
|
|
69
|
+
### createUploadistaClient()
|
|
70
|
+
|
|
71
|
+
Creates a browser-optimized Uploadista client with automatic service configuration.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
function createUploadistaClient(
|
|
75
|
+
options: UploadistaClientOptions
|
|
76
|
+
): UploadistaClient<File | Blob>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Options:**
|
|
80
|
+
|
|
81
|
+
- `endpoint` (string, required) - Upload endpoint URL
|
|
82
|
+
- `chunkSize` (number) - Chunk size in bytes (default: 5MB)
|
|
83
|
+
- `retryDelays` (number[]) - Retry delays in milliseconds (default: [0, 1000, 3000, 5000])
|
|
84
|
+
- `connectionPooling` (ConnectionPoolConfig) - HTTP connection pooling configuration
|
|
85
|
+
- `storeFingerprintForResuming` (boolean) - Enable resumable uploads (default: true)
|
|
86
|
+
- `allowedMetaFields` (string[]) - Allowed metadata field names
|
|
87
|
+
|
|
88
|
+
**Returns:** UploadistaClient configured for browser use
|
|
89
|
+
|
|
90
|
+
**Example:**
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const client = createUploadistaClient({
|
|
94
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
95
|
+
chunkSize: 10 * 1024 * 1024, // 10MB chunks
|
|
96
|
+
retryDelays: [1000, 3000, 5000, 10000],
|
|
97
|
+
connectionPooling: {
|
|
98
|
+
maxConnectionsPerHost: 6,
|
|
99
|
+
enableHttp2: true,
|
|
100
|
+
keepAliveTimeout: 60000
|
|
101
|
+
},
|
|
102
|
+
storeFingerprintForResuming: true,
|
|
103
|
+
allowedMetaFields: ['userId', 'projectId', 'tags']
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### createHttpClient()
|
|
108
|
+
|
|
109
|
+
Creates a Fetch-based HTTP client with connection pooling and metrics tracking.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
function createHttpClient(
|
|
113
|
+
config?: ConnectionPoolConfig
|
|
114
|
+
): HttpClient
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Config Options:**
|
|
118
|
+
|
|
119
|
+
- `maxConnectionsPerHost` (number) - Maximum concurrent connections per host (default: 6)
|
|
120
|
+
- `connectionTimeout` (number) - Connection timeout in milliseconds (default: 30000)
|
|
121
|
+
- `keepAliveTimeout` (number) - Keep-alive timeout in milliseconds (default: 60000)
|
|
122
|
+
- `enableHttp2` (boolean) - Enable HTTP/2 multiplexing (default: true)
|
|
123
|
+
- `retryOnConnectionError` (boolean) - Retry on connection errors (default: true)
|
|
124
|
+
|
|
125
|
+
**Returns:** HttpClient with connection pooling and health monitoring
|
|
126
|
+
|
|
127
|
+
**Example:**
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { createHttpClient } from '@uploadista/client-browser';
|
|
131
|
+
|
|
132
|
+
const httpClient = createHttpClient({
|
|
133
|
+
maxConnectionsPerHost: 10,
|
|
134
|
+
connectionTimeout: 60000,
|
|
135
|
+
keepAliveTimeout: 120000,
|
|
136
|
+
enableHttp2: true
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Make requests
|
|
140
|
+
const response = await httpClient.request('https://api.example.com/data', {
|
|
141
|
+
method: 'POST',
|
|
142
|
+
headers: { 'Content-Type': 'application/json' },
|
|
143
|
+
body: JSON.stringify({ key: 'value' })
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Check connection health
|
|
147
|
+
const metrics = httpClient.getDetailedMetrics();
|
|
148
|
+
console.log('Connection health:', metrics.health.status);
|
|
149
|
+
console.log('Reuse rate:', Math.round(metrics.reuseRate * 100) + '%');
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### createBrowserFileReaderService()
|
|
153
|
+
|
|
154
|
+
Creates a file reader service for opening and reading File/Blob objects in chunks.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
function createBrowserFileReaderService(): FileReaderService<BrowserUploadInput>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Returns:** FileReaderService configured for browser File/Blob objects
|
|
161
|
+
|
|
162
|
+
**Example:**
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { createBrowserFileReaderService } from '@uploadista/client-browser';
|
|
166
|
+
|
|
167
|
+
const fileReader = createBrowserFileReaderService();
|
|
168
|
+
|
|
169
|
+
// Open a file from input
|
|
170
|
+
const input = document.querySelector<HTMLInputElement>('input[type="file"]');
|
|
171
|
+
const file = input.files[0];
|
|
172
|
+
const source = await fileReader.openFile(file, 5 * 1024 * 1024); // 5MB chunks
|
|
173
|
+
|
|
174
|
+
console.log('File name:', source.name);
|
|
175
|
+
console.log('File size:', source.size);
|
|
176
|
+
console.log('File type:', source.type);
|
|
177
|
+
|
|
178
|
+
// Read first chunk
|
|
179
|
+
const chunk = await source.slice(0, 5 * 1024 * 1024);
|
|
180
|
+
console.log('Read', chunk.size, 'bytes');
|
|
181
|
+
|
|
182
|
+
// Close when done
|
|
183
|
+
source.close();
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### BrowserUploadInput
|
|
187
|
+
|
|
188
|
+
Type representing browser file inputs that can be uploaded.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
type BrowserUploadInput = File | Blob
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**File** - From file input elements, drag-and-drop, or File constructor
|
|
195
|
+
**Blob** - From programmatic creation or API responses
|
|
196
|
+
|
|
197
|
+
**Example:**
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// From file input
|
|
201
|
+
const fileInput = document.querySelector<HTMLInputElement>('input[type="file"]');
|
|
202
|
+
const file: BrowserUploadInput = fileInput.files[0];
|
|
203
|
+
|
|
204
|
+
// From drag and drop
|
|
205
|
+
element.addEventListener('drop', (e) => {
|
|
206
|
+
e.preventDefault();
|
|
207
|
+
const file: BrowserUploadInput = e.dataTransfer.files[0];
|
|
208
|
+
client.upload(file);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// From Blob
|
|
212
|
+
const blob: BrowserUploadInput = new Blob(
|
|
213
|
+
['Hello, world!'],
|
|
214
|
+
{ type: 'text/plain' }
|
|
215
|
+
);
|
|
216
|
+
client.upload(blob);
|
|
217
|
+
|
|
218
|
+
// From canvas
|
|
219
|
+
const canvas = document.querySelector('canvas');
|
|
220
|
+
canvas.toBlob((blob) => {
|
|
221
|
+
if (blob) {
|
|
222
|
+
client.upload(blob);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Configuration
|
|
228
|
+
|
|
229
|
+
### Connection Pooling
|
|
230
|
+
|
|
231
|
+
Optimize HTTP connection reuse for better upload performance:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const client = createUploadistaClient({
|
|
235
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
236
|
+
connectionPooling: {
|
|
237
|
+
maxConnectionsPerHost: 6, // Browser default for HTTP/1.1
|
|
238
|
+
connectionTimeout: 30000, // 30 seconds
|
|
239
|
+
keepAliveTimeout: 60000, // 1 minute
|
|
240
|
+
enableHttp2: true, // Enable HTTP/2 multiplexing
|
|
241
|
+
retryOnConnectionError: true // Retry on connection failures
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Chunk Size
|
|
247
|
+
|
|
248
|
+
Balance memory usage and upload efficiency:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const client = createUploadistaClient({
|
|
252
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
253
|
+
chunkSize: 5 * 1024 * 1024 // 5MB chunks (default)
|
|
254
|
+
|
|
255
|
+
// For slow connections
|
|
256
|
+
// chunkSize: 1 * 1024 * 1024 // 1MB chunks
|
|
257
|
+
|
|
258
|
+
// For fast connections
|
|
259
|
+
// chunkSize: 10 * 1024 * 1024 // 10MB chunks
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Resumable Uploads
|
|
264
|
+
|
|
265
|
+
Enable automatic upload resumption after interruptions:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
const client = createUploadistaClient({
|
|
269
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
270
|
+
storeFingerprintForResuming: true, // Enable resumable uploads
|
|
271
|
+
|
|
272
|
+
// Upload will be fingerprinted and stored in localStorage
|
|
273
|
+
// If interrupted, it will automatically resume from the last chunk
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Retry Configuration
|
|
278
|
+
|
|
279
|
+
Configure automatic retry with exponential backoff:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
const client = createUploadistaClient({
|
|
283
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
284
|
+
retryDelays: [0, 1000, 3000, 5000], // Retry after 0s, 1s, 3s, 5s
|
|
285
|
+
|
|
286
|
+
// First attempt: immediate
|
|
287
|
+
// Second attempt: 1 second delay
|
|
288
|
+
// Third attempt: 3 second delay
|
|
289
|
+
// Fourth attempt: 5 second delay
|
|
290
|
+
// After 4 retries, upload fails
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Examples
|
|
295
|
+
|
|
296
|
+
### Example 1: Basic File Upload
|
|
297
|
+
|
|
298
|
+
Simple file upload from a file input element:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
302
|
+
|
|
303
|
+
const client = createUploadistaClient({
|
|
304
|
+
endpoint: 'https://api.uploadista.com/upload'
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const fileInput = document.querySelector<HTMLInputElement>('input[type="file"]');
|
|
308
|
+
fileInput.addEventListener('change', async (e) => {
|
|
309
|
+
const file = e.target.files?.[0];
|
|
310
|
+
if (!file) return;
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
const upload = await client.upload(file);
|
|
314
|
+
console.log('Upload successful!');
|
|
315
|
+
console.log('File ID:', upload.id);
|
|
316
|
+
console.log('File URL:', upload.url);
|
|
317
|
+
} catch (error) {
|
|
318
|
+
console.error('Upload failed:', error);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Example 2: Upload with Progress Tracking
|
|
324
|
+
|
|
325
|
+
Track upload progress with real-time updates:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
329
|
+
|
|
330
|
+
const client = createUploadistaClient({
|
|
331
|
+
endpoint: 'https://api.uploadista.com/upload'
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const progressBar = document.querySelector<HTMLProgressElement>('.progress-bar');
|
|
335
|
+
const speedDisplay = document.querySelector<HTMLSpanElement>('.speed');
|
|
336
|
+
const etaDisplay = document.querySelector<HTMLSpanElement>('.eta');
|
|
337
|
+
|
|
338
|
+
const upload = await client.upload(file, {
|
|
339
|
+
onProgress: (event) => {
|
|
340
|
+
// Update progress bar
|
|
341
|
+
progressBar.value = event.progress;
|
|
342
|
+
|
|
343
|
+
// Display upload speed
|
|
344
|
+
const speedMBps = (event.bytesPerSecond / 1024 / 1024).toFixed(2);
|
|
345
|
+
speedDisplay.textContent = `${speedMBps} MB/s`;
|
|
346
|
+
|
|
347
|
+
// Calculate and display ETA
|
|
348
|
+
const remainingBytes = event.totalBytes - event.bytesUploaded;
|
|
349
|
+
const etaSeconds = Math.round(remainingBytes / event.bytesPerSecond);
|
|
350
|
+
etaDisplay.textContent = `${etaSeconds}s remaining`;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Example 3: Upload with WebSocket for Real-Time Updates
|
|
356
|
+
|
|
357
|
+
Use WebSocket for real-time upload events and flow processing updates:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
361
|
+
|
|
362
|
+
const client = createUploadistaClient({
|
|
363
|
+
endpoint: 'https://api.uploadista.com/upload'
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Upload file and get WebSocket connection
|
|
367
|
+
const upload = await client.upload(file);
|
|
368
|
+
|
|
369
|
+
// Connect to WebSocket for real-time events
|
|
370
|
+
const ws = new WebSocket(`wss://api.uploadista.com/ws/${upload.id}`);
|
|
371
|
+
|
|
372
|
+
ws.addEventListener('message', (event) => {
|
|
373
|
+
const message = JSON.parse(event.data);
|
|
374
|
+
|
|
375
|
+
switch (message.type) {
|
|
376
|
+
case 'upload_progress':
|
|
377
|
+
console.log('Progress:', message.payload.progress + '%');
|
|
378
|
+
break;
|
|
379
|
+
|
|
380
|
+
case 'upload_complete':
|
|
381
|
+
console.log('Upload complete:', message.payload);
|
|
382
|
+
break;
|
|
383
|
+
|
|
384
|
+
case 'flow_start':
|
|
385
|
+
console.log('Flow processing started');
|
|
386
|
+
break;
|
|
387
|
+
|
|
388
|
+
case 'flow_progress':
|
|
389
|
+
console.log('Flow progress:', message.payload.nodeId);
|
|
390
|
+
break;
|
|
391
|
+
|
|
392
|
+
case 'flow_complete':
|
|
393
|
+
console.log('Flow complete:', message.payload.result);
|
|
394
|
+
break;
|
|
395
|
+
|
|
396
|
+
case 'error':
|
|
397
|
+
console.error('Error:', message.message);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
ws.addEventListener('open', () => {
|
|
403
|
+
console.log('WebSocket connected');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
ws.addEventListener('error', (error) => {
|
|
407
|
+
console.error('WebSocket error:', error);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
ws.addEventListener('close', () => {
|
|
411
|
+
console.log('WebSocket disconnected');
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Example 4: Multi-File Upload
|
|
416
|
+
|
|
417
|
+
Upload multiple files with progress tracking for each file:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
421
|
+
|
|
422
|
+
const client = createUploadistaClient({
|
|
423
|
+
endpoint: 'https://api.uploadista.com/upload'
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const fileInput = document.querySelector<HTMLInputElement>('input[type="file"]');
|
|
427
|
+
fileInput.multiple = true;
|
|
428
|
+
|
|
429
|
+
fileInput.addEventListener('change', async (e) => {
|
|
430
|
+
const files = Array.from(e.target.files || []);
|
|
431
|
+
|
|
432
|
+
const uploadPromises = files.map(file =>
|
|
433
|
+
client.upload(file, {
|
|
434
|
+
onProgress: (event) => {
|
|
435
|
+
console.log(`${file.name}: ${event.progress}%`);
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
const results = await Promise.all(uploadPromises);
|
|
442
|
+
console.log('All uploads complete:', results);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error('One or more uploads failed:', error);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Example 5: Drag and Drop Upload
|
|
450
|
+
|
|
451
|
+
Handle drag-and-drop file uploads:
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
455
|
+
|
|
456
|
+
const client = createUploadistaClient({
|
|
457
|
+
endpoint: 'https://api.uploadista.com/upload'
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const dropZone = document.querySelector<HTMLDivElement>('.drop-zone');
|
|
461
|
+
|
|
462
|
+
dropZone.addEventListener('dragover', (e) => {
|
|
463
|
+
e.preventDefault();
|
|
464
|
+
dropZone.classList.add('drag-over');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
dropZone.addEventListener('dragleave', () => {
|
|
468
|
+
dropZone.classList.remove('drag-over');
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
dropZone.addEventListener('drop', async (e) => {
|
|
472
|
+
e.preventDefault();
|
|
473
|
+
dropZone.classList.remove('drag-over');
|
|
474
|
+
|
|
475
|
+
const files = Array.from(e.dataTransfer.files);
|
|
476
|
+
|
|
477
|
+
for (const file of files) {
|
|
478
|
+
try {
|
|
479
|
+
const upload = await client.upload(file, {
|
|
480
|
+
onProgress: (event) => {
|
|
481
|
+
console.log(`${file.name}: ${event.progress}%`);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
console.log(`${file.name} uploaded:`, upload.id);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
console.error(`${file.name} failed:`, error);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Example 6: Upload with Metadata
|
|
493
|
+
|
|
494
|
+
Include custom metadata with your uploads:
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
498
|
+
|
|
499
|
+
const client = createUploadistaClient({
|
|
500
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
501
|
+
allowedMetaFields: ['userId', 'projectId', 'tags', 'description']
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
const upload = await client.upload(file, {
|
|
505
|
+
metadata: {
|
|
506
|
+
userId: 'user-123',
|
|
507
|
+
projectId: 'project-456',
|
|
508
|
+
tags: 'profile-photo, avatar',
|
|
509
|
+
description: 'User profile photo'
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
console.log('Upload with metadata:', upload);
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### Example 7: Resumable Upload with Fingerprinting
|
|
517
|
+
|
|
518
|
+
Automatically resume interrupted uploads:
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
522
|
+
|
|
523
|
+
const client = createUploadistaClient({
|
|
524
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
525
|
+
storeFingerprintForResuming: true // Enable resumable uploads
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// First upload attempt
|
|
529
|
+
const upload = await client.upload(file, {
|
|
530
|
+
onProgress: (event) => {
|
|
531
|
+
console.log('Progress:', event.progress + '%');
|
|
532
|
+
|
|
533
|
+
// Simulate interruption at 50%
|
|
534
|
+
if (event.progress >= 50) {
|
|
535
|
+
window.location.reload(); // Page reload
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// After page reload, same file will automatically resume from 50%
|
|
541
|
+
// The client fingerprints the file and stores progress in localStorage
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Example 8: Connection Health Monitoring
|
|
545
|
+
|
|
546
|
+
Monitor HTTP connection health and performance:
|
|
547
|
+
|
|
548
|
+
```typescript
|
|
549
|
+
import { createHttpClient } from '@uploadista/client-browser';
|
|
550
|
+
|
|
551
|
+
const httpClient = createHttpClient({
|
|
552
|
+
maxConnectionsPerHost: 6,
|
|
553
|
+
keepAliveTimeout: 60000,
|
|
554
|
+
enableHttp2: true
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// Monitor connection health periodically
|
|
558
|
+
setInterval(() => {
|
|
559
|
+
const metrics = httpClient.getDetailedMetrics();
|
|
560
|
+
|
|
561
|
+
console.log('Connection Health:', metrics.health.status);
|
|
562
|
+
console.log('Health Score:', metrics.health.score);
|
|
563
|
+
console.log('Connection Reuse Rate:', Math.round(metrics.reuseRate * 100) + '%');
|
|
564
|
+
console.log('Average Connection Time:', Math.round(metrics.averageConnectionTime) + 'ms');
|
|
565
|
+
console.log('Requests/sec:', metrics.requestsPerSecond.toFixed(2));
|
|
566
|
+
console.log('Error Rate:', (metrics.errorRate * 100).toFixed(1) + '%');
|
|
567
|
+
|
|
568
|
+
if (metrics.health.status === 'degraded' || metrics.health.status === 'poor') {
|
|
569
|
+
console.warn('Connection Issues:', metrics.health.issues);
|
|
570
|
+
console.log('Recommendations:', metrics.health.recommendations);
|
|
571
|
+
}
|
|
572
|
+
}, 30000); // Check every 30 seconds
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Example 9: Flow Execution with File Upload
|
|
576
|
+
|
|
577
|
+
Upload a file and execute a processing flow:
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
581
|
+
|
|
582
|
+
const client = createUploadistaClient({
|
|
583
|
+
endpoint: 'https://api.uploadista.com/upload'
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// Upload image file
|
|
587
|
+
const imageFile = document.querySelector<HTMLInputElement>('input[type="file"]').files[0];
|
|
588
|
+
const upload = await client.upload(imageFile);
|
|
589
|
+
|
|
590
|
+
// Execute image processing flow
|
|
591
|
+
const flowResult = await client.executeFlow({
|
|
592
|
+
flowId: 'image-resize-optimize',
|
|
593
|
+
inputs: {
|
|
594
|
+
'input-1': upload.id // Reference uploaded file
|
|
595
|
+
},
|
|
596
|
+
storageId: 'storage-1'
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
console.log('Flow execution complete:', flowResult);
|
|
600
|
+
console.log('Processed images:', flowResult.outputs);
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
## Browser Compatibility
|
|
604
|
+
|
|
605
|
+
`@uploadista/client-browser` is compatible with modern browsers that support:
|
|
606
|
+
|
|
607
|
+
- **Fetch API** - Chrome 42+, Firefox 39+, Safari 10.1+, Edge 14+
|
|
608
|
+
- **File API** - Chrome 13+, Firefox 3.6+, Safari 6+, Edge 12+
|
|
609
|
+
- **Web Crypto API** - Chrome 37+, Firefox 34+, Safari 11+, Edge 79+
|
|
610
|
+
- **WebSocket** - Chrome 16+, Firefox 11+, Safari 7+, Edge 12+
|
|
611
|
+
- **LocalStorage** - Chrome 4+, Firefox 3.5+, Safari 4+, Edge 12+
|
|
612
|
+
|
|
613
|
+
### Minimum Browser Versions
|
|
614
|
+
|
|
615
|
+
- Chrome/Edge: 79+
|
|
616
|
+
- Firefox: 67+
|
|
617
|
+
- Safari: 11+
|
|
618
|
+
- Opera: 66+
|
|
619
|
+
|
|
620
|
+
### Features by Browser
|
|
621
|
+
|
|
622
|
+
| Feature | Chrome | Firefox | Safari | Edge |
|
|
623
|
+
|---------|--------|---------|--------|------|
|
|
624
|
+
| Chunked Upload | 42+ | 39+ | 10.1+ | 14+ |
|
|
625
|
+
| Resumable Upload | 37+ | 34+ | 11+ | 79+ |
|
|
626
|
+
| WebSocket Events | 16+ | 11+ | 7+ | 12+ |
|
|
627
|
+
| HTTP/2 Multiplexing | 49+ | 52+ | 10.1+ | 79+ |
|
|
628
|
+
| Connection Pooling | 42+ | 39+ | 10.1+ | 14+ |
|
|
629
|
+
|
|
630
|
+
### Polyfills
|
|
631
|
+
|
|
632
|
+
For older browser support, consider using polyfills:
|
|
633
|
+
|
|
634
|
+
- `whatwg-fetch` - Fetch API polyfill
|
|
635
|
+
- `abortcontroller-polyfill` - AbortController polyfill
|
|
636
|
+
- `webcrypto-shim` - Web Crypto API polyfill
|
|
637
|
+
|
|
638
|
+
```bash
|
|
639
|
+
npm install whatwg-fetch abortcontroller-polyfill webcrypto-shim
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
```typescript
|
|
643
|
+
import 'whatwg-fetch';
|
|
644
|
+
import 'abortcontroller-polyfill/dist/polyfill-patch-fetch';
|
|
645
|
+
import 'webcrypto-shim';
|
|
646
|
+
|
|
647
|
+
import { createUploadistaClient } from '@uploadista/client-browser';
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
## Related Packages
|
|
651
|
+
|
|
652
|
+
### Core Packages
|
|
653
|
+
- [@uploadista/core](../../../core/README.md) - Core flow engine and upload system
|
|
654
|
+
- [@uploadista/client-core](../core/README.md) - Platform-agnostic client logic
|
|
655
|
+
|
|
656
|
+
### Other Client Packages
|
|
657
|
+
- [@uploadista/react](../react/README.md) - React hooks and components for Uploadista
|
|
658
|
+
|
|
659
|
+
### Server Packages
|
|
660
|
+
- [@uploadista/server](../../server/README.md) - Server-side upload handling
|
|
661
|
+
|
|
662
|
+
## Troubleshooting
|
|
663
|
+
|
|
664
|
+
### Issue: Upload fails with CORS error
|
|
665
|
+
|
|
666
|
+
**Solution:** Ensure your server is configured to accept requests from your domain:
|
|
667
|
+
|
|
668
|
+
```javascript
|
|
669
|
+
// Server-side (example)
|
|
670
|
+
app.use((req, res, next) => {
|
|
671
|
+
res.setHeader('Access-Control-Allow-Origin', 'https://your-domain.com');
|
|
672
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
673
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
674
|
+
next();
|
|
675
|
+
});
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### Issue: Upload fails on mobile Safari
|
|
679
|
+
|
|
680
|
+
**Solution:** Mobile Safari has stricter memory limits. Use smaller chunk sizes:
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
const client = createUploadistaClient({
|
|
684
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
685
|
+
chunkSize: 1 * 1024 * 1024 // 1MB chunks for mobile
|
|
686
|
+
});
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Issue: Resumable uploads not working
|
|
690
|
+
|
|
691
|
+
**Solution:** Verify that:
|
|
692
|
+
1. `storeFingerprintForResuming` is enabled
|
|
693
|
+
2. LocalStorage is not disabled or full
|
|
694
|
+
3. The file hasn't been modified (fingerprint would change)
|
|
695
|
+
4. The server supports resumable uploads
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
// Check if localStorage is available
|
|
699
|
+
if (typeof localStorage !== 'undefined') {
|
|
700
|
+
const client = createUploadistaClient({
|
|
701
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
702
|
+
storeFingerprintForResuming: true
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
### Issue: Slow upload speeds
|
|
708
|
+
|
|
709
|
+
**Solution:**
|
|
710
|
+
1. Check connection health metrics
|
|
711
|
+
2. Increase chunk size for fast connections
|
|
712
|
+
3. Enable HTTP/2 multiplexing
|
|
713
|
+
4. Warm up connections before uploading
|
|
714
|
+
|
|
715
|
+
```typescript
|
|
716
|
+
const httpClient = createHttpClient({
|
|
717
|
+
maxConnectionsPerHost: 10,
|
|
718
|
+
enableHttp2: true,
|
|
719
|
+
keepAliveTimeout: 120000
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
// Warm up connections
|
|
723
|
+
await httpClient.warmupConnections(['https://api.uploadista.com']);
|
|
724
|
+
|
|
725
|
+
const client = createUploadistaClient({
|
|
726
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
727
|
+
chunkSize: 10 * 1024 * 1024, // 10MB chunks for fast connections
|
|
728
|
+
connectionPooling: {
|
|
729
|
+
maxConnectionsPerHost: 10,
|
|
730
|
+
enableHttp2: true
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Issue: Memory usage too high
|
|
736
|
+
|
|
737
|
+
**Solution:** Reduce chunk size to decrease memory usage:
|
|
738
|
+
|
|
739
|
+
```typescript
|
|
740
|
+
const client = createUploadistaClient({
|
|
741
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
742
|
+
chunkSize: 1 * 1024 * 1024 // 1MB chunks
|
|
743
|
+
});
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
### Issue: WebSocket connection fails
|
|
747
|
+
|
|
748
|
+
**Solution:**
|
|
749
|
+
1. Verify WebSocket URL is correct (wss:// for HTTPS)
|
|
750
|
+
2. Check firewall/proxy settings
|
|
751
|
+
3. Ensure server supports WebSocket upgrades
|
|
752
|
+
4. Implement reconnection logic
|
|
753
|
+
|
|
754
|
+
```typescript
|
|
755
|
+
let ws: WebSocket;
|
|
756
|
+
let reconnectAttempts = 0;
|
|
757
|
+
const maxReconnectAttempts = 5;
|
|
758
|
+
|
|
759
|
+
function connectWebSocket(uploadId: string) {
|
|
760
|
+
ws = new WebSocket(`wss://api.uploadista.com/ws/${uploadId}`);
|
|
761
|
+
|
|
762
|
+
ws.addEventListener('open', () => {
|
|
763
|
+
console.log('Connected');
|
|
764
|
+
reconnectAttempts = 0;
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
ws.addEventListener('close', () => {
|
|
768
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
769
|
+
reconnectAttempts++;
|
|
770
|
+
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
|
|
771
|
+
setTimeout(() => connectWebSocket(uploadId), delay);
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### Issue: File upload fails with large files
|
|
778
|
+
|
|
779
|
+
**Solution:**
|
|
780
|
+
1. Use chunked uploads (enabled by default)
|
|
781
|
+
2. Increase chunk size for better performance
|
|
782
|
+
3. Enable resumable uploads for reliability
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
const client = createUploadistaClient({
|
|
786
|
+
endpoint: 'https://api.uploadista.com/upload',
|
|
787
|
+
chunkSize: 5 * 1024 * 1024, // 5MB chunks
|
|
788
|
+
storeFingerprintForResuming: true, // Enable resumable uploads
|
|
789
|
+
retryDelays: [1000, 3000, 5000, 10000] // Retry on failure
|
|
790
|
+
});
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
## License
|
|
794
|
+
|
|
795
|
+
MIT
|