@tiktool/live 1.6.6 → 2.0.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.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,3 @@
1
- import { EventEmitter } from 'events';
2
-
3
1
  interface TikTokUser {
4
2
  id: string;
5
3
  nickname: string;
@@ -165,9 +163,20 @@ interface TikTokLiveOptions {
165
163
  maxReconnectAttempts?: number;
166
164
  heartbeatInterval?: number;
167
165
  debug?: boolean;
166
+ /** Provide a custom WebSocket class (e.g. `ws` for Node < 22). Auto-resolved if omitted. */
167
+ webSocketImpl?: any;
168
168
  }
169
169
 
170
- declare class TikTokLive extends EventEmitter {
170
+ type Listener = (...args: any[]) => void;
171
+ declare class TypedEmitter {
172
+ private _listeners;
173
+ on(event: string, fn: Listener): this;
174
+ once(event: string, fn: Listener): this;
175
+ off(event: string, fn: Listener): this;
176
+ emit(event: string, ...args: any[]): boolean;
177
+ removeAllListeners(event?: string): this;
178
+ }
179
+ declare class TikTokLive extends TypedEmitter {
171
180
  private ws;
172
181
  private heartbeatTimer;
173
182
  private reconnectAttempts;
@@ -183,6 +192,8 @@ declare class TikTokLive extends EventEmitter {
183
192
  private readonly maxReconnectAttempts;
184
193
  private readonly heartbeatInterval;
185
194
  private readonly debug;
195
+ private readonly webSocketImpl?;
196
+ private WS;
186
197
  constructor(options: TikTokLiveOptions);
187
198
  connect(): Promise<void>;
188
199
  disconnect(): void;
@@ -193,6 +204,7 @@ declare class TikTokLive extends EventEmitter {
193
204
  once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
194
205
  off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
195
206
  emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean;
207
+ private handleMessage;
196
208
  private handleFrame;
197
209
  private startHeartbeat;
198
210
  private stopHeartbeat;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { EventEmitter } from 'events';
2
-
3
1
  interface TikTokUser {
4
2
  id: string;
5
3
  nickname: string;
@@ -165,9 +163,20 @@ interface TikTokLiveOptions {
165
163
  maxReconnectAttempts?: number;
166
164
  heartbeatInterval?: number;
167
165
  debug?: boolean;
166
+ /** Provide a custom WebSocket class (e.g. `ws` for Node < 22). Auto-resolved if omitted. */
167
+ webSocketImpl?: any;
168
168
  }
169
169
 
170
- declare class TikTokLive extends EventEmitter {
170
+ type Listener = (...args: any[]) => void;
171
+ declare class TypedEmitter {
172
+ private _listeners;
173
+ on(event: string, fn: Listener): this;
174
+ once(event: string, fn: Listener): this;
175
+ off(event: string, fn: Listener): this;
176
+ emit(event: string, ...args: any[]): boolean;
177
+ removeAllListeners(event?: string): this;
178
+ }
179
+ declare class TikTokLive extends TypedEmitter {
171
180
  private ws;
172
181
  private heartbeatTimer;
173
182
  private reconnectAttempts;
@@ -183,6 +192,8 @@ declare class TikTokLive extends EventEmitter {
183
192
  private readonly maxReconnectAttempts;
184
193
  private readonly heartbeatInterval;
185
194
  private readonly debug;
195
+ private readonly webSocketImpl?;
196
+ private WS;
186
197
  constructor(options: TikTokLiveOptions);
187
198
  connect(): Promise<void>;
188
199
  disconnect(): void;
@@ -193,6 +204,7 @@ declare class TikTokLive extends EventEmitter {
193
204
  once<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
194
205
  off<K extends keyof TikTokLiveEvents>(event: K, listener: TikTokLiveEvents[K]): this;
195
206
  emit<K extends keyof TikTokLiveEvents>(event: K, ...args: Parameters<TikTokLiveEvents[K]>): boolean;
207
+ private handleMessage;
196
208
  private handleFrame;
197
209
  private startHeartbeat;
198
210
  private stopHeartbeat;
package/dist/index.js CHANGED
@@ -34,14 +34,28 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
- // src/client.ts
38
- var import_events = require("events");
39
- var http = __toESM(require("http"));
40
- var https = __toESM(require("https"));
41
- var zlib = __toESM(require("zlib"));
42
- var import_ws = __toESM(require("ws"));
43
-
44
37
  // src/proto.ts
38
+ var encoder = new TextEncoder();
39
+ var decoder = new TextDecoder();
40
+ function concatBytes(...arrays) {
41
+ let totalLength = 0;
42
+ for (const arr of arrays) totalLength += arr.length;
43
+ const result = new Uint8Array(totalLength);
44
+ let offset = 0;
45
+ for (const arr of arrays) {
46
+ result.set(arr, offset);
47
+ offset += arr.length;
48
+ }
49
+ return result;
50
+ }
51
+ function readInt32LE(buf, offset) {
52
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24;
53
+ }
54
+ function readBigInt64LE(buf, offset) {
55
+ const lo = BigInt(buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0);
56
+ const hi = BigInt(buf[offset + 4] | buf[offset + 5] << 8 | buf[offset + 6] << 16 | buf[offset + 7] << 24 >>> 0);
57
+ return hi << 32n | lo & 0xFFFFFFFFn;
58
+ }
45
59
  function decodeVarint(buf, offset) {
46
60
  let result = 0, shift = 0;
47
61
  while (offset < buf.length) {
@@ -71,15 +85,15 @@ function encodeVarint(v) {
71
85
  if (n > 0n) b |= 128;
72
86
  bytes.push(b);
73
87
  } while (n > 0n);
74
- return Buffer.from(bytes);
88
+ return new Uint8Array(bytes);
75
89
  }
76
90
  function encodeField(fn, wt, value) {
77
91
  const tag = encodeVarint(fn << 3 | wt);
78
92
  if (wt === 0) {
79
- return Buffer.concat([tag, encodeVarint(typeof value === "number" ? BigInt(value) : value)]);
93
+ return concatBytes(tag, encodeVarint(typeof value === "number" ? BigInt(value) : value));
80
94
  }
81
- const data = typeof value === "string" ? Buffer.from(value) : value;
82
- return Buffer.concat([tag, encodeVarint(data.length), data]);
95
+ const data = typeof value === "string" ? encoder.encode(value) : value;
96
+ return concatBytes(tag, encodeVarint(data.length), data);
83
97
  }
84
98
  function decodeProto(buf) {
85
99
  const fields = [];
@@ -100,10 +114,10 @@ function decodeProto(buf) {
100
114
  offset += lenR.value;
101
115
  fields.push({ fn, wt, value: data });
102
116
  } else if (wt === 1) {
103
- fields.push({ fn, wt, value: buf.readBigInt64LE(offset) });
117
+ fields.push({ fn, wt, value: readBigInt64LE(buf, offset) });
104
118
  offset += 8;
105
119
  } else if (wt === 5) {
106
- fields.push({ fn, wt, value: BigInt(buf.readInt32LE(offset)) });
120
+ fields.push({ fn, wt, value: BigInt(readInt32LE(buf, offset)) });
107
121
  offset += 4;
108
122
  } else {
109
123
  break;
@@ -113,7 +127,7 @@ function decodeProto(buf) {
113
127
  }
114
128
  function getStr(fields, fn) {
115
129
  const f = fields.find((x) => x.fn === fn && x.wt === 2);
116
- return f ? f.value.toString("utf-8") : "";
130
+ return f ? decoder.decode(f.value) : "";
117
131
  }
118
132
  function getBytes(fields, fn) {
119
133
  const f = fields.find((x) => x.fn === fn && x.wt === 2);
@@ -128,33 +142,33 @@ function getAllBytes(fields, fn) {
128
142
  }
129
143
  function buildHeartbeat(roomId) {
130
144
  const payload = encodeField(1, 0, BigInt(roomId));
131
- return Buffer.concat([
145
+ return concatBytes(
132
146
  encodeField(6, 2, "pb"),
133
147
  encodeField(7, 2, "hb"),
134
148
  encodeField(8, 2, payload)
135
- ]);
149
+ );
136
150
  }
137
151
  function buildImEnterRoom(roomId) {
138
- const inner = Buffer.concat([
152
+ const inner = concatBytes(
139
153
  encodeField(1, 0, BigInt(roomId)),
140
154
  encodeField(4, 0, 12n),
141
155
  encodeField(5, 2, "audience"),
142
156
  encodeField(6, 2, ""),
143
157
  encodeField(9, 2, ""),
144
158
  encodeField(10, 2, "")
145
- ]);
146
- return Buffer.concat([
159
+ );
160
+ return concatBytes(
147
161
  encodeField(6, 2, "pb"),
148
162
  encodeField(7, 2, "im_enter_room"),
149
163
  encodeField(8, 2, inner)
150
- ]);
164
+ );
151
165
  }
152
166
  function buildAck(id) {
153
- return Buffer.concat([
167
+ return concatBytes(
154
168
  encodeField(2, 0, id),
155
169
  encodeField(6, 2, "pb"),
156
170
  encodeField(7, 2, "ack")
157
- ]);
171
+ );
158
172
  }
159
173
  function parseUser(data) {
160
174
  const f = decodeProto(data);
@@ -167,7 +181,7 @@ function parseUser(data) {
167
181
  try {
168
182
  const avatarFields = decodeProto(avatarBuf);
169
183
  const urlBufs = getAllBytes(avatarFields, 1);
170
- if (urlBufs.length > 0) profilePicture = urlBufs[0].toString("utf-8");
184
+ if (urlBufs.length > 0) profilePicture = decoder.decode(urlBufs[0]);
171
185
  } catch {
172
186
  }
173
187
  }
@@ -484,27 +498,47 @@ function parseWebcastResponse(payload) {
484
498
  // src/client.ts
485
499
  var DEFAULT_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
486
500
  var DEFAULT_SIGN_SERVER = "https://api.tik.tools";
487
- function httpGet(url, headers) {
488
- return new Promise((resolve, reject) => {
489
- const mod = url.startsWith("https") ? https : http;
490
- const req = mod.get(url, { headers }, (res) => {
491
- const chunks = [];
492
- const enc = res.headers["content-encoding"];
493
- const stream = enc === "gzip" || enc === "br" ? res.pipe(enc === "br" ? zlib.createBrotliDecompress() : zlib.createGunzip()) : res;
494
- stream.on("data", (c) => chunks.push(c));
495
- stream.on("end", () => resolve({
496
- status: res.statusCode || 0,
497
- headers: res.headers,
498
- body: Buffer.concat(chunks)
499
- }));
500
- stream.on("error", reject);
501
- });
502
- req.on("error", reject);
503
- req.setTimeout(15e3, () => {
504
- req.destroy();
505
- reject(new Error("Request timeout"));
506
- });
507
- });
501
+ var TypedEmitter = class {
502
+ _listeners = /* @__PURE__ */ new Map();
503
+ on(event, fn) {
504
+ const arr = this._listeners.get(event) || [];
505
+ arr.push(fn);
506
+ this._listeners.set(event, arr);
507
+ return this;
508
+ }
509
+ once(event, fn) {
510
+ const wrapper = (...args) => {
511
+ this.off(event, wrapper);
512
+ fn(...args);
513
+ };
514
+ return this.on(event, wrapper);
515
+ }
516
+ off(event, fn) {
517
+ const arr = this._listeners.get(event);
518
+ if (arr) {
519
+ this._listeners.set(event, arr.filter((l) => l !== fn));
520
+ }
521
+ return this;
522
+ }
523
+ emit(event, ...args) {
524
+ const arr = this._listeners.get(event);
525
+ if (!arr || arr.length === 0) return false;
526
+ for (const fn of [...arr]) fn(...args);
527
+ return true;
528
+ }
529
+ removeAllListeners(event) {
530
+ if (event) this._listeners.delete(event);
531
+ else this._listeners.clear();
532
+ return this;
533
+ }
534
+ };
535
+ function gunzipSync(data) {
536
+ try {
537
+ const zlib = require("zlib");
538
+ return new Uint8Array(zlib.gunzipSync(data));
539
+ } catch {
540
+ return data;
541
+ }
508
542
  }
509
543
  function getWsHost(clusterRegion) {
510
544
  if (!clusterRegion) return "webcast-ws.tiktok.com";
@@ -513,7 +547,21 @@ function getWsHost(clusterRegion) {
513
547
  if (r.startsWith("us") || r.includes("us")) return "webcast-ws.us.tiktok.com";
514
548
  return "webcast-ws.tiktok.com";
515
549
  }
516
- var TikTokLive = class extends import_events.EventEmitter {
550
+ async function resolveWebSocket(userImpl) {
551
+ if (userImpl) return userImpl;
552
+ if (typeof globalThis.WebSocket !== "undefined") {
553
+ return globalThis.WebSocket;
554
+ }
555
+ try {
556
+ const ws = await import("ws");
557
+ return ws.default || ws;
558
+ } catch {
559
+ throw new Error(
560
+ 'No WebSocket implementation found. Either use Node.js 22+ (native WebSocket), Cloudflare Workers, or install the "ws" package: npm i ws'
561
+ );
562
+ }
563
+ }
564
+ var TikTokLive = class extends TypedEmitter {
517
565
  ws = null;
518
566
  heartbeatTimer = null;
519
567
  reconnectAttempts = 0;
@@ -521,7 +569,6 @@ var TikTokLive = class extends import_events.EventEmitter {
521
569
  _connected = false;
522
570
  _eventCount = 0;
523
571
  _roomId = "";
524
- // Cache host identities from battle events for enriching battleArmies
525
572
  _battleHosts = /* @__PURE__ */ new Map();
526
573
  uniqueId;
527
574
  signServerUrl;
@@ -530,6 +577,8 @@ var TikTokLive = class extends import_events.EventEmitter {
530
577
  maxReconnectAttempts;
531
578
  heartbeatInterval;
532
579
  debug;
580
+ webSocketImpl;
581
+ WS;
533
582
  constructor(options) {
534
583
  super();
535
584
  this.uniqueId = options.uniqueId.replace(/^@/, "");
@@ -540,24 +589,40 @@ var TikTokLive = class extends import_events.EventEmitter {
540
589
  this.maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
541
590
  this.heartbeatInterval = options.heartbeatInterval ?? 1e4;
542
591
  this.debug = options.debug ?? false;
592
+ this.webSocketImpl = options.webSocketImpl;
543
593
  }
544
594
  async connect() {
545
595
  this.intentionalClose = false;
546
- const resp = await httpGet(`https://www.tiktok.com/@${this.uniqueId}/live`, {
547
- "User-Agent": DEFAULT_UA,
548
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
549
- "Accept-Encoding": "gzip, deflate, br",
550
- "Accept-Language": "en-US,en;q=0.9"
596
+ if (!this.WS) {
597
+ this.WS = await resolveWebSocket(this.webSocketImpl);
598
+ }
599
+ const resp = await fetch(`https://www.tiktok.com/@${this.uniqueId}/live`, {
600
+ headers: {
601
+ "User-Agent": DEFAULT_UA,
602
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
603
+ "Accept-Language": "en-US,en;q=0.9"
604
+ },
605
+ redirect: "follow"
551
606
  });
552
607
  let ttwid = "";
553
- for (const sc of [resp.headers["set-cookie"] || []].flat()) {
554
- if (typeof sc === "string" && sc.startsWith("ttwid=")) {
555
- ttwid = sc.split(";")[0].split("=").slice(1).join("=");
608
+ const setCookies = resp.headers.get("set-cookie") || "";
609
+ for (const part of setCookies.split(",")) {
610
+ const trimmed = part.trim();
611
+ if (trimmed.startsWith("ttwid=")) {
612
+ ttwid = trimmed.split(";")[0].split("=").slice(1).join("=");
556
613
  break;
557
614
  }
558
615
  }
616
+ if (!ttwid && typeof resp.headers.getSetCookie === "function") {
617
+ for (const sc of resp.headers.getSetCookie()) {
618
+ if (typeof sc === "string" && sc.startsWith("ttwid=")) {
619
+ ttwid = sc.split(";")[0].split("=").slice(1).join("=");
620
+ break;
621
+ }
622
+ }
623
+ }
559
624
  if (!ttwid) throw new Error("Failed to obtain session cookie");
560
- const html = resp.body.toString();
625
+ const html = await resp.text();
561
626
  let roomId = "";
562
627
  const sigiMatch = html.match(/id="SIGI_STATE"[^>]*>([^<]+)/);
563
628
  if (sigiMatch) {
@@ -585,7 +650,7 @@ var TikTokLive = class extends import_events.EventEmitter {
585
650
  browser_name: "Mozilla",
586
651
  browser_version: DEFAULT_UA.split("Mozilla/")[1] || "5.0",
587
652
  browser_online: "true",
588
- tz_name: Intl.DateTimeFormat().resolvedOptions().timeZone,
653
+ tz_name: "Etc/UTC",
589
654
  app_name: "tiktok_web",
590
655
  sup_ws_ds_opt: "1",
591
656
  update_version_code: "2.0.0",
@@ -626,18 +691,27 @@ var TikTokLive = class extends import_events.EventEmitter {
626
691
  wsUrl = rawWsUrl.replace(/^https:\/\//, "wss://");
627
692
  }
628
693
  return new Promise((resolve, reject) => {
629
- this.ws = new import_ws.default(wsUrl, {
630
- headers: {
631
- "User-Agent": DEFAULT_UA,
632
- "Cookie": `ttwid=${ttwid}`,
633
- "Origin": "https://www.tiktok.com"
634
- }
635
- });
636
- this.ws.on("open", () => {
694
+ const connUrl = wsUrl + (wsUrl.includes("?") ? "&" : "?") + `ttwid=${ttwid}`;
695
+ try {
696
+ this.ws = new this.WS(connUrl, {
697
+ headers: {
698
+ "User-Agent": DEFAULT_UA,
699
+ "Cookie": `ttwid=${ttwid}`,
700
+ "Origin": "https://www.tiktok.com"
701
+ }
702
+ });
703
+ } catch {
704
+ this.ws = new this.WS(connUrl);
705
+ }
706
+ const ws = this.ws;
707
+ let settled = false;
708
+ ws.onopen = () => {
637
709
  this._connected = true;
638
710
  this.reconnectAttempts = 0;
639
- this.ws.send(buildHeartbeat(roomId));
640
- this.ws.send(buildImEnterRoom(roomId));
711
+ const hb = buildHeartbeat(roomId);
712
+ const enter = buildImEnterRoom(roomId);
713
+ ws.send(hb.buffer.byteLength === hb.length ? hb.buffer : hb.buffer.slice(hb.byteOffset, hb.byteOffset + hb.byteLength));
714
+ ws.send(enter.buffer.byteLength === enter.length ? enter.buffer : enter.buffer.slice(enter.byteOffset, enter.byteOffset + enter.byteLength));
641
715
  this.startHeartbeat(roomId);
642
716
  const roomInfo = {
643
717
  roomId,
@@ -647,26 +721,41 @@ var TikTokLive = class extends import_events.EventEmitter {
647
721
  };
648
722
  this.emit("connected");
649
723
  this.emit("roomInfo", roomInfo);
650
- resolve();
651
- });
652
- this.ws.on("message", (rawData) => {
653
- this.handleFrame(Buffer.from(rawData));
654
- });
655
- this.ws.on("close", (code, reason) => {
724
+ if (!settled) {
725
+ settled = true;
726
+ resolve();
727
+ }
728
+ };
729
+ ws.onmessage = (event) => {
730
+ const raw = event.data !== void 0 ? event.data : event;
731
+ this.handleMessage(raw);
732
+ };
733
+ ws.onclose = (event) => {
656
734
  this._connected = false;
657
735
  this.stopHeartbeat();
658
- const reasonStr = reason?.toString() || "";
659
- this.emit("disconnected", code, reasonStr);
736
+ const code = event?.code ?? 1006;
737
+ const reason = event?.reason ?? "";
738
+ this.emit("disconnected", code, reason?.toString?.() || "");
660
739
  if (!this.intentionalClose && this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
661
740
  this.reconnectAttempts++;
662
741
  const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts - 1), 3e4);
663
742
  setTimeout(() => this.connect().catch((e) => this.emit("error", e)), delay);
664
743
  }
665
- });
666
- this.ws.on("error", (err) => {
667
- this.emit("error", err);
668
- if (!this._connected) reject(err);
669
- });
744
+ };
745
+ ws.onerror = (err) => {
746
+ this.emit("error", err instanceof Error ? err : new Error(String(err?.message || err)));
747
+ if (!settled) {
748
+ settled = true;
749
+ reject(err);
750
+ }
751
+ };
752
+ setTimeout(() => {
753
+ if (!settled) {
754
+ settled = true;
755
+ ws.close();
756
+ reject(new Error("Connection timeout"));
757
+ }
758
+ }, 15e3);
670
759
  });
671
760
  }
672
761
  disconnect() {
@@ -687,6 +776,7 @@ var TikTokLive = class extends import_events.EventEmitter {
687
776
  get roomId() {
688
777
  return this._roomId;
689
778
  }
779
+ // Typed event emitter overrides
690
780
  on(event, listener) {
691
781
  return super.on(event, listener);
692
782
  }
@@ -699,6 +789,25 @@ var TikTokLive = class extends import_events.EventEmitter {
699
789
  emit(event, ...args) {
700
790
  return super.emit(event, ...args);
701
791
  }
792
+ // ── Message handling ──────────────────────────────────────────────
793
+ async handleMessage(raw) {
794
+ try {
795
+ let bytes;
796
+ if (raw instanceof ArrayBuffer) {
797
+ bytes = new Uint8Array(raw);
798
+ } else if (raw instanceof Uint8Array) {
799
+ bytes = raw;
800
+ } else if (typeof Blob !== "undefined" && raw instanceof Blob) {
801
+ bytes = new Uint8Array(await raw.arrayBuffer());
802
+ } else if (raw?.buffer instanceof ArrayBuffer) {
803
+ bytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
804
+ } else {
805
+ return;
806
+ }
807
+ this.handleFrame(bytes);
808
+ } catch {
809
+ }
810
+ }
702
811
  handleFrame(buf) {
703
812
  try {
704
813
  const fields = decodeProto(buf);
@@ -706,14 +815,15 @@ var TikTokLive = class extends import_events.EventEmitter {
706
815
  const id = idField ? idField.value : 0n;
707
816
  const type = getStr(fields, 7);
708
817
  const binary = getBytes(fields, 8);
709
- if (id > 0n && this.ws?.readyState === import_ws.default.OPEN) {
710
- this.ws.send(buildAck(id));
818
+ if (id > 0n && this.ws && this.ws.readyState === 1) {
819
+ const ack = buildAck(id);
820
+ this.ws.send(ack.buffer.byteLength === ack.length ? ack.buffer : ack.buffer.slice(ack.byteOffset, ack.byteOffset + ack.byteLength));
711
821
  }
712
822
  if (type === "msg" && binary && binary.length > 0) {
713
823
  let inner = binary;
714
824
  if (inner.length > 2 && inner[0] === 31 && inner[1] === 139) {
715
825
  try {
716
- inner = zlib.gunzipSync(inner);
826
+ inner = gunzipSync(inner);
717
827
  } catch {
718
828
  }
719
829
  }
@@ -751,8 +861,9 @@ var TikTokLive = class extends import_events.EventEmitter {
751
861
  startHeartbeat(roomId) {
752
862
  this.stopHeartbeat();
753
863
  this.heartbeatTimer = setInterval(() => {
754
- if (this.ws?.readyState === import_ws.default.OPEN) {
755
- this.ws.send(buildHeartbeat(roomId));
864
+ if (this.ws && this.ws.readyState === 1) {
865
+ const hb = buildHeartbeat(roomId);
866
+ this.ws.send(hb.buffer.byteLength === hb.length ? hb.buffer : hb.buffer.slice(hb.byteOffset, hb.byteOffset + hb.byteLength));
756
867
  }
757
868
  }, this.heartbeatInterval);
758
869
  }