@uploadista/client-core 0.0.6 → 0.0.7
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/dist/index.d.ts +53 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/auth/__tests__/auth-http-client.test.ts +95 -41
- package/src/auth/__tests__/{saas-auth.test.ts → uploadista-cloud-auth.test.ts} +129 -92
- package/src/auth/auth-http-client.ts +10 -7
- package/src/auth/index.ts +1 -1
- package/src/auth/types.ts +9 -14
- package/src/auth/{saas-auth.ts → uploadista-cloud-auth.ts} +5 -5
- package/src/client/create-uploadista-client.ts +44 -13
- package/src/client/uploadista-api.ts +97 -12
- package/src/error.ts +3 -1
- package/src/upload/flow-upload.ts +2 -2
|
@@ -5,12 +5,15 @@ import type {
|
|
|
5
5
|
} from "../services/http-client";
|
|
6
6
|
import type { DirectAuthManager } from "./direct-auth";
|
|
7
7
|
import type { NoAuthManager } from "./no-auth";
|
|
8
|
-
import type {
|
|
8
|
+
import type { UploadistaCloudAuthManager } from "./uploadista-cloud-auth";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Union type of all auth managers
|
|
12
12
|
*/
|
|
13
|
-
export type AuthManager =
|
|
13
|
+
export type AuthManager =
|
|
14
|
+
| DirectAuthManager
|
|
15
|
+
| UploadistaCloudAuthManager
|
|
16
|
+
| NoAuthManager;
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Auth-aware HTTP client wrapper.
|
|
@@ -49,7 +52,7 @@ export class AuthHttpClient implements HttpClient {
|
|
|
49
52
|
// include credentials for cors if needed
|
|
50
53
|
credentials:
|
|
51
54
|
this.authManager.getType() === "no-auth" ||
|
|
52
|
-
this.authManager.getType() === "
|
|
55
|
+
this.authManager.getType() === "uploadista-cloud"
|
|
53
56
|
? "omit"
|
|
54
57
|
: (options.credentials ?? "include"),
|
|
55
58
|
});
|
|
@@ -72,14 +75,14 @@ export class AuthHttpClient implements HttpClient {
|
|
|
72
75
|
headers: Record<string, string>,
|
|
73
76
|
url: string,
|
|
74
77
|
): Promise<Record<string, string>> {
|
|
75
|
-
// Check if this is a DirectAuthManager or
|
|
78
|
+
// Check if this is a DirectAuthManager or UploadistaCloudAuthManager
|
|
76
79
|
if ("attachCredentials" in this.authManager) {
|
|
77
80
|
// DirectAuthManager or NoAuthManager
|
|
78
81
|
return await this.authManager.attachCredentials(headers);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
if ("attachToken" in this.authManager) {
|
|
82
|
-
//
|
|
85
|
+
// UploadistaCloudAuthManager - extract job ID from URL if present
|
|
83
86
|
const jobId = this.extractJobIdFromUrl(url);
|
|
84
87
|
return await this.authManager.attachToken(headers, jobId);
|
|
85
88
|
}
|
|
@@ -97,7 +100,7 @@ export class AuthHttpClient implements HttpClient {
|
|
|
97
100
|
// - /api/upload/{uploadId}
|
|
98
101
|
// - /api/flow/{flowId}/{storageId}
|
|
99
102
|
// - /api/jobs/{jobId}/status
|
|
100
|
-
// - /api/jobs/{jobId}/
|
|
103
|
+
// - /api/jobs/{jobId}/resume/{nodeId}
|
|
101
104
|
|
|
102
105
|
const uploadMatch = url.match(/\/api\/upload\/([^/?]+)/);
|
|
103
106
|
if (uploadMatch) {
|
|
@@ -114,7 +117,7 @@ export class AuthHttpClient implements HttpClient {
|
|
|
114
117
|
return jobMatch[1];
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
// No job ID found -
|
|
120
|
+
// No job ID found - UploadistaCloud mode will use global token
|
|
118
121
|
return undefined;
|
|
119
122
|
}
|
|
120
123
|
|
package/src/auth/index.ts
CHANGED
package/src/auth/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export class BaseAuthManager {
|
|
2
|
-
constructor(private type: "direct" | "
|
|
2
|
+
constructor(private type: "direct" | "uploadista-cloud" | "no-auth") {}
|
|
3
3
|
|
|
4
4
|
getType() {
|
|
5
5
|
return this.type;
|
|
@@ -56,7 +56,7 @@ export type DirectAuthConfig = {
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* UploadistaCloud auth mode configuration.
|
|
60
60
|
* Client requests JWT tokens from a user-controlled auth server,
|
|
61
61
|
* which validates credentials and issues tokens using a secure API key.
|
|
62
62
|
*
|
|
@@ -69,26 +69,21 @@ export type DirectAuthConfig = {
|
|
|
69
69
|
* @example
|
|
70
70
|
* ```typescript
|
|
71
71
|
* {
|
|
72
|
-
* mode: '
|
|
72
|
+
* mode: 'uploadista-cloud',
|
|
73
73
|
* authServerUrl: 'https://auth.myapp.com/token',
|
|
74
|
-
*
|
|
75
|
-
* username: await getUsername(),
|
|
76
|
-
* password: await getPassword()
|
|
77
|
-
* })
|
|
74
|
+
* clientId: 'my-client-id'
|
|
78
75
|
* }
|
|
79
76
|
* ```
|
|
80
77
|
*/
|
|
81
|
-
export type
|
|
82
|
-
mode: "
|
|
78
|
+
export type UploadistaCloudAuthConfig = {
|
|
79
|
+
mode: "uploadista-cloud";
|
|
83
80
|
/**
|
|
84
81
|
* URL of the user's auth server that issues JWT tokens.
|
|
85
82
|
* Should be a GET endpoint that accepts client id and returns { token, expiresIn }.
|
|
86
83
|
*/
|
|
87
84
|
authServerUrl: string;
|
|
88
85
|
/**
|
|
89
|
-
*
|
|
90
|
-
* The auth server will validate these credentials before issuing a token.
|
|
91
|
-
* Credentials format is client id
|
|
86
|
+
* Client ID to use for authentication. It will be used to compare the API Key with the client id on the auth server.
|
|
92
87
|
*/
|
|
93
88
|
clientId: string;
|
|
94
89
|
};
|
|
@@ -97,9 +92,9 @@ export type SaasAuthConfig = {
|
|
|
97
92
|
* Authentication configuration for the uploadista client.
|
|
98
93
|
* Supports two modes:
|
|
99
94
|
* - Direct: Bring your own auth (any protocol)
|
|
100
|
-
* -
|
|
95
|
+
* - UploadistaCloud: Standard JWT token exchange with auth server
|
|
101
96
|
*
|
|
102
97
|
* Use a discriminated union to ensure type safety - TypeScript will
|
|
103
98
|
* enforce that the correct fields are present for each mode.
|
|
104
99
|
*/
|
|
105
|
-
export type AuthConfig = DirectAuthConfig |
|
|
100
|
+
export type AuthConfig = DirectAuthConfig | UploadistaCloudAuthConfig;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HttpClient } from "../services/http-client";
|
|
2
|
-
import { BaseAuthManager, type
|
|
2
|
+
import { BaseAuthManager, type UploadistaCloudAuthConfig } from "./types";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Token response from the auth server
|
|
@@ -20,7 +20,7 @@ type CachedToken = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* UploadistaCloud auth manager - handles JWT token exchange with an auth server.
|
|
24
24
|
*
|
|
25
25
|
* Token exchange flow:
|
|
26
26
|
* 1. Client calls getCredentials() to get user credentials
|
|
@@ -31,7 +31,7 @@ type CachedToken = {
|
|
|
31
31
|
*
|
|
32
32
|
* Security: API keys are kept server-side in the auth server, never exposed to clients.
|
|
33
33
|
*/
|
|
34
|
-
export class
|
|
34
|
+
export class UploadistaCloudAuthManager extends BaseAuthManager {
|
|
35
35
|
/** Token cache: maps job ID to cached token */
|
|
36
36
|
private tokenCache = new Map<string, CachedToken>();
|
|
37
37
|
|
|
@@ -39,10 +39,10 @@ export class SaasAuthManager extends BaseAuthManager {
|
|
|
39
39
|
private globalToken: CachedToken | null = null;
|
|
40
40
|
|
|
41
41
|
constructor(
|
|
42
|
-
private config:
|
|
42
|
+
private config: UploadistaCloudAuthConfig,
|
|
43
43
|
private httpClient: HttpClient,
|
|
44
44
|
) {
|
|
45
|
-
super("
|
|
45
|
+
super("uploadista-cloud");
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import type { FlowJob } from "@uploadista/core/flow";
|
|
1
2
|
import type { DataStoreCapabilities } from "@uploadista/core/types";
|
|
2
3
|
import type { AuthConfig, AuthManager } from "../auth";
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DirectAuthManager,
|
|
6
|
+
NoAuthManager,
|
|
7
|
+
UploadistaCloudAuthManager,
|
|
8
|
+
} from "../auth";
|
|
4
9
|
import type { Logger } from "../logger";
|
|
5
10
|
import { createLogger } from "../logger";
|
|
6
11
|
import { defaultClientCapabilities } from "../mock-data-store";
|
|
@@ -175,7 +180,7 @@ export type UploadistaClientOptions<UploadInput> = {
|
|
|
175
180
|
* Optional authentication configuration.
|
|
176
181
|
* Supports two modes:
|
|
177
182
|
* - Direct: Bring your own auth (headers, cookies, custom tokens)
|
|
178
|
-
* -
|
|
183
|
+
* - UploadistaCloud: Standard JWT token exchange with auth server
|
|
179
184
|
*
|
|
180
185
|
* If omitted, client operates in no-auth mode (backward compatible).
|
|
181
186
|
*
|
|
@@ -189,10 +194,10 @@ export type UploadistaClientOptions<UploadInput> = {
|
|
|
189
194
|
* }
|
|
190
195
|
* ```
|
|
191
196
|
*
|
|
192
|
-
* @example
|
|
197
|
+
* @example UploadistaCloud mode with auth server
|
|
193
198
|
* ```typescript
|
|
194
199
|
* auth: {
|
|
195
|
-
* mode: '
|
|
200
|
+
* mode: 'uploadista-cloud',
|
|
196
201
|
* authServerUrl: 'https://auth.myapp.com/token',
|
|
197
202
|
* getCredentials: () => ({ username: 'user', password: 'pass' })
|
|
198
203
|
* }
|
|
@@ -230,7 +235,7 @@ export const defaultConnectionPoolingConfig: ConnectionPoolConfig = {
|
|
|
230
235
|
* - Smart chunking based on network conditions
|
|
231
236
|
* - Flow-based file processing pipelines
|
|
232
237
|
* - WebSocket support for real-time progress
|
|
233
|
-
* - Authentication (direct,
|
|
238
|
+
* - Authentication (direct, uploadista-cloud, or no-auth modes)
|
|
234
239
|
*
|
|
235
240
|
* The client automatically:
|
|
236
241
|
* - Fetches server capabilities and adapts upload strategy
|
|
@@ -369,13 +374,13 @@ export function createUploadistaClient<UploadInput>({
|
|
|
369
374
|
const authManager: AuthManager = auth
|
|
370
375
|
? auth.mode === "direct"
|
|
371
376
|
? new DirectAuthManager(auth, platformService, logger)
|
|
372
|
-
: new
|
|
377
|
+
: new UploadistaCloudAuthManager(auth, httpClient)
|
|
373
378
|
: new NoAuthManager();
|
|
374
379
|
|
|
375
380
|
// Log auth mode for debugging (without exposing credentials)
|
|
376
381
|
if (auth) {
|
|
377
382
|
logger.log(
|
|
378
|
-
`Authentication enabled in ${auth.mode} mode${auth.mode === "
|
|
383
|
+
`Authentication enabled in ${auth.mode} mode${auth.mode === "uploadista-cloud" ? ` (server: ${auth.authServerUrl})` : ""}`,
|
|
379
384
|
);
|
|
380
385
|
}
|
|
381
386
|
|
|
@@ -666,7 +671,11 @@ export function createUploadistaClient<UploadInput>({
|
|
|
666
671
|
UploadistaUploadOptions,
|
|
667
672
|
"uploadLengthDeferred" | "uploadSize" | "metadata"
|
|
668
673
|
> = {},
|
|
669
|
-
): Promise<{
|
|
674
|
+
): Promise<{
|
|
675
|
+
abort: () => Promise<void>;
|
|
676
|
+
pause: () => Promise<FlowJob>;
|
|
677
|
+
jobId: string;
|
|
678
|
+
}> => {
|
|
670
679
|
const source = await fileReader.openFile(file, chunkSize);
|
|
671
680
|
|
|
672
681
|
const initializedSmartChunker = await initializeSmartChunker();
|
|
@@ -697,7 +706,10 @@ export function createUploadistaClient<UploadInput>({
|
|
|
697
706
|
|
|
698
707
|
if (!result) {
|
|
699
708
|
return {
|
|
700
|
-
abort: () => {},
|
|
709
|
+
abort: async () => {},
|
|
710
|
+
pause: async () => {
|
|
711
|
+
throw new Error("Flow upload not initialized");
|
|
712
|
+
},
|
|
701
713
|
jobId: "",
|
|
702
714
|
};
|
|
703
715
|
}
|
|
@@ -735,7 +747,17 @@ export function createUploadistaClient<UploadInput>({
|
|
|
735
747
|
});
|
|
736
748
|
|
|
737
749
|
return {
|
|
738
|
-
abort: () => {
|
|
750
|
+
abort: async () => {
|
|
751
|
+
// First, tell the server to cancel the flow
|
|
752
|
+
try {
|
|
753
|
+
await uploadistaApi.cancelFlow(jobId);
|
|
754
|
+
logger.log(`Flow cancelled on server: ${jobId}`);
|
|
755
|
+
} catch (err) {
|
|
756
|
+
// Log but don't throw - client cleanup should still happen
|
|
757
|
+
logger.log(`Failed to cancel flow on server: ${err}`);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Then do client-side cleanup
|
|
739
761
|
abortController.abort();
|
|
740
762
|
if (timeoutId) {
|
|
741
763
|
platformService.clearTimeout(timeoutId);
|
|
@@ -744,6 +766,7 @@ export function createUploadistaClient<UploadInput>({
|
|
|
744
766
|
wsManager.closeWebSocket(jobId);
|
|
745
767
|
wsManager.closeUploadWebSocket(uploadFile.id);
|
|
746
768
|
},
|
|
769
|
+
pause: () => uploadistaApi.pauseFlow(jobId),
|
|
747
770
|
jobId,
|
|
748
771
|
};
|
|
749
772
|
};
|
|
@@ -777,7 +800,7 @@ export function createUploadistaClient<UploadInput>({
|
|
|
777
800
|
return { status, job };
|
|
778
801
|
},
|
|
779
802
|
|
|
780
|
-
|
|
803
|
+
resumeFlow: async ({
|
|
781
804
|
jobId,
|
|
782
805
|
nodeId,
|
|
783
806
|
newData,
|
|
@@ -788,11 +811,19 @@ export function createUploadistaClient<UploadInput>({
|
|
|
788
811
|
newData: unknown;
|
|
789
812
|
contentType?: "application/json" | "application/octet-stream";
|
|
790
813
|
}) => {
|
|
791
|
-
return uploadistaApi.
|
|
814
|
+
return uploadistaApi.resumeFlow(jobId, nodeId, newData, {
|
|
792
815
|
contentType,
|
|
793
816
|
});
|
|
794
817
|
},
|
|
795
818
|
|
|
819
|
+
pauseFlow: async (jobId: string) => {
|
|
820
|
+
return uploadistaApi.pauseFlow(jobId);
|
|
821
|
+
},
|
|
822
|
+
|
|
823
|
+
cancelFlow: async (jobId: string) => {
|
|
824
|
+
return uploadistaApi.cancelFlow(jobId);
|
|
825
|
+
},
|
|
826
|
+
|
|
796
827
|
// Job operations (unified for both uploads and flows)
|
|
797
828
|
getJobStatus: async (jobId: string) => {
|
|
798
829
|
return uploadistaApi.getJobStatus(jobId);
|
|
@@ -874,7 +905,7 @@ export function createUploadistaClient<UploadInput>({
|
|
|
874
905
|
*
|
|
875
906
|
* The client provides methods for:
|
|
876
907
|
* - **Upload operations**: upload(), uploadWithFlow()
|
|
877
|
-
* - **Flow operations**: getFlow(), runFlow(),
|
|
908
|
+
* - **Flow operations**: getFlow(), runFlow(), resumeFlow()
|
|
878
909
|
* - **Job management**: getJobStatus()
|
|
879
910
|
* - **WebSocket management**: openUploadWebSocket(), openFlowWebSocket(), closeWebSocket()
|
|
880
911
|
* - **Metrics and diagnostics**: getNetworkMetrics(), getChunkingInsights(), exportMetrics()
|
|
@@ -215,7 +215,7 @@ export type UploadistaApi = {
|
|
|
215
215
|
* @returns Updated job metadata
|
|
216
216
|
* @throws {UploadistaError} If job not found or continuation fails
|
|
217
217
|
*/
|
|
218
|
-
|
|
218
|
+
resumeFlow: (
|
|
219
219
|
jobId: string,
|
|
220
220
|
nodeId: string,
|
|
221
221
|
newData: unknown,
|
|
@@ -224,6 +224,31 @@ export type UploadistaApi = {
|
|
|
224
224
|
},
|
|
225
225
|
) => Promise<FlowJob>;
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Pauses a running flow execution.
|
|
229
|
+
*
|
|
230
|
+
* The flow will stop at the next node boundary (not mid-node execution).
|
|
231
|
+
* Can be resumed later using resumeFlow.
|
|
232
|
+
*
|
|
233
|
+
* @param jobId - Job identifier for the running flow
|
|
234
|
+
* @returns Updated job metadata with "paused" status
|
|
235
|
+
* @throws {UploadistaError} If job not found or cannot be paused
|
|
236
|
+
*/
|
|
237
|
+
pauseFlow: (jobId: string) => Promise<FlowJob>;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Cancels a running or paused flow execution.
|
|
241
|
+
*
|
|
242
|
+
* The flow will stop at the next node boundary (not mid-node execution).
|
|
243
|
+
* Intermediate files are automatically cleaned up. This operation is terminal
|
|
244
|
+
* and cannot be undone.
|
|
245
|
+
*
|
|
246
|
+
* @param jobId - Job identifier for the flow to cancel
|
|
247
|
+
* @returns Updated job metadata with "cancelled" status
|
|
248
|
+
* @throws {UploadistaError} If job not found or cannot be cancelled
|
|
249
|
+
*/
|
|
250
|
+
cancelFlow: (jobId: string) => Promise<FlowJob>;
|
|
251
|
+
|
|
227
252
|
/**
|
|
228
253
|
* Retrieves current job status and outputs.
|
|
229
254
|
*
|
|
@@ -390,7 +415,7 @@ export function createUploadistaApi(
|
|
|
390
415
|
|
|
391
416
|
/**
|
|
392
417
|
* Helper function to extract auth token for WebSocket connection.
|
|
393
|
-
* Supports both DirectAuthManager (extracts from headers) and
|
|
418
|
+
* Supports both DirectAuthManager (extracts from headers) and UploadistaCloudAuthManager (gets cached token).
|
|
394
419
|
*/
|
|
395
420
|
const getAuthTokenForWebSocket = async (
|
|
396
421
|
manager: AuthManager,
|
|
@@ -398,17 +423,19 @@ export function createUploadistaApi(
|
|
|
398
423
|
): Promise<string | null> => {
|
|
399
424
|
logger?.log(`Getting auth token for WebSocket (jobId: ${jobId})`);
|
|
400
425
|
|
|
401
|
-
// Check if this is a
|
|
426
|
+
// Check if this is a UploadistaCloudAuthManager (has attachToken method)
|
|
402
427
|
if ("attachToken" in manager) {
|
|
403
|
-
logger?.log("Detected
|
|
428
|
+
logger?.log("Detected UploadistaCloudAuthManager, calling attachToken");
|
|
404
429
|
const headers = await manager.attachToken({}, jobId);
|
|
405
430
|
const authHeader = headers.Authorization;
|
|
406
431
|
if (authHeader?.startsWith("Bearer ")) {
|
|
407
|
-
logger?.log(
|
|
432
|
+
logger?.log(
|
|
433
|
+
"Successfully extracted Bearer token from UploadistaCloudAuthManager",
|
|
434
|
+
);
|
|
408
435
|
return authHeader.substring(7); // Remove "Bearer " prefix
|
|
409
436
|
}
|
|
410
437
|
logger?.log(
|
|
411
|
-
`No valid Authorization header from
|
|
438
|
+
`No valid Authorization header from UploadistaCloudAuthManager: ${authHeader}`,
|
|
412
439
|
);
|
|
413
440
|
}
|
|
414
441
|
|
|
@@ -629,7 +656,7 @@ export function createUploadistaApi(
|
|
|
629
656
|
return { status: res.status, job: data };
|
|
630
657
|
},
|
|
631
658
|
|
|
632
|
-
|
|
659
|
+
resumeFlow: async (
|
|
633
660
|
jobId: string,
|
|
634
661
|
nodeId: string,
|
|
635
662
|
newData: unknown,
|
|
@@ -637,8 +664,6 @@ export function createUploadistaApi(
|
|
|
637
664
|
contentType?: "application/json" | "application/octet-stream";
|
|
638
665
|
},
|
|
639
666
|
) => {
|
|
640
|
-
logger?.log(`continueFlow: ${jobId} at node: ${nodeId}`);
|
|
641
|
-
|
|
642
667
|
const contentType = options?.contentType || "application/json";
|
|
643
668
|
|
|
644
669
|
let body: RequestBody;
|
|
@@ -651,7 +676,7 @@ export function createUploadistaApi(
|
|
|
651
676
|
}
|
|
652
677
|
|
|
653
678
|
const res = await httpClient.request(
|
|
654
|
-
`${jobsEndpoint}/${jobId}/
|
|
679
|
+
`${jobsEndpoint}/${jobId}/resume/${nodeId}`,
|
|
655
680
|
{
|
|
656
681
|
method: "PATCH",
|
|
657
682
|
headers: {
|
|
@@ -665,12 +690,71 @@ export function createUploadistaApi(
|
|
|
665
690
|
const errorData = (await res.json().catch(() => ({}))) as ErrorResponse;
|
|
666
691
|
const errorName = mapServerErrorCodeToClientName(
|
|
667
692
|
errorData.code,
|
|
668
|
-
"
|
|
693
|
+
"FLOW_RESUMED_FAILED",
|
|
694
|
+
);
|
|
695
|
+
const errorMessage =
|
|
696
|
+
errorData.error ||
|
|
697
|
+
errorData.message ||
|
|
698
|
+
`Failed to resume flow for job ${jobId}`;
|
|
699
|
+
|
|
700
|
+
throw new UploadistaError({
|
|
701
|
+
name: errorName,
|
|
702
|
+
message: errorData.code
|
|
703
|
+
? `${errorMessage} (${errorData.code})`
|
|
704
|
+
: errorMessage,
|
|
705
|
+
status: res.status,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const data = (await res.json()) as FlowJob;
|
|
710
|
+
return data;
|
|
711
|
+
},
|
|
712
|
+
|
|
713
|
+
pauseFlow: async (jobId: string) => {
|
|
714
|
+
const res = await httpClient.request(`${jobsEndpoint}/${jobId}/pause`, {
|
|
715
|
+
method: "POST",
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
if (!res.ok) {
|
|
719
|
+
const errorData = (await res.json().catch(() => ({}))) as ErrorResponse;
|
|
720
|
+
const errorName = mapServerErrorCodeToClientName(
|
|
721
|
+
errorData.code,
|
|
722
|
+
"FLOW_PAUSE_FAILED",
|
|
723
|
+
);
|
|
724
|
+
const errorMessage =
|
|
725
|
+
errorData.error ||
|
|
726
|
+
errorData.message ||
|
|
727
|
+
`Failed to pause flow for job ${jobId}`;
|
|
728
|
+
|
|
729
|
+
throw new UploadistaError({
|
|
730
|
+
name: errorName,
|
|
731
|
+
message: errorData.code
|
|
732
|
+
? `${errorMessage} (${errorData.code})`
|
|
733
|
+
: errorMessage,
|
|
734
|
+
status: res.status,
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const data = (await res.json()) as FlowJob;
|
|
739
|
+
logger?.log(`Flow paused: ${jobId}, status: ${data.status}`);
|
|
740
|
+
return data;
|
|
741
|
+
},
|
|
742
|
+
|
|
743
|
+
cancelFlow: async (jobId: string) => {
|
|
744
|
+
const res = await httpClient.request(`${jobsEndpoint}/${jobId}/cancel`, {
|
|
745
|
+
method: "POST",
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
if (!res.ok) {
|
|
749
|
+
const errorData = (await res.json().catch(() => ({}))) as ErrorResponse;
|
|
750
|
+
const errorName = mapServerErrorCodeToClientName(
|
|
751
|
+
errorData.code,
|
|
752
|
+
"FLOW_CANCEL_FAILED",
|
|
669
753
|
);
|
|
670
754
|
const errorMessage =
|
|
671
755
|
errorData.error ||
|
|
672
756
|
errorData.message ||
|
|
673
|
-
`Failed to
|
|
757
|
+
`Failed to cancel flow for job ${jobId}`;
|
|
674
758
|
|
|
675
759
|
throw new UploadistaError({
|
|
676
760
|
name: errorName,
|
|
@@ -682,6 +766,7 @@ export function createUploadistaApi(
|
|
|
682
766
|
}
|
|
683
767
|
|
|
684
768
|
const data = (await res.json()) as FlowJob;
|
|
769
|
+
logger?.log(`Flow cancelled: ${jobId}, status: ${data.status}`);
|
|
685
770
|
return data;
|
|
686
771
|
},
|
|
687
772
|
|
package/src/error.ts
CHANGED
|
@@ -36,7 +36,9 @@ export type UploadistaErrorName =
|
|
|
36
36
|
| "FLOW_NOT_FOUND"
|
|
37
37
|
| "FLOW_INIT_FAILED"
|
|
38
38
|
| "FLOW_RUN_FAILED"
|
|
39
|
-
| "
|
|
39
|
+
| "FLOW_RESUMED_FAILED"
|
|
40
|
+
| "FLOW_PAUSE_FAILED"
|
|
41
|
+
| "FLOW_CANCEL_FAILED"
|
|
40
42
|
| "FLOW_UNEXPECTED_STATE"
|
|
41
43
|
| "FLOW_INCOMPATIBLE"
|
|
42
44
|
| "FLOW_NO_UPLOAD_ID"
|
|
@@ -144,7 +144,7 @@ export async function startFlowUpload({
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
* Upload chunks directly to the upload API (not through
|
|
147
|
+
* Upload chunks directly to the upload API (not through resumeFlow)
|
|
148
148
|
* This is more efficient and reuses the existing upload infrastructure
|
|
149
149
|
*/
|
|
150
150
|
export async function performFlowUpload({
|
|
@@ -265,7 +265,7 @@ export async function performFlowUpload({
|
|
|
265
265
|
logger.log(`Finalizing flow upload for job ${jobId}`);
|
|
266
266
|
|
|
267
267
|
try {
|
|
268
|
-
await uploadistaApi.
|
|
268
|
+
await uploadistaApi.resumeFlow(
|
|
269
269
|
jobId,
|
|
270
270
|
inputNodeId,
|
|
271
271
|
{
|