capnweb 0.4.0 → 0.5.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 +6 -2
- package/dist/index-workers.cjs +803 -36
- package/dist/index-workers.cjs.map +1 -1
- package/dist/index-workers.js +803 -36
- package/dist/index-workers.js.map +1 -1
- package/dist/index.cjs +803 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +803 -36
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index-workers.js
CHANGED
|
@@ -59,6 +59,16 @@ function typeForRpc(value) {
|
|
|
59
59
|
return "date";
|
|
60
60
|
case Uint8Array.prototype:
|
|
61
61
|
return "bytes";
|
|
62
|
+
case WritableStream.prototype:
|
|
63
|
+
return "writable";
|
|
64
|
+
case ReadableStream.prototype:
|
|
65
|
+
return "readable";
|
|
66
|
+
case Headers.prototype:
|
|
67
|
+
return "headers";
|
|
68
|
+
case Request.prototype:
|
|
69
|
+
return "request";
|
|
70
|
+
case Response.prototype:
|
|
71
|
+
return "response";
|
|
62
72
|
// TODO: All other structured clone types.
|
|
63
73
|
case RpcStub.prototype:
|
|
64
74
|
return "stub";
|
|
@@ -86,7 +96,34 @@ function mapNotLoaded() {
|
|
|
86
96
|
throw new Error("RPC map() implementation was not loaded.");
|
|
87
97
|
}
|
|
88
98
|
var mapImpl = { applyMap: mapNotLoaded, sendMap: mapNotLoaded };
|
|
99
|
+
function streamNotLoaded() {
|
|
100
|
+
throw new Error("Stream implementation was not loaded.");
|
|
101
|
+
}
|
|
102
|
+
var streamImpl = {
|
|
103
|
+
createWritableStreamHook: streamNotLoaded,
|
|
104
|
+
createWritableStreamFromHook: streamNotLoaded,
|
|
105
|
+
createReadableStreamHook: streamNotLoaded
|
|
106
|
+
};
|
|
89
107
|
var StubHook = class {
|
|
108
|
+
// Like call(), but designed for streaming calls (e.g. WritableStream writes). Returns:
|
|
109
|
+
// - promise: A Promise<void> for the completion of the call.
|
|
110
|
+
// - size: If the call was remote, the byte size of the serialized message. For local calls,
|
|
111
|
+
// undefined is returned, indicating the caller should await the promise to serialize writes
|
|
112
|
+
// (no overlapping).
|
|
113
|
+
stream(path, args) {
|
|
114
|
+
let hook = this.call(path, args);
|
|
115
|
+
let pulled = hook.pull();
|
|
116
|
+
let promise;
|
|
117
|
+
if (pulled instanceof Promise) {
|
|
118
|
+
promise = pulled.then((p) => {
|
|
119
|
+
p.dispose();
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
pulled.dispose();
|
|
123
|
+
promise = Promise.resolve();
|
|
124
|
+
}
|
|
125
|
+
return { promise };
|
|
126
|
+
}
|
|
90
127
|
};
|
|
91
128
|
var ErrorStubHook = class extends StubHook {
|
|
92
129
|
constructor(error) {
|
|
@@ -311,10 +348,10 @@ async function pullPromise(promise) {
|
|
|
311
348
|
}
|
|
312
349
|
var RpcPayload = class _RpcPayload {
|
|
313
350
|
// Private constructor; use factory functions above to construct.
|
|
314
|
-
constructor(value, source,
|
|
351
|
+
constructor(value, source, hooks, promises) {
|
|
315
352
|
this.value = value;
|
|
316
353
|
this.source = source;
|
|
317
|
-
this.
|
|
354
|
+
this.hooks = hooks;
|
|
318
355
|
this.promises = promises;
|
|
319
356
|
}
|
|
320
357
|
// Create a payload from a value passed as params to an RPC from the app.
|
|
@@ -339,13 +376,13 @@ var RpcPayload = class _RpcPayload {
|
|
|
339
376
|
// stubs is transferred from the inputs to the outputs, hence if the output is disposed, the
|
|
340
377
|
// inputs should not be. (In case of exception, nothing is disposed, though.)
|
|
341
378
|
static fromArray(array) {
|
|
342
|
-
let
|
|
379
|
+
let hooks = [];
|
|
343
380
|
let promises = [];
|
|
344
381
|
let resultArray = [];
|
|
345
382
|
for (let payload of array) {
|
|
346
383
|
payload.ensureDeepCopied();
|
|
347
|
-
for (let
|
|
348
|
-
|
|
384
|
+
for (let hook of payload.hooks) {
|
|
385
|
+
hooks.push(hook);
|
|
349
386
|
}
|
|
350
387
|
for (let promise of payload.promises) {
|
|
351
388
|
if (promise.parent === payload) {
|
|
@@ -359,12 +396,12 @@ var RpcPayload = class _RpcPayload {
|
|
|
359
396
|
}
|
|
360
397
|
resultArray.push(payload.value);
|
|
361
398
|
}
|
|
362
|
-
return new _RpcPayload(resultArray, "owned",
|
|
399
|
+
return new _RpcPayload(resultArray, "owned", hooks, promises);
|
|
363
400
|
}
|
|
364
401
|
// Create a payload from a value parsed off the wire using Evaluator.evaluate().
|
|
365
402
|
//
|
|
366
|
-
// A payload is constructed with a null value and the given
|
|
367
|
-
// is expected to be filled in by the evaluator, and the
|
|
403
|
+
// A payload is constructed with a null value and the given hooks and promises arrays. The value
|
|
404
|
+
// is expected to be filled in by the evaluator, and the hooks and promises arrays are expected
|
|
368
405
|
// to be extended with stubs found during parsing. (This weird usage model is necessary so that
|
|
369
406
|
// if the root value turns out to be a promise, its `parent` in `promises` can be the payload
|
|
370
407
|
// object itself.)
|
|
@@ -372,8 +409,8 @@ var RpcPayload = class _RpcPayload {
|
|
|
372
409
|
// When done, the payload takes ownership of the final value and all the stubs within. It may
|
|
373
410
|
// modify the value in preparation for delivery, and may deliver the value directly to the app
|
|
374
411
|
// without copying.
|
|
375
|
-
static forEvaluate(
|
|
376
|
-
return new _RpcPayload(null, "owned",
|
|
412
|
+
static forEvaluate(hooks, promises) {
|
|
413
|
+
return new _RpcPayload(null, "owned", hooks, promises);
|
|
377
414
|
}
|
|
378
415
|
// Deep-copy the given value, including dup()ing all stubs.
|
|
379
416
|
//
|
|
@@ -395,8 +432,8 @@ var RpcPayload = class _RpcPayload {
|
|
|
395
432
|
return result;
|
|
396
433
|
}
|
|
397
434
|
// For `source === "return"` payloads only, this tracks any StubHooks created around RpcTargets
|
|
398
|
-
// found in the payload at the time that it is serialized (or deep-copied) for
|
|
399
|
-
// can make sure they are not disposed before the pipeline ends.
|
|
435
|
+
// or WritableStreams found in the payload at the time that it is serialized (or deep-copied) for
|
|
436
|
+
// return, so that we can make sure they are not disposed before the pipeline ends.
|
|
400
437
|
//
|
|
401
438
|
// This is initialized on first use.
|
|
402
439
|
rpcTargets;
|
|
@@ -435,6 +472,64 @@ var RpcPayload = class _RpcPayload {
|
|
|
435
472
|
throw new Error("owned payload shouldn't contain raw RpcTargets");
|
|
436
473
|
}
|
|
437
474
|
}
|
|
475
|
+
// Get the StubHook representing the given WritableStream found inside this payload.
|
|
476
|
+
getHookForWritableStream(stream, parent, dupStubs = true) {
|
|
477
|
+
if (this.source === "params") {
|
|
478
|
+
return streamImpl.createWritableStreamHook(stream);
|
|
479
|
+
} else if (this.source === "return") {
|
|
480
|
+
let hook = this.rpcTargets?.get(stream);
|
|
481
|
+
if (hook) {
|
|
482
|
+
if (dupStubs) {
|
|
483
|
+
return hook.dup();
|
|
484
|
+
} else {
|
|
485
|
+
this.rpcTargets?.delete(stream);
|
|
486
|
+
return hook;
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
hook = streamImpl.createWritableStreamHook(stream);
|
|
490
|
+
if (dupStubs) {
|
|
491
|
+
if (!this.rpcTargets) {
|
|
492
|
+
this.rpcTargets = /* @__PURE__ */ new Map();
|
|
493
|
+
}
|
|
494
|
+
this.rpcTargets.set(stream, hook);
|
|
495
|
+
return hook.dup();
|
|
496
|
+
} else {
|
|
497
|
+
return hook;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
throw new Error("owned payload shouldn't contain raw WritableStreams");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// Get the StubHook representing the given ReadableStream found inside this payload.
|
|
505
|
+
getHookForReadableStream(stream, parent, dupStubs = true) {
|
|
506
|
+
if (this.source === "params") {
|
|
507
|
+
return streamImpl.createReadableStreamHook(stream);
|
|
508
|
+
} else if (this.source === "return") {
|
|
509
|
+
let hook = this.rpcTargets?.get(stream);
|
|
510
|
+
if (hook) {
|
|
511
|
+
if (dupStubs) {
|
|
512
|
+
return hook.dup();
|
|
513
|
+
} else {
|
|
514
|
+
this.rpcTargets?.delete(stream);
|
|
515
|
+
return hook;
|
|
516
|
+
}
|
|
517
|
+
} else {
|
|
518
|
+
hook = streamImpl.createReadableStreamHook(stream);
|
|
519
|
+
if (dupStubs) {
|
|
520
|
+
if (!this.rpcTargets) {
|
|
521
|
+
this.rpcTargets = /* @__PURE__ */ new Map();
|
|
522
|
+
}
|
|
523
|
+
this.rpcTargets.set(stream, hook);
|
|
524
|
+
return hook.dup();
|
|
525
|
+
} else {
|
|
526
|
+
return hook;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
throw new Error("owned payload shouldn't contain raw ReadableStreams");
|
|
531
|
+
}
|
|
532
|
+
}
|
|
438
533
|
deepCopy(value, oldParent, property, parent, dupStubs, owner) {
|
|
439
534
|
let kind = typeForRpc(value);
|
|
440
535
|
switch (kind) {
|
|
@@ -478,22 +573,21 @@ var RpcPayload = class _RpcPayload {
|
|
|
478
573
|
this.promises.push({ parent, property, promise });
|
|
479
574
|
return promise;
|
|
480
575
|
} else {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return newStub;
|
|
576
|
+
this.hooks.push(hook);
|
|
577
|
+
return new RpcStub(hook);
|
|
484
578
|
}
|
|
485
579
|
}
|
|
486
580
|
case "function":
|
|
487
581
|
case "rpc-target": {
|
|
488
582
|
let target = value;
|
|
489
|
-
let
|
|
583
|
+
let hook;
|
|
490
584
|
if (owner) {
|
|
491
|
-
|
|
585
|
+
hook = owner.getHookForRpcTarget(target, oldParent, dupStubs);
|
|
492
586
|
} else {
|
|
493
|
-
|
|
587
|
+
hook = TargetStubHook.create(target, oldParent);
|
|
494
588
|
}
|
|
495
|
-
this.
|
|
496
|
-
return
|
|
589
|
+
this.hooks.push(hook);
|
|
590
|
+
return new RpcStub(hook);
|
|
497
591
|
}
|
|
498
592
|
case "rpc-thenable": {
|
|
499
593
|
let target = value;
|
|
@@ -506,6 +600,44 @@ var RpcPayload = class _RpcPayload {
|
|
|
506
600
|
this.promises.push({ parent, property, promise });
|
|
507
601
|
return promise;
|
|
508
602
|
}
|
|
603
|
+
case "writable": {
|
|
604
|
+
let stream = value;
|
|
605
|
+
let hook;
|
|
606
|
+
if (owner) {
|
|
607
|
+
hook = owner.getHookForWritableStream(stream, oldParent, dupStubs);
|
|
608
|
+
} else {
|
|
609
|
+
hook = streamImpl.createWritableStreamHook(stream);
|
|
610
|
+
}
|
|
611
|
+
this.hooks.push(hook);
|
|
612
|
+
return stream;
|
|
613
|
+
}
|
|
614
|
+
case "readable": {
|
|
615
|
+
let stream = value;
|
|
616
|
+
let hook;
|
|
617
|
+
if (owner) {
|
|
618
|
+
hook = owner.getHookForReadableStream(stream, oldParent, dupStubs);
|
|
619
|
+
} else {
|
|
620
|
+
hook = streamImpl.createReadableStreamHook(stream);
|
|
621
|
+
}
|
|
622
|
+
this.hooks.push(hook);
|
|
623
|
+
return stream;
|
|
624
|
+
}
|
|
625
|
+
case "headers":
|
|
626
|
+
return new Headers(value);
|
|
627
|
+
case "request": {
|
|
628
|
+
let req = value;
|
|
629
|
+
if (req.body) {
|
|
630
|
+
this.deepCopy(req.body, req, "body", req, dupStubs, owner);
|
|
631
|
+
}
|
|
632
|
+
return new Request(req);
|
|
633
|
+
}
|
|
634
|
+
case "response": {
|
|
635
|
+
let resp = value;
|
|
636
|
+
if (resp.body) {
|
|
637
|
+
this.deepCopy(resp.body, resp, "body", resp, dupStubs, owner);
|
|
638
|
+
}
|
|
639
|
+
return new Response(resp.body, resp);
|
|
640
|
+
}
|
|
509
641
|
default:
|
|
510
642
|
throw new Error("unreachable");
|
|
511
643
|
}
|
|
@@ -515,12 +647,12 @@ var RpcPayload = class _RpcPayload {
|
|
|
515
647
|
ensureDeepCopied() {
|
|
516
648
|
if (this.source !== "owned") {
|
|
517
649
|
let dupStubs = this.source === "params";
|
|
518
|
-
this.
|
|
650
|
+
this.hooks = [];
|
|
519
651
|
this.promises = [];
|
|
520
652
|
try {
|
|
521
653
|
this.value = this.deepCopy(this.value, void 0, "value", this, dupStubs, this);
|
|
522
654
|
} catch (err) {
|
|
523
|
-
this.
|
|
655
|
+
this.hooks = void 0;
|
|
524
656
|
this.promises = void 0;
|
|
525
657
|
throw err;
|
|
526
658
|
}
|
|
@@ -623,7 +755,7 @@ var RpcPayload = class _RpcPayload {
|
|
|
623
755
|
}
|
|
624
756
|
dispose() {
|
|
625
757
|
if (this.source === "owned") {
|
|
626
|
-
this.
|
|
758
|
+
this.hooks.forEach((hook) => hook.dispose());
|
|
627
759
|
this.promises.forEach((promise) => promise.promise[Symbol.dispose]());
|
|
628
760
|
} else if (this.source === "return") {
|
|
629
761
|
this.disposeImpl(this.value, void 0);
|
|
@@ -632,7 +764,7 @@ var RpcPayload = class _RpcPayload {
|
|
|
632
764
|
}
|
|
633
765
|
} else ;
|
|
634
766
|
this.source = "owned";
|
|
635
|
-
this.
|
|
767
|
+
this.hooks = [];
|
|
636
768
|
this.promises = [];
|
|
637
769
|
}
|
|
638
770
|
// Recursive dispose, called only when `source` is "return".
|
|
@@ -685,6 +817,40 @@ var RpcPayload = class _RpcPayload {
|
|
|
685
817
|
}
|
|
686
818
|
case "rpc-thenable":
|
|
687
819
|
return;
|
|
820
|
+
case "headers":
|
|
821
|
+
return;
|
|
822
|
+
case "request": {
|
|
823
|
+
let req = value;
|
|
824
|
+
if (req.body) this.disposeImpl(req.body, req);
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
case "response": {
|
|
828
|
+
let resp = value;
|
|
829
|
+
if (resp.body) this.disposeImpl(resp.body, resp);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
case "writable": {
|
|
833
|
+
let stream = value;
|
|
834
|
+
let hook = this.rpcTargets?.get(stream);
|
|
835
|
+
if (hook) {
|
|
836
|
+
this.rpcTargets.delete(stream);
|
|
837
|
+
} else {
|
|
838
|
+
hook = streamImpl.createWritableStreamHook(stream);
|
|
839
|
+
}
|
|
840
|
+
hook.dispose();
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
case "readable": {
|
|
844
|
+
let stream = value;
|
|
845
|
+
let hook = this.rpcTargets?.get(stream);
|
|
846
|
+
if (hook) {
|
|
847
|
+
this.rpcTargets.delete(stream);
|
|
848
|
+
} else {
|
|
849
|
+
hook = streamImpl.createReadableStreamHook(stream);
|
|
850
|
+
}
|
|
851
|
+
hook.dispose();
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
688
854
|
default:
|
|
689
855
|
return;
|
|
690
856
|
}
|
|
@@ -693,9 +859,9 @@ var RpcPayload = class _RpcPayload {
|
|
|
693
859
|
// *would* be awaited if this payload were to be delivered. See the similarly-named method of
|
|
694
860
|
// StubHook for explanation.
|
|
695
861
|
ignoreUnhandledRejections() {
|
|
696
|
-
if (this.
|
|
697
|
-
this.
|
|
698
|
-
|
|
862
|
+
if (this.hooks) {
|
|
863
|
+
this.hooks.forEach((hook) => {
|
|
864
|
+
hook.ignoreUnhandledRejections();
|
|
699
865
|
});
|
|
700
866
|
this.promises.forEach(
|
|
701
867
|
(promise) => unwrapStubOrParent(promise.promise).ignoreUnhandledRejections()
|
|
@@ -716,6 +882,11 @@ var RpcPayload = class _RpcPayload {
|
|
|
716
882
|
case "undefined":
|
|
717
883
|
case "function":
|
|
718
884
|
case "rpc-target":
|
|
885
|
+
case "writable":
|
|
886
|
+
case "readable":
|
|
887
|
+
case "headers":
|
|
888
|
+
case "request":
|
|
889
|
+
case "response":
|
|
719
890
|
return;
|
|
720
891
|
case "array": {
|
|
721
892
|
let array = value;
|
|
@@ -788,11 +959,20 @@ function followPath(value, parent, path, owner) {
|
|
|
788
959
|
let { hook, pathIfPromise } = unwrapStubAndPath(value);
|
|
789
960
|
return { hook, remainingPath: pathIfPromise ? pathIfPromise.concat(path.slice(i)) : path.slice(i) };
|
|
790
961
|
}
|
|
962
|
+
case "writable":
|
|
963
|
+
value = void 0;
|
|
964
|
+
break;
|
|
965
|
+
case "readable":
|
|
966
|
+
value = void 0;
|
|
967
|
+
break;
|
|
791
968
|
case "primitive":
|
|
792
969
|
case "bigint":
|
|
793
970
|
case "bytes":
|
|
794
971
|
case "date":
|
|
795
972
|
case "error":
|
|
973
|
+
case "headers":
|
|
974
|
+
case "request":
|
|
975
|
+
case "response":
|
|
796
976
|
value = void 0;
|
|
797
977
|
break;
|
|
798
978
|
case "undefined":
|
|
@@ -1027,6 +1207,14 @@ var PromiseStubHook = class _PromiseStubHook extends StubHook {
|
|
|
1027
1207
|
args.ensureDeepCopied();
|
|
1028
1208
|
return new _PromiseStubHook(this.promise.then((hook) => hook.call(path, args)));
|
|
1029
1209
|
}
|
|
1210
|
+
stream(path, args) {
|
|
1211
|
+
args.ensureDeepCopied();
|
|
1212
|
+
let promise = this.promise.then((hook) => {
|
|
1213
|
+
let result = hook.stream(path, args);
|
|
1214
|
+
return result.promise;
|
|
1215
|
+
});
|
|
1216
|
+
return { promise };
|
|
1217
|
+
}
|
|
1030
1218
|
map(path, captures, instructions) {
|
|
1031
1219
|
return new _PromiseStubHook(this.promise.then(
|
|
1032
1220
|
(hook) => hook.map(path, captures, instructions),
|
|
@@ -1099,6 +1287,9 @@ var NullExporter = class {
|
|
|
1099
1287
|
}
|
|
1100
1288
|
unexport(ids) {
|
|
1101
1289
|
}
|
|
1290
|
+
createPipe(readable) {
|
|
1291
|
+
throw new Error("Cannot create pipes without an RPC session.");
|
|
1292
|
+
}
|
|
1102
1293
|
onSendError(error) {
|
|
1103
1294
|
}
|
|
1104
1295
|
};
|
|
@@ -1203,6 +1394,73 @@ var Devaluator = class _Devaluator {
|
|
|
1203
1394
|
];
|
|
1204
1395
|
}
|
|
1205
1396
|
}
|
|
1397
|
+
case "headers":
|
|
1398
|
+
return ["headers", [...value]];
|
|
1399
|
+
case "request": {
|
|
1400
|
+
let req = value;
|
|
1401
|
+
let init = {};
|
|
1402
|
+
if (req.method !== "GET") init.method = req.method;
|
|
1403
|
+
let headers = [...req.headers];
|
|
1404
|
+
if (headers.length > 0) {
|
|
1405
|
+
init.headers = headers;
|
|
1406
|
+
}
|
|
1407
|
+
if (req.body) {
|
|
1408
|
+
init.body = this.devaluateImpl(req.body, req, depth + 1);
|
|
1409
|
+
init.duplex = req.duplex || "half";
|
|
1410
|
+
} else if (req.body === void 0 && !["GET", "HEAD", "OPTIONS", "TRACE", "DELETE"].includes(req.method)) {
|
|
1411
|
+
let bodyPromise = req.arrayBuffer();
|
|
1412
|
+
let readable = new ReadableStream({
|
|
1413
|
+
async start(controller) {
|
|
1414
|
+
try {
|
|
1415
|
+
controller.enqueue(new Uint8Array(await bodyPromise));
|
|
1416
|
+
controller.close();
|
|
1417
|
+
} catch (err) {
|
|
1418
|
+
controller.error(err);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
});
|
|
1422
|
+
let hook = streamImpl.createReadableStreamHook(readable);
|
|
1423
|
+
let importId = this.exporter.createPipe(readable, hook);
|
|
1424
|
+
init.body = ["readable", importId];
|
|
1425
|
+
init.duplex = req.duplex || "half";
|
|
1426
|
+
}
|
|
1427
|
+
if (req.cache && req.cache !== "default") init.cache = req.cache;
|
|
1428
|
+
if (req.redirect !== "follow") init.redirect = req.redirect;
|
|
1429
|
+
if (req.integrity) init.integrity = req.integrity;
|
|
1430
|
+
if (req.mode && req.mode !== "cors") init.mode = req.mode;
|
|
1431
|
+
if (req.credentials && req.credentials !== "same-origin") {
|
|
1432
|
+
init.credentials = req.credentials;
|
|
1433
|
+
}
|
|
1434
|
+
if (req.referrer && req.referrer !== "about:client") init.referrer = req.referrer;
|
|
1435
|
+
if (req.referrerPolicy) init.referrerPolicy = req.referrerPolicy;
|
|
1436
|
+
if (req.keepalive) init.keepalive = req.keepalive;
|
|
1437
|
+
let cfReq = req;
|
|
1438
|
+
if (cfReq.cf) init.cf = cfReq.cf;
|
|
1439
|
+
if (cfReq.encodeResponseBody && cfReq.encodeResponseBody !== "automatic") {
|
|
1440
|
+
init.encodeResponseBody = cfReq.encodeResponseBody;
|
|
1441
|
+
}
|
|
1442
|
+
return ["request", req.url, init];
|
|
1443
|
+
}
|
|
1444
|
+
case "response": {
|
|
1445
|
+
let resp = value;
|
|
1446
|
+
let body = this.devaluateImpl(resp.body, resp, depth + 1);
|
|
1447
|
+
let init = {};
|
|
1448
|
+
if (resp.status !== 200) init.status = resp.status;
|
|
1449
|
+
if (resp.statusText) init.statusText = resp.statusText;
|
|
1450
|
+
let headers = [...resp.headers];
|
|
1451
|
+
if (headers.length > 0) {
|
|
1452
|
+
init.headers = headers;
|
|
1453
|
+
}
|
|
1454
|
+
let cfResp = resp;
|
|
1455
|
+
if (cfResp.cf) init.cf = cfResp.cf;
|
|
1456
|
+
if (cfResp.encodeBody && cfResp.encodeBody !== "automatic") {
|
|
1457
|
+
init.encodeBody = cfResp.encodeBody;
|
|
1458
|
+
}
|
|
1459
|
+
if (cfResp.webSocket) {
|
|
1460
|
+
throw new TypeError("Can't serialize a Response containing a webSocket.");
|
|
1461
|
+
}
|
|
1462
|
+
return ["response", body, init];
|
|
1463
|
+
}
|
|
1206
1464
|
case "error": {
|
|
1207
1465
|
let e = value;
|
|
1208
1466
|
let rewritten = this.exporter.onSendError(e);
|
|
@@ -1257,6 +1515,22 @@ var Devaluator = class _Devaluator {
|
|
|
1257
1515
|
let hook = this.source.getHookForRpcTarget(value, parent);
|
|
1258
1516
|
return this.devaluateHook("promise", hook);
|
|
1259
1517
|
}
|
|
1518
|
+
case "writable": {
|
|
1519
|
+
if (!this.source) {
|
|
1520
|
+
throw new Error("Can't serialize WritableStream in this context.");
|
|
1521
|
+
}
|
|
1522
|
+
let hook = this.source.getHookForWritableStream(value, parent);
|
|
1523
|
+
return this.devaluateHook("writable", hook);
|
|
1524
|
+
}
|
|
1525
|
+
case "readable": {
|
|
1526
|
+
if (!this.source) {
|
|
1527
|
+
throw new Error("Can't serialize ReadableStream in this context.");
|
|
1528
|
+
}
|
|
1529
|
+
let ws = value;
|
|
1530
|
+
let hook = this.source.getHookForReadableStream(ws, parent);
|
|
1531
|
+
let importId = this.exporter.createPipe(ws, hook);
|
|
1532
|
+
return ["readable", importId];
|
|
1533
|
+
}
|
|
1260
1534
|
default:
|
|
1261
1535
|
throw new Error("unreachable");
|
|
1262
1536
|
}
|
|
@@ -1281,16 +1555,27 @@ var NullImporter = class {
|
|
|
1281
1555
|
getExport(idx) {
|
|
1282
1556
|
return void 0;
|
|
1283
1557
|
}
|
|
1558
|
+
getPipeReadable(exportId) {
|
|
1559
|
+
throw new Error("Cannot retrieve pipe readable without an RPC session.");
|
|
1560
|
+
}
|
|
1284
1561
|
};
|
|
1285
1562
|
var NULL_IMPORTER = new NullImporter();
|
|
1563
|
+
function fixBrokenRequestBody(request, body) {
|
|
1564
|
+
let promise = new Response(body).arrayBuffer().then((arrayBuffer) => {
|
|
1565
|
+
let bytes = new Uint8Array(arrayBuffer);
|
|
1566
|
+
let result = new Request(request, { body: bytes });
|
|
1567
|
+
return new PayloadStubHook(RpcPayload.fromAppReturn(result));
|
|
1568
|
+
});
|
|
1569
|
+
return new RpcPromise(new PromiseStubHook(promise), []);
|
|
1570
|
+
}
|
|
1286
1571
|
var Evaluator = class _Evaluator {
|
|
1287
1572
|
constructor(importer) {
|
|
1288
1573
|
this.importer = importer;
|
|
1289
1574
|
}
|
|
1290
|
-
|
|
1575
|
+
hooks = [];
|
|
1291
1576
|
promises = [];
|
|
1292
1577
|
evaluate(value) {
|
|
1293
|
-
let payload = RpcPayload.forEvaluate(this.
|
|
1578
|
+
let payload = RpcPayload.forEvaluate(this.hooks, this.promises);
|
|
1294
1579
|
try {
|
|
1295
1580
|
payload.value = this.evaluateImpl(value, payload, "value");
|
|
1296
1581
|
return payload;
|
|
@@ -1360,6 +1645,56 @@ var Evaluator = class _Evaluator {
|
|
|
1360
1645
|
return -Infinity;
|
|
1361
1646
|
case "nan":
|
|
1362
1647
|
return NaN;
|
|
1648
|
+
case "headers":
|
|
1649
|
+
if (value.length === 2 && value[1] instanceof Array) {
|
|
1650
|
+
return new Headers(value[1]);
|
|
1651
|
+
}
|
|
1652
|
+
break;
|
|
1653
|
+
case "request": {
|
|
1654
|
+
if (value.length !== 3 || typeof value[1] !== "string") break;
|
|
1655
|
+
let url = value[1];
|
|
1656
|
+
let init = value[2];
|
|
1657
|
+
if (typeof init !== "object" || init === null) break;
|
|
1658
|
+
if (init.body) {
|
|
1659
|
+
init.body = this.evaluateImpl(init.body, init, "body");
|
|
1660
|
+
if (init.body === null || typeof init.body === "string" || init.body instanceof Uint8Array || init.body instanceof ReadableStream) ; else {
|
|
1661
|
+
throw new TypeError("Request body must be of type ReadableStream.");
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (init.signal) {
|
|
1665
|
+
init.signal = this.evaluateImpl(init.signal, init, "signal");
|
|
1666
|
+
if (!(init.signal instanceof AbortSignal)) {
|
|
1667
|
+
throw new TypeError("Request siganl must be of type AbortSignal.");
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
if (init.headers && !(init.headers instanceof Array)) {
|
|
1671
|
+
throw new TypeError("Request headers must be serialized as an array of pairs.");
|
|
1672
|
+
}
|
|
1673
|
+
let result = new Request(url, init);
|
|
1674
|
+
if (init.body instanceof ReadableStream && result.body === void 0) {
|
|
1675
|
+
let promise = fixBrokenRequestBody(result, init.body);
|
|
1676
|
+
this.promises.push({ promise, parent, property });
|
|
1677
|
+
return promise;
|
|
1678
|
+
} else {
|
|
1679
|
+
return result;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
case "response": {
|
|
1683
|
+
if (value.length !== 3) break;
|
|
1684
|
+
let body = this.evaluateImpl(value[1], parent, property);
|
|
1685
|
+
if (body === null || typeof body === "string" || body instanceof Uint8Array || body instanceof ReadableStream) ; else {
|
|
1686
|
+
throw new TypeError("Response body must be of type ReadableStream.");
|
|
1687
|
+
}
|
|
1688
|
+
let init = value[2];
|
|
1689
|
+
if (typeof init !== "object" || init === null) break;
|
|
1690
|
+
if (init.webSocket) {
|
|
1691
|
+
throw new TypeError("Can't deserialize a Response containing a webSocket.");
|
|
1692
|
+
}
|
|
1693
|
+
if (init.headers && !(init.headers instanceof Array)) {
|
|
1694
|
+
throw new TypeError("Request headers must be serialized as an array of pairs.");
|
|
1695
|
+
}
|
|
1696
|
+
return new Response(body, init);
|
|
1697
|
+
}
|
|
1363
1698
|
case "import":
|
|
1364
1699
|
case "pipeline": {
|
|
1365
1700
|
if (value.length < 2 || value.length > 4) {
|
|
@@ -1379,9 +1714,8 @@ var Evaluator = class _Evaluator {
|
|
|
1379
1714
|
this.promises.push({ promise, parent, property });
|
|
1380
1715
|
return promise;
|
|
1381
1716
|
} else {
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
return stub;
|
|
1717
|
+
this.hooks.push(hook2);
|
|
1718
|
+
return new RpcPromise(hook2, []);
|
|
1385
1719
|
}
|
|
1386
1720
|
};
|
|
1387
1721
|
if (value.length == 2) {
|
|
@@ -1459,12 +1793,27 @@ var Evaluator = class _Evaluator {
|
|
|
1459
1793
|
return promise;
|
|
1460
1794
|
} else {
|
|
1461
1795
|
let hook = this.importer.importStub(value[1]);
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
return stub;
|
|
1796
|
+
this.hooks.push(hook);
|
|
1797
|
+
return new RpcStub(hook);
|
|
1465
1798
|
}
|
|
1466
1799
|
}
|
|
1467
1800
|
break;
|
|
1801
|
+
case "writable":
|
|
1802
|
+
if (typeof value[1] == "number") {
|
|
1803
|
+
let hook = this.importer.importStub(value[1]);
|
|
1804
|
+
let stream = streamImpl.createWritableStreamFromHook(hook);
|
|
1805
|
+
this.hooks.push(hook);
|
|
1806
|
+
return stream;
|
|
1807
|
+
}
|
|
1808
|
+
break;
|
|
1809
|
+
case "readable":
|
|
1810
|
+
if (typeof value[1] == "number") {
|
|
1811
|
+
let stream = this.importer.getPipeReadable(value[1]);
|
|
1812
|
+
let hook = streamImpl.createReadableStreamHook(stream);
|
|
1813
|
+
this.hooks.push(hook);
|
|
1814
|
+
return stream;
|
|
1815
|
+
}
|
|
1816
|
+
break;
|
|
1468
1817
|
}
|
|
1469
1818
|
throw new TypeError(`unknown special value: ${JSON.stringify(value)}`);
|
|
1470
1819
|
} else if (value instanceof Object) {
|
|
@@ -1604,6 +1953,14 @@ var RpcImportHook = class _RpcImportHook extends StubHook {
|
|
|
1604
1953
|
return entry.session.sendCall(entry.importId, path, args);
|
|
1605
1954
|
}
|
|
1606
1955
|
}
|
|
1956
|
+
stream(path, args) {
|
|
1957
|
+
let entry = this.getEntry();
|
|
1958
|
+
if (entry.resolution) {
|
|
1959
|
+
return entry.resolution.stream(path, args);
|
|
1960
|
+
} else {
|
|
1961
|
+
return entry.session.sendStream(entry.importId, path, args);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1607
1964
|
map(path, captures, instructions) {
|
|
1608
1965
|
let entry;
|
|
1609
1966
|
try {
|
|
@@ -1776,19 +2133,23 @@ var RpcSessionImpl = class {
|
|
|
1776
2133
|
return payload;
|
|
1777
2134
|
}
|
|
1778
2135
|
};
|
|
2136
|
+
let autoRelease = exp.autoRelease;
|
|
1779
2137
|
++this.pullCount;
|
|
1780
2138
|
exp.pull = resolve().then(
|
|
1781
2139
|
(payload) => {
|
|
1782
2140
|
let value = Devaluator.devaluate(payload.value, void 0, this, payload);
|
|
1783
2141
|
this.send(["resolve", exportId, value]);
|
|
2142
|
+
if (autoRelease) this.releaseExport(exportId, 1);
|
|
1784
2143
|
},
|
|
1785
2144
|
(error) => {
|
|
1786
2145
|
this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
|
|
2146
|
+
if (autoRelease) this.releaseExport(exportId, 1);
|
|
1787
2147
|
}
|
|
1788
2148
|
).catch(
|
|
1789
2149
|
(error) => {
|
|
1790
2150
|
try {
|
|
1791
2151
|
this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
|
|
2152
|
+
if (autoRelease) this.releaseExport(exportId, 1);
|
|
1792
2153
|
} catch (error2) {
|
|
1793
2154
|
this.abort(error2);
|
|
1794
2155
|
}
|
|
@@ -1840,9 +2201,35 @@ var RpcSessionImpl = class {
|
|
|
1840
2201
|
getExport(idx) {
|
|
1841
2202
|
return this.exports[idx]?.hook;
|
|
1842
2203
|
}
|
|
2204
|
+
getPipeReadable(exportId) {
|
|
2205
|
+
let entry = this.exports[exportId];
|
|
2206
|
+
if (!entry || !entry.pipeReadable) {
|
|
2207
|
+
throw new Error(`Export ${exportId} is not a pipe or its readable end was already consumed.`);
|
|
2208
|
+
}
|
|
2209
|
+
let readable = entry.pipeReadable;
|
|
2210
|
+
entry.pipeReadable = void 0;
|
|
2211
|
+
return readable;
|
|
2212
|
+
}
|
|
2213
|
+
createPipe(readable, readableHook) {
|
|
2214
|
+
if (this.abortReason) throw this.abortReason;
|
|
2215
|
+
this.send(["pipe"]);
|
|
2216
|
+
let importId = this.imports.length;
|
|
2217
|
+
let entry = new ImportTableEntry(this, importId, false);
|
|
2218
|
+
this.imports.push(entry);
|
|
2219
|
+
let hook = new RpcImportHook(
|
|
2220
|
+
/*isPromise=*/
|
|
2221
|
+
false,
|
|
2222
|
+
entry
|
|
2223
|
+
);
|
|
2224
|
+
let writable = streamImpl.createWritableStreamFromHook(hook);
|
|
2225
|
+
readable.pipeTo(writable).catch(() => {
|
|
2226
|
+
}).finally(() => readableHook.dispose());
|
|
2227
|
+
return importId;
|
|
2228
|
+
}
|
|
2229
|
+
// Serializes and sends a message. Returns the byte length of the serialized message.
|
|
1843
2230
|
send(msg) {
|
|
1844
2231
|
if (this.abortReason !== void 0) {
|
|
1845
|
-
return;
|
|
2232
|
+
return 0;
|
|
1846
2233
|
}
|
|
1847
2234
|
let msgText;
|
|
1848
2235
|
try {
|
|
@@ -1855,6 +2242,7 @@ var RpcSessionImpl = class {
|
|
|
1855
2242
|
throw err;
|
|
1856
2243
|
}
|
|
1857
2244
|
this.transport.send(msgText).catch((err) => this.abort(err, false));
|
|
2245
|
+
return msgText.length;
|
|
1858
2246
|
}
|
|
1859
2247
|
sendCall(id, path, args) {
|
|
1860
2248
|
if (this.abortReason) throw this.abortReason;
|
|
@@ -1872,6 +2260,34 @@ var RpcSessionImpl = class {
|
|
|
1872
2260
|
entry
|
|
1873
2261
|
);
|
|
1874
2262
|
}
|
|
2263
|
+
sendStream(id, path, args) {
|
|
2264
|
+
if (this.abortReason) throw this.abortReason;
|
|
2265
|
+
let value = ["pipeline", id, path];
|
|
2266
|
+
let devalue = Devaluator.devaluate(args.value, void 0, this, args);
|
|
2267
|
+
value.push(devalue[0]);
|
|
2268
|
+
let size = this.send(["stream", value]);
|
|
2269
|
+
let importId = this.imports.length;
|
|
2270
|
+
let entry = new ImportTableEntry(
|
|
2271
|
+
this,
|
|
2272
|
+
importId,
|
|
2273
|
+
/*pulling=*/
|
|
2274
|
+
true
|
|
2275
|
+
);
|
|
2276
|
+
entry.remoteRefcount = 0;
|
|
2277
|
+
entry.localRefcount = 1;
|
|
2278
|
+
this.imports.push(entry);
|
|
2279
|
+
let promise = entry.awaitResolution().then(
|
|
2280
|
+
(p) => {
|
|
2281
|
+
p.dispose();
|
|
2282
|
+
delete this.imports[importId];
|
|
2283
|
+
},
|
|
2284
|
+
(err) => {
|
|
2285
|
+
delete this.imports[importId];
|
|
2286
|
+
throw err;
|
|
2287
|
+
}
|
|
2288
|
+
);
|
|
2289
|
+
return { promise, size };
|
|
2290
|
+
}
|
|
1875
2291
|
sendMap(id, path, captures, instructions) {
|
|
1876
2292
|
if (this.abortReason) {
|
|
1877
2293
|
for (let cap of captures) {
|
|
@@ -1959,6 +2375,24 @@ var RpcSessionImpl = class {
|
|
|
1959
2375
|
continue;
|
|
1960
2376
|
}
|
|
1961
2377
|
break;
|
|
2378
|
+
case "stream": {
|
|
2379
|
+
if (msg.length > 1) {
|
|
2380
|
+
let payload = new Evaluator(this).evaluate(msg[1]);
|
|
2381
|
+
let hook = new PayloadStubHook(payload);
|
|
2382
|
+
hook.ignoreUnhandledRejections();
|
|
2383
|
+
let exportId = this.exports.length;
|
|
2384
|
+
this.exports.push({ hook, refcount: 1, autoRelease: true });
|
|
2385
|
+
this.ensureResolvingExport(exportId);
|
|
2386
|
+
continue;
|
|
2387
|
+
}
|
|
2388
|
+
break;
|
|
2389
|
+
}
|
|
2390
|
+
case "pipe": {
|
|
2391
|
+
let { readable, writable } = new TransformStream();
|
|
2392
|
+
let hook = streamImpl.createWritableStreamHook(writable);
|
|
2393
|
+
this.exports.push({ hook, refcount: 1, pipeReadable: readable });
|
|
2394
|
+
continue;
|
|
2395
|
+
}
|
|
1962
2396
|
case "pull": {
|
|
1963
2397
|
let exportId = msg[1];
|
|
1964
2398
|
if (typeof exportId == "number") {
|
|
@@ -2454,6 +2888,9 @@ var MapBuilder = class {
|
|
|
2454
2888
|
}
|
|
2455
2889
|
unexport(ids) {
|
|
2456
2890
|
}
|
|
2891
|
+
createPipe(readable) {
|
|
2892
|
+
throw new Error("Cannot send ReadableStream inside a mapper function.");
|
|
2893
|
+
}
|
|
2457
2894
|
onSendError(error) {
|
|
2458
2895
|
}
|
|
2459
2896
|
};
|
|
@@ -2563,6 +3000,9 @@ var MapApplicator = class {
|
|
|
2563
3000
|
return this.variables[idx];
|
|
2564
3001
|
}
|
|
2565
3002
|
}
|
|
3003
|
+
getPipeReadable(exportId) {
|
|
3004
|
+
throw new Error("A mapper function cannot use pipe readables.");
|
|
3005
|
+
}
|
|
2566
3006
|
};
|
|
2567
3007
|
function applyMapToElement(input, parent, owner, captures, instructions) {
|
|
2568
3008
|
let inputHook = new PayloadStubHook(RpcPayload.deepCopyFrom(input, parent, owner));
|
|
@@ -2603,6 +3043,333 @@ mapImpl.applyMap = (input, parent, owner, captures, instructions) => {
|
|
|
2603
3043
|
}
|
|
2604
3044
|
}
|
|
2605
3045
|
};
|
|
3046
|
+
|
|
3047
|
+
// src/streams.ts
|
|
3048
|
+
var WritableStreamStubHook = class _WritableStreamStubHook extends StubHook {
|
|
3049
|
+
state;
|
|
3050
|
+
// undefined when disposed
|
|
3051
|
+
// Creates a new WritableStreamStubHook that is not duplicated from an existing hook.
|
|
3052
|
+
static create(stream) {
|
|
3053
|
+
let writer = stream.getWriter();
|
|
3054
|
+
return new _WritableStreamStubHook({ refcount: 1, writer, closed: false });
|
|
3055
|
+
}
|
|
3056
|
+
constructor(state, dupFrom) {
|
|
3057
|
+
super();
|
|
3058
|
+
this.state = state;
|
|
3059
|
+
if (dupFrom) {
|
|
3060
|
+
++state.refcount;
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
getState() {
|
|
3064
|
+
if (this.state) {
|
|
3065
|
+
return this.state;
|
|
3066
|
+
} else {
|
|
3067
|
+
throw new Error("Attempted to use a WritableStreamStubHook after it was disposed.");
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
call(path, args) {
|
|
3071
|
+
try {
|
|
3072
|
+
let state = this.getState();
|
|
3073
|
+
if (path.length !== 1 || typeof path[0] !== "string") {
|
|
3074
|
+
throw new Error("WritableStream stub only supports direct method calls");
|
|
3075
|
+
}
|
|
3076
|
+
const method = path[0];
|
|
3077
|
+
if (method !== "write" && method !== "close" && method !== "abort") {
|
|
3078
|
+
args.dispose();
|
|
3079
|
+
throw new Error(`Unknown WritableStream method: ${method}`);
|
|
3080
|
+
}
|
|
3081
|
+
if (method === "close" || method === "abort") {
|
|
3082
|
+
state.closed = true;
|
|
3083
|
+
}
|
|
3084
|
+
let func = state.writer[method];
|
|
3085
|
+
let promise = args.deliverCall(func, state.writer);
|
|
3086
|
+
return new PromiseStubHook(promise.then((payload) => new PayloadStubHook(payload)));
|
|
3087
|
+
} catch (err) {
|
|
3088
|
+
return new ErrorStubHook(err);
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
map(path, captures, instructions) {
|
|
3092
|
+
for (let cap of captures) {
|
|
3093
|
+
cap.dispose();
|
|
3094
|
+
}
|
|
3095
|
+
return new ErrorStubHook(new Error("Cannot use map() on a WritableStream"));
|
|
3096
|
+
}
|
|
3097
|
+
get(path) {
|
|
3098
|
+
return new ErrorStubHook(new Error("Cannot access properties on a WritableStream stub"));
|
|
3099
|
+
}
|
|
3100
|
+
dup() {
|
|
3101
|
+
let state = this.getState();
|
|
3102
|
+
return new _WritableStreamStubHook(state, this);
|
|
3103
|
+
}
|
|
3104
|
+
pull() {
|
|
3105
|
+
return Promise.reject(new Error("Cannot pull a WritableStream stub"));
|
|
3106
|
+
}
|
|
3107
|
+
ignoreUnhandledRejections() {
|
|
3108
|
+
}
|
|
3109
|
+
dispose() {
|
|
3110
|
+
let state = this.state;
|
|
3111
|
+
this.state = void 0;
|
|
3112
|
+
if (state) {
|
|
3113
|
+
if (--state.refcount === 0) {
|
|
3114
|
+
if (!state.closed) {
|
|
3115
|
+
state.writer.abort(new Error("WritableStream RPC stub was disposed without calling close()")).catch(() => {
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
3118
|
+
state.writer.releaseLock();
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
onBroken(callback) {
|
|
3123
|
+
}
|
|
3124
|
+
};
|
|
3125
|
+
var INITIAL_WINDOW = 256 * 1024;
|
|
3126
|
+
var MAX_WINDOW = 1024 * 1024 * 1024;
|
|
3127
|
+
var MIN_WINDOW = 64 * 1024;
|
|
3128
|
+
var STARTUP_GROWTH_FACTOR = 2;
|
|
3129
|
+
var STEADY_GROWTH_FACTOR = 1.25;
|
|
3130
|
+
var DECAY_FACTOR = 0.9;
|
|
3131
|
+
var STARTUP_EXIT_ROUNDS = 3;
|
|
3132
|
+
var FlowController = class {
|
|
3133
|
+
constructor(now) {
|
|
3134
|
+
this.now = now;
|
|
3135
|
+
}
|
|
3136
|
+
// The current window size in bytes. The sender blocks when bytesInFlight >= window.
|
|
3137
|
+
window = INITIAL_WINDOW;
|
|
3138
|
+
// Total bytes currently in flight (sent but not yet acked).
|
|
3139
|
+
bytesInFlight = 0;
|
|
3140
|
+
// Whether we're still in the startup phase.
|
|
3141
|
+
inStartupPhase = true;
|
|
3142
|
+
// ----- BDP estimation state (private) -----
|
|
3143
|
+
// Total bytes acked so far.
|
|
3144
|
+
delivered = 0;
|
|
3145
|
+
// Time of most recent ack.
|
|
3146
|
+
deliveredTime = 0;
|
|
3147
|
+
// Time when the very first ack was received.
|
|
3148
|
+
firstAckTime = 0;
|
|
3149
|
+
firstAckDelivered = 0;
|
|
3150
|
+
// Global minimum RTT observed (milliseconds).
|
|
3151
|
+
minRtt = Infinity;
|
|
3152
|
+
// For startup exit: count of consecutive RTT rounds where the window didn't meaningfully grow.
|
|
3153
|
+
roundsWithoutIncrease = 0;
|
|
3154
|
+
// Window size at the start of the current round, for startup exit detection.
|
|
3155
|
+
lastRoundWindow = 0;
|
|
3156
|
+
// Time when the current round started.
|
|
3157
|
+
roundStartTime = 0;
|
|
3158
|
+
// Called when a write of `size` bytes is about to be sent. Returns a token that must be
|
|
3159
|
+
// passed to onAck() when the ack arrives, and whether the sender should block (window full).
|
|
3160
|
+
onSend(size) {
|
|
3161
|
+
this.bytesInFlight += size;
|
|
3162
|
+
let token = {
|
|
3163
|
+
sentTime: this.now(),
|
|
3164
|
+
size,
|
|
3165
|
+
deliveredAtSend: this.delivered,
|
|
3166
|
+
deliveredTimeAtSend: this.deliveredTime,
|
|
3167
|
+
windowAtSend: this.window,
|
|
3168
|
+
windowFullAtSend: this.bytesInFlight >= this.window
|
|
3169
|
+
};
|
|
3170
|
+
return { token, shouldBlock: token.windowFullAtSend };
|
|
3171
|
+
}
|
|
3172
|
+
// Called when a previously-sent write fails. Restores bytesInFlight without updating
|
|
3173
|
+
// any BDP estimates.
|
|
3174
|
+
onError(token) {
|
|
3175
|
+
this.bytesInFlight -= token.size;
|
|
3176
|
+
}
|
|
3177
|
+
// Called when an ack is received for a previously-sent write. Updates BDP estimates and
|
|
3178
|
+
// the window. Returns whether a blocked sender should now unblock.
|
|
3179
|
+
onAck(token) {
|
|
3180
|
+
let ackTime = this.now();
|
|
3181
|
+
this.delivered += token.size;
|
|
3182
|
+
this.deliveredTime = ackTime;
|
|
3183
|
+
this.bytesInFlight -= token.size;
|
|
3184
|
+
let rtt = ackTime - token.sentTime;
|
|
3185
|
+
this.minRtt = Math.min(this.minRtt, rtt);
|
|
3186
|
+
if (this.firstAckTime === 0) {
|
|
3187
|
+
this.firstAckTime = ackTime;
|
|
3188
|
+
this.firstAckDelivered = this.delivered;
|
|
3189
|
+
} else {
|
|
3190
|
+
let baseTime;
|
|
3191
|
+
let baseDelivered;
|
|
3192
|
+
if (token.deliveredTimeAtSend === 0) {
|
|
3193
|
+
baseTime = this.firstAckTime;
|
|
3194
|
+
baseDelivered = this.firstAckDelivered;
|
|
3195
|
+
} else {
|
|
3196
|
+
baseTime = token.deliveredTimeAtSend;
|
|
3197
|
+
baseDelivered = token.deliveredAtSend;
|
|
3198
|
+
}
|
|
3199
|
+
let interval = ackTime - baseTime;
|
|
3200
|
+
let bytes = this.delivered - baseDelivered;
|
|
3201
|
+
let bandwidth = bytes / interval;
|
|
3202
|
+
let growthFactor = this.inStartupPhase ? STARTUP_GROWTH_FACTOR : STEADY_GROWTH_FACTOR;
|
|
3203
|
+
let newWindow = bandwidth * this.minRtt * growthFactor;
|
|
3204
|
+
newWindow = Math.min(newWindow, token.windowAtSend * growthFactor);
|
|
3205
|
+
if (token.windowFullAtSend) {
|
|
3206
|
+
newWindow = Math.max(newWindow, token.windowAtSend * DECAY_FACTOR);
|
|
3207
|
+
} else {
|
|
3208
|
+
newWindow = Math.max(newWindow, this.window);
|
|
3209
|
+
}
|
|
3210
|
+
this.window = Math.max(Math.min(newWindow, MAX_WINDOW), MIN_WINDOW);
|
|
3211
|
+
if (this.inStartupPhase && token.sentTime >= this.roundStartTime) {
|
|
3212
|
+
if (this.window > this.lastRoundWindow * STEADY_GROWTH_FACTOR) {
|
|
3213
|
+
this.roundsWithoutIncrease = 0;
|
|
3214
|
+
} else {
|
|
3215
|
+
if (++this.roundsWithoutIncrease >= STARTUP_EXIT_ROUNDS) {
|
|
3216
|
+
this.inStartupPhase = false;
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
this.roundStartTime = ackTime;
|
|
3220
|
+
this.lastRoundWindow = this.window;
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
return this.bytesInFlight < this.window;
|
|
3224
|
+
}
|
|
3225
|
+
};
|
|
3226
|
+
function createWritableStreamFromHook(hook) {
|
|
3227
|
+
let pendingError = void 0;
|
|
3228
|
+
let hookDisposed = false;
|
|
3229
|
+
let fc = new FlowController(() => performance.now());
|
|
3230
|
+
let windowResolve;
|
|
3231
|
+
let windowReject;
|
|
3232
|
+
const disposeHook = () => {
|
|
3233
|
+
if (!hookDisposed) {
|
|
3234
|
+
hookDisposed = true;
|
|
3235
|
+
hook.dispose();
|
|
3236
|
+
}
|
|
3237
|
+
};
|
|
3238
|
+
return new WritableStream({
|
|
3239
|
+
write(chunk, controller) {
|
|
3240
|
+
if (pendingError !== void 0) {
|
|
3241
|
+
throw pendingError;
|
|
3242
|
+
}
|
|
3243
|
+
const payload = RpcPayload.fromAppParams([chunk]);
|
|
3244
|
+
const { promise, size } = hook.stream(["write"], payload);
|
|
3245
|
+
if (size === void 0) {
|
|
3246
|
+
return promise.catch((err) => {
|
|
3247
|
+
if (pendingError === void 0) {
|
|
3248
|
+
pendingError = err;
|
|
3249
|
+
}
|
|
3250
|
+
throw err;
|
|
3251
|
+
});
|
|
3252
|
+
} else {
|
|
3253
|
+
let { token, shouldBlock } = fc.onSend(size);
|
|
3254
|
+
promise.then(() => {
|
|
3255
|
+
let hasCapacity = fc.onAck(token);
|
|
3256
|
+
if (hasCapacity && windowResolve) {
|
|
3257
|
+
windowResolve();
|
|
3258
|
+
windowResolve = void 0;
|
|
3259
|
+
windowReject = void 0;
|
|
3260
|
+
}
|
|
3261
|
+
}, (err) => {
|
|
3262
|
+
fc.onError(token);
|
|
3263
|
+
if (pendingError === void 0) {
|
|
3264
|
+
pendingError = err;
|
|
3265
|
+
controller.error(err);
|
|
3266
|
+
disposeHook();
|
|
3267
|
+
}
|
|
3268
|
+
if (windowReject) {
|
|
3269
|
+
windowReject(err);
|
|
3270
|
+
windowResolve = void 0;
|
|
3271
|
+
windowReject = void 0;
|
|
3272
|
+
}
|
|
3273
|
+
});
|
|
3274
|
+
if (shouldBlock) {
|
|
3275
|
+
return new Promise((resolve, reject) => {
|
|
3276
|
+
windowResolve = resolve;
|
|
3277
|
+
windowReject = reject;
|
|
3278
|
+
});
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
},
|
|
3282
|
+
async close() {
|
|
3283
|
+
if (pendingError !== void 0) {
|
|
3284
|
+
disposeHook();
|
|
3285
|
+
throw pendingError;
|
|
3286
|
+
}
|
|
3287
|
+
const { promise } = hook.stream(["close"], RpcPayload.fromAppParams([]));
|
|
3288
|
+
try {
|
|
3289
|
+
await promise;
|
|
3290
|
+
} catch (err) {
|
|
3291
|
+
throw pendingError ?? err;
|
|
3292
|
+
} finally {
|
|
3293
|
+
disposeHook();
|
|
3294
|
+
}
|
|
3295
|
+
},
|
|
3296
|
+
abort(reason) {
|
|
3297
|
+
if (pendingError !== void 0) {
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
3300
|
+
pendingError = reason ?? new Error("WritableStream was aborted");
|
|
3301
|
+
if (windowReject) {
|
|
3302
|
+
windowReject(pendingError);
|
|
3303
|
+
windowResolve = void 0;
|
|
3304
|
+
windowReject = void 0;
|
|
3305
|
+
}
|
|
3306
|
+
const { promise } = hook.stream(["abort"], RpcPayload.fromAppParams([reason]));
|
|
3307
|
+
promise.then(() => disposeHook(), () => disposeHook());
|
|
3308
|
+
}
|
|
3309
|
+
});
|
|
3310
|
+
}
|
|
3311
|
+
var ReadableStreamStubHook = class _ReadableStreamStubHook extends StubHook {
|
|
3312
|
+
state;
|
|
3313
|
+
// undefined when disposed
|
|
3314
|
+
// Creates a new ReadableStreamStubHook.
|
|
3315
|
+
static create(stream) {
|
|
3316
|
+
return new _ReadableStreamStubHook({ refcount: 1, stream, canceled: false });
|
|
3317
|
+
}
|
|
3318
|
+
constructor(state, dupFrom) {
|
|
3319
|
+
super();
|
|
3320
|
+
this.state = state;
|
|
3321
|
+
if (dupFrom) {
|
|
3322
|
+
++state.refcount;
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
call(path, args) {
|
|
3326
|
+
args.dispose();
|
|
3327
|
+
return new ErrorStubHook(new Error("Cannot call methods on a ReadableStream stub"));
|
|
3328
|
+
}
|
|
3329
|
+
map(path, captures, instructions) {
|
|
3330
|
+
for (let cap of captures) {
|
|
3331
|
+
cap.dispose();
|
|
3332
|
+
}
|
|
3333
|
+
return new ErrorStubHook(new Error("Cannot use map() on a ReadableStream"));
|
|
3334
|
+
}
|
|
3335
|
+
get(path) {
|
|
3336
|
+
return new ErrorStubHook(new Error("Cannot access properties on a ReadableStream stub"));
|
|
3337
|
+
}
|
|
3338
|
+
dup() {
|
|
3339
|
+
let state = this.state;
|
|
3340
|
+
if (!state) {
|
|
3341
|
+
throw new Error("Attempted to dup a ReadableStreamStubHook after it was disposed.");
|
|
3342
|
+
}
|
|
3343
|
+
return new _ReadableStreamStubHook(state, this);
|
|
3344
|
+
}
|
|
3345
|
+
pull() {
|
|
3346
|
+
return Promise.reject(new Error("Cannot pull a ReadableStream stub"));
|
|
3347
|
+
}
|
|
3348
|
+
ignoreUnhandledRejections() {
|
|
3349
|
+
}
|
|
3350
|
+
dispose() {
|
|
3351
|
+
let state = this.state;
|
|
3352
|
+
this.state = void 0;
|
|
3353
|
+
if (state) {
|
|
3354
|
+
if (--state.refcount === 0) {
|
|
3355
|
+
if (!state.canceled) {
|
|
3356
|
+
state.canceled = true;
|
|
3357
|
+
if (!state.stream.locked) {
|
|
3358
|
+
state.stream.cancel(
|
|
3359
|
+
new Error("ReadableStream RPC stub was disposed without being consumed")
|
|
3360
|
+
).catch(() => {
|
|
3361
|
+
});
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
onBroken(callback) {
|
|
3368
|
+
}
|
|
3369
|
+
};
|
|
3370
|
+
streamImpl.createWritableStreamHook = WritableStreamStubHook.create;
|
|
3371
|
+
streamImpl.createWritableStreamFromHook = createWritableStreamFromHook;
|
|
3372
|
+
streamImpl.createReadableStreamHook = ReadableStreamStubHook.create;
|
|
2606
3373
|
var RpcStub2 = RpcStub;
|
|
2607
3374
|
var RpcPromise2 = RpcPromise;
|
|
2608
3375
|
var RpcSession2 = RpcSession;
|