@laplace.live/ws 6.3.8 → 7.0.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/dist/browser.js CHANGED
@@ -1,293 +1,3 @@
1
- // src/buffer.ts
2
- var textEncoder = new TextEncoder();
3
- var textDecoder = new TextDecoder();
4
- function concatUint8Arrays(arrs) {
5
- let totalLength = 0;
6
- for (const arr of arrs) totalLength += arr.length;
7
- const result = new Uint8Array(totalLength);
8
- let offset = 0;
9
- for (const arr of arrs) {
10
- result.set(arr, offset);
11
- offset += arr.length;
12
- }
13
- return result;
14
- }
15
- var cutBuffer = (buffer) => {
16
- const bufferPacks = [];
17
- const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
18
- let size;
19
- for (let i = 0; i < buffer.length; i += size) {
20
- size = view.getInt32(i);
21
- bufferPacks.push(buffer.slice(i, i + size));
22
- }
23
- return bufferPacks;
24
- };
25
- var makeDecoder = ({ inflateAsync: inflateAsync2, brotliDecompressAsync: brotliDecompressAsync2 }) => {
26
- const decoder = async (buffer) => {
27
- const packs = await Promise.all(
28
- cutBuffer(buffer).map(async (buf) => {
29
- const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
30
- const body = buf.slice(16);
31
- const protocol = view.getInt16(6);
32
- const operation = view.getInt32(8);
33
- let type = "unknow";
34
- if (operation === 3) {
35
- type = "heartbeat";
36
- } else if (operation === 5) {
37
- type = "message";
38
- } else if (operation === 8) {
39
- type = "welcome";
40
- }
41
- let data;
42
- if (protocol === 0) {
43
- data = JSON.parse(textDecoder.decode(body));
44
- }
45
- if (protocol === 1 && body.length === 4) {
46
- const bodyView = new DataView(body.buffer, body.byteOffset, body.byteLength);
47
- data = bodyView.getUint32(0);
48
- }
49
- if (protocol === 2) {
50
- data = await decoder(await inflateAsync2(body));
51
- }
52
- if (protocol === 3) {
53
- data = await decoder(await brotliDecompressAsync2(body));
54
- }
55
- return { buf, type, protocol, data };
56
- })
57
- );
58
- return packs.flatMap((pack) => {
59
- if (pack.protocol === 2 || pack.protocol === 3) {
60
- return pack.data;
61
- }
62
- return pack;
63
- });
64
- };
65
- return decoder;
66
- };
67
- var encoder = (type, body = "") => {
68
- const encoded = typeof body === "string" ? body : JSON.stringify(body);
69
- const head = new Uint8Array(16);
70
- const headView = new DataView(head.buffer, head.byteOffset, head.byteLength);
71
- const buffer = textEncoder.encode(encoded);
72
- headView.setInt32(0, buffer.length + head.length);
73
- headView.setInt16(4, 16);
74
- headView.setInt16(6, 1);
75
- if (type === "heartbeat") {
76
- headView.setInt32(8, 2);
77
- }
78
- if (type === "join") {
79
- headView.setInt32(8, 7);
80
- }
81
- headView.setInt32(12, 1);
82
- return concatUint8Arrays([head, buffer]);
83
- };
84
-
85
- // src/common.ts
86
- var DataEvent = class extends Event {
87
- data;
88
- constructor(type, data) {
89
- super(type);
90
- this.data = data;
91
- }
92
- };
93
- var EventEvent = class extends Event {
94
- event;
95
- constructor(event) {
96
- super("event");
97
- this.event = event;
98
- }
99
- };
100
- var Live = class extends EventTarget {
101
- roomid;
102
- online;
103
- live;
104
- closed;
105
- timeout;
106
- send;
107
- close;
108
- constructor(inflates2, roomid, {
109
- send,
110
- close,
111
- protover = 3,
112
- key,
113
- authBody,
114
- uid = 0,
115
- buvid
116
- }) {
117
- if (typeof roomid !== "number" || Number.isNaN(roomid)) {
118
- throw new Error(`roomid ${roomid} must be Number not NaN`);
119
- }
120
- super();
121
- this.roomid = roomid;
122
- this.online = 0;
123
- this.live = false;
124
- this.closed = false;
125
- this.timeout = setTimeout(() => {
126
- }, 0);
127
- this.send = send;
128
- this.close = () => {
129
- this.closed = true;
130
- close();
131
- };
132
- const decode = makeDecoder(inflates2);
133
- this.addEventListener("message", async (e) => {
134
- const buffer = e.data;
135
- const packs = await decode(buffer);
136
- packs.forEach(({ type, data }) => {
137
- if (type === "welcome") {
138
- this.live = true;
139
- this.dispatchEvent(new Event("live"));
140
- this.send(encoder("heartbeat"));
141
- }
142
- if (type === "heartbeat") {
143
- this.online = data;
144
- clearTimeout(this.timeout);
145
- this.timeout = setTimeout(() => this.heartbeat(), 1e3 * 30);
146
- this.dispatchEvent(new DataEvent("heartbeat", this.online));
147
- }
148
- if (type === "message") {
149
- this.dispatchEvent(new DataEvent("msg", data));
150
- const cmd = data.cmd || data.msg?.cmd;
151
- if (cmd) {
152
- if (cmd.includes("DANMU_MSG")) {
153
- this.dispatchEvent(new DataEvent("DANMU_MSG", data));
154
- } else {
155
- this.dispatchEvent(new DataEvent(cmd, data));
156
- }
157
- }
158
- }
159
- });
160
- });
161
- this.addEventListener("open", () => {
162
- if (authBody) {
163
- this.send(authBody instanceof Uint8Array ? authBody : encoder("join", authBody));
164
- } else {
165
- const hi = { uid, roomid, protover, platform: "web", type: 2 };
166
- if (key) {
167
- hi.key = key;
168
- }
169
- if (buvid) {
170
- hi.buvid = buvid;
171
- }
172
- const buf = encoder("join", hi);
173
- this.send(buf);
174
- }
175
- });
176
- this.addEventListener("close", () => {
177
- clearTimeout(this.timeout);
178
- });
179
- this.addEventListener("_error", () => {
180
- this.close();
181
- this.dispatchEvent(new Event("error"));
182
- });
183
- }
184
- dispatchEvent(event) {
185
- const result = super.dispatchEvent(event);
186
- super.dispatchEvent(new EventEvent(event));
187
- return result;
188
- }
189
- heartbeat() {
190
- this.send(encoder("heartbeat"));
191
- }
192
- getOnline() {
193
- this.heartbeat();
194
- return new Promise(
195
- (resolve) => this.addEventListener("heartbeat", (e) => resolve(e.data), { once: true })
196
- );
197
- }
198
- on(type, listener, options) {
199
- this.addEventListener(type, listener, options);
200
- }
201
- off(type, listener, options) {
202
- this.removeEventListener(type, listener, options);
203
- }
204
- };
205
- var KeepLive = class extends EventTarget {
206
- params;
207
- closed;
208
- interval;
209
- timeout;
210
- connection;
211
- Base;
212
- constructor(Base, ...params) {
213
- super();
214
- this.params = params;
215
- this.closed = false;
216
- this.interval = 100;
217
- this.timeout = 45 * 1e3;
218
- this.connection = new Base(...this.params);
219
- this.Base = Base;
220
- this.connect(false);
221
- }
222
- dispatchEvent(event) {
223
- const result = super.dispatchEvent(event);
224
- super.dispatchEvent(new EventEvent(event));
225
- return result;
226
- }
227
- connect(reconnect = true) {
228
- if (reconnect) {
229
- this.connection.close();
230
- this.connection = new this.Base(...this.params);
231
- }
232
- const connection = this.connection;
233
- let timeout = setTimeout(() => {
234
- connection.close();
235
- connection.dispatchEvent(new Event("timeout"));
236
- }, this.timeout);
237
- connection.addEventListener("event", (e) => {
238
- const evt = e.event;
239
- if (evt.type !== "error") {
240
- if (evt instanceof DataEvent) {
241
- this.dispatchEvent(new DataEvent(evt.type, evt.data));
242
- } else {
243
- this.dispatchEvent(new Event(evt.type));
244
- }
245
- }
246
- });
247
- connection.addEventListener("error", () => this.dispatchEvent(new Event("e")));
248
- connection.addEventListener("close", () => {
249
- if (!this.closed) {
250
- setTimeout(() => this.connect(), this.interval);
251
- }
252
- });
253
- connection.addEventListener("heartbeat", () => {
254
- clearTimeout(timeout);
255
- timeout = setTimeout(() => {
256
- connection.close();
257
- connection.dispatchEvent(new Event("timeout"));
258
- }, this.timeout);
259
- });
260
- connection.addEventListener("close", () => {
261
- clearTimeout(timeout);
262
- });
263
- }
264
- get online() {
265
- return this.connection.online;
266
- }
267
- get roomid() {
268
- return this.connection.roomid;
269
- }
270
- close() {
271
- this.closed = true;
272
- this.connection.close();
273
- }
274
- heartbeat() {
275
- return this.connection.heartbeat();
276
- }
277
- getOnline() {
278
- return this.connection.getOnline();
279
- }
280
- send(data) {
281
- return this.connection.send(data);
282
- }
283
- on(type, listener, options) {
284
- this.addEventListener(type, listener, options);
285
- }
286
- off(type, listener, options) {
287
- this.removeEventListener(type, listener, options);
288
- }
289
- };
290
-
291
1
  // src/inflate/browser.ts
292
2
  import { inflate } from "pako";
293
3
 
@@ -1996,89 +1706,458 @@ var makeBrotliDecode = () => {
1996
1706
  function min(a, b) {
1997
1707
  return a <= b ? a : b;
1998
1708
  }
1999
- function copyBytes(dst, target, src, start, end) {
2000
- dst.set(src.slice(start, end), target);
1709
+ function copyBytes(dst, target, src, start, end) {
1710
+ dst.set(src.slice(start, end), target);
1711
+ }
1712
+ function readInput(src, dst, offset, length) {
1713
+ if (src == null) return -1;
1714
+ let end = min(src.offset + length, src.data.length);
1715
+ let bytesRead = end - src.offset;
1716
+ dst.set(src.data.subarray(src.offset, end), offset);
1717
+ src.offset += bytesRead;
1718
+ return bytesRead;
1719
+ }
1720
+ function closeInput(src) {
1721
+ return 0;
1722
+ }
1723
+ function asReadOnlyBuffer(src) {
1724
+ return src;
1725
+ }
1726
+ function isReadOnly(src) {
1727
+ return 1;
1728
+ }
1729
+ function isDirect(src) {
1730
+ return 1;
1731
+ }
1732
+ function flipBuffer(buffer) {
1733
+ }
1734
+ function toUsAsciiBytes(src) {
1735
+ let n = src.length;
1736
+ let result = new Int8Array(n);
1737
+ for (let i = 0; i < n; ++i) {
1738
+ result[i] = src.charCodeAt(i);
1739
+ }
1740
+ return result;
1741
+ }
1742
+ function decode(bytes, options) {
1743
+ let s = new State();
1744
+ initState(s, new InputStream(bytes));
1745
+ if (options) {
1746
+ let customDictionary = (
1747
+ /** @type {?Int8Array} */
1748
+ options["customDictionary"]
1749
+ );
1750
+ if (customDictionary) attachDictionaryChunk(s, customDictionary);
1751
+ }
1752
+ let totalOutput = 0;
1753
+ let chunks = [];
1754
+ while (true) {
1755
+ let chunk = new Int8Array(16384);
1756
+ chunks.push(chunk);
1757
+ s.output = chunk;
1758
+ s.outputOffset = 0;
1759
+ s.outputLength = 16384;
1760
+ s.outputUsed = 0;
1761
+ decompress(s);
1762
+ totalOutput += s.outputUsed;
1763
+ if (s.outputUsed < 16384) break;
1764
+ }
1765
+ close(s);
1766
+ let result = new Int8Array(totalOutput);
1767
+ let offset = 0;
1768
+ for (let i = 0; i < chunks.length; ++i) {
1769
+ let chunk = chunks[i];
1770
+ let end = min(totalOutput, offset + 16384);
1771
+ let len = end - offset;
1772
+ if (len < 16384) {
1773
+ result.set(chunk.subarray(0, len), offset);
1774
+ } else {
1775
+ result.set(chunk, offset);
1776
+ }
1777
+ offset += len;
1778
+ }
1779
+ return result;
1780
+ }
1781
+ return decode;
1782
+ };
1783
+ var BrotliDecode = makeBrotliDecode();
1784
+
1785
+ // src/inflate/browser.ts
1786
+ var inflateAsync = (d) => inflate(d);
1787
+ var brotliDecompressAsync = (d) => Uint8Array.from(BrotliDecode(Int8Array.from(d)));
1788
+ var inflates = { inflateAsync, brotliDecompressAsync };
1789
+
1790
+ // src/events.ts
1791
+ var DataEvent = class extends Event {
1792
+ data;
1793
+ constructor(type, data) {
1794
+ super(type);
1795
+ this.data = data;
1796
+ }
1797
+ };
1798
+
1799
+ // src/keep-live.ts
1800
+ var KeepLive = class extends EventTarget {
1801
+ /** @internal Stored constructor arguments for reconnection. */
1802
+ params;
1803
+ /** `true` after {@link close} has been called; prevents further reconnects. */
1804
+ closed;
1805
+ /** Delay in milliseconds before attempting a reconnect. */
1806
+ interval;
1807
+ /** Maximum milliseconds to wait for a heartbeat before forcing a reconnect. */
1808
+ timeout;
1809
+ /** The current underlying connection instance. */
1810
+ connection;
1811
+ /** @internal The base class constructor used to create new connections. */
1812
+ Base;
1813
+ constructor(Base, ...params) {
1814
+ super();
1815
+ this.params = params;
1816
+ this.closed = false;
1817
+ this.interval = 100;
1818
+ this.timeout = 45 * 1e3;
1819
+ this.connection = new Base(...this.params);
1820
+ this.Base = Base;
1821
+ this.connect(false);
1822
+ }
1823
+ /**
1824
+ * Overridden to also dispatch a `DataEvent<Event>` with type `"event"`
1825
+ * for every event, enabling catch-all listeners.
1826
+ */
1827
+ dispatchEvent(event) {
1828
+ const result = super.dispatchEvent(event);
1829
+ super.dispatchEvent(new DataEvent("event", event));
1830
+ return result;
1831
+ }
1832
+ /**
1833
+ * Wire up event forwarding and timeout handling on the current connection.
1834
+ * When `reconnect` is `true`, the previous connection is closed and a fresh
1835
+ * one is created from the stored constructor parameters.
1836
+ *
1837
+ * @param reconnect - Whether to tear down and recreate the connection first.
1838
+ */
1839
+ connect(reconnect = true) {
1840
+ if (reconnect) {
1841
+ this.connection.close();
1842
+ this.connection = new this.Base(...this.params);
1843
+ }
1844
+ const connection = this.connection;
1845
+ let timeout = setTimeout(() => {
1846
+ connection.close();
1847
+ connection.dispatchEvent(new Event("timeout"));
1848
+ }, this.timeout);
1849
+ connection.addEventListener("event", (e) => {
1850
+ const evt = e.data;
1851
+ if (evt.type !== "error") {
1852
+ if (evt instanceof DataEvent) {
1853
+ this.dispatchEvent(new DataEvent(evt.type, evt.data));
1854
+ } else {
1855
+ this.dispatchEvent(new Event(evt.type));
1856
+ }
1857
+ }
1858
+ });
1859
+ connection.addEventListener("error", () => this.dispatchEvent(new Event("e")));
1860
+ connection.addEventListener("close", () => {
1861
+ if (!this.closed) {
1862
+ setTimeout(() => this.connect(), this.interval);
1863
+ }
1864
+ });
1865
+ connection.addEventListener("heartbeat", () => {
1866
+ clearTimeout(timeout);
1867
+ timeout = setTimeout(() => {
1868
+ connection.close();
1869
+ connection.dispatchEvent(new Event("timeout"));
1870
+ }, this.timeout);
1871
+ });
1872
+ connection.addEventListener("close", () => {
1873
+ clearTimeout(timeout);
1874
+ });
1875
+ }
1876
+ /** Latest known online viewer count from the underlying connection. */
1877
+ get online() {
1878
+ return this.connection.online;
1879
+ }
1880
+ /** The live room ID. */
1881
+ get roomid() {
1882
+ return this.connection.roomid;
1883
+ }
1884
+ /** Permanently close the connection and stop reconnecting. */
1885
+ close() {
1886
+ this.closed = true;
1887
+ this.connection.close();
1888
+ }
1889
+ /** Send a heartbeat packet to the server. */
1890
+ heartbeat() {
1891
+ return this.connection.heartbeat();
1892
+ }
1893
+ /**
1894
+ * Send a heartbeat and resolve with the online viewer count from the
1895
+ * next heartbeat response.
1896
+ * @returns A promise that resolves with the current online count.
1897
+ */
1898
+ getOnline() {
1899
+ return this.connection.getOnline();
2001
1900
  }
2002
- function readInput(src, dst, offset, length) {
2003
- if (src == null) return -1;
2004
- let end = min(src.offset + length, src.data.length);
2005
- let bytesRead = end - src.offset;
2006
- dst.set(src.data.subarray(src.offset, end), offset);
2007
- src.offset += bytesRead;
2008
- return bytesRead;
1901
+ /**
1902
+ * Send raw binary data through the underlying connection.
1903
+ * @param data - The binary packet to send.
1904
+ */
1905
+ send(data) {
1906
+ return this.connection.send(data);
2009
1907
  }
2010
- function closeInput(src) {
2011
- return 0;
1908
+ /**
1909
+ * Subscribe to an event type with a typed {@link DataEvent} listener.
1910
+ *
1911
+ * @typeParam T - Expected data type carried by the event.
1912
+ * @param type - Event name (e.g. `"heartbeat"`, `"msg"`, `"DANMU_MSG"`).
1913
+ * @param listener - Callback receiving a {@link DataEvent DataEvent\<T\>}.
1914
+ * @param options - Standard `addEventListener` options.
1915
+ */
1916
+ on(type, listener, options) {
1917
+ this.addEventListener(type, listener, options);
2012
1918
  }
2013
- function asReadOnlyBuffer(src) {
2014
- return src;
1919
+ /**
1920
+ * Unsubscribe a previously registered listener.
1921
+ *
1922
+ * @typeParam T - Data type matching the original subscription.
1923
+ * @param type - Event name.
1924
+ * @param listener - The same function reference passed to {@link on}.
1925
+ * @param options - Standard `removeEventListener` options.
1926
+ */
1927
+ off(type, listener, options) {
1928
+ this.removeEventListener(type, listener, options);
2015
1929
  }
2016
- function isReadOnly(src) {
2017
- return 1;
1930
+ };
1931
+
1932
+ // src/buffer.ts
1933
+ var textEncoder = new TextEncoder();
1934
+ var textDecoder = new TextDecoder();
1935
+ function concatUint8Arrays(arrs) {
1936
+ let totalLength = 0;
1937
+ for (const arr of arrs) totalLength += arr.length;
1938
+ const result = new Uint8Array(totalLength);
1939
+ let offset = 0;
1940
+ for (const arr of arrs) {
1941
+ result.set(arr, offset);
1942
+ offset += arr.length;
2018
1943
  }
2019
- function isDirect(src) {
2020
- return 1;
1944
+ return result;
1945
+ }
1946
+ var cutBuffer = (buffer) => {
1947
+ const bufferPacks = [];
1948
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1949
+ let size;
1950
+ for (let i = 0; i < buffer.length; i += size) {
1951
+ size = view.getInt32(i);
1952
+ bufferPacks.push(buffer.slice(i, i + size));
2021
1953
  }
2022
- function flipBuffer(buffer) {
1954
+ return bufferPacks;
1955
+ };
1956
+ var makeDecoder = ({ inflateAsync: inflateAsync2, brotliDecompressAsync: brotliDecompressAsync2 }) => {
1957
+ const decoder = async (buffer) => {
1958
+ const packs = await Promise.all(
1959
+ cutBuffer(buffer).map(async (buf) => {
1960
+ const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
1961
+ const body = buf.slice(16);
1962
+ const protocol = view.getInt16(6);
1963
+ const operation = view.getInt32(8);
1964
+ let type = "unknow";
1965
+ if (operation === 3) {
1966
+ type = "heartbeat";
1967
+ } else if (operation === 5) {
1968
+ type = "message";
1969
+ } else if (operation === 8) {
1970
+ type = "welcome";
1971
+ }
1972
+ let data;
1973
+ if (protocol === 0) {
1974
+ data = JSON.parse(textDecoder.decode(body));
1975
+ }
1976
+ if (protocol === 1 && body.length === 4) {
1977
+ const bodyView = new DataView(body.buffer, body.byteOffset, body.byteLength);
1978
+ data = bodyView.getUint32(0);
1979
+ }
1980
+ if (protocol === 2) {
1981
+ data = await decoder(await inflateAsync2(body));
1982
+ }
1983
+ if (protocol === 3) {
1984
+ data = await decoder(await brotliDecompressAsync2(body));
1985
+ }
1986
+ return { buf, type, protocol, data };
1987
+ })
1988
+ );
1989
+ return packs.flatMap((pack) => {
1990
+ if (pack.protocol === 2 || pack.protocol === 3) {
1991
+ return pack.data;
1992
+ }
1993
+ return pack;
1994
+ });
1995
+ };
1996
+ return decoder;
1997
+ };
1998
+ var encoder = (type, body = "") => {
1999
+ const encoded = typeof body === "string" ? body : JSON.stringify(body);
2000
+ const head = new Uint8Array(16);
2001
+ const headView = new DataView(head.buffer, head.byteOffset, head.byteLength);
2002
+ const buffer = textEncoder.encode(encoded);
2003
+ headView.setInt32(0, buffer.length + head.length);
2004
+ headView.setInt16(4, 16);
2005
+ headView.setInt16(6, 1);
2006
+ if (type === "heartbeat") {
2007
+ headView.setInt32(8, 2);
2023
2008
  }
2024
- function toUsAsciiBytes(src) {
2025
- let n = src.length;
2026
- let result = new Int8Array(n);
2027
- for (let i = 0; i < n; ++i) {
2028
- result[i] = src.charCodeAt(i);
2029
- }
2030
- return result;
2009
+ if (type === "join") {
2010
+ headView.setInt32(8, 7);
2031
2011
  }
2032
- function decode(bytes, options) {
2033
- let s = new State();
2034
- initState(s, new InputStream(bytes));
2035
- if (options) {
2036
- let customDictionary = (
2037
- /** @type {?Int8Array} */
2038
- options["customDictionary"]
2039
- );
2040
- if (customDictionary) attachDictionaryChunk(s, customDictionary);
2041
- }
2042
- let totalOutput = 0;
2043
- let chunks = [];
2044
- while (true) {
2045
- let chunk = new Int8Array(16384);
2046
- chunks.push(chunk);
2047
- s.output = chunk;
2048
- s.outputOffset = 0;
2049
- s.outputLength = 16384;
2050
- s.outputUsed = 0;
2051
- decompress(s);
2052
- totalOutput += s.outputUsed;
2053
- if (s.outputUsed < 16384) break;
2012
+ headView.setInt32(12, 1);
2013
+ return concatUint8Arrays([head, buffer]);
2014
+ };
2015
+
2016
+ // src/live.ts
2017
+ var Live = class extends EventTarget {
2018
+ /** The live room ID this instance is connected to. */
2019
+ roomid;
2020
+ /** Latest known online viewer count, updated on each heartbeat. */
2021
+ online;
2022
+ /** `true` after the server acknowledges the join (`welcome` packet). */
2023
+ live;
2024
+ /** `true` after {@link close} has been called. */
2025
+ closed;
2026
+ /** @internal Handle for the heartbeat interval timer. */
2027
+ timeout;
2028
+ /** Send raw binary data over the underlying transport. */
2029
+ send;
2030
+ /** Gracefully close the connection and set {@link closed} to `true`. */
2031
+ close;
2032
+ constructor(inflates2, roomid, {
2033
+ send,
2034
+ close,
2035
+ protover = 3,
2036
+ key,
2037
+ authBody,
2038
+ uid = 0,
2039
+ buvid
2040
+ }) {
2041
+ if (typeof roomid !== "number" || Number.isNaN(roomid)) {
2042
+ throw new Error(`roomid ${roomid} must be Number not NaN`);
2054
2043
  }
2055
- close(s);
2056
- let result = new Int8Array(totalOutput);
2057
- let offset = 0;
2058
- for (let i = 0; i < chunks.length; ++i) {
2059
- let chunk = chunks[i];
2060
- let end = min(totalOutput, offset + 16384);
2061
- let len = end - offset;
2062
- if (len < 16384) {
2063
- result.set(chunk.subarray(0, len), offset);
2044
+ super();
2045
+ this.roomid = roomid;
2046
+ this.online = 0;
2047
+ this.live = false;
2048
+ this.closed = false;
2049
+ this.timeout = setTimeout(() => {
2050
+ }, 0);
2051
+ this.send = send;
2052
+ this.close = () => {
2053
+ this.closed = true;
2054
+ close();
2055
+ };
2056
+ const decode = makeDecoder(inflates2);
2057
+ this.addEventListener("message", async (e) => {
2058
+ const buffer = e.data;
2059
+ const packs = await decode(buffer);
2060
+ packs.forEach(({ type, data }) => {
2061
+ if (type === "welcome") {
2062
+ this.live = true;
2063
+ this.dispatchEvent(new Event("live"));
2064
+ this.send(encoder("heartbeat"));
2065
+ }
2066
+ if (type === "heartbeat") {
2067
+ this.online = data;
2068
+ clearTimeout(this.timeout);
2069
+ this.timeout = setTimeout(() => this.heartbeat(), 1e3 * 30);
2070
+ this.dispatchEvent(new DataEvent("heartbeat", this.online));
2071
+ }
2072
+ if (type === "message") {
2073
+ this.dispatchEvent(new DataEvent("msg", data));
2074
+ const cmd = data.cmd || data.msg?.cmd;
2075
+ if (cmd) {
2076
+ if (cmd.includes("DANMU_MSG")) {
2077
+ this.dispatchEvent(new DataEvent("DANMU_MSG", data));
2078
+ } else {
2079
+ this.dispatchEvent(new DataEvent(cmd, data));
2080
+ }
2081
+ }
2082
+ }
2083
+ });
2084
+ });
2085
+ this.addEventListener("open", () => {
2086
+ if (authBody) {
2087
+ this.send(authBody instanceof Uint8Array ? authBody : encoder("join", authBody));
2064
2088
  } else {
2065
- result.set(chunk, offset);
2089
+ const hi = { uid, roomid, protover, platform: "web", type: 2 };
2090
+ if (key) {
2091
+ hi.key = key;
2092
+ }
2093
+ if (buvid) {
2094
+ hi.buvid = buvid;
2095
+ }
2096
+ const buf = encoder("join", hi);
2097
+ this.send(buf);
2066
2098
  }
2067
- offset += len;
2068
- }
2099
+ });
2100
+ this.addEventListener("close", () => {
2101
+ clearTimeout(this.timeout);
2102
+ });
2103
+ this.addEventListener("_error", () => {
2104
+ this.close();
2105
+ this.dispatchEvent(new Event("error"));
2106
+ });
2107
+ }
2108
+ /**
2109
+ * Overridden to also dispatch a `DataEvent<Event>` with type `"event"`
2110
+ * for every event, enabling catch-all listeners.
2111
+ */
2112
+ dispatchEvent(event) {
2113
+ const result = super.dispatchEvent(event);
2114
+ super.dispatchEvent(new DataEvent("event", event));
2069
2115
  return result;
2070
2116
  }
2071
- return decode;
2117
+ /** Send a heartbeat packet to the server. */
2118
+ heartbeat() {
2119
+ this.send(encoder("heartbeat"));
2120
+ }
2121
+ /**
2122
+ * Send a heartbeat and resolve with the online viewer count from the
2123
+ * next heartbeat response.
2124
+ * @returns A promise that resolves with the current online count.
2125
+ */
2126
+ getOnline() {
2127
+ this.heartbeat();
2128
+ return new Promise(
2129
+ (resolve) => this.addEventListener("heartbeat", (e) => resolve(e.data), { once: true })
2130
+ );
2131
+ }
2132
+ /**
2133
+ * Subscribe to an event type with a typed {@link DataEvent} listener.
2134
+ * Convenience wrapper around {@link EventTarget.addEventListener}.
2135
+ *
2136
+ * @typeParam T - Expected data type carried by the event.
2137
+ * @param type - Event name (e.g. `"heartbeat"`, `"msg"`, `"DANMU_MSG"`).
2138
+ * @param listener - Callback receiving a {@link DataEvent DataEvent\<T\>}.
2139
+ * @param options - Standard `addEventListener` options.
2140
+ */
2141
+ on(type, listener, options) {
2142
+ this.addEventListener(type, listener, options);
2143
+ }
2144
+ /**
2145
+ * Unsubscribe a previously registered listener.
2146
+ * Convenience wrapper around {@link EventTarget.removeEventListener}.
2147
+ *
2148
+ * @typeParam T - Data type matching the original subscription.
2149
+ * @param type - Event name.
2150
+ * @param listener - The same function reference passed to {@link on}.
2151
+ * @param options - Standard `removeEventListener` options.
2152
+ */
2153
+ off(type, listener, options) {
2154
+ this.removeEventListener(type, listener, options);
2155
+ }
2072
2156
  };
2073
- var BrotliDecode = makeBrotliDecode();
2074
-
2075
- // src/inflate/browser.ts
2076
- var inflateAsync = (d) => inflate(d);
2077
- var brotliDecompressAsync = (d) => Uint8Array.from(BrotliDecode(Int8Array.from(d)));
2078
- var inflates = { inflateAsync, brotliDecompressAsync };
2079
2157
 
2080
- // src/ws-client.ts
2158
+ // src/ws.ts
2081
2159
  var LiveWSBase = class extends Live {
2160
+ /** The underlying native WebSocket instance. */
2082
2161
  ws;
2083
2162
  constructor(inflates2, roomid, { address = "wss://broadcastlv.chat.bilibili.com/sub", ...options } = {}) {
2084
2163
  const ws = new WebSocket(address);
@@ -2114,7 +2193,6 @@ var KeepLiveWS = class extends KeepLive {
2114
2193
  };
2115
2194
  export {
2116
2195
  DataEvent,
2117
- EventEvent,
2118
2196
  KeepLiveWS,
2119
2197
  LiveWS
2120
2198
  };