sonic-ws 1.2.2 → 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.
- package/dist/index.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/ws/Connection.d.ts +14 -13
- package/dist/ws/Connection.js +33 -1
- package/dist/ws/PacketProcessor.d.ts +124 -0
- package/dist/ws/PacketProcessor.js +19 -0
- package/dist/ws/client/core/ClientCore.d.ts +9 -6
- package/dist/ws/client/core/ClientCore.js +61 -52
- package/dist/ws/packets/Packets.d.ts +1 -1
- package/dist/ws/server/SonicWSConnection.d.ts +15 -5
- package/dist/ws/server/SonicWSConnection.js +63 -45
- package/dist/ws/server/SonicWSServer.d.ts +25 -21
- package/dist/ws/server/SonicWSServer.js +656 -27
- package/dist/ws/util/packets/CompressionUtil.js +1 -1
- package/dist/ws/util/packets/HashUtil.d.ts +2 -0
- package/dist/ws/util/packets/HashUtil.js +122 -0
- package/dist/ws/util/packets/PacketHolder.d.ts +2 -2
- package/dist/ws/util/packets/PacketHolder.js +2 -0
- package/dist/ws/util/packets/PacketUtils.d.ts +4 -2
- package/dist/ws/util/packets/PacketUtils.js +57 -38
- package/dist/ws/util/packets/RateHandler.js +2 -1
- package/package.json +3 -4
|
@@ -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
|
-
//
|
|
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,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):
|
|
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
|
|
@@ -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[],
|
|
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
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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 --
|
|
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",
|