@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.
Files changed (83) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/.turbo/turbo-check.log +130 -0
  3. package/AUTO_CAPABILITIES.md +98 -0
  4. package/FRAMEWORK_INTEGRATION.md +407 -0
  5. package/LICENSE +21 -0
  6. package/README.md +795 -0
  7. package/SMART_CHUNKING.md +140 -0
  8. package/dist/client/create-uploadista-client.d.ts +182 -0
  9. package/dist/client/create-uploadista-client.d.ts.map +1 -0
  10. package/dist/client/create-uploadista-client.js +76 -0
  11. package/dist/client/index.d.ts +2 -0
  12. package/dist/client/index.d.ts.map +1 -0
  13. package/dist/client/index.js +1 -0
  14. package/dist/framework-utils.d.ts +201 -0
  15. package/dist/framework-utils.d.ts.map +1 -0
  16. package/dist/framework-utils.js +282 -0
  17. package/dist/http-client.d.ts +44 -0
  18. package/dist/http-client.d.ts.map +1 -0
  19. package/dist/http-client.js +489 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +7 -0
  23. package/dist/services/abort-controller-factory.d.ts +30 -0
  24. package/dist/services/abort-controller-factory.d.ts.map +1 -0
  25. package/dist/services/abort-controller-factory.js +98 -0
  26. package/dist/services/checksum-service.d.ts +30 -0
  27. package/dist/services/checksum-service.d.ts.map +1 -0
  28. package/dist/services/checksum-service.js +44 -0
  29. package/dist/services/create-browser-services.d.ts +36 -0
  30. package/dist/services/create-browser-services.d.ts.map +1 -0
  31. package/dist/services/create-browser-services.js +56 -0
  32. package/dist/services/file-reader.d.ts +91 -0
  33. package/dist/services/file-reader.d.ts.map +1 -0
  34. package/dist/services/file-reader.js +251 -0
  35. package/dist/services/fingerprint-service.d.ts +41 -0
  36. package/dist/services/fingerprint-service.d.ts.map +1 -0
  37. package/dist/services/fingerprint-service.js +64 -0
  38. package/dist/services/id-generation/id-generation.d.ts +40 -0
  39. package/dist/services/id-generation/id-generation.d.ts.map +1 -0
  40. package/dist/services/id-generation/id-generation.js +58 -0
  41. package/dist/services/platform-service.d.ts +38 -0
  42. package/dist/services/platform-service.d.ts.map +1 -0
  43. package/dist/services/platform-service.js +221 -0
  44. package/dist/services/storage/local-storage-service.d.ts +55 -0
  45. package/dist/services/storage/local-storage-service.d.ts.map +1 -0
  46. package/dist/services/storage/local-storage-service.js +178 -0
  47. package/dist/services/storage/session-storage-service.d.ts +55 -0
  48. package/dist/services/storage/session-storage-service.d.ts.map +1 -0
  49. package/dist/services/storage/session-storage-service.js +179 -0
  50. package/dist/services/websocket-factory.d.ts +46 -0
  51. package/dist/services/websocket-factory.d.ts.map +1 -0
  52. package/dist/services/websocket-factory.js +196 -0
  53. package/dist/types/index.d.ts +2 -0
  54. package/dist/types/index.d.ts.map +1 -0
  55. package/dist/types/index.js +1 -0
  56. package/dist/types/upload-input.d.ts +26 -0
  57. package/dist/types/upload-input.d.ts.map +1 -0
  58. package/dist/types/upload-input.js +1 -0
  59. package/dist/utils/hash-util.d.ts +60 -0
  60. package/dist/utils/hash-util.d.ts.map +1 -0
  61. package/dist/utils/hash-util.js +75 -0
  62. package/package.json +32 -0
  63. package/src/client/create-uploadista-client.ts +150 -0
  64. package/src/client/index.ts +1 -0
  65. package/src/framework-utils.ts +446 -0
  66. package/src/http-client.ts +546 -0
  67. package/src/index.ts +8 -0
  68. package/src/services/abort-controller-factory.ts +108 -0
  69. package/src/services/checksum-service.ts +46 -0
  70. package/src/services/create-browser-services.ts +81 -0
  71. package/src/services/file-reader.ts +344 -0
  72. package/src/services/fingerprint-service.ts +67 -0
  73. package/src/services/id-generation/id-generation.ts +60 -0
  74. package/src/services/platform-service.ts +231 -0
  75. package/src/services/storage/local-storage-service.ts +187 -0
  76. package/src/services/storage/session-storage-service.ts +188 -0
  77. package/src/services/websocket-factory.ts +212 -0
  78. package/src/types/index.ts +1 -0
  79. package/src/types/upload-input.ts +25 -0
  80. package/src/utils/hash-util.ts +79 -0
  81. package/tsconfig.json +22 -0
  82. package/tsconfig.tsbuildinfo +1 -0
  83. 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"}