@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,387 @@
1
+ import type { UploadFile } from "@uploadista/core/types";
2
+ import type { UploadistaApi } from "../client/uploadista-api";
3
+ import { UploadistaError } from "../error";
4
+ import type { Logger } from "../logger";
5
+ import type {
6
+ AbortControllerFactory,
7
+ AbortControllerLike,
8
+ } from "../services/abort-controller-service";
9
+ import type { ChecksumService } from "../services/checksum-service";
10
+ import type { FileSource } from "../services/file-reader-service";
11
+ import type { IdGenerationService } from "../services/id-generation-service";
12
+ import type { PlatformService, Timeout } from "../services/platform-service";
13
+ import type { WebSocketLike } from "../services/websocket-service";
14
+ import type { SmartChunker, SmartChunkerConfig } from "../smart-chunker";
15
+ import type { ClientStorage } from "../storage/client-storage";
16
+ import { type Callbacks, createUpload, performUpload } from "./single-upload";
17
+ import type { UploadMetrics } from "./upload-metrics";
18
+ import { calculateSegments } from "./upload-utils";
19
+
20
+ export type ParallelUploadSegment = {
21
+ uploadId: string;
22
+ uploadIdStorageKey: string | undefined;
23
+ segmentIndex: number;
24
+ startByte: number;
25
+ endByte: number;
26
+ offset: number;
27
+ abortController: AbortControllerLike;
28
+ retryTimeout: Timeout | null;
29
+ };
30
+
31
+ export type ParallelUploadState = {
32
+ segments: ParallelUploadSegment[];
33
+ totalProgress: number;
34
+ completed: boolean;
35
+ failed: boolean;
36
+ error?: Error;
37
+ };
38
+
39
+ export type ParallelUploadResult = {
40
+ parallelState: ParallelUploadState;
41
+ abort: () => Promise<void>;
42
+ };
43
+
44
+ /**
45
+ * Initiate the uploading procedure for a parallelized upload, where one file is split into
46
+ * multiple request which are run in parallel.
47
+ */
48
+ export async function startParallelUpload({
49
+ source,
50
+ storageId,
51
+ fingerprint,
52
+ uploadLengthDeferred,
53
+ parallelUploads,
54
+ parallelChunkSize,
55
+ retryDelays,
56
+ smartChunker,
57
+ uploadistaApi,
58
+ logger,
59
+ checksumService,
60
+ smartChunking,
61
+ metrics,
62
+ clientStorage,
63
+ generateId,
64
+ storeFingerprintForResuming,
65
+ openWebSocket,
66
+ closeWebSocket,
67
+ terminate,
68
+ abortControllerFactory,
69
+ platformService,
70
+ ...callbacks
71
+ }: {
72
+ source: FileSource;
73
+ storageId: string;
74
+ fingerprint: string;
75
+ uploadLengthDeferred: boolean | undefined;
76
+ parallelUploads: number;
77
+ parallelChunkSize?: number;
78
+ retryDelays?: number[];
79
+ smartChunker: SmartChunker;
80
+ uploadistaApi: UploadistaApi;
81
+ checksumService: ChecksumService;
82
+ logger: Logger;
83
+ smartChunking?: SmartChunkerConfig;
84
+ metrics: UploadMetrics;
85
+ clientStorage: ClientStorage;
86
+ generateId: IdGenerationService;
87
+ storeFingerprintForResuming: boolean;
88
+ openWebSocket: (uploadId: string) => WebSocketLike;
89
+ closeWebSocket: (uploadId: string) => void;
90
+ terminate: (uploadId: string) => Promise<void>;
91
+ abortControllerFactory: AbortControllerFactory;
92
+ platformService: PlatformService;
93
+ } & Callbacks): Promise<ParallelUploadResult | undefined> {
94
+ if (!source.size || source.size === 0) {
95
+ callbacks.onError?.(
96
+ new UploadistaError({
97
+ name: "UPLOAD_SIZE_NOT_SPECIFIED",
98
+ message: "Parallel upload requires a known file size",
99
+ }),
100
+ );
101
+ return;
102
+ }
103
+
104
+ // Calculate segments for parallel upload
105
+ const segments = calculateSegments(
106
+ source.size,
107
+ parallelUploads,
108
+ parallelChunkSize,
109
+ );
110
+ logger.log(`Starting parallel upload with ${segments.length} segments`);
111
+
112
+ // Initialize parallel upload state
113
+ const parallelState: ParallelUploadState = {
114
+ segments: [],
115
+ totalProgress: 0,
116
+ completed: false,
117
+ failed: false,
118
+ };
119
+
120
+ // Progress tracking for aggregation
121
+ const segmentProgress = new Map<number, number>();
122
+ const segmentTotals = new Map<number, number>();
123
+
124
+ const updateTotalProgress = () => {
125
+ const totalBytes = Array.from(segmentTotals.values()).reduce(
126
+ (sum, size) => sum + size,
127
+ 0,
128
+ );
129
+ const progressBytes = Array.from(segmentProgress.values()).reduce(
130
+ (sum, progress) => sum + progress,
131
+ 0,
132
+ );
133
+ parallelState.totalProgress =
134
+ totalBytes > 0 ? progressBytes / totalBytes : 0;
135
+
136
+ // Aggregate progress callback
137
+ if (callbacks.onProgress && totalBytes > 0) {
138
+ callbacks.onProgress(`parallel-upload`, progressBytes, totalBytes);
139
+ }
140
+ };
141
+
142
+ try {
143
+ // Create upload sessions for each segment
144
+ const segmentUploads = await Promise.all(
145
+ segments.map(async (segment) => {
146
+ // Create a segmented source for this chunk
147
+ const segmentSource: FileSource = {
148
+ ...source,
149
+ size: segment.endByte - segment.startByte,
150
+ async slice(start, end) {
151
+ // Adjust slice to segment boundaries
152
+ const actualStart = segment.startByte + (start ?? 0);
153
+ const actualEnd = Math.min(
154
+ segment.startByte + (end ?? segment.endByte - segment.startByte),
155
+ segment.endByte,
156
+ );
157
+ return await source.slice(actualStart, actualEnd);
158
+ },
159
+ };
160
+
161
+ const createResult = await createUpload({
162
+ fingerprint: `${fingerprint}-segment-${segment.segmentIndex}`,
163
+ storageId,
164
+ source: segmentSource,
165
+ uploadLengthDeferred,
166
+ platformService,
167
+ metadata: {
168
+ parallelUpload: "true",
169
+ segmentIndex: segment.segmentIndex.toString(),
170
+ totalSegments: segments.length.toString(),
171
+ parentFingerprint: fingerprint,
172
+ },
173
+ checksumService,
174
+ uploadistaApi,
175
+ logger,
176
+ clientStorage,
177
+ generateId,
178
+ storeFingerprintForResuming,
179
+ openWebSocket,
180
+ closeWebSocket,
181
+ onSuccess: () => {},
182
+ onError: (error) =>
183
+ logger.log(
184
+ `Segment ${segment.segmentIndex} creation error: ${error}`,
185
+ ),
186
+ onStart: (info) => {
187
+ segmentTotals.set(segment.segmentIndex, info.size ?? 0);
188
+ updateTotalProgress();
189
+ },
190
+ });
191
+
192
+ if (!createResult) {
193
+ throw new UploadistaError({
194
+ name: "PARALLEL_SEGMENT_CREATION_FAILED",
195
+ message: `Failed to create upload segment ${segment.segmentIndex}`,
196
+ });
197
+ }
198
+
199
+ const parallelSegment: ParallelUploadSegment = {
200
+ uploadId: createResult.uploadId,
201
+ uploadIdStorageKey: createResult.uploadIdStorageKey,
202
+ segmentIndex: segment.segmentIndex,
203
+ startByte: segment.startByte,
204
+ endByte: segment.endByte,
205
+ offset: createResult.offset,
206
+ abortController: abortControllerFactory.create(),
207
+ retryTimeout: null,
208
+ };
209
+
210
+ return {
211
+ segment: parallelSegment,
212
+ source: segmentSource,
213
+ };
214
+ }),
215
+ );
216
+
217
+ // Store segments in state
218
+ parallelState.segments = segmentUploads.map((upload) => upload.segment);
219
+
220
+ // Notify start with combined upload info
221
+ callbacks.onStart?.({
222
+ uploadId: `parallel-${parallelState.segments.map((s) => s.uploadId).join(",")}`,
223
+ size: source.size,
224
+ });
225
+
226
+ // Start parallel upload for each segment
227
+ const uploadPromises = segmentUploads.map(
228
+ async ({ segment, source: segmentSource }) => {
229
+ try {
230
+ await performUpload({
231
+ uploadId: segment.uploadId,
232
+ offset: segment.offset,
233
+ source: segmentSource,
234
+ uploadLengthDeferred,
235
+ abortController: segment.abortController,
236
+ retryDelays,
237
+ smartChunker,
238
+ uploadistaApi,
239
+ platformService,
240
+ logger,
241
+ smartChunking,
242
+ metrics,
243
+ onProgress: (_, bytes, total) => {
244
+ segmentProgress.set(segment.segmentIndex, bytes);
245
+ if (total) segmentTotals.set(segment.segmentIndex, total);
246
+ updateTotalProgress();
247
+ },
248
+ onChunkComplete: (chunkSize, bytesAccepted, bytesTotal) => {
249
+ if (callbacks.onChunkComplete) {
250
+ callbacks.onChunkComplete(chunkSize, bytesAccepted, bytesTotal);
251
+ }
252
+ },
253
+ onSuccess: (_uploadFile) => {
254
+ logger.log(
255
+ `Segment ${segment.segmentIndex} completed successfully`,
256
+ );
257
+ // Mark this segment as completed
258
+ segmentProgress.set(
259
+ segment.segmentIndex,
260
+ segmentTotals.get(segment.segmentIndex) ?? 0,
261
+ );
262
+ updateTotalProgress();
263
+ },
264
+ onShouldRetry: (error, retryAttempt) => {
265
+ logger.log(
266
+ `Segment ${segment.segmentIndex} retry attempt ${retryAttempt}: ${error}`,
267
+ );
268
+ return retryAttempt < (retryDelays?.length ?? 0);
269
+ },
270
+ onRetry: (timeout) => {
271
+ segment.retryTimeout = timeout;
272
+ },
273
+ onError: (error) => {
274
+ logger.log(`Segment ${segment.segmentIndex} failed: ${error}`);
275
+ throw error;
276
+ },
277
+ });
278
+ } catch (error) {
279
+ logger.log(`Segment ${segment.segmentIndex} upload failed: ${error}`);
280
+ throw new UploadistaError({
281
+ name: "PARALLEL_SEGMENT_UPLOAD_FAILED",
282
+ message: `Segment ${segment.segmentIndex} upload failed`,
283
+ cause: error as Error,
284
+ });
285
+ }
286
+ },
287
+ );
288
+
289
+ // Wait for all segments to complete
290
+ await Promise.all(uploadPromises);
291
+
292
+ // Mark as completed
293
+ parallelState.completed = true;
294
+ logger.log("All parallel upload segments completed successfully");
295
+
296
+ // Call success callback with aggregated result
297
+ if (callbacks.onSuccess) {
298
+ const aggregatedResult: UploadFile = {
299
+ id: `parallel-${parallelState.segments.map((s) => s.uploadId).join(",")}`,
300
+ offset: source.size,
301
+ size: source.size,
302
+ storage: {
303
+ id: storageId,
304
+ type: "parallel-upload",
305
+ },
306
+ metadata: {
307
+ parallelUpload: "true",
308
+ totalSegments: segments.length.toString(),
309
+ fingerprint,
310
+ },
311
+ };
312
+ callbacks.onSuccess(aggregatedResult);
313
+ }
314
+
315
+ // Close all sources
316
+ for (const upload of segmentUploads) {
317
+ upload.source.close?.();
318
+ }
319
+
320
+ return {
321
+ parallelState,
322
+ abort: async () => {
323
+ await abortParallelUpload(
324
+ parallelState,
325
+ logger,
326
+ terminate,
327
+ closeWebSocket,
328
+ platformService,
329
+ );
330
+ },
331
+ };
332
+ } catch (error) {
333
+ parallelState.failed = true;
334
+ parallelState.error = error as Error;
335
+
336
+ // Clean up any created segments
337
+ await abortParallelUpload(
338
+ parallelState,
339
+ logger,
340
+ terminate,
341
+ closeWebSocket,
342
+ platformService,
343
+ );
344
+
345
+ callbacks.onError?.(error as Error);
346
+ throw error;
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Abort a parallel upload by cleaning up all segments
352
+ */
353
+ export async function abortParallelUpload(
354
+ state: ParallelUploadState,
355
+ logger: Logger,
356
+ terminate: (uploadId: string) => Promise<void>,
357
+ closeWebSocket: (uploadId: string) => void,
358
+ platformService: PlatformService,
359
+ ): Promise<void> {
360
+ logger.log("Aborting parallel upload...");
361
+
362
+ // Abort all segment controllers
363
+ for (const segment of state.segments) {
364
+ segment.abortController.abort();
365
+
366
+ if (segment.retryTimeout) {
367
+ platformService.clearTimeout(segment.retryTimeout);
368
+ segment.retryTimeout = null;
369
+ }
370
+
371
+ // Attempt to terminate the upload on the server
372
+ try {
373
+ await terminate(segment.uploadId);
374
+ } catch (error) {
375
+ logger.log(
376
+ `Failed to terminate segment ${segment.segmentIndex}: ${error}`,
377
+ );
378
+ }
379
+
380
+ // Close websockets
381
+ closeWebSocket(segment.uploadId);
382
+ }
383
+
384
+ state.completed = false;
385
+ state.failed = true;
386
+ logger.log("Parallel upload aborted");
387
+ }