@beam-network/sdk 0.2.1 → 0.2.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/dist/client.d.ts +1 -0
- package/dist/client.js +67 -12
- package/dist/provider-signing.d.ts +10 -0
- package/dist/provider-signing.js +41 -5
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare class BeamClient {
|
|
|
28
28
|
urlsExpiresAt?: string;
|
|
29
29
|
}): Promise<AttachSignedUrlsResponse>;
|
|
30
30
|
prepareProviderTransfer(input: ProviderTransferCreateInput): Promise<TransferPrepareResponse>;
|
|
31
|
+
private signProviderRoute;
|
|
31
32
|
createAndDistribute(input: RawTransferCreateInput): Promise<TransferCreateResponse>;
|
|
32
33
|
waitForTransfer(transferId: string, options?: {
|
|
33
34
|
timeoutMs?: number;
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { abortMultipartUpload, createMultipartUpload, nowIso, prepareProviderDestination, prepareProviderSource, signAbortMultipartUpload, signCompleteMultipartUpload, signDestinationRoute, signListMultipartUpload } from "./provider-signing.js";
|
|
1
|
+
import { abortMultipartUpload, createMultipartUpload, nowIso, prepareProviderDestination, prepareProviderSource, signAbortMultipartUpload, signCompleteMultipartUpload, signDeleteObject, signDestinationRoute, signListMultipartUpload, signUploadPartCopy } from "./provider-signing.js";
|
|
2
2
|
export const BEAM_DEFAULT_URL = "https://beamcore.b1m.ai";
|
|
3
3
|
export class BeamApiError extends Error {
|
|
4
4
|
status;
|
|
@@ -139,23 +139,17 @@ export class BeamClient {
|
|
|
139
139
|
}
|
|
140
140
|
multipartUploads.set(multipartKey, upload);
|
|
141
141
|
}
|
|
142
|
-
routes.push(await
|
|
142
|
+
routes.push(await this.signProviderRoute({
|
|
143
143
|
chunk,
|
|
144
144
|
target,
|
|
145
145
|
destination,
|
|
146
146
|
expiresIn,
|
|
147
|
-
|
|
147
|
+
upload,
|
|
148
|
+
transferId: prepared.transfer_id,
|
|
149
|
+
finalObjectKey,
|
|
148
150
|
partNumber: typeof metadata.part_number === "number"
|
|
149
151
|
? metadata.part_number
|
|
150
|
-
: chunk.source_chunk_index + 1
|
|
151
|
-
multipartObjectKey: finalObjectKey,
|
|
152
|
-
completeUrl: upload.completeUrl,
|
|
153
|
-
abortUrl: upload.abortUrl,
|
|
154
|
-
listUrl: upload.listUrl,
|
|
155
|
-
transferId: prepared.transfer_id,
|
|
156
|
-
multipartGroupId: upload.multipartGroupId,
|
|
157
|
-
multipartCreatedAt: upload.multipartCreatedAt,
|
|
158
|
-
fetchImpl: this.fetchImpl
|
|
152
|
+
: chunk.source_chunk_index + 1
|
|
159
153
|
}));
|
|
160
154
|
}
|
|
161
155
|
}
|
|
@@ -182,6 +176,54 @@ export class BeamClient {
|
|
|
182
176
|
}
|
|
183
177
|
return prepared;
|
|
184
178
|
}
|
|
179
|
+
async signProviderRoute(input) {
|
|
180
|
+
if (isHippiusDestination(input.destination)) {
|
|
181
|
+
return signDestinationRoute({
|
|
182
|
+
chunk: input.chunk,
|
|
183
|
+
target: input.target,
|
|
184
|
+
destination: input.destination,
|
|
185
|
+
expiresIn: input.expiresIn,
|
|
186
|
+
transferId: input.transferId,
|
|
187
|
+
multipartGroupId: input.upload.multipartGroupId,
|
|
188
|
+
multipartCreatedAt: input.upload.multipartCreatedAt,
|
|
189
|
+
fetchImpl: this.fetchImpl
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (!input.target.object_key) {
|
|
193
|
+
throw new Error("destination signing target is missing object_key");
|
|
194
|
+
}
|
|
195
|
+
const stagingObjectKey = buildStagingObjectKey({
|
|
196
|
+
finalObjectKey: input.finalObjectKey,
|
|
197
|
+
transferId: input.transferId,
|
|
198
|
+
sourceId: input.chunk.source_id,
|
|
199
|
+
destinationId: input.target.destination_id,
|
|
200
|
+
chunkIndex: input.chunk.chunk_index,
|
|
201
|
+
partNumber: input.partNumber
|
|
202
|
+
});
|
|
203
|
+
const { copyUrl, copySource } = await signUploadPartCopy(input.destination, input.finalObjectKey, stagingObjectKey, input.upload.uploadId, input.partNumber, input.expiresIn);
|
|
204
|
+
const deleteUrl = await signDeleteObject(input.destination, stagingObjectKey, input.expiresIn);
|
|
205
|
+
return signDestinationRoute({
|
|
206
|
+
chunk: input.chunk,
|
|
207
|
+
target: input.target,
|
|
208
|
+
destination: input.destination,
|
|
209
|
+
expiresIn: input.expiresIn,
|
|
210
|
+
uploadId: input.upload.uploadId,
|
|
211
|
+
partNumber: input.partNumber,
|
|
212
|
+
multipartObjectKey: input.finalObjectKey,
|
|
213
|
+
completeUrl: input.upload.completeUrl,
|
|
214
|
+
abortUrl: input.upload.abortUrl,
|
|
215
|
+
listUrl: input.upload.listUrl,
|
|
216
|
+
transferId: input.transferId,
|
|
217
|
+
multipartGroupId: input.upload.multipartGroupId,
|
|
218
|
+
multipartCreatedAt: input.upload.multipartCreatedAt,
|
|
219
|
+
commitMethod: "upload_part_copy",
|
|
220
|
+
stagingObjectKey,
|
|
221
|
+
copyUrl,
|
|
222
|
+
copySource,
|
|
223
|
+
deleteUrl,
|
|
224
|
+
fetchImpl: this.fetchImpl
|
|
225
|
+
});
|
|
226
|
+
}
|
|
185
227
|
async createAndDistribute(input) {
|
|
186
228
|
const transfer = await this.createRawTransfer(input);
|
|
187
229
|
if (transfer.success) {
|
|
@@ -258,6 +300,19 @@ function normalizeBaseUrl(value) {
|
|
|
258
300
|
function compact(input) {
|
|
259
301
|
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
|
|
260
302
|
}
|
|
303
|
+
function buildStagingObjectKey(input) {
|
|
304
|
+
const base = input.finalObjectKey.replace(/\/+$/, "") || "object";
|
|
305
|
+
return [
|
|
306
|
+
`${base}.beam-staging`,
|
|
307
|
+
safeObjectKeySegment(input.transferId),
|
|
308
|
+
safeObjectKeySegment(input.sourceId),
|
|
309
|
+
safeObjectKeySegment(input.destinationId),
|
|
310
|
+
`part-${input.partNumber}-chunk-${input.chunkIndex}`
|
|
311
|
+
].join("/");
|
|
312
|
+
}
|
|
313
|
+
function safeObjectKeySegment(value) {
|
|
314
|
+
return encodeURIComponent(value).replace(/%2F/gi, "_");
|
|
315
|
+
}
|
|
261
316
|
function validateId(value, name) {
|
|
262
317
|
if (!value || value.includes("/") || value.includes("..")) {
|
|
263
318
|
throw new Error(`${name} is invalid`);
|
|
@@ -14,6 +14,11 @@ export declare function createMultipartUpload(destination: ProviderDestinationCo
|
|
|
14
14
|
export declare function signCompleteMultipartUpload(destination: ProviderDestinationConfig, objectKey: string, uploadId: string, expiresIn: number): Promise<string>;
|
|
15
15
|
export declare function signAbortMultipartUpload(destination: ProviderDestinationConfig, objectKey: string, uploadId: string, expiresIn: number): Promise<string>;
|
|
16
16
|
export declare function signListMultipartUpload(destination: ProviderDestinationConfig, objectKey: string, uploadId: string, expiresIn: number): Promise<string>;
|
|
17
|
+
export declare function signUploadPartCopy(destination: ProviderDestinationConfig, finalObjectKey: string, stagingObjectKey: string, uploadId: string, partNumber: number, expiresIn: number): Promise<{
|
|
18
|
+
copyUrl: string;
|
|
19
|
+
copySource: string;
|
|
20
|
+
}>;
|
|
21
|
+
export declare function signDeleteObject(destination: ProviderDestinationConfig, objectKey: string, expiresIn: number): Promise<string>;
|
|
17
22
|
export declare function abortMultipartUpload(destination: ProviderDestinationConfig, objectKey: string, uploadId: string): Promise<void>;
|
|
18
23
|
export declare function signDestinationRoute(input: {
|
|
19
24
|
chunk: ChunkSigningPlanItem;
|
|
@@ -29,6 +34,11 @@ export declare function signDestinationRoute(input: {
|
|
|
29
34
|
transferId?: string;
|
|
30
35
|
multipartGroupId?: string;
|
|
31
36
|
multipartCreatedAt?: string;
|
|
37
|
+
commitMethod?: "upload_part_copy";
|
|
38
|
+
stagingObjectKey?: string;
|
|
39
|
+
copyUrl?: string;
|
|
40
|
+
copySource?: string;
|
|
41
|
+
deleteUrl?: string;
|
|
32
42
|
fetchImpl?: typeof fetch;
|
|
33
43
|
}): Promise<SignedChunkRoute>;
|
|
34
44
|
export declare function isS3CompatibleProvider(config: ProviderSourceConfig | ProviderDestinationConfig): config is AnyS3CompatibleProviderConfig;
|
package/dist/provider-signing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, GetObjectCommand, HeadObjectCommand, ListPartsCommand, PutObjectCommand, S3Client, UploadPartCommand } from "@aws-sdk/client-s3";
|
|
1
|
+
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListPartsCommand, PutObjectCommand, S3Client, UploadPartCopyCommand, UploadPartCommand } 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();
|
|
@@ -103,6 +103,30 @@ export async function signListMultipartUpload(destination, objectKey, uploadId,
|
|
|
103
103
|
}
|
|
104
104
|
throw new Error(`multipart list-parts is not supported for ${destination.provider}`);
|
|
105
105
|
}
|
|
106
|
+
export async function signUploadPartCopy(destination, finalObjectKey, stagingObjectKey, uploadId, partNumber, expiresIn) {
|
|
107
|
+
if (isS3CompatibleProvider(destination)) {
|
|
108
|
+
const client = createS3CompatibleClient(destination);
|
|
109
|
+
const copySource = copySourceHeader(destination, stagingObjectKey);
|
|
110
|
+
return {
|
|
111
|
+
copySource,
|
|
112
|
+
copyUrl: await getSignedUrl(client, new UploadPartCopyCommand({
|
|
113
|
+
Bucket: destination.bucket,
|
|
114
|
+
Key: finalObjectKey,
|
|
115
|
+
UploadId: uploadId,
|
|
116
|
+
PartNumber: partNumber,
|
|
117
|
+
CopySource: copySource
|
|
118
|
+
}), { expiresIn })
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`multipart copy is not supported for ${destination.provider}`);
|
|
122
|
+
}
|
|
123
|
+
export async function signDeleteObject(destination, objectKey, expiresIn) {
|
|
124
|
+
if (isS3CompatibleProvider(destination)) {
|
|
125
|
+
const client = createS3CompatibleClient(destination);
|
|
126
|
+
return getSignedUrl(client, new DeleteObjectCommand({ Bucket: destination.bucket, Key: objectKey }), { expiresIn });
|
|
127
|
+
}
|
|
128
|
+
throw new Error(`object delete signing is not supported for ${destination.provider}`);
|
|
129
|
+
}
|
|
106
130
|
export async function abortMultipartUpload(destination, objectKey, uploadId) {
|
|
107
131
|
if (isS3CompatibleProvider(destination)) {
|
|
108
132
|
await createS3CompatibleClient(destination).send(new AbortMultipartUploadCommand({ Bucket: destination.bucket, Key: objectKey, UploadId: uploadId }));
|
|
@@ -111,11 +135,14 @@ export async function abortMultipartUpload(destination, objectKey, uploadId) {
|
|
|
111
135
|
throw new Error(`multipart abort is not supported for ${destination.provider}`);
|
|
112
136
|
}
|
|
113
137
|
export async function signDestinationRoute(input) {
|
|
114
|
-
const
|
|
115
|
-
if (!
|
|
138
|
+
const targetObjectKey = input.target.object_key;
|
|
139
|
+
if (!targetObjectKey) {
|
|
116
140
|
throw new Error("destination signing target is missing object_key");
|
|
117
141
|
}
|
|
118
|
-
const
|
|
142
|
+
const objectKey = input.commitMethod === "upload_part_copy" && input.stagingObjectKey
|
|
143
|
+
? input.stagingObjectKey
|
|
144
|
+
: targetObjectKey;
|
|
145
|
+
const uploadPartObjectKey = input.multipartObjectKey ?? targetObjectKey;
|
|
119
146
|
const destUrl = await signDestinationUrl({
|
|
120
147
|
destination: input.destination,
|
|
121
148
|
objectKey,
|
|
@@ -123,6 +150,7 @@ export async function signDestinationRoute(input) {
|
|
|
123
150
|
expiresIn: input.expiresIn,
|
|
124
151
|
uploadId: input.uploadId,
|
|
125
152
|
partNumber: input.partNumber,
|
|
153
|
+
useStagingPut: input.commitMethod === "upload_part_copy",
|
|
126
154
|
fetchImpl: input.fetchImpl
|
|
127
155
|
});
|
|
128
156
|
return {
|
|
@@ -139,6 +167,11 @@ export async function signDestinationRoute(input) {
|
|
|
139
167
|
...(input.transferId ? { transfer_id: input.transferId } : {}),
|
|
140
168
|
...(input.multipartGroupId ? { multipart_group_id: input.multipartGroupId } : {}),
|
|
141
169
|
...(input.multipartCreatedAt ? { multipart_created_at: input.multipartCreatedAt } : {}),
|
|
170
|
+
...(input.commitMethod ? { commit_method: input.commitMethod } : {}),
|
|
171
|
+
...(input.stagingObjectKey ? { staging_object_key: input.stagingObjectKey } : {}),
|
|
172
|
+
...(input.copyUrl ? { copy_url: input.copyUrl } : {}),
|
|
173
|
+
...(input.copySource ? { copy_source: input.copySource } : {}),
|
|
174
|
+
...(input.deleteUrl ? { delete_url: input.deleteUrl } : {}),
|
|
142
175
|
...(input.uploadId ? { upload_id: input.uploadId } : {}),
|
|
143
176
|
...(input.partNumber ? { part_number: input.partNumber } : {}),
|
|
144
177
|
...(input.completeUrl ? { complete_url: input.completeUrl } : {}),
|
|
@@ -150,7 +183,7 @@ export async function signDestinationRoute(input) {
|
|
|
150
183
|
async function signDestinationUrl(input) {
|
|
151
184
|
if (isS3CompatibleProvider(input.destination)) {
|
|
152
185
|
const client = createS3CompatibleClient(input.destination);
|
|
153
|
-
if (input.uploadId && input.partNumber) {
|
|
186
|
+
if (input.uploadId && input.partNumber && !input.useStagingPut) {
|
|
154
187
|
return getSignedUrl(client, new UploadPartCommand({
|
|
155
188
|
Bucket: input.destination.bucket,
|
|
156
189
|
Key: input.uploadPartObjectKey,
|
|
@@ -269,3 +302,6 @@ function hasText(value) {
|
|
|
269
302
|
function unsupportedProviderConfig(value) {
|
|
270
303
|
throw new Error(`Unsupported provider config: ${JSON.stringify(value)}`);
|
|
271
304
|
}
|
|
305
|
+
function copySourceHeader(destination, objectKey) {
|
|
306
|
+
return `/${destination.bucket}/${objectKey.split("/").map(encodeURIComponent).join("/")}`;
|
|
307
|
+
}
|