sonic-ws 1.0.5 → 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.
Files changed (37) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +7 -2
  3. package/dist/index.js +1 -1
  4. package/dist/version.d.ts +1 -1
  5. package/dist/version.js +2 -2
  6. package/dist/ws/Connection.d.ts +4 -3
  7. package/dist/ws/Connection.js +1 -1
  8. package/dist/ws/client/core/ClientCore.d.ts +5 -6
  9. package/dist/ws/client/core/ClientCore.js +10 -9
  10. package/dist/ws/client/node/ClientNode.js +1 -1
  11. package/dist/ws/packets/PacketProcessors.js +31 -33
  12. package/dist/ws/packets/PacketType.js +1 -1
  13. package/dist/ws/packets/Packets.d.ts +4 -4
  14. package/dist/ws/packets/Packets.js +4 -3
  15. package/dist/ws/server/SonicWSConnection.d.ts +17 -11
  16. package/dist/ws/server/SonicWSConnection.js +32 -12
  17. package/dist/ws/server/SonicWSServer.d.ts +28 -15
  18. package/dist/ws/server/SonicWSServer.js +32 -2
  19. package/dist/ws/util/ArrayUtil.js +1 -1
  20. package/dist/ws/util/BufferUtil.d.ts +1 -1
  21. package/dist/ws/util/BufferUtil.js +1 -1
  22. package/dist/ws/util/StringUtil.js +1 -1
  23. package/dist/ws/util/enums/EnumHandler.d.ts +2 -1
  24. package/dist/ws/util/enums/EnumHandler.js +5 -1
  25. package/dist/ws/util/enums/EnumType.d.ts +8 -2
  26. package/dist/ws/util/enums/EnumType.js +11 -1
  27. package/dist/ws/util/packets/BatchHelper.d.ts +4 -4
  28. package/dist/ws/util/packets/BatchHelper.js +3 -3
  29. package/dist/ws/util/packets/CompressionUtil.d.ts +4 -0
  30. package/dist/ws/util/packets/CompressionUtil.js +37 -7
  31. package/dist/ws/util/packets/PacketHolder.d.ts +8 -7
  32. package/dist/ws/util/packets/PacketHolder.js +1 -1
  33. package/dist/ws/util/packets/PacketUtils.d.ts +5 -4
  34. package/dist/ws/util/packets/PacketUtils.js +10 -4
  35. package/dist/ws/util/packets/RateHandler.d.ts +3 -3
  36. package/dist/ws/util/packets/RateHandler.js +1 -1
  37. package/package.json +3 -2
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2025 Lily (liwybloc)
1
+ Copyright 2026 Lily (liwybloc)
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  You may not use this software except in compliance with the License.
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
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
package/dist/version.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Current protocol version */
2
- export declare const VERSION = 12;
2
+ export declare const VERSION = 14;
3
3
  /** Server data suffix */
4
4
  export declare const SERVER_SUFFIX = "SWS";
5
5
  /** Server data suffix in array */
package/dist/version.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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 = 12;
21
+ exports.VERSION = 14;
22
22
  /** Server data suffix */
23
23
  exports.SERVER_SUFFIX = "SWS";
24
24
  /** Server data suffix in array */
@@ -3,17 +3,18 @@
3
3
  */
4
4
  export interface Connection {
5
5
  /**
6
- * List of timers, in object just for efficiency.
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
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -14,14 +14,14 @@ export declare abstract class SonicWSCore implements Connection {
14
14
  protected preListen: {
15
15
  [key: string]: Array<(data: any[]) => void>;
16
16
  } | null;
17
- protected clientPackets: PacketHolder<any>;
18
- protected serverPackets: PacketHolder<any>;
17
+ protected clientPackets: PacketHolder;
18
+ protected serverPackets: PacketHolder;
19
19
  private pastKeys;
20
20
  private readyListeners;
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 2025 Lily (liwybloc)
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).forEach(clearTimeout);
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.setTimer(timeout);
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.setTimer(interval);
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];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -37,7 +37,7 @@ function SHORT_LEN(cap, min) {
37
37
  function VARINT_VERIF(cap, min) {
38
38
  return (data) => {
39
39
  if (data.length == 0)
40
- return false;
40
+ return min <= 0 ? [] : false;
41
41
  let sectors = 0, i = 0, computed = [];
42
42
  while (i < data.length) {
43
43
  const [off, varint] = (0, CompressionUtil_1.readVarInt)(data, i);
@@ -96,20 +96,22 @@ function createValidator(type, dataCap, dataMin, packet) {
96
96
  }
97
97
  ;
98
98
  case PacketType_1.PacketType.STRINGS_ASCII: return (data) => {
99
- let sectors = 0, index = 0, computed = [];
100
- while (index < data.length) {
101
- sectors++;
102
- if (sectors > dataCap)
103
- return false;
104
- const [off, varint] = (0, CompressionUtil_1.readVarInt)(data, index);
105
- computed.push([off - index, varint]);
106
- index = off + varint;
107
- if (index > data.length)
108
- return false;
99
+ let index = 0;
100
+ const [offCount, stringCount] = (0, CompressionUtil_1.readVarInt)(data, index);
101
+ index = offCount;
102
+ if (stringCount < dataMin || stringCount > dataCap)
103
+ return false;
104
+ const lengths = [];
105
+ let totalLength = 0;
106
+ for (let i = 0; i < stringCount; i++) {
107
+ const [offLen, strLen] = (0, CompressionUtil_1.readVarInt)(data, index);
108
+ index = offLen;
109
+ lengths.push(strLen);
110
+ totalLength += strLen;
109
111
  }
110
- if (sectors < dataMin)
112
+ if (index + Math.ceil(totalLength / 8) > data.length)
111
113
  return false;
112
- return computed;
114
+ return [stringCount, lengths, index];
113
115
  };
114
116
  case PacketType_1.PacketType.STRINGS_UTF16: return (data) => {
115
117
  let sectors = 0, index = 0, computed = [];
@@ -154,14 +156,17 @@ function createReceiveProcessor(type, enumData, cap) {
154
156
  const pkg = enumData[index];
155
157
  return Array.from(data).map(code => pkg.values[code]);
156
158
  };
157
- case PacketType_1.PacketType.STRINGS_ASCII: return (data, computed) => {
159
+ case PacketType_1.PacketType.STRINGS_ASCII: return (data, validationResult) => {
160
+ const [stringCount, lengths, dataStart] = validationResult;
161
+ const bitString = (0, CompressionUtil_1.bytesToBits)(data.subarray(dataStart));
162
+ const decoded = (0, CompressionUtil_1.decodeHuffman)(bitString);
163
+ if (!decoded)
164
+ return [];
158
165
  const strings = [];
159
- let index = 0;
160
- for (let i = 0; i < computed.length; i++) {
161
- const [strLenLen, strLen] = computed[i];
162
- index += strLenLen;
163
- const str = (0, BufferUtil_1.as8String)(data.subarray(index, index += strLen));
164
- strings.push(str);
166
+ let offset = 0;
167
+ for (let i = 0; i < stringCount; i++) {
168
+ strings.push(decoded.slice(offset, offset + lengths[i]));
169
+ offset += lengths[i];
165
170
  }
166
171
  return strings;
167
172
  };
@@ -188,18 +193,11 @@ function createSendProcessor(type) {
188
193
  case PacketType_1.PacketType.DOUBLES: return (doubles) => doubles.map(CompressionUtil_1.convertDouble).flat();
189
194
  case PacketType_1.PacketType.BOOLEANS: return (bools) => (0, ArrayUtil_1.splitArray)(bools, 8).map((bools) => (0, CompressionUtil_1.compressBools)(bools)).flat();
190
195
  case PacketType_1.PacketType.STRINGS_ASCII: return (strings) => {
191
- const res = [];
192
- for (const v of strings) {
193
- const string = String(v);
194
- const lenVI = (0, CompressionUtil_1.convertVarInt)(string.length);
195
- res.push(...lenVI);
196
- const codes = (0, StringUtil_1.processCharCodes)(string);
197
- const highCode = codes.find(x => x > CompressionUtil_1.MAX_BYTE);
198
- if (highCode)
199
- throw new Error(`Cannot store code ${highCode} (${String.fromCharCode(highCode)}) in a UTF-8 String! Use STRINGS_UTF16.`);
200
- res.push(...codes);
201
- }
202
- return res;
196
+ return [
197
+ ...(0, CompressionUtil_1.convertVarInt)(strings.length),
198
+ ...strings.map(str => (0, CompressionUtil_1.convertVarInt)(str.length)).flat(),
199
+ ...(0, CompressionUtil_1.encodeHuffman)(strings.reduce((a, b) => a + String(b), "")),
200
+ ];
203
201
  };
204
202
  case PacketType_1.PacketType.STRINGS_UTF16: return (strings) => {
205
203
  const res = [];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -1,7 +1,7 @@
1
1
  import { EnumPackage } from "../util/enums/EnumType";
2
2
  import { SonicWSConnection } from "../server/SonicWSConnection";
3
3
  import { PacketType } from "./PacketType";
4
- export type ValidatorFunction = ((socket: SonicWSConnection<any, any>, values: any[]) => boolean) | null;
4
+ export type ValidatorFunction = ((socket: SonicWSConnection, values: any) => boolean) | null;
5
5
  export type ConvertType<T> = T extends EnumPackage ? PacketType.ENUMS : T;
6
6
  type ImpactType<T extends (PacketType | readonly PacketType[]), K> = T extends PacketType[] ? K[] : K;
7
7
  export declare class Packet<T extends (PacketType | readonly PacketType[])> {
@@ -24,11 +24,11 @@ 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[]) => number[];
27
+ processSend: (data: any[]) => Uint8Array;
28
28
  validate: (data: Uint8Array) => boolean;
29
- customValidator: ((socket: SonicWSConnection<any, any>, ...values: any[]) => boolean) | null;
29
+ customValidator: ((socket: SonicWSConnection, ...values: any[]) => boolean) | null;
30
30
  constructor(tag: string, schema: PacketSchema<T>, customValidator: ValidatorFunction, enabled: boolean, client: boolean);
31
- listen(value: Uint8Array, socket: SonicWSConnection<any, any> | null): [processed: any, flatten: boolean] | string;
31
+ listen(value: Uint8Array, socket: SonicWSConnection | null): [processed: any, flatten: boolean] | string;
32
32
  serialize(): number[];
33
33
  private static readVarInts;
34
34
  static deserialize(data: Uint8Array, offset: number, client: boolean): [packet: Packet<any>, offset: number];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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,14 +85,15 @@ 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
  }
92
92
  listen(value, socket) {
93
93
  try {
94
94
  const validationResult = this.validate(value);
95
- if (!this.client && validationResult == false)
95
+ // holy shit i used === to fix another bug
96
+ if (!this.client && validationResult === false)
96
97
  return "Invalid packet";
97
98
  const processed = this.processReceive(value, validationResult);
98
99
  const useableData = this.autoFlatten ? (0, PacketUtils_1.UnFlattenData)(processed) : processed;
@@ -1,9 +1,8 @@
1
1
  import * as WS from 'ws';
2
- import { PacketTypings, SonicWSServer } from './SonicWSServer';
2
+ import { SonicWSServer } from './SonicWSServer';
3
3
  import { Packet } from '../packets/Packets';
4
4
  import { Connection } from '../Connection';
5
- import { PacketResponse } from '../packets/PacketProcessors';
6
- export declare class SonicWSConnection<ClientTypes extends PacketTypings, ServerTypes extends PacketTypings> implements Connection {
5
+ export declare class SonicWSConnection implements Connection {
7
6
  /** Raw 'ws' library socket */
8
7
  socket: WS.WebSocket;
9
8
  private host;
@@ -20,8 +19,9 @@ export declare class SonicWSConnection<ClientTypes extends PacketTypings, Server
20
19
  handshakeComplete: boolean;
21
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. */
22
21
  id: number;
23
- _timers: Record<number, number>;
24
- constructor(socket: WS.WebSocket, host: SonicWSServer<ClientTypes, ServerTypes>, id: number, handshakePacket: string | null, clientRateLimit: number, serverRateLimit: number);
22
+ _timers: Record<number, [number, (closed: boolean) => void, boolean]>;
23
+ private closed;
24
+ constructor(socket: WS.WebSocket, host: SonicWSServer, id: number, handshakePacket: string | null, clientRateLimit: number, serverRateLimit: number);
25
25
  private parseData;
26
26
  private handshakeHandler;
27
27
  private invalidPacket;
@@ -46,17 +46,17 @@ export declare class SonicWSConnection<ClientTypes extends PacketTypings, Server
46
46
  * Listens for when the connection closes
47
47
  * @param listener Called when it closes
48
48
  */
49
- on_close(listener: (code: number, reason: Buffer) => void): void;
49
+ on_close(listener: (code: number, reason: string) => void): void;
50
50
  /**
51
51
  * Listens for a packet
52
52
  * @param tag The tag of the key to listen for
53
53
  * @param listener A function to listen for it
54
54
  */
55
- on<K extends number, T extends ClientTypes[K], Tag extends T['tag'] = T['tag'], Type extends T['type'] = T['type'], DataMax extends T['dataMax'] = T['dataMax']>(tag: Tag, listener: T['dontSpread'] extends true ? (packet: PacketResponse<Type, DataMax>) => void : (...packet: PacketResponse<Type, DataMax>) => void): void;
55
+ on(tag: string, listener: (...values: any) => void): void;
56
56
  /**
57
57
  * For internal use.
58
58
  */
59
- send_processed(code: number, data: number[], packet: Packet<ClientTypes[number]['type']>): void;
59
+ send_processed(code: number, data: Uint8Array, packet: Packet<any>): void;
60
60
  /**
61
61
  * Sends a packet with the tag and values
62
62
  * @param tag The tag to send
@@ -68,7 +68,7 @@ export declare class SonicWSConnection<ClientTypes extends PacketTypings, Server
68
68
  * @param tag The tag to send
69
69
  * @param values The values to send
70
70
  */
71
- broadcastFiltered(tag: string, filter: (socket: SonicWSConnection<ClientTypes, ServerTypes>) => boolean, ...values: any[]): void;
71
+ broadcastFiltered(tag: string, filter: (socket: SonicWSConnection) => boolean, ...values: any[]): void;
72
72
  /**
73
73
  * Broadcasts a packet to all other users connected
74
74
  * @param tag The tag to send
@@ -81,8 +81,14 @@ export declare class SonicWSConnection<ClientTypes extends PacketTypings, Server
81
81
  togglePrint(): void;
82
82
  raw_send(data: Uint8Array): void;
83
83
  close(code?: number, reason?: string | Buffer): void;
84
- setTimeout(call: () => void, time: number): number;
85
- setInterval(call: () => void, time: number): number;
84
+ setTimeout(call: () => void, time: number, callOnClose?: boolean): number;
85
+ setInterval(call: () => void, time: number, callOnClose?: boolean): number;
86
86
  clearTimeout(id: number): void;
87
87
  clearInterval(id: number): void;
88
+ /**
89
+ * Tags the socket with a key
90
+ * @param tag The tag to add
91
+ * @param replace If it should replace a previous tag; defaults to true. If using false, you can add multiple tags.
92
+ */
93
+ tag(tag: string, replace?: boolean): void;
88
94
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -67,7 +67,7 @@ class SonicWSConnection {
67
67
  handshakedMessageLambda = (data) => {
68
68
  const parsed = this.parseData(data);
69
69
  if (parsed == null)
70
- return;
70
+ return this.socket.close(4004);
71
71
  if (parsed[0] == this.handshakePacket)
72
72
  return this.socket.close(4005);
73
73
  this.messageHandler(parsed);
@@ -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
- Object.values(this._timers).forEach(clearTimeout);
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) {
@@ -143,7 +149,7 @@ class SonicWSConnection {
143
149
  handshakeHandler(data) {
144
150
  const parsed = this.parseData(data);
145
151
  if (parsed == null)
146
- return;
152
+ return this.socket.close(4004);
147
153
  if (parsed[0] != this.handshakePacket) {
148
154
  this.socket.close(4004);
149
155
  return;
@@ -154,9 +160,12 @@ class SonicWSConnection {
154
160
  this.handshakeComplete = true;
155
161
  }
156
162
  invalidPacket(listened) {
163
+ console.log("Closure cause", listened);
157
164
  this.socket.close(4003, listened);
158
165
  }
159
166
  listenPacket(data, tag) {
167
+ if (this.closed)
168
+ return;
160
169
  (0, PacketUtils_1.listenPacket)(data, this.listeners[tag], this.invalidPacket);
161
170
  }
162
171
  messageHandler(data) {
@@ -195,14 +204,14 @@ class SonicWSConnection {
195
204
  * @returns If it's closed or not
196
205
  */
197
206
  isClosed() {
198
- return this.socket.readyState == WS.CLOSED;
207
+ return this.closed || this.socket.readyState == WS.CLOSED;
199
208
  }
200
209
  /**
201
210
  * Listens for when the connection closes
202
211
  * @param listener Called when it closes
203
212
  */
204
213
  on_close(listener) {
205
- this.socket.on('close', listener);
214
+ this.socket.on('close', (code, reason) => listener(code, String(reason)));
206
215
  }
207
216
  /**
208
217
  * Listens for a packet
@@ -258,8 +267,10 @@ class SonicWSConnection {
258
267
  }
259
268
  /* JSDocs in Connection.ts class */
260
269
  raw_send(data) {
261
- if (this.isClosed())
262
- throw new Error("Connection is already closed!");
270
+ if (this.isClosed()) {
271
+ console.warn("WARN! Connection already closed when trying to send message!", this.id, (0, BufferUtil_1.stringifyBuffer)(data));
272
+ return;
273
+ }
263
274
  if (this.rater.trigger(SERVER_RATELIMIT_TAG))
264
275
  return;
265
276
  if (this.print)
@@ -267,19 +278,20 @@ class SonicWSConnection {
267
278
  this.socket.send(data);
268
279
  }
269
280
  close(code = 1000, reason) {
281
+ this.closed = true;
270
282
  this.socket.close(code, reason);
271
283
  }
272
- setTimeout(call, time) {
284
+ setTimeout(call, time, callOnClose = false) {
273
285
  const timeout = setTimeout(() => {
274
286
  call();
275
287
  this.clearTimeout(timeout);
276
288
  }, time);
277
- this._timers[timeout] = timeout;
289
+ this._timers[timeout] = [timeout, call, callOnClose];
278
290
  return timeout;
279
291
  }
280
- setInterval(call, time) {
292
+ setInterval(call, time, callOnClose = false) {
281
293
  const interval = setInterval(call, time);
282
- this._timers[interval] = interval;
294
+ this._timers[interval] = [interval, call, callOnClose];
283
295
  return interval;
284
296
  }
285
297
  clearTimeout(id) {
@@ -289,5 +301,13 @@ class SonicWSConnection {
289
301
  clearInterval(id) {
290
302
  this.clearTimeout(id);
291
303
  }
304
+ /**
305
+ * Tags the socket with a key
306
+ * @param tag The tag to add
307
+ * @param replace If it should replace a previous tag; defaults to true. If using false, you can add multiple tags.
308
+ */
309
+ tag(tag, replace = true) {
310
+ this.host.tag(this, tag, replace);
311
+ }
292
312
  }
293
313
  exports.SonicWSConnection = SonicWSConnection;
@@ -6,33 +6,35 @@ import { PacketType } from '../packets/PacketType';
6
6
  /**
7
7
  * Sonic WS Server Options
8
8
  */
9
- export type SonicServerOptions<ClientTypes extends PacketTypings, ServerTypes extends PacketTypings> = {
9
+ export type SonicServerOptions = {
10
10
  /** An array of packets the client can send and server can listen for; using CreatePacket(), CreateObjPacket(), and CreateEnumPacket() */
11
- readonly clientPackets?: ClientTypes;
11
+ readonly clientPackets?: PacketTypings;
12
12
  /** An array of packets the server can send and client can listen for; using CreatePacket(), CreateObjPacket(), and CreateEnumPacket() */
13
- readonly serverPackets?: ServerTypes;
13
+ readonly serverPackets?: PacketTypings;
14
14
  /** Default WS Options */
15
15
  readonly websocketOptions?: WS.ServerOptions;
16
16
  };
17
17
  export type PacketTypings = readonly Packet<PacketType | readonly PacketType[]>[];
18
- export declare class SonicWSServer<ClientTypes extends PacketTypings, ServerTypes extends PacketTypings> {
18
+ export declare class SonicWSServer {
19
19
  private wss;
20
20
  private availableIds;
21
21
  private lastId;
22
22
  private connectListeners;
23
- clientPackets: PacketHolder<ClientTypes>;
24
- serverPackets: PacketHolder<ServerTypes>;
25
- connections: SonicWSServer.Connection[];
23
+ clientPackets: PacketHolder;
24
+ serverPackets: PacketHolder;
25
+ connections: SonicWSConnection[];
26
26
  private connectionMap;
27
27
  private clientRateLimit;
28
28
  private serverRateLimit;
29
29
  private handshakePacket;
30
+ tags: Map<SonicWSConnection, Set<String>>;
31
+ tagsInv: Map<String, Set<SonicWSConnection>>;
30
32
  /**
31
33
  * Initializes and hosts a websocket with sonic protocol
32
34
  * Rate limits can be set with wss.setClientRateLimit(x) and wss.setServerRateLimit(x); it is defaulted at 500/second per both
33
35
  * @param settings Sonic Server Options such as schema data for client and server packets, alongside websocket options
34
36
  */
35
- constructor(settings: SonicServerOptions<ClientTypes, ServerTypes>);
37
+ constructor(settings: SonicServerOptions);
36
38
  private generateSocketID;
37
39
  /**
38
40
  * Requires each client to send this packet upon initialization
@@ -79,7 +81,7 @@ export declare class SonicWSServer<ClientTypes extends PacketTypings, ServerType
79
81
  * Listens for whenever a client connects
80
82
  * @param runner Called when ready
81
83
  */
82
- on_connect(runner: (client: SonicWSConnection<ClientTypes, ServerTypes>) => void): void;
84
+ on_connect(runner: (client: SonicWSConnection) => void): void;
83
85
  /**
84
86
  * Listens for whenever the server is ready
85
87
  * @param runner Called when ready
@@ -90,13 +92,20 @@ export declare class SonicWSServer<ClientTypes extends PacketTypings, ServerType
90
92
  * @param callback Called when server closes
91
93
  */
92
94
  shutdown(callback: (err?: Error) => void): void;
95
+ /**
96
+ * Broadcasts a packet to tagged users; this is fast as it is a record rather than looping and filtering
97
+ * @param tag The tag to send packets to
98
+ * @param packetTag Packet tag to send
99
+ * @param values Values to send
100
+ */
101
+ broadcastTagged(tag: string, packetTag: string, ...values: any): void;
93
102
  /**
94
103
  * Broadcasts a packet to all users connected, but with a filter
95
104
  * @param tag The tag to send
96
105
  * @param filter The filter for who to send to
97
106
  * @param values The values to send
98
107
  */
99
- broadcastFiltered(tag: string, filter: (socket: SonicWSConnection<ClientTypes, ServerTypes>) => boolean, ...values: any): void;
108
+ broadcastFiltered(tag: string, filter: (socket: SonicWSConnection) => boolean, ...values: any): void;
100
109
  /**
101
110
  * Broadcasts a packet to all users connected
102
111
  * @param tag The tag to send
@@ -106,18 +115,22 @@ export declare class SonicWSServer<ClientTypes extends PacketTypings, ServerType
106
115
  /**
107
116
  * @returns All users connected to the socket
108
117
  */
109
- getConnected(): SonicWSServer.Connection[];
118
+ getConnected(): SonicWSConnection[];
110
119
  /**
111
120
  * @param id The socket id
112
121
  * @returns The socket
113
122
  */
114
- getSocket(id: number): SonicWSConnection<ClientTypes, ServerTypes>;
123
+ getSocket(id: number): SonicWSConnection;
115
124
  /**
116
125
  * Closes a socket by id
117
126
  * @param id The socket id
118
127
  */
119
128
  closeSocket(id: number, code?: number, reason?: string | Buffer): void;
120
- }
121
- export declare namespace SonicWSServer {
122
- type Connection<ClientTypes extends PacketTypings = any, ServerTypes extends PacketTypings = any> = import('./SonicWSConnection').SonicWSConnection<ClientTypes, ServerTypes>;
129
+ /**
130
+ * Tags the socket with a key
131
+ * @param socket The socket to tag
132
+ * @param tag The tag to add
133
+ * @param replace If it should replace a previous tag; defaults to true. If using false, you can add multiple tags.
134
+ */
135
+ tag(socket: SonicWSConnection, tag: string, replace?: boolean): void;
123
136
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -67,6 +67,8 @@ class SonicWSServer {
67
67
  clientRateLimit = 500;
68
68
  serverRateLimit = 500;
69
69
  handshakePacket = null;
70
+ tags = new Map();
71
+ tagsInv = new Map();
70
72
  /**
71
73
  * Initializes and hosts a websocket with sonic protocol
72
74
  * Rate limits can be set with wss.setClientRateLimit(x) and wss.setServerRateLimit(x); it is defaulted at 500/second per both
@@ -200,6 +202,18 @@ class SonicWSServer {
200
202
  shutdown(callback) {
201
203
  this.wss.close(callback);
202
204
  }
205
+ /**
206
+ * Broadcasts a packet to tagged users; this is fast as it is a record rather than looping and filtering
207
+ * @param tag The tag to send packets to
208
+ * @param packetTag Packet tag to send
209
+ * @param values Values to send
210
+ */
211
+ broadcastTagged(tag, packetTag, ...values) {
212
+ if (!this.tagsInv.has(tag))
213
+ return;
214
+ const data = (0, PacketUtils_1.processPacket)(this.serverPackets, packetTag, values);
215
+ this.tagsInv.get(tag).forEach(conn => conn.send_processed(...data));
216
+ }
203
217
  /**
204
218
  * Broadcasts a packet to all users connected, but with a filter
205
219
  * @param tag The tag to send
@@ -208,7 +222,6 @@ class SonicWSServer {
208
222
  */
209
223
  broadcastFiltered(tag, filter, ...values) {
210
224
  const data = (0, PacketUtils_1.processPacket)(this.serverPackets, tag, values);
211
- // weird type bug here so i make it as any
212
225
  this.connections.filter(filter).forEach(conn => conn.send_processed(...data));
213
226
  }
214
227
  /**
@@ -239,5 +252,22 @@ class SonicWSServer {
239
252
  closeSocket(id, code = 1000, reason) {
240
253
  this.getSocket(id).close(code, reason);
241
254
  }
255
+ /**
256
+ * Tags the socket with a key
257
+ * @param socket The socket to tag
258
+ * @param tag The tag to add
259
+ * @param replace If it should replace a previous tag; defaults to true. If using false, you can add multiple tags.
260
+ */
261
+ tag(socket, tag, replace = true) {
262
+ if (!this.tags.get(socket))
263
+ this.tags.set(socket, new Set());
264
+ if (!this.tagsInv.get(tag))
265
+ this.tagsInv.set(tag, new Set());
266
+ if (replace) {
267
+ this.tags.get(socket).forEach(v => this.tagsInv.get(v)?.delete(socket));
268
+ }
269
+ this.tags.get(socket).add(tag);
270
+ this.tagsInv.get(tag).add(socket);
271
+ }
242
272
  }
243
273
  exports.SonicWSServer = SonicWSServer;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -1,4 +1,4 @@
1
- export declare function toPacketBuffer(code: number, data: number[]): Uint8Array;
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;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -9,7 +9,7 @@ export declare const SET_PACKAGES: Record<string, EnumPackage>;
9
9
  * @param values The possible values of the enum
10
10
  * @returns A packaged enum
11
11
  */
12
- export declare function DefineEnum(tag: string, values: any[]): EnumPackage;
12
+ export declare function DefineEnum(tag: string, values: any[] | readonly any[]): EnumPackage;
13
13
  /**
14
14
  * Wraps an enum into a transmittable format
15
15
  * @param tag The tag of the enum
@@ -17,3 +17,4 @@ export declare function DefineEnum(tag: string, values: any[]): EnumPackage;
17
17
  * @returns A transmittable enum value
18
18
  */
19
19
  export declare function WrapEnum(tag: string, value: any): number;
20
+ export declare function DeWrapEnum(tag: string, value: number): any;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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,6 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.SET_PACKAGES = exports.ENUM_KEY_TO_TAG = exports.ENUM_TAG_TO_KEY = exports.MAX_ENUM_SIZE = void 0;
19
19
  exports.DefineEnum = DefineEnum;
20
20
  exports.WrapEnum = WrapEnum;
21
+ exports.DeWrapEnum = DeWrapEnum;
21
22
  const CompressionUtil_1 = require("../packets/CompressionUtil");
22
23
  const EnumType_1 = require("./EnumType");
23
24
  exports.MAX_ENUM_SIZE = CompressionUtil_1.MAX_BYTE;
@@ -54,3 +55,6 @@ function WrapEnum(tag, value) {
54
55
  throw new Error(`Value "${value}" does not exist in enum "${tag}"`);
55
56
  return exports.ENUM_TAG_TO_KEY[tag][value];
56
57
  }
58
+ function DeWrapEnum(tag, value) {
59
+ return exports.ENUM_KEY_TO_TAG[tag][value];
60
+ }
@@ -2,7 +2,13 @@ export declare const TYPE_CONVERSION_MAP: Record<number, (data: string) => strin
2
2
  export type EnumValue = string | number | boolean | undefined | null;
3
3
  export declare class EnumPackage {
4
4
  tag: string;
5
- values: EnumValue[];
6
- constructor(tag: string, values: any[]);
5
+ values: EnumValue[] | readonly EnumValue[];
6
+ constructor(tag: string, values: any[] | readonly any[]);
7
7
  serialize(): number[];
8
+ /**
9
+ * Wraps a value with this enum package
10
+ * @param value Value to wrap
11
+ * @returns Network encoded value
12
+ */
13
+ wrap(value: any): number;
8
14
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -17,6 +17,7 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.EnumPackage = exports.TYPE_CONVERSION_MAP = void 0;
19
19
  const StringUtil_1 = require("../StringUtil");
20
+ const EnumHandler_1 = require("./EnumHandler");
20
21
  const TYPE_INDEX_MAP = {
21
22
  'string': 0,
22
23
  'number': 1,
@@ -43,6 +44,7 @@ class EnumPackage {
43
44
  constructor(tag, values) {
44
45
  this.tag = tag;
45
46
  this.values = values;
47
+ this.wrap = this.wrap.bind(this);
46
48
  }
47
49
  serialize() {
48
50
  const tag = (0, StringUtil_1.processCharCodes)(this.tag);
@@ -57,5 +59,13 @@ class EnumPackage {
57
59
  ]).flat(),
58
60
  ];
59
61
  }
62
+ /**
63
+ * Wraps a value with this enum package
64
+ * @param value Value to wrap
65
+ * @returns Network encoded value
66
+ */
67
+ wrap(value) {
68
+ return (0, EnumHandler_1.WrapEnum)(this.tag, value);
69
+ }
60
70
  }
61
71
  exports.EnumPackage = EnumPackage;
@@ -1,15 +1,15 @@
1
1
  import { Connection } from "../../Connection";
2
2
  import { Packet } from "../../packets/Packets";
3
- import { SonicWSServer } from "../../server/SonicWSServer";
3
+ import { SonicWSConnection } from "../../server/SonicWSConnection";
4
4
  import { PacketHolder } from "./PacketHolder";
5
5
  export declare class BatchHelper {
6
6
  private batchTimes;
7
7
  private batchTimeouts;
8
8
  private batchedData;
9
9
  private conn;
10
- registerSendPackets(packetHolder: PacketHolder<any>, conn: Connection): void;
10
+ registerSendPackets(packetHolder: PacketHolder, conn: Connection): void;
11
11
  private initiateBatch;
12
12
  private startBatch;
13
- batchPacket(code: number, data: number[]): void;
14
- static unravelBatch(packet: Packet<any>, data: Uint8Array, socket: SonicWSServer.Connection | null): any[] | string;
13
+ batchPacket(code: number, data: Uint8Array): void;
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 2025 Lily (liwybloc)
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];
@@ -35,3 +35,7 @@ export declare function mapShort_ZZ(short: number): SHORT_BITS;
35
35
  export declare function convertVarInt(num: number): number[];
36
36
  export declare function readVarInt(arr: number[] | Uint8Array, off: number): [offset: number, number: number];
37
37
  export declare function deconvertVarInts(arr: Uint8Array | number[]): number[];
38
+ export declare function bytesToBits(bytes: ArrayLike<number>): string;
39
+ export declare function bitsToBytes(bitString: string): Uint8Array;
40
+ export declare function encodeHuffman(text: string): Uint8Array;
41
+ export declare function decodeHuffman(bits: string): string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -32,6 +32,10 @@ exports.mapShort_ZZ = mapShort_ZZ;
32
32
  exports.convertVarInt = convertVarInt;
33
33
  exports.readVarInt = readVarInt;
34
34
  exports.deconvertVarInts = deconvertVarInts;
35
+ exports.bytesToBits = bytesToBits;
36
+ exports.bitsToBytes = bitsToBytes;
37
+ exports.encodeHuffman = encodeHuffman;
38
+ exports.decodeHuffman = decodeHuffman;
35
39
  const ArrayUtil_1 = require("../ArrayUtil");
36
40
  // this shit is so complex so i commented it...
37
41
  // the highest 8-bit
@@ -259,14 +263,10 @@ function deconvertDouble(bytes) {
259
263
  }
260
264
  // zig_zag
261
265
  function mapZigZag(n) {
262
- return (n << 1) // shifts left (multiply by 2 to get into zigzag)
263
- ^
264
- (n >> 15); // then xor the sign away
266
+ return ((n << 1) ^ (n >> 31));
265
267
  }
266
268
  function demapZigZag(n) {
267
- return (n >>> 1) // shifts right unsigned to remove the sign & divide by 2
268
- ^
269
- -(n & 1); // flips bits to give negative back
269
+ return (n >>> 1) ^ -((n & 1));
270
270
  }
271
271
  function demapShort_ZZ(short) {
272
272
  return demapZigZag(fromShort(short));
@@ -302,3 +302,33 @@ function deconvertVarInts(arr) {
302
302
  }
303
303
  return res;
304
304
  }
305
+ function bytesToBits(bytes) {
306
+ return Array.from(bytes).map(b => b.toString(2).padStart(8, '0')).join('');
307
+ }
308
+ function bitsToBytes(bitString) {
309
+ const bytes = [];
310
+ for (let i = 0; i < bitString.length; i += 8) {
311
+ const byte = bitString.slice(i, i + 8).padEnd(8, '0');
312
+ bytes.push(parseInt(byte, 2));
313
+ }
314
+ return new Uint8Array(bytes);
315
+ }
316
+ const codeToChar = { "1000001": "w", "1000010": "m", "1000100": "u", "1000101": "c", "1000110": "l", "1000111": "d", "1001001": "r", "1001010": "h", "1001100": "s", "1001101": "n", "1001110": "i", "1001111": "o", "1010001": "a", "1010010": "t", "1010100": "e", "1010101": "", "10000000": "ˆ", "10000001": "‡", "10000111": "†", "10010000": "…", "10010001": "„", "10010111": "ƒ", "10100000": "‚", "10100001": "", "10100111": "€", "10101100": "", "10101101": "~", "10101111": "}", "10110000": "|", "10110001": "{", "10110011": "`", "10110100": "_", "10110101": "^", "10110111": "]", "10111000": "", "10111001": "[", "10111011": "@", "10111100": "?", "10111101": ">", "10111111": "=", "11000000": "<", "11000001": ";", "11000011": ":", "11000100": "9", "11000101": "8", "11000111": "7", "11001000": "6", "11001001": "5", "11001011": "4", "11001100": "3", "11001101": "2", "11001111": "1", "11010000": "0", "11010001": "/", "11010011": ".", "11010100": "-", "11010101": ",", "11010111": "+", "11011000": "*", "11011001": ")", "11011011": "(", "11011100": "'", "11011101": "&", "11011111": "$", "11100001": "#", "11100010": "\"", "11100100": "!", "11100101": "\u001f", "11100110": "\u001e", "11100111": "\u001d", "11101001": "\u001c", "11101010": "\u001b", "11101100": "\u001a", "11101101": "\u0019", "11101110": "\u0018", "11101111": "\u0017", "11110001": "\u0016", "11110010": "\u0015", "11110100": "\u0014", "11110101": "\u0013", "11110110": "\u0012", "11110111": "\u0011", "11111001": "\u0010", "11111010": "\u000f", "11111100": "\u000e", "11111101": "", "11111110": "", "11111111": "", "100001100": "Ã", "100101100": "Â", "100101101": "Á", "101001100": "À", "101011100": "¿", "101011101": "¾", "101100100": "½", "101101100": "¼", "101101101": "»", "101110100": "º", "101111100": "¹", "101111101": "¸", "110000100": "·", "110001100": "¶", "110001101": "µ", "110010100": "´", "110011100": "³", "110011101": "²", "110100100": "±", "110101100": "°", "110101101": "¯", "110110100": "®", "110111100": "­", "110111101": "%", "111000000": "¬", "111000001": "«", "111000111": "ª", "111010000": "©", "111010001": "¨", "111010111": "§", "111100000": "¦", "111100001": "¥", "111100111": "¤", "111110000": "£", "111110001": "¢", "111110111": "¡", "1000011010": "á", "1010011010": "à", "1010011011": "ß", "1011001010": "Þ", "1011101010": "Ý", "1011101011": "Ü", "1100001010": "Û", "1100101010": "Ú", "1100101011": "Ù", "1101001010": "Ø", "1101101010": "×", "1101101011": "Ö", "1110001100": "Õ", "1110101100": "Ô", "1110101101": "Ó", "1111001100": "Ò", "1111101100": "Ñ", "1111101101": "Ð", "00000000": " ", "00000001": "Ÿ", "0000001": "\n", "0000010": "", "000001100": "Ï", "0000011010": "ç", "00000110110": "ó", "000001101110": "ù", "0000011011110": "ü", "00000110111110": "þ", "00000110111111": "ý", "00000111": "ž", "0000100": "\b", "0000101": "\u0007", "0000110": "\u0006", "0000111": "\u0005", "00010000": "", "00010001": "œ", "0001001": "\u0004", "0001010": "\u0003", "000101100": "Î", "000101101": "Í", "00010111": "›", "0001100": "\u0002", "0001101": "\u0001", "0001110": "", "0001111": "Z", "00100000": "š", "00100001": "™", "0010001": "Q", "0010010": "X", "001001100": "Ì", "0010011010": "æ", "0010011011": "å", "00100111": "˜", "0010100": "J", "0010101": "K", "0010110": "V", "0010111": "B", "00110000": "—", "00110001": "–", "0011001": "P", "0011010": "Y", "001101100": "Ë", "001101101": "Ê", "00110111": "•", "0011100": "G", "0011101": "F", "0011110": "W", "0011111": "M", "01000000": "”", "01000001": "“", "0100001": "U", "0100010": "C", "010001100": "É", "0100011010": "ä", "01000110110": "ò", "01000110111": "ñ", "01000111": "’", "0100100": "L", "0100101": "D", "0100110": "R", "0100111": "H", "01010000": "‘", "01010001": "", "0101001": "S", "0101010": "N", "010101100": "È", "010101101": "Ç", "01010111": "", "0101100": "I", "0101101": "O", "0101110": "A", "0101111": "T", "01100000": "Ž", "01100001": "", "0110001": "E", "0110010": "z", "011001100": "Æ", "0110011010": "ã", "0110011011": "â", "01100111": "Œ", "0110100": "q", "0110101": "x", "0110110": "j", "0110111": "k", "01110000": "‹", "01110001": "Š", "0111001": "v", "0111010": "b", "011101100": "Å", "011101101": "Ä", "01110111": "‰", "0111100": "p", "0111101": "y", "0111110": "g", "0111111": "f", "10000110110": "ð", "100001101110": "ø", "100001101111": "÷", "10110010110": "ï", "10110010111": "î", "11000010110": "í", "110000101110": "ö", "1100001011110": "û", "1100001011111": "ú", "11010010110": "ì", "11010010111": "ë", "11100011010": "ê", "111000110110": "õ", "111000110111": "ô", "11110011010": "é", "11110011011": "è" };
317
+ const charToCode = Object.fromEntries(Object.entries(codeToChar).map(([code, char]) => [char, code]));
318
+ function encodeHuffman(text) {
319
+ return bitsToBytes(Array.from(text).map(char => charToCode[char]).join(""));
320
+ }
321
+ ;
322
+ function decodeHuffman(bits) {
323
+ let result = '';
324
+ let buffer = '';
325
+ for (const bit of bits) {
326
+ buffer += bit;
327
+ if (codeToChar[buffer]) {
328
+ result += codeToChar[buffer];
329
+ buffer = '';
330
+ }
331
+ }
332
+ return result;
333
+ }
334
+ ;
@@ -1,8 +1,9 @@
1
1
  import { Packet } from "../../packets/Packets";
2
+ import { PacketTypings } from "../../server/SonicWSServer";
2
3
  /**
3
4
  * Holds and maps packets to indexed keys and tags for serialization and lookup
4
5
  */
5
- export declare class PacketHolder<Typings extends readonly Packet<any>[], K extends Typings[number] = Typings[number], Type extends K['type'] = K['type'], Tag extends K['tag'] = K['tag']> {
6
+ export declare class PacketHolder {
6
7
  /** Current key index for packet tags */
7
8
  private key;
8
9
  /** Maps tags to keys */
@@ -17,19 +18,19 @@ export declare class PacketHolder<Typings extends readonly Packet<any>[], K exte
17
18
  * Creates a new PacketHolder with an array of packets
18
19
  * @param packets Array of packets to register
19
20
  */
20
- constructor(packets?: Typings);
21
+ constructor(packets?: PacketTypings);
21
22
  /** Assigns a new unique key to a tag */
22
- createKey(tag: Tag): void;
23
+ createKey(tag: string): void;
23
24
  /**
24
25
  * Registers an array of packets and assigns them keys
25
26
  * @param packets Array of packets to register
26
27
  */
27
- holdPackets(packets: Typings): void;
28
+ holdPackets(packets: PacketTypings): void;
28
29
  /**
29
30
  * Returns the numeric key for a given tag
30
31
  * @param tag The packet tag
31
32
  */
32
- getKey(tag: Tag): number;
33
+ getKey(tag: string): number;
33
34
  /**
34
35
  * Returns the tag associated with a given character key
35
36
  * @param key A 1-character string key
@@ -39,7 +40,7 @@ export declare class PacketHolder<Typings extends readonly Packet<any>[], K exte
39
40
  * Returns the packet instance associated with a tag
40
41
  * @param tag The packet tag
41
42
  */
42
- getPacket(tag: string): Packet<Type>;
43
+ getPacket(tag: string): Packet<any>;
43
44
  /**
44
45
  * Checks if a given character key exists
45
46
  * @param key A string index
@@ -57,7 +58,7 @@ export declare class PacketHolder<Typings extends readonly Packet<any>[], K exte
57
58
  /** Returns an array of all registered tags */
58
59
  getTags(): string[];
59
60
  /** Returns the list of all registered packets */
60
- getPackets(): Typings;
61
+ getPackets(): PacketTypings;
61
62
  /** Serializes all registered packets into a string */
62
63
  serialize(): number[];
63
64
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
@@ -2,7 +2,6 @@ import { PacketHolder } from "./PacketHolder";
2
2
  import { ConvertType, Packet, ValidatorFunction } from "../../packets/Packets";
3
3
  import { PacketType } from "../../packets/PacketType";
4
4
  import { EnumPackage } from "../enums/EnumType";
5
- import { PacketTypings } from "../../server/SonicWSServer";
6
5
  /**
7
6
  * Processes and verifies values into a sendable format
8
7
  * @param packets Packet holder
@@ -10,7 +9,7 @@ import { PacketTypings } from "../../server/SonicWSServer";
10
9
  * @param values The values
11
10
  * @returns The indexed code, the data, and the packet schema
12
11
  */
13
- export declare function processPacket<T extends PacketTypings, K extends T[number]>(packets: PacketHolder<T>, tag: K['tag'], values: any[]): [code: number, data: number[], packet: Packet<K['type']>];
12
+ export declare function processPacket(packets: PacketHolder, tag: string, values: any[]): [code: number, data: Uint8Array, packet: Packet<any>];
14
13
  /**
15
14
  * Calls the listener for a packet with error callback
16
15
  * @param listened The listened data
@@ -95,9 +94,11 @@ export declare function CreatePacket<T extends ArguableType>(settings: SinglePac
95
94
  * @returns The constructed packet structure data.
96
95
  * @throws {Error} If any type in `types` is invalid.
97
96
  */
98
- export declare function CreateObjPacket<T extends readonly PacketType[]>(settings: MultiPacketSettings & {
97
+ export declare function CreateObjPacket<T extends readonly ArguableType[], V extends readonly PacketType[] = {
98
+ [K in keyof T]: ConvertType<T[K]>;
99
+ }>(settings: MultiPacketSettings & {
99
100
  readonly types: T;
100
- }): Packet<T>;
101
+ }): Packet<V>;
101
102
  /**
102
103
  * Creates and defines an enum packet. This can be used to create an enum-based packet
103
104
  * with a specific tag and possible values.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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;
@@ -1,14 +1,14 @@
1
- import { SonicWSServer } from "../../server/SonicWSServer";
1
+ import { SonicWSConnection } from "../../server/SonicWSConnection";
2
2
  import { PacketHolder } from "./PacketHolder";
3
3
  export declare class RateHandler {
4
4
  private rates;
5
5
  private limits;
6
6
  private setInterval;
7
7
  private socket;
8
- constructor(host: SonicWSServer.Connection);
8
+ constructor(host: SonicWSConnection);
9
9
  start(): void;
10
10
  registerRate(tag: string, limit: number): void;
11
- registerAll(packetHolder: PacketHolder<any>, prefix: string): void;
11
+ registerAll(packetHolder: PacketHolder, prefix: string): void;
12
12
  trigger(tag: string | number): boolean;
13
13
  subtract(tag: string | number): void;
14
14
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- * Copyright 2025 Lily (liwybloc)
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sonic-ws",
3
- "version": "1.0.5",
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",