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