redux-cluster-ws 2.0.2 → 2.0.4

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.
@@ -5,295 +5,284 @@ import { hasher, replacer, generateUID } from "./utils.js";
5
5
  // Import WebSocket server for Node.js
6
6
  import { WebSocketServer } from "ws";
7
7
  export class ReduxClusterWSServerWrapper {
8
- store;
9
- uid;
10
- connections;
11
- authDatabase;
12
- ipBanDatabase;
13
- config;
14
- server;
15
- wss;
16
- unsubscribe;
17
- IP_BAN_ATTEMPTS = 15;
18
- IP_BAN_TIMEOUT = 10800000; // 3 hours
19
- CONNECTION_TIMEOUT = 30000;
20
- gcInterval;
21
8
  constructor(store) {
22
- this.store = store;
23
- this.uid = generateUID();
24
- this.connections = new Map();
25
- this.authDatabase = {};
26
- this.ipBanDatabase = {};
27
- this.config = { host: "0.0.0.0", port: 10002 };
28
- // Attach createWSServer method to store
29
- store.createWSServer = this.createWSServer.bind(this);
30
- }
31
- createWSServer = (config) => {
32
- this.config = { ...this.config, ...config };
33
- // Setup authentication database
34
- if (this.config.logins) {
35
- for (const [login, password] of Object.entries(this.config.logins)) {
36
- const hashedLogin = hasher(`REDUX_CLUSTER${login}`);
37
- const hashedPassword = hasher(`REDUX_CLUSTER${password}`);
38
- this.authDatabase[hashedLogin] = hashedPassword;
9
+ this.IP_BAN_ATTEMPTS = 15;
10
+ this.IP_BAN_TIMEOUT = 10800000; // 3 hours
11
+ this.CONNECTION_TIMEOUT = 30000;
12
+ this.createWSServer = (config) => {
13
+ this.config = { ...this.config, ...config };
14
+ // Setup authentication database
15
+ if (this.config.logins) {
16
+ for (const [login, password] of Object.entries(this.config.logins)) {
17
+ const hashedLogin = hasher(`REDUX_CLUSTER${login}`);
18
+ const hashedPassword = hasher(`REDUX_CLUSTER${password}`);
19
+ this.authDatabase[hashedLogin] = hashedPassword;
20
+ }
39
21
  }
40
- }
41
- // Start garbage collection for banned IPs
42
- this.startGarbageCollection();
43
- // Create HTTP/HTTPS server if not provided
44
- if (!this.config.server) {
45
- if (this.config.ssl?.key && this.config.ssl?.cert) {
46
- const ssl = {
47
- key: readFileSync(this.config.ssl.key),
48
- cert: readFileSync(this.config.ssl.cert) +
49
- (this.config.ssl.ca ? "\n" + readFileSync(this.config.ssl.ca) : ""),
50
- };
51
- this.server = createHttpsServer(ssl);
22
+ // Start garbage collection for banned IPs
23
+ this.startGarbageCollection();
24
+ // Create HTTP/HTTPS server if not provided
25
+ if (!this.config.server) {
26
+ if (this.config.ssl?.key && this.config.ssl?.cert) {
27
+ const ssl = {
28
+ key: readFileSync(this.config.ssl.key),
29
+ cert: readFileSync(this.config.ssl.cert) +
30
+ (this.config.ssl.ca ? "\n" + readFileSync(this.config.ssl.ca) : ""),
31
+ };
32
+ this.server = createHttpsServer(ssl);
33
+ }
34
+ else {
35
+ this.server = createHttpServer();
36
+ }
37
+ this.server.setTimeout(this.CONNECTION_TIMEOUT);
38
+ this.server.listen(this.config.port, this.config.host);
52
39
  }
53
40
  else {
54
- this.server = createHttpServer();
41
+ this.server = this.config.server;
55
42
  }
56
- this.server.setTimeout(this.CONNECTION_TIMEOUT);
57
- this.server.listen(this.config.port, this.config.host);
58
- }
59
- else {
60
- this.server = this.config.server;
61
- }
62
- // Create WebSocket server
63
- this.wss = new WebSocketServer({
64
- server: this.server,
65
- path: `/redux-cluster-${this.store.RCHash}`,
66
- });
67
- this.wss.on("connection", this.handleConnection.bind(this));
68
- // Subscribe to store changes
69
- this.unsubscribe = this.store.subscribe(() => {
70
- if (this.store.mode === "snapshot") {
71
- this.sendToAll();
72
- }
73
- });
74
- // For action mode, override dispatch to send individual actions
75
- if (this.store.mode === "action") {
76
- const originalDispatch = this.store.dispatch;
77
- this.store.dispatch = (action) => {
78
- const result = originalDispatch(action);
79
- // Send the action to all connected clients
80
- if (action.type !== "@@INIT" && action.type !== "REDUX_CLUSTER_SYNC") {
81
- this.sendToAll({
82
- _msg: "REDUX_CLUSTER_MSGTOWORKER",
83
- _hash: this.store.RCHash,
84
- _action: action,
85
- });
43
+ // Create WebSocket server
44
+ this.wss = new WebSocketServer({
45
+ server: this.server,
46
+ path: `/redux-cluster-${this.store.RCHash}`,
47
+ });
48
+ this.wss.on("connection", this.handleConnection.bind(this));
49
+ // Subscribe to store changes
50
+ this.unsubscribe = this.store.subscribe(() => {
51
+ if (this.store.mode === "snapshot") {
52
+ this.sendToAll();
86
53
  }
87
- return result;
54
+ });
55
+ // For action mode, override dispatch to send individual actions
56
+ if (this.store.mode === "action") {
57
+ const originalDispatch = this.store.dispatch;
58
+ this.store.dispatch = (action) => {
59
+ const result = originalDispatch(action);
60
+ // Send the action to all connected clients
61
+ if (action.type !== "@@INIT" && action.type !== "REDUX_CLUSTER_SYNC") {
62
+ this.sendToAll({
63
+ _msg: "REDUX_CLUSTER_MSGTOWORKER",
64
+ _hash: this.store.RCHash,
65
+ _action: action,
66
+ });
67
+ }
68
+ return result;
69
+ };
70
+ }
71
+ // Add server role
72
+ if (!this.store.role.includes("server")) {
73
+ this.store.role.push("server");
74
+ }
75
+ };
76
+ this.handleConnection = (ws, req) => {
77
+ const connectionId = generateUID();
78
+ const ip = this.extractIP(req);
79
+ const processedIP = replacer(ip, true);
80
+ // Check if IP is banned
81
+ if (this.isIPBanned(processedIP)) {
82
+ this.sendMessage(ws, {
83
+ _msg: "REDUX_CLUSTER_SOCKET_AUTHSTATE",
84
+ _hash: this.store.RCHash,
85
+ _value: false,
86
+ _banned: true,
87
+ });
88
+ ws.close();
89
+ return;
90
+ }
91
+ const connection = {
92
+ id: connectionId,
93
+ ws,
94
+ authenticated: false,
95
+ ip: processedIP,
88
96
  };
89
- }
90
- // Add server role
91
- if (!this.store.role.includes("server")) {
92
- this.store.role.push("server");
93
- }
94
- };
95
- handleConnection = (ws, req) => {
96
- const connectionId = generateUID();
97
- const ip = this.extractIP(req);
98
- const processedIP = replacer(ip, true);
99
- // Check if IP is banned
100
- if (this.isIPBanned(processedIP)) {
101
- this.sendMessage(ws, {
102
- _msg: "REDUX_CLUSTER_SOCKET_AUTHSTATE",
103
- _hash: this.store.RCHash,
104
- _value: false,
105
- _banned: true,
97
+ this.connections.set(connectionId, connection);
98
+ ws.on("message", (data) => {
99
+ try {
100
+ const message = JSON.parse(data.toString());
101
+ this.handleMessage(connectionId, message);
102
+ }
103
+ catch (error) {
104
+ this.store.stderr(`ReduxCluster.createWSServer message parse error: ${error}`);
105
+ this.removeConnection(connectionId);
106
+ }
107
+ });
108
+ ws.on("close", () => {
109
+ this.removeConnection(connectionId);
110
+ });
111
+ ws.on("error", (error) => {
112
+ this.store.stderr(`ReduxCluster.createWSServer connection error: ${error.message}`);
113
+ this.removeConnection(connectionId);
106
114
  });
107
- ws.close();
108
- return;
109
- }
110
- const connection = {
111
- id: connectionId,
112
- ws,
113
- authenticated: false,
114
- ip: processedIP,
115
115
  };
116
- this.connections.set(connectionId, connection);
117
- ws.on("message", (data) => {
118
- try {
119
- const message = JSON.parse(data.toString());
120
- this.handleMessage(connectionId, message);
116
+ this.handleMessage = (connectionId, message) => {
117
+ const connection = this.connections.get(connectionId);
118
+ if (!connection || message._hash !== this.store.RCHash) {
119
+ return;
121
120
  }
122
- catch (error) {
123
- this.store.stderr(`ReduxCluster.createWSServer message parse error: ${error}`);
121
+ switch (message._msg) {
122
+ case "REDUX_CLUSTER_SOCKET_AUTH":
123
+ this.handleAuthentication(connectionId, message);
124
+ break;
125
+ case "REDUX_CLUSTER_START":
126
+ if (connection.authenticated) {
127
+ this.sendMessage(connection.ws, {
128
+ _msg: "REDUX_CLUSTER_MSGTOWORKER",
129
+ _hash: this.store.RCHash,
130
+ _action: {
131
+ type: "REDUX_CLUSTER_SYNC",
132
+ payload: this.store.getState(),
133
+ },
134
+ });
135
+ }
136
+ break;
137
+ case "REDUX_CLUSTER_MSGTOMASTER":
138
+ if (connection.authenticated && message._action) {
139
+ if (message._action.type === "REDUX_CLUSTER_SYNC") {
140
+ throw new Error("Please don't use REDUX_CLUSTER_SYNC action type!");
141
+ }
142
+ this.store.dispatch(message._action);
143
+ }
144
+ break;
145
+ }
146
+ };
147
+ this.handleAuthentication = (connectionId, message) => {
148
+ const connection = this.connections.get(connectionId);
149
+ if (!connection)
150
+ return;
151
+ const { _login, _password } = message;
152
+ if (_login &&
153
+ _password &&
154
+ this.authDatabase[_login] &&
155
+ this.authDatabase[_login] === _password) {
156
+ // Authentication successful
157
+ connection.authenticated = true;
158
+ this.clearIPBan(connection.ip);
159
+ this.sendMessage(connection.ws, {
160
+ _msg: "REDUX_CLUSTER_SOCKET_AUTHSTATE",
161
+ _hash: this.store.RCHash,
162
+ _value: true,
163
+ });
164
+ }
165
+ else {
166
+ // Authentication failed
167
+ this.recordFailedAttempt(connection.ip);
168
+ this.sendMessage(connection.ws, {
169
+ _msg: "REDUX_CLUSTER_SOCKET_AUTHSTATE",
170
+ _hash: this.store.RCHash,
171
+ _value: false,
172
+ });
124
173
  this.removeConnection(connectionId);
125
174
  }
126
- });
127
- ws.on("close", () => {
128
- this.removeConnection(connectionId);
129
- });
130
- ws.on("error", (error) => {
131
- this.store.stderr(`ReduxCluster.createWSServer connection error: ${error.message}`);
132
- this.removeConnection(connectionId);
133
- });
134
- };
135
- handleMessage = (connectionId, message) => {
136
- const connection = this.connections.get(connectionId);
137
- if (!connection || message._hash !== this.store.RCHash) {
138
- return;
139
- }
140
- switch (message._msg) {
141
- case "REDUX_CLUSTER_SOCKET_AUTH":
142
- this.handleAuthentication(connectionId, message);
143
- break;
144
- case "REDUX_CLUSTER_START":
145
- if (connection.authenticated) {
146
- this.sendMessage(connection.ws, {
147
- _msg: "REDUX_CLUSTER_MSGTOWORKER",
148
- _hash: this.store.RCHash,
149
- _action: {
150
- type: "REDUX_CLUSTER_SYNC",
151
- payload: this.store.getState(),
152
- },
153
- });
154
- }
155
- break;
156
- case "REDUX_CLUSTER_MSGTOMASTER":
157
- if (connection.authenticated && message._action) {
158
- if (message._action.type === "REDUX_CLUSTER_SYNC") {
159
- throw new Error("Please don't use REDUX_CLUSTER_SYNC action type!");
175
+ };
176
+ this.sendToAll = (message) => {
177
+ const msg = message || {
178
+ _msg: "REDUX_CLUSTER_MSGTOWORKER",
179
+ _hash: this.store.RCHash,
180
+ _action: { type: "REDUX_CLUSTER_SYNC", payload: this.store.getState() },
181
+ };
182
+ for (const connection of this.connections.values()) {
183
+ if (connection.authenticated && connection.ws.readyState === 1) {
184
+ // WebSocket.OPEN = 1
185
+ try {
186
+ this.sendMessage(connection.ws, msg);
187
+ }
188
+ catch (error) {
189
+ this.store.stderr(`ReduxCluster.createWSServer write error: ${error}`);
160
190
  }
161
- this.store.dispatch(message._action);
162
191
  }
163
- break;
164
- }
165
- };
166
- handleAuthentication = (connectionId, message) => {
167
- const connection = this.connections.get(connectionId);
168
- if (!connection)
169
- return;
170
- const { _login, _password } = message;
171
- if (_login &&
172
- _password &&
173
- this.authDatabase[_login] &&
174
- this.authDatabase[_login] === _password) {
175
- // Authentication successful
176
- connection.authenticated = true;
177
- this.clearIPBan(connection.ip);
178
- this.sendMessage(connection.ws, {
179
- _msg: "REDUX_CLUSTER_SOCKET_AUTHSTATE",
180
- _hash: this.store.RCHash,
181
- _value: true,
182
- });
183
- }
184
- else {
185
- // Authentication failed
186
- this.recordFailedAttempt(connection.ip);
187
- this.sendMessage(connection.ws, {
188
- _msg: "REDUX_CLUSTER_SOCKET_AUTHSTATE",
189
- _hash: this.store.RCHash,
190
- _value: false,
191
- });
192
- this.removeConnection(connectionId);
193
- }
194
- };
195
- sendToAll = (message) => {
196
- const msg = message || {
197
- _msg: "REDUX_CLUSTER_MSGTOWORKER",
198
- _hash: this.store.RCHash,
199
- _action: { type: "REDUX_CLUSTER_SYNC", payload: this.store.getState() },
192
+ }
200
193
  };
201
- for (const connection of this.connections.values()) {
202
- if (connection.authenticated && connection.ws.readyState === 1) {
194
+ this.sendMessage = (ws, message) => {
195
+ if (ws.readyState === 1) {
203
196
  // WebSocket.OPEN = 1
197
+ ws.send(JSON.stringify(message));
198
+ }
199
+ };
200
+ this.extractIP = (req) => {
201
+ const forwarded = req.headers["x-forwarded-for"];
202
+ if (typeof forwarded === "string") {
203
+ return forwarded.split(",")[0].trim();
204
+ }
205
+ if (Array.isArray(forwarded)) {
206
+ return forwarded[0];
207
+ }
208
+ return req.socket.remoteAddress || "127.0.0.1";
209
+ };
210
+ this.isIPBanned = (ip) => {
211
+ const banInfo = this.ipBanDatabase[ip];
212
+ if (!banInfo)
213
+ return false;
214
+ const isTimedOut = banInfo.time + this.IP_BAN_TIMEOUT < Date.now();
215
+ if (isTimedOut) {
216
+ delete this.ipBanDatabase[ip];
217
+ return false;
218
+ }
219
+ return banInfo.count >= this.IP_BAN_ATTEMPTS;
220
+ };
221
+ this.recordFailedAttempt = (ip) => {
222
+ const existing = this.ipBanDatabase[ip];
223
+ let count = 0;
224
+ if (existing) {
225
+ const isTimedOut = existing.time + this.IP_BAN_TIMEOUT < Date.now();
226
+ count = isTimedOut ? 0 : existing.count;
227
+ }
228
+ this.ipBanDatabase[ip] = {
229
+ time: Date.now(),
230
+ count: count + 1,
231
+ };
232
+ };
233
+ this.clearIPBan = (ip) => {
234
+ delete this.ipBanDatabase[ip];
235
+ };
236
+ this.removeConnection = (connectionId) => {
237
+ const connection = this.connections.get(connectionId);
238
+ if (connection) {
204
239
  try {
205
- this.sendMessage(connection.ws, msg);
240
+ connection.ws.close();
206
241
  }
207
- catch (error) {
208
- this.store.stderr(`ReduxCluster.createWSServer write error: ${error}`);
242
+ catch {
243
+ // Ignore errors when closing
209
244
  }
245
+ this.connections.delete(connectionId);
210
246
  }
211
- }
212
- };
213
- sendMessage = (ws, message) => {
214
- if (ws.readyState === 1) {
215
- // WebSocket.OPEN = 1
216
- ws.send(JSON.stringify(message));
217
- }
218
- };
219
- extractIP = (req) => {
220
- const forwarded = req.headers["x-forwarded-for"];
221
- if (typeof forwarded === "string") {
222
- return forwarded.split(",")[0].trim();
223
- }
224
- if (Array.isArray(forwarded)) {
225
- return forwarded[0];
226
- }
227
- return req.socket.remoteAddress || "127.0.0.1";
228
- };
229
- isIPBanned = (ip) => {
230
- const banInfo = this.ipBanDatabase[ip];
231
- if (!banInfo)
232
- return false;
233
- const isTimedOut = banInfo.time + this.IP_BAN_TIMEOUT < Date.now();
234
- if (isTimedOut) {
235
- delete this.ipBanDatabase[ip];
236
- return false;
237
- }
238
- return banInfo.count >= this.IP_BAN_ATTEMPTS;
239
- };
240
- recordFailedAttempt = (ip) => {
241
- const existing = this.ipBanDatabase[ip];
242
- let count = 0;
243
- if (existing) {
244
- const isTimedOut = existing.time + this.IP_BAN_TIMEOUT < Date.now();
245
- count = isTimedOut ? 0 : existing.count;
246
- }
247
- this.ipBanDatabase[ip] = {
248
- time: Date.now(),
249
- count: count + 1,
250
247
  };
251
- };
252
- clearIPBan = (ip) => {
253
- delete this.ipBanDatabase[ip];
254
- };
255
- removeConnection = (connectionId) => {
256
- const connection = this.connections.get(connectionId);
257
- if (connection) {
258
- try {
259
- connection.ws.close();
248
+ this.startGarbageCollection = () => {
249
+ this.gcInterval = setInterval(() => {
250
+ const now = Date.now();
251
+ for (const [ip, banInfo] of Object.entries(this.ipBanDatabase)) {
252
+ if (banInfo.time + this.IP_BAN_TIMEOUT < now) {
253
+ delete this.ipBanDatabase[ip];
254
+ }
255
+ }
256
+ }, 60000); // Run every minute
257
+ };
258
+ this.destroy = () => {
259
+ if (this.gcInterval) {
260
+ clearInterval(this.gcInterval);
260
261
  }
261
- catch {
262
- // Ignore errors when closing
262
+ if (this.unsubscribe) {
263
+ this.unsubscribe();
263
264
  }
264
- this.connections.delete(connectionId);
265
- }
266
- };
267
- startGarbageCollection = () => {
268
- this.gcInterval = setInterval(() => {
269
- const now = Date.now();
270
- for (const [ip, banInfo] of Object.entries(this.ipBanDatabase)) {
271
- if (banInfo.time + this.IP_BAN_TIMEOUT < now) {
272
- delete this.ipBanDatabase[ip];
273
- }
265
+ for (const connection of this.connections.values()) {
266
+ this.removeConnection(connection.id);
267
+ }
268
+ if (this.wss) {
269
+ this.wss.close();
274
270
  }
275
- }, 60000); // Run every minute
276
- };
277
- destroy = () => {
278
- if (this.gcInterval) {
279
- clearInterval(this.gcInterval);
280
- }
281
- if (this.unsubscribe) {
282
- this.unsubscribe();
283
- }
284
- for (const connection of this.connections.values()) {
285
- this.removeConnection(connection.id);
286
- }
287
- if (this.wss) {
288
- this.wss.close();
289
- }
290
- if (this.server && this.server.close) {
291
- this.server.close();
292
- }
293
- };
271
+ if (this.server && this.server.close) {
272
+ this.server.close();
273
+ }
274
+ };
275
+ this.store = store;
276
+ this.uid = generateUID();
277
+ this.connections = new Map();
278
+ this.authDatabase = {};
279
+ this.ipBanDatabase = {};
280
+ this.config = { host: "0.0.0.0", port: 10002 };
281
+ // Attach createWSServer method to store
282
+ store.createWSServer = this.createWSServer.bind(this);
283
+ }
294
284
  }
295
285
  export function server(store) {
296
286
  new ReduxClusterWSServerWrapper(store);
297
287
  return store;
298
288
  }
299
- //# sourceMappingURL=server.js.map
package/dist/esm/types.js CHANGED
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=types.js.map
package/dist/esm/utils.js CHANGED
@@ -66,4 +66,3 @@ export function deepClone(obj) {
66
66
  }
67
67
  return obj;
68
68
  }
69
- //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,48 +1,52 @@
1
1
  {
2
2
  "name": "redux-cluster-ws",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "WebSocket client/server wrapper for redux-cluster with TypeScript support",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
7
7
  "module": "./dist/esm/index.js",
8
- "types": "./dist/esm/index.d.ts",
8
+ "types": "./dist/types/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
11
  "import": {
12
- "types": "./dist/esm/index.d.ts",
12
+ "types": "./dist/types/index.d.ts",
13
13
  "default": "./dist/esm/index.js"
14
14
  },
15
15
  "require": {
16
- "types": "./dist/cjs/index.d.ts",
16
+ "types": "./dist/types/index.d.ts",
17
17
  "default": "./dist/cjs/index.js"
18
18
  }
19
19
  },
20
20
  "./server": {
21
21
  "import": {
22
- "types": "./dist/esm/server.d.ts",
22
+ "types": "./dist/types/server.d.ts",
23
23
  "default": "./dist/esm/server.js"
24
24
  },
25
25
  "require": {
26
- "types": "./dist/cjs/server.d.ts",
26
+ "types": "./dist/types/server.d.ts",
27
27
  "default": "./dist/cjs/server.js"
28
28
  }
29
29
  },
30
30
  "./client": {
31
31
  "import": {
32
- "types": "./dist/esm/client.d.ts",
32
+ "types": "./dist/types/client.d.ts",
33
33
  "default": "./dist/esm/client.js"
34
34
  },
35
35
  "require": {
36
- "types": "./dist/cjs/client.d.ts",
36
+ "types": "./dist/types/client.d.ts",
37
37
  "default": "./dist/cjs/client.js"
38
38
  }
39
39
  }
40
40
  },
41
41
  "scripts": {
42
- "build": "npm run clean && npm run build:esm && npm run build:cjs && npm run build:pkg",
43
- "build:esm": "tsc --module ES2022 --outDir dist/esm --target ES2022 --declaration",
44
- "build:cjs": "tsc -p tsconfig.cjs.json --declaration",
42
+ "build": "npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:pkg",
43
+ "build:esm": "tsc --module ES2020 --outDir dist/esm --target ES2020 --declaration false",
44
+ "build:cjs": "tsc -p tsconfig.cjs.json --declaration false",
45
+ "build:types": "tsc --module ES2020 --outDir dist/types --target ES2020 --declaration true --emitDeclarationOnly",
45
46
  "build:pkg": "echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
47
+ "dev": "npm run clean && npm run build:esm:dev && npm run build:cjs:dev && npm run build:types",
48
+ "build:esm:dev": "tsc --module ES2020 --outDir dist/esm --target ES2020 --declaration false --sourceMap",
49
+ "build:cjs:dev": "tsc -p tsconfig.cjs.json --declaration false --sourceMap",
46
50
  "clean": "rm -rf dist",
47
51
  "lint": "eslint src/",
48
52
  "lint:fix": "eslint src/ --fix",
@@ -51,7 +55,6 @@
51
55
  "test:basic": "node tests/basic.test.cjs",
52
56
  "test:node": "node tests/node.test.cjs",
53
57
  "test:browser": "npm run build && node tests/browser/server.js",
54
- "dev": "tsc --watch --outDir dist/esm",
55
58
  "prepublishOnly": "npm run build"
56
59
  },
57
60
  "repository": {
@@ -101,9 +104,9 @@
101
104
  ],
102
105
  "dependencies": {
103
106
  "@sergdudko/objectstream": "^3.2.26",
104
- "protoobject": "^2.0.0",
107
+ "protoobject": "^2.0.1",
105
108
  "redux": "^5.0.1",
106
- "redux-cluster": "^2.0.2"
109
+ "redux-cluster": "^2.0.4"
107
110
  },
108
111
  "devDependencies": {
109
112
  "@eslint/js": "^9.35.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}