sonic-ws 1.2.2-why → 1.3.0-min

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.
@@ -361,7 +361,7 @@ async function decompressGzip(data) {
361
361
  const buffer = await new Response(stream).arrayBuffer();
362
362
  return new Uint8Array(buffer);
363
363
  }
364
- // BOOLEANS
364
+ // json (why did i say booleans idk)
365
365
  var JSONType;
366
366
  (function (JSONType) {
367
367
  JSONType[JSONType["NULL"] = 0] = "NULL";
@@ -0,0 +1,2 @@
1
+ export declare function setHashFunc(use64Bit: boolean): void;
2
+ export declare function hashValue(value: any): bigint | number;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright 2026 Lily (liwybloc)
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.setHashFunc = setHashFunc;
19
+ exports.hashValue = hashValue;
20
+ const HASH_INIT_64 = 14695981039346656037n;
21
+ const HASH_PRIME_64 = 1099511628211n;
22
+ const MASK_64 = (1n << 64n) - 1n;
23
+ const hashValue64 = (value) => {
24
+ let hash = HASH_INIT_64;
25
+ const walk = (v) => {
26
+ if (v === null) {
27
+ hash ^= 0n;
28
+ hash = (hash * HASH_PRIME_64) & MASK_64;
29
+ return;
30
+ }
31
+ const t = typeof v;
32
+ if (t === "number") {
33
+ hash ^= BigInt(Math.trunc(v));
34
+ hash = (hash * HASH_PRIME_64) & MASK_64;
35
+ return;
36
+ }
37
+ if (t === "string") {
38
+ for (let i = 0; i < v.length; i++) {
39
+ hash ^= BigInt(v.charCodeAt(i));
40
+ hash = (hash * HASH_PRIME_64) & MASK_64;
41
+ }
42
+ return;
43
+ }
44
+ if (t === "boolean") {
45
+ hash ^= v ? 1n : 0n;
46
+ hash = (hash * HASH_PRIME_64) & MASK_64;
47
+ return;
48
+ }
49
+ if (Array.isArray(v)) {
50
+ for (let i = 0; i < v.length; i++) {
51
+ walk(v[i]);
52
+ }
53
+ return;
54
+ }
55
+ if (t === "object") {
56
+ const keys = Object.keys(v).sort();
57
+ for (let i = 0; i < keys.length; i++) {
58
+ const k = keys[i];
59
+ for (let j = 0; j < k.length; j++) {
60
+ hash ^= BigInt(k.charCodeAt(j));
61
+ hash = (hash * HASH_PRIME_64) & MASK_64;
62
+ }
63
+ walk(v[k]);
64
+ }
65
+ }
66
+ };
67
+ walk(value);
68
+ return hash;
69
+ };
70
+ const HASH_INIT = 2166136261;
71
+ const hashValue32 = (value) => {
72
+ let hash = HASH_INIT;
73
+ const walk = (v) => {
74
+ if (v === null) {
75
+ hash ^= 0;
76
+ return;
77
+ }
78
+ const t = typeof v;
79
+ if (t === "number") {
80
+ hash ^= v | 0;
81
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
82
+ return;
83
+ }
84
+ if (t === "string") {
85
+ for (let i = 0; i < v.length; i++) {
86
+ hash ^= v.charCodeAt(i);
87
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
88
+ }
89
+ return;
90
+ }
91
+ if (t === "boolean") {
92
+ hash ^= v ? 1 : 0;
93
+ return;
94
+ }
95
+ if (Array.isArray(v)) {
96
+ for (let i = 0; i < v.length; i++) {
97
+ walk(v[i]);
98
+ }
99
+ return;
100
+ }
101
+ if (t === "object") {
102
+ const keys = Object.keys(v).sort();
103
+ for (let i = 0; i < keys.length; i++) {
104
+ const k = keys[i];
105
+ for (let j = 0; j < k.length; j++) {
106
+ hash ^= k.charCodeAt(j);
107
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
108
+ }
109
+ walk(v[k]);
110
+ }
111
+ }
112
+ };
113
+ walk(value);
114
+ return hash >>> 0;
115
+ };
116
+ let hashFunc = hashValue64;
117
+ function setHashFunc(use64Bit) {
118
+ hashFunc = use64Bit ? hashValue64 : hashValue32;
119
+ }
120
+ function hashValue(value) {
121
+ return hashFunc(value);
122
+ }
@@ -35,7 +35,7 @@ export declare class PacketHolder {
35
35
  * Returns the tag associated with a given character key
36
36
  * @param key Key bytre
37
37
  */
38
- getTag(key: number): string;
38
+ getTag(key: number): string | undefined;
39
39
  /**
40
40
  * Returns the packet instance associated with a tag
41
41
  * @param tag The packet tag
@@ -45,7 +45,7 @@ export declare class PacketHolder {
45
45
  * Checks if a given character key exists
46
46
  * @param key A string index
47
47
  */
48
- hasKey(key: number): boolean;
48
+ hasKey(key: number): key is keyof typeof this.tags;
49
49
  /**
50
50
  * Checks if a tag has been assigned a key
51
51
  * @param tag The packet tag
@@ -74,6 +74,8 @@ class PacketHolder {
74
74
  * @param key Key bytre
75
75
  */
76
76
  getTag(key) {
77
+ if (!(key in this.tags))
78
+ return undefined;
77
79
  return this.tags[key];
78
80
  }
79
81
  /**
@@ -2,6 +2,8 @@ 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 { SendQueue } from "../../PacketProcessor";
6
+ export type ProcessedPacket = [code: number, data: Uint8Array, packet: Packet<any>];
5
7
  /**
6
8
  * Processes and verifies values into a sendable format
7
9
  * @param packets Packet holder
@@ -9,7 +11,7 @@ import { EnumPackage } from "../enums/EnumType";
9
11
  * @param values The values
10
12
  * @returns The indexed code, the data, and the packet schema
11
13
  */
12
- export declare function processPacket(packets: PacketHolder, tag: string, values: any[], id: number): Promise<[code: number, data: Uint8Array, packet: Packet<any>]>;
14
+ export declare function processPacket(packets: PacketHolder, tag: string, values: any[], sendQueue: SendQueue, id: number, force?: boolean): Promise<ProcessedPacket>;
13
15
  /**
14
16
  * Calls the listener for a packet with error callback
15
17
  * @param listened The listened data
@@ -95,7 +97,7 @@ export type KeyEffectivePacketSettings = SharedPacketSettings & {
95
97
  * @throws {Error} If the `type` is invalid.
96
98
  */
97
99
  export declare function CreatePacket<T extends ArguableType>(settings: SinglePacketSettings & {
98
- type: T;
100
+ type?: T;
99
101
  }): Packet<ConvertType<T>>;
100
102
  /**
101
103
  * Creates a structure for an object (multi-typed) packet.
@@ -27,6 +27,7 @@ const Packets_1 = require("../../packets/Packets");
27
27
  const PacketType_1 = require("../../packets/PacketType");
28
28
  const EnumType_1 = require("../enums/EnumType");
29
29
  const CompressionUtil_1 = require("./CompressionUtil");
30
+ const HashUtil_1 = require("./HashUtil");
30
31
  /**
31
32
  * Processes and verifies values into a sendable format
32
33
  * @param packets Packet holder
@@ -34,50 +35,68 @@ const CompressionUtil_1 = require("./CompressionUtil");
34
35
  * @param values The values
35
36
  * @returns The indexed code, the data, and the packet schema
36
37
  */
37
- async function processPacket(packets, tag, values, id) {
38
+ async function processPacket(packets, tag, values, sendQueue, id, force = false) {
38
39
  const code = packets.getKey(tag);
39
40
  const packet = packets.getPacket(tag);
40
- if (packet.autoFlatten) {
41
- values = FlattenData(values[0]);
42
- }
43
- else {
44
- if (values.length > packet.maxSize)
45
- throw new Error(`Packet "${tag}" only allows ${packet.maxSize} values!`);
46
- if (values.length < packet.minSize)
47
- throw new Error(`Packet "${tag}" requires at least ${packet.minSize} values!`);
48
- }
49
- if (!packet.object) {
50
- if (packet.type != PacketType_1.PacketType.JSON) {
51
- const found = values.find(v => typeof v == 'object' && v != null);
52
- if (found)
53
- console.warn(`Passing an array will result in undefined behavior (${JSON.stringify(found)}). Spread the array with ...arr`);
41
+ return handleQueue(sendQueue, packets, id, tag, values, force, async () => {
42
+ if (packet.rereference) {
43
+ if (id === -1)
44
+ throw new Error("Cannot send a re-referenced packet from the server-wide sender!");
45
+ const serialized = (0, HashUtil_1.hashValue)(values);
46
+ if (packet.lastSent[id] === serialized) {
47
+ return [code, CompressionUtil_1.EMPTY_UINT8, packet];
48
+ }
49
+ packet.lastSent[id] = serialized;
54
50
  }
55
- }
56
- else {
57
- // also map non arrays to arrays to keep some code cleaner
58
- values = values.map(x => !Array.isArray(x) ? [x] : x);
59
- // something is weird with this
60
- if (!packet.autoFlatten) {
61
- const dataMins = packet.dataMin;
62
- const dataMaxes = packet.dataMax;
63
- for (let i = 0; i < dataMins.length; i++) {
64
- // these will be the same length
65
- if (values[i].length < dataMins[i])
66
- throw new Error(`Section ${i + 1} of packet "${tag}" requires at least ${dataMins[i]} values!`);
67
- if (values[i].length > dataMaxes[i])
68
- throw new Error(`Section ${i + 1} of packet "${tag}" only allows ${dataMaxes[i]} values!`);
51
+ if (packet.autoFlatten) {
52
+ values = FlattenData(values[0]);
53
+ }
54
+ else {
55
+ if (values.length > packet.maxSize)
56
+ throw new Error(`Packet "${tag}" only allows ${packet.maxSize} values!`);
57
+ if (values.length < packet.minSize)
58
+ throw new Error(`Packet "${tag}" requires at least ${packet.minSize} values!`);
59
+ }
60
+ if (!packet.object) {
61
+ if (packet.type !== PacketType_1.PacketType.JSON) {
62
+ const found = values.find(v => typeof v === 'object' && v != null);
63
+ if (found)
64
+ console.warn(`Passing an array will result in undefined behavior (${JSON.stringify(found)}). Spread the array with ...arr`);
65
+ }
66
+ }
67
+ else {
68
+ values = values.map(x => !Array.isArray(x) ? [x] : x);
69
+ if (!packet.autoFlatten) {
70
+ const dataMins = packet.dataMin;
71
+ const dataMaxes = packet.dataMax;
72
+ for (let i = 0; i < dataMins.length; i++) {
73
+ if (values[i].length < dataMins[i])
74
+ throw new Error(`Section ${i + 1} of packet "${tag}" requires at least ${dataMins[i]} values!`);
75
+ if (values[i].length > dataMaxes[i])
76
+ throw new Error(`Section ${i + 1} of packet "${tag}" only allows ${dataMaxes[i]} values!`);
77
+ }
69
78
  }
70
79
  }
80
+ const sendData = values.length > 0 ? await packet.processSend(values) : CompressionUtil_1.EMPTY_UINT8;
81
+ return [code, sendData, packet];
82
+ });
83
+ }
84
+ async function handleQueue(sendQueue, packets, id, tag, values, force, fn) {
85
+ if (sendQueue[0] && !force) {
86
+ return new Promise((resolve) => sendQueue[1].push([resolve, tag, values]));
71
87
  }
72
- const sendData = values.length > 0 ? await packet.processSend(values) : CompressionUtil_1.EMPTY_UINT8;
73
- if (packet.rereference) {
74
- if (id == -1)
75
- throw new Error("Cannot broadcast a packet that is rereference-enabled");
76
- if (packet.lastSent[id] == sendData)
77
- return [code, CompressionUtil_1.EMPTY_UINT8, packet];
78
- packet.lastSent[id] = sendData;
88
+ sendQueue[0] = true;
89
+ const result = await fn();
90
+ if (sendQueue[1].length > 0) {
91
+ const [resolve, nextTag, nextValues] = sendQueue[1].shift();
92
+ queueMicrotask(async () => {
93
+ resolve(await processPacket(packets, nextTag, nextValues, sendQueue, id, true));
94
+ });
95
+ }
96
+ else {
97
+ sendQueue[0] = false;
79
98
  }
80
- return [code, sendData, packet];
99
+ return result;
81
100
  }
82
101
  /**
83
102
  * Calls the listener for a packet with error callback
@@ -173,7 +192,7 @@ function CreateObjPacket(settings) {
173
192
  let { tag, types = [], dataMaxes, dataMins, noDataRange = false, dontSpread = false, autoFlatten = false, validator = null, dataBatching = 0, maxBatchSize = 10, rateLimit = 0, enabled = true, async = false, gzipCompression = types && types.includes(PacketType_1.PacketType.JSON) } = settings;
174
193
  if (!tag)
175
194
  throw new Error("Tag not selected!");
176
- if (types.length == 0)
195
+ if (!types || types.length == 0)
177
196
  throw new Error("Types is set to 0 length");
178
197
  for (const type of types) {
179
198
  if (!isInvalidType(type))
@@ -16,6 +16,7 @@
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.RateHandler = void 0;
19
+ const Connection_1 = require("../../Connection");
19
20
  class RateHandler {
20
21
  rates = {};
21
22
  limits = {};
@@ -50,7 +51,7 @@ class RateHandler {
50
51
  }
51
52
  trigger(tag) {
52
53
  if (tag in this.rates && ++this.rates[tag] > this.limits[tag]) {
53
- this.socket.close(4000);
54
+ this.socket.close(Connection_1.CloseCodes.RATELIMIT);
54
55
  return true;
55
56
  }
56
57
  return false;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "sonic-ws",
3
- "version": "1.2.2-why",
3
+ "version": "1.3.0-min",
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",
7
7
  "scripts": {
8
8
  "webpack": "webpack --mode production",
9
- "build_web": "rimraf ./html && tsc --moduleResolution nodenext --module nodenext --target ES6 --outdir ./html src/ws/client/browser/ClientBrowser.ts && npm run webpack",
9
+ "build_web": "rimraf ./html && tsc --moduleResolution nodenext --module nodenext --outDir ./html src/ws/client/browser/ClientBrowser.ts && npm run webpack",
10
10
  "build_node": "rimraf ./dist && tsc -d",
11
11
  "build": "npm run build_node && npm run build_web",
12
12
  "test_web": "npm run build && node test-site/server.mjs",
@@ -24,8 +24,7 @@
24
24
  "author": "lily",
25
25
  "license": "Apache-2.0",
26
26
  "dependencies": {
27
- "ws": "^8.18.2",
28
- "zstd-codec": "^0.1.5"
27
+ "ws": "^8.18.2"
29
28
  },
30
29
  "devDependencies": {
31
30
  "@types/node": "^24.2.1",