capnweb 0.0.0-b2fcb34 → 0.0.0-c2bb17b

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/dist/index.js CHANGED
@@ -1,16 +1,29 @@
1
1
  // src/symbols.ts
2
- var WORKERS_MODULE_SYMBOL = Symbol("workers-module");
2
+ var WORKERS_MODULE_SYMBOL = /* @__PURE__ */ Symbol("workers-module");
3
3
 
4
4
  // src/core.ts
5
5
  if (!Symbol.dispose) {
6
- Symbol.dispose = Symbol.for("dispose");
6
+ Symbol.dispose = /* @__PURE__ */ Symbol.for("dispose");
7
7
  }
8
8
  if (!Symbol.asyncDispose) {
9
- Symbol.asyncDispose = Symbol.for("asyncDispose");
9
+ Symbol.asyncDispose = /* @__PURE__ */ Symbol.for("asyncDispose");
10
+ }
11
+ if (!Promise.withResolvers) {
12
+ Promise.withResolvers = function() {
13
+ let resolve;
14
+ let reject;
15
+ const promise = new Promise((res, rej) => {
16
+ resolve = res;
17
+ reject = rej;
18
+ });
19
+ return { promise, resolve, reject };
20
+ };
10
21
  }
11
22
  var workersModule = globalThis[WORKERS_MODULE_SYMBOL];
12
23
  var RpcTarget = workersModule ? workersModule.RpcTarget : class {
13
24
  };
25
+ var AsyncFunction = (async function() {
26
+ }).constructor;
14
27
  function typeForRpc(value) {
15
28
  switch (typeof value) {
16
29
  case "boolean":
@@ -35,6 +48,7 @@ function typeForRpc(value) {
35
48
  case Object.prototype:
36
49
  return "object";
37
50
  case Function.prototype:
51
+ case AsyncFunction.prototype:
38
52
  return "function";
39
53
  case Array.prototype:
40
54
  return "array";
@@ -42,6 +56,10 @@ function typeForRpc(value) {
42
56
  return "date";
43
57
  case Uint8Array.prototype:
44
58
  return "bytes";
59
+ case WritableStream.prototype:
60
+ return "writable";
61
+ case ReadableStream.prototype:
62
+ return "readable";
45
63
  // TODO: All other structured clone types.
46
64
  case RpcStub.prototype:
47
65
  return "stub";
@@ -69,7 +87,34 @@ function mapNotLoaded() {
69
87
  throw new Error("RPC map() implementation was not loaded.");
70
88
  }
71
89
  var mapImpl = { applyMap: mapNotLoaded, sendMap: mapNotLoaded };
90
+ function streamNotLoaded() {
91
+ throw new Error("Stream implementation was not loaded.");
92
+ }
93
+ var streamImpl = {
94
+ createWritableStreamHook: streamNotLoaded,
95
+ createWritableStreamFromHook: streamNotLoaded,
96
+ createReadableStreamHook: streamNotLoaded
97
+ };
72
98
  var StubHook = class {
99
+ // Like call(), but designed for streaming calls (e.g. WritableStream writes). Returns:
100
+ // - promise: A Promise<void> for the completion of the call.
101
+ // - size: If the call was remote, the byte size of the serialized message. For local calls,
102
+ // undefined is returned, indicating the caller should await the promise to serialize writes
103
+ // (no overlapping).
104
+ stream(path, args) {
105
+ let hook = this.call(path, args);
106
+ let pulled = hook.pull();
107
+ let promise;
108
+ if (pulled instanceof Promise) {
109
+ promise = pulled.then((p) => {
110
+ p.dispose();
111
+ });
112
+ } else {
113
+ pulled.dispose();
114
+ promise = Promise.resolve();
115
+ }
116
+ return { promise };
117
+ }
73
118
  };
74
119
  var ErrorStubHook = class extends StubHook {
75
120
  constructor(error) {
@@ -118,7 +163,7 @@ function withCallInterceptor(interceptor, callback) {
118
163
  doCall = oldValue;
119
164
  }
120
165
  }
121
- var RAW_STUB = Symbol("realStub");
166
+ var RAW_STUB = /* @__PURE__ */ Symbol("realStub");
122
167
  var PROXY_HANDLERS = {
123
168
  apply(target, thisArg, argumentsList) {
124
169
  let stub = target.raw;
@@ -233,6 +278,9 @@ var RpcStub = class _RpcStub extends RpcTarget {
233
278
  let { hook, pathIfPromise } = this[RAW_STUB];
234
279
  return mapImpl.sendMap(hook, pathIfPromise || [], func);
235
280
  }
281
+ toString() {
282
+ return "[object RpcStub]";
283
+ }
236
284
  };
237
285
  var RpcPromise = class extends RpcStub {
238
286
  // TODO: Support passing target value or promise to constructor.
@@ -248,6 +296,9 @@ var RpcPromise = class extends RpcStub {
248
296
  finally(onfinally) {
249
297
  return pullPromise(this).finally(...arguments);
250
298
  }
299
+ toString() {
300
+ return "[object RpcPromise]";
301
+ }
251
302
  };
252
303
  function unwrapStubTakingOwnership(stub) {
253
304
  let { hook, pathIfPromise } = stub[RAW_STUB];
@@ -288,10 +339,10 @@ async function pullPromise(promise) {
288
339
  }
289
340
  var RpcPayload = class _RpcPayload {
290
341
  // Private constructor; use factory functions above to construct.
291
- constructor(value, source, stubs, promises) {
342
+ constructor(value, source, hooks, promises) {
292
343
  this.value = value;
293
344
  this.source = source;
294
- this.stubs = stubs;
345
+ this.hooks = hooks;
295
346
  this.promises = promises;
296
347
  }
297
348
  // Create a payload from a value passed as params to an RPC from the app.
@@ -316,13 +367,13 @@ var RpcPayload = class _RpcPayload {
316
367
  // stubs is transferred from the inputs to the outputs, hence if the output is disposed, the
317
368
  // inputs should not be. (In case of exception, nothing is disposed, though.)
318
369
  static fromArray(array) {
319
- let stubs = [];
370
+ let hooks = [];
320
371
  let promises = [];
321
372
  let resultArray = [];
322
373
  for (let payload of array) {
323
374
  payload.ensureDeepCopied();
324
- for (let stub of payload.stubs) {
325
- stubs.push(stub);
375
+ for (let hook of payload.hooks) {
376
+ hooks.push(hook);
326
377
  }
327
378
  for (let promise of payload.promises) {
328
379
  if (promise.parent === payload) {
@@ -336,12 +387,12 @@ var RpcPayload = class _RpcPayload {
336
387
  }
337
388
  resultArray.push(payload.value);
338
389
  }
339
- return new _RpcPayload(resultArray, "owned", stubs, promises);
390
+ return new _RpcPayload(resultArray, "owned", hooks, promises);
340
391
  }
341
392
  // Create a payload from a value parsed off the wire using Evaluator.evaluate().
342
393
  //
343
- // A payload is constructed with a null value and the given stubs and promises arrays. The value
344
- // is expected to be filled in by the evaluator, and the stubs and promises arrays are expected
394
+ // A payload is constructed with a null value and the given hooks and promises arrays. The value
395
+ // is expected to be filled in by the evaluator, and the hooks and promises arrays are expected
345
396
  // to be extended with stubs found during parsing. (This weird usage model is necessary so that
346
397
  // if the root value turns out to be a promise, its `parent` in `promises` can be the payload
347
398
  // object itself.)
@@ -349,8 +400,8 @@ var RpcPayload = class _RpcPayload {
349
400
  // When done, the payload takes ownership of the final value and all the stubs within. It may
350
401
  // modify the value in preparation for delivery, and may deliver the value directly to the app
351
402
  // without copying.
352
- static forEvaluate(stubs, promises) {
353
- return new _RpcPayload(null, "owned", stubs, promises);
403
+ static forEvaluate(hooks, promises) {
404
+ return new _RpcPayload(null, "owned", hooks, promises);
354
405
  }
355
406
  // Deep-copy the given value, including dup()ing all stubs.
356
407
  //
@@ -372,14 +423,20 @@ var RpcPayload = class _RpcPayload {
372
423
  return result;
373
424
  }
374
425
  // For `source === "return"` payloads only, this tracks any StubHooks created around RpcTargets
375
- // found in the payload at the time that it is serialized (or deep-copied) for return, so that we
376
- // can make sure they are not disposed before the pipeline ends.
426
+ // or WritableStreams found in the payload at the time that it is serialized (or deep-copied) for
427
+ // return, so that we can make sure they are not disposed before the pipeline ends.
377
428
  //
378
429
  // This is initialized on first use.
379
430
  rpcTargets;
380
431
  // Get the StubHook representing the given RpcTarget found inside this payload.
381
432
  getHookForRpcTarget(target, parent, dupStubs = true) {
382
433
  if (this.source === "params") {
434
+ if (dupStubs) {
435
+ let dupable = target;
436
+ if (typeof dupable.dup === "function") {
437
+ target = dupable.dup();
438
+ }
439
+ }
383
440
  return TargetStubHook.create(target, parent);
384
441
  } else if (this.source === "return") {
385
442
  let hook = this.rpcTargets?.get(target);
@@ -406,6 +463,64 @@ var RpcPayload = class _RpcPayload {
406
463
  throw new Error("owned payload shouldn't contain raw RpcTargets");
407
464
  }
408
465
  }
466
+ // Get the StubHook representing the given WritableStream found inside this payload.
467
+ getHookForWritableStream(stream, parent, dupStubs = true) {
468
+ if (this.source === "params") {
469
+ return streamImpl.createWritableStreamHook(stream);
470
+ } else if (this.source === "return") {
471
+ let hook = this.rpcTargets?.get(stream);
472
+ if (hook) {
473
+ if (dupStubs) {
474
+ return hook.dup();
475
+ } else {
476
+ this.rpcTargets?.delete(stream);
477
+ return hook;
478
+ }
479
+ } else {
480
+ hook = streamImpl.createWritableStreamHook(stream);
481
+ if (dupStubs) {
482
+ if (!this.rpcTargets) {
483
+ this.rpcTargets = /* @__PURE__ */ new Map();
484
+ }
485
+ this.rpcTargets.set(stream, hook);
486
+ return hook.dup();
487
+ } else {
488
+ return hook;
489
+ }
490
+ }
491
+ } else {
492
+ throw new Error("owned payload shouldn't contain raw WritableStreams");
493
+ }
494
+ }
495
+ // Get the StubHook representing the given ReadableStream found inside this payload.
496
+ getHookForReadableStream(stream, parent, dupStubs = true) {
497
+ if (this.source === "params") {
498
+ return streamImpl.createReadableStreamHook(stream);
499
+ } else if (this.source === "return") {
500
+ let hook = this.rpcTargets?.get(stream);
501
+ if (hook) {
502
+ if (dupStubs) {
503
+ return hook.dup();
504
+ } else {
505
+ this.rpcTargets?.delete(stream);
506
+ return hook;
507
+ }
508
+ } else {
509
+ hook = streamImpl.createReadableStreamHook(stream);
510
+ if (dupStubs) {
511
+ if (!this.rpcTargets) {
512
+ this.rpcTargets = /* @__PURE__ */ new Map();
513
+ }
514
+ this.rpcTargets.set(stream, hook);
515
+ return hook.dup();
516
+ } else {
517
+ return hook;
518
+ }
519
+ }
520
+ } else {
521
+ throw new Error("owned payload shouldn't contain raw ReadableStreams");
522
+ }
523
+ }
409
524
  deepCopy(value, oldParent, property, parent, dupStubs, owner) {
410
525
  let kind = typeForRpc(value);
411
526
  switch (kind) {
@@ -449,22 +564,21 @@ var RpcPayload = class _RpcPayload {
449
564
  this.promises.push({ parent, property, promise });
450
565
  return promise;
451
566
  } else {
452
- let newStub = new RpcStub(hook);
453
- this.stubs.push(newStub);
454
- return newStub;
567
+ this.hooks.push(hook);
568
+ return new RpcStub(hook);
455
569
  }
456
570
  }
457
571
  case "function":
458
572
  case "rpc-target": {
459
573
  let target = value;
460
- let stub;
574
+ let hook;
461
575
  if (owner) {
462
- stub = new RpcStub(owner.getHookForRpcTarget(target, oldParent, dupStubs));
576
+ hook = owner.getHookForRpcTarget(target, oldParent, dupStubs);
463
577
  } else {
464
- stub = new RpcStub(TargetStubHook.create(target, oldParent));
578
+ hook = TargetStubHook.create(target, oldParent);
465
579
  }
466
- this.stubs.push(stub);
467
- return stub;
580
+ this.hooks.push(hook);
581
+ return new RpcStub(hook);
468
582
  }
469
583
  case "rpc-thenable": {
470
584
  let target = value;
@@ -477,6 +591,28 @@ var RpcPayload = class _RpcPayload {
477
591
  this.promises.push({ parent, property, promise });
478
592
  return promise;
479
593
  }
594
+ case "writable": {
595
+ let stream = value;
596
+ let hook;
597
+ if (owner) {
598
+ hook = owner.getHookForWritableStream(stream, oldParent, dupStubs);
599
+ } else {
600
+ hook = streamImpl.createWritableStreamHook(stream);
601
+ }
602
+ this.hooks.push(hook);
603
+ return stream;
604
+ }
605
+ case "readable": {
606
+ let stream = value;
607
+ let hook;
608
+ if (owner) {
609
+ hook = owner.getHookForReadableStream(stream, oldParent, dupStubs);
610
+ } else {
611
+ hook = streamImpl.createReadableStreamHook(stream);
612
+ }
613
+ this.hooks.push(hook);
614
+ return stream;
615
+ }
480
616
  default:
481
617
  throw new Error("unreachable");
482
618
  }
@@ -486,12 +622,12 @@ var RpcPayload = class _RpcPayload {
486
622
  ensureDeepCopied() {
487
623
  if (this.source !== "owned") {
488
624
  let dupStubs = this.source === "params";
489
- this.stubs = [];
625
+ this.hooks = [];
490
626
  this.promises = [];
491
627
  try {
492
628
  this.value = this.deepCopy(this.value, void 0, "value", this, dupStubs, this);
493
629
  } catch (err) {
494
- this.stubs = void 0;
630
+ this.hooks = void 0;
495
631
  this.promises = void 0;
496
632
  throw err;
497
633
  }
@@ -594,7 +730,7 @@ var RpcPayload = class _RpcPayload {
594
730
  }
595
731
  dispose() {
596
732
  if (this.source === "owned") {
597
- this.stubs.forEach((stub) => stub[Symbol.dispose]());
733
+ this.hooks.forEach((hook) => hook.dispose());
598
734
  this.promises.forEach((promise) => promise.promise[Symbol.dispose]());
599
735
  } else if (this.source === "return") {
600
736
  this.disposeImpl(this.value, void 0);
@@ -603,7 +739,7 @@ var RpcPayload = class _RpcPayload {
603
739
  }
604
740
  } else ;
605
741
  this.source = "owned";
606
- this.stubs = [];
742
+ this.hooks = [];
607
743
  this.promises = [];
608
744
  }
609
745
  // Recursive dispose, called only when `source` is "return".
@@ -656,6 +792,28 @@ var RpcPayload = class _RpcPayload {
656
792
  }
657
793
  case "rpc-thenable":
658
794
  return;
795
+ case "writable": {
796
+ let stream = value;
797
+ let hook = this.rpcTargets?.get(stream);
798
+ if (hook) {
799
+ this.rpcTargets.delete(stream);
800
+ } else {
801
+ hook = streamImpl.createWritableStreamHook(stream);
802
+ }
803
+ hook.dispose();
804
+ return;
805
+ }
806
+ case "readable": {
807
+ let stream = value;
808
+ let hook = this.rpcTargets?.get(stream);
809
+ if (hook) {
810
+ this.rpcTargets.delete(stream);
811
+ } else {
812
+ hook = streamImpl.createReadableStreamHook(stream);
813
+ }
814
+ hook.dispose();
815
+ return;
816
+ }
659
817
  default:
660
818
  return;
661
819
  }
@@ -664,9 +822,9 @@ var RpcPayload = class _RpcPayload {
664
822
  // *would* be awaited if this payload were to be delivered. See the similarly-named method of
665
823
  // StubHook for explanation.
666
824
  ignoreUnhandledRejections() {
667
- if (this.stubs) {
668
- this.stubs.forEach((stub) => {
669
- unwrapStubOrParent(stub).ignoreUnhandledRejections();
825
+ if (this.hooks) {
826
+ this.hooks.forEach((hook) => {
827
+ hook.ignoreUnhandledRejections();
670
828
  });
671
829
  this.promises.forEach(
672
830
  (promise) => unwrapStubOrParent(promise.promise).ignoreUnhandledRejections()
@@ -687,6 +845,8 @@ var RpcPayload = class _RpcPayload {
687
845
  case "undefined":
688
846
  case "function":
689
847
  case "rpc-target":
848
+ case "writable":
849
+ case "readable":
690
850
  return;
691
851
  case "array": {
692
852
  let array = value;
@@ -745,7 +905,9 @@ function followPath(value, parent, path, owner) {
745
905
  case "rpc-target":
746
906
  case "rpc-thenable": {
747
907
  if (Object.hasOwn(value, part)) {
748
- value = void 0;
908
+ throw new TypeError(
909
+ `Attempted to access property '${part}', which is an instance property of the RpcTarget. To avoid leaking private internals, instance properties cannot be accessed over RPC. If you want to make this property available over RPC, define it as a method or getter on the class, instead of an instance property.`
910
+ );
749
911
  } else {
750
912
  value = value[part];
751
913
  }
@@ -757,6 +919,12 @@ function followPath(value, parent, path, owner) {
757
919
  let { hook, pathIfPromise } = unwrapStubAndPath(value);
758
920
  return { hook, remainingPath: pathIfPromise ? pathIfPromise.concat(path.slice(i)) : path.slice(i) };
759
921
  }
922
+ case "writable":
923
+ value = void 0;
924
+ break;
925
+ case "readable":
926
+ value = void 0;
927
+ break;
760
928
  case "primitive":
761
929
  case "bigint":
762
930
  case "bytes":
@@ -996,6 +1164,14 @@ var PromiseStubHook = class _PromiseStubHook extends StubHook {
996
1164
  args.ensureDeepCopied();
997
1165
  return new _PromiseStubHook(this.promise.then((hook) => hook.call(path, args)));
998
1166
  }
1167
+ stream(path, args) {
1168
+ args.ensureDeepCopied();
1169
+ let promise = this.promise.then((hook) => {
1170
+ let result = hook.stream(path, args);
1171
+ return result.promise;
1172
+ });
1173
+ return { promise };
1174
+ }
999
1175
  map(path, captures, instructions) {
1000
1176
  return new _PromiseStubHook(this.promise.then(
1001
1177
  (hook) => hook.map(path, captures, instructions),
@@ -1068,6 +1244,9 @@ var NullExporter = class {
1068
1244
  }
1069
1245
  unexport(ids) {
1070
1246
  }
1247
+ createPipe(readable) {
1248
+ throw new Error("Cannot create pipes without an RPC session.");
1249
+ }
1071
1250
  onSendError(error) {
1072
1251
  }
1073
1252
  };
@@ -1129,7 +1308,17 @@ var Devaluator = class _Devaluator {
1129
1308
  throw new TypeError(msg);
1130
1309
  }
1131
1310
  case "primitive":
1132
- return value;
1311
+ if (typeof value === "number" && !isFinite(value)) {
1312
+ if (value === Infinity) {
1313
+ return ["inf"];
1314
+ } else if (value === -Infinity) {
1315
+ return ["-inf"];
1316
+ } else {
1317
+ return ["nan"];
1318
+ }
1319
+ } else {
1320
+ return value;
1321
+ }
1133
1322
  case "object": {
1134
1323
  let object = value;
1135
1324
  let result = {};
@@ -1216,6 +1405,22 @@ var Devaluator = class _Devaluator {
1216
1405
  let hook = this.source.getHookForRpcTarget(value, parent);
1217
1406
  return this.devaluateHook("promise", hook);
1218
1407
  }
1408
+ case "writable": {
1409
+ if (!this.source) {
1410
+ throw new Error("Can't serialize WritableStream in this context.");
1411
+ }
1412
+ let hook = this.source.getHookForWritableStream(value, parent);
1413
+ return this.devaluateHook("writable", hook);
1414
+ }
1415
+ case "readable": {
1416
+ if (!this.source) {
1417
+ throw new Error("Can't serialize ReadableStream in this context.");
1418
+ }
1419
+ let ws = value;
1420
+ let hook = this.source.getHookForReadableStream(ws, parent);
1421
+ let importId = this.exporter.createPipe(ws, hook);
1422
+ return ["readable", importId];
1423
+ }
1219
1424
  default:
1220
1425
  throw new Error("unreachable");
1221
1426
  }
@@ -1240,16 +1445,19 @@ var NullImporter = class {
1240
1445
  getExport(idx) {
1241
1446
  return void 0;
1242
1447
  }
1448
+ getPipeReadable(exportId) {
1449
+ throw new Error("Cannot retrieve pipe readable without an RPC session.");
1450
+ }
1243
1451
  };
1244
1452
  var NULL_IMPORTER = new NullImporter();
1245
1453
  var Evaluator = class _Evaluator {
1246
1454
  constructor(importer) {
1247
1455
  this.importer = importer;
1248
1456
  }
1249
- stubs = [];
1457
+ hooks = [];
1250
1458
  promises = [];
1251
1459
  evaluate(value) {
1252
- let payload = RpcPayload.forEvaluate(this.stubs, this.promises);
1460
+ let payload = RpcPayload.forEvaluate(this.hooks, this.promises);
1253
1461
  try {
1254
1462
  payload.value = this.evaluateImpl(value, payload, "value");
1255
1463
  return payload;
@@ -1313,6 +1521,12 @@ var Evaluator = class _Evaluator {
1313
1521
  return void 0;
1314
1522
  }
1315
1523
  break;
1524
+ case "inf":
1525
+ return Infinity;
1526
+ case "-inf":
1527
+ return -Infinity;
1528
+ case "nan":
1529
+ return NaN;
1316
1530
  case "import":
1317
1531
  case "pipeline": {
1318
1532
  if (value.length < 2 || value.length > 4) {
@@ -1332,9 +1546,8 @@ var Evaluator = class _Evaluator {
1332
1546
  this.promises.push({ promise, parent, property });
1333
1547
  return promise;
1334
1548
  } else {
1335
- let stub = new RpcPromise(hook2, []);
1336
- this.stubs.push(stub);
1337
- return stub;
1549
+ this.hooks.push(hook2);
1550
+ return new RpcPromise(hook2, []);
1338
1551
  }
1339
1552
  };
1340
1553
  if (value.length == 2) {
@@ -1412,12 +1625,27 @@ var Evaluator = class _Evaluator {
1412
1625
  return promise;
1413
1626
  } else {
1414
1627
  let hook = this.importer.importStub(value[1]);
1415
- let stub = new RpcStub(hook);
1416
- this.stubs.push(stub);
1417
- return stub;
1628
+ this.hooks.push(hook);
1629
+ return new RpcStub(hook);
1418
1630
  }
1419
1631
  }
1420
1632
  break;
1633
+ case "writable":
1634
+ if (typeof value[1] == "number") {
1635
+ let hook = this.importer.importStub(value[1]);
1636
+ let stream = streamImpl.createWritableStreamFromHook(hook);
1637
+ this.hooks.push(hook);
1638
+ return stream;
1639
+ }
1640
+ break;
1641
+ case "readable":
1642
+ if (typeof value[1] == "number") {
1643
+ let stream = this.importer.getPipeReadable(value[1]);
1644
+ let hook = streamImpl.createReadableStreamHook(stream);
1645
+ this.hooks.push(hook);
1646
+ return stream;
1647
+ }
1648
+ break;
1421
1649
  }
1422
1650
  throw new TypeError(`unknown special value: ${JSON.stringify(value)}`);
1423
1651
  } else if (value instanceof Object) {
@@ -1557,6 +1785,14 @@ var RpcImportHook = class _RpcImportHook extends StubHook {
1557
1785
  return entry.session.sendCall(entry.importId, path, args);
1558
1786
  }
1559
1787
  }
1788
+ stream(path, args) {
1789
+ let entry = this.getEntry();
1790
+ if (entry.resolution) {
1791
+ return entry.resolution.stream(path, args);
1792
+ } else {
1793
+ return entry.session.sendStream(entry.importId, path, args);
1794
+ }
1795
+ }
1560
1796
  map(path, captures, instructions) {
1561
1797
  let entry;
1562
1798
  try {
@@ -1729,19 +1965,23 @@ var RpcSessionImpl = class {
1729
1965
  return payload;
1730
1966
  }
1731
1967
  };
1968
+ let autoRelease = exp.autoRelease;
1732
1969
  ++this.pullCount;
1733
1970
  exp.pull = resolve().then(
1734
1971
  (payload) => {
1735
1972
  let value = Devaluator.devaluate(payload.value, void 0, this, payload);
1736
1973
  this.send(["resolve", exportId, value]);
1974
+ if (autoRelease) this.releaseExport(exportId, 1);
1737
1975
  },
1738
1976
  (error) => {
1739
1977
  this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
1978
+ if (autoRelease) this.releaseExport(exportId, 1);
1740
1979
  }
1741
1980
  ).catch(
1742
1981
  (error) => {
1743
1982
  try {
1744
1983
  this.send(["reject", exportId, Devaluator.devaluate(error, void 0, this)]);
1984
+ if (autoRelease) this.releaseExport(exportId, 1);
1745
1985
  } catch (error2) {
1746
1986
  this.abort(error2);
1747
1987
  }
@@ -1793,9 +2033,35 @@ var RpcSessionImpl = class {
1793
2033
  getExport(idx) {
1794
2034
  return this.exports[idx]?.hook;
1795
2035
  }
2036
+ getPipeReadable(exportId) {
2037
+ let entry = this.exports[exportId];
2038
+ if (!entry || !entry.pipeReadable) {
2039
+ throw new Error(`Export ${exportId} is not a pipe or its readable end was already consumed.`);
2040
+ }
2041
+ let readable = entry.pipeReadable;
2042
+ entry.pipeReadable = void 0;
2043
+ return readable;
2044
+ }
2045
+ createPipe(readable, readableHook) {
2046
+ if (this.abortReason) throw this.abortReason;
2047
+ this.send(["pipe"]);
2048
+ let importId = this.imports.length;
2049
+ let entry = new ImportTableEntry(this, importId, false);
2050
+ this.imports.push(entry);
2051
+ let hook = new RpcImportHook(
2052
+ /*isPromise=*/
2053
+ false,
2054
+ entry
2055
+ );
2056
+ let writable = streamImpl.createWritableStreamFromHook(hook);
2057
+ readable.pipeTo(writable).catch(() => {
2058
+ }).finally(() => readableHook.dispose());
2059
+ return importId;
2060
+ }
2061
+ // Serializes and sends a message. Returns the byte length of the serialized message.
1796
2062
  send(msg) {
1797
2063
  if (this.abortReason !== void 0) {
1798
- return;
2064
+ return 0;
1799
2065
  }
1800
2066
  let msgText;
1801
2067
  try {
@@ -1808,6 +2074,7 @@ var RpcSessionImpl = class {
1808
2074
  throw err;
1809
2075
  }
1810
2076
  this.transport.send(msgText).catch((err) => this.abort(err, false));
2077
+ return msgText.length;
1811
2078
  }
1812
2079
  sendCall(id, path, args) {
1813
2080
  if (this.abortReason) throw this.abortReason;
@@ -1825,6 +2092,34 @@ var RpcSessionImpl = class {
1825
2092
  entry
1826
2093
  );
1827
2094
  }
2095
+ sendStream(id, path, args) {
2096
+ if (this.abortReason) throw this.abortReason;
2097
+ let value = ["pipeline", id, path];
2098
+ let devalue = Devaluator.devaluate(args.value, void 0, this, args);
2099
+ value.push(devalue[0]);
2100
+ let size = this.send(["stream", value]);
2101
+ let importId = this.imports.length;
2102
+ let entry = new ImportTableEntry(
2103
+ this,
2104
+ importId,
2105
+ /*pulling=*/
2106
+ true
2107
+ );
2108
+ entry.remoteRefcount = 0;
2109
+ entry.localRefcount = 1;
2110
+ this.imports.push(entry);
2111
+ let promise = entry.awaitResolution().then(
2112
+ (p) => {
2113
+ p.dispose();
2114
+ delete this.imports[importId];
2115
+ },
2116
+ (err) => {
2117
+ delete this.imports[importId];
2118
+ throw err;
2119
+ }
2120
+ );
2121
+ return { promise, size };
2122
+ }
1828
2123
  sendMap(id, path, captures, instructions) {
1829
2124
  if (this.abortReason) {
1830
2125
  for (let cap of captures) {
@@ -1912,6 +2207,24 @@ var RpcSessionImpl = class {
1912
2207
  continue;
1913
2208
  }
1914
2209
  break;
2210
+ case "stream": {
2211
+ if (msg.length > 1) {
2212
+ let payload = new Evaluator(this).evaluate(msg[1]);
2213
+ let hook = new PayloadStubHook(payload);
2214
+ hook.ignoreUnhandledRejections();
2215
+ let exportId = this.exports.length;
2216
+ this.exports.push({ hook, refcount: 1, autoRelease: true });
2217
+ this.ensureResolvingExport(exportId);
2218
+ continue;
2219
+ }
2220
+ break;
2221
+ }
2222
+ case "pipe": {
2223
+ let { readable, writable } = new TransformStream();
2224
+ let hook = streamImpl.createWritableStreamHook(writable);
2225
+ this.exports.push({ hook, refcount: 1, pipeReadable: readable });
2226
+ continue;
2227
+ }
1915
2228
  case "pull": {
1916
2229
  let exportId = msg[1];
1917
2230
  if (typeof exportId == "number") {
@@ -2407,6 +2720,9 @@ var MapBuilder = class {
2407
2720
  }
2408
2721
  unexport(ids) {
2409
2722
  }
2723
+ createPipe(readable) {
2724
+ throw new Error("Cannot send ReadableStream inside a mapper function.");
2725
+ }
2410
2726
  onSendError(error) {
2411
2727
  }
2412
2728
  };
@@ -2516,6 +2832,9 @@ var MapApplicator = class {
2516
2832
  return this.variables[idx];
2517
2833
  }
2518
2834
  }
2835
+ getPipeReadable(exportId) {
2836
+ throw new Error("A mapper function cannot use pipe readables.");
2837
+ }
2519
2838
  };
2520
2839
  function applyMapToElement(input, parent, owner, captures, instructions) {
2521
2840
  let inputHook = new PayloadStubHook(RpcPayload.deepCopyFrom(input, parent, owner));
@@ -2556,6 +2875,333 @@ mapImpl.applyMap = (input, parent, owner, captures, instructions) => {
2556
2875
  }
2557
2876
  }
2558
2877
  };
2878
+
2879
+ // src/streams.ts
2880
+ var WritableStreamStubHook = class _WritableStreamStubHook extends StubHook {
2881
+ state;
2882
+ // undefined when disposed
2883
+ // Creates a new WritableStreamStubHook that is not duplicated from an existing hook.
2884
+ static create(stream) {
2885
+ let writer = stream.getWriter();
2886
+ return new _WritableStreamStubHook({ refcount: 1, writer, closed: false });
2887
+ }
2888
+ constructor(state, dupFrom) {
2889
+ super();
2890
+ this.state = state;
2891
+ if (dupFrom) {
2892
+ ++state.refcount;
2893
+ }
2894
+ }
2895
+ getState() {
2896
+ if (this.state) {
2897
+ return this.state;
2898
+ } else {
2899
+ throw new Error("Attempted to use a WritableStreamStubHook after it was disposed.");
2900
+ }
2901
+ }
2902
+ call(path, args) {
2903
+ try {
2904
+ let state = this.getState();
2905
+ if (path.length !== 1 || typeof path[0] !== "string") {
2906
+ throw new Error("WritableStream stub only supports direct method calls");
2907
+ }
2908
+ const method = path[0];
2909
+ if (method !== "write" && method !== "close" && method !== "abort") {
2910
+ args.dispose();
2911
+ throw new Error(`Unknown WritableStream method: ${method}`);
2912
+ }
2913
+ if (method === "close" || method === "abort") {
2914
+ state.closed = true;
2915
+ }
2916
+ let func = state.writer[method];
2917
+ let promise = args.deliverCall(func, state.writer);
2918
+ return new PromiseStubHook(promise.then((payload) => new PayloadStubHook(payload)));
2919
+ } catch (err) {
2920
+ return new ErrorStubHook(err);
2921
+ }
2922
+ }
2923
+ map(path, captures, instructions) {
2924
+ for (let cap of captures) {
2925
+ cap.dispose();
2926
+ }
2927
+ return new ErrorStubHook(new Error("Cannot use map() on a WritableStream"));
2928
+ }
2929
+ get(path) {
2930
+ return new ErrorStubHook(new Error("Cannot access properties on a WritableStream stub"));
2931
+ }
2932
+ dup() {
2933
+ let state = this.getState();
2934
+ return new _WritableStreamStubHook(state, this);
2935
+ }
2936
+ pull() {
2937
+ return Promise.reject(new Error("Cannot pull a WritableStream stub"));
2938
+ }
2939
+ ignoreUnhandledRejections() {
2940
+ }
2941
+ dispose() {
2942
+ let state = this.state;
2943
+ this.state = void 0;
2944
+ if (state) {
2945
+ if (--state.refcount === 0) {
2946
+ if (!state.closed) {
2947
+ state.writer.abort(new Error("WritableStream RPC stub was disposed without calling close()")).catch(() => {
2948
+ });
2949
+ }
2950
+ state.writer.releaseLock();
2951
+ }
2952
+ }
2953
+ }
2954
+ onBroken(callback) {
2955
+ }
2956
+ };
2957
+ var INITIAL_WINDOW = 256 * 1024;
2958
+ var MAX_WINDOW = 1024 * 1024 * 1024;
2959
+ var MIN_WINDOW = 64 * 1024;
2960
+ var STARTUP_GROWTH_FACTOR = 2;
2961
+ var STEADY_GROWTH_FACTOR = 1.25;
2962
+ var DECAY_FACTOR = 0.9;
2963
+ var STARTUP_EXIT_ROUNDS = 3;
2964
+ var FlowController = class {
2965
+ constructor(now) {
2966
+ this.now = now;
2967
+ }
2968
+ // The current window size in bytes. The sender blocks when bytesInFlight >= window.
2969
+ window = INITIAL_WINDOW;
2970
+ // Total bytes currently in flight (sent but not yet acked).
2971
+ bytesInFlight = 0;
2972
+ // Whether we're still in the startup phase.
2973
+ inStartupPhase = true;
2974
+ // ----- BDP estimation state (private) -----
2975
+ // Total bytes acked so far.
2976
+ delivered = 0;
2977
+ // Time of most recent ack.
2978
+ deliveredTime = 0;
2979
+ // Time when the very first ack was received.
2980
+ firstAckTime = 0;
2981
+ firstAckDelivered = 0;
2982
+ // Global minimum RTT observed (milliseconds).
2983
+ minRtt = Infinity;
2984
+ // For startup exit: count of consecutive RTT rounds where the window didn't meaningfully grow.
2985
+ roundsWithoutIncrease = 0;
2986
+ // Window size at the start of the current round, for startup exit detection.
2987
+ lastRoundWindow = 0;
2988
+ // Time when the current round started.
2989
+ roundStartTime = 0;
2990
+ // Called when a write of `size` bytes is about to be sent. Returns a token that must be
2991
+ // passed to onAck() when the ack arrives, and whether the sender should block (window full).
2992
+ onSend(size) {
2993
+ this.bytesInFlight += size;
2994
+ let token = {
2995
+ sentTime: this.now(),
2996
+ size,
2997
+ deliveredAtSend: this.delivered,
2998
+ deliveredTimeAtSend: this.deliveredTime,
2999
+ windowAtSend: this.window,
3000
+ windowFullAtSend: this.bytesInFlight >= this.window
3001
+ };
3002
+ return { token, shouldBlock: token.windowFullAtSend };
3003
+ }
3004
+ // Called when a previously-sent write fails. Restores bytesInFlight without updating
3005
+ // any BDP estimates.
3006
+ onError(token) {
3007
+ this.bytesInFlight -= token.size;
3008
+ }
3009
+ // Called when an ack is received for a previously-sent write. Updates BDP estimates and
3010
+ // the window. Returns whether a blocked sender should now unblock.
3011
+ onAck(token) {
3012
+ let ackTime = this.now();
3013
+ this.delivered += token.size;
3014
+ this.deliveredTime = ackTime;
3015
+ this.bytesInFlight -= token.size;
3016
+ let rtt = ackTime - token.sentTime;
3017
+ this.minRtt = Math.min(this.minRtt, rtt);
3018
+ if (this.firstAckTime === 0) {
3019
+ this.firstAckTime = ackTime;
3020
+ this.firstAckDelivered = this.delivered;
3021
+ } else {
3022
+ let baseTime;
3023
+ let baseDelivered;
3024
+ if (token.deliveredTimeAtSend === 0) {
3025
+ baseTime = this.firstAckTime;
3026
+ baseDelivered = this.firstAckDelivered;
3027
+ } else {
3028
+ baseTime = token.deliveredTimeAtSend;
3029
+ baseDelivered = token.deliveredAtSend;
3030
+ }
3031
+ let interval = ackTime - baseTime;
3032
+ let bytes = this.delivered - baseDelivered;
3033
+ let bandwidth = bytes / interval;
3034
+ let growthFactor = this.inStartupPhase ? STARTUP_GROWTH_FACTOR : STEADY_GROWTH_FACTOR;
3035
+ let newWindow = bandwidth * this.minRtt * growthFactor;
3036
+ newWindow = Math.min(newWindow, token.windowAtSend * growthFactor);
3037
+ if (token.windowFullAtSend) {
3038
+ newWindow = Math.max(newWindow, token.windowAtSend * DECAY_FACTOR);
3039
+ } else {
3040
+ newWindow = Math.max(newWindow, this.window);
3041
+ }
3042
+ this.window = Math.max(Math.min(newWindow, MAX_WINDOW), MIN_WINDOW);
3043
+ if (this.inStartupPhase && token.sentTime >= this.roundStartTime) {
3044
+ if (this.window > this.lastRoundWindow * STEADY_GROWTH_FACTOR) {
3045
+ this.roundsWithoutIncrease = 0;
3046
+ } else {
3047
+ if (++this.roundsWithoutIncrease >= STARTUP_EXIT_ROUNDS) {
3048
+ this.inStartupPhase = false;
3049
+ }
3050
+ }
3051
+ this.roundStartTime = ackTime;
3052
+ this.lastRoundWindow = this.window;
3053
+ }
3054
+ }
3055
+ return this.bytesInFlight < this.window;
3056
+ }
3057
+ };
3058
+ function createWritableStreamFromHook(hook) {
3059
+ let pendingError = void 0;
3060
+ let hookDisposed = false;
3061
+ let fc = new FlowController(() => performance.now());
3062
+ let windowResolve;
3063
+ let windowReject;
3064
+ const disposeHook = () => {
3065
+ if (!hookDisposed) {
3066
+ hookDisposed = true;
3067
+ hook.dispose();
3068
+ }
3069
+ };
3070
+ return new WritableStream({
3071
+ write(chunk, controller) {
3072
+ if (pendingError !== void 0) {
3073
+ throw pendingError;
3074
+ }
3075
+ const payload = RpcPayload.fromAppParams([chunk]);
3076
+ const { promise, size } = hook.stream(["write"], payload);
3077
+ if (size === void 0) {
3078
+ return promise.catch((err) => {
3079
+ if (pendingError === void 0) {
3080
+ pendingError = err;
3081
+ }
3082
+ throw err;
3083
+ });
3084
+ } else {
3085
+ let { token, shouldBlock } = fc.onSend(size);
3086
+ promise.then(() => {
3087
+ let hasCapacity = fc.onAck(token);
3088
+ if (hasCapacity && windowResolve) {
3089
+ windowResolve();
3090
+ windowResolve = void 0;
3091
+ windowReject = void 0;
3092
+ }
3093
+ }, (err) => {
3094
+ fc.onError(token);
3095
+ if (pendingError === void 0) {
3096
+ pendingError = err;
3097
+ controller.error(err);
3098
+ disposeHook();
3099
+ }
3100
+ if (windowReject) {
3101
+ windowReject(err);
3102
+ windowResolve = void 0;
3103
+ windowReject = void 0;
3104
+ }
3105
+ });
3106
+ if (shouldBlock) {
3107
+ return new Promise((resolve, reject) => {
3108
+ windowResolve = resolve;
3109
+ windowReject = reject;
3110
+ });
3111
+ }
3112
+ }
3113
+ },
3114
+ async close() {
3115
+ if (pendingError !== void 0) {
3116
+ disposeHook();
3117
+ throw pendingError;
3118
+ }
3119
+ const { promise } = hook.stream(["close"], RpcPayload.fromAppParams([]));
3120
+ try {
3121
+ await promise;
3122
+ } catch (err) {
3123
+ throw pendingError ?? err;
3124
+ } finally {
3125
+ disposeHook();
3126
+ }
3127
+ },
3128
+ abort(reason) {
3129
+ if (pendingError !== void 0) {
3130
+ return;
3131
+ }
3132
+ pendingError = reason ?? new Error("WritableStream was aborted");
3133
+ if (windowReject) {
3134
+ windowReject(pendingError);
3135
+ windowResolve = void 0;
3136
+ windowReject = void 0;
3137
+ }
3138
+ const { promise } = hook.stream(["abort"], RpcPayload.fromAppParams([reason]));
3139
+ promise.then(() => disposeHook(), () => disposeHook());
3140
+ }
3141
+ });
3142
+ }
3143
+ var ReadableStreamStubHook = class _ReadableStreamStubHook extends StubHook {
3144
+ state;
3145
+ // undefined when disposed
3146
+ // Creates a new ReadableStreamStubHook.
3147
+ static create(stream) {
3148
+ return new _ReadableStreamStubHook({ refcount: 1, stream, canceled: false });
3149
+ }
3150
+ constructor(state, dupFrom) {
3151
+ super();
3152
+ this.state = state;
3153
+ if (dupFrom) {
3154
+ ++state.refcount;
3155
+ }
3156
+ }
3157
+ call(path, args) {
3158
+ args.dispose();
3159
+ return new ErrorStubHook(new Error("Cannot call methods on a ReadableStream stub"));
3160
+ }
3161
+ map(path, captures, instructions) {
3162
+ for (let cap of captures) {
3163
+ cap.dispose();
3164
+ }
3165
+ return new ErrorStubHook(new Error("Cannot use map() on a ReadableStream"));
3166
+ }
3167
+ get(path) {
3168
+ return new ErrorStubHook(new Error("Cannot access properties on a ReadableStream stub"));
3169
+ }
3170
+ dup() {
3171
+ let state = this.state;
3172
+ if (!state) {
3173
+ throw new Error("Attempted to dup a ReadableStreamStubHook after it was disposed.");
3174
+ }
3175
+ return new _ReadableStreamStubHook(state, this);
3176
+ }
3177
+ pull() {
3178
+ return Promise.reject(new Error("Cannot pull a ReadableStream stub"));
3179
+ }
3180
+ ignoreUnhandledRejections() {
3181
+ }
3182
+ dispose() {
3183
+ let state = this.state;
3184
+ this.state = void 0;
3185
+ if (state) {
3186
+ if (--state.refcount === 0) {
3187
+ if (!state.canceled) {
3188
+ state.canceled = true;
3189
+ if (!state.stream.locked) {
3190
+ state.stream.cancel(
3191
+ new Error("ReadableStream RPC stub was disposed without being consumed")
3192
+ ).catch(() => {
3193
+ });
3194
+ }
3195
+ }
3196
+ }
3197
+ }
3198
+ }
3199
+ onBroken(callback) {
3200
+ }
3201
+ };
3202
+ streamImpl.createWritableStreamHook = WritableStreamStubHook.create;
3203
+ streamImpl.createWritableStreamFromHook = createWritableStreamFromHook;
3204
+ streamImpl.createReadableStreamHook = ReadableStreamStubHook.create;
2559
3205
  var RpcStub2 = RpcStub;
2560
3206
  var RpcPromise2 = RpcPromise;
2561
3207
  var RpcSession2 = RpcSession;