@edgestore/sdk 1.0.0-next.1 → 1.0.0-next.2

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 (59) hide show
  1. package/dist/errors.d.ts +17 -0
  2. package/dist/errors.d.ts.map +1 -1
  3. package/dist/errors.js +15 -1
  4. package/dist/generated/api-v2.d.ts +71 -26
  5. package/dist/generated/api-v2.d.ts.map +1 -1
  6. package/dist/index.d.ts +2 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/internal/managementUploadOperations.d.ts +64 -0
  10. package/dist/internal/managementUploadOperations.d.ts.map +1 -0
  11. package/dist/internal/managementUploadOperations.js +72 -0
  12. package/dist/internal/multipartUpload.d.ts +6 -6
  13. package/dist/internal/multipartUpload.d.ts.map +1 -1
  14. package/dist/internal/runtimeOperations.d.ts.map +1 -1
  15. package/dist/internal/transport.d.ts.map +1 -1
  16. package/dist/internal/transport.js +20 -7
  17. package/dist/internal/uploadLifecycle.d.ts +57 -0
  18. package/dist/internal/uploadLifecycle.d.ts.map +1 -0
  19. package/dist/internal/uploadLifecycle.js +91 -0
  20. package/dist/internal/uploadProcessing.d.ts +20 -0
  21. package/dist/internal/uploadProcessing.d.ts.map +1 -0
  22. package/dist/internal/uploadProcessing.js +25 -0
  23. package/dist/internal/uploadSource.d.ts +18 -0
  24. package/dist/internal/uploadSource.d.ts.map +1 -0
  25. package/dist/internal/uploadSource.js +75 -0
  26. package/dist/internal/uploadTransfer.js +15 -1
  27. package/dist/managementClient.d.ts +2 -1
  28. package/dist/managementClient.d.ts.map +1 -1
  29. package/dist/managementClient.js +2 -2
  30. package/dist/managementResources.d.ts +5 -1
  31. package/dist/managementResources.d.ts.map +1 -1
  32. package/dist/managementResources.js +14 -49
  33. package/dist/managementUpload.d.ts +36 -0
  34. package/dist/managementUpload.d.ts.map +1 -0
  35. package/dist/managementUpload.js +74 -0
  36. package/dist/sdk.js +1 -1
  37. package/dist/upload.d.ts.map +1 -1
  38. package/dist/upload.js +42 -189
  39. package/dist/version.d.ts +1 -1
  40. package/dist/version.js +1 -1
  41. package/package.json +2 -2
  42. package/src/errors.ts +28 -0
  43. package/src/generated/api-v2.ts +71 -26
  44. package/src/generated/openapi-v2.json +237 -16
  45. package/src/generated/source.ts +2 -2
  46. package/src/index.ts +6 -0
  47. package/src/internal/managementUploadOperations.ts +99 -0
  48. package/src/internal/multipartUpload.ts +8 -8
  49. package/src/internal/transport.ts +21 -12
  50. package/src/internal/uploadLifecycle.ts +154 -0
  51. package/src/internal/uploadProcessing.ts +48 -0
  52. package/src/internal/uploadSource.ts +104 -0
  53. package/src/internal/uploadTransfer.ts +20 -1
  54. package/src/managementClient.ts +6 -2
  55. package/src/managementResources.ts +21 -51
  56. package/src/managementUpload.ts +139 -0
  57. package/src/sdk.ts +1 -1
  58. package/src/upload.ts +49 -274
  59. package/src/version.ts +1 -1
@@ -0,0 +1,99 @@
1
+ import type { OperationBody, OperationResult } from './operationTypes';
2
+ import type { Transport } from './transport';
3
+
4
+ type ProjectUploadInput = {
5
+ project: string;
6
+ uploadId: string;
7
+ signal?: AbortSignal;
8
+ };
9
+
10
+ export type ManagementUploadOperations = ReturnType<
11
+ typeof createManagementUploadOperations
12
+ >;
13
+
14
+ export function createManagementUploadOperations(transport: Transport) {
15
+ return {
16
+ request: (
17
+ input: OperationBody<'v2.management.uploads.request'> & {
18
+ project: string;
19
+ bucket: string;
20
+ signal?: AbortSignal;
21
+ },
22
+ ): Promise<OperationResult<'v2.management.uploads.request'>> => {
23
+ const { project, bucket, signal, ...body } = input;
24
+ return transport.execute((client) =>
25
+ client.POST(
26
+ '/management/projects/{projectRef}/buckets/{bucketName}/uploads',
27
+ {
28
+ params: { path: { projectRef: project, bucketName: bucket } },
29
+ body,
30
+ signal,
31
+ },
32
+ ),
33
+ );
34
+ },
35
+ get: (
36
+ input: ProjectUploadInput,
37
+ ): Promise<OperationResult<'v2.management.uploads.get'>> =>
38
+ transport.execute((client) =>
39
+ client.GET('/management/projects/{projectRef}/uploads/{uploadId}', {
40
+ params: {
41
+ path: { projectRef: input.project, uploadId: input.uploadId },
42
+ },
43
+ signal: input.signal,
44
+ }),
45
+ ),
46
+ getWithResponse: (input: ProjectUploadInput) =>
47
+ transport.executeWithResponse((client) =>
48
+ client.GET('/management/projects/{projectRef}/uploads/{uploadId}', {
49
+ params: {
50
+ path: { projectRef: input.project, uploadId: input.uploadId },
51
+ },
52
+ signal: input.signal,
53
+ }),
54
+ ),
55
+ cancel: (
56
+ input: ProjectUploadInput,
57
+ ): Promise<OperationResult<'v2.management.uploads.cancel'>> =>
58
+ transport.execute((client) =>
59
+ client.DELETE('/management/projects/{projectRef}/uploads/{uploadId}', {
60
+ params: {
61
+ path: { projectRef: input.project, uploadId: input.uploadId },
62
+ },
63
+ signal: input.signal,
64
+ }),
65
+ ),
66
+ createParts: (
67
+ input: ProjectUploadInput &
68
+ OperationBody<'v2.management.uploads.parts.create'>,
69
+ ): Promise<OperationResult<'v2.management.uploads.parts.create'>> => {
70
+ const { project, uploadId, signal, ...body } = input;
71
+ return transport.execute((client) =>
72
+ client.POST(
73
+ '/management/projects/{projectRef}/uploads/{uploadId}/parts',
74
+ {
75
+ params: { path: { projectRef: project, uploadId } },
76
+ body,
77
+ signal,
78
+ },
79
+ ),
80
+ );
81
+ },
82
+ completeMultipart: (
83
+ input: ProjectUploadInput &
84
+ OperationBody<'v2.management.uploads.multipart.complete'>,
85
+ ): Promise<OperationResult<'v2.management.uploads.multipart.complete'>> => {
86
+ const { project, uploadId, signal, ...body } = input;
87
+ return transport.execute((client) =>
88
+ client.POST(
89
+ '/management/projects/{projectRef}/uploads/{uploadId}/complete',
90
+ {
91
+ params: { path: { projectRef: project, uploadId } },
92
+ body,
93
+ signal,
94
+ },
95
+ ),
96
+ );
97
+ },
98
+ };
99
+ }
@@ -5,8 +5,8 @@ import type { Transport } from './transport';
5
5
  import { throwIfAborted } from './uploadRetry';
6
6
  import { putWithRetry } from './uploadTransfer';
7
7
 
8
- type CompletedPart = { partNumber: number; eTag: string };
9
- type UploadPart = { partNumber: number; signedUrl: string };
8
+ export type CompletedUploadPart = { partNumber: number; eTag: string };
9
+ export type SignedUploadPart = { partNumber: number; signedUrl: string };
10
10
  type ProgressHandler = RuntimeUploadInput['onProgress'];
11
11
 
12
12
  export async function uploadParts(
@@ -14,14 +14,14 @@ export async function uploadParts(
14
14
  options: {
15
15
  body: Blob;
16
16
  uploadId: string;
17
- parts: UploadPart[];
17
+ parts: SignedUploadPart[];
18
18
  partSizeBytes: number;
19
19
  concurrency: number;
20
20
  signal?: AbortSignal;
21
21
  onProgress?: ProgressHandler;
22
22
  },
23
- ): Promise<CompletedPart[]> {
24
- const completed: CompletedPart[] = Array(options.parts.length);
23
+ ): Promise<CompletedUploadPart[]> {
24
+ const completed: CompletedUploadPart[] = Array(options.parts.length);
25
25
  let nextIndex = 0;
26
26
  let transferredBytes = 0;
27
27
  const workerCount = Math.min(options.concurrency, options.parts.length);
@@ -70,14 +70,14 @@ export async function uploadStreamParts(
70
70
  stream: ReadableStream<Uint8Array>;
71
71
  totalBytes: number;
72
72
  uploadId: string;
73
- parts: UploadPart[];
73
+ parts: SignedUploadPart[];
74
74
  partSizeBytes: number;
75
75
  signal?: AbortSignal;
76
76
  onProgress?: ProgressHandler;
77
77
  },
78
- ): Promise<CompletedPart[]> {
78
+ ): Promise<CompletedUploadPart[]> {
79
79
  const reader = createSizedStreamReader(options.stream);
80
- const completed: CompletedPart[] = [];
80
+ const completed: CompletedUploadPart[] = [];
81
81
  let transferredBytes = 0;
82
82
 
83
83
  try {
@@ -8,6 +8,7 @@ import {
8
8
  EdgeStoreApiError,
9
9
  EdgeStoreError,
10
10
  EdgeStoreNetworkError,
11
+ EdgeStoreTimeoutError,
11
12
  } from '../errors';
12
13
  import type { paths } from '../generated/api-v2';
13
14
  import {
@@ -55,18 +56,26 @@ export function createTransport(options: TransportOptions): Transport {
55
56
  }
56
57
  const client = createClient<paths>({
57
58
  baseUrl: normalizeApiUrl(options.apiUrl ?? DEFAULT_API_URL),
58
- fetch: (request) =>
59
- fetch(
60
- new Request(request, {
61
- signal:
62
- controlTimeoutMs === 0
63
- ? request.signal
64
- : AbortSignal.any([
65
- request.signal,
66
- AbortSignal.timeout(controlTimeoutMs),
67
- ]),
68
- }),
69
- ),
59
+ fetch: async (request) => {
60
+ if (controlTimeoutMs === 0) return await fetch(request);
61
+
62
+ const timeoutSignal = AbortSignal.timeout(controlTimeoutMs);
63
+ try {
64
+ return await fetch(
65
+ new Request(request, {
66
+ signal: AbortSignal.any([request.signal, timeoutSignal]),
67
+ }),
68
+ );
69
+ } catch (error) {
70
+ if (timeoutSignal.aborted && !request.signal.aborted) {
71
+ throw new EdgeStoreTimeoutError(
72
+ `The EdgeStore API request timed out after ${controlTimeoutMs}ms.`,
73
+ { cause: error },
74
+ );
75
+ }
76
+ throw error;
77
+ }
78
+ },
70
79
  headers: {
71
80
  authorization,
72
81
  'user-agent': `${EDGE_STORE_PACKAGE_NAME}/${EDGE_STORE_PACKAGE_VERSION}`,
@@ -0,0 +1,154 @@
1
+ import {
2
+ EdgeStoreUploadCleanupError,
3
+ EdgeStoreUploadProcessingTimeoutError,
4
+ } from '../errors';
5
+ import type { MultipartUploadPlan } from '../multipartPlan';
6
+ import {
7
+ DEFAULT_MULTIPART_CONCURRENCY,
8
+ type UploadProgress,
9
+ } from '../uploadTypes';
10
+ import {
11
+ uploadParts,
12
+ uploadStreamParts,
13
+ type CompletedUploadPart,
14
+ type SignedUploadPart,
15
+ } from './multipartUpload';
16
+ import type { Transport } from './transport';
17
+ import { waitForUploadProcessing } from './uploadProcessing';
18
+ import {
19
+ reportUploadProgress,
20
+ type PreparedUploadSource,
21
+ } from './uploadSource';
22
+ import { putWithRetry } from './uploadTransfer';
23
+ import { getPositiveInteger } from './uploadValidation';
24
+
25
+ type RequestedUpload = {
26
+ upload:
27
+ | { id: string; kind: 'single'; signedUrl: string }
28
+ | { id: string; kind: 'multipart'; parts: SignedUploadPart[] };
29
+ };
30
+
31
+ type UploadState = { upload: { status: string } };
32
+
33
+ type UploadLifecycleOperations<TResult extends UploadState> = {
34
+ completeMultipart(input: {
35
+ uploadId: string;
36
+ parts: CompletedUploadPart[];
37
+ signal?: AbortSignal;
38
+ }): Promise<unknown>;
39
+ get(input: {
40
+ uploadId: string;
41
+ signal?: AbortSignal;
42
+ }): Promise<{ data: TResult; response: Response }>;
43
+ cancel(input: { uploadId: string }): Promise<unknown>;
44
+ };
45
+
46
+ export async function executeUploadLifecycle<
47
+ TResult extends UploadState,
48
+ >(options: {
49
+ transport: Transport;
50
+ requested: RequestedUpload;
51
+ prepared: PreparedUploadSource;
52
+ multipartPlan: MultipartUploadPlan | null;
53
+ multipartConcurrency?: number;
54
+ defaultMultipartConcurrency?: number;
55
+ processingTimeoutMs: number;
56
+ signal?: AbortSignal;
57
+ onProgress?: (progress: UploadProgress) => void;
58
+ operations: UploadLifecycleOperations<TResult>;
59
+ cleanupPolicy: 'preserve-original' | 'report-failure';
60
+ }): Promise<Extract<TResult, { upload: { status: 'completed' } }>> {
61
+ const {
62
+ transport,
63
+ requested,
64
+ prepared,
65
+ multipartPlan,
66
+ processingTimeoutMs,
67
+ signal,
68
+ onProgress,
69
+ operations,
70
+ } = options;
71
+ const uploadId = requested.upload.id;
72
+ const totalBytes = prepared.sizeBytes;
73
+
74
+ try {
75
+ reportUploadProgress(onProgress, {
76
+ transferredBytes: 0,
77
+ totalBytes,
78
+ phase: 'uploading',
79
+ });
80
+
81
+ if (prepared.kind === 'body' && requested.upload.kind === 'single') {
82
+ await putWithRetry(transport, {
83
+ uploadId,
84
+ url: requested.upload.signedUrl,
85
+ body: prepared.body,
86
+ signal,
87
+ });
88
+ reportUploadProgress(onProgress, {
89
+ transferredBytes: totalBytes,
90
+ totalBytes,
91
+ phase: 'uploading',
92
+ });
93
+ } else {
94
+ if (requested.upload.kind !== 'multipart' || !multipartPlan) {
95
+ throw new TypeError(
96
+ 'The API returned an upload plan that does not match the request.',
97
+ );
98
+ }
99
+ const transfer = {
100
+ uploadId,
101
+ parts: requested.upload.parts,
102
+ partSizeBytes: multipartPlan.partSizeBytes,
103
+ signal,
104
+ onProgress,
105
+ };
106
+ const parts =
107
+ prepared.kind === 'stream'
108
+ ? await uploadStreamParts(transport, {
109
+ ...transfer,
110
+ stream: prepared.stream,
111
+ totalBytes,
112
+ })
113
+ : await uploadParts(transport, {
114
+ ...transfer,
115
+ body: prepared.body,
116
+ concurrency: getPositiveInteger(
117
+ options.multipartConcurrency,
118
+ options.defaultMultipartConcurrency ??
119
+ DEFAULT_MULTIPART_CONCURRENCY,
120
+ 'multipart.concurrency',
121
+ ),
122
+ });
123
+ await operations.completeMultipart({ uploadId, parts, signal });
124
+ }
125
+
126
+ reportUploadProgress(onProgress, {
127
+ transferredBytes: totalBytes,
128
+ totalBytes,
129
+ phase: 'processing',
130
+ });
131
+ return await waitForUploadProcessing<TResult>({
132
+ uploadId,
133
+ signal,
134
+ timeoutMs: processingTimeoutMs,
135
+ get: (requestSignal) =>
136
+ operations.get({ uploadId, signal: requestSignal }),
137
+ });
138
+ } catch (error) {
139
+ if (error instanceof EdgeStoreUploadProcessingTimeoutError) throw error;
140
+ try {
141
+ await operations.cancel({ uploadId });
142
+ } catch (cleanupError) {
143
+ if (options.cleanupPolicy === 'report-failure') {
144
+ throw new EdgeStoreUploadCleanupError({
145
+ message: `Automatic cancellation of upload ${uploadId} failed.`,
146
+ uploadId,
147
+ uploadCause: error,
148
+ cleanupCause: cleanupError,
149
+ });
150
+ }
151
+ }
152
+ throw error;
153
+ }
154
+ }
@@ -0,0 +1,48 @@
1
+ import {
2
+ EdgeStoreUploadCanceledError,
3
+ EdgeStoreUploadProcessingTimeoutError,
4
+ } from '../errors';
5
+ import { getRetryAfterMs, retryOperation, sleep } from './uploadRetry';
6
+
7
+ type UploadState = { upload: { status: string } };
8
+
9
+ export async function waitForUploadProcessing<
10
+ TResult extends UploadState,
11
+ >(options: {
12
+ uploadId: string;
13
+ signal?: AbortSignal;
14
+ timeoutMs: number;
15
+ get(signal?: AbortSignal): Promise<{ data: TResult; response: Response }>;
16
+ }): Promise<Extract<TResult, { upload: { status: 'completed' } }>> {
17
+ const deadline = Date.now() + options.timeoutMs;
18
+ const timeoutError = () =>
19
+ new EdgeStoreUploadProcessingTimeoutError(
20
+ 'Timed out while EdgeStore was processing the upload.',
21
+ options.uploadId,
22
+ );
23
+
24
+ while (true) {
25
+ const { data, response } = await retryOperation(
26
+ (signal) => options.get(signal),
27
+ {
28
+ signal: options.signal,
29
+ deadline,
30
+ timeoutError,
31
+ },
32
+ );
33
+
34
+ if (data.upload.status === 'completed') {
35
+ return data as Extract<TResult, { upload: { status: 'completed' } }>;
36
+ }
37
+ if (data.upload.status === 'canceled') {
38
+ throw new EdgeStoreUploadCanceledError(
39
+ 'The EdgeStore upload was canceled.',
40
+ options.uploadId,
41
+ );
42
+ }
43
+
44
+ const delayMs = getRetryAfterMs(response) ?? 1000;
45
+ if (Date.now() + delayMs > deadline) throw timeoutError();
46
+ await sleep(delayMs, options.signal);
47
+ }
48
+ }
@@ -0,0 +1,104 @@
1
+ import type {
2
+ UploadMetadataValue,
3
+ UploadProgress,
4
+ UploadSource,
5
+ UploadStreamSource,
6
+ } from '../uploadTypes';
7
+
8
+ type PreparedUploadDetails = {
9
+ sizeBytes: number;
10
+ fileName?: string;
11
+ mimeType?: string;
12
+ };
13
+
14
+ export type PreparedUploadSource =
15
+ | (PreparedUploadDetails & { kind: 'body'; body: Blob })
16
+ | (PreparedUploadDetails & {
17
+ kind: 'stream';
18
+ stream: ReadableStream<Uint8Array>;
19
+ });
20
+
21
+ export function prepareUploadSource(
22
+ source: UploadSource,
23
+ ): PreparedUploadSource {
24
+ if (typeof source === 'string') {
25
+ const body = new Blob([source], { type: 'text/plain' });
26
+ return {
27
+ kind: 'body',
28
+ body,
29
+ sizeBytes: body.size,
30
+ mimeType: body.type,
31
+ };
32
+ }
33
+ if (isStreamSource(source)) {
34
+ if (!Number.isSafeInteger(source.sizeBytes) || source.sizeBytes < 0) {
35
+ throw new RangeError('source.sizeBytes must be a non-negative integer.');
36
+ }
37
+ return {
38
+ kind: 'stream',
39
+ stream: source.stream,
40
+ sizeBytes: source.sizeBytes,
41
+ };
42
+ }
43
+ if (source instanceof Blob) {
44
+ return {
45
+ kind: 'body',
46
+ body: source,
47
+ sizeBytes: source.size,
48
+ fileName:
49
+ 'name' in source && typeof source.name === 'string'
50
+ ? source.name
51
+ : undefined,
52
+ mimeType: source.type || undefined,
53
+ };
54
+ }
55
+ if (source instanceof ArrayBuffer) {
56
+ const body = new Blob([source]);
57
+ return { kind: 'body', body, sizeBytes: body.size };
58
+ }
59
+ const body = new Blob([
60
+ Uint8Array.from(
61
+ new Uint8Array(source.buffer, source.byteOffset, source.byteLength),
62
+ ),
63
+ ]);
64
+ return { kind: 'body', body, sizeBytes: body.size };
65
+ }
66
+
67
+ export function normalizeUploadMetadata(
68
+ metadata?: Record<string, UploadMetadataValue>,
69
+ ): Record<string, string> | undefined {
70
+ if (!metadata) return undefined;
71
+ const entries = Object.entries(metadata).flatMap(([key, value]) =>
72
+ value === null || value === undefined ? [] : [[key, String(value)]],
73
+ );
74
+ return entries.length ? Object.fromEntries(entries) : undefined;
75
+ }
76
+
77
+ export function reportUploadProgress(
78
+ onProgress: ((progress: UploadProgress) => void) | undefined,
79
+ progress: Omit<UploadProgress, 'percentage'>,
80
+ ): void {
81
+ const { transferredBytes, totalBytes, phase } = progress;
82
+ onProgress?.({
83
+ ...progress,
84
+ percentage:
85
+ totalBytes === 0
86
+ ? phase === 'preparing'
87
+ ? 0
88
+ : 100
89
+ : Math.round((transferredBytes / totalBytes) * 10_000) / 100,
90
+ });
91
+ }
92
+
93
+ function isStreamSource(source: UploadSource): source is UploadStreamSource {
94
+ const stream =
95
+ typeof source === 'object' && source !== null && 'stream' in source
96
+ ? Reflect.get(source, 'stream')
97
+ : undefined;
98
+ return (
99
+ typeof stream === 'object' &&
100
+ stream !== null &&
101
+ 'getReader' in stream &&
102
+ typeof Reflect.get(stream, 'getReader') === 'function'
103
+ );
104
+ }
@@ -79,7 +79,8 @@ export async function putWithRetry(
79
79
  isRetryable: (error) =>
80
80
  error instanceof SignedUploadResponseError
81
81
  ? isRetryableStatus(error.status)
82
- : !(error instanceof EdgeStoreUploadError),
82
+ : !findBlobReadError(error) &&
83
+ !(error instanceof EdgeStoreUploadError),
83
84
  getRetryDelayMs: (error) =>
84
85
  error instanceof SignedUploadResponseError
85
86
  ? error.retryAfterMs
@@ -89,6 +90,8 @@ export async function putWithRetry(
89
90
  } catch (error) {
90
91
  if (error instanceof EdgeStoreAbortError) throw error;
91
92
  if (error instanceof EdgeStoreUploadError) throw error;
93
+ const blobReadError = findBlobReadError(error);
94
+ if (blobReadError) throw blobReadError;
92
95
  if (error instanceof SignedUploadResponseError) {
93
96
  throw new EdgeStoreUploadError(error.message, options.uploadId);
94
97
  }
@@ -99,6 +102,22 @@ export async function putWithRetry(
99
102
  }
100
103
  }
101
104
 
105
+ function findBlobReadError(error: unknown): DOMException | undefined {
106
+ const seen = new Set<unknown>();
107
+ let current = error;
108
+ while (current instanceof Error && !seen.has(current)) {
109
+ if (
110
+ current instanceof DOMException &&
111
+ current.name === 'NotReadableError'
112
+ ) {
113
+ return current;
114
+ }
115
+ seen.add(current);
116
+ current = current.cause;
117
+ }
118
+ return undefined;
119
+ }
120
+
102
121
  async function cancelResponseBody(
103
122
  body: ReadableStream<Uint8Array> | null,
104
123
  ): Promise<void> {
@@ -8,6 +8,7 @@ import {
8
8
  createManagementResourceClient,
9
9
  type ManagementResourceClient,
10
10
  } from './managementResources';
11
+ import type { UploadDefaults } from './uploadTypes';
11
12
 
12
13
  /** Complete administrative client available to Bearer credentials. */
13
14
  export type ManagementClient = ManagementResourceClient &
@@ -19,9 +20,12 @@ export type ManagementClient = ManagementResourceClient &
19
20
  }): Promise<OperationResult<'v2.whoami'>>;
20
21
  };
21
22
 
22
- export function createManagementClient(transport: Transport): ManagementClient {
23
+ export function createManagementClient(
24
+ transport: Transport,
25
+ uploadDefaults?: UploadDefaults,
26
+ ): ManagementClient {
23
27
  return {
24
- ...createManagementResourceClient(transport),
28
+ ...createManagementResourceClient(transport, uploadDefaults),
25
29
  ...createManagementAccessClient(transport),
26
30
  whoami: (options) =>
27
31
  transport.execute((client) =>
@@ -1,3 +1,4 @@
1
+ import { createManagementUploadOperations } from './internal/managementUploadOperations';
1
2
  import type {
2
3
  OperationBody,
3
4
  OperationId,
@@ -5,6 +6,12 @@ import type {
5
6
  OperationResult,
6
7
  } from './internal/operationTypes';
7
8
  import type { Transport } from './internal/transport';
9
+ import {
10
+ uploadManagementFile,
11
+ type ManagementUploadInput,
12
+ type ManagementUploadResult,
13
+ } from './managementUpload';
14
+ import type { UploadDefaults } from './uploadTypes';
8
15
 
9
16
  type CallOptions = {
10
17
  /** Cancels the request. */
@@ -126,6 +133,8 @@ export type ManagementResourceClient = {
126
133
  ): Promise<Result<'v2.management.files.delete'>>;
127
134
  };
128
135
  uploads: {
136
+ /** Transfers a file and waits for server-side processing to complete. */
137
+ upload(input: ManagementUploadInput): Promise<ManagementUploadResult>;
129
138
  /** Requests signed upload destination(s) without transferring data. */
130
139
  request(
131
140
  input: BucketInput &
@@ -157,7 +166,9 @@ export type ManagementResourceClient = {
157
166
 
158
167
  export function createManagementResourceClient(
159
168
  transport: Transport,
169
+ uploadDefaults: UploadDefaults = {},
160
170
  ): ManagementResourceClient {
171
+ const uploadOperations = createManagementUploadOperations(transport);
161
172
  return {
162
173
  projects: {
163
174
  list: ({ account, signal }) =>
@@ -323,58 +334,17 @@ export function createManagementResourceClient(
323
334
  ),
324
335
  },
325
336
  uploads: {
326
- request: ({ project, bucket, signal, ...body }) =>
327
- transport.execute((client) =>
328
- client.POST(
329
- '/management/projects/{projectRef}/buckets/{bucketName}/uploads',
330
- {
331
- params: {
332
- path: { projectRef: project, bucketName: bucket },
333
- },
334
- body,
335
- signal,
336
- },
337
- ),
338
- ),
339
- get: ({ project, uploadId, signal }) =>
340
- transport.execute((client) =>
341
- client.GET('/management/projects/{projectRef}/uploads/{uploadId}', {
342
- params: { path: { projectRef: project, uploadId } },
343
- signal,
344
- }),
345
- ),
346
- cancel: ({ project, uploadId, signal }) =>
347
- transport.execute((client) =>
348
- client.DELETE(
349
- '/management/projects/{projectRef}/uploads/{uploadId}',
350
- {
351
- params: { path: { projectRef: project, uploadId } },
352
- signal,
353
- },
354
- ),
355
- ),
356
- createParts: ({ project, uploadId, signal, ...body }) =>
357
- transport.execute((client) =>
358
- client.POST(
359
- '/management/projects/{projectRef}/uploads/{uploadId}/parts',
360
- {
361
- params: { path: { projectRef: project, uploadId } },
362
- body,
363
- signal,
364
- },
365
- ),
366
- ),
367
- completeMultipart: ({ project, uploadId, signal, ...body }) =>
368
- transport.execute((client) =>
369
- client.POST(
370
- '/management/projects/{projectRef}/uploads/{uploadId}/complete',
371
- {
372
- params: { path: { projectRef: project, uploadId } },
373
- body,
374
- signal,
375
- },
376
- ),
337
+ upload: (input) =>
338
+ uploadManagementFile(
339
+ { transport, operations: uploadOperations },
340
+ input,
341
+ uploadDefaults,
377
342
  ),
343
+ request: uploadOperations.request,
344
+ get: uploadOperations.get,
345
+ cancel: uploadOperations.cancel,
346
+ createParts: uploadOperations.createParts,
347
+ completeMultipart: uploadOperations.completeMultipart,
378
348
  },
379
349
  };
380
350
  }