cloudstorm 0.3.1 → 0.4.1
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.md +1 -1
- package/README.md +3 -5
- package/dist/Client.d.ts +16 -6
- package/dist/Client.js +9 -18
- package/dist/Constants.d.ts +2 -50
- package/dist/Constants.js +2 -2
- package/dist/Intents.d.ts +23 -28
- package/dist/Intents.js +11 -47
- package/dist/Shard.d.ts +9 -4
- package/dist/Shard.js +3 -9
- package/dist/ShardManager.d.ts +10 -4
- package/dist/ShardManager.js +15 -32
- package/dist/Types.d.ts +4 -31
- package/dist/connector/DiscordConnector.d.ts +13 -7
- package/dist/connector/DiscordConnector.js +67 -96
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/structures/BetterWs.d.ts +17 -53
- package/dist/structures/BetterWs.js +500 -153
- package/dist/structures/RatelimitBucket.d.ts +1 -0
- package/dist/structures/RatelimitBucket.js +4 -7
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -16
|
@@ -3,191 +3,538 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
const events_1 = require("events");
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
catch (e) {
|
|
12
|
-
Erlpack = null;
|
|
13
|
-
}
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const zlib_1 = require("zlib");
|
|
8
|
+
const https_1 = require("https");
|
|
9
|
+
const util_1 = __importDefault(require("util"));
|
|
10
|
+
const Z_SYNC_FLUSH = zlib_1.constants.Z_SYNC_FLUSH;
|
|
14
11
|
const Constants_1 = require("../Constants");
|
|
15
|
-
const ws_1 = __importDefault(require("ws"));
|
|
16
12
|
const RatelimitBucket_1 = __importDefault(require("./RatelimitBucket"));
|
|
17
13
|
/**
|
|
18
14
|
* Helper Class for simplifying the websocket connection to Discord.
|
|
19
15
|
*/
|
|
20
16
|
class BetterWs extends events_1.EventEmitter {
|
|
21
|
-
|
|
22
|
-
* Create a new BetterWs instance.
|
|
23
|
-
*/
|
|
24
|
-
constructor(address, options = {}) {
|
|
17
|
+
constructor(address, options) {
|
|
25
18
|
super();
|
|
26
|
-
this.zlibInflate = null;
|
|
27
|
-
this.ws = new ws_1.default(address, options.socket);
|
|
28
|
-
this.bindWs(this.ws);
|
|
29
19
|
this.wsBucket = new RatelimitBucket_1.default(120, 60000);
|
|
30
|
-
this.presenceBucket = new RatelimitBucket_1.default(5,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
else
|
|
36
|
-
this.compress = false;
|
|
20
|
+
this.presenceBucket = new RatelimitBucket_1.default(5, 60000);
|
|
21
|
+
this._connecting = false;
|
|
22
|
+
this.encoding = options.encoding === "etf" ? "etf" : "json";
|
|
23
|
+
this.compress = options.compress || false;
|
|
24
|
+
this.address = address;
|
|
37
25
|
this.options = options;
|
|
26
|
+
this._socket = null;
|
|
27
|
+
this._internal = {
|
|
28
|
+
closePromise: null,
|
|
29
|
+
zlib: null,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
get status() {
|
|
33
|
+
const internal = this._internal;
|
|
34
|
+
if (this._connecting)
|
|
35
|
+
return 2;
|
|
36
|
+
if (internal.closePromise)
|
|
37
|
+
return 3; // closing
|
|
38
|
+
if (!this._socket)
|
|
39
|
+
return 4; // closed
|
|
40
|
+
return 1; // connected
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
connect() {
|
|
43
|
+
if (this._socket)
|
|
44
|
+
return Promise.resolve(void 0);
|
|
45
|
+
const key = (0, crypto_1.randomBytes)(16).toString("base64");
|
|
46
|
+
this.emit("debug", "Creating connection");
|
|
47
|
+
const url = new URL(this.address);
|
|
48
|
+
const req = (0, https_1.request)({
|
|
49
|
+
hostname: url.hostname,
|
|
50
|
+
path: `${url.pathname}${url.search}`,
|
|
51
|
+
headers: {
|
|
52
|
+
"Connection": "Upgrade",
|
|
53
|
+
"Upgrade": "websocket",
|
|
54
|
+
"Sec-WebSocket-Key": key,
|
|
55
|
+
"Sec-WebSocket-Version": "13",
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
this._connecting = true;
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
req.on("upgrade", (res, socket) => {
|
|
61
|
+
const hash = (0, crypto_1.createHash)("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
|
|
62
|
+
const accept = res.headers["sec-websocket-accept"];
|
|
63
|
+
if (hash !== accept) {
|
|
64
|
+
socket.end(() => {
|
|
65
|
+
this.emit("debug", "Failed websocket-key validation");
|
|
66
|
+
this._connecting = false;
|
|
67
|
+
reject(new Error(`Invalid Sec-Websocket-Accept | expected: ${hash} | received: ${accept}`));
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
socket.on("error", this._onError.bind(this));
|
|
72
|
+
socket.on("close", this._onClose.bind(this));
|
|
73
|
+
socket.on("readable", this._onReadable.bind(this));
|
|
74
|
+
this._socket = socket;
|
|
75
|
+
this._connecting = false;
|
|
76
|
+
if (this.compress) {
|
|
77
|
+
const z = (0, zlib_1.createInflate)();
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
z._c = z.close;
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
z._h = z._handle;
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
z._hc = z._handle.close;
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
z._v = () => void 0;
|
|
86
|
+
this._internal.zlib = z;
|
|
87
|
+
}
|
|
88
|
+
this.emit("ws_open");
|
|
89
|
+
resolve(void 0);
|
|
90
|
+
});
|
|
91
|
+
req.on("error", e => {
|
|
92
|
+
this._connecting = false;
|
|
93
|
+
reject(e);
|
|
94
|
+
});
|
|
95
|
+
req.end();
|
|
96
|
+
});
|
|
44
97
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
98
|
+
async close() {
|
|
99
|
+
const internal = this._internal;
|
|
100
|
+
if (internal.closePromise)
|
|
101
|
+
return internal.closePromise;
|
|
102
|
+
if (!this._socket)
|
|
103
|
+
return Promise.resolve(void 0);
|
|
104
|
+
let resolver;
|
|
105
|
+
const promise = new Promise(resolve => {
|
|
106
|
+
resolver = resolve;
|
|
107
|
+
this._write(Buffer.allocUnsafe(0), 8);
|
|
108
|
+
}).then(() => {
|
|
109
|
+
internal.closePromise = null;
|
|
52
110
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
111
|
+
// @ts-ignore
|
|
112
|
+
promise.resolve = resolver;
|
|
113
|
+
internal.closePromise = promise;
|
|
114
|
+
}
|
|
115
|
+
sendMessage(data) {
|
|
116
|
+
if (!isValidRequest(data))
|
|
117
|
+
return Promise.reject(new Error("Invalid request"));
|
|
118
|
+
return new Promise(res => {
|
|
119
|
+
const presence = data.op === Constants_1.GATEWAY_OP_CODES.PRESENCE_UPDATE;
|
|
120
|
+
const sendMsg = () => {
|
|
121
|
+
this.wsBucket.queue(() => {
|
|
122
|
+
this.emit("debug_send", data);
|
|
123
|
+
if (this.encoding === "json")
|
|
124
|
+
this._write(Buffer.from(JSON.stringify(data)), 1);
|
|
125
|
+
else {
|
|
126
|
+
const etf = writeETF(data);
|
|
127
|
+
this._write(etf, 2);
|
|
128
|
+
}
|
|
129
|
+
res(void 0);
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
if (presence)
|
|
133
|
+
this.presenceBucket.queue(sendMsg);
|
|
134
|
+
else
|
|
135
|
+
sendMsg();
|
|
56
136
|
});
|
|
57
|
-
ws.on("open", () => this.onOpen());
|
|
58
137
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
138
|
+
_write(packet, opcode) {
|
|
139
|
+
const socket = this._socket;
|
|
140
|
+
if (!socket || !socket.writable)
|
|
141
|
+
return;
|
|
142
|
+
const length = packet.length;
|
|
143
|
+
let frame;
|
|
144
|
+
if (length < 126) {
|
|
145
|
+
frame = Buffer.allocUnsafe(6 + length);
|
|
146
|
+
frame[1] = 128 + length;
|
|
147
|
+
}
|
|
148
|
+
else if (length < (1 << 16)) {
|
|
149
|
+
frame = Buffer.allocUnsafe(8 + length);
|
|
150
|
+
frame[1] = 254;
|
|
151
|
+
frame[2] = length >> 8;
|
|
152
|
+
frame[3] = length & 255;
|
|
69
153
|
}
|
|
70
154
|
else {
|
|
71
|
-
|
|
72
|
-
|
|
155
|
+
frame = Buffer.allocUnsafe(14 + length);
|
|
156
|
+
frame[1] = 255;
|
|
157
|
+
frame.writeBigUInt64BE(BigInt(length), 2);
|
|
73
158
|
}
|
|
74
|
-
|
|
75
|
-
|
|
159
|
+
frame[0] = 128 + opcode;
|
|
160
|
+
frame.writeUInt32BE(0, frame.length - length - 4);
|
|
161
|
+
frame.set(packet, frame.length - length);
|
|
162
|
+
socket.write(frame);
|
|
163
|
+
}
|
|
164
|
+
_onError(error) {
|
|
165
|
+
if (!this._socket)
|
|
166
|
+
return;
|
|
167
|
+
this.emit("debug", util_1.default.inspect(error, true, 1, false));
|
|
168
|
+
this._write(Buffer.allocUnsafe(0), 8);
|
|
169
|
+
}
|
|
170
|
+
_onClose() {
|
|
171
|
+
const socket = this._socket;
|
|
172
|
+
const internal = this._internal;
|
|
173
|
+
if (!socket)
|
|
174
|
+
return;
|
|
175
|
+
this.emit("debug", "Connection closed");
|
|
176
|
+
socket.removeListener("data", this._onReadable);
|
|
177
|
+
socket.removeListener("error", this._onError);
|
|
178
|
+
socket.removeListener("close", this._onClose);
|
|
76
179
|
this.wsBucket.dropQueue();
|
|
77
180
|
this.presenceBucket.dropQueue();
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
181
|
+
this._socket = null;
|
|
182
|
+
if (internal.zlib) {
|
|
183
|
+
internal.zlib.close();
|
|
184
|
+
internal.zlib = null;
|
|
185
|
+
}
|
|
186
|
+
if (internal.closePromise) {
|
|
187
|
+
// @ts-ignore
|
|
188
|
+
internal.closePromise.resolve(void 0);
|
|
189
|
+
}
|
|
87
190
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
let msg;
|
|
97
|
-
if (this.compress && this.zlibInflate) {
|
|
98
|
-
const length = message.length;
|
|
99
|
-
const flush = length >= 4 &&
|
|
100
|
-
message[length - 4] === 0x00 &&
|
|
101
|
-
message[length - 3] === 0x00 &&
|
|
102
|
-
message[length - 2] === 0xFF &&
|
|
103
|
-
message[length - 1] === 0xFF;
|
|
104
|
-
this.zlibInflate.push(message, flush ? zlib_sync_1.default.Z_SYNC_FLUSH : false);
|
|
105
|
-
if (!flush)
|
|
191
|
+
_onReadable() {
|
|
192
|
+
const socket = this._socket;
|
|
193
|
+
while (socket.readableLength > 1) {
|
|
194
|
+
let length = readRange(socket, 1, 1) & 127;
|
|
195
|
+
let bytes = 0;
|
|
196
|
+
if (length > 125) {
|
|
197
|
+
bytes = length === 126 ? 2 : 8;
|
|
198
|
+
if (socket.readableLength < 2 + bytes)
|
|
106
199
|
return;
|
|
107
|
-
|
|
200
|
+
length = readRange(socket, 2, bytes);
|
|
108
201
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
202
|
+
const frame = socket.read(2 + bytes + length);
|
|
203
|
+
if (!frame)
|
|
204
|
+
return;
|
|
205
|
+
const fin = frame[0] >> 7;
|
|
206
|
+
const opcode = frame[0] & 15;
|
|
207
|
+
if (fin !== 1 || opcode === 0)
|
|
208
|
+
this.emit("debug", "discord actually does send messages with fin=0. if you see this error let me know");
|
|
209
|
+
const payload = frame.slice(2 + bytes);
|
|
210
|
+
this._processFrame(opcode, payload);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
_processFrame(opcode, message) {
|
|
214
|
+
const internal = this._internal;
|
|
215
|
+
switch (opcode) {
|
|
216
|
+
case 1: {
|
|
217
|
+
const packet = JSON.parse(message.toString());
|
|
218
|
+
this.emit("ws_message", packet);
|
|
219
|
+
break;
|
|
113
220
|
}
|
|
114
|
-
|
|
115
|
-
|
|
221
|
+
case 2: {
|
|
222
|
+
let packet;
|
|
223
|
+
if (this.compress) {
|
|
224
|
+
const z = internal.zlib;
|
|
225
|
+
let error = null;
|
|
226
|
+
let data = null;
|
|
227
|
+
// @ts-ignore
|
|
228
|
+
z.close = z._handle.close = z._v;
|
|
229
|
+
try {
|
|
230
|
+
// @ts-ignore
|
|
231
|
+
data = z._processChunk(message, Z_SYNC_FLUSH);
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
error = e;
|
|
235
|
+
}
|
|
236
|
+
const l = message.length;
|
|
237
|
+
if (message[l - 4] !== 0 || message[l - 3] !== 0 || message[l - 2] !== 255 || message[l - 1] !== 255)
|
|
238
|
+
this.emit("debug", "discord actually does send fragmented zlib messages. if you see this error let me know");
|
|
239
|
+
// @ts-ignore
|
|
240
|
+
z.close = z._c;
|
|
241
|
+
// @ts-ignore
|
|
242
|
+
z._handle = z._h;
|
|
243
|
+
// @ts-ignore
|
|
244
|
+
z._handle.close = z._hc;
|
|
245
|
+
// @ts-ignore
|
|
246
|
+
z._events.error = void 0;
|
|
247
|
+
// @ts-ignore
|
|
248
|
+
z._eventCount--;
|
|
249
|
+
z.removeAllListeners("error");
|
|
250
|
+
if (error) {
|
|
251
|
+
this.emit("debug", "Zlib error");
|
|
252
|
+
this._write(Buffer.allocUnsafe(0), 8);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (!data)
|
|
256
|
+
return; // This should never run, but TS is lame
|
|
257
|
+
packet = this.encoding === "json" ? JSON.parse(String(data)) : readETF(data, 1);
|
|
258
|
+
}
|
|
259
|
+
else if (this.encoding === "json") {
|
|
260
|
+
const data = (0, zlib_1.inflateSync)(message);
|
|
261
|
+
packet = JSON.parse(data.toString());
|
|
262
|
+
}
|
|
263
|
+
else
|
|
264
|
+
packet = readETF(message, 1);
|
|
265
|
+
this.emit("ws_message", packet);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case 8: {
|
|
269
|
+
const code = message.length > 1 ? (message[0] << 8) + message[1] : 0;
|
|
270
|
+
const reason = message.length > 2 ? message.slice(2).toString() : "";
|
|
271
|
+
this.emit("ws_close", code, reason);
|
|
272
|
+
this._write(Buffer.from([code >> 8, code & 255]), 8);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case 9: {
|
|
276
|
+
this._write(message, 10);
|
|
277
|
+
break;
|
|
116
278
|
}
|
|
117
279
|
}
|
|
118
|
-
catch (e) {
|
|
119
|
-
this.emit("error", `Message: ${message} was not parseable`);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
this.emit("ws_message", parsed);
|
|
123
280
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
281
|
+
}
|
|
282
|
+
function isValidRequest(value) {
|
|
283
|
+
return value && typeof value === "object" && Number.isInteger(value.op) && typeof value.d !== "undefined";
|
|
284
|
+
}
|
|
285
|
+
function readRange(socket, index, bytes) {
|
|
286
|
+
// @ts-ignore
|
|
287
|
+
let head = socket._readableState.buffer.head;
|
|
288
|
+
let cursor = 0;
|
|
289
|
+
let read = 0;
|
|
290
|
+
let num = 0;
|
|
291
|
+
do {
|
|
292
|
+
for (let i = 0; i < head.data.length; i++) {
|
|
293
|
+
if (++cursor > index) {
|
|
294
|
+
num *= 256;
|
|
295
|
+
num += head.data[i];
|
|
296
|
+
if (++read === bytes)
|
|
297
|
+
return num;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
} while ((head = head.next));
|
|
301
|
+
throw new Error("readRange failed?");
|
|
302
|
+
}
|
|
303
|
+
function readETF(data, start) {
|
|
304
|
+
let view;
|
|
305
|
+
let x = start;
|
|
306
|
+
const loop = () => {
|
|
307
|
+
const type = data[x++];
|
|
308
|
+
switch (type) {
|
|
309
|
+
case 97: {
|
|
310
|
+
return data[x++];
|
|
311
|
+
}
|
|
312
|
+
case 98: {
|
|
313
|
+
const int = data.readInt32BE(x);
|
|
314
|
+
x += 4;
|
|
315
|
+
return int;
|
|
316
|
+
}
|
|
317
|
+
case 100: {
|
|
318
|
+
const length = data.readUInt16BE(x);
|
|
319
|
+
let atom = "";
|
|
320
|
+
if (length > 30) {
|
|
321
|
+
// @ts-ignore
|
|
322
|
+
atom = data.latin1Slice(x += 2, x + length);
|
|
145
323
|
}
|
|
146
324
|
else {
|
|
147
|
-
|
|
325
|
+
for (let i = x += 2; i < x + length; i++) {
|
|
326
|
+
atom += String.fromCharCode(data[i]);
|
|
327
|
+
}
|
|
148
328
|
}
|
|
329
|
+
x += length;
|
|
330
|
+
if (!atom)
|
|
331
|
+
return undefined;
|
|
332
|
+
if (atom === "nil" || atom === "null")
|
|
333
|
+
return null;
|
|
334
|
+
if (atom === "true")
|
|
335
|
+
return true;
|
|
336
|
+
if (atom === "false")
|
|
337
|
+
return false;
|
|
338
|
+
return atom;
|
|
149
339
|
}
|
|
150
|
-
|
|
151
|
-
|
|
340
|
+
case 108:
|
|
341
|
+
case 106: {
|
|
342
|
+
const array = [];
|
|
343
|
+
if (type === 108) {
|
|
344
|
+
const length = data.readUInt32BE(x);
|
|
345
|
+
x += 4;
|
|
346
|
+
for (let i = 0; i < length; i++) {
|
|
347
|
+
array.push(loop());
|
|
348
|
+
}
|
|
349
|
+
x++;
|
|
350
|
+
}
|
|
351
|
+
return array;
|
|
152
352
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
-
if (presence) {
|
|
165
|
-
// same here
|
|
166
|
-
this.presenceBucket.queue(sendMsg);
|
|
353
|
+
case 107: {
|
|
354
|
+
const array = [];
|
|
355
|
+
const length = data.readUInt16BE(x);
|
|
356
|
+
x += 2;
|
|
357
|
+
for (let i = 0; i < length; i++) {
|
|
358
|
+
array.push(data[x++]);
|
|
359
|
+
}
|
|
360
|
+
return array;
|
|
167
361
|
}
|
|
168
|
-
|
|
169
|
-
|
|
362
|
+
case 109: {
|
|
363
|
+
const length = data.readUInt32BE(x);
|
|
364
|
+
let str = "";
|
|
365
|
+
if (length > 30) {
|
|
366
|
+
// @ts-ignore
|
|
367
|
+
str = data.utf8Slice(x += 4, x + length);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
let i = x += 4;
|
|
371
|
+
const l = x + length;
|
|
372
|
+
while (i < l) {
|
|
373
|
+
const byte = data[i++];
|
|
374
|
+
if (byte < 128)
|
|
375
|
+
str += String.fromCharCode(byte);
|
|
376
|
+
else if (byte < 224)
|
|
377
|
+
str += String.fromCharCode(((byte & 31) << 6) + (data[i++] & 63));
|
|
378
|
+
else if (byte < 240)
|
|
379
|
+
str += String.fromCharCode(((byte & 15) << 12) + ((data[i++] & 63) << 6) + (data[i++] & 63));
|
|
380
|
+
else
|
|
381
|
+
str += String.fromCodePoint(((byte & 7) << 18) + ((data[i++] & 63) << 12) + ((data[i++] & 63) << 6) + (data[i++] & 63));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
x += length;
|
|
385
|
+
return str;
|
|
170
386
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
387
|
+
case 110: {
|
|
388
|
+
// @ts-ignore
|
|
389
|
+
if (!view)
|
|
390
|
+
view = new DataView(data.buffer, data.offset, data.byteLength);
|
|
391
|
+
const length = data[x++];
|
|
392
|
+
const sign = data[x++];
|
|
393
|
+
let left = length;
|
|
394
|
+
let num = BigInt(0);
|
|
395
|
+
while (left > 0) {
|
|
396
|
+
if (left >= 8) {
|
|
397
|
+
num <<= BigInt(64);
|
|
398
|
+
num += view.getBigUint64(x + (left -= 8), true);
|
|
399
|
+
}
|
|
400
|
+
else if (left >= 4) {
|
|
401
|
+
num <<= BigInt(32);
|
|
402
|
+
// @ts-ignore
|
|
403
|
+
num += BigInt(view.getUint32(x + (left -= 4)), true);
|
|
404
|
+
}
|
|
405
|
+
else if (left >= 2) {
|
|
406
|
+
num <<= BigInt(16);
|
|
407
|
+
// @ts-ignore
|
|
408
|
+
num += BigInt(view.getUint16(x + (left -= 2)), true);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
num <<= BigInt(8);
|
|
412
|
+
num += BigInt(data[x]);
|
|
413
|
+
left--;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
x += length;
|
|
417
|
+
return (sign ? -num : num).toString();
|
|
418
|
+
}
|
|
419
|
+
case 116: {
|
|
420
|
+
const obj = {};
|
|
421
|
+
const length = data.readUInt32BE(x);
|
|
422
|
+
x += 4;
|
|
423
|
+
for (let i = 0; i < length; i++) {
|
|
424
|
+
const key = loop();
|
|
425
|
+
// @ts-ignore
|
|
426
|
+
obj[key] = loop();
|
|
427
|
+
}
|
|
428
|
+
return obj;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
throw new Error(`Missing etf type: ${type}`);
|
|
432
|
+
};
|
|
433
|
+
return loop();
|
|
434
|
+
}
|
|
435
|
+
function writeETF(data) {
|
|
436
|
+
const b = Buffer.allocUnsafe(1 << 12);
|
|
437
|
+
b[0] = 131;
|
|
438
|
+
let i = 1;
|
|
439
|
+
const loop = (obj) => {
|
|
440
|
+
const type = typeof obj;
|
|
441
|
+
switch (type) {
|
|
442
|
+
case "boolean": {
|
|
443
|
+
b[i++] = 100;
|
|
444
|
+
if (obj) {
|
|
445
|
+
b.writeUInt16BE(4, i);
|
|
446
|
+
// @ts-ignore
|
|
447
|
+
b.latin1Write("true", i += 2);
|
|
448
|
+
i += 4;
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
b.writeUInt16BE(5, i);
|
|
452
|
+
// @ts-ignore
|
|
453
|
+
b.latin1Write("false", i += 2);
|
|
454
|
+
i += 5;
|
|
455
|
+
}
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
case "string": {
|
|
459
|
+
const length = Buffer.byteLength(obj);
|
|
460
|
+
b[i++] = 109;
|
|
461
|
+
b.writeUInt32BE(length, i);
|
|
462
|
+
// @ts-ignore
|
|
463
|
+
b.utf8Write(obj, i += 4);
|
|
464
|
+
i += length;
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
case "number": {
|
|
468
|
+
if (Number.isInteger(obj)) {
|
|
469
|
+
const abs = Math.abs(obj);
|
|
470
|
+
if (abs < 2147483648) {
|
|
471
|
+
b[i++] = 98;
|
|
472
|
+
b.writeInt32BE(obj, i);
|
|
473
|
+
i += 4;
|
|
474
|
+
}
|
|
475
|
+
else if (abs < Number.MAX_SAFE_INTEGER) {
|
|
476
|
+
b[i++] = 110;
|
|
477
|
+
b[i++] = 8;
|
|
478
|
+
b[i++] = Number(obj < 0);
|
|
479
|
+
b.writeBigUInt64LE(BigInt(abs), i);
|
|
480
|
+
i += 8;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
b[i++] = 70;
|
|
485
|
+
b.writeDoubleBE(obj, i);
|
|
486
|
+
i += 8;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
b[i++] = 70;
|
|
491
|
+
b.writeDoubleBE(obj, i);
|
|
492
|
+
i += 8;
|
|
493
|
+
}
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
case "bigint": {
|
|
497
|
+
b[i++] = 110;
|
|
498
|
+
b[i++] = 8;
|
|
499
|
+
b[i++] = Number(obj < 0);
|
|
500
|
+
b.writeBigUInt64LE(obj, i);
|
|
501
|
+
i += 8;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case "object": {
|
|
505
|
+
if (obj === null) {
|
|
506
|
+
b[i++] = 100;
|
|
507
|
+
b.writeUInt16BE(3, i);
|
|
508
|
+
// @ts-ignore
|
|
509
|
+
b.latin1Write("nil", i += 2);
|
|
510
|
+
i += 3;
|
|
511
|
+
}
|
|
512
|
+
else if (Array.isArray(obj)) {
|
|
513
|
+
if (obj.length) {
|
|
514
|
+
b[i++] = 108;
|
|
515
|
+
b.writeUInt32BE(obj.length, i);
|
|
516
|
+
i += 4;
|
|
517
|
+
for (const item of obj) {
|
|
518
|
+
loop(item);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
b[i++] = 106;
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
const entries = Object.entries(obj).filter(x => typeof x[1] !== "undefined");
|
|
525
|
+
b[i++] = 116;
|
|
526
|
+
b.writeUInt32BE(entries.length, i);
|
|
527
|
+
i += 4;
|
|
528
|
+
for (const [key, value] of entries) {
|
|
529
|
+
loop(key);
|
|
530
|
+
loop(value);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
loop(data);
|
|
538
|
+
return Buffer.from(b.slice(0, i));
|
|
192
539
|
}
|
|
193
540
|
module.exports = BetterWs;
|