sonic-ws 1.0.6 → 1.1.0
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/LICENSE +1 -1
- package/README.md +7 -2
- package/dist/index.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/dist/ws/Connection.d.ts +4 -3
- package/dist/ws/Connection.js +1 -1
- package/dist/ws/client/core/ClientCore.d.ts +3 -4
- package/dist/ws/client/core/ClientCore.js +10 -9
- package/dist/ws/client/node/ClientNode.js +1 -1
- package/dist/ws/packets/PacketProcessors.js +1 -1
- package/dist/ws/packets/PacketType.js +1 -1
- package/dist/ws/packets/Packets.d.ts +1 -1
- package/dist/ws/packets/Packets.js +2 -2
- package/dist/ws/server/SonicWSConnection.d.ts +5 -4
- package/dist/ws/server/SonicWSConnection.js +16 -7
- package/dist/ws/server/SonicWSServer.js +1 -1
- package/dist/ws/util/ArrayUtil.js +1 -1
- package/dist/ws/util/BufferUtil.d.ts +1 -1
- package/dist/ws/util/BufferUtil.js +1 -1
- package/dist/ws/util/StringUtil.js +1 -1
- package/dist/ws/util/enums/EnumHandler.js +1 -1
- package/dist/ws/util/enums/EnumType.js +1 -1
- package/dist/ws/util/packets/BatchHelper.d.ts +1 -1
- package/dist/ws/util/packets/BatchHelper.js +3 -3
- package/dist/ws/util/packets/CompressionUtil.js +1 -1
- package/dist/ws/util/packets/PacketHolder.js +1 -1
- package/dist/ws/util/packets/PacketUtils.d.ts +1 -1
- package/dist/ws/util/packets/PacketUtils.js +10 -4
- package/dist/ws/util/packets/RateHandler.js +1 -1
- package/package.json +3 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ Developer Friendly:
|
|
|
18
18
|
|
|
19
19
|
Security:
|
|
20
20
|
- Tamper-proof; any invalid packet instantly causes closure, and tampering becomes incredibly difficult
|
|
21
|
+
- Basic but immensely effective anti-tampering for browser clients
|
|
21
22
|
- Built-in ability for handshake packets, preventing repetitive initiation checks in listeners (for example, removes if(!init) everywhere)
|
|
22
23
|
- Built-in rate limiting for packets; ability for global send & receive, alongside per-packet rate limiting
|
|
23
24
|
- Built-in disabling & enabling of packets to prevent abuse
|
|
@@ -35,7 +36,7 @@ Developer Experience:
|
|
|
35
36
|
- Many data types to maximize speed, clarity, bandwidth, and security
|
|
36
37
|
- Debug tools for socket ids, byte size, data logging, etc. for troubleshooting
|
|
37
38
|
- Very minimal learning curve, easy to work in
|
|
38
|
-
- JSDoc's for understanding
|
|
39
|
+
- JSDoc's for understanding; immensely intuitive (personally, I took a break for half a year and came back and snapped right back in)
|
|
39
40
|
|
|
40
41
|
Whether you're making a real-time game, a dashboard, a distributed system, or anything else, SonicWS gets you safe, structured packets, fast.
|
|
41
42
|
|
|
@@ -109,6 +110,10 @@ ws.on_close((event) => {
|
|
|
109
110
|
|
|
110
111
|
## KNOWN ISSUES
|
|
111
112
|
|
|
113
|
+
Some weird error messages when invalid inputs are in like CreatePacket() and stuff
|
|
114
|
+
|
|
112
115
|
## PLANNED FEATURES
|
|
113
116
|
|
|
114
|
-
Better error handling
|
|
117
|
+
Better error handling
|
|
118
|
+
|
|
119
|
+
ZLib/gzip compression
|
package/dist/index.js
CHANGED
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* Copyright
|
|
3
|
+
* Copyright 2026 Lily (liwybloc)
|
|
4
4
|
*
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
* you may not use this file except in compliance with the License.
|
|
@@ -18,7 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.SERVER_SUFFIX_NUMS = exports.SERVER_SUFFIX = exports.VERSION = void 0;
|
|
19
19
|
const StringUtil_1 = require("./ws/util/StringUtil");
|
|
20
20
|
/** Current protocol version */
|
|
21
|
-
exports.VERSION =
|
|
21
|
+
exports.VERSION = 14;
|
|
22
22
|
/** Server data suffix */
|
|
23
23
|
exports.SERVER_SUFFIX = "SWS";
|
|
24
24
|
/** Server data suffix in array */
|
package/dist/ws/Connection.d.ts
CHANGED
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export interface Connection {
|
|
5
5
|
/**
|
|
6
|
-
* List of timers
|
|
6
|
+
* List of timers.
|
|
7
7
|
* For internal use only.
|
|
8
8
|
*/
|
|
9
|
-
_timers: Record<number, number>;
|
|
9
|
+
_timers: Record<number, [number, (closed: boolean) => void, boolean]>;
|
|
10
10
|
/**
|
|
11
11
|
* Sets a timeout that will automatically end when the socket closes
|
|
12
12
|
* @param call The function to call
|
|
13
13
|
* @param time The time between now and the call (ms)
|
|
14
|
+
* @param callOnClose If the callback should be fired anyways when the socket closes
|
|
14
15
|
* @returns The timeout id to be used with socket.clearInterval(id)
|
|
15
16
|
*/
|
|
16
|
-
setTimeout(call: () => void, time: number): number;
|
|
17
|
+
setTimeout(call: () => void, time: number, callOnClose: boolean): number;
|
|
17
18
|
/**
|
|
18
19
|
* Sets an interval that will automatically end when the socket closes
|
|
19
20
|
* @param call The function to call
|
package/dist/ws/Connection.js
CHANGED
|
@@ -21,7 +21,7 @@ export declare abstract class SonicWSCore implements Connection {
|
|
|
21
21
|
private batcher;
|
|
22
22
|
private bufferHandler;
|
|
23
23
|
id: number;
|
|
24
|
-
_timers: Record<number, number>;
|
|
24
|
+
_timers: Record<number, [number, (closed: boolean) => void, boolean]>;
|
|
25
25
|
constructor(ws: WebSocket, bufferHandler: (val: MessageEvent) => Promise<Uint8Array>);
|
|
26
26
|
private reading;
|
|
27
27
|
private serverKeyHandler;
|
|
@@ -62,9 +62,8 @@ export declare abstract class SonicWSCore implements Connection {
|
|
|
62
62
|
*/
|
|
63
63
|
on(tag: string, listener: (value: any[]) => void): void;
|
|
64
64
|
raw_send(data: Uint8Array): void;
|
|
65
|
-
setTimeout(call: () => void, time: number): number;
|
|
66
|
-
setInterval(call: () => void, time: number): number;
|
|
67
|
-
private setTimer;
|
|
65
|
+
setTimeout(call: () => void, time: number, callOnClose?: boolean): number;
|
|
66
|
+
setInterval(call: () => void, time: number, callOnClose?: boolean): number;
|
|
68
67
|
clearTimeout(id: number): void;
|
|
69
68
|
clearInterval(id: number): void;
|
|
70
69
|
close(code?: number, reason?: string): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* Copyright
|
|
3
|
+
* Copyright 2026 Lily (liwybloc)
|
|
4
4
|
*
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
* you may not use this file except in compliance with the License.
|
|
@@ -52,7 +52,11 @@ class SonicWSCore {
|
|
|
52
52
|
this.socket.addEventListener('message', this.serverKeyHandler);
|
|
53
53
|
this.socket.addEventListener('close', (event) => {
|
|
54
54
|
this.listeners.close.forEach(listener => listener(event));
|
|
55
|
-
Object.values(this._timers)
|
|
55
|
+
for (const [id, callback, shouldCall] of Object.values(this._timers)) {
|
|
56
|
+
this.clearTimeout(id);
|
|
57
|
+
if (shouldCall)
|
|
58
|
+
callback(true);
|
|
59
|
+
}
|
|
56
60
|
});
|
|
57
61
|
this.bufferHandler = bufferHandler;
|
|
58
62
|
}
|
|
@@ -192,22 +196,19 @@ class SonicWSCore {
|
|
|
192
196
|
this.listeners.send.forEach(d => d(data));
|
|
193
197
|
this.socket.send(data);
|
|
194
198
|
}
|
|
195
|
-
setTimeout(call, time) {
|
|
199
|
+
setTimeout(call, time, callOnClose = false) {
|
|
196
200
|
const timeout = setTimeout(() => {
|
|
197
201
|
call();
|
|
198
202
|
this.clearTimeout(timeout);
|
|
199
203
|
}, time);
|
|
200
|
-
this.
|
|
204
|
+
this._timers[timeout] = [timeout, call, callOnClose];
|
|
201
205
|
return timeout;
|
|
202
206
|
}
|
|
203
|
-
setInterval(call, time) {
|
|
207
|
+
setInterval(call, time, callOnClose = false) {
|
|
204
208
|
const interval = setInterval(call, time);
|
|
205
|
-
this.
|
|
209
|
+
this._timers[interval] = [interval, call, callOnClose];
|
|
206
210
|
return interval;
|
|
207
211
|
}
|
|
208
|
-
setTimer(id) {
|
|
209
|
-
this._timers[id] = id;
|
|
210
|
-
}
|
|
211
212
|
clearTimeout(id) {
|
|
212
213
|
clearTimeout(id);
|
|
213
214
|
delete this._timers[id];
|
|
@@ -24,7 +24,7 @@ export declare class Packet<T extends (PacketType | readonly PacketType[])> {
|
|
|
24
24
|
private sendProcessor;
|
|
25
25
|
private validator;
|
|
26
26
|
processReceive: (data: Uint8Array, validationResult: any) => any;
|
|
27
|
-
processSend: (data: any[]) =>
|
|
27
|
+
processSend: (data: any[]) => Uint8Array;
|
|
28
28
|
validate: (data: Uint8Array) => boolean;
|
|
29
29
|
customValidator: ((socket: SonicWSConnection, ...values: any[]) => boolean) | null;
|
|
30
30
|
constructor(tag: string, schema: PacketSchema<T>, customValidator: ValidatorFunction, enabled: boolean, client: boolean);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* Copyright
|
|
3
|
+
* Copyright 2026 Lily (liwybloc)
|
|
4
4
|
*
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
* you may not use this file except in compliance with the License.
|
|
@@ -85,7 +85,7 @@ class Packet {
|
|
|
85
85
|
this.sendProcessor = (0, PacketProcessors_1.createSendProcessor)(this.type);
|
|
86
86
|
}
|
|
87
87
|
this.processReceive = (data, validationResult) => this.receiveProcessor(data, validationResult, 0);
|
|
88
|
-
this.processSend = (data) => this.sendProcessor(data);
|
|
88
|
+
this.processSend = (data) => new Uint8Array(this.sendProcessor(data));
|
|
89
89
|
this.validate = (data) => this.validator(data, 0);
|
|
90
90
|
this.customValidator = customValidator;
|
|
91
91
|
}
|
|
@@ -19,7 +19,8 @@ export declare class SonicWSConnection implements Connection {
|
|
|
19
19
|
handshakeComplete: boolean;
|
|
20
20
|
/** The index of the connection; unique for all connected, recycles old disconnected ids. Should be safe for INTS_C unless you have more than 27,647 connected at once. */
|
|
21
21
|
id: number;
|
|
22
|
-
_timers: Record<number, number>;
|
|
22
|
+
_timers: Record<number, [number, (closed: boolean) => void, boolean]>;
|
|
23
|
+
private closed;
|
|
23
24
|
constructor(socket: WS.WebSocket, host: SonicWSServer, id: number, handshakePacket: string | null, clientRateLimit: number, serverRateLimit: number);
|
|
24
25
|
private parseData;
|
|
25
26
|
private handshakeHandler;
|
|
@@ -55,7 +56,7 @@ export declare class SonicWSConnection implements Connection {
|
|
|
55
56
|
/**
|
|
56
57
|
* For internal use.
|
|
57
58
|
*/
|
|
58
|
-
send_processed(code: number, data:
|
|
59
|
+
send_processed(code: number, data: Uint8Array, packet: Packet<any>): void;
|
|
59
60
|
/**
|
|
60
61
|
* Sends a packet with the tag and values
|
|
61
62
|
* @param tag The tag to send
|
|
@@ -80,8 +81,8 @@ export declare class SonicWSConnection implements Connection {
|
|
|
80
81
|
togglePrint(): void;
|
|
81
82
|
raw_send(data: Uint8Array): void;
|
|
82
83
|
close(code?: number, reason?: string | Buffer): void;
|
|
83
|
-
setTimeout(call: () => void, time: number): number;
|
|
84
|
-
setInterval(call: () => void, time: number): number;
|
|
84
|
+
setTimeout(call: () => void, time: number, callOnClose?: boolean): number;
|
|
85
|
+
setInterval(call: () => void, time: number, callOnClose?: boolean): number;
|
|
85
86
|
clearTimeout(id: number): void;
|
|
86
87
|
clearInterval(id: number): void;
|
|
87
88
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* Copyright
|
|
3
|
+
* Copyright 2026 Lily (liwybloc)
|
|
4
4
|
*
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
* you may not use this file except in compliance with the License.
|
|
@@ -80,6 +80,7 @@ class SonicWSConnection {
|
|
|
80
80
|
/** The index of the connection; unique for all connected, recycles old disconnected ids. Should be safe for INTS_C unless you have more than 27,647 connected at once. */
|
|
81
81
|
id;
|
|
82
82
|
_timers = {};
|
|
83
|
+
closed = false;
|
|
83
84
|
constructor(socket, host, id, handshakePacket, clientRateLimit, serverRateLimit) {
|
|
84
85
|
this.socket = socket;
|
|
85
86
|
this.host = host;
|
|
@@ -108,7 +109,12 @@ class SonicWSConnection {
|
|
|
108
109
|
this.socket.addEventListener('message', this.handshakeLambda);
|
|
109
110
|
}
|
|
110
111
|
this.socket.on('close', () => {
|
|
111
|
-
|
|
112
|
+
this.closed = true;
|
|
113
|
+
for (const [id, callback, shouldCall] of Object.values(this._timers)) {
|
|
114
|
+
this.clearTimeout(id);
|
|
115
|
+
if (shouldCall)
|
|
116
|
+
callback(true);
|
|
117
|
+
}
|
|
112
118
|
});
|
|
113
119
|
}
|
|
114
120
|
parseData(event) {
|
|
@@ -158,6 +164,8 @@ class SonicWSConnection {
|
|
|
158
164
|
this.socket.close(4003, listened);
|
|
159
165
|
}
|
|
160
166
|
listenPacket(data, tag) {
|
|
167
|
+
if (this.closed)
|
|
168
|
+
return;
|
|
161
169
|
(0, PacketUtils_1.listenPacket)(data, this.listeners[tag], this.invalidPacket);
|
|
162
170
|
}
|
|
163
171
|
messageHandler(data) {
|
|
@@ -196,7 +204,7 @@ class SonicWSConnection {
|
|
|
196
204
|
* @returns If it's closed or not
|
|
197
205
|
*/
|
|
198
206
|
isClosed() {
|
|
199
|
-
return this.socket.readyState == WS.CLOSED;
|
|
207
|
+
return this.closed || this.socket.readyState == WS.CLOSED;
|
|
200
208
|
}
|
|
201
209
|
/**
|
|
202
210
|
* Listens for when the connection closes
|
|
@@ -270,19 +278,20 @@ class SonicWSConnection {
|
|
|
270
278
|
this.socket.send(data);
|
|
271
279
|
}
|
|
272
280
|
close(code = 1000, reason) {
|
|
281
|
+
this.closed = true;
|
|
273
282
|
this.socket.close(code, reason);
|
|
274
283
|
}
|
|
275
|
-
setTimeout(call, time) {
|
|
284
|
+
setTimeout(call, time, callOnClose = false) {
|
|
276
285
|
const timeout = setTimeout(() => {
|
|
277
286
|
call();
|
|
278
287
|
this.clearTimeout(timeout);
|
|
279
288
|
}, time);
|
|
280
|
-
this._timers[timeout] = timeout;
|
|
289
|
+
this._timers[timeout] = [timeout, call, callOnClose];
|
|
281
290
|
return timeout;
|
|
282
291
|
}
|
|
283
|
-
setInterval(call, time) {
|
|
292
|
+
setInterval(call, time, callOnClose = false) {
|
|
284
293
|
const interval = setInterval(call, time);
|
|
285
|
-
this._timers[interval] = interval;
|
|
294
|
+
this._timers[interval] = [interval, call, callOnClose];
|
|
286
295
|
return interval;
|
|
287
296
|
}
|
|
288
297
|
clearTimeout(id) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function toPacketBuffer(code: number, data:
|
|
1
|
+
export declare function toPacketBuffer(code: number, data: Uint8Array): Uint8Array;
|
|
2
2
|
export declare function splitBuffer(arr: Uint8Array, x: number): number[][];
|
|
3
3
|
export declare function as8String(data: Uint8Array): string;
|
|
4
4
|
export declare function as16String(data: Uint8Array): string;
|
|
@@ -10,6 +10,6 @@ export declare class BatchHelper {
|
|
|
10
10
|
registerSendPackets(packetHolder: PacketHolder, conn: Connection): void;
|
|
11
11
|
private initiateBatch;
|
|
12
12
|
private startBatch;
|
|
13
|
-
batchPacket(code: number, data:
|
|
13
|
+
batchPacket(code: number, data: Uint8Array): void;
|
|
14
14
|
static unravelBatch(packet: Packet<any>, data: Uint8Array, socket: SonicWSConnection | null): any[] | string;
|
|
15
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* Copyright
|
|
3
|
+
* Copyright 2026 Lily (liwybloc)
|
|
4
4
|
*
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
* you may not use this file except in compliance with the License.
|
|
@@ -39,10 +39,10 @@ class BatchHelper {
|
|
|
39
39
|
}
|
|
40
40
|
startBatch(code) {
|
|
41
41
|
this.batchTimeouts[code] = this.conn.setTimeout(() => {
|
|
42
|
-
this.conn.raw_send((0, BufferUtil_1.toPacketBuffer)(code, this.batchedData[code]));
|
|
42
|
+
this.conn.raw_send((0, BufferUtil_1.toPacketBuffer)(code, new Uint8Array(this.batchedData[code])));
|
|
43
43
|
this.batchedData[code] = [];
|
|
44
44
|
delete this.batchTimeouts[code];
|
|
45
|
-
}, this.batchTimes[code]);
|
|
45
|
+
}, this.batchTimes[code], false);
|
|
46
46
|
}
|
|
47
47
|
batchPacket(code, data) {
|
|
48
48
|
const batch = this.batchedData[code];
|
|
@@ -9,7 +9,7 @@ import { EnumPackage } from "../enums/EnumType";
|
|
|
9
9
|
* @param values The values
|
|
10
10
|
* @returns The indexed code, the data, and the packet schema
|
|
11
11
|
*/
|
|
12
|
-
export declare function processPacket(packets: PacketHolder, tag: string, values: any[]): [code: number, data:
|
|
12
|
+
export declare function processPacket(packets: PacketHolder, tag: string, values: any[]): [code: number, data: Uint8Array, packet: Packet<any>];
|
|
13
13
|
/**
|
|
14
14
|
* Calls the listener for a packet with error callback
|
|
15
15
|
* @param listened The listened data
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/*
|
|
3
|
-
* Copyright
|
|
3
|
+
* Copyright 2026 Lily (liwybloc)
|
|
4
4
|
*
|
|
5
5
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
* you may not use this file except in compliance with the License.
|
|
@@ -65,7 +65,7 @@ function processPacket(packets, tag, values) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
-
return [code, values.length > 0 ? packet.processSend(values) : [], packet];
|
|
68
|
+
return [code, values.length > 0 ? packet.processSend(values) : new Uint8Array([]), packet];
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
71
|
* Calls the listener for a packet with error callback
|
|
@@ -127,7 +127,9 @@ function clampDataMin(dataMin, dataMax) {
|
|
|
127
127
|
* @throws {Error} If the `type` is invalid.
|
|
128
128
|
*/
|
|
129
129
|
function CreatePacket(settings) {
|
|
130
|
-
let { tag, type = PacketType_1.PacketType.NONE, dataMax = 1, dataMin, noDataRange = false, dontSpread = false, validator = null, dataBatching = 0, maxBatchSize = 10, rateLimit = 0, enabled = true } = settings;
|
|
130
|
+
let { tag, type = PacketType_1.PacketType.NONE, dataMax = 1, dataMin = 1, noDataRange = false, dontSpread = false, validator = null, dataBatching = 0, maxBatchSize = 10, rateLimit = 0, enabled = true, } = settings;
|
|
131
|
+
if (!tag)
|
|
132
|
+
throw new Error("Tag not selected!");
|
|
131
133
|
if (noDataRange) {
|
|
132
134
|
dataMin = 0;
|
|
133
135
|
dataMax = MAX_DATA_MAX;
|
|
@@ -148,7 +150,11 @@ function CreatePacket(settings) {
|
|
|
148
150
|
* @throws {Error} If any type in `types` is invalid.
|
|
149
151
|
*/
|
|
150
152
|
function CreateObjPacket(settings) {
|
|
151
|
-
let { tag, types, dataMaxes, dataMins, noDataRange = false, dontSpread = false, autoFlatten = false, validator = null, dataBatching = 0, maxBatchSize = 10, rateLimit = 0, enabled = true } = settings;
|
|
153
|
+
let { tag, types = [], dataMaxes, dataMins, noDataRange = false, dontSpread = false, autoFlatten = false, validator = null, dataBatching = 0, maxBatchSize = 10, rateLimit = 0, enabled = true } = settings;
|
|
154
|
+
if (!tag)
|
|
155
|
+
throw new Error("Tag not selected!");
|
|
156
|
+
if (types.length == 0)
|
|
157
|
+
throw new Error("Types is set to 0 length");
|
|
152
158
|
for (const type of types) {
|
|
153
159
|
if (!isInvalidType(type))
|
|
154
160
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonic-ws",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Ultra-lightweight, high-performance, and bandwidth efficient websocket library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"author": "lily",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"ws": "^8.18.2"
|
|
27
|
+
"ws": "^8.18.2",
|
|
28
|
+
"zstd-codec": "^0.1.5"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@types/node": "^24.2.1",
|