@rivetkit/engine-runner 2.0.27 → 2.0.29-rc.1

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/mod.cjs CHANGED
@@ -83,10 +83,27 @@ function idToStr(id) {
83
83
  const bytes = new Uint8Array(id);
84
84
  return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
85
85
  }
86
+ function stringifyError(error) {
87
+ var _a;
88
+ if (error instanceof Error) {
89
+ return `${error.name}: ${error.message}${error.stack ? `
90
+ ${error.stack}` : ""}`;
91
+ } else if (typeof error === "string") {
92
+ return error;
93
+ } else if (typeof error === "object" && error !== null) {
94
+ try {
95
+ return `${JSON.stringify(error)}`;
96
+ } catch (e2) {
97
+ return `[object ${((_a = error.constructor) == null ? void 0 : _a.name) || "Object"}]`;
98
+ }
99
+ } else {
100
+ return String(error);
101
+ }
102
+ }
86
103
 
87
104
  // src/actor.ts
88
105
  var RunnerActor = (_class = class {
89
- constructor(actorId, generation, config, hibernatingRequests) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);
106
+ constructor(actorId, generation, config, hibernatingRequests) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);
90
107
  this.hibernatingRequests = hibernatingRequests;
91
108
  this.actorId = actorId;
92
109
  this.generation = generation;
@@ -99,11 +116,14 @@ var RunnerActor = (_class = class {
99
116
  __init() {this.pendingRequests = []}
100
117
  __init2() {this.webSockets = []}
101
118
 
119
+ __init3() {this.lastCommandIdx = -1n}
120
+ __init4() {this.nextEventIdx = 0n}
121
+ __init5() {this.eventHistory = []}
102
122
  /**
103
123
  * If restoreHibernatingRequests has been called. This is used to assert
104
124
  * that the caller is implemented correctly.
105
125
  **/
106
- __init3() {this.hibernationRestored = false}
126
+ __init6() {this.hibernationRestored = false}
107
127
  // Pending request methods
108
128
  getPendingRequest(gatewayId, requestId) {
109
129
  var _a;
@@ -215,6 +235,14 @@ var RunnerActor = (_class = class {
215
235
  this.webSockets.splice(index, 1);
216
236
  }
217
237
  }
238
+ handleAckEvents(lastEventIdx) {
239
+ this.eventHistory = this.eventHistory.filter(
240
+ (event) => event.checkpoint.index > lastEventIdx
241
+ );
242
+ }
243
+ recordEvent(eventWrapper) {
244
+ this.eventHistory.push(eventWrapper);
245
+ }
218
246
  }, _class);
219
247
 
220
248
  // src/stringify.ts
@@ -233,8 +261,6 @@ function stringifyMessageId(messageId) {
233
261
  }
234
262
  function stringifyToServerTunnelMessageKind(kind) {
235
263
  switch (kind.tag) {
236
- case "DeprecatedTunnelAck":
237
- return "DeprecatedTunnelAck";
238
264
  case "ToServerResponseStart": {
239
265
  const { status, headers, body, stream } = kind.val;
240
266
  const bodyStr = body === null ? "null" : stringifyArrayBuffer(body);
@@ -268,8 +294,6 @@ function stringifyToServerTunnelMessageKind(kind) {
268
294
  }
269
295
  function stringifyToClientTunnelMessageKind(kind) {
270
296
  switch (kind.tag) {
271
- case "DeprecatedTunnelAck":
272
- return "DeprecatedTunnelAck";
273
297
  case "ToClientRequestStart": {
274
298
  const { actorId, method, path, headers, body, stream } = kind.val;
275
299
  const bodyStr = body === null ? "null" : stringifyArrayBuffer(body);
@@ -300,30 +324,29 @@ function stringifyToClientTunnelMessageKind(kind) {
300
324
  function stringifyCommand(command) {
301
325
  switch (command.tag) {
302
326
  case "CommandStartActor": {
303
- const { actorId, generation, config, hibernatingRequests } = command.val;
327
+ const { config, hibernatingRequests } = command.val;
304
328
  const keyStr = config.key === null ? "null" : `"${config.key}"`;
305
329
  const inputStr = config.input === null ? "null" : stringifyArrayBuffer(config.input);
306
330
  const hibernatingRequestsStr = hibernatingRequests.length > 0 ? `[${hibernatingRequests.map((hr) => `{gatewayId: ${idToStr(hr.gatewayId)}, requestId: ${idToStr(hr.requestId)}}`).join(", ")}]` : "[]";
307
- return `CommandStartActor{actorId: "${actorId}", generation: ${generation}, config: {name: "${config.name}", key: ${keyStr}, createTs: ${stringifyBigInt(config.createTs)}, input: ${inputStr}}, hibernatingRequests: ${hibernatingRequestsStr}}`;
331
+ return `CommandStartActor{config: {name: "${config.name}", key: ${keyStr}, createTs: ${stringifyBigInt(config.createTs)}, input: ${inputStr}}, hibernatingRequests: ${hibernatingRequestsStr}}`;
308
332
  }
309
333
  case "CommandStopActor": {
310
- const { actorId, generation } = command.val;
311
- return `CommandStopActor{actorId: "${actorId}", generation: ${generation}}`;
334
+ return `CommandStopActor`;
312
335
  }
313
336
  }
314
337
  }
315
338
  function stringifyCommandWrapper(wrapper) {
316
- return `CommandWrapper{index: ${stringifyBigInt(wrapper.index)}, inner: ${stringifyCommand(wrapper.inner)}}`;
339
+ return `CommandWrapper{actorId: "${wrapper.checkpoint.actorId}", generation: "${wrapper.checkpoint.generation}", index: ${stringifyBigInt(wrapper.checkpoint.index)}, inner: ${stringifyCommand(wrapper.inner)}}`;
317
340
  }
318
341
  function stringifyEvent(event) {
319
342
  switch (event.tag) {
320
343
  case "EventActorIntent": {
321
- const { actorId, generation, intent } = event.val;
344
+ const { intent } = event.val;
322
345
  const intentStr = intent.tag === "ActorIntentSleep" ? "Sleep" : intent.tag === "ActorIntentStop" ? "Stop" : "Unknown";
323
- return `EventActorIntent{actorId: "${actorId}", generation: ${generation}, intent: ${intentStr}}`;
346
+ return `EventActorIntent{intent: ${intentStr}}`;
324
347
  }
325
348
  case "EventActorStateUpdate": {
326
- const { actorId, generation, state } = event.val;
349
+ const { state } = event.val;
327
350
  let stateStr;
328
351
  if (state.tag === "ActorStateRunning") {
329
352
  stateStr = "Running";
@@ -334,17 +357,17 @@ function stringifyEvent(event) {
334
357
  } else {
335
358
  stateStr = "Unknown";
336
359
  }
337
- return `EventActorStateUpdate{actorId: "${actorId}", generation: ${generation}, state: ${stateStr}}`;
360
+ return `EventActorStateUpdate{state: ${stateStr}}`;
338
361
  }
339
362
  case "EventActorSetAlarm": {
340
- const { actorId, generation, alarmTs } = event.val;
363
+ const { alarmTs } = event.val;
341
364
  const alarmTsStr = alarmTs === null ? "null" : stringifyBigInt(alarmTs);
342
- return `EventActorSetAlarm{actorId: "${actorId}", generation: ${generation}, alarmTs: ${alarmTsStr}}`;
365
+ return `EventActorSetAlarm{alarmTs: ${alarmTsStr}}`;
343
366
  }
344
367
  }
345
368
  }
346
369
  function stringifyEventWrapper(wrapper) {
347
- return `EventWrapper{index: ${stringifyBigInt(wrapper.index)}, inner: ${stringifyEvent(wrapper.inner)}}`;
370
+ return `EventWrapper{actorId: ${wrapper.checkpoint.actorId}, generation: "${wrapper.checkpoint.generation}", index: ${stringifyBigInt(wrapper.checkpoint.index)}, inner: ${stringifyEvent(wrapper.inner)}}`;
348
371
  }
349
372
  function stringifyToServer(message) {
350
373
  switch (message.tag) {
@@ -353,28 +376,27 @@ function stringifyToServer(message) {
353
376
  name,
354
377
  version,
355
378
  totalSlots,
356
- lastCommandIdx,
357
379
  prepopulateActorNames,
358
380
  metadata
359
381
  } = message.val;
360
- const lastCommandIdxStr = lastCommandIdx === null ? "null" : stringifyBigInt(lastCommandIdx);
361
382
  const prepopulateActorNamesStr = prepopulateActorNames === null ? "null" : `Map(${prepopulateActorNames.size})`;
362
383
  const metadataStr = metadata === null ? "null" : `"${metadata}"`;
363
- return `ToServerInit{name: "${name}", version: ${version}, totalSlots: ${totalSlots}, lastCommandIdx: ${lastCommandIdxStr}, prepopulateActorNames: ${prepopulateActorNamesStr}, metadata: ${metadataStr}}`;
384
+ return `ToServerInit{name: "${name}", version: ${version}, totalSlots: ${totalSlots}, prepopulateActorNames: ${prepopulateActorNamesStr}, metadata: ${metadataStr}}`;
364
385
  }
365
386
  case "ToServerEvents": {
366
387
  const events = message.val;
367
388
  return `ToServerEvents{count: ${events.length}, events: [${events.map((e) => stringifyEventWrapper(e)).join(", ")}]}`;
368
389
  }
369
390
  case "ToServerAckCommands": {
370
- const { lastCommandIdx } = message.val;
371
- return `ToServerAckCommands{lastCommandIdx: ${stringifyBigInt(lastCommandIdx)}}`;
391
+ const { lastCommandCheckpoints } = message.val;
392
+ const checkpointsStr = lastCommandCheckpoints.length > 0 ? `[${lastCommandCheckpoints.map((cp) => `{actorId: "${cp.actorId}", index: ${stringifyBigInt(cp.index)}}`).join(", ")}]` : "[]";
393
+ return `ToServerAckCommands{lastCommandCheckpoints: ${checkpointsStr}}`;
372
394
  }
373
395
  case "ToServerStopping":
374
396
  return "ToServerStopping";
375
- case "ToServerPing": {
397
+ case "ToServerPong": {
376
398
  const { ts } = message.val;
377
- return `ToServerPing{ts: ${stringifyBigInt(ts)}}`;
399
+ return `ToServerPong{ts: ${stringifyBigInt(ts)}}`;
378
400
  }
379
401
  case "ToServerKvRequest": {
380
402
  const { actorId, requestId, data } = message.val;
@@ -390,19 +412,21 @@ function stringifyToServer(message) {
390
412
  function stringifyToClient(message) {
391
413
  switch (message.tag) {
392
414
  case "ToClientInit": {
393
- const { runnerId, lastEventIdx, metadata } = message.val;
415
+ const { runnerId, metadata } = message.val;
394
416
  const metadataStr = `{runnerLostThreshold: ${stringifyBigInt(metadata.runnerLostThreshold)}}`;
395
- return `ToClientInit{runnerId: "${runnerId}", lastEventIdx: ${stringifyBigInt(lastEventIdx)}, metadata: ${metadataStr}}`;
417
+ return `ToClientInit{runnerId: "${runnerId}", metadata: ${metadataStr}}`;
396
418
  }
397
- case "ToClientClose":
398
- return "ToClientClose";
419
+ case "ToClientPing":
420
+ const { ts } = message.val;
421
+ return `ToClientPing{ts: ${stringifyBigInt(ts)}}`;
399
422
  case "ToClientCommands": {
400
423
  const commands = message.val;
401
424
  return `ToClientCommands{count: ${commands.length}, commands: [${commands.map((c) => stringifyCommandWrapper(c)).join(", ")}]}`;
402
425
  }
403
426
  case "ToClientAckEvents": {
404
- const { lastEventIdx } = message.val;
405
- return `ToClientAckEvents{lastEventIdx: ${stringifyBigInt(lastEventIdx)}}`;
427
+ const { lastEventCheckpoints } = message.val;
428
+ const checkpointsStr = lastEventCheckpoints.length > 0 ? `[${lastEventCheckpoints.map((cp) => `{actorId: "${cp.actorId}", index: ${stringifyBigInt(cp.index)}}`).join(", ")}]` : "[]";
429
+ return `ToClientAckEvents{lastEventCheckpoints: ${checkpointsStr}}`;
406
430
  }
407
431
  case "ToClientKvResponse": {
408
432
  const { requestId, data } = message.val;
@@ -479,7 +503,7 @@ function stringifyKvResponseData(data) {
479
503
  // src/websocket-tunnel-adapter.ts
480
504
  var HIBERNATABLE_SYMBOL = Symbol("hibernatable");
481
505
  var WebSocketTunnelAdapter = (_class2 = class {
482
- constructor(tunnel, actorId, requestId, serverMessageIndex, hibernatable, isRestoringHibernatable, request, sendCallback, closeCallback) {;_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);
506
+ constructor(tunnel, actorId, requestId, serverMessageIndex, hibernatable, isRestoringHibernatable, request, sendCallback, closeCallback) {;_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);
483
507
  this.request = request;
484
508
  var _a;
485
509
  this.#tunnel = tunnel;
@@ -866,10 +890,10 @@ var WebSocketTunnelAdapter = (_class2 = class {
866
890
  static __initStatic2() {this.OPEN = 1}
867
891
  static __initStatic3() {this.CLOSING = 2}
868
892
  static __initStatic4() {this.CLOSED = 3}
869
- __init4() {this.CONNECTING = 0}
870
- __init5() {this.OPEN = 1}
871
- __init6() {this.CLOSING = 2}
872
- __init7() {this.CLOSED = 3}
893
+ __init7() {this.CONNECTING = 0}
894
+ __init8() {this.OPEN = 1}
895
+ __init9() {this.CLOSING = 2}
896
+ __init10() {this.CLOSED = 3}
873
897
  // Additional methods for compatibility
874
898
  ping(data, mask, cb) {
875
899
  if (cb) cb(new Error("Ping not supported in tunnel adapter"));
@@ -893,11 +917,6 @@ var WebSocketTunnelAdapter = (_class2 = class {
893
917
  }, _class2.__initStatic(), _class2.__initStatic2(), _class2.__initStatic3(), _class2.__initStatic4(), _class2);
894
918
 
895
919
  // src/tunnel.ts
896
- var RunnerShutdownError = class extends Error {
897
- constructor() {
898
- super("Runner shut down");
899
- }
900
- };
901
920
  var Tunnel = class {
902
921
  #runner;
903
922
  /** Maps request IDs to actor IDs for lookup */
@@ -1019,7 +1038,7 @@ var Tunnel = class {
1019
1038
  (_a2 = this.log) == null ? void 0 : _a2.error({
1020
1039
  msg: "error creating websocket during restore",
1021
1040
  requestId: requestIdStr,
1022
- err
1041
+ error: stringifyError(err)
1023
1042
  });
1024
1043
  this.#sendMessage(gatewayId, requestId, {
1025
1044
  tag: "ToServerWebSocketClose",
@@ -1068,7 +1087,7 @@ var Tunnel = class {
1068
1087
  (_a2 = this.log) == null ? void 0 : _a2.error({
1069
1088
  msg: "error creating stale websocket during restore",
1070
1089
  requestId: requestIdStr,
1071
- err
1090
+ error: stringifyError(err)
1072
1091
  });
1073
1092
  });
1074
1093
  backgroundOperations.push(cleanupOperation);
@@ -1348,8 +1367,6 @@ var Tunnel = class {
1348
1367
  message.messageKind.val
1349
1368
  );
1350
1369
  break;
1351
- case "DeprecatedTunnelAck":
1352
- break;
1353
1370
  default:
1354
1371
  unreachable(message.messageKind);
1355
1372
  }
@@ -1720,7 +1737,7 @@ async function importWebSocket() {
1720
1737
  const ws = await Promise.resolve().then(() => _interopRequireWildcard(require("ws")));
1721
1738
  _WebSocket = ws.default;
1722
1739
  (_b = logger()) == null ? void 0 : _b.debug({ msg: "using websocket from npm" });
1723
- } catch (e2) {
1740
+ } catch (e3) {
1724
1741
  _WebSocket = class MockWebSocket {
1725
1742
  constructor() {
1726
1743
  throw new Error(
@@ -1738,10 +1755,14 @@ async function importWebSocket() {
1738
1755
 
1739
1756
  // src/mod.ts
1740
1757
  var KV_EXPIRE = 3e4;
1741
- var PROTOCOL_VERSION = 3;
1742
- var RUNNER_PING_INTERVAL = 3e3;
1758
+ var PROTOCOL_VERSION = 5;
1743
1759
  var EVENT_BACKLOG_WARN_THRESHOLD = 1e4;
1744
1760
  var SIGNAL_HANDLERS = [];
1761
+ var RunnerShutdownError = class extends Error {
1762
+ constructor() {
1763
+ super("Runner shut down");
1764
+ }
1765
+ };
1745
1766
  var Runner = class {
1746
1767
  #config;
1747
1768
  get config() {
@@ -1751,9 +1772,6 @@ var Runner = class {
1751
1772
  // WebSocket
1752
1773
 
1753
1774
 
1754
- #lastCommandIdx = -1;
1755
- #pingLoop;
1756
- #nextEventIdx = 0n;
1757
1775
  #started = false;
1758
1776
  #shutdown = false;
1759
1777
  #shuttingDown = false;
@@ -1763,7 +1781,6 @@ var Runner = class {
1763
1781
  #runnerLostThreshold;
1764
1782
  #runnerLostTimeout;
1765
1783
  // Event storage for resending
1766
- #eventHistory = [];
1767
1784
  #eventBacklogWarned = false;
1768
1785
  // Command acknowledgment
1769
1786
  #ackInterval;
@@ -1794,7 +1811,15 @@ var Runner = class {
1794
1811
  this.#config = config;
1795
1812
  if (this.#config.logger) setLogger(this.#config.logger);
1796
1813
  this.#kvCleanupInterval = setInterval(() => {
1797
- this.#cleanupOldKvRequests();
1814
+ var _a;
1815
+ try {
1816
+ this.#cleanupOldKvRequests();
1817
+ } catch (err) {
1818
+ (_a = this.log) == null ? void 0 : _a.error({
1819
+ msg: "error cleaning up kv requests",
1820
+ error: stringifyError(err)
1821
+ });
1822
+ }
1798
1823
  }, 15e3);
1799
1824
  }
1800
1825
  // MARK: Manage actors
@@ -1809,7 +1834,11 @@ var Runner = class {
1809
1834
  this.#sendActorIntent(actorId, actor.generation, "stop");
1810
1835
  }
1811
1836
  async forceStopActor(actorId, generation) {
1812
- var _a;
1837
+ var _a, _b;
1838
+ (_a = this.log) == null ? void 0 : _a.debug({
1839
+ msg: "force stopping actor",
1840
+ actorId
1841
+ });
1813
1842
  const actor = this.getActor(actorId, generation);
1814
1843
  if (!actor) return;
1815
1844
  try {
@@ -1817,18 +1846,32 @@ var Runner = class {
1817
1846
  } catch (err) {
1818
1847
  console.error(`Error in onActorStop for actor ${actorId}:`, err);
1819
1848
  }
1820
- (_a = this.#tunnel) == null ? void 0 : _a.closeActiveRequests(actor);
1821
- this.#removeActor(actorId, generation);
1849
+ (_b = this.#tunnel) == null ? void 0 : _b.closeActiveRequests(actor);
1822
1850
  this.#sendActorStateUpdate(actorId, actor.generation, "stopped");
1851
+ this.#removeActor(actorId, generation);
1823
1852
  }
1824
- #stopAllActors() {
1853
+ #handleLost() {
1825
1854
  var _a;
1826
1855
  (_a = this.log) == null ? void 0 : _a.info({
1827
- msg: "stopping all actors due to runner lost threshold exceeded"
1856
+ msg: "stopping all actors due to runner lost threshold"
1828
1857
  });
1858
+ for (const [_, request] of this.#kvRequests.entries()) {
1859
+ request.reject(new RunnerShutdownError());
1860
+ }
1861
+ this.#kvRequests.clear();
1862
+ this.#stopAllActors();
1863
+ }
1864
+ #stopAllActors() {
1829
1865
  const actorIds = Array.from(this.#actors.keys());
1830
1866
  for (const actorId of actorIds) {
1831
- this.forceStopActor(actorId);
1867
+ this.forceStopActor(actorId).catch((err) => {
1868
+ var _a;
1869
+ (_a = this.log) == null ? void 0 : _a.error({
1870
+ msg: "error stopping actor",
1871
+ actorId,
1872
+ error: stringifyError(err)
1873
+ });
1874
+ });
1832
1875
  }
1833
1876
  }
1834
1877
  getActor(actorId, generation) {
@@ -1956,10 +1999,6 @@ var Runner = class {
1956
1999
  clearTimeout(this.#runnerLostTimeout);
1957
2000
  this.#runnerLostTimeout = void 0;
1958
2001
  }
1959
- if (this.#pingLoop) {
1960
- clearInterval(this.#pingLoop);
1961
- this.#pingLoop = void 0;
1962
- }
1963
2002
  if (this.#ackInterval) {
1964
2003
  clearInterval(this.#ackInterval);
1965
2004
  this.#ackInterval = void 0;
@@ -2167,7 +2206,6 @@ var Runner = class {
2167
2206
  name: this.#config.runnerName,
2168
2207
  version: this.#config.version,
2169
2208
  totalSlots: this.#config.totalSlots,
2170
- lastCommandIdx: this.#lastCommandIdx >= 0 ? BigInt(this.#lastCommandIdx) : null,
2171
2209
  prepopulateActorNames: new Map(
2172
2210
  Object.entries(this.#config.prepopulateActorNames).map(
2173
2211
  ([name, data]) => [
@@ -2182,39 +2220,29 @@ var Runner = class {
2182
2220
  tag: "ToServerInit",
2183
2221
  val: init
2184
2222
  });
2185
- const pingLoop = setInterval(() => {
2186
- var _a3;
2187
- if (ws.readyState === 1) {
2188
- this.__sendToServer({
2189
- tag: "ToServerPing",
2190
- val: {
2191
- ts: BigInt(Date.now())
2192
- }
2193
- });
2194
- } else {
2195
- clearInterval(pingLoop);
2196
- (_a3 = this.log) == null ? void 0 : _a3.info({
2197
- msg: "WebSocket not open, stopping ping loop"
2198
- });
2199
- }
2200
- }, RUNNER_PING_INTERVAL);
2201
- this.#pingLoop = pingLoop;
2202
2223
  const ackInterval = 5 * 60 * 1e3;
2203
2224
  const ackLoop = setInterval(() => {
2204
- var _a3;
2205
- if (ws.readyState === 1) {
2206
- this.#sendCommandAcknowledgment();
2207
- } else {
2208
- clearInterval(ackLoop);
2209
- (_a3 = this.log) == null ? void 0 : _a3.info({
2210
- msg: "WebSocket not open, stopping ack loop"
2225
+ var _a3, _b2;
2226
+ try {
2227
+ if (ws.readyState === 1) {
2228
+ this.#sendCommandAcknowledgment();
2229
+ } else {
2230
+ clearInterval(ackLoop);
2231
+ (_a3 = this.log) == null ? void 0 : _a3.info({
2232
+ msg: "WebSocket not open, stopping ack loop"
2233
+ });
2234
+ }
2235
+ } catch (err) {
2236
+ (_b2 = this.log) == null ? void 0 : _b2.error({
2237
+ msg: "error in command acknowledgment loop",
2238
+ error: stringifyError(err)
2211
2239
  });
2212
2240
  }
2213
2241
  }, ackInterval);
2214
2242
  this.#ackInterval = ackLoop;
2215
2243
  });
2216
2244
  ws.addEventListener("message", async (ev) => {
2217
- var _a2, _b, _c, _d, _e, _f;
2245
+ var _a2, _b, _c, _d, _e;
2218
2246
  let buf;
2219
2247
  if (ev.data instanceof Blob) {
2220
2248
  buf = new Uint8Array(await ev.data.arrayBuffer());
@@ -2232,16 +2260,15 @@ var Runner = class {
2232
2260
  const init = message.val;
2233
2261
  if (this.runnerId !== init.runnerId) {
2234
2262
  this.runnerId = init.runnerId;
2235
- this.#eventHistory.length = 0;
2263
+ this.#stopAllActors();
2236
2264
  }
2237
2265
  this.#runnerLostThreshold = ((_b = init.metadata) == null ? void 0 : _b.runnerLostThreshold) ? Number(init.metadata.runnerLostThreshold) : void 0;
2238
2266
  (_c = this.log) == null ? void 0 : _c.info({
2239
2267
  msg: "received init",
2240
- lastEventIdx: init.lastEventIdx,
2241
2268
  runnerLostThreshold: this.#runnerLostThreshold
2242
2269
  });
2243
2270
  this.#processUnsentKvRequests();
2244
- this.#resendUnacknowledgedEvents(init.lastEventIdx);
2271
+ this.#resendUnacknowledgedEvents();
2245
2272
  (_d = this.#tunnel) == null ? void 0 : _d.resendBufferedEvents();
2246
2273
  this.#config.onConnected();
2247
2274
  } else if (message.tag === "ToClientCommands") {
@@ -2253,10 +2280,20 @@ var Runner = class {
2253
2280
  const kvResponse = message.val;
2254
2281
  this.#handleKvResponse(kvResponse);
2255
2282
  } else if (message.tag === "ToClientTunnelMessage") {
2256
- (_e = this.#tunnel) == null ? void 0 : _e.handleTunnelMessage(message.val);
2257
- } else if (message.tag === "ToClientClose") {
2258
- (_f = this.#tunnel) == null ? void 0 : _f.shutdown();
2259
- ws.close(1e3, "manual closure");
2283
+ (_e = this.#tunnel) == null ? void 0 : _e.handleTunnelMessage(message.val).catch((err) => {
2284
+ var _a3;
2285
+ (_a3 = this.log) == null ? void 0 : _a3.error({
2286
+ msg: "error handling tunnel message",
2287
+ error: stringifyError(err)
2288
+ });
2289
+ });
2290
+ } else if (message.tag === "ToClientPing") {
2291
+ this.__sendToServer({
2292
+ tag: "ToServerPong",
2293
+ val: {
2294
+ ts: message.val.ts
2295
+ }
2296
+ });
2260
2297
  } else {
2261
2298
  unreachable(message);
2262
2299
  }
@@ -2273,7 +2310,15 @@ var Runner = class {
2273
2310
  seconds: this.#runnerLostThreshold / 1e3
2274
2311
  });
2275
2312
  this.#runnerLostTimeout = setTimeout(() => {
2276
- this.#stopAllActors();
2313
+ var _a3;
2314
+ try {
2315
+ this.#handleLost();
2316
+ } catch (err) {
2317
+ (_a3 = this.log) == null ? void 0 : _a3.error({
2318
+ msg: "error handling runner lost",
2319
+ error: stringifyError(err)
2320
+ });
2321
+ }
2277
2322
  }, this.#runnerLostThreshold);
2278
2323
  }
2279
2324
  this.#scheduleReconnect();
@@ -2299,10 +2344,6 @@ var Runner = class {
2299
2344
  }
2300
2345
  this.#config.onDisconnected(ev.code, ev.reason);
2301
2346
  }
2302
- if (this.#pingLoop) {
2303
- clearInterval(this.#pingLoop);
2304
- this.#pingLoop = void 0;
2305
- }
2306
2347
  if (this.#ackInterval) {
2307
2348
  clearInterval(this.#ackInterval);
2308
2349
  this.#ackInterval = void 0;
@@ -2314,7 +2355,15 @@ var Runner = class {
2314
2355
  seconds: this.#runnerLostThreshold / 1e3
2315
2356
  });
2316
2357
  this.#runnerLostTimeout = setTimeout(() => {
2317
- this.#stopAllActors();
2358
+ var _a3;
2359
+ try {
2360
+ this.#handleLost();
2361
+ } catch (err) {
2362
+ (_a3 = this.log) == null ? void 0 : _a3.error({
2363
+ msg: "error handling runner lost",
2364
+ error: stringifyError(err)
2365
+ });
2366
+ }
2318
2367
  }, this.#runnerLostThreshold);
2319
2368
  }
2320
2369
  this.#scheduleReconnect();
@@ -2329,43 +2378,75 @@ var Runner = class {
2329
2378
  });
2330
2379
  for (const commandWrapper of commands) {
2331
2380
  if (commandWrapper.inner.tag === "CommandStartActor") {
2332
- this.#handleCommandStartActor(commandWrapper);
2381
+ this.#handleCommandStartActor(commandWrapper).catch((err) => {
2382
+ var _a2;
2383
+ (_a2 = this.log) == null ? void 0 : _a2.error({
2384
+ msg: "error handling start actor command",
2385
+ actorId: commandWrapper.checkpoint.actorId,
2386
+ error: stringifyError(err)
2387
+ });
2388
+ });
2389
+ const actor = this.getActor(
2390
+ commandWrapper.checkpoint.actorId,
2391
+ commandWrapper.checkpoint.generation
2392
+ );
2393
+ if (actor) actor.lastCommandIdx = commandWrapper.checkpoint.index;
2333
2394
  } else if (commandWrapper.inner.tag === "CommandStopActor") {
2334
- this.#handleCommandStopActor(commandWrapper);
2395
+ this.#handleCommandStopActor(commandWrapper).catch((err) => {
2396
+ var _a2;
2397
+ (_a2 = this.log) == null ? void 0 : _a2.error({
2398
+ msg: "error handling stop actor command",
2399
+ actorId: commandWrapper.checkpoint.actorId,
2400
+ error: stringifyError(err)
2401
+ });
2402
+ });
2335
2403
  } else {
2336
2404
  unreachable(commandWrapper.inner);
2337
2405
  }
2338
- this.#lastCommandIdx = Number(commandWrapper.index);
2339
2406
  }
2340
2407
  }
2341
2408
  #handleAckEvents(ack) {
2342
2409
  var _a;
2343
- const lastAckedIdx = ack.lastEventIdx;
2344
- const originalLength = this.#eventHistory.length;
2345
- this.#eventHistory = this.#eventHistory.filter(
2346
- (event) => event.index > lastAckedIdx
2410
+ let originalTotalEvents = Array.from(this.#actors).reduce(
2411
+ (s, [_, actor]) => s + actor.eventHistory.length,
2412
+ 0
2347
2413
  );
2348
- const prunedCount = originalLength - this.#eventHistory.length;
2414
+ for (const [_, actor] of this.#actors) {
2415
+ let checkpoint = ack.lastEventCheckpoints.find(
2416
+ (x) => x.actorId == actor.actorId
2417
+ );
2418
+ if (checkpoint) actor.handleAckEvents(checkpoint.index);
2419
+ }
2420
+ const totalEvents = Array.from(this.#actors).reduce(
2421
+ (s, [_, actor]) => s + actor.eventHistory.length,
2422
+ 0
2423
+ );
2424
+ const prunedCount = originalTotalEvents - totalEvents;
2349
2425
  if (prunedCount > 0) {
2350
2426
  (_a = this.log) == null ? void 0 : _a.info({
2351
2427
  msg: "pruned acknowledged events",
2352
- lastAckedIdx: lastAckedIdx.toString(),
2353
2428
  prunedCount
2354
2429
  });
2355
2430
  }
2356
- if (this.#eventHistory.length <= EVENT_BACKLOG_WARN_THRESHOLD) {
2431
+ if (totalEvents <= EVENT_BACKLOG_WARN_THRESHOLD) {
2357
2432
  this.#eventBacklogWarned = false;
2358
2433
  }
2359
2434
  }
2360
2435
  /** Track events to send to the server in case we need to resend it on disconnect. */
2361
2436
  #recordEvent(eventWrapper) {
2362
2437
  var _a;
2363
- this.#eventHistory.push(eventWrapper);
2364
- if (this.#eventHistory.length > EVENT_BACKLOG_WARN_THRESHOLD && !this.#eventBacklogWarned) {
2438
+ const actor = this.getActor(eventWrapper.checkpoint.actorId);
2439
+ if (!actor) return;
2440
+ actor.recordEvent(eventWrapper);
2441
+ let totalEvents = Array.from(this.#actors).reduce(
2442
+ (s, [_, actor2]) => s + actor2.eventHistory.length,
2443
+ 0
2444
+ );
2445
+ if (totalEvents > EVENT_BACKLOG_WARN_THRESHOLD && !this.#eventBacklogWarned) {
2365
2446
  this.#eventBacklogWarned = true;
2366
2447
  (_a = this.log) == null ? void 0 : _a.warn({
2367
2448
  msg: "unacknowledged event backlog exceeds threshold",
2368
- backlogSize: this.#eventHistory.length,
2449
+ backlogSize: totalEvents,
2369
2450
  threshold: EVENT_BACKLOG_WARN_THRESHOLD
2370
2451
  });
2371
2452
  }
@@ -2374,8 +2455,8 @@ var Runner = class {
2374
2455
  var _a, _b, _c, _d;
2375
2456
  if (!this.#tunnel) throw new Error("missing tunnel on actor start");
2376
2457
  const startCommand = commandWrapper.inner.val;
2377
- const actorId = startCommand.actorId;
2378
- const generation = startCommand.generation;
2458
+ const actorId = commandWrapper.checkpoint.actorId;
2459
+ const generation = commandWrapper.checkpoint.generation;
2379
2460
  const config = startCommand.config;
2380
2461
  const actorConfig = {
2381
2462
  name: config.name,
@@ -2433,11 +2514,13 @@ var Runner = class {
2433
2514
  }
2434
2515
  async #handleCommandStopActor(commandWrapper) {
2435
2516
  const stopCommand = commandWrapper.inner.val;
2436
- const actorId = stopCommand.actorId;
2437
- const generation = stopCommand.generation;
2517
+ const actorId = commandWrapper.checkpoint.actorId;
2518
+ const generation = commandWrapper.checkpoint.generation;
2438
2519
  await this.forceStopActor(actorId, generation);
2439
2520
  }
2440
2521
  #sendActorIntent(actorId, generation, intentType) {
2522
+ const actor = this.getActor(actorId, generation);
2523
+ if (!actor) return;
2441
2524
  let actorIntent;
2442
2525
  if (intentType === "sleep") {
2443
2526
  actorIntent = { tag: "ActorIntentSleep", val: null };
@@ -2450,13 +2533,14 @@ var Runner = class {
2450
2533
  unreachable(intentType);
2451
2534
  }
2452
2535
  const intentEvent = {
2453
- actorId,
2454
- generation,
2455
2536
  intent: actorIntent
2456
2537
  };
2457
- const eventIndex = this.#nextEventIdx++;
2458
2538
  const eventWrapper = {
2459
- index: eventIndex,
2539
+ checkpoint: {
2540
+ actorId,
2541
+ generation,
2542
+ index: actor.nextEventIdx++
2543
+ },
2460
2544
  inner: {
2461
2545
  tag: "EventActorIntent",
2462
2546
  val: intentEvent
@@ -2469,6 +2553,8 @@ var Runner = class {
2469
2553
  });
2470
2554
  }
2471
2555
  #sendActorStateUpdate(actorId, generation, stateType) {
2556
+ const actor = this.getActor(actorId, generation);
2557
+ if (!actor) return;
2472
2558
  let actorState;
2473
2559
  if (stateType === "running") {
2474
2560
  actorState = { tag: "ActorStateRunning", val: null };
@@ -2484,13 +2570,14 @@ var Runner = class {
2484
2570
  unreachable(stateType);
2485
2571
  }
2486
2572
  const stateUpdateEvent = {
2487
- actorId,
2488
- generation,
2489
2573
  state: actorState
2490
2574
  };
2491
- const eventIndex = this.#nextEventIdx++;
2492
2575
  const eventWrapper = {
2493
- index: eventIndex,
2576
+ checkpoint: {
2577
+ actorId,
2578
+ generation,
2579
+ index: actor.nextEventIdx++
2580
+ },
2494
2581
  inner: {
2495
2582
  tag: "EventActorStateUpdate",
2496
2583
  val: stateUpdateEvent
@@ -2503,13 +2590,21 @@ var Runner = class {
2503
2590
  });
2504
2591
  }
2505
2592
  #sendCommandAcknowledgment() {
2506
- if (this.#lastCommandIdx < 0) {
2507
- return;
2593
+ const lastCommandCheckpoints = [];
2594
+ for (const [_, actor] of this.#actors) {
2595
+ if (actor.lastCommandIdx < 0) {
2596
+ continue;
2597
+ }
2598
+ lastCommandCheckpoints.push({
2599
+ actorId: actor.actorId,
2600
+ generation: actor.generation,
2601
+ index: actor.lastCommandIdx
2602
+ });
2508
2603
  }
2509
2604
  this.__sendToServer({
2510
2605
  tag: "ToServerAckCommands",
2511
2606
  val: {
2512
- lastCommandIdx: BigInt(this.#lastCommandIdx)
2607
+ lastCommandCheckpoints
2513
2608
  }
2514
2609
  });
2515
2610
  }
@@ -2739,13 +2834,14 @@ var Runner = class {
2739
2834
  const actor = this.getActor(actorId, generation);
2740
2835
  if (!actor) return;
2741
2836
  const alarmEvent = {
2742
- actorId,
2743
- generation: actor.generation,
2744
2837
  alarmTs: alarmTs !== null ? BigInt(alarmTs) : null
2745
2838
  };
2746
- const eventIndex = this.#nextEventIdx++;
2747
2839
  const eventWrapper = {
2748
- index: eventIndex,
2840
+ checkpoint: {
2841
+ actorId,
2842
+ generation: actor.generation,
2843
+ index: actor.nextEventIdx++
2844
+ },
2749
2845
  inner: {
2750
2846
  tag: "EventActorSetAlarm",
2751
2847
  val: alarmEvent
@@ -2872,7 +2968,8 @@ var Runner = class {
2872
2968
  const data = protocol.encodeToServerlessServer({
2873
2969
  tag: "ToServerlessServerInit",
2874
2970
  val: {
2875
- runnerId: this.runnerId
2971
+ runnerId: this.runnerId,
2972
+ runnerProtocolVersion: PROTOCOL_VERSION
2876
2973
  }
2877
2974
  });
2878
2975
  const buffer = Buffer.alloc(data.length + 2);
@@ -2897,26 +2994,33 @@ var Runner = class {
2897
2994
  (_b = this.log) == null ? void 0 : _b.debug({
2898
2995
  msg: `Scheduling reconnect attempt ${this.#reconnectAttempt + 1} in ${delay}ms`
2899
2996
  });
2900
- this.#reconnectTimeout = setTimeout(async () => {
2997
+ this.#reconnectTimeout = setTimeout(() => {
2901
2998
  var _a2;
2902
2999
  if (!this.#shutdown) {
2903
3000
  this.#reconnectAttempt++;
2904
3001
  (_a2 = this.log) == null ? void 0 : _a2.debug({
2905
3002
  msg: `Attempting to reconnect (attempt ${this.#reconnectAttempt})...`
2906
3003
  });
2907
- await this.#openPegboardWebSocket();
3004
+ this.#openPegboardWebSocket().catch((err) => {
3005
+ var _a3;
3006
+ (_a3 = this.log) == null ? void 0 : _a3.error({
3007
+ msg: "error during websocket reconnection",
3008
+ error: stringifyError(err)
3009
+ });
3010
+ });
2908
3011
  }
2909
3012
  }, delay);
2910
3013
  }
2911
- #resendUnacknowledgedEvents(lastEventIdx) {
3014
+ #resendUnacknowledgedEvents() {
2912
3015
  var _a;
2913
- const eventsToResend = this.#eventHistory.filter(
2914
- (event) => event.index > lastEventIdx
2915
- );
3016
+ const eventsToResend = [];
3017
+ for (const [_, actor] of this.#actors) {
3018
+ eventsToResend.push(...actor.eventHistory);
3019
+ }
2916
3020
  if (eventsToResend.length === 0) return;
2917
3021
  (_a = this.log) == null ? void 0 : _a.info({
2918
3022
  msg: "resending unacknowledged events",
2919
- fromIndex: lastEventIdx + 1n
3023
+ count: eventsToResend.length
2920
3024
  });
2921
3025
  this.__sendToServer({
2922
3026
  tag: "ToServerEvents",
@@ -2947,5 +3051,6 @@ var Runner = class {
2947
3051
 
2948
3052
 
2949
3053
 
2950
- exports.Runner = Runner; exports.RunnerActor = RunnerActor; exports.idToStr = idToStr;
3054
+
3055
+ exports.Runner = Runner; exports.RunnerActor = RunnerActor; exports.RunnerShutdownError = RunnerShutdownError; exports.idToStr = idToStr;
2951
3056
  //# sourceMappingURL=mod.cjs.map