capnweb 0.9.1 → 0.10.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/dist/index.cjs CHANGED
@@ -931,6 +931,12 @@ var PromiseStubHook = class PromiseStubHook extends StubHook {
931
931
 
932
932
  //#endregion
933
933
  //#region src/serialize.ts
934
+ const DEFAULT_MAX_DEPTH = 256;
935
+ const DEFAULT_LIMITS = {
936
+ maxBigIntDigits: 16384,
937
+ maxDepth: 256,
938
+ maxMessageSize: 32 * 1024 * 1024
939
+ };
934
940
  var NullExporter = class {
935
941
  exportStub(stub) {
936
942
  throw new Error("Cannot serialize RPC stubs without an RPC session.");
@@ -950,7 +956,7 @@ async function streamToBlob(stream, type) {
950
956
  let b = await new Response(stream).blob();
951
957
  return b.type === type ? b : b.slice(0, b.size, type);
952
958
  }
953
- const ERROR_TYPES = {
959
+ const ERROR_TYPES = Object.assign(Object.create(null), {
954
960
  Error,
955
961
  EvalError,
956
962
  RangeError,
@@ -959,7 +965,7 @@ const ERROR_TYPES = {
959
965
  TypeError,
960
966
  URIError,
961
967
  AggregateError
962
- };
968
+ });
963
969
  var Devaluator = class Devaluator {
964
970
  exporter;
965
971
  source;
@@ -982,7 +988,7 @@ var Devaluator = class Devaluator {
982
988
  }
983
989
  exports;
984
990
  devaluateImpl(value, parent, depth) {
985
- if (depth >= 64) throw new Error("Serialization exceeded maximum allowed depth. (Does the message contain cycles?)");
991
+ if (depth >= 256) throw new Error("Serialization exceeded maximum allowed depth. (Does the message contain cycles?)");
986
992
  switch (typeForRpc(value)) {
987
993
  case "unsupported": {
988
994
  let msg;
@@ -1216,6 +1222,9 @@ var NullImporter = class {
1216
1222
  getPipeReadable(exportId) {
1217
1223
  throw new Error("Cannot retrieve pipe readable without an RPC session.");
1218
1224
  }
1225
+ getLimits() {
1226
+ return DEFAULT_LIMITS;
1227
+ }
1219
1228
  };
1220
1229
  const NULL_IMPORTER = new NullImporter();
1221
1230
  function fixBrokenRequestBody(request, body) {
@@ -1233,16 +1242,21 @@ function streamToBlobPromise(stream, type) {
1233
1242
  var Evaluator = class Evaluator {
1234
1243
  importer;
1235
1244
  encodingLevel;
1245
+ limits;
1236
1246
  constructor(importer, encodingLevel = "string") {
1237
1247
  this.importer = importer;
1238
1248
  this.encodingLevel = encodingLevel;
1249
+ this.limits = importer.getLimits();
1239
1250
  }
1240
1251
  hooks = [];
1241
1252
  promises = [];
1242
1253
  evaluate(value) {
1254
+ return this.evaluateWithDepth(value, 0);
1255
+ }
1256
+ evaluateWithDepth(value, depth) {
1243
1257
  let payload = RpcPayload.forEvaluate(this.hooks, this.promises);
1244
1258
  try {
1245
- payload.value = this.evaluateImpl(value, payload, "value");
1259
+ payload.value = this.evaluateImpl(value, payload, "value", depth);
1246
1260
  return payload;
1247
1261
  } catch (err) {
1248
1262
  payload.dispose();
@@ -1252,18 +1266,25 @@ var Evaluator = class Evaluator {
1252
1266
  evaluateCopy(value) {
1253
1267
  return this.evaluate(structuredClone(value));
1254
1268
  }
1255
- evaluateImpl(value, parent, property) {
1269
+ evaluateImpl(value, parent, property, depth) {
1270
+ let maxDepth = this.limits.maxDepth;
1271
+ if (depth >= maxDepth) throw new TypeError(`Deserialization exceeded maximum allowed message depth of ${maxDepth}.`);
1256
1272
  if (this.encodingLevel === "structuredClonable") {
1257
1273
  if (value instanceof Date || typeof value === "bigint") return value;
1258
1274
  }
1259
1275
  if (value instanceof Array) {
1260
1276
  if (value.length == 1 && value[0] instanceof Array) {
1261
1277
  let result = value[0];
1262
- for (let i = 0; i < result.length; i++) result[i] = this.evaluateImpl(result[i], result, i);
1278
+ for (let i = 0; i < result.length; i++) result[i] = this.evaluateImpl(result[i], result, i, depth + 1);
1263
1279
  return result;
1264
1280
  } else switch (value[0]) {
1265
1281
  case "bigint":
1266
- if (typeof value[1] == "string") return BigInt(value[1]);
1282
+ if (typeof value[1] == "string") {
1283
+ let digits = value[1];
1284
+ let maxBigIntDigits = this.limits.maxBigIntDigits;
1285
+ if (digits.length > maxBigIntDigits) throw new TypeError(`Deserialized bigint exceeds maximum length of ${maxBigIntDigits} digits.`);
1286
+ return BigInt(digits);
1287
+ }
1267
1288
  break;
1268
1289
  case "date":
1269
1290
  if (value[1] === null) return /* @__PURE__ */ new Date(NaN);
@@ -1293,7 +1314,11 @@ var Evaluator = class Evaluator {
1293
1314
  let propsObj = props;
1294
1315
  for (let key of Object.keys(propsObj)) {
1295
1316
  if (key === "name" || key === "message" || key === "stack") continue;
1296
- anyResult[key] = this.evaluateImpl(propsObj[key], result, key);
1317
+ if (key in Object.prototype || key === "toJSON") {
1318
+ this.evaluateImpl(propsObj[key], result, key, depth + 1);
1319
+ continue;
1320
+ }
1321
+ anyResult[key] = this.evaluateImpl(propsObj[key], result, key, depth + 1);
1297
1322
  }
1298
1323
  }
1299
1324
  return result;
@@ -1314,11 +1339,11 @@ var Evaluator = class Evaluator {
1314
1339
  let init = value[2];
1315
1340
  if (typeof init !== "object" || init === null) break;
1316
1341
  if (init.body) {
1317
- init.body = this.evaluateImpl(init.body, init, "body");
1342
+ init.body = this.evaluateImpl(init.body, init, "body", depth + 1);
1318
1343
  if (init.body === null || typeof init.body === "string" || init.body instanceof Uint8Array || init.body instanceof ReadableStream) {} else throw new TypeError("Request body must be of type ReadableStream.");
1319
1344
  }
1320
1345
  if (init.signal) {
1321
- init.signal = this.evaluateImpl(init.signal, init, "signal");
1346
+ init.signal = this.evaluateImpl(init.signal, init, "signal", depth + 1);
1322
1347
  if (!(init.signal instanceof AbortSignal)) throw new TypeError("Request siganl must be of type AbortSignal.");
1323
1348
  }
1324
1349
  if (init.headers && !(init.headers instanceof Array)) throw new TypeError("Request headers must be serialized as an array of pairs.");
@@ -1335,7 +1360,7 @@ var Evaluator = class Evaluator {
1335
1360
  }
1336
1361
  case "response": {
1337
1362
  if (value.length !== 3) break;
1338
- let body = this.evaluateImpl(value[1], parent, property);
1363
+ let body = this.evaluateImpl(value[1], parent, property, depth + 1);
1339
1364
  if (body === null || typeof body === "string" || body instanceof Uint8Array || body instanceof ReadableStream) {} else throw new TypeError("Response body must be of type ReadableStream.");
1340
1365
  let init = value[2];
1341
1366
  if (typeof init !== "object" || init === null) break;
@@ -1346,7 +1371,7 @@ var Evaluator = class Evaluator {
1346
1371
  case "blob": {
1347
1372
  if (value.length !== 3 || typeof value[1] !== "string") break;
1348
1373
  let contentType = value[1];
1349
- let content = this.evaluateImpl(value[2], parent, property);
1374
+ let content = this.evaluateImpl(value[2], parent, property, depth + 1);
1350
1375
  if (!(content instanceof ReadableStream)) throw new TypeError("Blob content must be serialized as a ReadableStream.");
1351
1376
  let promise = streamToBlobPromise(content, contentType);
1352
1377
  this.promises.push({
@@ -1387,7 +1412,7 @@ var Evaluator = class Evaluator {
1387
1412
  if (value.length == 3) return addStub(hook.get(path));
1388
1413
  let args = value[3];
1389
1414
  if (!(args instanceof Array)) break;
1390
- args = new Evaluator(this.importer).evaluate([args]);
1415
+ args = new Evaluator(this.importer).evaluateWithDepth([args], depth);
1391
1416
  return addStub(hook.call(path, args));
1392
1417
  }
1393
1418
  case "remap": {
@@ -1453,9 +1478,9 @@ var Evaluator = class Evaluator {
1453
1478
  } else if (value instanceof Object) {
1454
1479
  let result = value;
1455
1480
  for (let key in result) if (key in Object.prototype || key === "toJSON") {
1456
- this.evaluateImpl(result[key], result, key);
1481
+ this.evaluateImpl(result[key], result, key, depth + 1);
1457
1482
  delete result[key];
1458
- } else result[key] = this.evaluateImpl(result[key], result, key);
1483
+ } else result[key] = this.evaluateImpl(result[key], result, key, depth + 1);
1459
1484
  return result;
1460
1485
  } else return value;
1461
1486
  }
@@ -1526,6 +1551,10 @@ var ImportTableEntry = class {
1526
1551
  resolution;
1527
1552
  onBrokenRegistrations;
1528
1553
  resolve(resolution) {
1554
+ if (this.resolution) {
1555
+ resolution.dispose();
1556
+ return;
1557
+ }
1529
1558
  if (this.localRefcount == 0) {
1530
1559
  resolution.dispose();
1531
1560
  return;
@@ -1678,6 +1707,7 @@ var RpcSessionImpl = class {
1678
1707
  pullCount = 0;
1679
1708
  onBrokenCallbacks = [];
1680
1709
  encodingLevel;
1710
+ limits;
1681
1711
  constructor(transport, mainHook, options) {
1682
1712
  this.transport = transport;
1683
1713
  this.options = options;
@@ -1690,6 +1720,10 @@ var RpcSessionImpl = class {
1690
1720
  }
1691
1721
  }
1692
1722
  this.encodingLevel = level;
1723
+ this.limits = {
1724
+ ...DEFAULT_LIMITS,
1725
+ ...options.limits
1726
+ };
1693
1727
  this.exports.push({
1694
1728
  hook: mainHook,
1695
1729
  refcount: 1
@@ -1832,6 +1866,9 @@ var RpcSessionImpl = class {
1832
1866
  entry.pipeReadable = void 0;
1833
1867
  return readable;
1834
1868
  }
1869
+ getLimits() {
1870
+ return this.limits;
1871
+ }
1835
1872
  createPipe(readable, readableHook) {
1836
1873
  if (this.abortReason) throw this.abortReason;
1837
1874
  this.send(["pipe"]);
@@ -1991,6 +2028,7 @@ var RpcSessionImpl = class {
1991
2028
  } finally {
1992
2029
  if (this.cancelReadLoop === readCanceled.reject) this.cancelReadLoop = void 0;
1993
2030
  }
2031
+ if (this.encodingLevel === "string" && raw.length > this.limits.maxMessageSize) throw new TypeError(`Incoming message exceeds maximum size of ${this.limits.maxMessageSize} UTF-16 code units.`);
1994
2032
  if (this.abortReason) break;
1995
2033
  let msg = this.encodingLevel === "string" ? JSON.parse(raw) : raw;
1996
2034
  if (msg instanceof Array) switch (msg[0]) {
@@ -2065,7 +2103,7 @@ var RpcSessionImpl = class {
2065
2103
  case "abort": {
2066
2104
  let payload = new Evaluator(this, this.encodingLevel).evaluate(msg[1]);
2067
2105
  payload.dispose();
2068
- this.abort(payload, false);
2106
+ this.abort(payload.value, false);
2069
2107
  break;
2070
2108
  }
2071
2109
  }
@@ -2191,6 +2229,8 @@ var WebSocketTransport = class {
2191
2229
  let message;
2192
2230
  if (reason instanceof Error) message = reason.message;
2193
2231
  else message = `${reason}`;
2232
+ let reasonBytes = new TextEncoder().encode(message);
2233
+ if (reasonBytes.length > 123) message = new TextDecoder().decode(reasonBytes.subarray(0, 123), { stream: true });
2194
2234
  this.#webSocket.close(3e3, message);
2195
2235
  if (!this.#error) this.#error = reason;
2196
2236
  }
@@ -2576,6 +2616,9 @@ var MapApplicator = class {
2576
2616
  getPipeReadable(exportId) {
2577
2617
  throw new Error("A mapper function cannot use pipe readables.");
2578
2618
  }
2619
+ getLimits() {
2620
+ return DEFAULT_LIMITS;
2621
+ }
2579
2622
  };
2580
2623
  function applyMapToElement(input, parent, owner, captures, instructions) {
2581
2624
  let mapper = new MapApplicator(captures, new PayloadStubHook(RpcPayload.deepCopyFrom(input, parent, owner)));
@@ -2914,16 +2957,18 @@ let newMessagePortRpcSession = newMessagePortRpcSession$1;
2914
2957
  * credentials as parameters and returns the authorized API), then cross-origin requests should
2915
2958
  * be safe.
2916
2959
  */
2917
- async function newWorkersRpcResponse(request, localMain) {
2960
+ async function newWorkersRpcResponse(request, localMain, options) {
2918
2961
  if (request.method === "POST") {
2919
- let response = await newHttpBatchRpcResponse(request, localMain);
2962
+ let response = await newHttpBatchRpcResponse(request, localMain, options);
2920
2963
  response.headers.set("Access-Control-Allow-Origin", "*");
2921
2964
  return response;
2922
- } else if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") return newWorkersWebSocketRpcResponse(request, localMain);
2965
+ } else if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") return newWorkersWebSocketRpcResponse(request, localMain, options);
2923
2966
  else return new Response("This endpoint only accepts POST or WebSocket requests.", { status: 400 });
2924
2967
  }
2925
2968
 
2926
2969
  //#endregion
2970
+ exports.DEFAULT_LIMITS = DEFAULT_LIMITS;
2971
+ exports.DEFAULT_MAX_DEPTH = DEFAULT_MAX_DEPTH;
2927
2972
  exports.RpcPromise = RpcPromise;
2928
2973
  exports.RpcSession = RpcSession;
2929
2974
  exports.RpcStub = RpcStub;