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