@rivetkit/engine-runner 2.3.0-rc.3 → 2.3.0-rc.5

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.3.0-rc.3",
3
+ "version": "2.3.0-rc.5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -25,8 +25,8 @@
25
25
  "bench": "tsx benches/actor-lifecycle.bench.ts"
26
26
  },
27
27
  "dependencies": {
28
- "@rivetkit/virtual-websocket": "2.3.0-rc.3",
29
- "@rivetkit/engine-runner-protocol": "2.3.0-rc.3",
28
+ "@rivetkit/virtual-websocket": "2.3.0-rc.5",
29
+ "@rivetkit/engine-runner-protocol": "2.3.0-rc.5",
30
30
  "uuid": "^12.0.0",
31
31
  "pino": "^9.9.5",
32
32
  "ws": "^8.18.3"
package/src/mod.ts CHANGED
@@ -12,9 +12,7 @@ import {
12
12
  unreachable,
13
13
  } from "./utils";
14
14
  import { importWebSocket } from "./websocket.js";
15
- import {
16
- v4 as uuidv4,
17
- } from "uuid";
15
+ import { v4 as uuidv4 } from "uuid";
18
16
 
19
17
  export type { HibernatingWebSocketMetadata };
20
18
  export { RunnerActor, type ActorConfig };
@@ -562,9 +560,18 @@ export class Runner {
562
560
  this.#kvRequests.clear();
563
561
 
564
562
  // Close WebSocket
565
- const pegboardWebSocket = this.getPegboardWebSocketIfReady();
566
- if (pegboardWebSocket) {
567
- if (immediate) {
563
+ //
564
+ // A CONNECTING socket cannot send graceful stopping messages, so
565
+ // it is always closed directly. Without this, a shutdown that
566
+ // races with a reconnect leaves the in-flight WebSocket alive
567
+ // while the rest of the runner state (tunnel, intervals) has been
568
+ // torn down.
569
+ const pegboardWebSocket = this.#pegboardWebSocket;
570
+ const readyState = pegboardWebSocket?.readyState;
571
+ const isOpen = readyState === 1;
572
+ const isConnecting = readyState === 0;
573
+ if (pegboardWebSocket && (isOpen || isConnecting)) {
574
+ if (immediate || isConnecting) {
568
575
  // Stop immediately
569
576
  pegboardWebSocket.close(1000, "pegboard.runner_shutdown");
570
577
  } else {
@@ -876,6 +883,10 @@ export class Runner {
876
883
  if (this.runnerId !== init.runnerId) {
877
884
  this.runnerId = init.runnerId;
878
885
 
886
+ // Invalidate cached child logger so subsequent log
887
+ // lines pick up the new runnerId.
888
+ this.#logCached = undefined;
889
+
879
890
  // Clear actors if runner id changed
880
891
  this.#stopAllActors();
881
892
  }
@@ -1253,9 +1264,10 @@ export class Runner {
1253
1264
  actorState = {
1254
1265
  tag: "ActorStateStopped",
1255
1266
  val: {
1256
- code: actor.stopIntentSent || this.#draining
1257
- ? protocol.StopCode.Ok
1258
- : protocol.StopCode.Error,
1267
+ code:
1268
+ actor.stopIntentSent || this.#draining
1269
+ ? protocol.StopCode.Ok
1270
+ : protocol.StopCode.Error,
1259
1271
  message: null,
1260
1272
  },
1261
1273
  };
package/src/tunnel.ts CHANGED
@@ -10,7 +10,13 @@ import {
10
10
  stringifyToClientTunnelMessageKind,
11
11
  stringifyToServerTunnelMessageKind,
12
12
  } from "./stringify";
13
- import { arraysEqual, idToStr, MAX_PAYLOAD_SIZE, stringifyError, unreachable } from "./utils";
13
+ import {
14
+ arraysEqual,
15
+ idToStr,
16
+ MAX_PAYLOAD_SIZE,
17
+ stringifyError,
18
+ unreachable,
19
+ } from "./utils";
14
20
  import {
15
21
  HIBERNATABLE_SYMBOL,
16
22
  WebSocketTunnelAdapter,
@@ -1,7 +1,16 @@
1
1
  import type { Logger } from "pino";
2
- import { VirtualWebSocket, type UniversalWebSocket, type RivetMessageEvent } from "@rivetkit/virtual-websocket";
2
+ import {
3
+ VirtualWebSocket,
4
+ type UniversalWebSocket,
5
+ type RivetMessageEvent,
6
+ } from "@rivetkit/virtual-websocket";
3
7
  import type { Tunnel } from "./tunnel";
4
- import { MAX_PAYLOAD_SIZE, wrappingAddU16, wrappingLteU16, wrappingSubU16 } from "./utils";
8
+ import {
9
+ MAX_PAYLOAD_SIZE,
10
+ wrappingAddU16,
11
+ wrappingLteU16,
12
+ wrappingSubU16,
13
+ } from "./utils";
5
14
 
6
15
  export const HIBERNATABLE_SYMBOL = Symbol("hibernatable");
7
16
 
@@ -77,18 +86,28 @@ export class WebSocketTunnelAdapter {
77
86
 
78
87
  messageData = data;
79
88
  } else if (data instanceof ArrayBuffer) {
80
- if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large");
89
+ if (data.byteLength > MAX_PAYLOAD_SIZE)
90
+ throw new Error("WebSocket message too large");
81
91
 
82
92
  isBinary = true;
83
93
  messageData = data;
84
94
  } else if (ArrayBuffer.isView(data)) {
85
- if (data.byteLength > MAX_PAYLOAD_SIZE) throw new Error("WebSocket message too large");
95
+ if (data.byteLength > MAX_PAYLOAD_SIZE)
96
+ throw new Error("WebSocket message too large");
86
97
 
87
98
  isBinary = true;
88
99
  const view = data;
89
- const buffer = view.buffer instanceof SharedArrayBuffer
90
- ? new Uint8Array(view.buffer, view.byteOffset, view.byteLength).slice().buffer
91
- : view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
100
+ const buffer =
101
+ view.buffer instanceof SharedArrayBuffer
102
+ ? new Uint8Array(
103
+ view.buffer,
104
+ view.byteOffset,
105
+ view.byteLength,
106
+ ).slice().buffer
107
+ : view.buffer.slice(
108
+ view.byteOffset,
109
+ view.byteOffset + view.byteLength,
110
+ );
92
111
  messageData = buffer as ArrayBuffer;
93
112
  } else {
94
113
  throw new Error("Unsupported data type");
@@ -101,7 +120,11 @@ export class WebSocketTunnelAdapter {
101
120
  _handleOpen(requestId: ArrayBuffer): void {
102
121
  if (this.#readyState !== 0) return;
103
122
  this.#readyState = 1;
104
- this.#ws.dispatchEvent({ type: "open", rivetRequestId: requestId, target: this.#ws });
123
+ this.#ws.dispatchEvent({
124
+ type: "open",
125
+ rivetRequestId: requestId,
126
+ target: this.#ws,
127
+ });
105
128
  }
106
129
 
107
130
  // Called by Tunnel when message is received
@@ -147,7 +170,10 @@ export class WebSocketTunnelAdapter {
147
170
  expectedIndex,
148
171
  receivedIndex: serverMessageIndex,
149
172
  closeReason,
150
- gap: wrappingSubU16(wrappingSubU16(serverMessageIndex, previousIndex), 1),
173
+ gap: wrappingSubU16(
174
+ wrappingSubU16(serverMessageIndex, previousIndex),
175
+ 1,
176
+ ),
151
177
  });
152
178
  this.#close(1008, closeReason, true);
153
179
  return true;
@@ -162,7 +188,10 @@ export class WebSocketTunnelAdapter {
162
188
  if (this.#binaryType === "nodebuffer") {
163
189
  messageData = Buffer.from(data);
164
190
  } else if (this.#binaryType === "arraybuffer") {
165
- messageData = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
191
+ messageData = data.buffer.slice(
192
+ data.byteOffset,
193
+ data.byteOffset + data.byteLength,
194
+ );
166
195
  }
167
196
  }
168
197
 
@@ -178,7 +207,11 @@ export class WebSocketTunnelAdapter {
178
207
  }
179
208
 
180
209
  // Called by Tunnel when close is received
181
- _handleClose(_requestId: ArrayBuffer, code?: number, reason?: string): void {
210
+ _handleClose(
211
+ _requestId: ArrayBuffer,
212
+ code?: number,
213
+ reason?: string,
214
+ ): void {
182
215
  this.#close(code, reason, true);
183
216
  }
184
217
 
@@ -192,7 +225,11 @@ export class WebSocketTunnelAdapter {
192
225
  this.#close(code, reason, true);
193
226
  }
194
227
 
195
- #close(code: number | undefined, reason: string | undefined, sendCallback: boolean): void {
228
+ #close(
229
+ code: number | undefined,
230
+ reason: string | undefined,
231
+ sendCallback: boolean,
232
+ ): void {
196
233
  if (this.#readyState >= 2) return;
197
234
 
198
235
  this.#readyState = 2;