@uploadista/client-core 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 (235) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +100 -0
  4. package/dist/auth/auth-http-client.d.ts +50 -0
  5. package/dist/auth/auth-http-client.d.ts.map +1 -0
  6. package/dist/auth/auth-http-client.js +110 -0
  7. package/dist/auth/direct-auth.d.ts +38 -0
  8. package/dist/auth/direct-auth.d.ts.map +1 -0
  9. package/dist/auth/direct-auth.js +95 -0
  10. package/dist/auth/index.d.ts +6 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +5 -0
  13. package/dist/auth/no-auth.d.ts +26 -0
  14. package/dist/auth/no-auth.d.ts.map +1 -0
  15. package/dist/auth/no-auth.js +33 -0
  16. package/dist/auth/saas-auth.d.ts +80 -0
  17. package/dist/auth/saas-auth.d.ts.map +1 -0
  18. package/dist/auth/saas-auth.js +167 -0
  19. package/dist/auth/types.d.ts +101 -0
  20. package/dist/auth/types.d.ts.map +1 -0
  21. package/dist/auth/types.js +8 -0
  22. package/dist/chunk-buffer.d.ts +209 -0
  23. package/dist/chunk-buffer.d.ts.map +1 -0
  24. package/dist/chunk-buffer.js +236 -0
  25. package/dist/client/create-uploadista-client.d.ts +369 -0
  26. package/dist/client/create-uploadista-client.d.ts.map +1 -0
  27. package/dist/client/create-uploadista-client.js +518 -0
  28. package/dist/client/index.d.ts +4 -0
  29. package/dist/client/index.d.ts.map +1 -0
  30. package/dist/client/index.js +3 -0
  31. package/dist/client/uploadista-api.d.ts +284 -0
  32. package/dist/client/uploadista-api.d.ts.map +1 -0
  33. package/dist/client/uploadista-api.js +444 -0
  34. package/dist/client/uploadista-websocket-manager.d.ts +110 -0
  35. package/dist/client/uploadista-websocket-manager.d.ts.map +1 -0
  36. package/dist/client/uploadista-websocket-manager.js +207 -0
  37. package/dist/error.d.ts +106 -0
  38. package/dist/error.d.ts.map +1 -0
  39. package/dist/error.js +69 -0
  40. package/dist/index.d.ts +9 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +12 -0
  43. package/dist/logger.d.ts +70 -0
  44. package/dist/logger.d.ts.map +1 -0
  45. package/dist/logger.js +59 -0
  46. package/dist/mock-data-store.d.ts +30 -0
  47. package/dist/mock-data-store.d.ts.map +1 -0
  48. package/dist/mock-data-store.js +88 -0
  49. package/dist/network-monitor.d.ts +262 -0
  50. package/dist/network-monitor.d.ts.map +1 -0
  51. package/dist/network-monitor.js +291 -0
  52. package/dist/services/abort-controller-service.d.ts +19 -0
  53. package/dist/services/abort-controller-service.d.ts.map +1 -0
  54. package/dist/services/abort-controller-service.js +4 -0
  55. package/dist/services/checksum-service.d.ts +4 -0
  56. package/dist/services/checksum-service.d.ts.map +1 -0
  57. package/dist/services/checksum-service.js +1 -0
  58. package/dist/services/file-reader-service.d.ts +38 -0
  59. package/dist/services/file-reader-service.d.ts.map +1 -0
  60. package/dist/services/file-reader-service.js +4 -0
  61. package/dist/services/fingerprint-service.d.ts +4 -0
  62. package/dist/services/fingerprint-service.d.ts.map +1 -0
  63. package/dist/services/fingerprint-service.js +1 -0
  64. package/dist/services/http-client.d.ts +182 -0
  65. package/dist/services/http-client.d.ts.map +1 -0
  66. package/dist/services/http-client.js +1 -0
  67. package/dist/services/id-generation-service.d.ts +10 -0
  68. package/dist/services/id-generation-service.d.ts.map +1 -0
  69. package/dist/services/id-generation-service.js +1 -0
  70. package/dist/services/index.d.ts +11 -0
  71. package/dist/services/index.d.ts.map +1 -0
  72. package/dist/services/index.js +10 -0
  73. package/dist/services/platform-service.d.ts +48 -0
  74. package/dist/services/platform-service.d.ts.map +1 -0
  75. package/dist/services/platform-service.js +10 -0
  76. package/dist/services/service-container.d.ts +25 -0
  77. package/dist/services/service-container.d.ts.map +1 -0
  78. package/dist/services/service-container.js +1 -0
  79. package/dist/services/storage-service.d.ts +26 -0
  80. package/dist/services/storage-service.d.ts.map +1 -0
  81. package/dist/services/storage-service.js +1 -0
  82. package/dist/services/websocket-service.d.ts +36 -0
  83. package/dist/services/websocket-service.d.ts.map +1 -0
  84. package/dist/services/websocket-service.js +4 -0
  85. package/dist/smart-chunker.d.ts +72 -0
  86. package/dist/smart-chunker.d.ts.map +1 -0
  87. package/dist/smart-chunker.js +317 -0
  88. package/dist/storage/client-storage.d.ts +148 -0
  89. package/dist/storage/client-storage.d.ts.map +1 -0
  90. package/dist/storage/client-storage.js +62 -0
  91. package/dist/storage/in-memory-storage-service.d.ts +7 -0
  92. package/dist/storage/in-memory-storage-service.d.ts.map +1 -0
  93. package/dist/storage/in-memory-storage-service.js +24 -0
  94. package/dist/storage/index.d.ts +3 -0
  95. package/dist/storage/index.d.ts.map +1 -0
  96. package/dist/storage/index.js +2 -0
  97. package/dist/types/buffered-chunk.d.ts +6 -0
  98. package/dist/types/buffered-chunk.d.ts.map +1 -0
  99. package/dist/types/buffered-chunk.js +1 -0
  100. package/dist/types/chunk-metrics.d.ts +12 -0
  101. package/dist/types/chunk-metrics.d.ts.map +1 -0
  102. package/dist/types/chunk-metrics.js +1 -0
  103. package/dist/types/flow-result.d.ts +11 -0
  104. package/dist/types/flow-result.d.ts.map +1 -0
  105. package/dist/types/flow-result.js +1 -0
  106. package/dist/types/flow-upload-config.d.ts +54 -0
  107. package/dist/types/flow-upload-config.d.ts.map +1 -0
  108. package/dist/types/flow-upload-config.js +1 -0
  109. package/dist/types/flow-upload-item.d.ts +16 -0
  110. package/dist/types/flow-upload-item.d.ts.map +1 -0
  111. package/dist/types/flow-upload-item.js +1 -0
  112. package/dist/types/flow-upload-options.d.ts +41 -0
  113. package/dist/types/flow-upload-options.d.ts.map +1 -0
  114. package/dist/types/flow-upload-options.js +1 -0
  115. package/dist/types/index.d.ts +14 -0
  116. package/dist/types/index.d.ts.map +1 -0
  117. package/dist/types/index.js +13 -0
  118. package/dist/types/multi-flow-upload-options.d.ts +33 -0
  119. package/dist/types/multi-flow-upload-options.d.ts.map +1 -0
  120. package/dist/types/multi-flow-upload-options.js +1 -0
  121. package/dist/types/multi-flow-upload-state.d.ts +9 -0
  122. package/dist/types/multi-flow-upload-state.d.ts.map +1 -0
  123. package/dist/types/multi-flow-upload-state.js +1 -0
  124. package/dist/types/performance-insights.d.ts +11 -0
  125. package/dist/types/performance-insights.d.ts.map +1 -0
  126. package/dist/types/performance-insights.js +1 -0
  127. package/dist/types/previous-upload.d.ts +20 -0
  128. package/dist/types/previous-upload.d.ts.map +1 -0
  129. package/dist/types/previous-upload.js +9 -0
  130. package/dist/types/upload-options.d.ts +40 -0
  131. package/dist/types/upload-options.d.ts.map +1 -0
  132. package/dist/types/upload-options.js +1 -0
  133. package/dist/types/upload-response.d.ts +6 -0
  134. package/dist/types/upload-response.d.ts.map +1 -0
  135. package/dist/types/upload-response.js +1 -0
  136. package/dist/types/upload-result.d.ts +57 -0
  137. package/dist/types/upload-result.d.ts.map +1 -0
  138. package/dist/types/upload-result.js +1 -0
  139. package/dist/types/upload-session-metrics.d.ts +16 -0
  140. package/dist/types/upload-session-metrics.d.ts.map +1 -0
  141. package/dist/types/upload-session-metrics.js +1 -0
  142. package/dist/upload/chunk-upload.d.ts +40 -0
  143. package/dist/upload/chunk-upload.d.ts.map +1 -0
  144. package/dist/upload/chunk-upload.js +82 -0
  145. package/dist/upload/flow-upload.d.ts +48 -0
  146. package/dist/upload/flow-upload.d.ts.map +1 -0
  147. package/dist/upload/flow-upload.js +240 -0
  148. package/dist/upload/index.d.ts +3 -0
  149. package/dist/upload/index.d.ts.map +1 -0
  150. package/dist/upload/index.js +2 -0
  151. package/dist/upload/parallel-upload.d.ts +65 -0
  152. package/dist/upload/parallel-upload.d.ts.map +1 -0
  153. package/dist/upload/parallel-upload.js +231 -0
  154. package/dist/upload/single-upload.d.ts +118 -0
  155. package/dist/upload/single-upload.d.ts.map +1 -0
  156. package/dist/upload/single-upload.js +332 -0
  157. package/dist/upload/upload-manager.d.ts +30 -0
  158. package/dist/upload/upload-manager.d.ts.map +1 -0
  159. package/dist/upload/upload-manager.js +57 -0
  160. package/dist/upload/upload-metrics.d.ts +37 -0
  161. package/dist/upload/upload-metrics.d.ts.map +1 -0
  162. package/dist/upload/upload-metrics.js +236 -0
  163. package/dist/upload/upload-storage.d.ts +32 -0
  164. package/dist/upload/upload-storage.d.ts.map +1 -0
  165. package/dist/upload/upload-storage.js +46 -0
  166. package/dist/upload/upload-strategy.d.ts +66 -0
  167. package/dist/upload/upload-strategy.d.ts.map +1 -0
  168. package/dist/upload/upload-strategy.js +171 -0
  169. package/dist/upload/upload-utils.d.ts +26 -0
  170. package/dist/upload/upload-utils.d.ts.map +1 -0
  171. package/dist/upload/upload-utils.js +80 -0
  172. package/package.json +29 -0
  173. package/src/__tests__/smart-chunking.test.ts +399 -0
  174. package/src/auth/__tests__/auth-http-client.test.ts +327 -0
  175. package/src/auth/__tests__/direct-auth.test.ts +135 -0
  176. package/src/auth/__tests__/no-auth.test.ts +40 -0
  177. package/src/auth/__tests__/saas-auth.test.ts +337 -0
  178. package/src/auth/auth-http-client.ts +150 -0
  179. package/src/auth/direct-auth.ts +121 -0
  180. package/src/auth/index.ts +5 -0
  181. package/src/auth/no-auth.ts +39 -0
  182. package/src/auth/saas-auth.ts +218 -0
  183. package/src/auth/types.ts +105 -0
  184. package/src/chunk-buffer.ts +287 -0
  185. package/src/client/create-uploadista-client.ts +901 -0
  186. package/src/client/index.ts +3 -0
  187. package/src/client/uploadista-api.ts +857 -0
  188. package/src/client/uploadista-websocket-manager.ts +275 -0
  189. package/src/error.ts +149 -0
  190. package/src/index.ts +13 -0
  191. package/src/logger.ts +104 -0
  192. package/src/mock-data-store.ts +97 -0
  193. package/src/network-monitor.ts +445 -0
  194. package/src/services/abort-controller-service.ts +21 -0
  195. package/src/services/checksum-service.ts +3 -0
  196. package/src/services/file-reader-service.ts +44 -0
  197. package/src/services/fingerprint-service.ts +6 -0
  198. package/src/services/http-client.ts +229 -0
  199. package/src/services/id-generation-service.ts +9 -0
  200. package/src/services/index.ts +10 -0
  201. package/src/services/platform-service.ts +65 -0
  202. package/src/services/service-container.ts +24 -0
  203. package/src/services/storage-service.ts +29 -0
  204. package/src/services/websocket-service.ts +33 -0
  205. package/src/smart-chunker.ts +451 -0
  206. package/src/storage/client-storage.ts +186 -0
  207. package/src/storage/in-memory-storage-service.ts +33 -0
  208. package/src/storage/index.ts +2 -0
  209. package/src/types/buffered-chunk.ts +5 -0
  210. package/src/types/chunk-metrics.ts +11 -0
  211. package/src/types/flow-result.ts +14 -0
  212. package/src/types/flow-upload-config.ts +56 -0
  213. package/src/types/flow-upload-item.ts +16 -0
  214. package/src/types/flow-upload-options.ts +56 -0
  215. package/src/types/index.ts +13 -0
  216. package/src/types/multi-flow-upload-options.ts +39 -0
  217. package/src/types/multi-flow-upload-state.ts +9 -0
  218. package/src/types/performance-insights.ts +7 -0
  219. package/src/types/previous-upload.ts +22 -0
  220. package/src/types/upload-options.ts +56 -0
  221. package/src/types/upload-response.ts +6 -0
  222. package/src/types/upload-result.ts +60 -0
  223. package/src/types/upload-session-metrics.ts +15 -0
  224. package/src/upload/chunk-upload.ts +151 -0
  225. package/src/upload/flow-upload.ts +367 -0
  226. package/src/upload/index.ts +2 -0
  227. package/src/upload/parallel-upload.ts +387 -0
  228. package/src/upload/single-upload.ts +554 -0
  229. package/src/upload/upload-manager.ts +106 -0
  230. package/src/upload/upload-metrics.ts +340 -0
  231. package/src/upload/upload-storage.ts +87 -0
  232. package/src/upload/upload-strategy.ts +296 -0
  233. package/src/upload/upload-utils.ts +114 -0
  234. package/tsconfig.json +23 -0
  235. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,901 @@
1
+ import type { DataStoreCapabilities } from "@uploadista/core/types";
2
+ import type { AuthConfig, AuthManager } from "../auth";
3
+ import { DirectAuthManager, NoAuthManager, SaasAuthManager } from "../auth";
4
+ import type { Logger } from "../logger";
5
+ import { createLogger } from "../logger";
6
+ import { defaultClientCapabilities } from "../mock-data-store";
7
+ import { NetworkMonitor, type NetworkMonitorConfig } from "../network-monitor";
8
+ import type { AbortControllerFactory } from "../services/abort-controller-service";
9
+ import type { ChecksumService } from "../services/checksum-service";
10
+ import type { FileReaderService } from "../services/file-reader-service";
11
+ import type { FingerprintService } from "../services/fingerprint-service";
12
+ import type { ConnectionPoolConfig, HttpClient } from "../services/http-client";
13
+ import type { IdGenerationService } from "../services/id-generation-service";
14
+ import type { PlatformService, Timeout } from "../services/platform-service";
15
+ import type {
16
+ WebSocketFactory,
17
+ WebSocketLike,
18
+ } from "../services/websocket-service";
19
+ import { SmartChunker, type SmartChunkerConfig } from "../smart-chunker";
20
+ import type { ClientStorage } from "../storage/client-storage";
21
+ import type { FlowUploadConfig } from "../types/flow-upload-config";
22
+
23
+ import { performFlowUpload, startFlowUpload } from "../upload/flow-upload";
24
+ import { startParallelUpload } from "../upload/parallel-upload";
25
+ import {
26
+ type Callbacks,
27
+ performUpload,
28
+ startSingleUpload,
29
+ } from "../upload/single-upload";
30
+ import { abort, terminate } from "../upload/upload-manager";
31
+ import {
32
+ UploadMetrics,
33
+ type UploadMetricsConfig,
34
+ } from "../upload/upload-metrics";
35
+ import {
36
+ findPreviousUploads,
37
+ resumeFromPreviousUpload,
38
+ } from "../upload/upload-storage";
39
+ import {
40
+ negotiateUploadStrategy,
41
+ type UploadStrategyConfig,
42
+ validateAndThrow,
43
+ validateConfiguration,
44
+ } from "../upload/upload-strategy";
45
+ import { calculateFileSize } from "../upload/upload-utils";
46
+ import { createUploadistaApi } from "./uploadista-api";
47
+ import {
48
+ type UploadistaWebSocketEventHandler,
49
+ UploadistaWebSocketManager,
50
+ } from "./uploadista-websocket-manager";
51
+
52
+ /**
53
+ * Options for individual upload operations.
54
+ *
55
+ * Extends the base upload callbacks with configuration for deferred length,
56
+ * size overrides, metadata, and checksum computation.
57
+ */
58
+ export type UploadistaUploadOptions = {
59
+ /**
60
+ * Whether to defer specifying the upload size until later.
61
+ * Useful for streaming uploads where size isn't known upfront.
62
+ * Defaults to false.
63
+ */
64
+ uploadLengthDeferred?: boolean;
65
+
66
+ /**
67
+ * Manual override for upload size in bytes.
68
+ * If not provided, size is determined from the file/blob.
69
+ */
70
+ uploadSize?: number;
71
+
72
+ /**
73
+ * Custom metadata to attach to the upload.
74
+ * Stored as key-value pairs on the server.
75
+ */
76
+ metadata?: Record<string, string>;
77
+
78
+ /**
79
+ * Whether to compute checksums for uploaded chunks.
80
+ * Enables integrity verification but adds computational overhead.
81
+ * Defaults to false.
82
+ */
83
+ computeChecksum?: boolean;
84
+
85
+ /**
86
+ * Checksum algorithm to use (e.g., "sha256", "md5").
87
+ * Only relevant if computeChecksum is true.
88
+ */
89
+ checksumAlgorithm?: string;
90
+ } & Callbacks;
91
+
92
+ /**
93
+ * Configuration options for creating an Uploadista client.
94
+ *
95
+ * This comprehensive configuration object allows customization of all aspects
96
+ * of upload behavior including chunking, retries, authentication, storage,
97
+ * network monitoring, and platform-specific services.
98
+ *
99
+ * @template UploadInput - The platform-specific file/blob type (e.g., File, Blob, Buffer)
100
+ */
101
+ export type UploadistaClientOptions<UploadInput> = {
102
+ /** Base URL of the Uploadista server (e.g., "https://upload.example.com") */
103
+ baseUrl: string;
104
+
105
+ /** Base path for Uploadista endpoints. Defaults to "uploadista" */
106
+ uploadistaBasePath?: string;
107
+
108
+ /** Storage backend identifier configured on the server */
109
+ storageId: string;
110
+
111
+ /** Retry delay intervals in milliseconds. Defaults to [1000, 3000, 5000] */
112
+ retryDelays?: number[];
113
+
114
+ /** Default chunk size in bytes for uploads */
115
+ chunkSize: number;
116
+
117
+ /** Number of parallel upload streams. Defaults to 1 (sequential) */
118
+ parallelUploads?: number;
119
+
120
+ /** Chunk size for parallel uploads. Required if parallelUploads > 1 */
121
+ parallelChunkSize?: number;
122
+
123
+ /** Service for computing checksums of uploaded chunks */
124
+ checksumService: ChecksumService;
125
+
126
+ /** Strategy configuration for determining upload approach (single/parallel/chunked) */
127
+ uploadStrategy?: UploadStrategyConfig;
128
+
129
+ /** Smart chunking configuration for adaptive chunk sizes based on network conditions */
130
+ smartChunking?: SmartChunkerConfig;
131
+
132
+ /** Network monitoring configuration for tracking upload performance */
133
+ networkMonitoring?: NetworkMonitorConfig;
134
+
135
+ /** Upload metrics configuration for performance insights */
136
+ uploadMetrics?: UploadMetricsConfig;
137
+
138
+ /** HTTP client with connection pooling support */
139
+ httpClient: HttpClient;
140
+
141
+ /** Service for generating unique IDs */
142
+ generateId: IdGenerationService;
143
+
144
+ /** Client-side storage for upload resumption data */
145
+ clientStorage: ClientStorage;
146
+
147
+ /** Platform-specific file reading service */
148
+ fileReader: FileReaderService<UploadInput>;
149
+
150
+ /** Logger for debugging and monitoring */
151
+ logger: Logger;
152
+
153
+ /** Service for computing file fingerprints for resumption */
154
+ fingerprintService: FingerprintService<UploadInput>;
155
+
156
+ /** Whether to store fingerprints for upload resumption. Defaults to true */
157
+ storeFingerprintForResuming: boolean;
158
+
159
+ /** Factory for creating WebSocket connections */
160
+ webSocketFactory: WebSocketFactory;
161
+
162
+ /** Factory for creating abort controllers */
163
+ abortControllerFactory: AbortControllerFactory;
164
+
165
+ /** Platform-specific service for timers and async operations */
166
+ platformService: PlatformService;
167
+
168
+ /** Global error handler for all upload operations */
169
+ onError?: (error: Error) => void;
170
+
171
+ /** WebSocket event handler for real-time upload/flow events */
172
+ onEvent?: UploadistaWebSocketEventHandler;
173
+
174
+ /**
175
+ * Optional authentication configuration.
176
+ * Supports two modes:
177
+ * - Direct: Bring your own auth (headers, cookies, custom tokens)
178
+ * - SaaS: Standard JWT token exchange with auth server
179
+ *
180
+ * If omitted, client operates in no-auth mode (backward compatible).
181
+ *
182
+ * @example Direct mode with Bearer token
183
+ * ```typescript
184
+ * auth: {
185
+ * mode: 'direct',
186
+ * getCredentials: () => ({
187
+ * headers: { 'Authorization': 'Bearer token123' }
188
+ * })
189
+ * }
190
+ * ```
191
+ *
192
+ * @example SaaS mode with auth server
193
+ * ```typescript
194
+ * auth: {
195
+ * mode: 'saas',
196
+ * authServerUrl: 'https://auth.myapp.com/token',
197
+ * getCredentials: () => ({ username: 'user', password: 'pass' })
198
+ * }
199
+ * ```
200
+ */
201
+ auth?: AuthConfig;
202
+ };
203
+
204
+ /**
205
+ * Default connection pooling configuration with health monitoring.
206
+ *
207
+ * Optimized for typical upload scenarios with support for HTTP/2 multiplexing,
208
+ * connection reuse, and automatic retry on connection errors.
209
+ */
210
+ export const defaultConnectionPoolingConfig: ConnectionPoolConfig = {
211
+ /** Maximum concurrent connections per host */
212
+ maxConnectionsPerHost: 8,
213
+ /** Timeout for establishing new connections in milliseconds */
214
+ connectionTimeout: 20000,
215
+ /** Keep-alive timeout for idle connections in milliseconds */
216
+ keepAliveTimeout: 90000,
217
+ /** Enable HTTP/2 for connection multiplexing */
218
+ enableHttp2: true,
219
+ /** Automatically retry requests on connection errors */
220
+ retryOnConnectionError: true,
221
+ };
222
+
223
+ /**
224
+ * Creates a unified Uploadista client for file uploads and flow processing.
225
+ *
226
+ * This is the primary factory function for creating an Uploadista client instance.
227
+ * It configures all upload capabilities including:
228
+ * - Resumable chunked uploads with automatic retry
229
+ * - Parallel upload streams for large files
230
+ * - Smart chunking based on network conditions
231
+ * - Flow-based file processing pipelines
232
+ * - WebSocket support for real-time progress
233
+ * - Authentication (direct, SaaS, or no-auth modes)
234
+ *
235
+ * The client automatically:
236
+ * - Fetches server capabilities and adapts upload strategy
237
+ * - Monitors network performance for optimal chunking
238
+ * - Stores upload state for resumption across sessions
239
+ * - Manages WebSocket connections for progress tracking
240
+ *
241
+ * @template UploadInput - Platform-specific file type (File, Blob, Buffer, etc.)
242
+ * @param options - Comprehensive client configuration
243
+ * @returns Uploadista client instance with upload and flow methods
244
+ *
245
+ * @example Basic browser setup
246
+ * ```typescript
247
+ * import { createUploadistaClient } from '@uploadista/client-core';
248
+ * import { browserServices } from '@uploadista/client-browser';
249
+ *
250
+ * const client = createUploadistaClient({
251
+ * baseUrl: 'https://upload.example.com',
252
+ * storageId: 'my-storage',
253
+ * chunkSize: 5 * 1024 * 1024, // 5MB chunks
254
+ * ...browserServices,
255
+ * });
256
+ *
257
+ * // Upload a file
258
+ * const { abort } = await client.upload(file, {
259
+ * onProgress: (progress) => console.log(`${progress}% complete`),
260
+ * onSuccess: (result) => console.log('Upload complete:', result),
261
+ * });
262
+ * ```
263
+ *
264
+ * @example Upload with flow processing
265
+ * ```typescript
266
+ * const client = createUploadistaClient(config);
267
+ *
268
+ * // Upload and process through a flow
269
+ * const { abort, jobId } = await client.uploadWithFlow(file, {
270
+ * flowId: 'image-optimization-flow',
271
+ * storageId: 'images',
272
+ * outputNodeId: 'optimized-output',
273
+ * }, {
274
+ * onProgress: (progress) => console.log(`${progress}%`),
275
+ * onSuccess: (result) => console.log('Processed:', result),
276
+ * });
277
+ *
278
+ * // Monitor job status
279
+ * const status = await client.getJobStatus(jobId);
280
+ * ```
281
+ *
282
+ * @example Parallel uploads for large files
283
+ * ```typescript
284
+ * const client = createUploadistaClient({
285
+ * baseUrl: 'https://upload.example.com',
286
+ * storageId: 'large-files',
287
+ * chunkSize: 10 * 1024 * 1024, // 10MB
288
+ * parallelUploads: 4, // 4 concurrent streams
289
+ * parallelChunkSize: 5 * 1024 * 1024, // 5MB per stream
290
+ * ...browserServices,
291
+ * });
292
+ *
293
+ * await client.upload(largeFile);
294
+ * ```
295
+ *
296
+ * @example With authentication
297
+ * ```typescript
298
+ * const client = createUploadistaClient({
299
+ * baseUrl: 'https://upload.example.com',
300
+ * storageId: 'protected',
301
+ * chunkSize: 5 * 1024 * 1024,
302
+ * auth: {
303
+ * mode: 'direct',
304
+ * getCredentials: async () => ({
305
+ * headers: {
306
+ * 'Authorization': `Bearer ${await getToken()}`,
307
+ * },
308
+ * }),
309
+ * },
310
+ * ...browserServices,
311
+ * });
312
+ * ```
313
+ *
314
+ * @example Smart chunking with network monitoring
315
+ * ```typescript
316
+ * const client = createUploadistaClient({
317
+ * baseUrl: 'https://upload.example.com',
318
+ * storageId: 'adaptive',
319
+ * chunkSize: 1 * 1024 * 1024, // Fallback: 1MB
320
+ * smartChunking: {
321
+ * enabled: true,
322
+ * minChunkSize: 256 * 1024, // 256KB min
323
+ * maxChunkSize: 10 * 1024 * 1024, // 10MB max
324
+ * },
325
+ * networkMonitoring: {
326
+ * slowThreshold: 50 * 1024, // 50 KB/s
327
+ * fastThreshold: 5 * 1024 * 1024, // 5 MB/s
328
+ * },
329
+ * ...browserServices,
330
+ * });
331
+ *
332
+ * // Monitor network conditions
333
+ * const condition = client.getNetworkCondition();
334
+ * console.log(`Network: ${condition.type} (confidence: ${condition.confidence})`);
335
+ * ```
336
+ *
337
+ * @see {@link UploadistaClientOptions} for full configuration options
338
+ * @see {@link UploadistaUploadOptions} for per-upload options
339
+ */
340
+ export function createUploadistaClient<UploadInput>({
341
+ baseUrl: _baseUrl,
342
+ uploadistaBasePath = "uploadista",
343
+ storageId,
344
+ retryDelays = [1000, 3000, 5000],
345
+ chunkSize,
346
+ parallelUploads = 1,
347
+ parallelChunkSize,
348
+ uploadStrategy,
349
+ smartChunking,
350
+ networkMonitoring,
351
+ uploadMetrics,
352
+ checksumService,
353
+ onEvent,
354
+ generateId,
355
+ httpClient,
356
+ logger = createLogger(true),
357
+ fileReader,
358
+ fingerprintService,
359
+ clientStorage,
360
+ storeFingerprintForResuming = true,
361
+ webSocketFactory,
362
+ abortControllerFactory,
363
+ platformService,
364
+ auth,
365
+ }: UploadistaClientOptions<UploadInput>) {
366
+ const baseUrl = _baseUrl.replace(/\/$/, "");
367
+
368
+ // Create auth manager based on configuration
369
+ const authManager: AuthManager = auth
370
+ ? auth.mode === "direct"
371
+ ? new DirectAuthManager(auth, platformService, logger)
372
+ : new SaasAuthManager(auth, httpClient)
373
+ : new NoAuthManager();
374
+
375
+ // Log auth mode for debugging (without exposing credentials)
376
+ if (auth) {
377
+ logger.log(
378
+ `Authentication enabled in ${auth.mode} mode${auth.mode === "saas" ? ` (server: ${auth.authServerUrl})` : ""}`,
379
+ );
380
+ }
381
+
382
+ // Create the unified API with auth support
383
+ const uploadistaApi = createUploadistaApi(baseUrl, uploadistaBasePath, {
384
+ logger,
385
+ httpClient,
386
+ authManager,
387
+ webSocketFactory,
388
+ });
389
+
390
+ // Initialize smart chunking components
391
+ const networkMonitor = new NetworkMonitor(networkMonitoring);
392
+ const metrics = new UploadMetrics(uploadMetrics);
393
+
394
+ // Cache for server capabilities
395
+ let cachedCapabilities: DataStoreCapabilities | null = null;
396
+
397
+ const getCapabilities = async (): Promise<DataStoreCapabilities> => {
398
+ if (cachedCapabilities) {
399
+ return cachedCapabilities;
400
+ }
401
+ cachedCapabilities = await uploadistaApi.getCapabilities(storageId);
402
+ return cachedCapabilities;
403
+ };
404
+
405
+ // Initialize smart chunker with datastore constraints from server capabilities
406
+ let smartChunker: SmartChunker;
407
+ const initializeSmartChunker = async () => {
408
+ if (smartChunker) return smartChunker;
409
+
410
+ const capabilities = await getCapabilities();
411
+
412
+ const datastoreConstraints =
413
+ capabilities.minChunkSize &&
414
+ capabilities.maxChunkSize &&
415
+ capabilities.optimalChunkSize
416
+ ? {
417
+ minChunkSize: capabilities.minChunkSize,
418
+ maxChunkSize: capabilities.maxChunkSize,
419
+ optimalChunkSize: capabilities.optimalChunkSize,
420
+ requiresOrderedChunks: capabilities.requiresOrderedChunks,
421
+ }
422
+ : undefined;
423
+
424
+ smartChunker = new SmartChunker(networkMonitor, {
425
+ enabled: true,
426
+ ...smartChunking,
427
+ fallbackChunkSize: chunkSize,
428
+ datastoreConstraints,
429
+ });
430
+
431
+ logger.log(
432
+ `Smart chunker initialized with datastore constraints: ${JSON.stringify(datastoreConstraints)}`,
433
+ );
434
+
435
+ return smartChunker;
436
+ };
437
+
438
+ // WebSocket management (uses uploadistaApi for both upload and flow websockets)
439
+ const wsManager = new UploadistaWebSocketManager(
440
+ uploadistaApi,
441
+ logger,
442
+ onEvent,
443
+ );
444
+
445
+ /**
446
+ * Upload a file
447
+ */
448
+ const upload = async (
449
+ file: UploadInput,
450
+ {
451
+ uploadLengthDeferred = false,
452
+ uploadSize,
453
+ onProgress,
454
+ onChunkComplete,
455
+ onSuccess,
456
+ onShouldRetry,
457
+ onError,
458
+ }: UploadistaUploadOptions = {},
459
+ ): Promise<{ abort: () => void }> => {
460
+ let uploadId: string | null = null;
461
+ let uploadIdStorageKey: string | null = null;
462
+
463
+ const fingerprint = await fingerprintService.computeFingerprint(
464
+ file,
465
+ `${baseUrl}/${uploadistaBasePath}/api/upload`,
466
+ );
467
+
468
+ logger.log(`fingerprint: ${fingerprint}`);
469
+ if (!fingerprint) {
470
+ throw new Error("unable calculate fingerprint for this input file");
471
+ }
472
+
473
+ const previousUploads = await findPreviousUploads(
474
+ clientStorage,
475
+ fingerprint,
476
+ );
477
+ if (previousUploads.length > 0 && previousUploads[0] != null) {
478
+ const previousUpload = resumeFromPreviousUpload(previousUploads[0]);
479
+ uploadIdStorageKey = previousUpload.clientStorageKey;
480
+ uploadId = previousUpload.uploadId;
481
+ }
482
+
483
+ const source = await fileReader.openFile(file, chunkSize);
484
+
485
+ const size = calculateFileSize(source.size, {
486
+ uploadLengthDeferred,
487
+ uploadSize,
488
+ });
489
+ source.size = size;
490
+
491
+ const initializedSmartChunker = await initializeSmartChunker();
492
+
493
+ const isSmartChunkingEnabled = smartChunking?.enabled !== false;
494
+ if (isSmartChunkingEnabled) {
495
+ metrics.startSession(fingerprint, size || 0, true);
496
+ }
497
+
498
+ const capabilities = await getCapabilities();
499
+
500
+ const negotiatedStrategy = negotiateUploadStrategy({
501
+ capabilities,
502
+ fileSize: size,
503
+ chunkSize,
504
+ parallelUploads,
505
+ uploadLengthDeferred,
506
+ strategyConfig: uploadStrategy,
507
+ logger,
508
+ });
509
+
510
+ if (negotiatedStrategy.strategy === "parallel") {
511
+ logger.log(
512
+ `Using parallel upload with ${negotiatedStrategy.parallelUploads} streams`,
513
+ );
514
+
515
+ const parallelResult = await startParallelUpload({
516
+ checksumService,
517
+ source,
518
+ storageId,
519
+ fingerprint,
520
+ uploadLengthDeferred,
521
+ parallelUploads: negotiatedStrategy.parallelUploads,
522
+ parallelChunkSize,
523
+ retryDelays,
524
+ smartChunker: initializedSmartChunker,
525
+ uploadistaApi,
526
+ logger,
527
+ smartChunking,
528
+ metrics,
529
+ clientStorage,
530
+ generateId,
531
+ storeFingerprintForResuming,
532
+ abortControllerFactory,
533
+ platformService,
534
+ openWebSocket: (id) => {
535
+ wsManager.openUploadWebSocket(id);
536
+ // Note: WebSocket opening is now async due to auth, but this callback is sync
537
+ // The WebSocket will be opened in the background
538
+ return null as unknown as WebSocketLike;
539
+ },
540
+ closeWebSocket: (id) => wsManager.closeUploadWebSocket(id),
541
+ terminate: (id) =>
542
+ terminate(id, uploadistaApi, platformService, retryDelays),
543
+ onProgress,
544
+ onChunkComplete,
545
+ onSuccess,
546
+ onError,
547
+ });
548
+
549
+ if (parallelResult) {
550
+ return {
551
+ abort: async () => {
552
+ await parallelResult.abort();
553
+ },
554
+ };
555
+ }
556
+
557
+ logger.log("Parallel upload failed, falling back to single upload");
558
+ }
559
+
560
+ // Single upload path
561
+ const result = await startSingleUpload({
562
+ source,
563
+ storageId,
564
+ uploadId,
565
+ platformService,
566
+ uploadIdStorageKey,
567
+ checksumService,
568
+ fingerprint,
569
+ uploadLengthDeferred,
570
+ uploadistaApi,
571
+ logger,
572
+ clientStorage,
573
+ generateId,
574
+ storeFingerprintForResuming,
575
+ openWebSocket: (id) => {
576
+ wsManager.openUploadWebSocket(id);
577
+ // Note: WebSocket opening is now async due to auth, but this callback is sync
578
+ // The WebSocket will be opened in the background
579
+ return null as unknown as WebSocketLike;
580
+ },
581
+ closeWebSocket: (id) => wsManager.closeUploadWebSocket(id),
582
+ onProgress,
583
+ onChunkComplete,
584
+ onSuccess,
585
+ onError,
586
+ });
587
+
588
+ if (result) {
589
+ const abortController = abortControllerFactory.create();
590
+ const { uploadId, uploadIdStorageKey, offset } = result;
591
+
592
+ let timeoutId: Timeout | null = null;
593
+
594
+ performUpload({
595
+ platformService,
596
+ uploadId,
597
+ offset,
598
+ source,
599
+ uploadLengthDeferred,
600
+ retryDelays,
601
+ smartChunker: initializedSmartChunker,
602
+ uploadistaApi,
603
+ logger,
604
+ smartChunking,
605
+ metrics,
606
+ abortController,
607
+ onProgress,
608
+ onChunkComplete,
609
+ onSuccess,
610
+ onShouldRetry,
611
+ onRetry: (timeout) => {
612
+ timeoutId = timeout;
613
+ },
614
+ onError,
615
+ });
616
+
617
+ return {
618
+ abort: () => {
619
+ abort({
620
+ platformService,
621
+ uploadId,
622
+ uploadIdStorageKey,
623
+ retryTimeout: timeoutId,
624
+ shouldTerminate: true,
625
+ abortController,
626
+ uploadistaApi,
627
+ retryDelays,
628
+ clientStorage,
629
+ });
630
+ },
631
+ };
632
+ }
633
+
634
+ return {
635
+ abort: () => {},
636
+ };
637
+ };
638
+
639
+ // Run validation on client creation
640
+ validateAndThrow(
641
+ {
642
+ baseUrl,
643
+ storageId,
644
+ chunkSize,
645
+ parallelUploads,
646
+ parallelChunkSize,
647
+ uploadStrategy,
648
+ },
649
+ logger,
650
+ );
651
+
652
+ /**
653
+ * Upload a file through a flow (using streaming-input-node)
654
+ */
655
+ const uploadWithFlow = async (
656
+ file: UploadInput,
657
+ flowConfig: FlowUploadConfig,
658
+ {
659
+ onProgress,
660
+ onChunkComplete,
661
+ onSuccess,
662
+ onShouldRetry,
663
+ onJobStart,
664
+ onError,
665
+ }: Omit<
666
+ UploadistaUploadOptions,
667
+ "uploadLengthDeferred" | "uploadSize" | "metadata"
668
+ > = {},
669
+ ): Promise<{ abort: () => void; jobId: string }> => {
670
+ const source = await fileReader.openFile(file, chunkSize);
671
+
672
+ const initializedSmartChunker = await initializeSmartChunker();
673
+
674
+ const isSmartChunkingEnabled = smartChunking?.enabled !== false;
675
+ if (isSmartChunkingEnabled) {
676
+ const fingerprint = await fingerprintService.computeFingerprint(
677
+ file,
678
+ `${baseUrl}/${uploadistaBasePath}/api/flow`,
679
+ );
680
+ metrics.startSession(fingerprint || "unknown", source.size || 0, true);
681
+ }
682
+
683
+ const result = await startFlowUpload({
684
+ source,
685
+ flowConfig,
686
+ uploadistaApi,
687
+ logger,
688
+ platformService,
689
+ openWebSocket: (id) => wsManager.openFlowWebSocket(id),
690
+ closeWebSocket: (id) => wsManager.closeWebSocket(id),
691
+ onProgress,
692
+ onChunkComplete,
693
+ onSuccess,
694
+ onJobStart,
695
+ onError,
696
+ });
697
+
698
+ if (!result) {
699
+ return {
700
+ abort: () => {},
701
+ jobId: "",
702
+ };
703
+ }
704
+
705
+ const { jobId, uploadFile, inputNodeId } = result;
706
+ const abortController = abortControllerFactory.create();
707
+
708
+ // Open upload WebSocket to receive upload progress events
709
+ await wsManager.openUploadWebSocket(uploadFile.id);
710
+
711
+ let timeoutId: Timeout | null = null;
712
+
713
+ performFlowUpload({
714
+ jobId,
715
+ uploadFile,
716
+ inputNodeId,
717
+ offset: uploadFile.offset,
718
+ source,
719
+ retryDelays,
720
+ smartChunker: initializedSmartChunker,
721
+ uploadistaApi,
722
+ logger,
723
+ smartChunking,
724
+ metrics,
725
+ platformService,
726
+ abortController,
727
+ onProgress,
728
+ onChunkComplete,
729
+ onSuccess,
730
+ onShouldRetry,
731
+ onRetry: (timeout) => {
732
+ timeoutId = timeout;
733
+ },
734
+ onError,
735
+ });
736
+
737
+ return {
738
+ abort: () => {
739
+ abortController.abort();
740
+ if (timeoutId) {
741
+ platformService.clearTimeout(timeoutId);
742
+ }
743
+ // Close both flow and upload WebSockets
744
+ wsManager.closeWebSocket(jobId);
745
+ wsManager.closeUploadWebSocket(uploadFile.id);
746
+ },
747
+ jobId,
748
+ };
749
+ };
750
+
751
+ return {
752
+ // Upload operations
753
+ upload,
754
+ uploadWithFlow,
755
+ abort: (params: Parameters<typeof abort>[0]) => abort(params),
756
+
757
+ // Flow operations
758
+ getFlow: async (flowId: string) => {
759
+ const { status, flow } = await uploadistaApi.getFlow(flowId);
760
+ return { status, flow };
761
+ },
762
+
763
+ runFlow: async ({
764
+ flowId,
765
+ inputs,
766
+ storageId: flowStorageId,
767
+ }: {
768
+ flowId: string;
769
+ inputs: Record<string, unknown>;
770
+ storageId?: string;
771
+ }) => {
772
+ const { status, job } = await uploadistaApi.runFlow(
773
+ flowId,
774
+ flowStorageId || storageId,
775
+ inputs,
776
+ );
777
+ return { status, job };
778
+ },
779
+
780
+ continueFlow: async ({
781
+ jobId,
782
+ nodeId,
783
+ newData,
784
+ contentType,
785
+ }: {
786
+ jobId: string;
787
+ nodeId: string;
788
+ newData: unknown;
789
+ contentType?: "application/json" | "application/octet-stream";
790
+ }) => {
791
+ return uploadistaApi.continueFlow(jobId, nodeId, newData, {
792
+ contentType,
793
+ });
794
+ },
795
+
796
+ // Job operations (unified for both uploads and flows)
797
+ getJobStatus: async (jobId: string) => {
798
+ return uploadistaApi.getJobStatus(jobId);
799
+ },
800
+
801
+ // WebSocket management methods
802
+ openUploadWebSocket: (uploadId: string) =>
803
+ wsManager.openUploadWebSocket(uploadId),
804
+ openFlowWebSocket: (jobId: string) => wsManager.openFlowWebSocket(jobId),
805
+ openWebSocket: (id: string) => wsManager.openWebSocket(id),
806
+ closeWebSocket: (id: string) => wsManager.closeWebSocket(id),
807
+ closeAllWebSockets: () => wsManager.closeAll(),
808
+ sendPing: (jobId: string) => wsManager.sendPing(jobId),
809
+ isWebSocketConnected: (id: string) => wsManager.isConnected(id),
810
+ getWebSocketConnectionCount: () => wsManager.getConnectionCount(),
811
+ getWebSocketConnectionCountByType: () =>
812
+ wsManager.getConnectionCountByType(),
813
+
814
+ // Smart chunking utilities
815
+ getNetworkMetrics: () => networkMonitor.getCurrentMetrics(),
816
+ getNetworkCondition: () => networkMonitor.getNetworkCondition(),
817
+ getChunkingInsights: () => metrics.getPerformanceInsights(),
818
+ exportMetrics: () => metrics.exportMetrics(),
819
+
820
+ // Connection pooling utilities
821
+ getConnectionMetrics: () => uploadistaApi.getConnectionMetrics(),
822
+ getDetailedConnectionMetrics: () =>
823
+ uploadistaApi.getDetailedConnectionMetrics(),
824
+ warmupConnections: (urls: string[]) =>
825
+ uploadistaApi.warmupConnections(urls),
826
+
827
+ // Smart chunking insights
828
+ getConnectionPoolingInsights: async () => {
829
+ const chunker = await initializeSmartChunker();
830
+ return chunker.getConnectionPoolingInsights();
831
+ },
832
+
833
+ resetMetrics: async () => {
834
+ networkMonitor.reset();
835
+ const chunker = await initializeSmartChunker();
836
+ chunker.reset();
837
+ metrics.reset();
838
+ },
839
+
840
+ // Configuration validation
841
+ validateConfiguration: (options: UploadistaClientOptions<UploadInput>) => {
842
+ return validateConfiguration(options, defaultClientCapabilities, logger);
843
+ },
844
+
845
+ validateConfigurationAsync: async (
846
+ options: UploadistaClientOptions<UploadInput>,
847
+ ) => {
848
+ const errors: string[] = [];
849
+ const warnings: string[] = [];
850
+
851
+ // Fetch capabilities using the authenticated HTTP client
852
+ const capabilities = await uploadistaApi.getCapabilities(
853
+ options.storageId,
854
+ );
855
+
856
+ const validation = validateConfiguration(options, capabilities, logger);
857
+ errors.push(...validation.errors);
858
+ warnings.push(...validation.warnings);
859
+
860
+ return {
861
+ valid: errors.length === 0,
862
+ errors,
863
+ warnings,
864
+ capabilities,
865
+ };
866
+ },
867
+
868
+ getCapabilities,
869
+ };
870
+ }
871
+
872
+ /**
873
+ * Uploadista client instance type.
874
+ *
875
+ * The client provides methods for:
876
+ * - **Upload operations**: upload(), uploadWithFlow()
877
+ * - **Flow operations**: getFlow(), runFlow(), continueFlow()
878
+ * - **Job management**: getJobStatus()
879
+ * - **WebSocket management**: openUploadWebSocket(), openFlowWebSocket(), closeWebSocket()
880
+ * - **Metrics and diagnostics**: getNetworkMetrics(), getChunkingInsights(), exportMetrics()
881
+ * - **Connection pooling**: getConnectionMetrics(), warmupConnections()
882
+ * - **Configuration validation**: validateConfiguration(), validateConfigurationAsync()
883
+ *
884
+ * @example Basic usage
885
+ * ```typescript
886
+ * const client = createUploadistaClient(config);
887
+ *
888
+ * // Upload a file
889
+ * await client.upload(file, {
890
+ * onProgress: (progress) => console.log(`${progress}%`),
891
+ * onSuccess: (result) => console.log('Done:', result.id),
892
+ * });
893
+ *
894
+ * // Get network metrics
895
+ * const metrics = client.getNetworkMetrics();
896
+ * console.log(`Speed: ${metrics.averageSpeed / 1024} KB/s`);
897
+ * ```
898
+ *
899
+ * @see {@link createUploadistaClient} for creating an instance
900
+ */
901
+ export type UploadistaClient = ReturnType<typeof createUploadistaClient>;