@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.
- package/.turbo/turbo-build.log +5 -0
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/auth/auth-http-client.d.ts +50 -0
- package/dist/auth/auth-http-client.d.ts.map +1 -0
- package/dist/auth/auth-http-client.js +110 -0
- package/dist/auth/direct-auth.d.ts +38 -0
- package/dist/auth/direct-auth.d.ts.map +1 -0
- package/dist/auth/direct-auth.js +95 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/no-auth.d.ts +26 -0
- package/dist/auth/no-auth.d.ts.map +1 -0
- package/dist/auth/no-auth.js +33 -0
- package/dist/auth/saas-auth.d.ts +80 -0
- package/dist/auth/saas-auth.d.ts.map +1 -0
- package/dist/auth/saas-auth.js +167 -0
- package/dist/auth/types.d.ts +101 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +8 -0
- package/dist/chunk-buffer.d.ts +209 -0
- package/dist/chunk-buffer.d.ts.map +1 -0
- package/dist/chunk-buffer.js +236 -0
- package/dist/client/create-uploadista-client.d.ts +369 -0
- package/dist/client/create-uploadista-client.d.ts.map +1 -0
- package/dist/client/create-uploadista-client.js +518 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/uploadista-api.d.ts +284 -0
- package/dist/client/uploadista-api.d.ts.map +1 -0
- package/dist/client/uploadista-api.js +444 -0
- package/dist/client/uploadista-websocket-manager.d.ts +110 -0
- package/dist/client/uploadista-websocket-manager.d.ts.map +1 -0
- package/dist/client/uploadista-websocket-manager.js +207 -0
- package/dist/error.d.ts +106 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +69 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/logger.d.ts +70 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +59 -0
- package/dist/mock-data-store.d.ts +30 -0
- package/dist/mock-data-store.d.ts.map +1 -0
- package/dist/mock-data-store.js +88 -0
- package/dist/network-monitor.d.ts +262 -0
- package/dist/network-monitor.d.ts.map +1 -0
- package/dist/network-monitor.js +291 -0
- package/dist/services/abort-controller-service.d.ts +19 -0
- package/dist/services/abort-controller-service.d.ts.map +1 -0
- package/dist/services/abort-controller-service.js +4 -0
- package/dist/services/checksum-service.d.ts +4 -0
- package/dist/services/checksum-service.d.ts.map +1 -0
- package/dist/services/checksum-service.js +1 -0
- package/dist/services/file-reader-service.d.ts +38 -0
- package/dist/services/file-reader-service.d.ts.map +1 -0
- package/dist/services/file-reader-service.js +4 -0
- package/dist/services/fingerprint-service.d.ts +4 -0
- package/dist/services/fingerprint-service.d.ts.map +1 -0
- package/dist/services/fingerprint-service.js +1 -0
- package/dist/services/http-client.d.ts +182 -0
- package/dist/services/http-client.d.ts.map +1 -0
- package/dist/services/http-client.js +1 -0
- package/dist/services/id-generation-service.d.ts +10 -0
- package/dist/services/id-generation-service.d.ts.map +1 -0
- package/dist/services/id-generation-service.js +1 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +10 -0
- package/dist/services/platform-service.d.ts +48 -0
- package/dist/services/platform-service.d.ts.map +1 -0
- package/dist/services/platform-service.js +10 -0
- package/dist/services/service-container.d.ts +25 -0
- package/dist/services/service-container.d.ts.map +1 -0
- package/dist/services/service-container.js +1 -0
- package/dist/services/storage-service.d.ts +26 -0
- package/dist/services/storage-service.d.ts.map +1 -0
- package/dist/services/storage-service.js +1 -0
- package/dist/services/websocket-service.d.ts +36 -0
- package/dist/services/websocket-service.d.ts.map +1 -0
- package/dist/services/websocket-service.js +4 -0
- package/dist/smart-chunker.d.ts +72 -0
- package/dist/smart-chunker.d.ts.map +1 -0
- package/dist/smart-chunker.js +317 -0
- package/dist/storage/client-storage.d.ts +148 -0
- package/dist/storage/client-storage.d.ts.map +1 -0
- package/dist/storage/client-storage.js +62 -0
- package/dist/storage/in-memory-storage-service.d.ts +7 -0
- package/dist/storage/in-memory-storage-service.d.ts.map +1 -0
- package/dist/storage/in-memory-storage-service.js +24 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/types/buffered-chunk.d.ts +6 -0
- package/dist/types/buffered-chunk.d.ts.map +1 -0
- package/dist/types/buffered-chunk.js +1 -0
- package/dist/types/chunk-metrics.d.ts +12 -0
- package/dist/types/chunk-metrics.d.ts.map +1 -0
- package/dist/types/chunk-metrics.js +1 -0
- package/dist/types/flow-result.d.ts +11 -0
- package/dist/types/flow-result.d.ts.map +1 -0
- package/dist/types/flow-result.js +1 -0
- package/dist/types/flow-upload-config.d.ts +54 -0
- package/dist/types/flow-upload-config.d.ts.map +1 -0
- package/dist/types/flow-upload-config.js +1 -0
- package/dist/types/flow-upload-item.d.ts +16 -0
- package/dist/types/flow-upload-item.d.ts.map +1 -0
- package/dist/types/flow-upload-item.js +1 -0
- package/dist/types/flow-upload-options.d.ts +41 -0
- package/dist/types/flow-upload-options.d.ts.map +1 -0
- package/dist/types/flow-upload-options.js +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/multi-flow-upload-options.d.ts +33 -0
- package/dist/types/multi-flow-upload-options.d.ts.map +1 -0
- package/dist/types/multi-flow-upload-options.js +1 -0
- package/dist/types/multi-flow-upload-state.d.ts +9 -0
- package/dist/types/multi-flow-upload-state.d.ts.map +1 -0
- package/dist/types/multi-flow-upload-state.js +1 -0
- package/dist/types/performance-insights.d.ts +11 -0
- package/dist/types/performance-insights.d.ts.map +1 -0
- package/dist/types/performance-insights.js +1 -0
- package/dist/types/previous-upload.d.ts +20 -0
- package/dist/types/previous-upload.d.ts.map +1 -0
- package/dist/types/previous-upload.js +9 -0
- package/dist/types/upload-options.d.ts +40 -0
- package/dist/types/upload-options.d.ts.map +1 -0
- package/dist/types/upload-options.js +1 -0
- package/dist/types/upload-response.d.ts +6 -0
- package/dist/types/upload-response.d.ts.map +1 -0
- package/dist/types/upload-response.js +1 -0
- package/dist/types/upload-result.d.ts +57 -0
- package/dist/types/upload-result.d.ts.map +1 -0
- package/dist/types/upload-result.js +1 -0
- package/dist/types/upload-session-metrics.d.ts +16 -0
- package/dist/types/upload-session-metrics.d.ts.map +1 -0
- package/dist/types/upload-session-metrics.js +1 -0
- package/dist/upload/chunk-upload.d.ts +40 -0
- package/dist/upload/chunk-upload.d.ts.map +1 -0
- package/dist/upload/chunk-upload.js +82 -0
- package/dist/upload/flow-upload.d.ts +48 -0
- package/dist/upload/flow-upload.d.ts.map +1 -0
- package/dist/upload/flow-upload.js +240 -0
- package/dist/upload/index.d.ts +3 -0
- package/dist/upload/index.d.ts.map +1 -0
- package/dist/upload/index.js +2 -0
- package/dist/upload/parallel-upload.d.ts +65 -0
- package/dist/upload/parallel-upload.d.ts.map +1 -0
- package/dist/upload/parallel-upload.js +231 -0
- package/dist/upload/single-upload.d.ts +118 -0
- package/dist/upload/single-upload.d.ts.map +1 -0
- package/dist/upload/single-upload.js +332 -0
- package/dist/upload/upload-manager.d.ts +30 -0
- package/dist/upload/upload-manager.d.ts.map +1 -0
- package/dist/upload/upload-manager.js +57 -0
- package/dist/upload/upload-metrics.d.ts +37 -0
- package/dist/upload/upload-metrics.d.ts.map +1 -0
- package/dist/upload/upload-metrics.js +236 -0
- package/dist/upload/upload-storage.d.ts +32 -0
- package/dist/upload/upload-storage.d.ts.map +1 -0
- package/dist/upload/upload-storage.js +46 -0
- package/dist/upload/upload-strategy.d.ts +66 -0
- package/dist/upload/upload-strategy.d.ts.map +1 -0
- package/dist/upload/upload-strategy.js +171 -0
- package/dist/upload/upload-utils.d.ts +26 -0
- package/dist/upload/upload-utils.d.ts.map +1 -0
- package/dist/upload/upload-utils.js +80 -0
- package/package.json +29 -0
- package/src/__tests__/smart-chunking.test.ts +399 -0
- package/src/auth/__tests__/auth-http-client.test.ts +327 -0
- package/src/auth/__tests__/direct-auth.test.ts +135 -0
- package/src/auth/__tests__/no-auth.test.ts +40 -0
- package/src/auth/__tests__/saas-auth.test.ts +337 -0
- package/src/auth/auth-http-client.ts +150 -0
- package/src/auth/direct-auth.ts +121 -0
- package/src/auth/index.ts +5 -0
- package/src/auth/no-auth.ts +39 -0
- package/src/auth/saas-auth.ts +218 -0
- package/src/auth/types.ts +105 -0
- package/src/chunk-buffer.ts +287 -0
- package/src/client/create-uploadista-client.ts +901 -0
- package/src/client/index.ts +3 -0
- package/src/client/uploadista-api.ts +857 -0
- package/src/client/uploadista-websocket-manager.ts +275 -0
- package/src/error.ts +149 -0
- package/src/index.ts +13 -0
- package/src/logger.ts +104 -0
- package/src/mock-data-store.ts +97 -0
- package/src/network-monitor.ts +445 -0
- package/src/services/abort-controller-service.ts +21 -0
- package/src/services/checksum-service.ts +3 -0
- package/src/services/file-reader-service.ts +44 -0
- package/src/services/fingerprint-service.ts +6 -0
- package/src/services/http-client.ts +229 -0
- package/src/services/id-generation-service.ts +9 -0
- package/src/services/index.ts +10 -0
- package/src/services/platform-service.ts +65 -0
- package/src/services/service-container.ts +24 -0
- package/src/services/storage-service.ts +29 -0
- package/src/services/websocket-service.ts +33 -0
- package/src/smart-chunker.ts +451 -0
- package/src/storage/client-storage.ts +186 -0
- package/src/storage/in-memory-storage-service.ts +33 -0
- package/src/storage/index.ts +2 -0
- package/src/types/buffered-chunk.ts +5 -0
- package/src/types/chunk-metrics.ts +11 -0
- package/src/types/flow-result.ts +14 -0
- package/src/types/flow-upload-config.ts +56 -0
- package/src/types/flow-upload-item.ts +16 -0
- package/src/types/flow-upload-options.ts +56 -0
- package/src/types/index.ts +13 -0
- package/src/types/multi-flow-upload-options.ts +39 -0
- package/src/types/multi-flow-upload-state.ts +9 -0
- package/src/types/performance-insights.ts +7 -0
- package/src/types/previous-upload.ts +22 -0
- package/src/types/upload-options.ts +56 -0
- package/src/types/upload-response.ts +6 -0
- package/src/types/upload-result.ts +60 -0
- package/src/types/upload-session-metrics.ts +15 -0
- package/src/upload/chunk-upload.ts +151 -0
- package/src/upload/flow-upload.ts +367 -0
- package/src/upload/index.ts +2 -0
- package/src/upload/parallel-upload.ts +387 -0
- package/src/upload/single-upload.ts +554 -0
- package/src/upload/upload-manager.ts +106 -0
- package/src/upload/upload-metrics.ts +340 -0
- package/src/upload/upload-storage.ts +87 -0
- package/src/upload/upload-strategy.ts +296 -0
- package/src/upload/upload-utils.ts +114 -0
- package/tsconfig.json +23 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,367 @@
|
|
|
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 { AbortControllerLike } from "../services";
|
|
6
|
+
import type { FileSource } from "../services/file-reader-service";
|
|
7
|
+
import type { PlatformService, Timeout } from "../services/platform-service";
|
|
8
|
+
import type { SmartChunker, SmartChunkerConfig } from "../smart-chunker";
|
|
9
|
+
import type { FlowUploadConfig } from "../types/flow-upload-config";
|
|
10
|
+
|
|
11
|
+
import { shouldRetry } from "./chunk-upload";
|
|
12
|
+
import type { Callbacks } from "./single-upload";
|
|
13
|
+
import type { UploadMetrics } from "./upload-metrics";
|
|
14
|
+
import { inStatusCategory } from "./upload-utils";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Start a flow-based upload by initializing the streaming input node
|
|
18
|
+
*/
|
|
19
|
+
export async function startFlowUpload({
|
|
20
|
+
source,
|
|
21
|
+
flowConfig,
|
|
22
|
+
uploadistaApi,
|
|
23
|
+
logger,
|
|
24
|
+
platformService,
|
|
25
|
+
openWebSocket,
|
|
26
|
+
closeWebSocket,
|
|
27
|
+
...callbacks
|
|
28
|
+
}: {
|
|
29
|
+
source: FileSource;
|
|
30
|
+
flowConfig: FlowUploadConfig;
|
|
31
|
+
uploadistaApi: UploadistaApi;
|
|
32
|
+
logger: Logger;
|
|
33
|
+
platformService: PlatformService;
|
|
34
|
+
openWebSocket: (jobId: string) => void;
|
|
35
|
+
closeWebSocket: (jobId: string) => void;
|
|
36
|
+
} & Callbacks): Promise<
|
|
37
|
+
{ jobId: string; uploadFile: UploadFile; inputNodeId: string } | undefined
|
|
38
|
+
> {
|
|
39
|
+
const { flowId, storageId } = flowConfig;
|
|
40
|
+
|
|
41
|
+
// Get the flow to find the streaming input node
|
|
42
|
+
const { flow } = await uploadistaApi.getFlow(flowId);
|
|
43
|
+
|
|
44
|
+
// Find the streaming-input-node in the flow
|
|
45
|
+
const inputNode = flow.nodes.find((node) => node.type === "input");
|
|
46
|
+
|
|
47
|
+
if (!inputNode) {
|
|
48
|
+
const error = new UploadistaError({
|
|
49
|
+
name: "FLOW_INCOMPATIBLE",
|
|
50
|
+
message: `Flow ${flowId} does not have a streaming input node. The flow must contain a node with type "input" to support flow uploads.`,
|
|
51
|
+
});
|
|
52
|
+
callbacks.onError?.(error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const inputNodeId = inputNode.id;
|
|
57
|
+
|
|
58
|
+
// Step 1: Initialize the flow with init operation
|
|
59
|
+
const metadata = {
|
|
60
|
+
originalName: source.name ?? "unknown",
|
|
61
|
+
mimeType: source.type ?? "application/octet-stream",
|
|
62
|
+
size: source.size ?? 0,
|
|
63
|
+
...flowConfig.metadata,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
logger.log(`Starting flow upload for flow ${flowId}, node ${inputNodeId}`);
|
|
67
|
+
|
|
68
|
+
const { status, job } = await uploadistaApi.runFlow(flowId, storageId, {
|
|
69
|
+
[inputNodeId]: {
|
|
70
|
+
operation: "init",
|
|
71
|
+
storageId,
|
|
72
|
+
metadata,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const jobId = job.id;
|
|
77
|
+
|
|
78
|
+
if (!inStatusCategory(status, 200) || !jobId) {
|
|
79
|
+
const error = new UploadistaError({
|
|
80
|
+
name: "FLOW_INIT_FAILED",
|
|
81
|
+
message: "Failed to initialize flow upload",
|
|
82
|
+
});
|
|
83
|
+
callbacks.onError?.(error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
callbacks.onJobStart?.(jobId);
|
|
88
|
+
|
|
89
|
+
logger.log(`Flow job ${jobId} created, opening WebSocket`);
|
|
90
|
+
|
|
91
|
+
// Open WebSocket to listen for flow events
|
|
92
|
+
// Events are buffered in the Durable Object until connection is established
|
|
93
|
+
openWebSocket(jobId);
|
|
94
|
+
|
|
95
|
+
logger.log(`Waiting for upload ID from node`);
|
|
96
|
+
|
|
97
|
+
// Step 2: Wait for the streaming-input-node to pause and return the upload file
|
|
98
|
+
// Poll job status until paused (with timeout)
|
|
99
|
+
const maxAttempts = 60; // 30 seconds total
|
|
100
|
+
const pollInterval = 500; // 0.5 second
|
|
101
|
+
let attempts = 0;
|
|
102
|
+
let jobStatus = await uploadistaApi.getJobStatus(jobId);
|
|
103
|
+
|
|
104
|
+
while (jobStatus.status !== "paused" && attempts < maxAttempts) {
|
|
105
|
+
await new Promise<void>((resolve) =>
|
|
106
|
+
platformService.setTimeout(resolve, pollInterval),
|
|
107
|
+
);
|
|
108
|
+
jobStatus = await uploadistaApi.getJobStatus(jobId);
|
|
109
|
+
attempts++;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (jobStatus.status !== "paused") {
|
|
113
|
+
const error = new UploadistaError({
|
|
114
|
+
name: "FLOW_TIMEOUT",
|
|
115
|
+
message: `Flow did not pause after init (status: ${jobStatus.status})`,
|
|
116
|
+
});
|
|
117
|
+
callbacks.onError?.(error);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Get the upload file from streaming input node task result
|
|
122
|
+
const streamingInputTask = jobStatus.tasks.find(
|
|
123
|
+
(task) => task.nodeId === inputNodeId,
|
|
124
|
+
);
|
|
125
|
+
const uploadFile = streamingInputTask?.result as UploadFile;
|
|
126
|
+
|
|
127
|
+
if (!uploadFile?.id) {
|
|
128
|
+
const error = new UploadistaError({
|
|
129
|
+
name: "FLOW_NO_UPLOAD_ID",
|
|
130
|
+
message: "Flow did not return upload ID after init",
|
|
131
|
+
});
|
|
132
|
+
callbacks.onError?.(error);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
logger.log(`Upload ID received: ${uploadFile.id}`);
|
|
137
|
+
|
|
138
|
+
callbacks.onStart?.({
|
|
139
|
+
uploadId: uploadFile.id,
|
|
140
|
+
size: source.size ?? null,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return { jobId, uploadFile, inputNodeId };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Upload chunks directly to the upload API (not through continueFlow)
|
|
148
|
+
* This is more efficient and reuses the existing upload infrastructure
|
|
149
|
+
*/
|
|
150
|
+
export async function performFlowUpload({
|
|
151
|
+
jobId,
|
|
152
|
+
uploadFile,
|
|
153
|
+
inputNodeId,
|
|
154
|
+
offset,
|
|
155
|
+
source,
|
|
156
|
+
retryAttempt = 0,
|
|
157
|
+
abortController,
|
|
158
|
+
retryDelays,
|
|
159
|
+
smartChunker,
|
|
160
|
+
uploadistaApi,
|
|
161
|
+
logger,
|
|
162
|
+
smartChunking,
|
|
163
|
+
metrics,
|
|
164
|
+
platformService,
|
|
165
|
+
onRetry,
|
|
166
|
+
...callbacks
|
|
167
|
+
}: {
|
|
168
|
+
jobId: string;
|
|
169
|
+
uploadFile: UploadFile;
|
|
170
|
+
inputNodeId: string;
|
|
171
|
+
offset: number;
|
|
172
|
+
retryAttempt?: number;
|
|
173
|
+
source: FileSource;
|
|
174
|
+
abortController: AbortControllerLike;
|
|
175
|
+
retryDelays: number[] | undefined;
|
|
176
|
+
smartChunker: SmartChunker;
|
|
177
|
+
uploadistaApi: UploadistaApi;
|
|
178
|
+
logger: Logger;
|
|
179
|
+
smartChunking?: SmartChunkerConfig;
|
|
180
|
+
metrics: UploadMetrics;
|
|
181
|
+
platformService: PlatformService;
|
|
182
|
+
onRetry?: (timeout: Timeout) => void;
|
|
183
|
+
} & Callbacks): Promise<void> {
|
|
184
|
+
let offsetBeforeRetry = offset;
|
|
185
|
+
let currentOffset = offset;
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
// Get optimal chunk size
|
|
189
|
+
const remainingBytes = source.size ? source.size - offset : undefined;
|
|
190
|
+
const chunkSizeDecision = smartChunker.getNextChunkSize(remainingBytes);
|
|
191
|
+
const chunkSize = chunkSizeDecision.size;
|
|
192
|
+
const endByte = Math.min(offset + chunkSize, source.size ?? 0);
|
|
193
|
+
const sliceResult = await source.slice(offset, endByte);
|
|
194
|
+
|
|
195
|
+
if (!sliceResult || !sliceResult.value) {
|
|
196
|
+
throw new UploadistaError({
|
|
197
|
+
name: "NETWORK_ERROR",
|
|
198
|
+
message: "Failed to read chunk from file",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const chunkData = sliceResult.value;
|
|
203
|
+
|
|
204
|
+
// Upload chunk directly to upload API (bypassing flow)
|
|
205
|
+
const startTime = Date.now();
|
|
206
|
+
|
|
207
|
+
const res = await uploadistaApi.uploadChunk(uploadFile.id, chunkData, {
|
|
208
|
+
abortController,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const duration = Date.now() - startTime;
|
|
212
|
+
|
|
213
|
+
if (!res.upload) {
|
|
214
|
+
throw new UploadistaError({
|
|
215
|
+
name: "UPLOAD_CHUNK_FAILED",
|
|
216
|
+
message: "Upload chunk response missing upload data",
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
currentOffset = res.upload.offset;
|
|
221
|
+
|
|
222
|
+
callbacks.onProgress?.(uploadFile.id, currentOffset, source.size ?? 0);
|
|
223
|
+
callbacks.onChunkComplete?.(
|
|
224
|
+
currentOffset - offset,
|
|
225
|
+
offset,
|
|
226
|
+
source.size ?? 0,
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// Record detailed chunk metrics
|
|
230
|
+
if (smartChunking?.enabled !== false) {
|
|
231
|
+
const chunkIndex = Math.floor(offset / chunkSize);
|
|
232
|
+
|
|
233
|
+
metrics.recordChunk({
|
|
234
|
+
chunkIndex,
|
|
235
|
+
size: chunkSize,
|
|
236
|
+
duration,
|
|
237
|
+
speed: chunkSize / (duration / 1000),
|
|
238
|
+
success: true,
|
|
239
|
+
retryCount: retryAttempt,
|
|
240
|
+
networkCondition:
|
|
241
|
+
smartChunker.getLastDecision()?.networkCondition?.type,
|
|
242
|
+
chunkingStrategy: smartChunker.getLastDecision()?.strategy,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Update smart chunker with connection metrics
|
|
246
|
+
const connectionMetrics = uploadistaApi.getConnectionMetrics();
|
|
247
|
+
smartChunker.updateConnectionMetrics(connectionMetrics);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Check if upload is complete after uploading the chunk
|
|
251
|
+
if (currentOffset >= (source.size ?? 0)) {
|
|
252
|
+
if (source) source.close();
|
|
253
|
+
|
|
254
|
+
// Complete metrics session
|
|
255
|
+
if (smartChunking?.enabled !== false) {
|
|
256
|
+
const sessionMetrics = metrics.endSession();
|
|
257
|
+
if (sessionMetrics) {
|
|
258
|
+
logger.log(
|
|
259
|
+
`Flow upload completed: ${sessionMetrics.totalSize} bytes in ${sessionMetrics.totalDuration}ms, avg speed: ${Math.round(sessionMetrics.averageSpeed / 1024)}KB/s`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Upload is complete - finalize the flow
|
|
265
|
+
logger.log(`Finalizing flow upload for job ${jobId}`);
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
await uploadistaApi.continueFlow(
|
|
269
|
+
jobId,
|
|
270
|
+
inputNodeId,
|
|
271
|
+
{
|
|
272
|
+
operation: "finalize",
|
|
273
|
+
uploadId: uploadFile.id,
|
|
274
|
+
},
|
|
275
|
+
{ contentType: "application/json" },
|
|
276
|
+
);
|
|
277
|
+
} catch (err) {
|
|
278
|
+
// Finalization errors should not trigger chunk retry logic
|
|
279
|
+
const error = new UploadistaError({
|
|
280
|
+
name: "FLOW_FINALIZE_FAILED",
|
|
281
|
+
message: `Failed to finalize flow upload for job ${jobId}`,
|
|
282
|
+
cause: err as Error,
|
|
283
|
+
});
|
|
284
|
+
callbacks.onError?.(error);
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Continue uploading next chunk
|
|
291
|
+
await performFlowUpload({
|
|
292
|
+
jobId,
|
|
293
|
+
uploadFile,
|
|
294
|
+
inputNodeId,
|
|
295
|
+
offset: currentOffset,
|
|
296
|
+
source,
|
|
297
|
+
platformService,
|
|
298
|
+
retryDelays,
|
|
299
|
+
smartChunker,
|
|
300
|
+
uploadistaApi,
|
|
301
|
+
logger,
|
|
302
|
+
smartChunking,
|
|
303
|
+
metrics,
|
|
304
|
+
onRetry,
|
|
305
|
+
abortController,
|
|
306
|
+
...callbacks,
|
|
307
|
+
});
|
|
308
|
+
} catch (err) {
|
|
309
|
+
// Retry logic similar to single-upload
|
|
310
|
+
if (retryDelays != null) {
|
|
311
|
+
const shouldResetDelays =
|
|
312
|
+
offset != null && currentOffset > offsetBeforeRetry;
|
|
313
|
+
if (shouldResetDelays) {
|
|
314
|
+
retryAttempt = 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const castedErr = !(err instanceof UploadistaError)
|
|
318
|
+
? new UploadistaError({
|
|
319
|
+
name: "NETWORK_ERROR",
|
|
320
|
+
message: "Network error during flow upload",
|
|
321
|
+
cause: err as Error,
|
|
322
|
+
})
|
|
323
|
+
: err;
|
|
324
|
+
|
|
325
|
+
if (
|
|
326
|
+
shouldRetry(
|
|
327
|
+
platformService,
|
|
328
|
+
castedErr,
|
|
329
|
+
retryAttempt,
|
|
330
|
+
retryDelays,
|
|
331
|
+
callbacks.onShouldRetry,
|
|
332
|
+
)
|
|
333
|
+
) {
|
|
334
|
+
const delay = retryDelays[retryAttempt];
|
|
335
|
+
offsetBeforeRetry = offset;
|
|
336
|
+
|
|
337
|
+
const timeout = platformService.setTimeout(async () => {
|
|
338
|
+
await performFlowUpload({
|
|
339
|
+
jobId,
|
|
340
|
+
uploadFile,
|
|
341
|
+
inputNodeId,
|
|
342
|
+
offset,
|
|
343
|
+
source,
|
|
344
|
+
retryAttempt: retryAttempt + 1,
|
|
345
|
+
retryDelays,
|
|
346
|
+
smartChunker,
|
|
347
|
+
uploadistaApi,
|
|
348
|
+
logger,
|
|
349
|
+
smartChunking,
|
|
350
|
+
metrics,
|
|
351
|
+
platformService,
|
|
352
|
+
onRetry,
|
|
353
|
+
abortController,
|
|
354
|
+
...callbacks,
|
|
355
|
+
});
|
|
356
|
+
}, delay);
|
|
357
|
+
onRetry?.(timeout);
|
|
358
|
+
} else {
|
|
359
|
+
throw new UploadistaError({
|
|
360
|
+
name: "UPLOAD_CHUNK_FAILED",
|
|
361
|
+
message: `Failed to upload chunk for job ${jobId} at offset ${offset}`,
|
|
362
|
+
cause: err as Error,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|