@rivetkit/engine-runner 25.7.2-rc.1 → 25.7.3

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