@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
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Integration Utilities
|
|
3
|
+
*
|
|
4
|
+
* This module provides TypeScript utilities and helper types for building
|
|
5
|
+
* framework-specific wrappers around the Uploadista client.
|
|
6
|
+
*
|
|
7
|
+
* @module framework-utils
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Utility: Calculate aggregate upload statistics
|
|
11
|
+
*/
|
|
12
|
+
export function calculateMultiUploadStats(uploads) {
|
|
13
|
+
const totalFiles = uploads.length;
|
|
14
|
+
const completedFiles = uploads.filter((u) => u.status === "success").length;
|
|
15
|
+
const failedFiles = uploads.filter((u) => u.status === "error").length;
|
|
16
|
+
const totalBytes = uploads.reduce((sum, u) => sum + u.totalBytes, 0);
|
|
17
|
+
const uploadedBytes = uploads.reduce((sum, u) => sum + u.bytesUploaded, 0);
|
|
18
|
+
const totalProgress = totalBytes > 0 ? (uploadedBytes / totalBytes) * 100 : 0;
|
|
19
|
+
const allComplete = uploads.every((u) => u.status === "success");
|
|
20
|
+
const hasErrors = uploads.some((u) => u.status === "error");
|
|
21
|
+
return {
|
|
22
|
+
totalFiles,
|
|
23
|
+
completedFiles,
|
|
24
|
+
failedFiles,
|
|
25
|
+
totalBytes,
|
|
26
|
+
uploadedBytes,
|
|
27
|
+
totalProgress,
|
|
28
|
+
allComplete,
|
|
29
|
+
hasErrors,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Utility: Format file size for display
|
|
34
|
+
*/
|
|
35
|
+
export function formatFileSize(bytes) {
|
|
36
|
+
if (bytes === 0)
|
|
37
|
+
return "0 Bytes";
|
|
38
|
+
const k = 1024;
|
|
39
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
|
40
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
41
|
+
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Utility: Format progress percentage
|
|
45
|
+
*/
|
|
46
|
+
export function formatProgress(progress) {
|
|
47
|
+
return `${Math.round(progress)}%`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Utility: Get file extension
|
|
51
|
+
*/
|
|
52
|
+
export function getFileExtension(filename) {
|
|
53
|
+
const lastDot = filename.lastIndexOf(".");
|
|
54
|
+
return lastDot === -1 ? "" : filename.slice(lastDot + 1).toLowerCase();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Utility: Check if file is an image
|
|
58
|
+
*/
|
|
59
|
+
export function isImageFile(file) {
|
|
60
|
+
return file.type.startsWith("image/");
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Utility: Check if file is a video
|
|
64
|
+
*/
|
|
65
|
+
export function isVideoFile(file) {
|
|
66
|
+
return file.type.startsWith("video/");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Utility: Create file size validator
|
|
70
|
+
*/
|
|
71
|
+
export function createFileSizeValidator(maxSizeBytes) {
|
|
72
|
+
return (file) => {
|
|
73
|
+
if (file.size > maxSizeBytes) {
|
|
74
|
+
return {
|
|
75
|
+
valid: false,
|
|
76
|
+
error: `File size exceeds maximum of ${formatFileSize(maxSizeBytes)}`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { valid: true };
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Utility: Create file type validator
|
|
84
|
+
*/
|
|
85
|
+
export function createFileTypeValidator(allowedTypes) {
|
|
86
|
+
return (file) => {
|
|
87
|
+
const fileType = file.type.toLowerCase();
|
|
88
|
+
const fileExt = getFileExtension(file.name);
|
|
89
|
+
const isAllowed = allowedTypes.some((type) => {
|
|
90
|
+
if (type.startsWith(".")) {
|
|
91
|
+
return type.slice(1) === fileExt;
|
|
92
|
+
}
|
|
93
|
+
if (type.includes("*")) {
|
|
94
|
+
const pattern = type.replace("*", "");
|
|
95
|
+
return fileType.startsWith(pattern);
|
|
96
|
+
}
|
|
97
|
+
return fileType === type;
|
|
98
|
+
});
|
|
99
|
+
if (!isAllowed) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
error: `File type not allowed. Allowed types: ${allowedTypes.join(", ")}`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return { valid: true };
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Utility: Compose multiple validators
|
|
110
|
+
*/
|
|
111
|
+
export function composeValidators(...validators) {
|
|
112
|
+
return (file) => {
|
|
113
|
+
for (const validator of validators) {
|
|
114
|
+
const result = validator(file);
|
|
115
|
+
if (!result.valid) {
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { valid: true };
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Utility: Generate unique upload ID
|
|
124
|
+
*/
|
|
125
|
+
export function generateUploadId() {
|
|
126
|
+
return `upload-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Utility: Create delay promise for retry logic
|
|
130
|
+
*/
|
|
131
|
+
export function delay(ms) {
|
|
132
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Utility: Calculate exponential backoff delay
|
|
136
|
+
*/
|
|
137
|
+
export function calculateBackoff(attempt, baseDelay = 1000, maxDelay = 30000) {
|
|
138
|
+
const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
|
|
139
|
+
// Add jitter to prevent thundering herd
|
|
140
|
+
return delay + Math.random() * 1000;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Utility: Create retry wrapper for upload function
|
|
144
|
+
*/
|
|
145
|
+
export function createRetryWrapper(fn, maxAttempts = 3, shouldRetry = () => true) {
|
|
146
|
+
return async () => {
|
|
147
|
+
let lastError;
|
|
148
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
149
|
+
try {
|
|
150
|
+
return await fn();
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
lastError = error;
|
|
154
|
+
if (attempt < maxAttempts - 1 && shouldRetry(error)) {
|
|
155
|
+
const delayMs = calculateBackoff(attempt);
|
|
156
|
+
await delay(delayMs);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw lastError;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Type guard: Check if error is network-related (should retry)
|
|
167
|
+
*/
|
|
168
|
+
export function isNetworkError(error) {
|
|
169
|
+
if (error instanceof Error) {
|
|
170
|
+
return (error.message.includes("network") ||
|
|
171
|
+
error.message.includes("timeout") ||
|
|
172
|
+
error.message.includes("connection") ||
|
|
173
|
+
error.message.includes("ECONNREFUSED") ||
|
|
174
|
+
error.message.includes("ETIMEDOUT"));
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Type guard: Check if error is abort-related (should not retry)
|
|
180
|
+
*/
|
|
181
|
+
export function isAbortError(error) {
|
|
182
|
+
if (error instanceof Error) {
|
|
183
|
+
return error.name === "AbortError" || error.message.includes("abort");
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Format upload speed in human-readable format
|
|
189
|
+
*/
|
|
190
|
+
export function formatSpeed(bytesPerSecond) {
|
|
191
|
+
if (bytesPerSecond === 0)
|
|
192
|
+
return "0 B/s";
|
|
193
|
+
const k = 1024;
|
|
194
|
+
const sizes = ["B/s", "KB/s", "MB/s", "GB/s"];
|
|
195
|
+
const i = Math.floor(Math.log(bytesPerSecond) / Math.log(k));
|
|
196
|
+
return `${parseFloat((bytesPerSecond / k ** i).toFixed(1))} ${sizes[i]}`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Format duration in human-readable format
|
|
200
|
+
*/
|
|
201
|
+
export function formatDuration(milliseconds) {
|
|
202
|
+
if (milliseconds < 1000) {
|
|
203
|
+
return `${Math.round(milliseconds)}ms`;
|
|
204
|
+
}
|
|
205
|
+
if (milliseconds < 60000) {
|
|
206
|
+
return `${Math.round(milliseconds / 1000)}s`;
|
|
207
|
+
}
|
|
208
|
+
if (milliseconds < 3600000) {
|
|
209
|
+
const minutes = Math.floor(milliseconds / 60000);
|
|
210
|
+
const seconds = Math.round((milliseconds % 60000) / 1000);
|
|
211
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
212
|
+
}
|
|
213
|
+
const hours = Math.floor(milliseconds / 3600000);
|
|
214
|
+
const minutes = Math.round((milliseconds % 3600000) / 60000);
|
|
215
|
+
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Validate file type against accepted types
|
|
219
|
+
*/
|
|
220
|
+
export function validateFileType(file, accept) {
|
|
221
|
+
if (!accept || accept.length === 0)
|
|
222
|
+
return true;
|
|
223
|
+
return accept.some((acceptType) => {
|
|
224
|
+
if (acceptType.startsWith(".")) {
|
|
225
|
+
// File extension check
|
|
226
|
+
return file.name.toLowerCase().endsWith(acceptType.toLowerCase());
|
|
227
|
+
}
|
|
228
|
+
// MIME type check (supports wildcards like image/*)
|
|
229
|
+
if (acceptType.endsWith("/*")) {
|
|
230
|
+
const baseType = acceptType.slice(0, -2);
|
|
231
|
+
return file.type.startsWith(baseType);
|
|
232
|
+
}
|
|
233
|
+
return file.type === acceptType;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Check if a file is an audio file
|
|
238
|
+
*/
|
|
239
|
+
export function isAudioFile(file) {
|
|
240
|
+
return file.type.startsWith("audio/");
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Check if a file is a document
|
|
244
|
+
*/
|
|
245
|
+
export function isDocumentFile(file) {
|
|
246
|
+
const documentTypes = [
|
|
247
|
+
"application/pdf",
|
|
248
|
+
"application/msword",
|
|
249
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
250
|
+
"application/vnd.ms-excel",
|
|
251
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
252
|
+
"application/vnd.ms-powerpoint",
|
|
253
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
254
|
+
"text/plain",
|
|
255
|
+
"text/csv",
|
|
256
|
+
"application/rtf",
|
|
257
|
+
];
|
|
258
|
+
return documentTypes.includes(file.type);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Create a preview URL for a file (if supported)
|
|
262
|
+
*/
|
|
263
|
+
export function createFilePreview(file) {
|
|
264
|
+
if (isImageFile(file) || isVideoFile(file) || isAudioFile(file)) {
|
|
265
|
+
return URL.createObjectURL(file);
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Clean up a preview URL created with createFilePreview
|
|
271
|
+
*/
|
|
272
|
+
export function revokeFilePreview(previewUrl) {
|
|
273
|
+
URL.revokeObjectURL(previewUrl);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Calculate progress percentage
|
|
277
|
+
*/
|
|
278
|
+
export function calculateProgress(current, total) {
|
|
279
|
+
if (total === 0)
|
|
280
|
+
return 0;
|
|
281
|
+
return Math.min(100, Math.max(0, Math.round((current / total) * 100)));
|
|
282
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ConnectionPoolConfig, HttpClient } from "@uploadista/client-core";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a browser-optimized HTTP client using the Fetch API.
|
|
4
|
+
*
|
|
5
|
+
* This factory function returns an HttpClient implementation that uses the browser's
|
|
6
|
+
* native fetch() API with connection keep-alive headers for optimal performance.
|
|
7
|
+
* The client automatically manages connection pooling, tracks metrics, and provides
|
|
8
|
+
* connection health monitoring.
|
|
9
|
+
*
|
|
10
|
+
* @param config - Optional connection pooling configuration
|
|
11
|
+
* @returns A configured HTTP client ready for making requests
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { createHttpClient } from '@uploadista/client-browser';
|
|
16
|
+
*
|
|
17
|
+
* // Basic usage with defaults
|
|
18
|
+
* const client = createHttpClient();
|
|
19
|
+
*
|
|
20
|
+
* // With custom configuration
|
|
21
|
+
* const client = createHttpClient({
|
|
22
|
+
* maxConnectionsPerHost: 10,
|
|
23
|
+
* connectionTimeout: 60000,
|
|
24
|
+
* keepAliveTimeout: 120000,
|
|
25
|
+
* enableHttp2: true,
|
|
26
|
+
* retryOnConnectionError: true
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Make a request
|
|
30
|
+
* const response = await client.request('https://api.example.com/data', {
|
|
31
|
+
* method: 'POST',
|
|
32
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
33
|
+
* body: JSON.stringify({ key: 'value' })
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Check connection health
|
|
37
|
+
* const metrics = client.getDetailedMetrics();
|
|
38
|
+
* console.log('Connection health:', metrics.health.status);
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @see {@link BrowserHttpClient} for implementation details
|
|
42
|
+
*/
|
|
43
|
+
export declare function createHttpClient(config?: ConnectionPoolConfig): HttpClient;
|
|
44
|
+
//# sourceMappingURL=http-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,oBAAoB,EAGpB,UAAU,EAGX,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,oBAAoB,GAAG,UAAU,CAE1E"}
|