@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,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for uploading a file through a flow pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Flows enable processing uploaded files through a sequence of transformation
|
|
5
|
+
* nodes (e.g., image resize, format conversion, validation) before final storage.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic flow upload
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const config: FlowUploadConfig = {
|
|
10
|
+
* flowId: 'image-optimization',
|
|
11
|
+
* storageId: 'processed-images',
|
|
12
|
+
* };
|
|
13
|
+
*
|
|
14
|
+
* await client.uploadWithFlow(file, config, {
|
|
15
|
+
* onProgress: (progress) => console.log(`${progress}%`),
|
|
16
|
+
* onSuccess: (result) => console.log('Processed:', result),
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @example With specific output node
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const config: FlowUploadConfig = {
|
|
23
|
+
* flowId: 'multi-format-conversion',
|
|
24
|
+
* storageId: 'images',
|
|
25
|
+
* outputNodeId: 'webp-output', // Get WebP version instead of first output
|
|
26
|
+
* metadata: {
|
|
27
|
+
* userId: '123',
|
|
28
|
+
* album: 'vacation-2024',
|
|
29
|
+
* },
|
|
30
|
+
* };
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export type FlowUploadConfig = {
|
|
34
|
+
/** Unique identifier of the flow to execute */
|
|
35
|
+
flowId: string;
|
|
36
|
+
|
|
37
|
+
/** Storage backend where flow outputs will be saved */
|
|
38
|
+
storageId: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Specify which output node to use for single-value callbacks like onSuccess.
|
|
42
|
+
*
|
|
43
|
+
* For flows with multiple output nodes, this determines which output
|
|
44
|
+
* is passed to the onSuccess callback. If not specified, uses the first
|
|
45
|
+
* output node. The onFlowComplete callback receives all outputs regardless.
|
|
46
|
+
*/
|
|
47
|
+
outputNodeId?: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Additional metadata to attach to the upload and flow execution.
|
|
51
|
+
*
|
|
52
|
+
* This metadata is stored with the upload and can be used for tracking,
|
|
53
|
+
* filtering, or providing context to flow nodes.
|
|
54
|
+
*/
|
|
55
|
+
metadata?: Record<string, string>;
|
|
56
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { UploadFile } from "@uploadista/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Flow upload item for multi-flow-upload tracking
|
|
5
|
+
*/
|
|
6
|
+
export interface FlowUploadItem<UploadInput> {
|
|
7
|
+
id: string;
|
|
8
|
+
file: UploadInput;
|
|
9
|
+
status: "pending" | "uploading" | "success" | "error" | "aborted";
|
|
10
|
+
progress: number;
|
|
11
|
+
bytesUploaded: number;
|
|
12
|
+
totalBytes: number;
|
|
13
|
+
error: Error | null;
|
|
14
|
+
result: UploadFile | null;
|
|
15
|
+
jobId: string | null;
|
|
16
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { UploadFile } from "@uploadista/core/types";
|
|
2
|
+
import type { FlowUploadConfig } from "./flow-upload-config";
|
|
3
|
+
|
|
4
|
+
export interface FlowUploadOptions<TOutput = UploadFile> {
|
|
5
|
+
/**
|
|
6
|
+
* Flow configuration
|
|
7
|
+
*/
|
|
8
|
+
flowConfig: FlowUploadConfig;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Called when upload progress updates
|
|
12
|
+
*/
|
|
13
|
+
onProgress?: (
|
|
14
|
+
progress: number,
|
|
15
|
+
bytesUploaded: number,
|
|
16
|
+
totalBytes: number | null,
|
|
17
|
+
) => void;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Called when a chunk completes
|
|
21
|
+
*/
|
|
22
|
+
onChunkComplete?: (
|
|
23
|
+
chunkSize: number,
|
|
24
|
+
bytesAccepted: number,
|
|
25
|
+
bytesTotal: number | null,
|
|
26
|
+
) => void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Called when the flow completes successfully (receives full flow outputs)
|
|
30
|
+
* This is the recommended callback for multi-output flows
|
|
31
|
+
* Format: { [outputNodeId]: result, ... }
|
|
32
|
+
*/
|
|
33
|
+
onFlowComplete?: (outputs: Record<string, unknown>) => void;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Called when upload succeeds (legacy, single-output flows)
|
|
37
|
+
* For single-output flows, receives the value from the specified outputNodeId
|
|
38
|
+
* or the first output node if outputNodeId is not specified
|
|
39
|
+
*/
|
|
40
|
+
onSuccess?: (result: TOutput) => void;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Called when upload fails
|
|
44
|
+
*/
|
|
45
|
+
onError?: (error: Error) => void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Called when upload is aborted
|
|
49
|
+
*/
|
|
50
|
+
onAbort?: () => void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Custom retry logic
|
|
54
|
+
*/
|
|
55
|
+
onShouldRetry?: (error: Error, retryAttempt: number) => boolean;
|
|
56
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./chunk-metrics";
|
|
2
|
+
export * from "./flow-result";
|
|
3
|
+
export * from "./flow-upload-config";
|
|
4
|
+
export * from "./flow-upload-item";
|
|
5
|
+
export * from "./flow-upload-options";
|
|
6
|
+
export * from "./multi-flow-upload-options";
|
|
7
|
+
export * from "./multi-flow-upload-state";
|
|
8
|
+
export * from "./performance-insights";
|
|
9
|
+
export * from "./previous-upload";
|
|
10
|
+
export * from "./upload-options";
|
|
11
|
+
export * from "./upload-response";
|
|
12
|
+
export * from "./upload-result";
|
|
13
|
+
export * from "./upload-session-metrics";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { FlowUploadConfig } from "./flow-upload-config";
|
|
2
|
+
import type { FlowUploadItem } from "./flow-upload-item";
|
|
3
|
+
|
|
4
|
+
export interface MultiFlowUploadOptions<UploadInput> {
|
|
5
|
+
/**
|
|
6
|
+
* Flow configuration
|
|
7
|
+
*/
|
|
8
|
+
flowConfig: FlowUploadConfig;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Maximum number of concurrent uploads (default: 3)
|
|
12
|
+
*/
|
|
13
|
+
maxConcurrent?: number;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Called when an individual upload progresses
|
|
17
|
+
*/
|
|
18
|
+
onItemProgress?: (item: FlowUploadItem<UploadInput>) => void;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Called when an individual upload succeeds
|
|
22
|
+
*/
|
|
23
|
+
onItemSuccess?: (item: FlowUploadItem<UploadInput>) => void;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Called when an individual upload fails
|
|
27
|
+
*/
|
|
28
|
+
onItemError?: (item: FlowUploadItem<UploadInput>, error: Error) => void;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Called when all uploads complete
|
|
32
|
+
*/
|
|
33
|
+
onComplete?: (items: FlowUploadItem<UploadInput>[]) => void;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Custom retry logic
|
|
37
|
+
*/
|
|
38
|
+
onShouldRetry?: (error: Error, retryAttempt: number) => boolean;
|
|
39
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
|
|
3
|
+
export type PreviousUpload = {
|
|
4
|
+
size: number | null;
|
|
5
|
+
metadata: { [key: string]: string | number | boolean };
|
|
6
|
+
creationTime: string;
|
|
7
|
+
uploadId?: string;
|
|
8
|
+
parallelUploadUrls?: string[];
|
|
9
|
+
clientStorageKey: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const previousUploadSchema = z.object({
|
|
13
|
+
size: z.number().nullable(),
|
|
14
|
+
metadata: z.record(
|
|
15
|
+
z.string(),
|
|
16
|
+
z.union([z.string(), z.number(), z.boolean()]),
|
|
17
|
+
),
|
|
18
|
+
creationTime: z.string(),
|
|
19
|
+
uploadId: z.string().optional(),
|
|
20
|
+
parallelUploadUrls: z.array(z.string()).optional(),
|
|
21
|
+
clientStorageKey: z.string(),
|
|
22
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { UploadFile } from "@uploadista/core";
|
|
2
|
+
|
|
3
|
+
export interface UploadOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Upload metadata to attach to the file
|
|
6
|
+
*/
|
|
7
|
+
metadata?: Record<string, string>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Whether to defer the upload size calculation
|
|
11
|
+
*/
|
|
12
|
+
uploadLengthDeferred?: boolean;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Manual upload size override
|
|
16
|
+
*/
|
|
17
|
+
uploadSize?: number;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Called when upload progress updates
|
|
21
|
+
*/
|
|
22
|
+
onProgress?: (
|
|
23
|
+
progress: number,
|
|
24
|
+
bytesUploaded: number,
|
|
25
|
+
totalBytes: number | null,
|
|
26
|
+
) => void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Called when a chunk completes
|
|
30
|
+
*/
|
|
31
|
+
onChunkComplete?: (
|
|
32
|
+
chunkSize: number,
|
|
33
|
+
bytesAccepted: number,
|
|
34
|
+
bytesTotal: number | null,
|
|
35
|
+
) => void;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Called when upload succeeds
|
|
39
|
+
*/
|
|
40
|
+
onSuccess?: (result: UploadFile) => void;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Called when upload fails
|
|
44
|
+
*/
|
|
45
|
+
onError?: (error: Error) => void;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Called when upload is aborted
|
|
49
|
+
*/
|
|
50
|
+
onAbort?: () => void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Custom retry logic
|
|
54
|
+
*/
|
|
55
|
+
onShouldRetry?: (error: Error, retryAttempt: number) => boolean;
|
|
56
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { UploadFile } from "@uploadista/core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Discriminated union representing the result of an upload operation.
|
|
5
|
+
*
|
|
6
|
+
* Provides a type-safe way to handle the three possible outcomes of an upload:
|
|
7
|
+
* success, error, or cancellation. This pattern enables exhaustive checking
|
|
8
|
+
* of all cases at compile time.
|
|
9
|
+
*
|
|
10
|
+
* @template TOutput - The type of the successful result value. Defaults to UploadFile
|
|
11
|
+
*
|
|
12
|
+
* @example Handling upload results
|
|
13
|
+
* ```typescript
|
|
14
|
+
* function handleUploadResult(result: UploadResult) {
|
|
15
|
+
* switch (result.type) {
|
|
16
|
+
* case 'success':
|
|
17
|
+
* console.log('Upload complete:', result.value.id);
|
|
18
|
+
* break;
|
|
19
|
+
* case 'error':
|
|
20
|
+
* console.error('Upload failed:', result.error.message);
|
|
21
|
+
* break;
|
|
22
|
+
* case 'cancelled':
|
|
23
|
+
* console.log('Upload was cancelled by user');
|
|
24
|
+
* break;
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example With custom output type
|
|
30
|
+
* ```typescript
|
|
31
|
+
* interface ProcessedImage {
|
|
32
|
+
* url: string;
|
|
33
|
+
* width: number;
|
|
34
|
+
* height: number;
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* const result: UploadResult<ProcessedImage> = await uploadAndProcess(file);
|
|
38
|
+
*
|
|
39
|
+
* if (result.type === 'success') {
|
|
40
|
+
* console.log(`Image processed: ${result.value.width}x${result.value.height}`);
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export type UploadResult<TOutput = UploadFile> =
|
|
45
|
+
| {
|
|
46
|
+
/** Indicates the upload completed successfully */
|
|
47
|
+
type: "success";
|
|
48
|
+
/** The successful result value (e.g., upload metadata or processed output) */
|
|
49
|
+
value: TOutput;
|
|
50
|
+
}
|
|
51
|
+
| {
|
|
52
|
+
/** Indicates the upload failed with an error */
|
|
53
|
+
type: "error";
|
|
54
|
+
/** The error that caused the upload to fail */
|
|
55
|
+
error: Error;
|
|
56
|
+
}
|
|
57
|
+
| {
|
|
58
|
+
/** Indicates the upload was cancelled by the user or application */
|
|
59
|
+
type: "cancelled";
|
|
60
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface UploadSessionMetrics {
|
|
2
|
+
uploadId: string;
|
|
3
|
+
totalSize: number;
|
|
4
|
+
totalDuration: number;
|
|
5
|
+
chunksCompleted: number;
|
|
6
|
+
chunksTotal: number;
|
|
7
|
+
averageSpeed: number;
|
|
8
|
+
peakSpeed: number;
|
|
9
|
+
minSpeed: number;
|
|
10
|
+
totalRetries: number;
|
|
11
|
+
successRate: number;
|
|
12
|
+
adaptiveChunkingEnabled: boolean;
|
|
13
|
+
startTime: number;
|
|
14
|
+
endTime?: number;
|
|
15
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { UploadistaApi } from "../client/uploadista-api";
|
|
2
|
+
import { UploadistaError } from "../error";
|
|
3
|
+
import type { Logger } from "../logger";
|
|
4
|
+
import type { AbortControllerLike } from "../services";
|
|
5
|
+
import type { FileSource } from "../services/file-reader-service";
|
|
6
|
+
import type { PlatformService } from "../services/platform-service";
|
|
7
|
+
import type { SmartChunker } from "../smart-chunker";
|
|
8
|
+
import type { UploadResponse } from "../types/upload-response";
|
|
9
|
+
import { inStatusCategory } from "./upload-utils";
|
|
10
|
+
|
|
11
|
+
export type OnProgress = (
|
|
12
|
+
uploadId: string,
|
|
13
|
+
bytesSent: number,
|
|
14
|
+
bytesTotal: number | null,
|
|
15
|
+
) => void;
|
|
16
|
+
|
|
17
|
+
export type OnShouldRetry = (
|
|
18
|
+
error: UploadistaError,
|
|
19
|
+
retryAttempt: number,
|
|
20
|
+
) => boolean;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* uploadChunk reads a chunk from the source and sends it using the
|
|
24
|
+
* supplied request object. It will not handle the response.
|
|
25
|
+
*/
|
|
26
|
+
export async function uploadChunk({
|
|
27
|
+
uploadId,
|
|
28
|
+
source,
|
|
29
|
+
offset,
|
|
30
|
+
uploadLengthDeferred,
|
|
31
|
+
abortController,
|
|
32
|
+
onProgress,
|
|
33
|
+
smartChunker,
|
|
34
|
+
uploadistaApi,
|
|
35
|
+
logger,
|
|
36
|
+
}: {
|
|
37
|
+
uploadId: string;
|
|
38
|
+
source: FileSource;
|
|
39
|
+
offset: number;
|
|
40
|
+
uploadLengthDeferred: boolean | undefined;
|
|
41
|
+
abortController: AbortControllerLike;
|
|
42
|
+
onProgress?: OnProgress;
|
|
43
|
+
smartChunker: SmartChunker;
|
|
44
|
+
uploadistaApi: UploadistaApi;
|
|
45
|
+
logger: Logger;
|
|
46
|
+
}): Promise<UploadResponse> {
|
|
47
|
+
const start = offset ?? 0;
|
|
48
|
+
const remainingBytes = source.size ? source.size - start : undefined;
|
|
49
|
+
const chunkSizeDecision = smartChunker.getNextChunkSize(remainingBytes);
|
|
50
|
+
const currentChunkSize = chunkSizeDecision.size;
|
|
51
|
+
let end = start + currentChunkSize;
|
|
52
|
+
|
|
53
|
+
// The specified chunkSize may be Infinity or the calcluated end position
|
|
54
|
+
// may exceed the file's size. In both cases, we limit the end position to
|
|
55
|
+
// the input's total size for simpler calculations and correctness.
|
|
56
|
+
if (
|
|
57
|
+
source.size &&
|
|
58
|
+
(end === Number.POSITIVE_INFINITY || end > source.size) &&
|
|
59
|
+
!uploadLengthDeferred
|
|
60
|
+
) {
|
|
61
|
+
end = source.size;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { value, size, done } = await source.slice(start, end);
|
|
65
|
+
const sizeOfValue = size ?? 0;
|
|
66
|
+
const chunkStartTime = Date.now();
|
|
67
|
+
|
|
68
|
+
// If the upload length is deferred, the upload size was not specified during
|
|
69
|
+
// upload creation. So, if the file reader is done reading, we know the total
|
|
70
|
+
// upload size and can tell the tus server.
|
|
71
|
+
if (uploadLengthDeferred && done) {
|
|
72
|
+
source.size = offset + sizeOfValue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// The specified uploadSize might not match the actual amount of data that a source
|
|
76
|
+
// provides. In these cases, we cannot successfully complete the upload, so we
|
|
77
|
+
// rather error out and let the user know. If not, tus-js-client will be stuck
|
|
78
|
+
// in a loop of repeating empty PATCH requests.
|
|
79
|
+
// See https://community.transloadit.com/t/how-to-abort-hanging-companion-uploads/16488/13
|
|
80
|
+
const newSize = offset + sizeOfValue;
|
|
81
|
+
if (!uploadLengthDeferred && done && newSize !== source.size) {
|
|
82
|
+
throw new UploadistaError({
|
|
83
|
+
name: "WRONG_UPLOAD_SIZE",
|
|
84
|
+
message: `upload was configured with a size of ${size} bytes, but the source is done after ${newSize} bytes`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const result = await uploadistaApi.uploadChunk(uploadId, value, {
|
|
89
|
+
onProgress: (bytes, total) => {
|
|
90
|
+
onProgress?.(uploadId, bytes, total);
|
|
91
|
+
},
|
|
92
|
+
abortController,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Record performance metrics
|
|
96
|
+
const chunkDuration = Date.now() - chunkStartTime;
|
|
97
|
+
const success = result.status >= 200 && result.status < 300;
|
|
98
|
+
|
|
99
|
+
smartChunker.recordChunkResult(sizeOfValue, chunkDuration, success);
|
|
100
|
+
|
|
101
|
+
logger.log(
|
|
102
|
+
`Chunk upload ${success ? "succeeded" : "failed"}: ${sizeOfValue} bytes in ${chunkDuration}ms (${chunkSizeDecision.strategy} strategy)`,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Checks whether or not it is ok to retry a request.
|
|
110
|
+
* @param {UploadistaError} err the error returned from the last request
|
|
111
|
+
* @param {number} retryAttempt the number of times the request has already been retried
|
|
112
|
+
* @param {number[]} retryDelays configured retry delays
|
|
113
|
+
* @param {OnShouldRetry} onShouldRetry optional custom retry logic
|
|
114
|
+
*/
|
|
115
|
+
export function shouldRetry(
|
|
116
|
+
platformService: PlatformService,
|
|
117
|
+
err: UploadistaError,
|
|
118
|
+
retryAttempt: number,
|
|
119
|
+
retryDelays?: number[],
|
|
120
|
+
onShouldRetry?: OnShouldRetry,
|
|
121
|
+
): boolean {
|
|
122
|
+
if (
|
|
123
|
+
retryDelays == null ||
|
|
124
|
+
retryAttempt >= retryDelays.length ||
|
|
125
|
+
!err.isNetworkError()
|
|
126
|
+
) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (onShouldRetry) {
|
|
131
|
+
return onShouldRetry(err, retryAttempt);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return defaultOnShouldRetry(platformService, err);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* determines if the request should be retried. Will only retry if not a status 4xx except a 409 or 423
|
|
139
|
+
* @param {UploadistaError} err
|
|
140
|
+
* @returns {boolean}
|
|
141
|
+
*/
|
|
142
|
+
export function defaultOnShouldRetry(
|
|
143
|
+
platformService: PlatformService,
|
|
144
|
+
err: UploadistaError,
|
|
145
|
+
): boolean {
|
|
146
|
+
const status = err.status ?? 0;
|
|
147
|
+
return (
|
|
148
|
+
(!inStatusCategory(status, 400) || status === 409 || status === 423) &&
|
|
149
|
+
platformService.isOnline()
|
|
150
|
+
);
|
|
151
|
+
}
|