@beam-network/sdk 0.6.0 → 0.7.0
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 +30 -3
- package/dist/client.d.ts +6 -1
- package/dist/client.js +220 -72
- package/dist/models.d.ts +75 -0
- package/dist/nats-control.d.ts +13 -2
- package/dist/nats-control.js +117 -30
- package/dist/performance.d.ts +39 -0
- package/dist/performance.js +124 -0
- package/dist/provider-signing.d.ts +20 -0
- package/dist/provider-signing.js +55 -14
- package/package.json +1 -1
package/dist/provider-signing.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { currentSdkPerformance, measureSdkPhase } from "./performance.js";
|
|
1
2
|
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, GetObjectCommand, HeadObjectCommand, ListPartsCommand, PutObjectCommand, S3Client, UploadPartCommand } from "@aws-sdk/client-s3";
|
|
2
3
|
import { UploadPartCopyCommand, DeleteObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
|
|
3
4
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
@@ -69,7 +70,7 @@ export function nowIso() {
|
|
|
69
70
|
return new Date().toISOString();
|
|
70
71
|
}
|
|
71
72
|
export function expiresAtIso(expiresIn) {
|
|
72
|
-
return new Date(Date.now() + expiresIn * 1000).toISOString();
|
|
73
|
+
return new Date((Math.floor(Date.now() / 1000) + expiresIn) * 1000).toISOString();
|
|
73
74
|
}
|
|
74
75
|
export async function prepareProviderSource(source, options = {}) {
|
|
75
76
|
const index = options.index ?? 0;
|
|
@@ -82,7 +83,7 @@ export async function prepareProviderSource(source, options = {}) {
|
|
|
82
83
|
if (isS3CompatibleProvider(source)) {
|
|
83
84
|
const endpoint = s3CompatibleEndpoint(source);
|
|
84
85
|
const client = createS3CompatibleClient(source, endpoint);
|
|
85
|
-
const head = await client.send(new HeadObjectCommand({ Bucket: source.bucket, Key: source.key }), { abortSignal: options.signal });
|
|
86
|
+
const head = await measureSdkPhase("sdk.metadata_request", () => client.send(new HeadObjectCommand({ Bucket: source.bucket, Key: source.key }), { abortSignal: options.signal }));
|
|
86
87
|
const getUrl = await getSignedUrl(client, new GetObjectCommand({ Bucket: source.bucket, Key: source.key }), { expiresIn });
|
|
87
88
|
return {
|
|
88
89
|
source_id: source.id ?? `src_${index}`,
|
|
@@ -113,7 +114,7 @@ export async function prepareProviderSource(source, options = {}) {
|
|
|
113
114
|
size,
|
|
114
115
|
filename: filename(source.key),
|
|
115
116
|
expires_at: expiresAtIso(expiresIn),
|
|
116
|
-
metadata: { bucket: source.bucket, key: source.key, base_url: baseUrl }
|
|
117
|
+
metadata: compactMetadata({ bucket: source.bucket, key: source.key, base_url: baseUrl, storage_location: source.storage_location })
|
|
117
118
|
};
|
|
118
119
|
}
|
|
119
120
|
if (isHuggingFaceProvider(source)) {
|
|
@@ -161,7 +162,7 @@ export async function prepareProviderSourceForPlan(source, options = {}) {
|
|
|
161
162
|
provider: "hippius",
|
|
162
163
|
size,
|
|
163
164
|
filename: filename(source.key),
|
|
164
|
-
metadata: { bucket: source.bucket, key: source.key, base_url: baseUrl }
|
|
165
|
+
metadata: compactMetadata({ bucket: source.bucket, key: source.key, base_url: baseUrl, storage_location: source.storage_location })
|
|
165
166
|
};
|
|
166
167
|
}
|
|
167
168
|
if (isHuggingFaceProvider(source)) {
|
|
@@ -199,6 +200,7 @@ export function prepareProviderDestination(destination, options = {}) {
|
|
|
199
200
|
metadata: {
|
|
200
201
|
bucket: destination.bucket,
|
|
201
202
|
key: destination.key,
|
|
203
|
+
storage_location: destination.storage_location,
|
|
202
204
|
base_url: destination.base_url ?? "https://api.hippius.com"
|
|
203
205
|
}
|
|
204
206
|
};
|
|
@@ -307,21 +309,44 @@ export async function inspectDestinationObject(destination, objectKey, signal) {
|
|
|
307
309
|
const result = await createS3CompatibleClient(destination).send(new HeadObjectCommand({ Bucket: destination.bucket, Key: objectKey }), { abortSignal: signal });
|
|
308
310
|
return { size: result.ContentLength, etag: result.ETag, versionId: result.VersionId, metadata: result.Metadata ?? {} };
|
|
309
311
|
}
|
|
312
|
+
// Never advertise a renewed lifetime for a previously issued provider URL.
|
|
313
|
+
export function boundedGrantExpiry(url, upperBound) {
|
|
314
|
+
let expiry = Date.parse(upperBound);
|
|
315
|
+
const query = new URL(url).searchParams;
|
|
316
|
+
for (const prefix of ["X-Amz", "X-Goog"]) {
|
|
317
|
+
const date = query.get(`${prefix}-Date`), duration = Number(query.get(`${prefix}-Expires`));
|
|
318
|
+
if (date && /^\d{8}T\d{6}Z$/.test(date) && Number.isFinite(duration) && duration > 0) {
|
|
319
|
+
const start = Date.parse(`${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)}T${date.slice(9, 11)}:${date.slice(11, 13)}:${date.slice(13, 15)}Z`);
|
|
320
|
+
if (Number.isFinite(start))
|
|
321
|
+
expiry = Math.min(expiry, start + duration * 1000);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const epoch = Number(query.get("Expires"));
|
|
325
|
+
if (Number.isFinite(epoch) && epoch > 0)
|
|
326
|
+
expiry = Math.min(expiry, epoch * 1000);
|
|
327
|
+
return new Date(expiry).toISOString();
|
|
328
|
+
}
|
|
329
|
+
export async function signSourceChunk(input) {
|
|
330
|
+
const expiresAt = expiresAtIso(input.expiresIn);
|
|
331
|
+
const grant = await signSourceRoute(input);
|
|
332
|
+
return { ...grant, expiresAt: boundedGrantExpiry(grant.url, expiresAt) };
|
|
333
|
+
}
|
|
310
334
|
export async function signDestinationRoute(input) {
|
|
311
335
|
const targetObjectKey = input.target.object_key;
|
|
312
336
|
if (!targetObjectKey) {
|
|
313
337
|
throw new Error("destination signing target is missing object_key");
|
|
314
338
|
}
|
|
339
|
+
const destinationExpiresAt = expiresAtIso(input.expiresIn);
|
|
315
340
|
const [destUrl, sourceRoute] = await Promise.all([
|
|
316
|
-
input.destUrl ?? signDestinationUrl({
|
|
341
|
+
input.destUrl ?? measureSdkPhase("sdk.destination_signing", () => signDestinationUrl({
|
|
317
342
|
destination: input.destination,
|
|
318
343
|
objectKey: targetObjectKey,
|
|
319
344
|
uploadId: input.uploadId,
|
|
320
345
|
partNumber: input.partNumber,
|
|
321
346
|
expiresIn: input.expiresIn,
|
|
322
347
|
fetchImpl: input.fetchImpl,
|
|
323
|
-
}),
|
|
324
|
-
|
|
348
|
+
})),
|
|
349
|
+
input.sourceGrant ?? signSourceChunk({
|
|
325
350
|
source: input.source,
|
|
326
351
|
fallbackUrl: input.chunk.source_url,
|
|
327
352
|
chunk: input.chunk,
|
|
@@ -337,8 +362,8 @@ export async function signDestinationRoute(input) {
|
|
|
337
362
|
dest_url: destUrl,
|
|
338
363
|
source_offset: input.chunk.source_offset,
|
|
339
364
|
chunk_size: input.chunk.chunk_size,
|
|
340
|
-
expires_at:
|
|
341
|
-
headers: sourceRoute.headers,
|
|
365
|
+
expires_at: boundedGrantExpiry(destUrl, new Date(Math.min(Date.parse(destinationExpiresAt), Date.parse(sourceRoute.expiresAt))).toISOString()),
|
|
366
|
+
headers: { ...sourceRoute.headers },
|
|
342
367
|
metadata: {
|
|
343
368
|
...(input.target.metadata ?? {}),
|
|
344
369
|
...(input.multipartGroupId ? { multipart_group_id: input.multipartGroupId } : {}),
|
|
@@ -475,7 +500,16 @@ export async function signDestinationUrl(input) {
|
|
|
475
500
|
}
|
|
476
501
|
return unsupportedProviderConfig(input.destination);
|
|
477
502
|
}
|
|
478
|
-
const s3ClientCache = new
|
|
503
|
+
const s3ClientCache = new WeakMap();
|
|
504
|
+
export function releaseProviderClients(configs) {
|
|
505
|
+
for (const config of configs) {
|
|
506
|
+
if (!isS3CompatibleProvider(config))
|
|
507
|
+
continue;
|
|
508
|
+
const cached = s3ClientCache.get(config);
|
|
509
|
+
s3ClientCache.delete(config);
|
|
510
|
+
cached?.client.destroy();
|
|
511
|
+
}
|
|
512
|
+
}
|
|
479
513
|
function s3ClientCacheKey(source, endpoint) {
|
|
480
514
|
return JSON.stringify({
|
|
481
515
|
provider: source.provider,
|
|
@@ -489,9 +523,12 @@ function s3ClientCacheKey(source, endpoint) {
|
|
|
489
523
|
}
|
|
490
524
|
function createS3CompatibleClient(source, endpoint = s3CompatibleEndpoint(source)) {
|
|
491
525
|
const cacheKey = s3ClientCacheKey(source, endpoint);
|
|
492
|
-
const cached = s3ClientCache.get(
|
|
493
|
-
if (cached)
|
|
494
|
-
|
|
526
|
+
const cached = s3ClientCache.get(source);
|
|
527
|
+
if (cached?.identity === cacheKey) {
|
|
528
|
+
currentSdkPerformance()?.increment("provider_clients_reused");
|
|
529
|
+
return cached.client;
|
|
530
|
+
}
|
|
531
|
+
const setupStarted = performance.now();
|
|
495
532
|
const client = new S3Client({
|
|
496
533
|
region: s3CompatibleRegion(source),
|
|
497
534
|
endpoint,
|
|
@@ -503,7 +540,9 @@ function createS3CompatibleClient(source, endpoint = s3CompatibleEndpoint(source
|
|
|
503
540
|
sessionToken: "session_token" in source ? source.session_token : undefined
|
|
504
541
|
}
|
|
505
542
|
});
|
|
506
|
-
s3ClientCache.set(cacheKey, client);
|
|
543
|
+
s3ClientCache.set(source, { identity: cacheKey, client });
|
|
544
|
+
currentSdkPerformance()?.increment("provider_clients_created");
|
|
545
|
+
currentSdkPerformance()?.observe("sdk.provider_client_setup", performance.now() - setupStarted);
|
|
507
546
|
return client;
|
|
508
547
|
}
|
|
509
548
|
export function isS3CompatibleProvider(config) {
|
|
@@ -554,6 +593,7 @@ function s3CompatibleMetadata(source, endpoint = s3CompatibleEndpoint(source)) {
|
|
|
554
593
|
bucket: source.bucket,
|
|
555
594
|
key: source.key,
|
|
556
595
|
region: s3CompatibleRegion(source),
|
|
596
|
+
storage_location: source.storage_location,
|
|
557
597
|
endpoint_url: endpoint,
|
|
558
598
|
account_id: "account_id" in source ? source.account_id : undefined
|
|
559
599
|
});
|
|
@@ -567,6 +607,7 @@ export function isHuggingFaceProvider(config) {
|
|
|
567
607
|
function huggingFaceMetadata(config, etag, commitHash) {
|
|
568
608
|
return compactMetadata({
|
|
569
609
|
driver: "huggingface",
|
|
610
|
+
storage_location: config.storage_location,
|
|
570
611
|
repo_id: config.repo_id,
|
|
571
612
|
repo_type: huggingFaceRepoType(config),
|
|
572
613
|
revision: huggingFaceRevision(config),
|