@eryxenx/insta-chat 2.0.11 → 2.1.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.
@@ -0,0 +1,609 @@
1
+ /*!
2
+ * insta-chat - Unofficial Instagram Chat API
3
+ * Copyright (c) 2026 EryXenX (https://github.com/EryXenX)
4
+ * Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE.
5
+ * Original author credit must be retained in all copies and derivatives.
6
+ */
7
+
8
+ // The MQTToT wire format Instagram's realtime/push brokers speak. It is MQTT
9
+ // 3.1.1 with a Facebook twist: the CONNECT payload is a Thrift compact struct
10
+ // (MQTToTConnection) instead of the spec's UTF-8 fields, and publish payloads
11
+ // are zlib-compressed. This module is a dependency-free codec (Thrift compact
12
+ // protocol + MQTT packets) plus a TLS transport, so the realtime client needs
13
+ // nothing beyond Node's net/tls/zlib.
14
+
15
+ "use strict";
16
+
17
+ const tls = require("tls");
18
+ const zlib = require("zlib");
19
+
20
+ const ThriftTypes = {
21
+ STOP: 0x00,
22
+ TRUE: 0x01,
23
+ FALSE: 0x02,
24
+ BYTE: 0x03,
25
+ INT_16: 0x04,
26
+ INT_32: 0x05,
27
+ INT_64: 0x06,
28
+ DOUBLE: 0x07,
29
+ BINARY: 0x08,
30
+ LIST: 0x09,
31
+ SET: 0x0a,
32
+ MAP: 0x0b,
33
+ STRUCT: 0x0c,
34
+ BOOLEAN: 0xa1,
35
+ LIST_INT_32: (0x05 << 8) | 0x09,
36
+ LIST_BINARY: (0x08 << 8) | 0x09,
37
+ MAP_BINARY_BINARY: (0x88 << 8) | 0x0b
38
+ };
39
+
40
+ // Thrift compact integers are zigzag + LEB128. Values above 2^53 (appId, a
41
+ // 64-bit userId) need BigInt, so the varint writer always works in BigInt.
42
+ function zigzag(value, bits) {
43
+ const v = typeof value === "bigint" ? value : BigInt(value);
44
+ return (v << 1n) ^ (v >> BigInt(bits - 1));
45
+ }
46
+
47
+ function unzigzag(value) {
48
+ const v = typeof value === "bigint" ? value : BigInt(value);
49
+ return (v >> 1n) ^ -(v & 1n);
50
+ }
51
+
52
+ class ThriftWriter {
53
+ constructor() {
54
+ this.buffer = [];
55
+ this._fieldStack = [];
56
+ this._field = 0;
57
+ }
58
+
59
+ writeStop() {
60
+ this.buffer.push(ThriftTypes.STOP);
61
+ if (this._fieldStack.length) this._field = this._fieldStack.pop();
62
+ }
63
+
64
+ writeField(field, fieldType) {
65
+ const delta = field - this._field;
66
+ const thriftType = fieldType & 0x0f;
67
+ if (delta > 0 && delta <= 15) {
68
+ this.buffer.push((delta << 4) | thriftType);
69
+ } else {
70
+ this.buffer.push(thriftType);
71
+ this.writeVarint(zigzag(field, 16));
72
+ }
73
+ this._field = field;
74
+ }
75
+
76
+ writeVarint(value) {
77
+ let v = typeof value === "bigint" ? value : BigInt(value);
78
+ if (v < 0n) v &= 0xffffffffffffffffn;
79
+ for (;;) {
80
+ if ((v & ~0x7fn) === 0n) {
81
+ this.buffer.push(Number(v));
82
+ return;
83
+ }
84
+ this.buffer.push(Number((v & 0x7fn) | 0x80n));
85
+ v >>= 7n;
86
+ }
87
+ }
88
+
89
+ writeStringDirect(value) {
90
+ const raw = Buffer.from(String(value), "utf8");
91
+ this.writeVarint(raw.length);
92
+ for (const b of raw) this.buffer.push(b);
93
+ }
94
+
95
+ writeString(field, value) {
96
+ this.writeField(field, ThriftTypes.BINARY);
97
+ this.writeStringDirect(value);
98
+ }
99
+
100
+ writeBool(field, value) {
101
+ this.writeField(field, value ? ThriftTypes.TRUE : ThriftTypes.FALSE);
102
+ }
103
+
104
+ writeInt(field, value, bits) {
105
+ const fieldType = { 8: ThriftTypes.BYTE, 16: ThriftTypes.INT_16, 32: ThriftTypes.INT_32, 64: ThriftTypes.INT_64 }[bits];
106
+ this.writeField(field, fieldType);
107
+ if (bits === 8) {
108
+ this.buffer.push(Number(BigInt(value) & 0xffn));
109
+ } else {
110
+ this.writeVarint(zigzag(value, bits));
111
+ }
112
+ }
113
+
114
+ pushStruct(field) {
115
+ this.writeField(field, ThriftTypes.STRUCT);
116
+ this._fieldStack.push(this._field);
117
+ this._field = 0;
118
+ }
119
+ }
120
+
121
+ class ThriftReader {
122
+ constructor(data) {
123
+ this.data = data;
124
+ this.pos = 0;
125
+ this._fieldStack = [];
126
+ this._field = 0;
127
+ }
128
+
129
+ readStruct(descriptors) {
130
+ const result = {};
131
+ while (this.pos < this.data.length) {
132
+ const fieldType = this.readField();
133
+ if (fieldType === ThriftTypes.STOP) {
134
+ if (this._fieldStack.length) this._field = this._fieldStack.pop();
135
+ break;
136
+ }
137
+ const descriptor = findDescriptor(descriptors, this._field, fieldType);
138
+ const value = this.readValue(fieldType, descriptor);
139
+ if (descriptor) result[descriptor.name] = value;
140
+ }
141
+ return result;
142
+ }
143
+
144
+ readField() {
145
+ const byte = this.readByte();
146
+ if (byte === ThriftTypes.STOP) return ThriftTypes.STOP;
147
+ const delta = (byte & 0xf0) >> 4;
148
+ if (delta) this._field += delta;
149
+ else this._field = Number(unzigzag(this.readVarint()));
150
+ return byte & 0x0f;
151
+ }
152
+
153
+ readValue(fieldType, descriptor) {
154
+ if (fieldType === ThriftTypes.TRUE) return true;
155
+ if (fieldType === ThriftTypes.FALSE) return false;
156
+ if (fieldType === ThriftTypes.BYTE) return this.readBytes(1).readInt8(0);
157
+ if (fieldType === ThriftTypes.INT_16 || fieldType === ThriftTypes.INT_32 || fieldType === ThriftTypes.INT_64) {
158
+ return Number(unzigzag(this.readVarint()));
159
+ }
160
+ if (fieldType === ThriftTypes.BINARY) return this.readBytes(Number(this.readVarint())).toString("utf8");
161
+ if (fieldType === ThriftTypes.LIST) return this.readList();
162
+ if (fieldType === ThriftTypes.MAP) return this.readMap();
163
+ if (fieldType === ThriftTypes.STRUCT) {
164
+ this._fieldStack.push(this._field);
165
+ this._field = 0;
166
+ return this.readStruct(descriptor ? descriptor.children : []);
167
+ }
168
+ throw new Error(`Unsupported thrift type: ${fieldType}`);
169
+ }
170
+
171
+ readList() {
172
+ const header = this.readByte();
173
+ let size = header >> 4;
174
+ const itemType = header & 0x0f;
175
+ if (size === 0x0f) size = Number(this.readVarint());
176
+ const items = [];
177
+ for (let i = 0; i < size; i++) items.push(this.readValue(itemType, null));
178
+ return items;
179
+ }
180
+
181
+ readMap() {
182
+ const size = Number(this.readVarint());
183
+ if (!size) return {};
184
+ const itemTypes = this.readByte();
185
+ const keyType = (itemTypes & 0xf0) >> 4;
186
+ const valueType = itemTypes & 0x0f;
187
+ const result = {};
188
+ for (let i = 0; i < size; i++) {
189
+ const key = this.readValue(keyType, null);
190
+ result[key] = this.readValue(valueType, null);
191
+ }
192
+ return result;
193
+ }
194
+
195
+ readByte() {
196
+ const value = this.data[this.pos];
197
+ this.pos += 1;
198
+ return value;
199
+ }
200
+
201
+ readBytes(size) {
202
+ const value = this.data.slice(this.pos, this.pos + Number(size));
203
+ this.pos += Number(size);
204
+ return value;
205
+ }
206
+
207
+ readVarint() {
208
+ let shift = 0n;
209
+ let result = 0n;
210
+ while (this.pos < this.data.length) {
211
+ const byte = this.readByte();
212
+ result |= BigInt(byte & 0x7f) << shift;
213
+ if ((byte & 0x80) === 0) break;
214
+ shift += 7n;
215
+ }
216
+ return result;
217
+ }
218
+ }
219
+
220
+ function findDescriptor(descriptors, field, fieldType) {
221
+ for (const descriptor of descriptors) {
222
+ if (descriptor.field !== field) continue;
223
+ const descriptorType = descriptor.type & 0x0f;
224
+ if (descriptorType === fieldType) return descriptor;
225
+ if (descriptor.type === ThriftTypes.BOOLEAN && (fieldType === ThriftTypes.TRUE || fieldType === ThriftTypes.FALSE)) return descriptor;
226
+ }
227
+ return null;
228
+ }
229
+
230
+ function writeThriftStruct(writer, data, descriptors) {
231
+ const byName = {};
232
+ for (const d of descriptors) byName[d.name] = d;
233
+ for (const [name, value] of Object.entries(data)) {
234
+ if (value === null || value === undefined) continue;
235
+ const descriptor = byName[name];
236
+ if (!descriptor) continue;
237
+ const thriftType = descriptor.type & 0xff;
238
+ if (thriftType === ThriftTypes.BOOLEAN) writer.writeBool(descriptor.field, !!value);
239
+ else if (thriftType === ThriftTypes.BYTE) writer.writeInt(descriptor.field, value, 8);
240
+ else if (thriftType === ThriftTypes.INT_16) writer.writeInt(descriptor.field, value, 16);
241
+ else if (thriftType === ThriftTypes.INT_32) writer.writeInt(descriptor.field, value, 32);
242
+ else if (thriftType === ThriftTypes.INT_64) writer.writeInt(descriptor.field, value, 64);
243
+ else if (thriftType === ThriftTypes.BINARY) writer.writeString(descriptor.field, value);
244
+ else if (thriftType === ThriftTypes.STRUCT) {
245
+ writer.pushStruct(descriptor.field);
246
+ writeThriftStruct(writer, value, descriptor.children);
247
+ writer.writeStop();
248
+ } else if (thriftType === ThriftTypes.LIST) {
249
+ writer.writeField(descriptor.field, ThriftTypes.LIST);
250
+ writeThriftList(writer, descriptor.type >> 8, value);
251
+ } else if (thriftType === ThriftTypes.MAP) {
252
+ writeThriftMap(writer, descriptor.field, value);
253
+ } else {
254
+ throw new Error(`Unsupported thrift type: ${descriptor.type}`);
255
+ }
256
+ }
257
+ }
258
+
259
+ function writeThriftList(writer, itemType, values) {
260
+ const size = values.length;
261
+ if (size < 0x0f) writer.buffer.push((size << 4) | itemType);
262
+ else {
263
+ writer.buffer.push(0xf0 | itemType);
264
+ writer.writeVarint(size);
265
+ }
266
+ for (const value of values) {
267
+ if (itemType === ThriftTypes.INT_32) writer.writeVarint(zigzag(value, 32));
268
+ else if (itemType === ThriftTypes.BINARY) writer.writeStringDirect(value);
269
+ else throw new Error(`Unsupported thrift list type: ${itemType}`);
270
+ }
271
+ }
272
+
273
+ function writeThriftMap(writer, field, values) {
274
+ writer.writeField(field, ThriftTypes.MAP);
275
+ const keys = Object.keys(values);
276
+ writer.writeVarint(keys.length);
277
+ if (!keys.length) return;
278
+ writer.buffer.push((ThriftTypes.BINARY << 4) | ThriftTypes.BINARY);
279
+ for (const key of keys) {
280
+ writer.writeStringDirect(key);
281
+ writer.writeStringDirect(values[key]);
282
+ }
283
+ }
284
+
285
+ function writeThriftObject(data, descriptors) {
286
+ const writer = new ThriftWriter();
287
+ writeThriftStruct(writer, data, descriptors);
288
+ writer.writeStop();
289
+ return Buffer.from(writer.buffer);
290
+ }
291
+
292
+ // The CONNECT payload: a Thrift compact MQTToTConnection struct.
293
+ const CONNECTION_DESCRIPTORS = [
294
+ { name: "clientIdentifier", field: 1, type: ThriftTypes.BINARY, children: [] },
295
+ { name: "willTopic", field: 2, type: ThriftTypes.BINARY, children: [] },
296
+ { name: "willMessage", field: 3, type: ThriftTypes.BINARY, children: [] },
297
+ {
298
+ name: "clientInfo",
299
+ field: 4,
300
+ type: ThriftTypes.STRUCT,
301
+ children: [
302
+ { name: "userId", field: 1, type: ThriftTypes.INT_64, children: [] },
303
+ { name: "userAgent", field: 2, type: ThriftTypes.BINARY, children: [] },
304
+ { name: "clientCapabilities", field: 3, type: ThriftTypes.INT_64, children: [] },
305
+ { name: "endpointCapabilities", field: 4, type: ThriftTypes.INT_64, children: [] },
306
+ { name: "publishFormat", field: 5, type: ThriftTypes.INT_32, children: [] },
307
+ { name: "noAutomaticForeground", field: 6, type: ThriftTypes.BOOLEAN, children: [] },
308
+ { name: "makeUserAvailableInForeground", field: 7, type: ThriftTypes.BOOLEAN, children: [] },
309
+ { name: "deviceId", field: 8, type: ThriftTypes.BINARY, children: [] },
310
+ { name: "isInitiallyForeground", field: 9, type: ThriftTypes.BOOLEAN, children: [] },
311
+ { name: "networkType", field: 10, type: ThriftTypes.INT_32, children: [] },
312
+ { name: "networkSubtype", field: 11, type: ThriftTypes.INT_32, children: [] },
313
+ { name: "clientMqttSessionId", field: 12, type: ThriftTypes.INT_64, children: [] },
314
+ { name: "clientIpAddress", field: 13, type: ThriftTypes.BINARY, children: [] },
315
+ { name: "subscribeTopics", field: 14, type: ThriftTypes.LIST_INT_32, children: [] },
316
+ { name: "clientType", field: 15, type: ThriftTypes.BINARY, children: [] },
317
+ { name: "appId", field: 16, type: ThriftTypes.INT_64, children: [] },
318
+ { name: "overrideNectarLogging", field: 17, type: ThriftTypes.BOOLEAN, children: [] },
319
+ { name: "connectTokenHash", field: 18, type: ThriftTypes.BINARY, children: [] },
320
+ { name: "regionPreference", field: 19, type: ThriftTypes.BINARY, children: [] },
321
+ { name: "deviceSecret", field: 20, type: ThriftTypes.BINARY, children: [] },
322
+ { name: "clientStack", field: 21, type: ThriftTypes.BYTE, children: [] },
323
+ { name: "fbnsConnectionKey", field: 22, type: ThriftTypes.INT_64, children: [] },
324
+ { name: "fbnsConnectionSecret", field: 23, type: ThriftTypes.BINARY, children: [] },
325
+ { name: "fbnsDeviceId", field: 24, type: ThriftTypes.BINARY, children: [] },
326
+ { name: "fbnsDeviceSecret", field: 25, type: ThriftTypes.BINARY, children: [] },
327
+ { name: "anotherUnknown", field: 26, type: ThriftTypes.INT_64, children: [] }
328
+ ]
329
+ },
330
+ { name: "password", field: 5, type: ThriftTypes.BINARY, children: [] },
331
+ { name: "getDiffsRequests", field: 6, type: ThriftTypes.LIST_BINARY, children: [] },
332
+ { name: "zeroRatingTokenHash", field: 9, type: ThriftTypes.BINARY, children: [] },
333
+ { name: "appSpecificInfo", field: 10, type: ThriftTypes.MAP_BINARY_BINARY, children: [] }
334
+ ];
335
+
336
+ class MQTToTConnection {
337
+ constructor({ clientIdentifier, clientInfo, password, appSpecificInfo }) {
338
+ this.clientIdentifier = clientIdentifier;
339
+ this.clientInfo = clientInfo;
340
+ this.password = password;
341
+ this.appSpecificInfo = appSpecificInfo || null;
342
+ }
343
+
344
+ toThrift() {
345
+ const payload = {
346
+ clientIdentifier: this.clientIdentifier,
347
+ clientInfo: this.clientInfo,
348
+ password: this.password
349
+ };
350
+ if (this.appSpecificInfo) payload.appSpecificInfo = this.appSpecificInfo;
351
+ return writeThriftObject(payload, CONNECTION_DESCRIPTORS);
352
+ }
353
+ }
354
+
355
+ function compressPayload(data) {
356
+ return zlib.deflateSync(data, { level: 9 });
357
+ }
358
+
359
+ // Publish payloads are zlib streams (0x78 ...). Anything else is left as-is.
360
+ function tryDecompressPayload(data) {
361
+ if (!data || !data.length || data[0] !== 0x78) return data;
362
+ try {
363
+ return zlib.inflateSync(data);
364
+ } catch (_) {
365
+ return data;
366
+ }
367
+ }
368
+
369
+ function writeConnectPacket(connection, keepAlive = 20) {
370
+ const payload = compressPayload(connection.toThrift());
371
+ const header = Buffer.concat([
372
+ writeUtf8("MQTToT"),
373
+ Buffer.from([3, 0xc2]),
374
+ uint16BE(keepAlive)
375
+ ]);
376
+ const remaining = Buffer.concat([header, payload]);
377
+ return Buffer.concat([Buffer.from([0x10]), encodeRemainingLength(remaining.length), remaining]);
378
+ }
379
+
380
+ function writePublishPacket(topic, payload, qos = 1, packetId = 1) {
381
+ if (qos !== 0 && qos !== 1) throw new Error("Only QoS 0 and QoS 1 are supported");
382
+ let body = writeUtf8(String(topic));
383
+ if (qos) body = Buffer.concat([body, uint16BE(packetId)]);
384
+ body = Buffer.concat([body, payload]);
385
+ return Buffer.concat([Buffer.from([0x30 | (qos << 1)]), encodeRemainingLength(body.length), body]);
386
+ }
387
+
388
+ function writeSubscribePacket(topic, packetId = 1, qos = 1) {
389
+ const body = Buffer.concat([uint16BE(packetId), writeUtf8(String(topic)), Buffer.from([qos])]);
390
+ return Buffer.concat([Buffer.from([0x82]), encodeRemainingLength(body.length), body]);
391
+ }
392
+
393
+ function writePingreqPacket() {
394
+ return Buffer.from([0xc0, 0x00]);
395
+ }
396
+
397
+ function writeDisconnectPacket() {
398
+ return Buffer.from([0xe0, 0x00]);
399
+ }
400
+
401
+ function decodePacket(packet) {
402
+ const packetTypeId = packet[0] >> 4;
403
+ const flags = packet[0] & 0x0f;
404
+ const { value: remainingLength, offset } = decodeRemainingLength(packet, 1);
405
+ const body = packet.slice(offset, offset + remainingLength);
406
+ if (packetTypeId === 1) {
407
+ const { value: protocolName, offset: pos } = readUtf8(body, 0);
408
+ return {
409
+ packetType: "connect",
410
+ protocolName,
411
+ protocolLevel: body[pos],
412
+ connectFlags: body[pos + 1],
413
+ keepAlive: body.readUInt16BE(pos + 2),
414
+ payload: body.slice(pos + 4)
415
+ };
416
+ }
417
+ if (packetTypeId === 2) {
418
+ return { packetType: "connack", returnCode: body[1], payload: body.slice(2) };
419
+ }
420
+ if (packetTypeId === 3) {
421
+ const { value: topic, offset: pos } = readUtf8(body, 0);
422
+ const qos = (flags >> 1) & 0x03;
423
+ let packetId = null;
424
+ let at = pos;
425
+ if (qos) {
426
+ packetId = body.readUInt16BE(at);
427
+ at += 2;
428
+ }
429
+ return { packetType: "publish", topic, qos, packetId, payload: body.slice(at) };
430
+ }
431
+ if (packetTypeId === 12) return { packetType: "pingreq", payload: body };
432
+ if (packetTypeId === 13) return { packetType: "pingresp", payload: body };
433
+ if (packetTypeId === 14) return { packetType: "disconnect", payload: body };
434
+ return { packetType: String(packetTypeId), payload: body };
435
+ }
436
+
437
+ function parseJsonPayload(payload) {
438
+ return JSON.parse(tryDecompressPayload(payload).toString("utf8"));
439
+ }
440
+
441
+ function uint16BE(value) {
442
+ const buf = Buffer.alloc(2);
443
+ buf.writeUInt16BE(value & 0xffff, 0);
444
+ return buf;
445
+ }
446
+
447
+ function writeUtf8(value) {
448
+ const raw = Buffer.from(value, "utf8");
449
+ return Buffer.concat([uint16BE(raw.length), raw]);
450
+ }
451
+
452
+ function readUtf8(data, offset) {
453
+ const size = data.readUInt16BE(offset);
454
+ offset += 2;
455
+ return { value: data.slice(offset, offset + size).toString("utf8"), offset: offset + size };
456
+ }
457
+
458
+ function encodeRemainingLength(value) {
459
+ const encoded = [];
460
+ for (;;) {
461
+ let byte = value % 128;
462
+ value = Math.floor(value / 128);
463
+ if (value) byte |= 0x80;
464
+ encoded.push(byte);
465
+ if (!value) return Buffer.from(encoded);
466
+ }
467
+ }
468
+
469
+ function decodeRemainingLength(data, offset) {
470
+ let multiplier = 1;
471
+ let value = 0;
472
+ let byte;
473
+ do {
474
+ byte = data[offset];
475
+ offset += 1;
476
+ value += (byte & 127) * multiplier;
477
+ multiplier *= 128;
478
+ } while (byte & 128);
479
+ return { value, offset };
480
+ }
481
+
482
+ // A TLS transport that frames MQTT packets. `recvPacket()` resolves with one
483
+ // complete packet; the realtime client drives it in a loop. Node's socket is
484
+ // asynchronous, so unlike the reference (blocking) transport this buffers and
485
+ // hands out packets as they arrive.
486
+ class SocketMQTToTTransport {
487
+ constructor(host, port = 443, { timeout = 30000 } = {}) {
488
+ this.host = host;
489
+ this.port = port;
490
+ this.timeout = timeout;
491
+ this.socket = null;
492
+ this._buffer = Buffer.alloc(0);
493
+ this._waiters = [];
494
+ this._closed = false;
495
+ this._error = null;
496
+ }
497
+
498
+ connect() {
499
+ return new Promise((resolve, reject) => {
500
+ const socket = tls.connect({
501
+ host: this.host,
502
+ port: this.port,
503
+ servername: this.host,
504
+ minVersion: "TLSv1.2"
505
+ });
506
+ const onError = (err) => { cleanup(); reject(err); };
507
+ const onConnect = () => { cleanup(); this._attach(socket); resolve(); };
508
+ const cleanup = () => {
509
+ socket.removeListener("error", onError);
510
+ socket.removeListener("secureConnect", onConnect);
511
+ };
512
+ socket.setTimeout(this.timeout, () => socket.destroy(new Error("MQTT socket timeout")));
513
+ socket.once("error", onError);
514
+ socket.once("secureConnect", onConnect);
515
+ });
516
+ }
517
+
518
+ _attach(socket) {
519
+ this.socket = socket;
520
+ socket.on("data", (chunk) => {
521
+ this._buffer = Buffer.concat([this._buffer, chunk]);
522
+ this._drain();
523
+ });
524
+ socket.on("error", (err) => this._fail(err));
525
+ socket.on("close", () => this._fail(new Error("MQTT socket closed")));
526
+ }
527
+
528
+ _drain() {
529
+ while (this._waiters.length) {
530
+ const packet = this._tryRead();
531
+ if (!packet) break;
532
+ this._waiters.shift().resolve(packet);
533
+ }
534
+ }
535
+
536
+ _tryRead() {
537
+ if (this._buffer.length < 2) return null;
538
+ let multiplier = 1;
539
+ let size = 0;
540
+ let offset = 1;
541
+ let byte;
542
+ do {
543
+ if (offset >= this._buffer.length) return null;
544
+ byte = this._buffer[offset];
545
+ size += (byte & 0x7f) * multiplier;
546
+ multiplier *= 128;
547
+ offset += 1;
548
+ } while (byte & 0x80);
549
+ const total = offset + size;
550
+ if (this._buffer.length < total) return null;
551
+ const packet = this._buffer.slice(0, total);
552
+ this._buffer = this._buffer.slice(total);
553
+ return packet;
554
+ }
555
+
556
+ recvPacket() {
557
+ const packet = this._tryRead();
558
+ if (packet) return Promise.resolve(packet);
559
+ if (this._closed) return Promise.reject(this._error || new Error("MQTT socket closed"));
560
+ return new Promise((resolve, reject) => this._waiters.push({ resolve, reject }));
561
+ }
562
+
563
+ send(packet) {
564
+ if (!this.socket) return Promise.reject(new Error("MQTT transport is not connected"));
565
+ return new Promise((resolve, reject) => {
566
+ this.socket.write(packet, (err) => (err ? reject(err) : resolve()));
567
+ });
568
+ }
569
+
570
+ disconnect() {
571
+ if (!this.socket) return;
572
+ const socket = this.socket;
573
+ this.socket = null;
574
+ try { socket.end(); } catch (_) {}
575
+ try { socket.destroy(); } catch (_) {}
576
+ this._fail(new Error("MQTT transport closed"));
577
+ }
578
+
579
+ _fail(err) {
580
+ if (this._closed && this._error) return;
581
+ this._closed = true;
582
+ this._error = err;
583
+ for (const waiter of this._waiters) waiter.reject(err);
584
+ this._waiters = [];
585
+ }
586
+ }
587
+
588
+ module.exports = {
589
+ ThriftTypes,
590
+ ThriftWriter,
591
+ ThriftReader,
592
+ MQTToTConnection,
593
+ CONNECTION_DESCRIPTORS,
594
+ SocketMQTToTTransport,
595
+ compressPayload,
596
+ tryDecompressPayload,
597
+ writeConnectPacket,
598
+ writePublishPacket,
599
+ writeSubscribePacket,
600
+ writePingreqPacket,
601
+ writeDisconnectPacket,
602
+ decodePacket,
603
+ parseJsonPayload,
604
+ writeThriftObject,
605
+ writeThriftStruct,
606
+ readThriftObject: (data, descriptors) => new ThriftReader(data).readStruct(descriptors),
607
+ zigzag,
608
+ unzigzag
609
+ };