@rivetkit/engine-runner 2.0.21

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.js ADDED
@@ -0,0 +1,2027 @@
1
+ // src/mod.ts
2
+ import * as protocol from "@rivetkit/engine-runner-protocol";
3
+
4
+ // src/log.ts
5
+ var LOGGER;
6
+ function setLogger(logger2) {
7
+ LOGGER = logger2;
8
+ }
9
+ function logger() {
10
+ return LOGGER;
11
+ }
12
+
13
+ // src/tunnel.ts
14
+ import { v4 as uuidv4 } from "uuid";
15
+
16
+ // src/utils.ts
17
+ function unreachable(x) {
18
+ throw `Unreachable: ${x}`;
19
+ }
20
+ function calculateBackoff(attempt, options = {}) {
21
+ const {
22
+ initialDelay = 1e3,
23
+ maxDelay = 3e4,
24
+ multiplier = 2,
25
+ jitter = true
26
+ } = options;
27
+ let delay = Math.min(initialDelay * multiplier ** attempt, maxDelay);
28
+ if (jitter) {
29
+ delay = delay * (1 + Math.random() * 0.25);
30
+ }
31
+ return Math.floor(delay);
32
+ }
33
+
34
+ // src/websocket-tunnel-adapter.ts
35
+ var WebSocketTunnelAdapter = class {
36
+ #webSocketId;
37
+ #readyState = 0;
38
+ // CONNECTING
39
+ #eventListeners = /* @__PURE__ */ new Map();
40
+ #onopen = null;
41
+ #onclose = null;
42
+ #onerror = null;
43
+ #onmessage = null;
44
+ #bufferedAmount = 0;
45
+ #binaryType = "nodebuffer";
46
+ #extensions = "";
47
+ #protocol = "";
48
+ #url = "";
49
+ #sendCallback;
50
+ #closeCallback;
51
+ // Event buffering for events fired before listeners are attached
52
+ #bufferedEvents = [];
53
+ constructor(webSocketId, sendCallback, closeCallback) {
54
+ this.#webSocketId = webSocketId;
55
+ this.#sendCallback = sendCallback;
56
+ this.#closeCallback = closeCallback;
57
+ }
58
+ get readyState() {
59
+ return this.#readyState;
60
+ }
61
+ get bufferedAmount() {
62
+ return this.#bufferedAmount;
63
+ }
64
+ get binaryType() {
65
+ return this.#binaryType;
66
+ }
67
+ set binaryType(value) {
68
+ if (value === "nodebuffer" || value === "arraybuffer" || value === "blob") {
69
+ this.#binaryType = value;
70
+ }
71
+ }
72
+ get extensions() {
73
+ return this.#extensions;
74
+ }
75
+ get protocol() {
76
+ return this.#protocol;
77
+ }
78
+ get url() {
79
+ return this.#url;
80
+ }
81
+ get onopen() {
82
+ return this.#onopen;
83
+ }
84
+ set onopen(value) {
85
+ this.#onopen = value;
86
+ if (value) {
87
+ this.#flushBufferedEvents("open");
88
+ }
89
+ }
90
+ get onclose() {
91
+ return this.#onclose;
92
+ }
93
+ set onclose(value) {
94
+ this.#onclose = value;
95
+ if (value) {
96
+ this.#flushBufferedEvents("close");
97
+ }
98
+ }
99
+ get onerror() {
100
+ return this.#onerror;
101
+ }
102
+ set onerror(value) {
103
+ this.#onerror = value;
104
+ if (value) {
105
+ this.#flushBufferedEvents("error");
106
+ }
107
+ }
108
+ get onmessage() {
109
+ return this.#onmessage;
110
+ }
111
+ set onmessage(value) {
112
+ this.#onmessage = value;
113
+ if (value) {
114
+ this.#flushBufferedEvents("message");
115
+ }
116
+ }
117
+ send(data) {
118
+ if (this.#readyState !== 1) {
119
+ throw new Error("WebSocket is not open");
120
+ }
121
+ let isBinary = false;
122
+ let messageData;
123
+ if (typeof data === "string") {
124
+ messageData = data;
125
+ } else if (data instanceof ArrayBuffer) {
126
+ isBinary = true;
127
+ messageData = data;
128
+ } else if (ArrayBuffer.isView(data)) {
129
+ isBinary = true;
130
+ const view = data;
131
+ if (view.buffer instanceof SharedArrayBuffer) {
132
+ const bytes = new Uint8Array(
133
+ view.buffer,
134
+ view.byteOffset,
135
+ view.byteLength
136
+ );
137
+ messageData = bytes.buffer.slice(
138
+ bytes.byteOffset,
139
+ bytes.byteOffset + bytes.byteLength
140
+ );
141
+ } else {
142
+ messageData = view.buffer.slice(
143
+ view.byteOffset,
144
+ view.byteOffset + view.byteLength
145
+ );
146
+ }
147
+ } else if (data instanceof Blob) {
148
+ throw new Error("Blob sending not implemented in tunnel adapter");
149
+ } else if (typeof Buffer !== "undefined" && Buffer.isBuffer(data)) {
150
+ isBinary = true;
151
+ const buf = data;
152
+ if (buf.buffer instanceof SharedArrayBuffer) {
153
+ const bytes = new Uint8Array(
154
+ buf.buffer,
155
+ buf.byteOffset,
156
+ buf.byteLength
157
+ );
158
+ messageData = bytes.buffer.slice(
159
+ bytes.byteOffset,
160
+ bytes.byteOffset + bytes.byteLength
161
+ );
162
+ } else {
163
+ messageData = buf.buffer.slice(
164
+ buf.byteOffset,
165
+ buf.byteOffset + buf.byteLength
166
+ );
167
+ }
168
+ } else {
169
+ throw new Error("Invalid data type");
170
+ }
171
+ this.#sendCallback(messageData, isBinary);
172
+ }
173
+ close(code, reason) {
174
+ if (this.#readyState === 2 || // CLOSING
175
+ this.#readyState === 3) {
176
+ return;
177
+ }
178
+ this.#readyState = 2;
179
+ this.#closeCallback(code, reason);
180
+ this.#readyState = 3;
181
+ const closeEvent = {
182
+ wasClean: true,
183
+ code: code || 1e3,
184
+ reason: reason || "",
185
+ type: "close",
186
+ target: this
187
+ };
188
+ this.#fireEvent("close", closeEvent);
189
+ }
190
+ addEventListener(type, listener, options) {
191
+ if (typeof listener === "function") {
192
+ let listeners = this.#eventListeners.get(type);
193
+ if (!listeners) {
194
+ listeners = /* @__PURE__ */ new Set();
195
+ this.#eventListeners.set(type, listeners);
196
+ }
197
+ listeners.add(listener);
198
+ this.#flushBufferedEvents(type);
199
+ }
200
+ }
201
+ removeEventListener(type, listener, options) {
202
+ if (typeof listener === "function") {
203
+ const listeners = this.#eventListeners.get(type);
204
+ if (listeners) {
205
+ listeners.delete(listener);
206
+ }
207
+ }
208
+ }
209
+ dispatchEvent(event) {
210
+ return true;
211
+ }
212
+ #fireEvent(type, event) {
213
+ var _a, _b, _c, _d, _e;
214
+ const listeners = this.#eventListeners.get(type);
215
+ let hasListeners = false;
216
+ if (listeners && listeners.size > 0) {
217
+ hasListeners = true;
218
+ for (const listener of listeners) {
219
+ try {
220
+ listener.call(this, event);
221
+ } catch (error) {
222
+ (_a = logger()) == null ? void 0 : _a.error({
223
+ msg: "error in websocket event listener",
224
+ error,
225
+ type
226
+ });
227
+ }
228
+ }
229
+ }
230
+ switch (type) {
231
+ case "open":
232
+ if (this.#onopen) {
233
+ hasListeners = true;
234
+ try {
235
+ this.#onopen.call(this, event);
236
+ } catch (error) {
237
+ (_b = logger()) == null ? void 0 : _b.error({
238
+ msg: "error in onopen handler",
239
+ error
240
+ });
241
+ }
242
+ }
243
+ break;
244
+ case "close":
245
+ if (this.#onclose) {
246
+ hasListeners = true;
247
+ try {
248
+ this.#onclose.call(this, event);
249
+ } catch (error) {
250
+ (_c = logger()) == null ? void 0 : _c.error({
251
+ msg: "error in onclose handler",
252
+ error
253
+ });
254
+ }
255
+ }
256
+ break;
257
+ case "error":
258
+ if (this.#onerror) {
259
+ hasListeners = true;
260
+ try {
261
+ this.#onerror.call(this, event);
262
+ } catch (error) {
263
+ (_d = logger()) == null ? void 0 : _d.error({
264
+ msg: "error in onerror handler",
265
+ error
266
+ });
267
+ }
268
+ }
269
+ break;
270
+ case "message":
271
+ if (this.#onmessage) {
272
+ hasListeners = true;
273
+ try {
274
+ this.#onmessage.call(this, event);
275
+ } catch (error) {
276
+ (_e = logger()) == null ? void 0 : _e.error({
277
+ msg: "error in onmessage handler",
278
+ error
279
+ });
280
+ }
281
+ }
282
+ break;
283
+ }
284
+ if (!hasListeners) {
285
+ this.#bufferedEvents.push({ type, event });
286
+ }
287
+ }
288
+ #flushBufferedEvents(type) {
289
+ var _a, _b, _c, _d, _e;
290
+ const eventsToFlush = this.#bufferedEvents.filter(
291
+ (buffered) => buffered.type === type
292
+ );
293
+ this.#bufferedEvents = this.#bufferedEvents.filter(
294
+ (buffered) => buffered.type !== type
295
+ );
296
+ for (const { event } of eventsToFlush) {
297
+ const listeners = this.#eventListeners.get(type);
298
+ if (listeners) {
299
+ for (const listener of listeners) {
300
+ try {
301
+ listener.call(this, event);
302
+ } catch (error) {
303
+ (_a = logger()) == null ? void 0 : _a.error({
304
+ msg: "error in websocket event listener",
305
+ error,
306
+ type
307
+ });
308
+ }
309
+ }
310
+ }
311
+ switch (type) {
312
+ case "open":
313
+ if (this.#onopen) {
314
+ try {
315
+ this.#onopen.call(this, event);
316
+ } catch (error) {
317
+ (_b = logger()) == null ? void 0 : _b.error({
318
+ msg: "error in onopen handler",
319
+ error
320
+ });
321
+ }
322
+ }
323
+ break;
324
+ case "close":
325
+ if (this.#onclose) {
326
+ try {
327
+ this.#onclose.call(this, event);
328
+ } catch (error) {
329
+ (_c = logger()) == null ? void 0 : _c.error({
330
+ msg: "error in onclose handler",
331
+ error
332
+ });
333
+ }
334
+ }
335
+ break;
336
+ case "error":
337
+ if (this.#onerror) {
338
+ try {
339
+ this.#onerror.call(this, event);
340
+ } catch (error) {
341
+ (_d = logger()) == null ? void 0 : _d.error({
342
+ msg: "error in onerror handler",
343
+ error
344
+ });
345
+ }
346
+ }
347
+ break;
348
+ case "message":
349
+ if (this.#onmessage) {
350
+ try {
351
+ this.#onmessage.call(this, event);
352
+ } catch (error) {
353
+ (_e = logger()) == null ? void 0 : _e.error({
354
+ msg: "error in onmessage handler",
355
+ error
356
+ });
357
+ }
358
+ }
359
+ break;
360
+ }
361
+ }
362
+ }
363
+ // Internal methods called by the Tunnel class
364
+ _handleOpen() {
365
+ if (this.#readyState !== 0) {
366
+ return;
367
+ }
368
+ this.#readyState = 1;
369
+ const event = {
370
+ type: "open",
371
+ target: this
372
+ };
373
+ this.#fireEvent("open", event);
374
+ }
375
+ _handleMessage(data, isBinary) {
376
+ if (this.#readyState !== 1) {
377
+ return;
378
+ }
379
+ let messageData;
380
+ if (isBinary) {
381
+ if (this.#binaryType === "nodebuffer") {
382
+ messageData = Buffer.from(data);
383
+ } else if (this.#binaryType === "arraybuffer") {
384
+ if (data instanceof Uint8Array) {
385
+ messageData = data.buffer.slice(
386
+ data.byteOffset,
387
+ data.byteOffset + data.byteLength
388
+ );
389
+ } else {
390
+ messageData = data;
391
+ }
392
+ } else {
393
+ throw new Error(
394
+ "Blob binaryType not supported in tunnel adapter"
395
+ );
396
+ }
397
+ } else {
398
+ messageData = data;
399
+ }
400
+ const event = {
401
+ data: messageData,
402
+ type: "message",
403
+ target: this
404
+ };
405
+ this.#fireEvent("message", event);
406
+ }
407
+ _handleClose(code, reason) {
408
+ if (this.#readyState === 3) {
409
+ return;
410
+ }
411
+ this.#readyState = 3;
412
+ const event = {
413
+ wasClean: true,
414
+ code: code || 1e3,
415
+ reason: reason || "",
416
+ type: "close",
417
+ target: this
418
+ };
419
+ this.#fireEvent("close", event);
420
+ }
421
+ _handleError(error) {
422
+ const event = {
423
+ type: "error",
424
+ target: this,
425
+ error
426
+ };
427
+ this.#fireEvent("error", event);
428
+ }
429
+ // WebSocket constants for compatibility
430
+ static CONNECTING = 0;
431
+ static OPEN = 1;
432
+ static CLOSING = 2;
433
+ static CLOSED = 3;
434
+ // Instance constants
435
+ CONNECTING = 0;
436
+ OPEN = 1;
437
+ CLOSING = 2;
438
+ CLOSED = 3;
439
+ // Additional methods for compatibility
440
+ ping(data, mask, cb) {
441
+ if (cb) cb(new Error("Ping not supported in tunnel adapter"));
442
+ }
443
+ pong(data, mask, cb) {
444
+ if (cb) cb(new Error("Pong not supported in tunnel adapter"));
445
+ }
446
+ terminate() {
447
+ this.#readyState = 3;
448
+ this.#closeCallback(1006, "Abnormal Closure");
449
+ const event = {
450
+ wasClean: false,
451
+ code: 1006,
452
+ reason: "Abnormal Closure",
453
+ type: "close",
454
+ target: this
455
+ };
456
+ this.#fireEvent("close", event);
457
+ }
458
+ };
459
+
460
+ // src/tunnel.ts
461
+ var GC_INTERVAL = 6e4;
462
+ var MESSAGE_ACK_TIMEOUT = 5e3;
463
+ var Tunnel = class {
464
+ #runner;
465
+ /** Requests over the tunnel to the actor that are in flight. */
466
+ #actorPendingRequests = /* @__PURE__ */ new Map();
467
+ /** WebSockets over the tunnel to the actor that are in flight. */
468
+ #actorWebSockets = /* @__PURE__ */ new Map();
469
+ /** Messages sent from the actor over the tunnel that have not been acked by the gateway. */
470
+ #pendingTunnelMessages = /* @__PURE__ */ new Map();
471
+ #gcInterval;
472
+ constructor(runner) {
473
+ this.#runner = runner;
474
+ }
475
+ start() {
476
+ this.#startGarbageCollector();
477
+ }
478
+ shutdown() {
479
+ if (this.#gcInterval) {
480
+ clearInterval(this.#gcInterval);
481
+ this.#gcInterval = void 0;
482
+ }
483
+ for (const [_, request] of this.#actorPendingRequests) {
484
+ request.reject(new Error("Tunnel shutting down"));
485
+ }
486
+ this.#actorPendingRequests.clear();
487
+ for (const [_, ws] of this.#actorWebSockets) {
488
+ ws.close();
489
+ }
490
+ this.#actorWebSockets.clear();
491
+ }
492
+ #sendMessage(requestId, messageKind) {
493
+ var _a;
494
+ if (!this.#runner.__webSocketReady()) {
495
+ (_a = logger()) == null ? void 0 : _a.warn(
496
+ "cannot send tunnel message, socket not connected to engine"
497
+ );
498
+ return;
499
+ }
500
+ const messageId = generateUuidBuffer();
501
+ const requestIdStr = bufferToString(requestId);
502
+ this.#pendingTunnelMessages.set(bufferToString(messageId), {
503
+ sentAt: Date.now(),
504
+ requestIdStr
505
+ });
506
+ const message = {
507
+ tag: "ToServerTunnelMessage",
508
+ val: {
509
+ requestId,
510
+ messageId,
511
+ messageKind
512
+ }
513
+ };
514
+ this.#runner.__sendToServer(message);
515
+ }
516
+ #sendAck(requestId, messageId) {
517
+ if (!this.#runner.__webSocketReady()) {
518
+ return;
519
+ }
520
+ const message = {
521
+ tag: "ToServerTunnelMessage",
522
+ val: {
523
+ requestId,
524
+ messageId,
525
+ messageKind: { tag: "TunnelAck", val: null }
526
+ }
527
+ };
528
+ this.#runner.__sendToServer(message);
529
+ }
530
+ #startGarbageCollector() {
531
+ if (this.#gcInterval) {
532
+ clearInterval(this.#gcInterval);
533
+ }
534
+ this.#gcInterval = setInterval(() => {
535
+ this.#gc();
536
+ }, GC_INTERVAL);
537
+ }
538
+ #gc() {
539
+ var _a;
540
+ const now = Date.now();
541
+ const messagesToDelete = [];
542
+ for (const [messageId, pendingMessage] of this.#pendingTunnelMessages) {
543
+ if (now - pendingMessage.sentAt > MESSAGE_ACK_TIMEOUT) {
544
+ messagesToDelete.push(messageId);
545
+ const requestIdStr = pendingMessage.requestIdStr;
546
+ const pendingRequest = this.#actorPendingRequests.get(requestIdStr);
547
+ if (pendingRequest) {
548
+ pendingRequest.reject(
549
+ new Error("Message acknowledgment timeout")
550
+ );
551
+ if (pendingRequest.streamController) {
552
+ pendingRequest.streamController.error(
553
+ new Error("Message acknowledgment timeout")
554
+ );
555
+ }
556
+ this.#actorPendingRequests.delete(requestIdStr);
557
+ }
558
+ const webSocket = this.#actorWebSockets.get(requestIdStr);
559
+ if (webSocket) {
560
+ webSocket.close(1e3, "Message acknowledgment timeout");
561
+ this.#actorWebSockets.delete(requestIdStr);
562
+ }
563
+ }
564
+ }
565
+ if (messagesToDelete.length > 0) {
566
+ (_a = logger()) == null ? void 0 : _a.warn({
567
+ msg: "purging unacked tunnel messages, this indicates that the Rivet Engine is disconnected or not responding",
568
+ count: messagesToDelete.length
569
+ });
570
+ for (const messageId of messagesToDelete) {
571
+ this.#pendingTunnelMessages.delete(messageId);
572
+ }
573
+ }
574
+ }
575
+ unregisterActor(actor) {
576
+ const actorId = actor.actorId;
577
+ for (const requestId of actor.requests) {
578
+ const pending = this.#actorPendingRequests.get(requestId);
579
+ if (pending) {
580
+ pending.reject(new Error(`Actor ${actorId} stopped`));
581
+ this.#actorPendingRequests.delete(requestId);
582
+ }
583
+ }
584
+ actor.requests.clear();
585
+ for (const webSocketId of actor.webSockets) {
586
+ const ws = this.#actorWebSockets.get(webSocketId);
587
+ if (ws) {
588
+ ws.close(1e3, "Actor stopped");
589
+ this.#actorWebSockets.delete(webSocketId);
590
+ }
591
+ }
592
+ actor.webSockets.clear();
593
+ }
594
+ async #fetch(actorId, request) {
595
+ var _a;
596
+ if (!this.#runner.hasActor(actorId)) {
597
+ (_a = logger()) == null ? void 0 : _a.warn({
598
+ msg: "ignoring request for unknown actor",
599
+ actorId
600
+ });
601
+ return new Response("Actor not found", { status: 404 });
602
+ }
603
+ const fetchHandler = this.#runner.config.fetch(
604
+ this.#runner,
605
+ actorId,
606
+ request
607
+ );
608
+ if (!fetchHandler) {
609
+ return new Response("Not Implemented", { status: 501 });
610
+ }
611
+ return fetchHandler;
612
+ }
613
+ async handleTunnelMessage(message) {
614
+ if (message.messageKind.tag === "TunnelAck") {
615
+ const msgIdStr = bufferToString(message.messageId);
616
+ const pending = this.#pendingTunnelMessages.get(msgIdStr);
617
+ if (pending) {
618
+ this.#pendingTunnelMessages.delete(msgIdStr);
619
+ }
620
+ } else {
621
+ this.#sendAck(message.requestId, message.messageId);
622
+ switch (message.messageKind.tag) {
623
+ case "ToClientRequestStart":
624
+ await this.#handleRequestStart(
625
+ message.requestId,
626
+ message.messageKind.val
627
+ );
628
+ break;
629
+ case "ToClientRequestChunk":
630
+ await this.#handleRequestChunk(
631
+ message.requestId,
632
+ message.messageKind.val
633
+ );
634
+ break;
635
+ case "ToClientRequestAbort":
636
+ await this.#handleRequestAbort(message.requestId);
637
+ break;
638
+ case "ToClientWebSocketOpen":
639
+ await this.#handleWebSocketOpen(
640
+ message.requestId,
641
+ message.messageKind.val
642
+ );
643
+ break;
644
+ case "ToClientWebSocketMessage":
645
+ await this.#handleWebSocketMessage(
646
+ message.requestId,
647
+ message.messageKind.val
648
+ );
649
+ break;
650
+ case "ToClientWebSocketClose":
651
+ await this.#handleWebSocketClose(
652
+ message.requestId,
653
+ message.messageKind.val
654
+ );
655
+ break;
656
+ default:
657
+ unreachable(message.messageKind);
658
+ }
659
+ }
660
+ }
661
+ async #handleRequestStart(requestId, req) {
662
+ var _a;
663
+ const requestIdStr = bufferToString(requestId);
664
+ const actor = this.#runner.getActor(req.actorId);
665
+ if (actor) {
666
+ actor.requests.add(requestIdStr);
667
+ }
668
+ try {
669
+ const headers = new Headers();
670
+ for (const [key, value] of req.headers) {
671
+ headers.append(key, value);
672
+ }
673
+ const request = new Request(`http://localhost${req.path}`, {
674
+ method: req.method,
675
+ headers,
676
+ body: req.body ? new Uint8Array(req.body) : void 0
677
+ });
678
+ if (req.stream) {
679
+ const stream = new ReadableStream({
680
+ start: (controller) => {
681
+ const existing = this.#actorPendingRequests.get(requestIdStr);
682
+ if (existing) {
683
+ existing.streamController = controller;
684
+ existing.actorId = req.actorId;
685
+ } else {
686
+ this.#actorPendingRequests.set(requestIdStr, {
687
+ resolve: () => {
688
+ },
689
+ reject: () => {
690
+ },
691
+ streamController: controller,
692
+ actorId: req.actorId
693
+ });
694
+ }
695
+ }
696
+ });
697
+ const streamingRequest = new Request(request, {
698
+ body: stream,
699
+ duplex: "half"
700
+ });
701
+ const response = await this.#fetch(
702
+ req.actorId,
703
+ streamingRequest
704
+ );
705
+ await this.#sendResponse(requestId, response);
706
+ } else {
707
+ const response = await this.#fetch(req.actorId, request);
708
+ await this.#sendResponse(requestId, response);
709
+ }
710
+ } catch (error) {
711
+ (_a = logger()) == null ? void 0 : _a.error({ msg: "error handling request", error });
712
+ this.#sendResponseError(requestId, 500, "Internal Server Error");
713
+ } finally {
714
+ const actor2 = this.#runner.getActor(req.actorId);
715
+ if (actor2) {
716
+ actor2.requests.delete(requestIdStr);
717
+ }
718
+ }
719
+ }
720
+ async #handleRequestChunk(requestId, chunk) {
721
+ const requestIdStr = bufferToString(requestId);
722
+ const pending = this.#actorPendingRequests.get(requestIdStr);
723
+ if (pending == null ? void 0 : pending.streamController) {
724
+ pending.streamController.enqueue(new Uint8Array(chunk.body));
725
+ if (chunk.finish) {
726
+ pending.streamController.close();
727
+ this.#actorPendingRequests.delete(requestIdStr);
728
+ }
729
+ }
730
+ }
731
+ async #handleRequestAbort(requestId) {
732
+ const requestIdStr = bufferToString(requestId);
733
+ const pending = this.#actorPendingRequests.get(requestIdStr);
734
+ if (pending == null ? void 0 : pending.streamController) {
735
+ pending.streamController.error(new Error("Request aborted"));
736
+ }
737
+ this.#actorPendingRequests.delete(requestIdStr);
738
+ }
739
+ async #sendResponse(requestId, response) {
740
+ const body = response.body ? await response.arrayBuffer() : null;
741
+ const headers = /* @__PURE__ */ new Map();
742
+ response.headers.forEach((value, key) => {
743
+ headers.set(key, value);
744
+ });
745
+ if (body && !headers.has("content-length")) {
746
+ headers.set("content-length", String(body.byteLength));
747
+ }
748
+ this.#sendMessage(requestId, {
749
+ tag: "ToServerResponseStart",
750
+ val: {
751
+ status: response.status,
752
+ headers,
753
+ body: body || null,
754
+ stream: false
755
+ }
756
+ });
757
+ }
758
+ #sendResponseError(requestId, status, message) {
759
+ const headers = /* @__PURE__ */ new Map();
760
+ headers.set("content-type", "text/plain");
761
+ this.#sendMessage(requestId, {
762
+ tag: "ToServerResponseStart",
763
+ val: {
764
+ status,
765
+ headers,
766
+ body: new TextEncoder().encode(message).buffer,
767
+ stream: false
768
+ }
769
+ });
770
+ }
771
+ async #handleWebSocketOpen(requestId, open) {
772
+ var _a, _b, _c;
773
+ const webSocketId = bufferToString(requestId);
774
+ const actor = this.#runner.getActor(open.actorId);
775
+ if (!actor) {
776
+ (_a = logger()) == null ? void 0 : _a.warn({
777
+ msg: "ignoring websocket for unknown actor",
778
+ actorId: open.actorId
779
+ });
780
+ this.#sendMessage(requestId, {
781
+ tag: "ToServerWebSocketClose",
782
+ val: {
783
+ code: 1011,
784
+ reason: "Actor not found"
785
+ }
786
+ });
787
+ return;
788
+ }
789
+ const websocketHandler = this.#runner.config.websocket;
790
+ if (!websocketHandler) {
791
+ (_b = logger()) == null ? void 0 : _b.error({
792
+ msg: "no websocket handler configured for tunnel"
793
+ });
794
+ this.#sendMessage(requestId, {
795
+ tag: "ToServerWebSocketClose",
796
+ val: {
797
+ code: 1011,
798
+ reason: "Not Implemented"
799
+ }
800
+ });
801
+ return;
802
+ }
803
+ if (actor) {
804
+ actor.webSockets.add(webSocketId);
805
+ }
806
+ try {
807
+ const adapter = new WebSocketTunnelAdapter(
808
+ webSocketId,
809
+ (data, isBinary) => {
810
+ const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data).buffer : data;
811
+ this.#sendMessage(requestId, {
812
+ tag: "ToServerWebSocketMessage",
813
+ val: {
814
+ data: dataBuffer,
815
+ binary: isBinary
816
+ }
817
+ });
818
+ },
819
+ (code, reason) => {
820
+ this.#sendMessage(requestId, {
821
+ tag: "ToServerWebSocketClose",
822
+ val: {
823
+ code: code || null,
824
+ reason: reason || null
825
+ }
826
+ });
827
+ this.#actorWebSockets.delete(webSocketId);
828
+ if (actor) {
829
+ actor.webSockets.delete(webSocketId);
830
+ }
831
+ }
832
+ );
833
+ this.#actorWebSockets.set(webSocketId, adapter);
834
+ this.#sendMessage(requestId, {
835
+ tag: "ToServerWebSocketOpen",
836
+ val: null
837
+ });
838
+ adapter._handleOpen();
839
+ const headerInit = {};
840
+ if (open.headers) {
841
+ for (const [k, v] of open.headers) {
842
+ headerInit[k] = v;
843
+ }
844
+ }
845
+ headerInit["Upgrade"] = "websocket";
846
+ headerInit["Connection"] = "Upgrade";
847
+ const request = new Request(`http://localhost${open.path}`, {
848
+ method: "GET",
849
+ headers: headerInit
850
+ });
851
+ await websocketHandler(
852
+ this.#runner,
853
+ open.actorId,
854
+ adapter,
855
+ request
856
+ );
857
+ } catch (error) {
858
+ (_c = logger()) == null ? void 0 : _c.error({ msg: "error handling websocket open", error });
859
+ this.#sendMessage(requestId, {
860
+ tag: "ToServerWebSocketClose",
861
+ val: {
862
+ code: 1011,
863
+ reason: "Server Error"
864
+ }
865
+ });
866
+ this.#actorWebSockets.delete(webSocketId);
867
+ if (actor) {
868
+ actor.webSockets.delete(webSocketId);
869
+ }
870
+ }
871
+ }
872
+ async #handleWebSocketMessage(requestId, msg) {
873
+ const webSocketId = bufferToString(requestId);
874
+ const adapter = this.#actorWebSockets.get(webSocketId);
875
+ if (adapter) {
876
+ const data = msg.binary ? new Uint8Array(msg.data) : new TextDecoder().decode(new Uint8Array(msg.data));
877
+ adapter._handleMessage(data, msg.binary);
878
+ }
879
+ }
880
+ async #handleWebSocketClose(requestId, close) {
881
+ const webSocketId = bufferToString(requestId);
882
+ const adapter = this.#actorWebSockets.get(webSocketId);
883
+ if (adapter) {
884
+ adapter._handleClose(
885
+ close.code || void 0,
886
+ close.reason || void 0
887
+ );
888
+ this.#actorWebSockets.delete(webSocketId);
889
+ }
890
+ }
891
+ };
892
+ function bufferToString(buffer) {
893
+ return Buffer.from(buffer).toString("base64");
894
+ }
895
+ function generateUuidBuffer() {
896
+ const buffer = new Uint8Array(16);
897
+ uuidv4(void 0, buffer);
898
+ return buffer.buffer;
899
+ }
900
+
901
+ // src/websocket.ts
902
+ var webSocketPromise = null;
903
+ async function importWebSocket() {
904
+ if (webSocketPromise !== null) {
905
+ return webSocketPromise;
906
+ }
907
+ webSocketPromise = (async () => {
908
+ var _a, _b, _c;
909
+ let _WebSocket;
910
+ if (typeof WebSocket !== "undefined") {
911
+ _WebSocket = WebSocket;
912
+ (_a = logger()) == null ? void 0 : _a.debug({ msg: "using native websocket" });
913
+ } else {
914
+ try {
915
+ const ws = await import("ws");
916
+ _WebSocket = ws.default;
917
+ (_b = logger()) == null ? void 0 : _b.debug({ msg: "using websocket from npm" });
918
+ } catch {
919
+ _WebSocket = class MockWebSocket {
920
+ constructor() {
921
+ throw new Error(
922
+ 'WebSocket support requires installing the "ws" peer dependency.'
923
+ );
924
+ }
925
+ };
926
+ (_c = logger()) == null ? void 0 : _c.debug({ msg: "using mock websocket" });
927
+ }
928
+ }
929
+ return _WebSocket;
930
+ })();
931
+ return webSocketPromise;
932
+ }
933
+
934
+ // src/mod.ts
935
+ var KV_EXPIRE = 3e4;
936
+ var PROTOCOL_VERSION = 1;
937
+ var EVENT_BACKLOG_WARN_THRESHOLD = 1e4;
938
+ var SIGNAL_HANDLERS = [];
939
+ var Runner = class {
940
+ #config;
941
+ get config() {
942
+ return this.#config;
943
+ }
944
+ #actors = /* @__PURE__ */ new Map();
945
+ #actorWebSockets = /* @__PURE__ */ new Map();
946
+ // WebSocket
947
+ #pegboardWebSocket;
948
+ runnerId;
949
+ #lastCommandIdx = -1;
950
+ #pingLoop;
951
+ #nextEventIdx = 0n;
952
+ #started = false;
953
+ #shutdown = false;
954
+ #reconnectAttempt = 0;
955
+ #reconnectTimeout;
956
+ // Runner lost threshold management
957
+ #runnerLostThreshold;
958
+ #runnerLostTimeout;
959
+ // Event storage for resending
960
+ #eventHistory = [];
961
+ #eventBacklogWarned = false;
962
+ // Command acknowledgment
963
+ #ackInterval;
964
+ // KV operations
965
+ #nextRequestId = 0;
966
+ #kvRequests = /* @__PURE__ */ new Map();
967
+ #kvCleanupInterval;
968
+ // Tunnel for HTTP/WebSocket forwarding
969
+ #tunnel;
970
+ constructor(config) {
971
+ this.#config = config;
972
+ if (this.#config.logger) setLogger(this.#config.logger);
973
+ this.#kvCleanupInterval = setInterval(() => {
974
+ this.#cleanupOldKvRequests();
975
+ }, 15e3);
976
+ }
977
+ // MARK: Manage actors
978
+ sleepActor(actorId, generation) {
979
+ const actor = this.getActor(actorId, generation);
980
+ if (!actor) return;
981
+ this.#sendActorIntent(actorId, actor.generation, "sleep");
982
+ }
983
+ async stopActor(actorId, generation) {
984
+ const actor = this.getActor(actorId, generation);
985
+ if (!actor) return;
986
+ this.#sendActorIntent(actorId, actor.generation, "stop");
987
+ }
988
+ async forceStopActor(actorId, generation) {
989
+ var _a;
990
+ const actor = this.#removeActor(actorId, generation);
991
+ if (!actor) return;
992
+ (_a = this.#tunnel) == null ? void 0 : _a.unregisterActor(actor);
993
+ try {
994
+ await this.#config.onActorStop(actorId, actor.generation);
995
+ } catch (err) {
996
+ console.error(`Error in onActorStop for actor ${actorId}:`, err);
997
+ }
998
+ this.#sendActorStateUpdate(actorId, actor.generation, "stopped");
999
+ this.#config.onActorStop(actorId, actor.generation).catch((err) => {
1000
+ var _a2;
1001
+ (_a2 = logger()) == null ? void 0 : _a2.error({
1002
+ msg: "error in onactorstop for actor",
1003
+ runnerId: this.runnerId,
1004
+ actorId,
1005
+ err
1006
+ });
1007
+ });
1008
+ }
1009
+ #stopAllActors() {
1010
+ var _a;
1011
+ (_a = logger()) == null ? void 0 : _a.info({
1012
+ msg: "stopping all actors due to runner lost threshold exceeded",
1013
+ runnerId: this.runnerId
1014
+ });
1015
+ const actorIds = Array.from(this.#actors.keys());
1016
+ for (const actorId of actorIds) {
1017
+ this.forceStopActor(actorId);
1018
+ }
1019
+ }
1020
+ getActor(actorId, generation) {
1021
+ var _a, _b;
1022
+ const actor = this.#actors.get(actorId);
1023
+ if (!actor) {
1024
+ (_a = logger()) == null ? void 0 : _a.error({
1025
+ msg: "actor not found",
1026
+ runnerId: this.runnerId,
1027
+ actorId
1028
+ });
1029
+ return void 0;
1030
+ }
1031
+ if (generation !== void 0 && actor.generation !== generation) {
1032
+ (_b = logger()) == null ? void 0 : _b.error({
1033
+ msg: "actor generation mismatch",
1034
+ runnerId: this.runnerId,
1035
+ actorId,
1036
+ generation
1037
+ });
1038
+ return void 0;
1039
+ }
1040
+ return actor;
1041
+ }
1042
+ hasActor(actorId, generation) {
1043
+ const actor = this.#actors.get(actorId);
1044
+ return !!actor && (generation === void 0 || actor.generation === generation);
1045
+ }
1046
+ #removeActor(actorId, generation) {
1047
+ var _a, _b, _c;
1048
+ const actor = this.#actors.get(actorId);
1049
+ if (!actor) {
1050
+ (_a = logger()) == null ? void 0 : _a.error({
1051
+ msg: "actor not found for removal",
1052
+ runnerId: this.runnerId,
1053
+ actorId
1054
+ });
1055
+ return void 0;
1056
+ }
1057
+ if (generation !== void 0 && actor.generation !== generation) {
1058
+ (_b = logger()) == null ? void 0 : _b.error({
1059
+ msg: "actor generation mismatch",
1060
+ runnerId: this.runnerId,
1061
+ actorId,
1062
+ generation
1063
+ });
1064
+ return void 0;
1065
+ }
1066
+ this.#actors.delete(actorId);
1067
+ const actorWebSockets = this.#actorWebSockets.get(actorId);
1068
+ if (actorWebSockets) {
1069
+ for (const ws of actorWebSockets) {
1070
+ try {
1071
+ ws.close(1e3, "Actor stopped");
1072
+ } catch (err) {
1073
+ (_c = logger()) == null ? void 0 : _c.error({
1074
+ msg: "error closing websocket for actor",
1075
+ runnerId: this.runnerId,
1076
+ actorId,
1077
+ err
1078
+ });
1079
+ }
1080
+ }
1081
+ this.#actorWebSockets.delete(actorId);
1082
+ }
1083
+ return actor;
1084
+ }
1085
+ // MARK: Start
1086
+ async start() {
1087
+ var _a, _b;
1088
+ if (this.#started) throw new Error("Cannot call runner.start twice");
1089
+ this.#started = true;
1090
+ (_a = logger()) == null ? void 0 : _a.info({ msg: "starting runner" });
1091
+ this.#tunnel = new Tunnel(this);
1092
+ this.#tunnel.start();
1093
+ try {
1094
+ await this.#openPegboardWebSocket();
1095
+ } catch (error) {
1096
+ this.#started = false;
1097
+ throw error;
1098
+ }
1099
+ if (!this.#config.noAutoShutdown) {
1100
+ if (!SIGNAL_HANDLERS.length) {
1101
+ process.on("SIGTERM", () => {
1102
+ var _a2;
1103
+ (_a2 = logger()) == null ? void 0 : _a2.debug("received SIGTERM");
1104
+ for (const handler of SIGNAL_HANDLERS) {
1105
+ handler();
1106
+ }
1107
+ process.exit(0);
1108
+ });
1109
+ process.on("SIGINT", () => {
1110
+ var _a2;
1111
+ (_a2 = logger()) == null ? void 0 : _a2.debug("received SIGINT");
1112
+ for (const handler of SIGNAL_HANDLERS) {
1113
+ handler();
1114
+ }
1115
+ process.exit(0);
1116
+ });
1117
+ (_b = logger()) == null ? void 0 : _b.debug({
1118
+ msg: "added SIGTERM listeners"
1119
+ });
1120
+ }
1121
+ SIGNAL_HANDLERS.push(() => {
1122
+ var _a2;
1123
+ const weak = new WeakRef(this);
1124
+ (_a2 = weak.deref()) == null ? void 0 : _a2.shutdown(false, false);
1125
+ });
1126
+ }
1127
+ }
1128
+ // MARK: Shutdown
1129
+ async shutdown(immediate, exit = false) {
1130
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1131
+ (_a = logger()) == null ? void 0 : _a.info({
1132
+ msg: "starting shutdown",
1133
+ runnerId: this.runnerId,
1134
+ immediate,
1135
+ exit
1136
+ });
1137
+ this.#shutdown = true;
1138
+ if (this.#reconnectTimeout) {
1139
+ clearTimeout(this.#reconnectTimeout);
1140
+ this.#reconnectTimeout = void 0;
1141
+ }
1142
+ if (this.#runnerLostTimeout) {
1143
+ clearTimeout(this.#runnerLostTimeout);
1144
+ this.#runnerLostTimeout = void 0;
1145
+ }
1146
+ if (this.#pingLoop) {
1147
+ clearInterval(this.#pingLoop);
1148
+ this.#pingLoop = void 0;
1149
+ }
1150
+ if (this.#ackInterval) {
1151
+ clearInterval(this.#ackInterval);
1152
+ this.#ackInterval = void 0;
1153
+ }
1154
+ if (this.#kvCleanupInterval) {
1155
+ clearInterval(this.#kvCleanupInterval);
1156
+ this.#kvCleanupInterval = void 0;
1157
+ }
1158
+ for (const request of this.#kvRequests.values()) {
1159
+ request.reject(
1160
+ new Error("WebSocket connection closed during shutdown")
1161
+ );
1162
+ }
1163
+ this.#kvRequests.clear();
1164
+ if (this.#pegboardWebSocket && this.#pegboardWebSocket.readyState === 1) {
1165
+ const pegboardWebSocket = this.#pegboardWebSocket;
1166
+ if (immediate) {
1167
+ pegboardWebSocket.close(1e3, "Stopping");
1168
+ } else {
1169
+ try {
1170
+ (_b = logger()) == null ? void 0 : _b.info({
1171
+ msg: "sending stopping message",
1172
+ runnerId: this.runnerId,
1173
+ readyState: pegboardWebSocket.readyState
1174
+ });
1175
+ const encoded = protocol.encodeToServer({
1176
+ tag: "ToServerStopping",
1177
+ val: null
1178
+ });
1179
+ if (this.#pegboardWebSocket && this.#pegboardWebSocket.readyState === 1) {
1180
+ this.#pegboardWebSocket.send(encoded);
1181
+ } else {
1182
+ (_c = logger()) == null ? void 0 : _c.error(
1183
+ "WebSocket not available or not open for sending data"
1184
+ );
1185
+ }
1186
+ const closePromise = new Promise((resolve) => {
1187
+ if (!pegboardWebSocket)
1188
+ throw new Error("missing pegboardWebSocket");
1189
+ pegboardWebSocket.addEventListener("close", (ev) => {
1190
+ var _a2;
1191
+ (_a2 = logger()) == null ? void 0 : _a2.info({
1192
+ msg: "connection closed",
1193
+ runnerId: this.runnerId,
1194
+ code: ev.code,
1195
+ reason: ev.reason.toString()
1196
+ });
1197
+ resolve();
1198
+ });
1199
+ });
1200
+ (_d = logger()) == null ? void 0 : _d.info({
1201
+ msg: "closing WebSocket",
1202
+ runnerId: this.runnerId
1203
+ });
1204
+ pegboardWebSocket.close(1e3, "Stopping");
1205
+ await closePromise;
1206
+ (_e = logger()) == null ? void 0 : _e.info({
1207
+ msg: "websocket shutdown completed",
1208
+ runnerId: this.runnerId
1209
+ });
1210
+ } catch (error) {
1211
+ (_f = logger()) == null ? void 0 : _f.error({
1212
+ msg: "error during websocket shutdown:",
1213
+ runnerId: this.runnerId,
1214
+ error
1215
+ });
1216
+ pegboardWebSocket.close();
1217
+ }
1218
+ }
1219
+ } else {
1220
+ (_h = logger()) == null ? void 0 : _h.warn({
1221
+ msg: "no runner WebSocket to shutdown or already closed",
1222
+ runnerId: this.runnerId,
1223
+ readyState: (_g = this.#pegboardWebSocket) == null ? void 0 : _g.readyState
1224
+ });
1225
+ }
1226
+ if (this.#tunnel) {
1227
+ this.#tunnel.shutdown();
1228
+ this.#tunnel = void 0;
1229
+ }
1230
+ if (exit) process.exit(0);
1231
+ this.#config.onShutdown();
1232
+ }
1233
+ // MARK: Networking
1234
+ get pegboardUrl() {
1235
+ const endpoint = this.#config.pegboardEndpoint || this.#config.endpoint;
1236
+ const wsEndpoint = endpoint.replace("http://", "ws://").replace("https://", "wss://");
1237
+ return `${wsEndpoint}?protocol_version=${PROTOCOL_VERSION}&namespace=${encodeURIComponent(this.#config.namespace)}&runner_key=${encodeURIComponent(this.#config.runnerKey)}`;
1238
+ }
1239
+ // MARK: Runner protocol
1240
+ async #openPegboardWebSocket() {
1241
+ const protocols = ["rivet", `rivet_target.runner`];
1242
+ if (this.config.token)
1243
+ protocols.push(`rivet_token.${this.config.token}`);
1244
+ const WS = await importWebSocket();
1245
+ const ws = new WS(this.pegboardUrl, protocols);
1246
+ this.#pegboardWebSocket = ws;
1247
+ ws.addEventListener("open", () => {
1248
+ var _a;
1249
+ (_a = logger()) == null ? void 0 : _a.info({ msg: "Connected" });
1250
+ this.#reconnectAttempt = 0;
1251
+ if (this.#reconnectTimeout) {
1252
+ clearTimeout(this.#reconnectTimeout);
1253
+ this.#reconnectTimeout = void 0;
1254
+ }
1255
+ if (this.#runnerLostTimeout) {
1256
+ clearTimeout(this.#runnerLostTimeout);
1257
+ this.#runnerLostTimeout = void 0;
1258
+ }
1259
+ const init = {
1260
+ name: this.#config.runnerName,
1261
+ version: this.#config.version,
1262
+ totalSlots: this.#config.totalSlots,
1263
+ lastCommandIdx: this.#lastCommandIdx >= 0 ? BigInt(this.#lastCommandIdx) : null,
1264
+ prepopulateActorNames: new Map(
1265
+ Object.entries(this.#config.prepopulateActorNames).map(
1266
+ ([name, data]) => [
1267
+ name,
1268
+ { metadata: JSON.stringify(data.metadata) }
1269
+ ]
1270
+ )
1271
+ ),
1272
+ metadata: JSON.stringify(this.#config.metadata)
1273
+ };
1274
+ this.__sendToServer({
1275
+ tag: "ToServerInit",
1276
+ val: init
1277
+ });
1278
+ this.#processUnsentKvRequests();
1279
+ const pingInterval = 1e3;
1280
+ const pingLoop = setInterval(() => {
1281
+ var _a2;
1282
+ if (ws.readyState === 1) {
1283
+ this.__sendToServer({
1284
+ tag: "ToServerPing",
1285
+ val: {
1286
+ ts: BigInt(Date.now())
1287
+ }
1288
+ });
1289
+ } else {
1290
+ clearInterval(pingLoop);
1291
+ (_a2 = logger()) == null ? void 0 : _a2.info({
1292
+ msg: "WebSocket not open, stopping ping loop",
1293
+ runnerId: this.runnerId
1294
+ });
1295
+ }
1296
+ }, pingInterval);
1297
+ this.#pingLoop = pingLoop;
1298
+ const ackInterval = 5 * 60 * 1e3;
1299
+ const ackLoop = setInterval(() => {
1300
+ var _a2;
1301
+ if (ws.readyState === 1) {
1302
+ this.#sendCommandAcknowledgment();
1303
+ } else {
1304
+ clearInterval(ackLoop);
1305
+ (_a2 = logger()) == null ? void 0 : _a2.info({
1306
+ msg: "WebSocket not open, stopping ack loop",
1307
+ runnerId: this.runnerId
1308
+ });
1309
+ }
1310
+ }, ackInterval);
1311
+ this.#ackInterval = ackLoop;
1312
+ });
1313
+ ws.addEventListener("message", async (ev) => {
1314
+ var _a, _b, _c, _d;
1315
+ let buf;
1316
+ if (ev.data instanceof Blob) {
1317
+ buf = new Uint8Array(await ev.data.arrayBuffer());
1318
+ } else if (Buffer.isBuffer(ev.data)) {
1319
+ buf = new Uint8Array(ev.data);
1320
+ } else {
1321
+ throw new Error(`expected binary data, got ${typeof ev.data}`);
1322
+ }
1323
+ const message = protocol.decodeToClient(buf);
1324
+ if (message.tag === "ToClientInit") {
1325
+ const init = message.val;
1326
+ if (this.runnerId !== init.runnerId) {
1327
+ this.runnerId = init.runnerId;
1328
+ this.#eventHistory.length = 0;
1329
+ }
1330
+ this.#runnerLostThreshold = ((_a = init.metadata) == null ? void 0 : _a.runnerLostThreshold) ? Number(init.metadata.runnerLostThreshold) : void 0;
1331
+ (_b = logger()) == null ? void 0 : _b.info({
1332
+ msg: "received init",
1333
+ runnerId: init.runnerId,
1334
+ lastEventIdx: init.lastEventIdx,
1335
+ runnerLostThreshold: this.#runnerLostThreshold
1336
+ });
1337
+ this.#resendUnacknowledgedEvents(init.lastEventIdx);
1338
+ this.#config.onConnected();
1339
+ } else if (message.tag === "ToClientCommands") {
1340
+ const commands = message.val;
1341
+ this.#handleCommands(commands);
1342
+ } else if (message.tag === "ToClientAckEvents") {
1343
+ this.#handleAckEvents(message.val);
1344
+ } else if (message.tag === "ToClientKvResponse") {
1345
+ const kvResponse = message.val;
1346
+ this.#handleKvResponse(kvResponse);
1347
+ } else if (message.tag === "ToClientTunnelMessage") {
1348
+ (_c = this.#tunnel) == null ? void 0 : _c.handleTunnelMessage(message.val);
1349
+ } else if (message.tag === "ToClientClose") {
1350
+ (_d = this.#tunnel) == null ? void 0 : _d.shutdown();
1351
+ ws.close(1e3, "manual closure");
1352
+ } else {
1353
+ unreachable(message);
1354
+ }
1355
+ });
1356
+ ws.addEventListener("error", (ev) => {
1357
+ var _a, _b;
1358
+ (_a = logger()) == null ? void 0 : _a.error({
1359
+ msg: `WebSocket error: ${ev.error}`,
1360
+ runnerId: this.runnerId
1361
+ });
1362
+ if (!this.#shutdown) {
1363
+ if (!this.#runnerLostTimeout && this.#runnerLostThreshold && this.#runnerLostThreshold > 0) {
1364
+ (_b = logger()) == null ? void 0 : _b.info({
1365
+ msg: "starting runner lost timeout",
1366
+ runnerId: this.runnerId,
1367
+ seconds: this.#runnerLostThreshold / 1e3
1368
+ });
1369
+ this.#runnerLostTimeout = setTimeout(() => {
1370
+ this.#stopAllActors();
1371
+ }, this.#runnerLostThreshold);
1372
+ }
1373
+ this.#scheduleReconnect();
1374
+ }
1375
+ });
1376
+ ws.addEventListener("close", async (ev) => {
1377
+ var _a, _b, _c;
1378
+ (_a = logger()) == null ? void 0 : _a.info({
1379
+ msg: "connection closed",
1380
+ runnerId: this.runnerId,
1381
+ code: ev.code,
1382
+ reason: ev.reason.toString()
1383
+ });
1384
+ this.#config.onDisconnected();
1385
+ if (ev.reason.toString().startsWith("ws.eviction")) {
1386
+ (_b = logger()) == null ? void 0 : _b.info({
1387
+ msg: "runner evicted",
1388
+ runnerId: this.runnerId
1389
+ });
1390
+ await this.shutdown(true);
1391
+ }
1392
+ if (this.#pingLoop) {
1393
+ clearInterval(this.#pingLoop);
1394
+ this.#pingLoop = void 0;
1395
+ }
1396
+ if (this.#ackInterval) {
1397
+ clearInterval(this.#ackInterval);
1398
+ this.#ackInterval = void 0;
1399
+ }
1400
+ if (!this.#shutdown) {
1401
+ if (!this.#runnerLostTimeout && this.#runnerLostThreshold && this.#runnerLostThreshold > 0) {
1402
+ (_c = logger()) == null ? void 0 : _c.info({
1403
+ msg: "starting runner lost timeout",
1404
+ runnerId: this.runnerId,
1405
+ seconds: this.#runnerLostThreshold / 1e3
1406
+ });
1407
+ this.#runnerLostTimeout = setTimeout(() => {
1408
+ this.#stopAllActors();
1409
+ }, this.#runnerLostThreshold);
1410
+ }
1411
+ this.#scheduleReconnect();
1412
+ }
1413
+ });
1414
+ }
1415
+ #handleCommands(commands) {
1416
+ var _a, _b;
1417
+ (_a = logger()) == null ? void 0 : _a.info({
1418
+ msg: "received commands",
1419
+ runnerId: this.runnerId,
1420
+ commandCount: commands.length
1421
+ });
1422
+ for (const commandWrapper of commands) {
1423
+ (_b = logger()) == null ? void 0 : _b.info({
1424
+ msg: "received command",
1425
+ runnerId: this.runnerId,
1426
+ commandWrapper
1427
+ });
1428
+ if (commandWrapper.inner.tag === "CommandStartActor") {
1429
+ this.#handleCommandStartActor(commandWrapper);
1430
+ } else if (commandWrapper.inner.tag === "CommandStopActor") {
1431
+ this.#handleCommandStopActor(commandWrapper);
1432
+ } else {
1433
+ unreachable(commandWrapper.inner);
1434
+ }
1435
+ this.#lastCommandIdx = Number(commandWrapper.index);
1436
+ }
1437
+ }
1438
+ #handleAckEvents(ack) {
1439
+ var _a;
1440
+ const lastAckedIdx = ack.lastEventIdx;
1441
+ const originalLength = this.#eventHistory.length;
1442
+ this.#eventHistory = this.#eventHistory.filter(
1443
+ (event) => event.index > lastAckedIdx
1444
+ );
1445
+ const prunedCount = originalLength - this.#eventHistory.length;
1446
+ if (prunedCount > 0) {
1447
+ (_a = logger()) == null ? void 0 : _a.info({
1448
+ msg: "pruned acknowledged events",
1449
+ runnerId: this.runnerId,
1450
+ lastAckedIdx: lastAckedIdx.toString(),
1451
+ prunedCount
1452
+ });
1453
+ }
1454
+ if (this.#eventHistory.length <= EVENT_BACKLOG_WARN_THRESHOLD) {
1455
+ this.#eventBacklogWarned = false;
1456
+ }
1457
+ }
1458
+ /** Track events to send to the server in case we need to resend it on disconnect. */
1459
+ #recordEvent(eventWrapper) {
1460
+ var _a;
1461
+ this.#eventHistory.push(eventWrapper);
1462
+ if (this.#eventHistory.length > EVENT_BACKLOG_WARN_THRESHOLD && !this.#eventBacklogWarned) {
1463
+ this.#eventBacklogWarned = true;
1464
+ (_a = logger()) == null ? void 0 : _a.warn({
1465
+ msg: "unacknowledged event backlog exceeds threshold",
1466
+ runnerId: this.runnerId,
1467
+ backlogSize: this.#eventHistory.length,
1468
+ threshold: EVENT_BACKLOG_WARN_THRESHOLD
1469
+ });
1470
+ }
1471
+ }
1472
+ #handleCommandStartActor(commandWrapper) {
1473
+ const startCommand = commandWrapper.inner.val;
1474
+ const actorId = startCommand.actorId;
1475
+ const generation = startCommand.generation;
1476
+ const config = startCommand.config;
1477
+ const actorConfig = {
1478
+ name: config.name,
1479
+ key: config.key,
1480
+ createTs: config.createTs,
1481
+ input: config.input ? new Uint8Array(config.input) : null
1482
+ };
1483
+ const instance = {
1484
+ actorId,
1485
+ generation,
1486
+ config: actorConfig,
1487
+ requests: /* @__PURE__ */ new Set(),
1488
+ webSockets: /* @__PURE__ */ new Set()
1489
+ };
1490
+ this.#actors.set(actorId, instance);
1491
+ this.#sendActorStateUpdate(actorId, generation, "running");
1492
+ this.#config.onActorStart(actorId, generation, actorConfig).catch((err) => {
1493
+ var _a;
1494
+ (_a = logger()) == null ? void 0 : _a.error({
1495
+ msg: "error in onactorstart for actor",
1496
+ runnerId: this.runnerId,
1497
+ actorId,
1498
+ err
1499
+ });
1500
+ this.forceStopActor(actorId, generation);
1501
+ });
1502
+ }
1503
+ #handleCommandStopActor(commandWrapper) {
1504
+ const stopCommand = commandWrapper.inner.val;
1505
+ const actorId = stopCommand.actorId;
1506
+ const generation = stopCommand.generation;
1507
+ this.forceStopActor(actorId, generation);
1508
+ }
1509
+ #sendActorIntent(actorId, generation, intentType) {
1510
+ var _a, _b;
1511
+ if (this.#shutdown) {
1512
+ (_a = logger()) == null ? void 0 : _a.warn({
1513
+ msg: "Runner is shut down, cannot send actor intent",
1514
+ runnerId: this.runnerId
1515
+ });
1516
+ return;
1517
+ }
1518
+ let actorIntent;
1519
+ if (intentType === "sleep") {
1520
+ actorIntent = { tag: "ActorIntentSleep", val: null };
1521
+ } else if (intentType === "stop") {
1522
+ actorIntent = {
1523
+ tag: "ActorIntentStop",
1524
+ val: null
1525
+ };
1526
+ } else {
1527
+ unreachable(intentType);
1528
+ }
1529
+ const intentEvent = {
1530
+ actorId,
1531
+ generation,
1532
+ intent: actorIntent
1533
+ };
1534
+ const eventIndex = this.#nextEventIdx++;
1535
+ const eventWrapper = {
1536
+ index: eventIndex,
1537
+ inner: {
1538
+ tag: "EventActorIntent",
1539
+ val: intentEvent
1540
+ }
1541
+ };
1542
+ this.#recordEvent(eventWrapper);
1543
+ (_b = logger()) == null ? void 0 : _b.info({
1544
+ msg: "sending event to server",
1545
+ runnerId: this.runnerId,
1546
+ index: eventWrapper.index,
1547
+ tag: eventWrapper.inner.tag,
1548
+ val: eventWrapper.inner.val
1549
+ });
1550
+ this.__sendToServer({
1551
+ tag: "ToServerEvents",
1552
+ val: [eventWrapper]
1553
+ });
1554
+ }
1555
+ #sendActorStateUpdate(actorId, generation, stateType) {
1556
+ var _a, _b;
1557
+ if (this.#shutdown) {
1558
+ (_a = logger()) == null ? void 0 : _a.warn({
1559
+ msg: "Runner is shut down, cannot send actor state update",
1560
+ runnerId: this.runnerId
1561
+ });
1562
+ return;
1563
+ }
1564
+ let actorState;
1565
+ if (stateType === "running") {
1566
+ actorState = { tag: "ActorStateRunning", val: null };
1567
+ } else if (stateType === "stopped") {
1568
+ actorState = {
1569
+ tag: "ActorStateStopped",
1570
+ val: {
1571
+ code: protocol.StopCode.Ok,
1572
+ message: null
1573
+ }
1574
+ };
1575
+ } else {
1576
+ unreachable(stateType);
1577
+ }
1578
+ const stateUpdateEvent = {
1579
+ actorId,
1580
+ generation,
1581
+ state: actorState
1582
+ };
1583
+ const eventIndex = this.#nextEventIdx++;
1584
+ const eventWrapper = {
1585
+ index: eventIndex,
1586
+ inner: {
1587
+ tag: "EventActorStateUpdate",
1588
+ val: stateUpdateEvent
1589
+ }
1590
+ };
1591
+ this.#recordEvent(eventWrapper);
1592
+ (_b = logger()) == null ? void 0 : _b.info({
1593
+ msg: "sending event to server",
1594
+ runnerId: this.runnerId,
1595
+ index: eventWrapper.index,
1596
+ tag: eventWrapper.inner.tag,
1597
+ val: eventWrapper.inner.val
1598
+ });
1599
+ this.__sendToServer({
1600
+ tag: "ToServerEvents",
1601
+ val: [eventWrapper]
1602
+ });
1603
+ }
1604
+ #sendCommandAcknowledgment() {
1605
+ var _a;
1606
+ if (this.#shutdown) {
1607
+ (_a = logger()) == null ? void 0 : _a.warn({
1608
+ msg: "Runner is shut down, cannot send command acknowledgment",
1609
+ runnerId: this.runnerId
1610
+ });
1611
+ return;
1612
+ }
1613
+ if (this.#lastCommandIdx < 0) {
1614
+ return;
1615
+ }
1616
+ this.__sendToServer({
1617
+ tag: "ToServerAckCommands",
1618
+ val: {
1619
+ lastCommandIdx: BigInt(this.#lastCommandIdx)
1620
+ }
1621
+ });
1622
+ }
1623
+ #handleKvResponse(response) {
1624
+ var _a;
1625
+ const requestId = response.requestId;
1626
+ const request = this.#kvRequests.get(requestId);
1627
+ if (!request) {
1628
+ const msg = "received kv response for unknown request id";
1629
+ (_a = logger()) == null ? void 0 : _a.error({ msg, runnerId: this.runnerId, requestId });
1630
+ return;
1631
+ }
1632
+ this.#kvRequests.delete(requestId);
1633
+ if (response.data.tag === "KvErrorResponse") {
1634
+ request.reject(
1635
+ new Error(response.data.val.message || "Unknown KV error")
1636
+ );
1637
+ } else {
1638
+ request.resolve(response.data.val);
1639
+ }
1640
+ }
1641
+ #parseGetResponseSimple(response, requestedKeys) {
1642
+ const responseKeys = [];
1643
+ const responseValues = [];
1644
+ for (const key of response.keys) {
1645
+ responseKeys.push(new Uint8Array(key));
1646
+ }
1647
+ for (const value of response.values) {
1648
+ responseValues.push(new Uint8Array(value));
1649
+ }
1650
+ const result = [];
1651
+ for (const requestedKey of requestedKeys) {
1652
+ let found = false;
1653
+ for (let i = 0; i < responseKeys.length; i++) {
1654
+ if (this.#keysEqual(requestedKey, responseKeys[i])) {
1655
+ result.push(responseValues[i]);
1656
+ found = true;
1657
+ break;
1658
+ }
1659
+ }
1660
+ if (!found) {
1661
+ result.push(null);
1662
+ }
1663
+ }
1664
+ return result;
1665
+ }
1666
+ #keysEqual(key1, key2) {
1667
+ if (key1.length !== key2.length) return false;
1668
+ for (let i = 0; i < key1.length; i++) {
1669
+ if (key1[i] !== key2[i]) return false;
1670
+ }
1671
+ return true;
1672
+ }
1673
+ //#parseGetResponse(response: protocol.KvGetResponse) {
1674
+ // const keys: string[] = [];
1675
+ // const values: Uint8Array[] = [];
1676
+ // const metadata: { version: Uint8Array; createTs: bigint }[] = [];
1677
+ //
1678
+ // for (const key of response.keys) {
1679
+ // keys.push(new TextDecoder().decode(key));
1680
+ // }
1681
+ //
1682
+ // for (const value of response.values) {
1683
+ // values.push(new Uint8Array(value));
1684
+ // }
1685
+ //
1686
+ // for (const meta of response.metadata) {
1687
+ // metadata.push({
1688
+ // version: new Uint8Array(meta.version),
1689
+ // createTs: meta.createTs,
1690
+ // });
1691
+ // }
1692
+ //
1693
+ // return { keys, values, metadata };
1694
+ //}
1695
+ #parseListResponseSimple(response) {
1696
+ const result = [];
1697
+ for (let i = 0; i < response.keys.length; i++) {
1698
+ const key = response.keys[i];
1699
+ const value = response.values[i];
1700
+ if (key && value) {
1701
+ const keyBytes = new Uint8Array(key);
1702
+ const valueBytes = new Uint8Array(value);
1703
+ result.push([keyBytes, valueBytes]);
1704
+ }
1705
+ }
1706
+ return result;
1707
+ }
1708
+ //#parseListResponse(response: protocol.KvListResponse) {
1709
+ // const keys: string[] = [];
1710
+ // const values: Uint8Array[] = [];
1711
+ // const metadata: { version: Uint8Array; createTs: bigint }[] = [];
1712
+ //
1713
+ // for (const key of response.keys) {
1714
+ // keys.push(new TextDecoder().decode(key));
1715
+ // }
1716
+ //
1717
+ // for (const value of response.values) {
1718
+ // values.push(new Uint8Array(value));
1719
+ // }
1720
+ //
1721
+ // for (const meta of response.metadata) {
1722
+ // metadata.push({
1723
+ // version: new Uint8Array(meta.version),
1724
+ // createTs: meta.createTs,
1725
+ // });
1726
+ // }
1727
+ //
1728
+ // return { keys, values, metadata };
1729
+ //}
1730
+ // MARK: KV Operations
1731
+ async kvGet(actorId, keys) {
1732
+ const kvKeys = keys.map(
1733
+ (key) => key.buffer.slice(
1734
+ key.byteOffset,
1735
+ key.byteOffset + key.byteLength
1736
+ )
1737
+ );
1738
+ const requestData = {
1739
+ tag: "KvGetRequest",
1740
+ val: { keys: kvKeys }
1741
+ };
1742
+ const response = await this.#sendKvRequest(actorId, requestData);
1743
+ return this.#parseGetResponseSimple(response, keys);
1744
+ }
1745
+ async kvListAll(actorId, options) {
1746
+ const requestData = {
1747
+ tag: "KvListRequest",
1748
+ val: {
1749
+ query: { tag: "KvListAllQuery", val: null },
1750
+ reverse: (options == null ? void 0 : options.reverse) || null,
1751
+ limit: (options == null ? void 0 : options.limit) !== void 0 ? BigInt(options.limit) : null
1752
+ }
1753
+ };
1754
+ const response = await this.#sendKvRequest(actorId, requestData);
1755
+ return this.#parseListResponseSimple(response);
1756
+ }
1757
+ async kvListRange(actorId, start, end, exclusive, options) {
1758
+ const startKey = start.buffer.slice(
1759
+ start.byteOffset,
1760
+ start.byteOffset + start.byteLength
1761
+ );
1762
+ const endKey = end.buffer.slice(
1763
+ end.byteOffset,
1764
+ end.byteOffset + end.byteLength
1765
+ );
1766
+ const requestData = {
1767
+ tag: "KvListRequest",
1768
+ val: {
1769
+ query: {
1770
+ tag: "KvListRangeQuery",
1771
+ val: {
1772
+ start: startKey,
1773
+ end: endKey,
1774
+ exclusive: exclusive || false
1775
+ }
1776
+ },
1777
+ reverse: (options == null ? void 0 : options.reverse) || null,
1778
+ limit: (options == null ? void 0 : options.limit) !== void 0 ? BigInt(options.limit) : null
1779
+ }
1780
+ };
1781
+ const response = await this.#sendKvRequest(actorId, requestData);
1782
+ return this.#parseListResponseSimple(response);
1783
+ }
1784
+ async kvListPrefix(actorId, prefix, options) {
1785
+ const prefixKey = prefix.buffer.slice(
1786
+ prefix.byteOffset,
1787
+ prefix.byteOffset + prefix.byteLength
1788
+ );
1789
+ const requestData = {
1790
+ tag: "KvListRequest",
1791
+ val: {
1792
+ query: {
1793
+ tag: "KvListPrefixQuery",
1794
+ val: { key: prefixKey }
1795
+ },
1796
+ reverse: (options == null ? void 0 : options.reverse) || null,
1797
+ limit: (options == null ? void 0 : options.limit) !== void 0 ? BigInt(options.limit) : null
1798
+ }
1799
+ };
1800
+ const response = await this.#sendKvRequest(actorId, requestData);
1801
+ return this.#parseListResponseSimple(response);
1802
+ }
1803
+ async kvPut(actorId, entries) {
1804
+ const keys = entries.map(
1805
+ ([key, _value]) => key.buffer.slice(
1806
+ key.byteOffset,
1807
+ key.byteOffset + key.byteLength
1808
+ )
1809
+ );
1810
+ const values = entries.map(
1811
+ ([_key, value]) => value.buffer.slice(
1812
+ value.byteOffset,
1813
+ value.byteOffset + value.byteLength
1814
+ )
1815
+ );
1816
+ const requestData = {
1817
+ tag: "KvPutRequest",
1818
+ val: { keys, values }
1819
+ };
1820
+ await this.#sendKvRequest(actorId, requestData);
1821
+ }
1822
+ async kvDelete(actorId, keys) {
1823
+ const kvKeys = keys.map(
1824
+ (key) => key.buffer.slice(
1825
+ key.byteOffset,
1826
+ key.byteOffset + key.byteLength
1827
+ )
1828
+ );
1829
+ const requestData = {
1830
+ tag: "KvDeleteRequest",
1831
+ val: { keys: kvKeys }
1832
+ };
1833
+ await this.#sendKvRequest(actorId, requestData);
1834
+ }
1835
+ async kvDrop(actorId) {
1836
+ const requestData = {
1837
+ tag: "KvDropRequest",
1838
+ val: null
1839
+ };
1840
+ await this.#sendKvRequest(actorId, requestData);
1841
+ }
1842
+ // MARK: Alarm Operations
1843
+ setAlarm(actorId, alarmTs, generation) {
1844
+ const actor = this.getActor(actorId, generation);
1845
+ if (!actor) return;
1846
+ if (this.#shutdown) {
1847
+ console.warn("Runner is shut down, cannot set alarm");
1848
+ return;
1849
+ }
1850
+ const alarmEvent = {
1851
+ actorId,
1852
+ generation: actor.generation,
1853
+ alarmTs: alarmTs !== null ? BigInt(alarmTs) : null
1854
+ };
1855
+ const eventIndex = this.#nextEventIdx++;
1856
+ const eventWrapper = {
1857
+ index: eventIndex,
1858
+ inner: {
1859
+ tag: "EventActorSetAlarm",
1860
+ val: alarmEvent
1861
+ }
1862
+ };
1863
+ this.#recordEvent(eventWrapper);
1864
+ this.__sendToServer({
1865
+ tag: "ToServerEvents",
1866
+ val: [eventWrapper]
1867
+ });
1868
+ }
1869
+ clearAlarm(actorId, generation) {
1870
+ this.setAlarm(actorId, null, generation);
1871
+ }
1872
+ #sendKvRequest(actorId, requestData) {
1873
+ return new Promise((resolve, reject) => {
1874
+ if (this.#shutdown) {
1875
+ reject(new Error("Runner is shut down"));
1876
+ return;
1877
+ }
1878
+ const requestId = this.#nextRequestId++;
1879
+ const isConnected = this.#pegboardWebSocket && this.#pegboardWebSocket.readyState === 1;
1880
+ const requestEntry = {
1881
+ actorId,
1882
+ data: requestData,
1883
+ resolve,
1884
+ reject,
1885
+ sent: false,
1886
+ timestamp: Date.now()
1887
+ };
1888
+ this.#kvRequests.set(requestId, requestEntry);
1889
+ if (isConnected) {
1890
+ this.#sendSingleKvRequest(requestId);
1891
+ }
1892
+ });
1893
+ }
1894
+ #sendSingleKvRequest(requestId) {
1895
+ const request = this.#kvRequests.get(requestId);
1896
+ if (!request || request.sent) return;
1897
+ try {
1898
+ const kvRequest = {
1899
+ actorId: request.actorId,
1900
+ requestId,
1901
+ data: request.data
1902
+ };
1903
+ this.__sendToServer({
1904
+ tag: "ToServerKvRequest",
1905
+ val: kvRequest
1906
+ });
1907
+ request.sent = true;
1908
+ request.timestamp = Date.now();
1909
+ } catch (error) {
1910
+ this.#kvRequests.delete(requestId);
1911
+ request.reject(error);
1912
+ }
1913
+ }
1914
+ #processUnsentKvRequests() {
1915
+ if (!this.#pegboardWebSocket || this.#pegboardWebSocket.readyState !== 1) {
1916
+ return;
1917
+ }
1918
+ let processedCount = 0;
1919
+ for (const [requestId, request] of this.#kvRequests.entries()) {
1920
+ if (!request.sent) {
1921
+ this.#sendSingleKvRequest(requestId);
1922
+ processedCount++;
1923
+ }
1924
+ }
1925
+ if (processedCount > 0) {
1926
+ }
1927
+ }
1928
+ __webSocketReady() {
1929
+ return this.#pegboardWebSocket ? this.#pegboardWebSocket.readyState === 1 : false;
1930
+ }
1931
+ __sendToServer(message) {
1932
+ var _a, _b;
1933
+ if (this.#shutdown) {
1934
+ (_a = logger()) == null ? void 0 : _a.warn({
1935
+ msg: "Runner is shut down, cannot send message to server",
1936
+ runnerId: this.runnerId
1937
+ });
1938
+ return;
1939
+ }
1940
+ const encoded = protocol.encodeToServer(message);
1941
+ if (this.#pegboardWebSocket && this.#pegboardWebSocket.readyState === 1) {
1942
+ this.#pegboardWebSocket.send(encoded);
1943
+ } else {
1944
+ (_b = logger()) == null ? void 0 : _b.error({
1945
+ msg: "WebSocket not available or not open for sending data",
1946
+ runnerId: this.runnerId
1947
+ });
1948
+ }
1949
+ }
1950
+ getServerlessInitPacket() {
1951
+ if (!this.runnerId) return void 0;
1952
+ const data = protocol.encodeToServerlessServer({
1953
+ tag: "ToServerlessServerInit",
1954
+ val: {
1955
+ runnerId: this.runnerId
1956
+ }
1957
+ });
1958
+ const buffer = Buffer.alloc(data.length + 2);
1959
+ buffer.writeUInt16LE(PROTOCOL_VERSION, 0);
1960
+ Buffer.from(data).copy(buffer, 2);
1961
+ return buffer.toString("base64");
1962
+ }
1963
+ #scheduleReconnect() {
1964
+ var _a, _b;
1965
+ if (this.#shutdown) {
1966
+ (_a = logger()) == null ? void 0 : _a.debug({
1967
+ msg: "Runner is shut down, not attempting reconnect",
1968
+ runnerId: this.runnerId
1969
+ });
1970
+ return;
1971
+ }
1972
+ const delay = calculateBackoff(this.#reconnectAttempt, {
1973
+ initialDelay: 1e3,
1974
+ maxDelay: 3e4,
1975
+ multiplier: 2,
1976
+ jitter: true
1977
+ });
1978
+ (_b = logger()) == null ? void 0 : _b.debug({
1979
+ msg: `Scheduling reconnect attempt ${this.#reconnectAttempt + 1} in ${delay}ms`,
1980
+ runnerId: this.runnerId
1981
+ });
1982
+ this.#reconnectTimeout = setTimeout(async () => {
1983
+ var _a2;
1984
+ if (!this.#shutdown) {
1985
+ this.#reconnectAttempt++;
1986
+ (_a2 = logger()) == null ? void 0 : _a2.debug({
1987
+ msg: `Attempting to reconnect (attempt ${this.#reconnectAttempt})...`,
1988
+ runnerId: this.runnerId
1989
+ });
1990
+ await this.#openPegboardWebSocket();
1991
+ }
1992
+ }, delay);
1993
+ }
1994
+ #resendUnacknowledgedEvents(lastEventIdx) {
1995
+ const eventsToResend = this.#eventHistory.filter(
1996
+ (event) => event.index > lastEventIdx
1997
+ );
1998
+ if (eventsToResend.length === 0) return;
1999
+ this.__sendToServer({
2000
+ tag: "ToServerEvents",
2001
+ val: eventsToResend
2002
+ });
2003
+ }
2004
+ #cleanupOldKvRequests() {
2005
+ const thirtySecondsAgo = Date.now() - KV_EXPIRE;
2006
+ const toDelete = [];
2007
+ for (const [requestId, request] of this.#kvRequests.entries()) {
2008
+ if (request.timestamp < thirtySecondsAgo) {
2009
+ request.reject(
2010
+ new Error(
2011
+ "KV request timed out waiting for WebSocket connection"
2012
+ )
2013
+ );
2014
+ toDelete.push(requestId);
2015
+ }
2016
+ }
2017
+ for (const requestId of toDelete) {
2018
+ this.#kvRequests.delete(requestId);
2019
+ }
2020
+ if (toDelete.length > 0) {
2021
+ }
2022
+ }
2023
+ };
2024
+ export {
2025
+ Runner
2026
+ };
2027
+ //# sourceMappingURL=mod.js.map