@rivetkit/engine-runner 2.1.6 → 2.1.7

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.6",
3
+ "version": "2.1.7",
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.6"
25
+ "@rivetkit/engine-runner-protocol": "2.1.7"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.18.1",
package/src/mod.ts CHANGED
@@ -12,13 +12,16 @@ 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
18
 
16
19
  export type { HibernatingWebSocketMetadata };
17
20
  export { RunnerActor, type ActorConfig };
18
21
  export { idToStr } from "./utils";
19
22
 
20
23
  const KV_EXPIRE: number = 30_000;
21
- const PROTOCOL_VERSION: number = 6;
24
+ const PROTOCOL_VERSION: number = 7;
22
25
 
23
26
  /** Warn once the backlog significantly exceeds the server's ack batch size. */
24
27
  const EVENT_BACKLOG_WARN_THRESHOLD = 10_000;
@@ -40,7 +43,6 @@ export interface RunnerConfig {
40
43
  namespace: string;
41
44
  totalSlots: number;
42
45
  runnerName: string;
43
- runnerKey: string;
44
46
  prepopulateActorNames: Record<string, { metadata: Record<string, any> }>;
45
47
  metadata?: Record<string, any>;
46
48
  onConnected: () => void;
@@ -175,6 +177,15 @@ export interface RunnerConfig {
175
177
 
176
178
  onActorStop: (actorId: string, generation: number) => Promise<void>;
177
179
  noAutoShutdown?: boolean;
180
+
181
+ /**
182
+ * Debug option to inject artificial latency (in ms) into WebSocket
183
+ * communication. Messages are queued and delivered in order after the
184
+ * configured delay.
185
+ *
186
+ * @experimental For testing only.
187
+ */
188
+ debugLatencyMs?: number;
178
189
  }
179
190
 
180
191
  export interface KvListOptions {
@@ -193,6 +204,7 @@ interface KvRequestEntry {
193
204
 
194
205
  export class Runner {
195
206
  #config: RunnerConfig;
207
+ #runnerKey: string = uuidv4();
196
208
 
197
209
  get config(): RunnerConfig {
198
210
  return this.#config;
@@ -710,7 +722,7 @@ export class Runner {
710
722
  const baseUrl = wsEndpoint.endsWith("/")
711
723
  ? wsEndpoint.slice(0, -1)
712
724
  : wsEndpoint;
713
- return `${baseUrl}/runners/connect?protocol_version=${PROTOCOL_VERSION}&namespace=${encodeURIComponent(this.#config.namespace)}&runner_key=${encodeURIComponent(this.#config.runnerKey)}`;
725
+ return `${baseUrl}/runners/connect?protocol_version=${PROTOCOL_VERSION}&namespace=${encodeURIComponent(this.#config.namespace)}&runner_key=${encodeURIComponent(this.#runnerKey)}`;
714
726
  }
715
727
 
716
728
  // MARK: Runner protocol
@@ -740,7 +752,7 @@ export class Runner {
740
752
  msg: "connecting",
741
753
  endpoint: this.pegboardEndpoint,
742
754
  namespace: this.#config.namespace,
743
- runnerKey: this.#config.runnerKey,
755
+ runnerKey: this.#runnerKey,
744
756
  hasToken: !!this.config.token,
745
757
  });
746
758
 
@@ -828,6 +840,8 @@ export class Runner {
828
840
  throw new Error(`expected binary data, got ${typeof ev.data}`);
829
841
  }
830
842
 
843
+ await this.#injectLatency();
844
+
831
845
  // Parse message
832
846
  const message = protocol.decodeToClient(buf);
833
847
  this.log?.debug({
@@ -1556,6 +1570,31 @@ export class Runner {
1556
1570
  await this.#sendKvRequest(actorId, requestData);
1557
1571
  }
1558
1572
 
1573
+ async kvDeleteRange(
1574
+ actorId: string,
1575
+ start: Uint8Array,
1576
+ end: Uint8Array,
1577
+ ): Promise<void> {
1578
+ const startKey: protocol.KvKey = start.buffer.slice(
1579
+ start.byteOffset,
1580
+ start.byteOffset + start.byteLength,
1581
+ ) as ArrayBuffer;
1582
+ const endKey: protocol.KvKey = end.buffer.slice(
1583
+ end.byteOffset,
1584
+ end.byteOffset + end.byteLength,
1585
+ ) as ArrayBuffer;
1586
+
1587
+ const requestData: protocol.KvRequestData = {
1588
+ tag: "KvDeleteRangeRequest",
1589
+ val: {
1590
+ start: startKey,
1591
+ end: endKey,
1592
+ },
1593
+ };
1594
+
1595
+ await this.#sendKvRequest(actorId, requestData);
1596
+ }
1597
+
1559
1598
  async kvDrop(actorId: string): Promise<void> {
1560
1599
  const requestData: protocol.KvRequestData = {
1561
1600
  tag: "KvDropRequest",
@@ -1667,6 +1706,13 @@ export class Runner {
1667
1706
  }
1668
1707
  }
1669
1708
 
1709
+ /** Resolves after the configured debug latency, or immediately if none. */
1710
+ #injectLatency(): Promise<void> {
1711
+ const ms = this.#config.debugLatencyMs;
1712
+ if (!ms) return Promise.resolve();
1713
+ return new Promise((resolve) => setTimeout(resolve, ms));
1714
+ }
1715
+
1670
1716
  /** Asserts WebSocket exists and is ready. */
1671
1717
  getPegboardWebSocketIfReady(): WebSocket | undefined {
1672
1718
  if (
@@ -1686,14 +1732,19 @@ export class Runner {
1686
1732
  });
1687
1733
 
1688
1734
  const encoded = protocol.encodeToServer(message);
1689
- const pegboardWebSocket = this.getPegboardWebSocketIfReady();
1690
- if (pegboardWebSocket) {
1691
- pegboardWebSocket.send(encoded);
1692
- } else {
1693
- this.log?.error({
1694
- msg: "WebSocket not available or not open for sending data",
1695
- });
1696
- }
1735
+
1736
+ // Normally synchronous. When debugLatencyMs is set, the send is
1737
+ // deferred but message order is preserved.
1738
+ this.#injectLatency().then(() => {
1739
+ const pegboardWebSocket = this.getPegboardWebSocketIfReady();
1740
+ if (pegboardWebSocket) {
1741
+ pegboardWebSocket.send(encoded);
1742
+ } else {
1743
+ this.log?.error({
1744
+ msg: "WebSocket not available or not open for sending data",
1745
+ });
1746
+ }
1747
+ });
1697
1748
  }
1698
1749
 
1699
1750
  sendHibernatableWebSocketMessageAck(
package/src/stringify.ts CHANGED
@@ -303,6 +303,10 @@ function stringifyKvRequestData(data: protocol.KvRequestData): string {
303
303
  const { keys } = data.val;
304
304
  return `KvDeleteRequest{keys: ${keys.length}}`;
305
305
  }
306
+ case "KvDeleteRangeRequest": {
307
+ const { start, end } = data.val;
308
+ return `KvDeleteRangeRequest{start: ${stringifyArrayBuffer(start)}, end: ${stringifyArrayBuffer(end)}}`;
309
+ }
306
310
  case "KvDropRequest":
307
311
  return "KvDropRequest";
308
312
  }
package/src/tunnel.ts CHANGED
@@ -5,11 +5,6 @@ import type {
5
5
  RequestId,
6
6
  } from "@rivetkit/engine-runner-protocol";
7
7
  import type { Logger } from "pino";
8
- import {
9
- parse as uuidparse,
10
- stringify as uuidstringify,
11
- v4 as uuidv4,
12
- } from "uuid";
13
8
  import { type Runner, type RunnerActor, RunnerShutdownError } from "./mod";
14
9
  import {
15
10
  stringifyToClientTunnelMessageKind,