@beam-network/sdk 0.4.1 → 0.4.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.
- package/README.md +2 -2
- package/dist/client.js +67 -21
- package/dist/models.d.ts +3 -3
- package/dist/nats-control.d.ts +2 -2
- package/dist/nats-control.js +5 -3
- package/dist/provider-signing.d.ts +10 -0
- package/dist/provider-signing.js +21 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,9 +96,9 @@ For low-level raw transfer configs, use `createRawTransfer`; lifecycle transport
|
|
|
96
96
|
|
|
97
97
|
## Route Streaming And Payload Size
|
|
98
98
|
|
|
99
|
-
Provider transfers
|
|
99
|
+
Provider transfers default to `signed_url_v1` and `transfer-client-control/v3`. S3, R2, and S3-compatible destinations create one final multipart upload per source/destination object, then each route signs direct `UploadPart` to that upload. Core receives per-route completion, abort, ListParts, and final HEAD controls and completes the destination after all winning ETags are known. `signed_url_v2` remains explicitly selectable for the legacy staged-copy manifest path.
|
|
100
100
|
|
|
101
|
-
The client signs up to 64 routes concurrently by default and emits
|
|
101
|
+
The client signs up to 64 routes concurrently by default and emits 2,048-route logical batches, split only when encoded MessagePack requests exceed `maxPayloadBytes` (24 MiB by default). The stream ID derives from the transfer, selected flow, and immutable plan identity, and each ordered batch ID includes its route-coordinate checksum. Manual multi-destination attachment requires `delivery_index` on every route. Lifecycle mutations retry transient failures up to three times with the same request identity. `waitForTransfer` subscribes to the API-key-owned, at-most-once terminal signal before its first status read and reconciles every signal through authoritative status; subscription failure degrades to the same jittered 15-to-30-second status fallback. Call `close()` when the client is no longer needed; it also closes outstanding terminal waiters.
|
|
102
102
|
|
|
103
103
|
Hippius uses genuine non-multipart v2 routes, so its manifest is empty and group-level final HEAD verification is not available from that provider flow.
|
|
104
104
|
|
package/dist/client.js
CHANGED
|
@@ -63,7 +63,7 @@ export class BeamClient {
|
|
|
63
63
|
callbacks: input.callbacks,
|
|
64
64
|
test_mode: input.testMode || undefined,
|
|
65
65
|
progressive_mode: input.progressiveMode || undefined,
|
|
66
|
-
signed_url_flow: "
|
|
66
|
+
signed_url_flow: input.signedUrlFlow ?? "signed_url_v1"
|
|
67
67
|
});
|
|
68
68
|
return this.control.request("transfer.create", body, {
|
|
69
69
|
transferId,
|
|
@@ -99,7 +99,7 @@ export class BeamClient {
|
|
|
99
99
|
name: input.name,
|
|
100
100
|
test_mode: input.testMode || undefined,
|
|
101
101
|
urls_expires_at: input.urlsExpiresAt,
|
|
102
|
-
signed_url_flow: "
|
|
102
|
+
signed_url_flow: input.signedUrlFlow ?? "signed_url_v1"
|
|
103
103
|
}));
|
|
104
104
|
if (result.success)
|
|
105
105
|
validateCompactTransferPlan(result.plan_descriptor, result.signed_url_flow);
|
|
@@ -114,7 +114,7 @@ export class BeamClient {
|
|
|
114
114
|
name: input.name,
|
|
115
115
|
test_mode: input.testMode || undefined,
|
|
116
116
|
urls_expires_at: input.urlsExpiresAt,
|
|
117
|
-
signed_url_flow: "
|
|
117
|
+
signed_url_flow: input.signedUrlFlow ?? "signed_url_v1"
|
|
118
118
|
}), {
|
|
119
119
|
transferId,
|
|
120
120
|
idempotencyKey: `transfer:${transferId}:prepare`
|
|
@@ -142,7 +142,8 @@ export class BeamClient {
|
|
|
142
142
|
transferId,
|
|
143
143
|
planIdentity: await routeCoordinateChecksum(orderedRoutes),
|
|
144
144
|
totalRoutes: orderedRoutes.length,
|
|
145
|
-
totalChunks: countDistinctRouteChunks(orderedRoutes)
|
|
145
|
+
totalChunks: countDistinctRouteChunks(orderedRoutes),
|
|
146
|
+
signedUrlFlow: input.multipartGroupManifest.length ? "signed_url_v2" : "signed_url_v1"
|
|
146
147
|
});
|
|
147
148
|
const stream = new RouteStreamSender(this.control, {
|
|
148
149
|
streamId,
|
|
@@ -150,6 +151,7 @@ export class BeamClient {
|
|
|
150
151
|
totalRoutes: orderedRoutes.length,
|
|
151
152
|
totalChunks: countDistinctRouteChunks(orderedRoutes),
|
|
152
153
|
urlsExpiresAt: input.urlsExpiresAt,
|
|
154
|
+
signedUrlFlow: input.multipartGroupManifest.length ? "signed_url_v2" : "signed_url_v1",
|
|
153
155
|
autoDistribute: input.autoDistribute ?? true
|
|
154
156
|
});
|
|
155
157
|
await stream.begin();
|
|
@@ -163,7 +165,7 @@ export class BeamClient {
|
|
|
163
165
|
}
|
|
164
166
|
async prepareProviderTransfer(input) {
|
|
165
167
|
const expiresIn = input.expiresIn ?? 3600;
|
|
166
|
-
const requestedSignedUrlFlow = "
|
|
168
|
+
const requestedSignedUrlFlow = input.signedUrlFlow ?? "signed_url_v1";
|
|
167
169
|
const preparedDestinations = input.destinations.map((destination, index) => prepareProviderDestination(destination, { index }));
|
|
168
170
|
const destinationsById = new Map();
|
|
169
171
|
preparedDestinations.forEach((preparedDestination, index) => {
|
|
@@ -212,13 +214,15 @@ export class BeamClient {
|
|
|
212
214
|
transferId: prepared.transfer_id,
|
|
213
215
|
planIdentity: prepared.plan_descriptor.plan_nonce,
|
|
214
216
|
totalRoutes: prepared.plan_descriptor.delivery_route_count,
|
|
215
|
-
totalChunks: prepared.plan_descriptor.logical_chunk_count
|
|
217
|
+
totalChunks: prepared.plan_descriptor.logical_chunk_count,
|
|
218
|
+
signedUrlFlow: requestedSignedUrlFlow
|
|
216
219
|
});
|
|
217
220
|
routeStream = new RouteStreamSender(this.control, {
|
|
218
221
|
streamId,
|
|
219
222
|
transferId: prepared.transfer_id,
|
|
220
223
|
totalRoutes: prepared.plan_descriptor.delivery_route_count,
|
|
221
224
|
totalChunks: prepared.plan_descriptor.logical_chunk_count,
|
|
225
|
+
signedUrlFlow: requestedSignedUrlFlow,
|
|
222
226
|
autoDistribute: input.distribute !== false
|
|
223
227
|
});
|
|
224
228
|
routeStreamBeginAttempted = true;
|
|
@@ -229,10 +233,13 @@ export class BeamClient {
|
|
|
229
233
|
destinationsById,
|
|
230
234
|
multipartUploads,
|
|
231
235
|
expiresIn,
|
|
236
|
+
signedUrlFlow: requestedSignedUrlFlow,
|
|
232
237
|
concurrency: this.routeSigningConcurrency,
|
|
233
238
|
onGroupReady: async (state) => {
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
if (requestedSignedUrlFlow === "signed_url_v2") {
|
|
240
|
+
validateMultipartGroupManifest([state.manifest], prepared.transfer_id);
|
|
241
|
+
await routeStream.addManifestGroups([state.manifest]);
|
|
242
|
+
}
|
|
236
243
|
multipartGroupWaiters.get(state.manifest.multipart_group_id)?.resolve(state);
|
|
237
244
|
},
|
|
238
245
|
onGroupFailed: (groupId, error) => multipartGroupWaiters.get(groupId)?.reject(error)
|
|
@@ -281,6 +288,7 @@ export class BeamClient {
|
|
|
281
288
|
...(upload ? { upload } : {}),
|
|
282
289
|
transferId: prepared.transfer_id,
|
|
283
290
|
finalObjectKey,
|
|
291
|
+
signedUrlFlow: requestedSignedUrlFlow,
|
|
284
292
|
partNumber: typeof metadata.part_number === "number"
|
|
285
293
|
? metadata.part_number
|
|
286
294
|
: multipartPartNumber(chunk.source_chunk_index),
|
|
@@ -294,7 +302,9 @@ export class BeamClient {
|
|
|
294
302
|
while (pendingRoutes.size)
|
|
295
303
|
await streamNextCompletedRoute();
|
|
296
304
|
const multipartGroupManifest = await multipartManifestTask;
|
|
297
|
-
|
|
305
|
+
if (requestedSignedUrlFlow === "signed_url_v2") {
|
|
306
|
+
validateMultipartGroupManifest(multipartGroupManifest, prepared.transfer_id);
|
|
307
|
+
}
|
|
298
308
|
const attached = await routeStream.complete();
|
|
299
309
|
if (!attached.success) {
|
|
300
310
|
throw new Error(attached.error ?? attached.message ?? "route stream failed");
|
|
@@ -330,8 +340,33 @@ export class BeamClient {
|
|
|
330
340
|
}
|
|
331
341
|
const upload = input.upload;
|
|
332
342
|
if (!upload)
|
|
333
|
-
throw new Error(
|
|
343
|
+
throw new Error(`${input.signedUrlFlow} route is missing its multipart upload`);
|
|
334
344
|
validateMultipartPartNumber(input.partNumber, upload.manifest);
|
|
345
|
+
if (input.signedUrlFlow === "signed_url_v1") {
|
|
346
|
+
return signDestinationRoute({
|
|
347
|
+
chunk: input.chunk,
|
|
348
|
+
target: {
|
|
349
|
+
...input.target,
|
|
350
|
+
object_key: input.finalObjectKey,
|
|
351
|
+
metadata: partRouteMetadata(input.target.metadata)
|
|
352
|
+
},
|
|
353
|
+
source: input.source,
|
|
354
|
+
destination: input.destination,
|
|
355
|
+
expiresIn: input.expiresIn,
|
|
356
|
+
uploadId: upload.uploadId,
|
|
357
|
+
partNumber: input.partNumber,
|
|
358
|
+
finalObjectKey: input.finalObjectKey,
|
|
359
|
+
expectedObjectSize: upload.manifest.expected_object_size,
|
|
360
|
+
expectedPartCount: upload.manifest.expected_part_count,
|
|
361
|
+
maxPartNumber: upload.manifest.max_part_number,
|
|
362
|
+
completeUrl: upload.manifest.complete_url,
|
|
363
|
+
abortUrl: upload.manifest.abort_url,
|
|
364
|
+
listPageUrl: upload.manifest.list_page_urls[multipartListPageIndex(input.partNumber)],
|
|
365
|
+
finalHeadUrl: upload.manifest.final_head_url,
|
|
366
|
+
finalObjectMetadata: upload.manifest.final_object_metadata,
|
|
367
|
+
fetchImpl: this.fetchImpl
|
|
368
|
+
});
|
|
369
|
+
}
|
|
335
370
|
const stagingObjectPrefix = buildStagingObjectPrefix({
|
|
336
371
|
finalObjectKey: input.finalObjectKey,
|
|
337
372
|
transferId: input.transferId,
|
|
@@ -475,9 +510,9 @@ class RouteStreamSender {
|
|
|
475
510
|
stream_id: this.streamId,
|
|
476
511
|
total_routes: this.options.totalRoutes,
|
|
477
512
|
total_chunks: this.options.totalChunks,
|
|
478
|
-
route_contract_version:
|
|
513
|
+
route_contract_version: this.options.signedUrlFlow,
|
|
479
514
|
urls_expires_at: this.options.urlsExpiresAt,
|
|
480
|
-
signed_url_flow:
|
|
515
|
+
signed_url_flow: this.options.signedUrlFlow,
|
|
481
516
|
auto_distribute: this.options.autoDistribute
|
|
482
517
|
}), {
|
|
483
518
|
transferId: this.options.transferId,
|
|
@@ -633,7 +668,7 @@ async function routeCoordinateChecksum(routes) {
|
|
|
633
668
|
return checksum.value();
|
|
634
669
|
}
|
|
635
670
|
async function stableRouteStreamId(input) {
|
|
636
|
-
const digest = await sha256Bytes(`beam:route-stream
|
|
671
|
+
const digest = await sha256Bytes(`beam:route-stream:${input.signedUrlFlow}:${input.transferId}:${input.planIdentity}:${input.totalRoutes}:${input.totalChunks}`);
|
|
637
672
|
const bytes = digest.slice(0, 16);
|
|
638
673
|
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x50;
|
|
639
674
|
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;
|
|
@@ -843,7 +878,7 @@ async function createMultipartGroupManifest(input) {
|
|
|
843
878
|
multipartGroupId = multipartGroupStateKey(input.prepared.transfer_id, destination.destination_id, source.source_id, finalObjectKey);
|
|
844
879
|
const finalObjectMetadata = {
|
|
845
880
|
"beam-transfer-id": input.prepared.transfer_id,
|
|
846
|
-
"beam-multipart-group-id": multipartGroupId
|
|
881
|
+
...(input.signedUrlFlow === "signed_url_v2" ? { "beam-multipart-group-id": multipartGroupId } : {})
|
|
847
882
|
};
|
|
848
883
|
const uploadId = await createMultipartUpload({
|
|
849
884
|
destination: destinationConfig,
|
|
@@ -874,7 +909,9 @@ async function createMultipartGroupManifest(input) {
|
|
|
874
909
|
signCompleteMultipartUpload(destinationConfig, finalObjectKey, uploadId, input.expiresIn),
|
|
875
910
|
signAbortMultipartUpload(destinationConfig, finalObjectKey, uploadId, input.expiresIn),
|
|
876
911
|
Promise.all(listPageMarkers.map((partNumberMarker) => signListMultipartUpload(destinationConfig, finalObjectKey, uploadId, input.expiresIn, { maxParts: 1_000, ...(partNumberMarker ? { partNumberMarker } : {}) }))),
|
|
877
|
-
|
|
912
|
+
input.signedUrlFlow === "signed_url_v2"
|
|
913
|
+
? Promise.all(stagingListStartAfter.map((startAfter) => signListObjects(destinationConfig, stagingListPrefix, input.expiresIn, startAfter ? { startAfter } : {})))
|
|
914
|
+
: Promise.resolve([]),
|
|
878
915
|
signFinalObjectHead(destinationConfig, finalObjectKey, input.expiresIn)
|
|
879
916
|
]);
|
|
880
917
|
const manifest = {
|
|
@@ -980,6 +1017,9 @@ function validateMultipartPartNumber(partNumber, manifest) {
|
|
|
980
1017
|
throw new Error(`multipart part_number ${partNumber} is outside group ${manifest.multipart_group_id} range 1-${manifest.max_part_number}`);
|
|
981
1018
|
}
|
|
982
1019
|
}
|
|
1020
|
+
function multipartListPageIndex(partNumber) {
|
|
1021
|
+
return Math.floor((partNumber - 1) / 1_000);
|
|
1022
|
+
}
|
|
983
1023
|
function validateSignedRouteManifestContract(routes, manifest) {
|
|
984
1024
|
const groups = new Map(manifest.map((group) => [group.multipart_group_id, group]));
|
|
985
1025
|
for (const route of routes) {
|
|
@@ -1019,7 +1059,7 @@ const COMPACT_TRANSFER_PLAN_FORMULAS = {
|
|
|
1019
1059
|
staging_object_key: "{final_object_key}.beam-staging/{transfer_id}/{source_id}/{destination_id}/part-{part_number}-chunk-{chunk_index}"
|
|
1020
1060
|
};
|
|
1021
1061
|
function validateCompactTransferPlan(descriptor, signedUrlFlow) {
|
|
1022
|
-
if (signedUrlFlow !== "signed_url_v2") {
|
|
1062
|
+
if (signedUrlFlow !== "signed_url_v1" && signedUrlFlow !== "signed_url_v2") {
|
|
1023
1063
|
throw new Error(`BeamCore returned unsupported signed_url_flow: ${String(signedUrlFlow)}`);
|
|
1024
1064
|
}
|
|
1025
1065
|
const keys = Object.keys(descriptor);
|
|
@@ -1094,15 +1134,21 @@ function validateMultipartGroupManifest(manifest, transferId) {
|
|
|
1094
1134
|
throw new Error(`multipart group ${group.multipart_group_id} requires ${expectedListPageCount} unique list_page_urls`);
|
|
1095
1135
|
}
|
|
1096
1136
|
if (!Array.isArray(group.staging_list_urls)
|
|
1097
|
-
|| group.staging_list_urls.length !== expectedListPageCount
|
|
1098
|
-
|| new Set(group.staging_list_urls).size !== expectedListPageCount
|
|
1099
1137
|
|| group.staging_list_urls.some((url) => typeof url !== "string" || url.length === 0)) {
|
|
1100
|
-
throw new Error(`multipart group ${group.multipart_group_id}
|
|
1138
|
+
throw new Error(`multipart group ${group.multipart_group_id} has invalid staging_list_urls`);
|
|
1139
|
+
}
|
|
1140
|
+
if (group.staging_list_urls.length > 0
|
|
1141
|
+
&& (group.staging_list_urls.length !== expectedListPageCount
|
|
1142
|
+
|| new Set(group.staging_list_urls).size !== expectedListPageCount)) {
|
|
1143
|
+
throw new Error(`multipart group ${group.multipart_group_id} requires ${expectedListPageCount} unique staging_list_urls when staging is used`);
|
|
1101
1144
|
}
|
|
1102
1145
|
const metadataKeys = Object.keys(group.final_object_metadata);
|
|
1103
|
-
|
|
1146
|
+
const stagedCopyMetadata = group.staging_list_urls.length > 0;
|
|
1147
|
+
if (metadataKeys.length !== (stagedCopyMetadata ? 2 : 1)
|
|
1104
1148
|
|| group.final_object_metadata["beam-transfer-id"] !== transferId
|
|
1105
|
-
||
|
|
1149
|
+
|| (stagedCopyMetadata
|
|
1150
|
+
? group.final_object_metadata["beam-multipart-group-id"] !== group.multipart_group_id
|
|
1151
|
+
: group.final_object_metadata["beam-multipart-group-id"] !== undefined)) {
|
|
1106
1152
|
throw new Error(`multipart group ${group.multipart_group_id} has invalid final_object_metadata`);
|
|
1107
1153
|
}
|
|
1108
1154
|
}
|
package/dist/models.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SignedUrlFlow = "signed_url_v2";
|
|
1
|
+
export type SignedUrlFlow = "signed_url_v1" | "signed_url_v2";
|
|
2
2
|
export interface BeamClientOptions {
|
|
3
3
|
apiKey?: string;
|
|
4
4
|
natsUrl?: string;
|
|
@@ -325,9 +325,9 @@ export interface MultipartGroupManifest {
|
|
|
325
325
|
list_page_urls: string[];
|
|
326
326
|
staging_list_urls: string[];
|
|
327
327
|
final_head_url: string;
|
|
328
|
-
final_object_metadata: {
|
|
328
|
+
final_object_metadata: Record<string, string> & {
|
|
329
329
|
"beam-transfer-id": string;
|
|
330
|
-
"beam-multipart-group-id"
|
|
330
|
+
"beam-multipart-group-id"?: string;
|
|
331
331
|
};
|
|
332
332
|
urls_expires_at: string;
|
|
333
333
|
}
|
package/dist/nats-control.d.ts
CHANGED
|
@@ -31,9 +31,9 @@ export interface RouteStreamBeginPayload {
|
|
|
31
31
|
stream_id: string;
|
|
32
32
|
total_routes: number;
|
|
33
33
|
total_chunks: number;
|
|
34
|
-
route_contract_version: "signed_url_v2";
|
|
34
|
+
route_contract_version: "signed_url_v1" | "signed_url_v2";
|
|
35
35
|
urls_expires_at?: string;
|
|
36
|
-
signed_url_flow: "signed_url_v2";
|
|
36
|
+
signed_url_flow: "signed_url_v1" | "signed_url_v2";
|
|
37
37
|
auto_distribute: boolean;
|
|
38
38
|
}
|
|
39
39
|
export interface RouteStreamManifestPayload {
|
package/dist/nats-control.js
CHANGED
|
@@ -402,9 +402,11 @@ export function compactSignedRoutes(routes) {
|
|
|
402
402
|
const multipartGroupId = typeof metadata.multipart_group_id === "string"
|
|
403
403
|
? metadata.multipart_group_id
|
|
404
404
|
: undefined;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
405
|
+
if (multipartGroupId) {
|
|
406
|
+
for (const key of Object.keys(metadata)) {
|
|
407
|
+
if (!ROUTE_ATTEMPT_METADATA_KEYS.has(key))
|
|
408
|
+
delete metadata[key];
|
|
409
|
+
}
|
|
408
410
|
}
|
|
409
411
|
compactRoutes.push(compact({
|
|
410
412
|
source_ref: sourceRef,
|
|
@@ -42,6 +42,16 @@ export declare function signDestinationRoute(input: {
|
|
|
42
42
|
destination: ProviderDestinationConfig;
|
|
43
43
|
expiresIn: number;
|
|
44
44
|
partNumber?: number;
|
|
45
|
+
uploadId?: string;
|
|
46
|
+
completeUrl?: string;
|
|
47
|
+
abortUrl?: string;
|
|
48
|
+
listPageUrl?: string;
|
|
49
|
+
finalHeadUrl?: string;
|
|
50
|
+
finalObjectKey?: string;
|
|
51
|
+
expectedObjectSize?: number;
|
|
52
|
+
expectedPartCount?: number;
|
|
53
|
+
maxPartNumber?: number;
|
|
54
|
+
finalObjectMetadata?: Record<string, string>;
|
|
45
55
|
multipartGroupId?: string;
|
|
46
56
|
commitMethod?: "upload_part_copy";
|
|
47
57
|
stagingObjectKey?: string;
|
package/dist/provider-signing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, ListPartsCommand, PutObjectCommand, S3Client, UploadPartCopyCommand } from "@aws-sdk/client-s3";
|
|
1
|
+
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, ListPartsCommand, PutObjectCommand, S3Client, UploadPartCommand, UploadPartCopyCommand } from "@aws-sdk/client-s3";
|
|
2
2
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
3
3
|
export function nowIso() {
|
|
4
4
|
return new Date().toISOString();
|
|
@@ -230,6 +230,8 @@ export async function signDestinationRoute(input) {
|
|
|
230
230
|
signDestinationUrl({
|
|
231
231
|
destination: input.destination,
|
|
232
232
|
objectKey,
|
|
233
|
+
uploadId: input.uploadId,
|
|
234
|
+
partNumber: input.partNumber,
|
|
233
235
|
expiresIn: input.expiresIn,
|
|
234
236
|
fetchImpl: input.fetchImpl,
|
|
235
237
|
}),
|
|
@@ -260,6 +262,16 @@ export async function signDestinationRoute(input) {
|
|
|
260
262
|
...(input.copySource ? { copy_source: input.copySource } : {}),
|
|
261
263
|
...(input.copySourceRange ? { copy_source_range: input.copySourceRange } : {}),
|
|
262
264
|
...(input.deleteUrl ? { delete_url: input.deleteUrl } : {}),
|
|
265
|
+
...(input.uploadId ? { upload_id: input.uploadId } : {}),
|
|
266
|
+
...(input.completeUrl ? { complete_url: input.completeUrl } : {}),
|
|
267
|
+
...(input.abortUrl ? { abort_url: input.abortUrl } : {}),
|
|
268
|
+
...(input.listPageUrl ? { list_page_url: input.listPageUrl } : {}),
|
|
269
|
+
...(input.finalHeadUrl ? { final_head_url: input.finalHeadUrl } : {}),
|
|
270
|
+
...(input.finalObjectKey ? { final_object_key: input.finalObjectKey } : {}),
|
|
271
|
+
...(input.expectedObjectSize !== undefined ? { expected_object_size: input.expectedObjectSize } : {}),
|
|
272
|
+
...(input.expectedPartCount !== undefined ? { expected_part_count: input.expectedPartCount } : {}),
|
|
273
|
+
...(input.maxPartNumber !== undefined ? { max_part_number: input.maxPartNumber } : {}),
|
|
274
|
+
...(input.finalObjectMetadata ? { final_object_metadata: input.finalObjectMetadata } : {}),
|
|
263
275
|
...(input.partNumber ? { part_number: input.partNumber } : {})
|
|
264
276
|
}
|
|
265
277
|
};
|
|
@@ -299,6 +311,14 @@ async function signSourceRoute(input) {
|
|
|
299
311
|
async function signDestinationUrl(input) {
|
|
300
312
|
if (isS3CompatibleProvider(input.destination)) {
|
|
301
313
|
const client = createS3CompatibleClient(input.destination);
|
|
314
|
+
if (input.uploadId && input.partNumber) {
|
|
315
|
+
return getSignedUrl(client, new UploadPartCommand({
|
|
316
|
+
Bucket: input.destination.bucket,
|
|
317
|
+
Key: input.objectKey,
|
|
318
|
+
UploadId: input.uploadId,
|
|
319
|
+
PartNumber: input.partNumber
|
|
320
|
+
}), { expiresIn: input.expiresIn });
|
|
321
|
+
}
|
|
302
322
|
return getSignedUrl(client, new PutObjectCommand({ Bucket: input.destination.bucket, Key: input.objectKey }), { expiresIn: input.expiresIn });
|
|
303
323
|
}
|
|
304
324
|
if (isHippiusProvider(input.destination)) {
|