@rivetkit/engine-runner 2.1.3 → 2.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rivetkit/engine-runner",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -22,7 +22,7 @@
22
22
  "pino": "^9.9.5",
23
23
  "ws": "^8.18.3",
24
24
  "@rivetkit/virtual-websocket": "2.0.33",
25
- "@rivetkit/engine-runner-protocol": "2.1.3"
25
+ "@rivetkit/engine-runner-protocol": "2.1.4"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.18.1",
package/src/mod.ts CHANGED
@@ -208,8 +208,10 @@ export class Runner {
208
208
  #reconnectAttempt: number = 0;
209
209
  #reconnectTimeout?: NodeJS.Timeout;
210
210
 
211
+ // Protocol metadata
212
+ #protocolMetadata?: protocol.ProtocolMetadata;
213
+
211
214
  // Runner lost threshold management
212
- #runnerLostThreshold?: number;
213
215
  #runnerLostTimeout?: NodeJS.Timeout;
214
216
 
215
217
  // Event storage for resending
@@ -839,14 +841,11 @@ export class Runner {
839
841
  this.#stopAllActors();
840
842
  }
841
843
 
842
- // Store the runner lost threshold from metadata
843
- this.#runnerLostThreshold = init.metadata?.runnerLostThreshold
844
- ? Number(init.metadata.runnerLostThreshold)
845
- : undefined;
844
+ this.#protocolMetadata = init.metadata;
846
845
 
847
846
  this.log?.info({
848
847
  msg: "received init",
849
- runnerLostThreshold: this.#runnerLostThreshold,
848
+ protocolMetadata: this.#protocolMetadata,
850
849
  });
851
850
 
852
851
  // Resend pending events
@@ -888,27 +887,7 @@ export class Runner {
888
887
  });
889
888
 
890
889
  if (!this.#shutdown) {
891
- // Start runner lost timeout if we have a threshold and are not shutting down
892
- if (
893
- !this.#runnerLostTimeout &&
894
- this.#runnerLostThreshold &&
895
- this.#runnerLostThreshold > 0
896
- ) {
897
- this.log?.info({
898
- msg: "starting runner lost timeout",
899
- seconds: this.#runnerLostThreshold / 1000,
900
- });
901
- this.#runnerLostTimeout = setTimeout(() => {
902
- try {
903
- this.#handleLost();
904
- } catch (err) {
905
- this.log?.error({
906
- msg: "error handling runner lost",
907
- error: stringifyError(err),
908
- });
909
- }
910
- }, this.#runnerLostThreshold);
911
- }
890
+ this.#startRunnerLostTimeout();
912
891
 
913
892
  // Attempt to reconnect if not stopped
914
893
  this.#scheduleReconnect();
@@ -944,27 +923,7 @@ export class Runner {
944
923
  this.#ackInterval = undefined;
945
924
  }
946
925
 
947
- // Start runner lost timeout if we have a threshold and are not shutting down
948
- if (
949
- !this.#runnerLostTimeout &&
950
- this.#runnerLostThreshold &&
951
- this.#runnerLostThreshold > 0
952
- ) {
953
- this.log?.info({
954
- msg: "starting runner lost timeout",
955
- seconds: this.#runnerLostThreshold / 1000,
956
- });
957
- this.#runnerLostTimeout = setTimeout(() => {
958
- try {
959
- this.#handleLost();
960
- } catch (err) {
961
- this.log?.error({
962
- msg: "error handling runner lost",
963
- error: stringifyError(err),
964
- });
965
- }
966
- }, this.#runnerLostThreshold);
967
- }
926
+ this.#startRunnerLostTimeout();
968
927
 
969
928
  // Attempt to reconnect if not stopped
970
929
  this.#scheduleReconnect();
@@ -976,6 +935,30 @@ export class Runner {
976
935
  });
977
936
  }
978
937
 
938
+ #startRunnerLostTimeout() {
939
+ // Start runner lost timeout if we have a threshold and are not shutting down
940
+ if (
941
+ !this.#runnerLostTimeout &&
942
+ this.#protocolMetadata &&
943
+ this.#protocolMetadata.runnerLostThreshold > 0
944
+ ) {
945
+ this.log?.info({
946
+ msg: "starting runner lost timeout",
947
+ seconds: this.#protocolMetadata.runnerLostThreshold / 1000n,
948
+ });
949
+ this.#runnerLostTimeout = setTimeout(() => {
950
+ try {
951
+ this.#handleLost();
952
+ } catch (err) {
953
+ this.log?.error({
954
+ msg: "error handling runner lost",
955
+ error: stringifyError(err),
956
+ });
957
+ }
958
+ }, Number(this.#protocolMetadata.runnerLostThreshold));
959
+ }
960
+ }
961
+
979
962
  #handleCommands(commands: protocol.ToClientCommands) {
980
963
  this.log?.info({
981
964
  msg: "received commands",
@@ -1859,4 +1842,8 @@ export class Runner {
1859
1842
  //this.#log?.log(`Cleaned up ${toDelete.length} expired KV requests`);
1860
1843
  }
1861
1844
  }
1845
+
1846
+ getProtocolMetadata(): protocol.ProtocolMetadata | undefined {
1847
+ return this.#protocolMetadata;
1848
+ }
1862
1849
  }
package/src/tunnel.ts CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  stringifyToClientTunnelMessageKind,
16
16
  stringifyToServerTunnelMessageKind,
17
17
  } from "./stringify";
18
- import { arraysEqual, idToStr, stringifyError, unreachable } from "./utils";
18
+ import { arraysEqual, idToStr, MAX_PAYLOAD_SIZE, stringifyError, unreachable } from "./utils";
19
19
  import {
20
20
  HIBERNATABLE_SYMBOL,
21
21
  WebSocketTunnelAdapter,
@@ -855,6 +855,10 @@ export class Tunnel {
855
855
  // Read the body first to get the actual content
856
856
  const body = response.body ? await response.arrayBuffer() : null;
857
857
 
858
+ if (body && body.byteLength > MAX_PAYLOAD_SIZE) {
859
+ throw new Error("Response body too large");
860
+ }
861
+
858
862
  // Convert headers to map and add Content-Length if not present
859
863
  const headers = new Map<string, string>();
860
864
  response.headers.forEach((value, key) => {
@@ -1079,7 +1083,7 @@ export class Tunnel {
1079
1083
  });
1080
1084
 
1081
1085
  if (clientMessageIndex < 0 || clientMessageIndex > 65535)
1082
- throw new Error("invalid websocket ack index");
1086
+ throw new Error("Invalid websocket ack index");
1083
1087
 
1084
1088
  // Get the actor to find the gatewayId
1085
1089
  //
@@ -1157,7 +1161,7 @@ function buildRequestForWebSocket(
1157
1161
  };
1158
1162
 
1159
1163
  if (!path.startsWith("/")) {
1160
- throw new Error("path must start with leading slash");
1164
+ throw new Error("Path must start with leading slash");
1161
1165
  }
1162
1166
 
1163
1167
  const request = new Request(`http://actor${path}`, {
package/src/utils.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import { logger } from "./log";
2
2
 
3
+ // 20MiB. Keep in sync with runner_max_response_payload_body_size from engine/packages/config/src/config/pegboard.rs
4
+ export const MAX_PAYLOAD_SIZE = 20 * 1024 * 1024;
5
+
3
6
  export function unreachable(x: never): never {
4
7
  throw `Unreachable: ${x}`;
5
8
  }
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from "pino";
2
2
  import { VirtualWebSocket, type UniversalWebSocket, type RivetMessageEvent } from "@rivetkit/virtual-websocket";
3
3
  import type { Tunnel } from "./tunnel";
4
- import { wrappingAddU16, wrappingLteU16, wrappingSubU16 } from "./utils";
4
+ import { MAX_PAYLOAD_SIZE, wrappingAddU16, wrappingLteU16, wrappingSubU16 } from "./utils";
5
5
 
6
6
  export const HIBERNATABLE_SYMBOL = Symbol("hibernatable");
7
7
 
@@ -70,11 +70,20 @@ export class WebSocketTunnelAdapter {
70
70
  let messageData: string | ArrayBuffer;
71
71
 
72
72
  if (typeof data === "string") {
73
+ const encoder = new TextEncoder();
74
+ if (encoder.encode(data).byteLength > MAX_PAYLOAD_SIZE) {
75
+ throw new Error("WebSocket message too large");
76
+ }
77
+
73
78
  messageData = data;
74
79
  } else if (data instanceof ArrayBuffer) {
80
+ if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large");
81
+
75
82
  isBinary = true;
76
83
  messageData = data;
77
84
  } else if (ArrayBuffer.isView(data)) {
85
+ if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large");
86
+
78
87
  isBinary = true;
79
88
  const view = data;
80
89
  const buffer = view.buffer instanceof SharedArrayBuffer