@beam-network/sdk 0.2.17 → 0.2.19
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 +1 -1
- package/dist/client.js +50 -29
- package/dist/nats-control.js +24 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,6 +95,6 @@ For low-level raw transfer configs, use `createRawTransfer`; lifecycle transport
|
|
|
95
95
|
|
|
96
96
|
## Route Streaming And Payload Size
|
|
97
97
|
|
|
98
|
-
Provider transfers stream signed routes over NATS in target batches of
|
|
98
|
+
Provider transfers stream signed routes over NATS in target batches of 128 routes. Signing continues while completed batches are sent in order. The SDK still splits a batch earlier when the encoded MessagePack request would exceed `maxPayloadBytes`, which defaults to 24 MiB.
|
|
99
99
|
|
|
100
100
|
NATS requires this guard because the broker rejects messages above its configured `max_payload`. This limit applies only to lifecycle/control metadata; transfer file bytes do not flow through NATS.
|
package/dist/client.js
CHANGED
|
@@ -113,7 +113,7 @@ export class BeamClient {
|
|
|
113
113
|
});
|
|
114
114
|
await stream.begin();
|
|
115
115
|
for (const route of input.chunkRoutes) {
|
|
116
|
-
|
|
116
|
+
stream.addRoute(route);
|
|
117
117
|
}
|
|
118
118
|
return stream.complete();
|
|
119
119
|
}
|
|
@@ -223,7 +223,7 @@ export class BeamClient {
|
|
|
223
223
|
}
|
|
224
224
|
multipartUploads.set(multipartKey, upload);
|
|
225
225
|
}
|
|
226
|
-
|
|
226
|
+
routeStream.addRoute(await this.signProviderRoute({
|
|
227
227
|
chunk,
|
|
228
228
|
target,
|
|
229
229
|
source,
|
|
@@ -245,6 +245,7 @@ export class BeamClient {
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
catch (error) {
|
|
248
|
+
routeStream.abort();
|
|
248
249
|
this.stopRecoverySigner(prepared.transfer_id);
|
|
249
250
|
await abortCreatedUploads(multipartUploads);
|
|
250
251
|
if (routeStreamStarted) {
|
|
@@ -440,9 +441,9 @@ export class BeamClient {
|
|
|
440
441
|
}
|
|
441
442
|
}
|
|
442
443
|
}
|
|
443
|
-
// Target route-stream batches at
|
|
444
|
+
// Target route-stream batches at 128 signed routes. The transport still splits
|
|
444
445
|
// earlier when the encoded NATS request would exceed maxPayloadBytes.
|
|
445
|
-
const ROUTE_STREAM_TARGET_BATCH_ROUTES =
|
|
446
|
+
const ROUTE_STREAM_TARGET_BATCH_ROUTES = 128;
|
|
446
447
|
class RouteStreamSender {
|
|
447
448
|
control;
|
|
448
449
|
options;
|
|
@@ -450,6 +451,8 @@ class RouteStreamSender {
|
|
|
450
451
|
checksum = new RouteKeysChecksum();
|
|
451
452
|
batch = [];
|
|
452
453
|
batchIndex = 0;
|
|
454
|
+
sendTail = Promise.resolve();
|
|
455
|
+
sendError = null;
|
|
453
456
|
constructor(control, options) {
|
|
454
457
|
this.control = control;
|
|
455
458
|
this.options = options;
|
|
@@ -465,15 +468,17 @@ class RouteStreamSender {
|
|
|
465
468
|
auto_distribute: this.options.autoDistribute
|
|
466
469
|
}), this.options.transferId);
|
|
467
470
|
}
|
|
468
|
-
|
|
469
|
-
await this.checksum.add(routeKeyForSignedRoute(route));
|
|
471
|
+
addRoute(route) {
|
|
470
472
|
this.batch.push(route);
|
|
471
473
|
if (this.batch.length >= ROUTE_STREAM_TARGET_BATCH_ROUTES) {
|
|
472
|
-
|
|
474
|
+
this.enqueueFlush();
|
|
473
475
|
}
|
|
474
476
|
}
|
|
475
477
|
async complete() {
|
|
476
|
-
|
|
478
|
+
this.enqueueFlush();
|
|
479
|
+
await this.sendTail;
|
|
480
|
+
if (this.sendError)
|
|
481
|
+
throw this.sendError;
|
|
477
482
|
return this.control.request("transfer.route_stream.complete", {
|
|
478
483
|
transfer_id: this.options.transferId,
|
|
479
484
|
stream_id: this.streamId,
|
|
@@ -482,7 +487,7 @@ class RouteStreamSender {
|
|
|
482
487
|
route_keys_checksum: this.checksum.value()
|
|
483
488
|
}, this.options.transferId);
|
|
484
489
|
}
|
|
485
|
-
|
|
490
|
+
enqueueFlush() {
|
|
486
491
|
if (!this.batch.length)
|
|
487
492
|
return;
|
|
488
493
|
const routes = this.batch.splice(0, this.batch.length);
|
|
@@ -495,39 +500,55 @@ class RouteStreamSender {
|
|
|
495
500
|
for (const chunk of chunks) {
|
|
496
501
|
const batchIndex = this.batchIndex;
|
|
497
502
|
this.batchIndex += 1;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
503
|
+
this.sendTail = this.sendTail.then(async () => {
|
|
504
|
+
if (this.sendError)
|
|
505
|
+
return;
|
|
506
|
+
try {
|
|
507
|
+
const chunkChecksum = new RouteKeysChecksum();
|
|
508
|
+
await chunkChecksum.addMany(chunk.map(routeKeyForSignedRoute));
|
|
509
|
+
this.checksum.merge(chunkChecksum);
|
|
510
|
+
await this.control.request("transfer.route_stream.batch", {
|
|
511
|
+
transfer_id: this.options.transferId,
|
|
512
|
+
stream_id: this.streamId,
|
|
513
|
+
batch_id: `${this.streamId}:${batchIndex}`,
|
|
514
|
+
batch_index: batchIndex,
|
|
515
|
+
chunk_routes: chunk,
|
|
516
|
+
route_count: chunk.length,
|
|
517
|
+
route_keys_checksum: chunkChecksum.value()
|
|
518
|
+
}, this.options.transferId);
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
this.sendError = error;
|
|
522
|
+
}
|
|
523
|
+
});
|
|
507
524
|
}
|
|
508
525
|
}
|
|
526
|
+
abort() {
|
|
527
|
+
this.sendError ??= new Error("route_stream_aborted");
|
|
528
|
+
}
|
|
509
529
|
}
|
|
510
530
|
class RouteKeysChecksum {
|
|
511
531
|
bytes = new Uint8Array(32);
|
|
512
532
|
count = 0;
|
|
513
|
-
async
|
|
514
|
-
const
|
|
533
|
+
async addMany(routeKeys) {
|
|
534
|
+
const digests = await Promise.all(routeKeys.map(sha256Bytes));
|
|
535
|
+
for (const digest of digests) {
|
|
536
|
+
for (let index = 0; index < this.bytes.length; index += 1) {
|
|
537
|
+
this.bytes[index] ^= digest[index] ?? 0;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
this.count += routeKeys.length;
|
|
541
|
+
}
|
|
542
|
+
merge(other) {
|
|
515
543
|
for (let index = 0; index < this.bytes.length; index += 1) {
|
|
516
|
-
this.bytes[index] ^=
|
|
544
|
+
this.bytes[index] ^= other.bytes[index] ?? 0;
|
|
517
545
|
}
|
|
518
|
-
this.count +=
|
|
546
|
+
this.count += other.count;
|
|
519
547
|
}
|
|
520
548
|
value() {
|
|
521
549
|
return `sha256-xor-v1:${this.count}:${hex(this.bytes)}`;
|
|
522
550
|
}
|
|
523
551
|
}
|
|
524
|
-
async function routeKeysChecksum(routeKeys) {
|
|
525
|
-
const checksum = new RouteKeysChecksum();
|
|
526
|
-
for (const routeKeyValue of routeKeys) {
|
|
527
|
-
await checksum.add(routeKeyValue);
|
|
528
|
-
}
|
|
529
|
-
return checksum.value();
|
|
530
|
-
}
|
|
531
552
|
function routeKeyForSignedRoute(route) {
|
|
532
553
|
return `${route.source_id}:${route.destination_id}:${route.chunk_index}`;
|
|
533
554
|
}
|
package/dist/nats-control.js
CHANGED
|
@@ -7,6 +7,25 @@ export const BEAM_DEFAULT_NATS_WS_URL = "wss://nats.b1m.ai:443";
|
|
|
7
7
|
// messages before the broker rejects them; transfer bytes never flow through NATS.
|
|
8
8
|
export const BEAM_DEFAULT_MAX_PAYLOAD_BYTES = 24 * 1024 * 1024;
|
|
9
9
|
export const ROUTE_RECOVERY_SIGN_MESSAGE_TYPE = "transfer.route_recovery.sign";
|
|
10
|
+
function buildConnectionOptions(options) {
|
|
11
|
+
const connectionOptions = {
|
|
12
|
+
servers: options.natsUrl,
|
|
13
|
+
user: options.keyPrefix,
|
|
14
|
+
pass: options.apiKey,
|
|
15
|
+
name: `beam-sdk-${options.keyPrefix}`
|
|
16
|
+
};
|
|
17
|
+
if (options.natsUrl.startsWith("tls://")) {
|
|
18
|
+
const tlsOptions = { handshakeFirst: true };
|
|
19
|
+
try {
|
|
20
|
+
tlsOptions.servername = new URL(options.natsUrl).hostname;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// URL validation stays with the NATS client; TLS-first is still required.
|
|
24
|
+
}
|
|
25
|
+
connectionOptions.tls = tlsOptions;
|
|
26
|
+
}
|
|
27
|
+
return connectionOptions;
|
|
28
|
+
}
|
|
10
29
|
export class BeamTransferControl {
|
|
11
30
|
environment;
|
|
12
31
|
keyPrefix;
|
|
@@ -145,12 +164,11 @@ export class BeamTransferControl {
|
|
|
145
164
|
async connection() {
|
|
146
165
|
if (this.nc && !this.nc.isClosed())
|
|
147
166
|
return this.nc;
|
|
148
|
-
this.nc = await connect({
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
167
|
+
this.nc = await connect(buildConnectionOptions({
|
|
168
|
+
natsUrl: this.natsUrl,
|
|
169
|
+
apiKey: this.apiKey,
|
|
170
|
+
keyPrefix: this.keyPrefix
|
|
171
|
+
}));
|
|
154
172
|
return this.nc;
|
|
155
173
|
}
|
|
156
174
|
async resolveAuthToken() {
|