@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,340 @@
1
+ import type { ChunkMetrics } from "../types/chunk-metrics";
2
+ import type { PerformanceInsights } from "../types/performance-insights";
3
+ import type { UploadSessionMetrics } from "../types/upload-session-metrics";
4
+
5
+ export interface UploadMetricsConfig {
6
+ maxChunkHistory?: number;
7
+ enableDetailedMetrics?: boolean;
8
+ performanceThresholds?: {
9
+ slowSpeed: number; // bytes per second
10
+ fastSpeed: number; // bytes per second
11
+ highRetryRate: number; // ratio
12
+ };
13
+ }
14
+
15
+ export class UploadMetrics {
16
+ private config: Required<UploadMetricsConfig>;
17
+ private chunkHistory: ChunkMetrics[] = [];
18
+ private currentSession: Partial<UploadSessionMetrics> = {};
19
+ private sessionStartTime = 0;
20
+
21
+ constructor(config: UploadMetricsConfig = {}) {
22
+ this.config = {
23
+ maxChunkHistory: config.maxChunkHistory ?? 1000,
24
+ enableDetailedMetrics: config.enableDetailedMetrics ?? true,
25
+ performanceThresholds: {
26
+ slowSpeed: 100 * 1024, // 100 KB/s
27
+ fastSpeed: 5 * 1024 * 1024, // 5 MB/s
28
+ highRetryRate: 0.2, // 20%
29
+ ...config.performanceThresholds,
30
+ },
31
+ };
32
+ }
33
+
34
+ startSession(
35
+ uploadId: string,
36
+ totalSize: number,
37
+ adaptiveChunkingEnabled: boolean,
38
+ ): void {
39
+ this.sessionStartTime = Date.now();
40
+ this.currentSession = {
41
+ uploadId,
42
+ totalSize,
43
+ chunksCompleted: 0,
44
+ chunksTotal: Math.ceil(totalSize / (1024 * 1024)), // rough estimate
45
+ totalDuration: 0,
46
+ totalRetries: 0,
47
+ adaptiveChunkingEnabled,
48
+ startTime: this.sessionStartTime,
49
+ };
50
+ this.chunkHistory = [];
51
+ }
52
+
53
+ recordChunk(metrics: Omit<ChunkMetrics, "timestamp">): void {
54
+ const chunkMetrics: ChunkMetrics = {
55
+ ...metrics,
56
+ timestamp: Date.now(),
57
+ };
58
+
59
+ this.chunkHistory.push(chunkMetrics);
60
+
61
+ // Keep history within limits
62
+ if (this.chunkHistory.length > this.config.maxChunkHistory) {
63
+ this.chunkHistory = this.chunkHistory.slice(-this.config.maxChunkHistory);
64
+ }
65
+
66
+ // Update session metrics
67
+ if (this.currentSession && chunkMetrics.success) {
68
+ this.currentSession.chunksCompleted =
69
+ (this.currentSession.chunksCompleted || 0) + 1;
70
+ this.currentSession.totalDuration =
71
+ (this.currentSession.totalDuration || 0) + chunkMetrics.duration;
72
+ this.currentSession.totalRetries =
73
+ (this.currentSession.totalRetries || 0) + chunkMetrics.retryCount;
74
+ }
75
+ }
76
+
77
+ endSession(): UploadSessionMetrics | null {
78
+ if (!this.currentSession.uploadId) {
79
+ return null;
80
+ }
81
+
82
+ const endTime = Date.now();
83
+ const totalDuration = endTime - this.sessionStartTime;
84
+ const successfulChunks = this.chunkHistory.filter((chunk) => chunk.success);
85
+
86
+ if (successfulChunks.length === 0) {
87
+ return null;
88
+ }
89
+
90
+ const speeds = successfulChunks.map((chunk) => chunk.speed);
91
+ const averageSpeed =
92
+ speeds.reduce((sum, speed) => sum + speed, 0) / speeds.length;
93
+ const peakSpeed = Math.max(...speeds);
94
+ const minSpeed = Math.min(...speeds);
95
+ const successRate = successfulChunks.length / this.chunkHistory.length;
96
+
97
+ const sessionMetrics: UploadSessionMetrics = {
98
+ uploadId: this.currentSession.uploadId || "",
99
+ totalSize: this.currentSession.totalSize || 0,
100
+ totalDuration,
101
+ chunksCompleted: successfulChunks.length,
102
+ chunksTotal: this.chunkHistory.length,
103
+ averageSpeed,
104
+ peakSpeed,
105
+ minSpeed,
106
+ totalRetries: this.currentSession.totalRetries || 0,
107
+ successRate,
108
+ adaptiveChunkingEnabled:
109
+ this.currentSession.adaptiveChunkingEnabled || false,
110
+ startTime: this.currentSession.startTime || 0,
111
+ endTime,
112
+ };
113
+
114
+ // Reset current session
115
+ this.currentSession = {};
116
+
117
+ return sessionMetrics;
118
+ }
119
+
120
+ getCurrentSessionMetrics(): Partial<UploadSessionMetrics> {
121
+ return { ...this.currentSession };
122
+ }
123
+
124
+ getChunkHistory(count?: number): ChunkMetrics[] {
125
+ const history = this.chunkHistory.slice();
126
+ return count ? history.slice(-count) : history;
127
+ }
128
+
129
+ getPerformanceInsights(): PerformanceInsights {
130
+ if (this.chunkHistory.length < 5) {
131
+ return {
132
+ overallEfficiency: 0,
133
+ chunkingEffectiveness: 0,
134
+ networkStability: 0,
135
+ recommendations: ["Insufficient data for analysis"],
136
+ optimalChunkSizeRange: { min: 256 * 1024, max: 2 * 1024 * 1024 },
137
+ };
138
+ }
139
+
140
+ const successfulChunks = this.chunkHistory.filter((chunk) => chunk.success);
141
+ const speeds = successfulChunks.map((chunk) => chunk.speed);
142
+
143
+ // Calculate metrics
144
+ const averageSpeed =
145
+ speeds.length > 0
146
+ ? speeds.reduce((sum, speed) => sum + speed, 0) / speeds.length
147
+ : 0;
148
+ const speedVariance = this.calculateVariance(speeds);
149
+ const speedStdDev = Math.sqrt(speedVariance);
150
+ const coefficientOfVariation = speedStdDev / averageSpeed;
151
+
152
+ // Overall efficiency based on speed and retry rate
153
+ const successRate = successfulChunks.length / this.chunkHistory.length;
154
+ const speedScore = Math.min(
155
+ 1,
156
+ averageSpeed / this.config.performanceThresholds.fastSpeed,
157
+ );
158
+ const overallEfficiency = speedScore * 0.7 + successRate * 0.3;
159
+
160
+ // Network stability (lower coefficient of variation = higher stability)
161
+ const networkStability = Math.max(
162
+ 0,
163
+ 1 - Math.min(1, coefficientOfVariation),
164
+ );
165
+
166
+ // Chunking effectiveness based on how well chunk sizes correlate with performance
167
+ const chunkingEffectiveness =
168
+ this.calculateChunkingEffectiveness(successfulChunks);
169
+
170
+ // Generate recommendations
171
+ const recommendations = this.generateRecommendations(
172
+ averageSpeed,
173
+ successRate,
174
+ coefficientOfVariation,
175
+ );
176
+
177
+ // Calculate optimal chunk size range
178
+ const optimalChunkSizeRange =
179
+ this.calculateOptimalChunkSizeRange(successfulChunks);
180
+
181
+ return {
182
+ overallEfficiency,
183
+ chunkingEffectiveness,
184
+ networkStability,
185
+ recommendations,
186
+ optimalChunkSizeRange,
187
+ };
188
+ }
189
+
190
+ exportMetrics(): {
191
+ session: Partial<UploadSessionMetrics>;
192
+ chunks: ChunkMetrics[];
193
+ insights: PerformanceInsights;
194
+ } {
195
+ return {
196
+ session: this.getCurrentSessionMetrics(),
197
+ chunks: this.getChunkHistory(),
198
+ insights: this.getPerformanceInsights(),
199
+ };
200
+ }
201
+
202
+ reset(): void {
203
+ this.chunkHistory = [];
204
+ this.currentSession = {};
205
+ this.sessionStartTime = 0;
206
+ }
207
+
208
+ private calculateVariance(values: number[]): number {
209
+ if (values.length === 0) return 0;
210
+
211
+ const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
212
+ const squaredDifferences = values.map((value) => (value - mean) ** 2);
213
+ return (
214
+ squaredDifferences.reduce((sum, diff) => sum + diff, 0) / values.length
215
+ );
216
+ }
217
+
218
+ private calculateChunkingEffectiveness(chunks: ChunkMetrics[]): number {
219
+ if (chunks.length < 3) return 0.5;
220
+
221
+ // Look for correlation between chunk size and upload speed
222
+ // Better chunking should show consistent performance across different sizes
223
+ const sizeGroups = this.groupChunksBySize(chunks);
224
+
225
+ if (Object.keys(sizeGroups).length < 2) return 0.5;
226
+
227
+ // Calculate coefficient of variation for each size group
228
+ const groupVariations = Object.values(sizeGroups).map((group) => {
229
+ const speeds = group.map((chunk) => chunk.speed);
230
+ const mean =
231
+ speeds.reduce((sum, speed) => sum + speed, 0) / speeds.length;
232
+ const variance = this.calculateVariance(speeds);
233
+ return Math.sqrt(variance) / mean;
234
+ });
235
+
236
+ // Lower average variation indicates better chunking effectiveness
237
+ const averageVariation =
238
+ groupVariations.reduce((sum, cv) => sum + cv, 0) / groupVariations.length;
239
+ return Math.max(0, 1 - Math.min(1, averageVariation));
240
+ }
241
+
242
+ private groupChunksBySize(
243
+ chunks: ChunkMetrics[],
244
+ ): Record<string, ChunkMetrics[]> {
245
+ const groups: Record<string, ChunkMetrics[]> = {};
246
+
247
+ chunks.forEach((chunk) => {
248
+ // Group by size ranges (64KB, 128KB, 256KB, 512KB, 1MB, 2MB, 4MB, 8MB+)
249
+ let sizeGroup: string;
250
+ if (chunk.size < 128 * 1024) sizeGroup = "64KB";
251
+ else if (chunk.size < 256 * 1024) sizeGroup = "128KB";
252
+ else if (chunk.size < 512 * 1024) sizeGroup = "256KB";
253
+ else if (chunk.size < 1024 * 1024) sizeGroup = "512KB";
254
+ else if (chunk.size < 2 * 1024 * 1024) sizeGroup = "1MB";
255
+ else if (chunk.size < 4 * 1024 * 1024) sizeGroup = "2MB";
256
+ else if (chunk.size < 8 * 1024 * 1024) sizeGroup = "4MB";
257
+ else sizeGroup = "8MB+";
258
+
259
+ if (!groups[sizeGroup]) groups[sizeGroup] = [];
260
+ const group = groups[sizeGroup];
261
+ if (group) group.push(chunk);
262
+ });
263
+
264
+ return groups;
265
+ }
266
+
267
+ private generateRecommendations(
268
+ averageSpeed: number,
269
+ successRate: number,
270
+ coefficientOfVariation: number,
271
+ ): string[] {
272
+ const recommendations: string[] = [];
273
+
274
+ if (averageSpeed < this.config.performanceThresholds.slowSpeed) {
275
+ recommendations.push(
276
+ "Consider using smaller chunk sizes for better performance on slow connections",
277
+ );
278
+ }
279
+
280
+ if (averageSpeed > this.config.performanceThresholds.fastSpeed) {
281
+ recommendations.push(
282
+ "Network is fast - larger chunk sizes may improve efficiency",
283
+ );
284
+ }
285
+
286
+ if (successRate < 0.9) {
287
+ recommendations.push(
288
+ "High failure rate detected - consider more conservative chunking strategy",
289
+ );
290
+ }
291
+
292
+ if (coefficientOfVariation > 0.5) {
293
+ recommendations.push(
294
+ "Network appears unstable - smaller, more frequent chunks may be more reliable",
295
+ );
296
+ }
297
+
298
+ if (
299
+ coefficientOfVariation < 0.2 &&
300
+ averageSpeed > this.config.performanceThresholds.slowSpeed
301
+ ) {
302
+ recommendations.push(
303
+ "Stable network detected - larger chunks may improve efficiency",
304
+ );
305
+ }
306
+
307
+ if (recommendations.length === 0) {
308
+ recommendations.push(
309
+ "Performance appears optimal with current configuration",
310
+ );
311
+ }
312
+
313
+ return recommendations;
314
+ }
315
+
316
+ private calculateOptimalChunkSizeRange(chunks: ChunkMetrics[]): {
317
+ min: number;
318
+ max: number;
319
+ } {
320
+ if (chunks.length < 5) {
321
+ return { min: 256 * 1024, max: 2 * 1024 * 1024 };
322
+ }
323
+
324
+ // Find chunks with best performance (top 30% by speed)
325
+ const sortedBySpeed = chunks.slice().sort((a, b) => b.speed - a.speed);
326
+ const topPerformers = sortedBySpeed.slice(
327
+ 0,
328
+ Math.ceil(chunks.length * 0.3),
329
+ );
330
+
331
+ const topSizes = topPerformers.map((chunk) => chunk.size);
332
+ const minOptimal = Math.min(...topSizes);
333
+ const maxOptimal = Math.max(...topSizes);
334
+
335
+ return {
336
+ min: Math.max(64 * 1024, minOptimal), // At least 64KB
337
+ max: Math.min(32 * 1024 * 1024, maxOptimal), // At most 32MB
338
+ };
339
+ }
340
+ }
@@ -0,0 +1,87 @@
1
+ import type { IdGenerationService } from "../services/id-generation-service";
2
+ import type { ClientStorage } from "../storage/client-storage";
3
+ import type { PreviousUpload } from "../types/previous-upload";
4
+
5
+ /**
6
+ * Find previous uploads by fingerprint
7
+ */
8
+ export async function findPreviousUploads(
9
+ clientStorage: ClientStorage,
10
+ fingerprint: string,
11
+ ): Promise<PreviousUpload[]> {
12
+ return clientStorage.findUploadsByFingerprint(fingerprint);
13
+ }
14
+
15
+ /**
16
+ * Resume from a previous upload
17
+ */
18
+ export function resumeFromPreviousUpload(previousUpload: PreviousUpload): {
19
+ uploadId: string | null;
20
+ parallelUploadUrls: string[] | undefined;
21
+ clientStorageKey: string | null;
22
+ } {
23
+ return {
24
+ uploadId: previousUpload.uploadId ?? null,
25
+ parallelUploadUrls: previousUpload.parallelUploadUrls,
26
+ clientStorageKey: previousUpload.clientStorageKey,
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Add the upload URL to the URL storage, if possible.
32
+ */
33
+ export async function saveUploadInClientStorage({
34
+ clientStorage,
35
+ fingerprint,
36
+ size,
37
+ metadata,
38
+ clientStorageKey,
39
+ storeFingerprintForResuming,
40
+ generateId,
41
+ }: {
42
+ clientStorage: ClientStorage;
43
+ fingerprint: string;
44
+ size: number;
45
+ metadata: Record<string, string | number | boolean>;
46
+ clientStorageKey: string | null;
47
+ storeFingerprintForResuming: boolean;
48
+ generateId: IdGenerationService;
49
+ }): Promise<string | undefined> {
50
+ // We do not store the upload key
51
+ // - if it was disabled in the option, or
52
+ // - if no fingerprint was calculated for the input (i.e. a stream), or
53
+ // - if the key is already stored.
54
+ if (
55
+ !storeFingerprintForResuming ||
56
+ !fingerprint ||
57
+ clientStorageKey != null
58
+ ) {
59
+ return undefined;
60
+ }
61
+
62
+ const storedUpload: PreviousUpload = {
63
+ size,
64
+ metadata,
65
+ creationTime: new Date().toString(),
66
+ clientStorageKey: fingerprint,
67
+ };
68
+
69
+ const newClientStorageKey = await clientStorage.addUpload(
70
+ fingerprint,
71
+ storedUpload,
72
+ { generateId },
73
+ );
74
+
75
+ return newClientStorageKey;
76
+ }
77
+
78
+ /**
79
+ * Remove the entry in the URL storage, if it has been saved before.
80
+ */
81
+ export async function removeFromClientStorage(
82
+ clientStorage: ClientStorage,
83
+ clientStorageKey: string,
84
+ ): Promise<void> {
85
+ if (!clientStorageKey) return;
86
+ await clientStorage.removeUpload(clientStorageKey);
87
+ }