@omen.foundation/node-microservice-runtime 0.1.76 → 0.1.78

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.
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BeamableWebSocket = void 0;
7
+ const promises_1 = require("node:timers/promises");
8
+ const node_events_1 = require("node:events");
9
+ const ws_1 = __importDefault(require("ws"));
10
+ class BeamableWebSocket {
11
+ constructor(options) {
12
+ var _a, _b;
13
+ this.emitter = new node_events_1.EventEmitter();
14
+ this.socket = null;
15
+ this.isDisposed = false;
16
+ this.logger = options.logger.child({ component: 'BeamableWebSocket' });
17
+ this.url = options.url;
18
+ this.maxRetries = (_a = options.maxRetries) !== null && _a !== void 0 ? _a : 20;
19
+ this.retryDelayMs = (_b = options.retryDelayMs) !== null && _b !== void 0 ? _b : 1000;
20
+ }
21
+ on(event, listener) {
22
+ this.emitter.on(event, listener);
23
+ return this;
24
+ }
25
+ off(event, listener) {
26
+ this.emitter.off(event, listener);
27
+ return this;
28
+ }
29
+ emit(event, ...args) {
30
+ this.emitter.emit(event, ...args);
31
+ }
32
+ async connect() {
33
+ let attempt = 0;
34
+ while (!this.isDisposed) {
35
+ try {
36
+ attempt += 1;
37
+ await this.createSocket();
38
+ return;
39
+ }
40
+ catch (error) {
41
+ const err = error instanceof Error ? error : new Error(String(error));
42
+ this.logger.warn({ err, attempt }, 'WebSocket connection attempt failed.');
43
+ if (attempt >= this.maxRetries) {
44
+ throw err;
45
+ }
46
+ await (0, promises_1.setTimeout)(this.retryDelayMs * attempt);
47
+ }
48
+ }
49
+ throw new Error('WebSocket disposed before connection could be established.');
50
+ }
51
+ async createSocket() {
52
+ if (this.socket) {
53
+ this.socket.removeAllListeners();
54
+ this.socket.terminate();
55
+ this.socket = null;
56
+ }
57
+ return new Promise((resolve, reject) => {
58
+ const ws = new ws_1.default(this.url, {
59
+ perMessageDeflate: true,
60
+ handshakeTimeout: 30000,
61
+ });
62
+ const handleOpen = () => {
63
+ this.logger.info({ url: this.url }, 'WebSocket connected.');
64
+ this.socket = ws;
65
+ ws.off('error', handleError);
66
+ ws.off('close', handlePrematureClose);
67
+ this.registerSocketHandlers(ws);
68
+ this.emit('open');
69
+ resolve();
70
+ };
71
+ const handleError = (error) => {
72
+ ws.off('open', handleOpen);
73
+ ws.off('close', handlePrematureClose);
74
+ reject(error);
75
+ };
76
+ const handlePrematureClose = (code, reason) => {
77
+ ws.off('open', handleOpen);
78
+ ws.off('error', handleError);
79
+ const err = new Error(`WebSocket closed before open handshake. code=${code} reason=${reason.toString()}`);
80
+ reject(err);
81
+ };
82
+ ws.on('open', handleOpen);
83
+ ws.on('error', handleError);
84
+ ws.on('close', handlePrematureClose);
85
+ });
86
+ }
87
+ registerSocketHandlers(ws) {
88
+ ws.on('message', (data) => {
89
+ const payload = typeof data === 'string' ? data : data.toString('utf8');
90
+ this.logger.debug({ payload }, 'WebSocket received frame.');
91
+ this.emit('message', payload);
92
+ });
93
+ ws.on('close', (code, reason) => {
94
+ this.logger.warn({ code, reason: reason.toString() }, 'WebSocket closed.');
95
+ this.emit('close', code === 1000);
96
+ if (!this.isDisposed) {
97
+ void this.reconnect();
98
+ }
99
+ });
100
+ ws.on('error', (error) => {
101
+ const err = error instanceof Error ? error : new Error(String(error));
102
+ this.logger.error({ err }, 'WebSocket error.');
103
+ this.emit('error', err);
104
+ });
105
+ }
106
+ async reconnect() {
107
+ this.logger.info('Attempting to reconnect websocket.');
108
+ let attempt = 0;
109
+ while (!this.isDisposed) {
110
+ try {
111
+ attempt += 1;
112
+ await this.createSocket();
113
+ this.emit('open');
114
+ return;
115
+ }
116
+ catch (error) {
117
+ const err = error instanceof Error ? error : new Error(String(error));
118
+ this.logger.warn({ err, attempt }, 'Retrying websocket connection.');
119
+ await (0, promises_1.setTimeout)(this.retryDelayMs * Math.min(attempt, 10));
120
+ }
121
+ }
122
+ }
123
+ async send(text) {
124
+ if (!this.socket || this.socket.readyState !== ws_1.default.OPEN) {
125
+ throw new Error('WebSocket is not connected.');
126
+ }
127
+ this.logger.debug({ message: text }, 'Sending websocket frame.');
128
+ this.socket.send(text);
129
+ }
130
+ async close() {
131
+ this.isDisposed = true;
132
+ if (!this.socket) {
133
+ return;
134
+ }
135
+ const ws = this.socket;
136
+ if (ws.readyState === ws_1.default.CLOSED || ws.readyState === ws_1.default.CLOSING) {
137
+ return;
138
+ }
139
+ ws.close(1000, 'shutdown');
140
+ }
141
+ }
142
+ exports.BeamableWebSocket = BeamableWebSocket;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omen.foundation/node-microservice-runtime",
3
- "version": "0.1.76",
3
+ "version": "0.1.78",
4
4
  "description": "Beamable microservice runtime for Node.js/TypeScript services.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",