capnweb 0.7.0 → 0.8.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 CHANGED
@@ -201,6 +201,7 @@ The following types can be passed over RPC (in arguments or return values), and
201
201
  * `Date`
202
202
  * `Uint8Array`
203
203
  * `Error` and its well-known subclasses
204
+ * `Blob`
204
205
  * `ReadableStream` and `WritableStream`, with automatic flow control.
205
206
  * `Headers`, `Request`, and `Response` from the Fetch API.
206
207
 
@@ -70,6 +70,8 @@ function typeForRpc(value) {
70
70
  return "request";
71
71
  case Response.prototype:
72
72
  return "response";
73
+ case Blob.prototype:
74
+ return "blob";
73
75
  // TODO: All other structured clone types.
74
76
  case RpcStub.prototype:
75
77
  return "stub";
@@ -540,6 +542,7 @@ var RpcPayload = class _RpcPayload {
540
542
  case "bigint":
541
543
  case "date":
542
544
  case "bytes":
545
+ case "blob":
543
546
  case "error":
544
547
  case "undefined":
545
548
  return value;
@@ -776,6 +779,7 @@ var RpcPayload = class _RpcPayload {
776
779
  case "primitive":
777
780
  case "bigint":
778
781
  case "bytes":
782
+ case "blob":
779
783
  case "date":
780
784
  case "error":
781
785
  case "undefined":
@@ -878,6 +882,7 @@ var RpcPayload = class _RpcPayload {
878
882
  case "primitive":
879
883
  case "bigint":
880
884
  case "bytes":
885
+ case "blob":
881
886
  case "date":
882
887
  case "error":
883
888
  case "undefined":
@@ -969,6 +974,7 @@ function followPath(value, parent, path, owner) {
969
974
  case "primitive":
970
975
  case "bigint":
971
976
  case "bytes":
977
+ case "blob":
972
978
  case "date":
973
979
  case "error":
974
980
  case "headers":
@@ -1295,6 +1301,10 @@ var NullExporter = class {
1295
1301
  }
1296
1302
  };
1297
1303
  var NULL_EXPORTER = new NullExporter();
1304
+ async function streamToBlob(stream, type) {
1305
+ let b = await new Response(stream).blob();
1306
+ return b.type === type ? b : b.slice(0, b.size, type);
1307
+ }
1298
1308
  var ERROR_TYPES = {
1299
1309
  Error,
1300
1310
  EvalError,
@@ -1382,8 +1392,10 @@ var Devaluator = class _Devaluator {
1382
1392
  }
1383
1393
  case "bigint":
1384
1394
  return ["bigint", value.toString()];
1385
- case "date":
1386
- return ["date", value.getTime()];
1395
+ case "date": {
1396
+ const time = value.getTime();
1397
+ return ["date", Number.isNaN(time) ? null : time];
1398
+ }
1387
1399
  case "bytes": {
1388
1400
  let bytes = value;
1389
1401
  if (bytes.toBase64) {
@@ -1469,14 +1481,52 @@ var Devaluator = class _Devaluator {
1469
1481
  }
1470
1482
  return ["response", body, init];
1471
1483
  }
1484
+ case "blob": {
1485
+ let blob = value;
1486
+ let readable = blob.stream();
1487
+ let hook = streamImpl.createReadableStreamHook(readable);
1488
+ let importId = this.exporter.createPipe(readable, hook);
1489
+ return ["blob", blob.type, ["readable", importId]];
1490
+ }
1472
1491
  case "error": {
1473
1492
  let e = value;
1474
1493
  let rewritten = this.exporter.onSendError(e);
1475
1494
  if (rewritten) {
1476
1495
  e = rewritten;
1477
1496
  }
1497
+ let anyE = e;
1498
+ let props;
1499
+ let captureProp = (key, val) => {
1500
+ let exportsBefore = this.exports?.length ?? 0;
1501
+ try {
1502
+ let encoded = this.devaluateImpl(val, e, depth + 1);
1503
+ if (!props) props = {};
1504
+ props[key] = encoded;
1505
+ } catch (err) {
1506
+ if (this.exports && this.exports.length > exportsBefore) {
1507
+ let tail = this.exports.splice(exportsBefore);
1508
+ try {
1509
+ this.exporter.unexport(tail);
1510
+ } catch (err2) {
1511
+ }
1512
+ }
1513
+ }
1514
+ };
1515
+ for (let key of Object.keys(e)) {
1516
+ if (key === "name" || key === "message" || key === "stack") continue;
1517
+ captureProp(key, anyE[key]);
1518
+ }
1519
+ if ("cause" in e) {
1520
+ captureProp("cause", anyE.cause);
1521
+ }
1522
+ if (e instanceof AggregateError) {
1523
+ captureProp("errors", e.errors);
1524
+ }
1478
1525
  let result = ["error", e.name, e.message];
1479
- if (rewritten && rewritten.stack) {
1526
+ if (props) {
1527
+ result.push(rewritten && rewritten.stack ? rewritten.stack : null);
1528
+ result.push(props);
1529
+ } else if (rewritten && rewritten.stack) {
1480
1530
  result.push(rewritten.stack);
1481
1531
  }
1482
1532
  return result;
@@ -1576,6 +1626,12 @@ function fixBrokenRequestBody(request, body) {
1576
1626
  });
1577
1627
  return new RpcPromise(new PromiseStubHook(promise), []);
1578
1628
  }
1629
+ function streamToBlobPromise(stream, type) {
1630
+ let promise = streamToBlob(stream, type).then((blob) => {
1631
+ return new PayloadStubHook(RpcPayload.fromAppReturn(blob));
1632
+ });
1633
+ return new RpcPromise(new PromiseStubHook(promise), []);
1634
+ }
1579
1635
  var Evaluator = class _Evaluator {
1580
1636
  constructor(importer) {
1581
1637
  this.importer = importer;
@@ -1611,6 +1667,9 @@ var Evaluator = class _Evaluator {
1611
1667
  }
1612
1668
  break;
1613
1669
  case "date":
1670
+ if (value[1] === null) {
1671
+ return /* @__PURE__ */ new Date(NaN);
1672
+ }
1614
1673
  if (typeof value[1] == "number") {
1615
1674
  return new Date(value[1]);
1616
1675
  }
@@ -1636,10 +1695,22 @@ var Evaluator = class _Evaluator {
1636
1695
  case "error":
1637
1696
  if (value.length >= 3 && typeof value[1] === "string" && typeof value[2] === "string") {
1638
1697
  let cls = ERROR_TYPES[value[1]] || Error;
1639
- let result = new cls(value[2]);
1698
+ let result = cls === AggregateError ? new cls([], value[2]) : new cls(value[2]);
1640
1699
  if (typeof value[3] === "string") {
1641
1700
  result.stack = value[3];
1642
1701
  }
1702
+ if (value.length >= 5) {
1703
+ let props = value[4];
1704
+ if (!props || typeof props !== "object" || Array.isArray(props)) {
1705
+ break;
1706
+ }
1707
+ let anyResult = result;
1708
+ let propsObj = props;
1709
+ for (let key of Object.keys(propsObj)) {
1710
+ if (key === "name" || key === "message" || key === "stack") continue;
1711
+ anyResult[key] = this.evaluateImpl(propsObj[key], result, key);
1712
+ }
1713
+ }
1643
1714
  return result;
1644
1715
  }
1645
1716
  break;
@@ -1704,6 +1775,17 @@ var Evaluator = class _Evaluator {
1704
1775
  }
1705
1776
  return new Response(body, init);
1706
1777
  }
1778
+ case "blob": {
1779
+ if (value.length !== 3 || typeof value[1] !== "string") break;
1780
+ let contentType = value[1];
1781
+ let content = this.evaluateImpl(value[2], parent, property);
1782
+ if (!(content instanceof ReadableStream)) {
1783
+ throw new TypeError("Blob content must be serialized as a ReadableStream.");
1784
+ }
1785
+ let promise = streamToBlobPromise(content, contentType);
1786
+ this.promises.push({ promise, parent, property });
1787
+ return promise;
1788
+ }
1707
1789
  case "import":
1708
1790
  case "pipeline": {
1709
1791
  if (value.length < 2 || value.length > 4) {
@@ -2044,12 +2126,7 @@ var RpcSessionImpl = class {
2044
2126
  this.options = options;
2045
2127
  this.exports.push({ hook: mainHook, refcount: 1 });
2046
2128
  this.imports.push(new ImportTableEntry(this, 0, false));
2047
- let rejectFunc;
2048
- let abortPromise = new Promise((resolve, reject) => {
2049
- rejectFunc = reject;
2050
- });
2051
- this.cancelReadLoop = rejectFunc;
2052
- this.readLoop(abortPromise).catch((err) => this.abort(err));
2129
+ this.readLoop().catch((err) => this.abort(err));
2053
2130
  }
2054
2131
  exports = [];
2055
2132
  reverseExports = /* @__PURE__ */ new Map();
@@ -2333,7 +2410,8 @@ var RpcSessionImpl = class {
2333
2410
  }
2334
2411
  abort(error, trySendAbortMessage = true) {
2335
2412
  if (this.abortReason !== void 0) return;
2336
- this.cancelReadLoop(error);
2413
+ this.cancelReadLoop?.(error);
2414
+ this.cancelReadLoop = void 0;
2337
2415
  if (trySendAbortMessage) {
2338
2416
  try {
2339
2417
  this.transport.send(JSON.stringify(["abort", Devaluator.devaluate(error, void 0, this)])).catch((err) => {
@@ -2369,9 +2447,19 @@ var RpcSessionImpl = class {
2369
2447
  this.exports[i].hook.dispose();
2370
2448
  }
2371
2449
  }
2372
- async readLoop(abortPromise) {
2450
+ async readLoop() {
2373
2451
  while (!this.abortReason) {
2374
- let msg = JSON.parse(await Promise.race([this.transport.receive(), abortPromise]));
2452
+ let readCanceled = Promise.withResolvers();
2453
+ this.cancelReadLoop = readCanceled.reject;
2454
+ let msgText;
2455
+ try {
2456
+ msgText = await Promise.race([this.transport.receive(), readCanceled.promise]);
2457
+ } finally {
2458
+ if (this.cancelReadLoop === readCanceled.reject) {
2459
+ this.cancelReadLoop = void 0;
2460
+ }
2461
+ }
2462
+ let msg = JSON.parse(msgText);
2375
2463
  if (this.abortReason) break;
2376
2464
  if (msg instanceof Array) {
2377
2465
  switch (msg[0]) {