@aka_openclaw_plugin/mychat 0.1.8 → 0.1.10

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,4538 @@
1
+ import { c as __toCommonJS, i as setMychatRuntime, l as __toESM, n as init_runtime, o as __commonJSMin, r as runtime_exports, s as __require, t as getMychatRuntime } from "./runtime-PfFuZ2Rm.js";
2
+ import { t as mychatConfigSchema } from "./config-schema-C42X7OSY.js";
3
+ import { createChannelPluginBase, createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
4
+ import { createAccountStatusSink } from "openclaw/plugin-sdk/channel-lifecycle";
5
+ import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/channel-plugin-common";
6
+ //#region src/accounts.ts
7
+ const DEFAULT_DM_POLICY = "open";
8
+ const DEFAULT_GROUP_POLICY = "open";
9
+ const DEFAULT_HISTORY_LIMIT = 50;
10
+ const DEFAULT_MEDIA_MAX_MB = 10;
11
+ function resolveWsUrl(baseUrl) {
12
+ const url = new URL(baseUrl);
13
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
14
+ url.pathname = url.pathname.replace(/\/+$/, "") + "/ws/bot";
15
+ return url.toString();
16
+ }
17
+ function mergeAccountConfig(channel, account) {
18
+ if (!account) return channel;
19
+ return {
20
+ ...channel,
21
+ ...account,
22
+ actions: {
23
+ messages: account.actions?.messages ?? channel.actions?.messages ?? true,
24
+ threads: account.actions?.threads ?? channel.actions?.threads ?? true,
25
+ reactions: account.actions?.reactions ?? channel.actions?.reactions ?? true
26
+ },
27
+ groups: {
28
+ ...channel.groups ?? {},
29
+ ...account.groups ?? {}
30
+ },
31
+ reconnect: account.reconnect ?? channel.reconnect
32
+ };
33
+ }
34
+ function resolveMychatAccounts(cfg) {
35
+ const mychatConfig = cfg.channels?.mychat;
36
+ if (!mychatConfig) return [];
37
+ const accounts = mychatConfig.accounts ?? {};
38
+ const accountIds = Object.keys(accounts);
39
+ if (accountIds.length === 0) {
40
+ const resolved = resolveSingleAccount({
41
+ accountId: DEFAULT_ACCOUNT_ID,
42
+ config: mychatConfig
43
+ });
44
+ return resolved ? [resolved] : [];
45
+ }
46
+ return accountIds.map((id) => {
47
+ return resolveSingleAccount({
48
+ accountId: id,
49
+ config: mergeAccountConfig(mychatConfig, accounts[id])
50
+ });
51
+ }).filter(Boolean);
52
+ }
53
+ function resolveSingleAccount(params) {
54
+ const { accountId, config } = params;
55
+ const enabled = config.enabled ?? true;
56
+ if (!enabled) return null;
57
+ const token = resolveToken(config);
58
+ if (!token) return null;
59
+ const baseUrl = config.baseUrl.replace(/\/+$/, "");
60
+ const wsUrl = config.wsUrl ?? resolveWsUrl(baseUrl);
61
+ return {
62
+ accountId: normalizeAccountId(accountId),
63
+ enabled,
64
+ name: config.name,
65
+ baseUrl,
66
+ wsUrl,
67
+ token,
68
+ defaultTo: config.defaultTo,
69
+ dmPolicy: config.dmPolicy ?? DEFAULT_DM_POLICY,
70
+ groupPolicy: config.groupPolicy ?? DEFAULT_GROUP_POLICY,
71
+ allowFrom: config.allowFrom ?? [],
72
+ groupAllowFrom: config.groupAllowFrom ?? [],
73
+ requireMention: config.requireMention ?? false,
74
+ historyLimit: config.historyLimit ?? DEFAULT_HISTORY_LIMIT,
75
+ mediaMaxMb: config.mediaMaxMb ?? DEFAULT_MEDIA_MAX_MB,
76
+ actions: {
77
+ messages: config.actions?.messages ?? true,
78
+ threads: config.actions?.threads ?? true,
79
+ reactions: config.actions?.reactions ?? true
80
+ },
81
+ groups: config.groups ?? {},
82
+ reconnect: config.reconnect ?? {}
83
+ };
84
+ }
85
+ function resolveToken(config) {
86
+ if (config.token) return config.token;
87
+ if (typeof process !== "undefined") {
88
+ const envToken = process.env.MYCHAT_BOT_TOKEN;
89
+ if (envToken) return envToken;
90
+ }
91
+ return null;
92
+ }
93
+ function resolveMychatAccount(params) {
94
+ const accounts = resolveMychatAccounts(params.cfg);
95
+ if (accounts.length === 0) return null;
96
+ if (!params.accountId) return accounts[0];
97
+ return accounts.find((a) => a.accountId === params.accountId) ?? accounts[0] ?? null;
98
+ }
99
+ //#endregion
100
+ //#region src/client/http-client.ts
101
+ function createMychatHttpClient(params) {
102
+ const { baseUrl, token } = params;
103
+ const base = baseUrl.replace(/\/+$/, "");
104
+ async function request(method, path, body) {
105
+ const url = `${base}${path}`;
106
+ try {
107
+ const headers = { authorization: `Bearer ${token}` };
108
+ if (body !== void 0) headers["content-type"] = "application/json";
109
+ const response = await fetch(url, {
110
+ method,
111
+ headers,
112
+ body: body !== void 0 ? JSON.stringify(body) : void 0
113
+ });
114
+ if (!response.ok) {
115
+ let errorBody = "";
116
+ try {
117
+ errorBody = await response.text();
118
+ } catch {}
119
+ console.error(`[mychat] HTTP ${method} ${url} failed: ${response.status} ${response.statusText}${errorBody ? ` body=${errorBody.slice(0, 200)}` : ""}`);
120
+ return null;
121
+ }
122
+ return await response.json();
123
+ } catch (err) {
124
+ const errMsg = err instanceof Error ? `${err.message}` : String(err);
125
+ console.error(`[mychat] HTTP ${method} ${url} error: ${errMsg}`);
126
+ return null;
127
+ }
128
+ }
129
+ return {
130
+ async getSelf() {
131
+ return request("GET", "/api/bot/self");
132
+ },
133
+ async getMe() {
134
+ return request("GET", "/api/auth/me");
135
+ },
136
+ async sendMessage(body) {
137
+ return request("POST", "/api/bot/messages", body);
138
+ },
139
+ async listMessages(params) {
140
+ const searchParams = new URLSearchParams();
141
+ if (params.conversationId) searchParams.set("conversationId", params.conversationId);
142
+ if (params.limit) searchParams.set("limit", String(params.limit));
143
+ if (params.after) searchParams.set("after", String(params.after));
144
+ const qs = searchParams.toString();
145
+ return (await request("GET", `/api/bot/messages${qs ? `?${qs}` : ""}`))?.messages ?? [];
146
+ },
147
+ async addReaction(messageId, body) {
148
+ return request("POST", `/api/bot/messages/${messageId}/reactions`, body);
149
+ },
150
+ async removeReaction(messageId, emoji) {
151
+ return request("POST", `/api/bot/messages/${messageId}/reactions`, {
152
+ emoji,
153
+ action: "remove"
154
+ });
155
+ },
156
+ async uploadFile(params) {
157
+ const url = `${base}/api/bot/uploads`;
158
+ try {
159
+ const formData = new FormData();
160
+ let blob;
161
+ if (params.file instanceof Blob) blob = params.file;
162
+ else {
163
+ const bytes = new Uint8Array(params.file.buffer, params.file.byteOffset, params.file.byteLength);
164
+ blob = new Blob([bytes], { type: params.mimeType });
165
+ }
166
+ formData.append("file", blob, params.filename);
167
+ if (params.scopeKind) formData.append("scopeKind", params.scopeKind);
168
+ if (params.scopeId) formData.append("scopeId", params.scopeId);
169
+ const response = await fetch(url, {
170
+ method: "POST",
171
+ headers: { authorization: `Bearer ${token}` },
172
+ body: formData
173
+ });
174
+ if (!response.ok) {
175
+ let errorBody = "";
176
+ try {
177
+ errorBody = await response.text();
178
+ } catch {}
179
+ console.error(`[mychat] HTTP POST ${url} failed: ${response.status} ${response.statusText}${errorBody ? ` body=${errorBody.slice(0, 200)}` : ""}`);
180
+ return null;
181
+ }
182
+ return await response.json();
183
+ } catch (err) {
184
+ const errMsg = err instanceof Error ? `${err.message}` : String(err);
185
+ console.error(`[mychat] HTTP POST ${url} error: ${errMsg}`);
186
+ return null;
187
+ }
188
+ },
189
+ async healthCheck() {
190
+ const url = `${base}/health`;
191
+ const start = Date.now();
192
+ try {
193
+ const response = await fetch(url);
194
+ const latencyMs = Date.now() - start;
195
+ if (!response.ok) console.error(`[mychat] Health check failed: ${url} status=${response.status} latencyMs=${latencyMs}`);
196
+ return {
197
+ ok: response.ok,
198
+ latencyMs
199
+ };
200
+ } catch (err) {
201
+ const errMsg = err instanceof Error ? `${err.message}` : String(err);
202
+ console.error(`[mychat] Health check error: ${url} error=${errMsg} latencyMs=${Date.now() - start}`);
203
+ return {
204
+ ok: false,
205
+ latencyMs: Date.now() - start
206
+ };
207
+ }
208
+ }
209
+ };
210
+ }
211
+ //#endregion
212
+ //#region ../../node_modules/ws/lib/constants.js
213
+ var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
214
+ const BINARY_TYPES = [
215
+ "nodebuffer",
216
+ "arraybuffer",
217
+ "fragments"
218
+ ];
219
+ const hasBlob = typeof Blob !== "undefined";
220
+ if (hasBlob) BINARY_TYPES.push("blob");
221
+ module.exports = {
222
+ BINARY_TYPES,
223
+ CLOSE_TIMEOUT: 3e4,
224
+ EMPTY_BUFFER: Buffer.alloc(0),
225
+ GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
226
+ hasBlob,
227
+ kForOnEventAttribute: Symbol("kIsForOnEventAttribute"),
228
+ kListener: Symbol("kListener"),
229
+ kStatusCode: Symbol("status-code"),
230
+ kWebSocket: Symbol("websocket"),
231
+ NOOP: () => {}
232
+ };
233
+ }));
234
+ //#endregion
235
+ //#region ../../node_modules/ws/lib/buffer-util.js
236
+ var require_buffer_util = /* @__PURE__ */ __commonJSMin(((exports, module) => {
237
+ const { EMPTY_BUFFER } = require_constants();
238
+ const FastBuffer = Buffer[Symbol.species];
239
+ /**
240
+ * Merges an array of buffers into a new buffer.
241
+ *
242
+ * @param {Buffer[]} list The array of buffers to concat
243
+ * @param {Number} totalLength The total length of buffers in the list
244
+ * @return {Buffer} The resulting buffer
245
+ * @public
246
+ */
247
+ function concat(list, totalLength) {
248
+ if (list.length === 0) return EMPTY_BUFFER;
249
+ if (list.length === 1) return list[0];
250
+ const target = Buffer.allocUnsafe(totalLength);
251
+ let offset = 0;
252
+ for (let i = 0; i < list.length; i++) {
253
+ const buf = list[i];
254
+ target.set(buf, offset);
255
+ offset += buf.length;
256
+ }
257
+ if (offset < totalLength) return new FastBuffer(target.buffer, target.byteOffset, offset);
258
+ return target;
259
+ }
260
+ /**
261
+ * Masks a buffer using the given mask.
262
+ *
263
+ * @param {Buffer} source The buffer to mask
264
+ * @param {Buffer} mask The mask to use
265
+ * @param {Buffer} output The buffer where to store the result
266
+ * @param {Number} offset The offset at which to start writing
267
+ * @param {Number} length The number of bytes to mask.
268
+ * @public
269
+ */
270
+ function _mask(source, mask, output, offset, length) {
271
+ for (let i = 0; i < length; i++) output[offset + i] = source[i] ^ mask[i & 3];
272
+ }
273
+ /**
274
+ * Unmasks a buffer using the given mask.
275
+ *
276
+ * @param {Buffer} buffer The buffer to unmask
277
+ * @param {Buffer} mask The mask to use
278
+ * @public
279
+ */
280
+ function _unmask(buffer, mask) {
281
+ for (let i = 0; i < buffer.length; i++) buffer[i] ^= mask[i & 3];
282
+ }
283
+ /**
284
+ * Converts a buffer to an `ArrayBuffer`.
285
+ *
286
+ * @param {Buffer} buf The buffer to convert
287
+ * @return {ArrayBuffer} Converted buffer
288
+ * @public
289
+ */
290
+ function toArrayBuffer(buf) {
291
+ if (buf.length === buf.buffer.byteLength) return buf.buffer;
292
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
293
+ }
294
+ /**
295
+ * Converts `data` to a `Buffer`.
296
+ *
297
+ * @param {*} data The data to convert
298
+ * @return {Buffer} The buffer
299
+ * @throws {TypeError}
300
+ * @public
301
+ */
302
+ function toBuffer(data) {
303
+ toBuffer.readOnly = true;
304
+ if (Buffer.isBuffer(data)) return data;
305
+ let buf;
306
+ if (data instanceof ArrayBuffer) buf = new FastBuffer(data);
307
+ else if (ArrayBuffer.isView(data)) buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);
308
+ else {
309
+ buf = Buffer.from(data);
310
+ toBuffer.readOnly = false;
311
+ }
312
+ return buf;
313
+ }
314
+ module.exports = {
315
+ concat,
316
+ mask: _mask,
317
+ toArrayBuffer,
318
+ toBuffer,
319
+ unmask: _unmask
320
+ };
321
+ /* istanbul ignore else */
322
+ if (!process.env.WS_NO_BUFFER_UTIL) try {
323
+ const bufferUtil = __require("bufferutil");
324
+ module.exports.mask = function(source, mask, output, offset, length) {
325
+ if (length < 48) _mask(source, mask, output, offset, length);
326
+ else bufferUtil.mask(source, mask, output, offset, length);
327
+ };
328
+ module.exports.unmask = function(buffer, mask) {
329
+ if (buffer.length < 32) _unmask(buffer, mask);
330
+ else bufferUtil.unmask(buffer, mask);
331
+ };
332
+ } catch (e) {}
333
+ }));
334
+ //#endregion
335
+ //#region ../../node_modules/ws/lib/limiter.js
336
+ var require_limiter = /* @__PURE__ */ __commonJSMin(((exports, module) => {
337
+ const kDone = Symbol("kDone");
338
+ const kRun = Symbol("kRun");
339
+ /**
340
+ * A very simple job queue with adjustable concurrency. Adapted from
341
+ * https://github.com/STRML/async-limiter
342
+ */
343
+ var Limiter = class {
344
+ /**
345
+ * Creates a new `Limiter`.
346
+ *
347
+ * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
348
+ * to run concurrently
349
+ */
350
+ constructor(concurrency) {
351
+ this[kDone] = () => {
352
+ this.pending--;
353
+ this[kRun]();
354
+ };
355
+ this.concurrency = concurrency || Infinity;
356
+ this.jobs = [];
357
+ this.pending = 0;
358
+ }
359
+ /**
360
+ * Adds a job to the queue.
361
+ *
362
+ * @param {Function} job The job to run
363
+ * @public
364
+ */
365
+ add(job) {
366
+ this.jobs.push(job);
367
+ this[kRun]();
368
+ }
369
+ /**
370
+ * Removes a job from the queue and runs it if possible.
371
+ *
372
+ * @private
373
+ */
374
+ [kRun]() {
375
+ if (this.pending === this.concurrency) return;
376
+ if (this.jobs.length) {
377
+ const job = this.jobs.shift();
378
+ this.pending++;
379
+ job(this[kDone]);
380
+ }
381
+ }
382
+ };
383
+ module.exports = Limiter;
384
+ }));
385
+ //#endregion
386
+ //#region ../../node_modules/ws/lib/permessage-deflate.js
387
+ var require_permessage_deflate = /* @__PURE__ */ __commonJSMin(((exports, module) => {
388
+ const zlib = __require("zlib");
389
+ const bufferUtil = require_buffer_util();
390
+ const Limiter = require_limiter();
391
+ const { kStatusCode } = require_constants();
392
+ const FastBuffer = Buffer[Symbol.species];
393
+ const TRAILER = Buffer.from([
394
+ 0,
395
+ 0,
396
+ 255,
397
+ 255
398
+ ]);
399
+ const kPerMessageDeflate = Symbol("permessage-deflate");
400
+ const kTotalLength = Symbol("total-length");
401
+ const kCallback = Symbol("callback");
402
+ const kBuffers = Symbol("buffers");
403
+ const kError = Symbol("error");
404
+ let zlibLimiter;
405
+ /**
406
+ * permessage-deflate implementation.
407
+ */
408
+ var PerMessageDeflate = class {
409
+ /**
410
+ * Creates a PerMessageDeflate instance.
411
+ *
412
+ * @param {Object} [options] Configuration options
413
+ * @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
414
+ * for, or request, a custom client window size
415
+ * @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
416
+ * acknowledge disabling of client context takeover
417
+ * @param {Number} [options.concurrencyLimit=10] The number of concurrent
418
+ * calls to zlib
419
+ * @param {Boolean} [options.isServer=false] Create the instance in either
420
+ * server or client mode
421
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
422
+ * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
423
+ * use of a custom server window size
424
+ * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
425
+ * disabling of server context takeover
426
+ * @param {Number} [options.threshold=1024] Size (in bytes) below which
427
+ * messages should not be compressed if context takeover is disabled
428
+ * @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
429
+ * deflate
430
+ * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
431
+ * inflate
432
+ */
433
+ constructor(options) {
434
+ this._options = options || {};
435
+ this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
436
+ this._maxPayload = this._options.maxPayload | 0;
437
+ this._isServer = !!this._options.isServer;
438
+ this._deflate = null;
439
+ this._inflate = null;
440
+ this.params = null;
441
+ if (!zlibLimiter) zlibLimiter = new Limiter(this._options.concurrencyLimit !== void 0 ? this._options.concurrencyLimit : 10);
442
+ }
443
+ /**
444
+ * @type {String}
445
+ */
446
+ static get extensionName() {
447
+ return "permessage-deflate";
448
+ }
449
+ /**
450
+ * Create an extension negotiation offer.
451
+ *
452
+ * @return {Object} Extension parameters
453
+ * @public
454
+ */
455
+ offer() {
456
+ const params = {};
457
+ if (this._options.serverNoContextTakeover) params.server_no_context_takeover = true;
458
+ if (this._options.clientNoContextTakeover) params.client_no_context_takeover = true;
459
+ if (this._options.serverMaxWindowBits) params.server_max_window_bits = this._options.serverMaxWindowBits;
460
+ if (this._options.clientMaxWindowBits) params.client_max_window_bits = this._options.clientMaxWindowBits;
461
+ else if (this._options.clientMaxWindowBits == null) params.client_max_window_bits = true;
462
+ return params;
463
+ }
464
+ /**
465
+ * Accept an extension negotiation offer/response.
466
+ *
467
+ * @param {Array} configurations The extension negotiation offers/reponse
468
+ * @return {Object} Accepted configuration
469
+ * @public
470
+ */
471
+ accept(configurations) {
472
+ configurations = this.normalizeParams(configurations);
473
+ this.params = this._isServer ? this.acceptAsServer(configurations) : this.acceptAsClient(configurations);
474
+ return this.params;
475
+ }
476
+ /**
477
+ * Releases all resources used by the extension.
478
+ *
479
+ * @public
480
+ */
481
+ cleanup() {
482
+ if (this._inflate) {
483
+ this._inflate.close();
484
+ this._inflate = null;
485
+ }
486
+ if (this._deflate) {
487
+ const callback = this._deflate[kCallback];
488
+ this._deflate.close();
489
+ this._deflate = null;
490
+ if (callback) callback(/* @__PURE__ */ new Error("The deflate stream was closed while data was being processed"));
491
+ }
492
+ }
493
+ /**
494
+ * Accept an extension negotiation offer.
495
+ *
496
+ * @param {Array} offers The extension negotiation offers
497
+ * @return {Object} Accepted configuration
498
+ * @private
499
+ */
500
+ acceptAsServer(offers) {
501
+ const opts = this._options;
502
+ const accepted = offers.find((params) => {
503
+ if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) return false;
504
+ return true;
505
+ });
506
+ if (!accepted) throw new Error("None of the extension offers can be accepted");
507
+ if (opts.serverNoContextTakeover) accepted.server_no_context_takeover = true;
508
+ if (opts.clientNoContextTakeover) accepted.client_no_context_takeover = true;
509
+ if (typeof opts.serverMaxWindowBits === "number") accepted.server_max_window_bits = opts.serverMaxWindowBits;
510
+ if (typeof opts.clientMaxWindowBits === "number") accepted.client_max_window_bits = opts.clientMaxWindowBits;
511
+ else if (accepted.client_max_window_bits === true || opts.clientMaxWindowBits === false) delete accepted.client_max_window_bits;
512
+ return accepted;
513
+ }
514
+ /**
515
+ * Accept the extension negotiation response.
516
+ *
517
+ * @param {Array} response The extension negotiation response
518
+ * @return {Object} Accepted configuration
519
+ * @private
520
+ */
521
+ acceptAsClient(response) {
522
+ const params = response[0];
523
+ if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) throw new Error("Unexpected parameter \"client_no_context_takeover\"");
524
+ if (!params.client_max_window_bits) {
525
+ if (typeof this._options.clientMaxWindowBits === "number") params.client_max_window_bits = this._options.clientMaxWindowBits;
526
+ } else if (this._options.clientMaxWindowBits === false || typeof this._options.clientMaxWindowBits === "number" && params.client_max_window_bits > this._options.clientMaxWindowBits) throw new Error("Unexpected or invalid parameter \"client_max_window_bits\"");
527
+ return params;
528
+ }
529
+ /**
530
+ * Normalize parameters.
531
+ *
532
+ * @param {Array} configurations The extension negotiation offers/reponse
533
+ * @return {Array} The offers/response with normalized parameters
534
+ * @private
535
+ */
536
+ normalizeParams(configurations) {
537
+ configurations.forEach((params) => {
538
+ Object.keys(params).forEach((key) => {
539
+ let value = params[key];
540
+ if (value.length > 1) throw new Error(`Parameter "${key}" must have only a single value`);
541
+ value = value[0];
542
+ if (key === "client_max_window_bits") {
543
+ if (value !== true) {
544
+ const num = +value;
545
+ if (!Number.isInteger(num) || num < 8 || num > 15) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
546
+ value = num;
547
+ } else if (!this._isServer) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
548
+ } else if (key === "server_max_window_bits") {
549
+ const num = +value;
550
+ if (!Number.isInteger(num) || num < 8 || num > 15) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
551
+ value = num;
552
+ } else if (key === "client_no_context_takeover" || key === "server_no_context_takeover") {
553
+ if (value !== true) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
554
+ } else throw new Error(`Unknown parameter "${key}"`);
555
+ params[key] = value;
556
+ });
557
+ });
558
+ return configurations;
559
+ }
560
+ /**
561
+ * Decompress data. Concurrency limited.
562
+ *
563
+ * @param {Buffer} data Compressed data
564
+ * @param {Boolean} fin Specifies whether or not this is the last fragment
565
+ * @param {Function} callback Callback
566
+ * @public
567
+ */
568
+ decompress(data, fin, callback) {
569
+ zlibLimiter.add((done) => {
570
+ this._decompress(data, fin, (err, result) => {
571
+ done();
572
+ callback(err, result);
573
+ });
574
+ });
575
+ }
576
+ /**
577
+ * Compress data. Concurrency limited.
578
+ *
579
+ * @param {(Buffer|String)} data Data to compress
580
+ * @param {Boolean} fin Specifies whether or not this is the last fragment
581
+ * @param {Function} callback Callback
582
+ * @public
583
+ */
584
+ compress(data, fin, callback) {
585
+ zlibLimiter.add((done) => {
586
+ this._compress(data, fin, (err, result) => {
587
+ done();
588
+ callback(err, result);
589
+ });
590
+ });
591
+ }
592
+ /**
593
+ * Decompress data.
594
+ *
595
+ * @param {Buffer} data Compressed data
596
+ * @param {Boolean} fin Specifies whether or not this is the last fragment
597
+ * @param {Function} callback Callback
598
+ * @private
599
+ */
600
+ _decompress(data, fin, callback) {
601
+ const endpoint = this._isServer ? "client" : "server";
602
+ if (!this._inflate) {
603
+ const key = `${endpoint}_max_window_bits`;
604
+ const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
605
+ this._inflate = zlib.createInflateRaw({
606
+ ...this._options.zlibInflateOptions,
607
+ windowBits
608
+ });
609
+ this._inflate[kPerMessageDeflate] = this;
610
+ this._inflate[kTotalLength] = 0;
611
+ this._inflate[kBuffers] = [];
612
+ this._inflate.on("error", inflateOnError);
613
+ this._inflate.on("data", inflateOnData);
614
+ }
615
+ this._inflate[kCallback] = callback;
616
+ this._inflate.write(data);
617
+ if (fin) this._inflate.write(TRAILER);
618
+ this._inflate.flush(() => {
619
+ const err = this._inflate[kError];
620
+ if (err) {
621
+ this._inflate.close();
622
+ this._inflate = null;
623
+ callback(err);
624
+ return;
625
+ }
626
+ const data = bufferUtil.concat(this._inflate[kBuffers], this._inflate[kTotalLength]);
627
+ if (this._inflate._readableState.endEmitted) {
628
+ this._inflate.close();
629
+ this._inflate = null;
630
+ } else {
631
+ this._inflate[kTotalLength] = 0;
632
+ this._inflate[kBuffers] = [];
633
+ if (fin && this.params[`${endpoint}_no_context_takeover`]) this._inflate.reset();
634
+ }
635
+ callback(null, data);
636
+ });
637
+ }
638
+ /**
639
+ * Compress data.
640
+ *
641
+ * @param {(Buffer|String)} data Data to compress
642
+ * @param {Boolean} fin Specifies whether or not this is the last fragment
643
+ * @param {Function} callback Callback
644
+ * @private
645
+ */
646
+ _compress(data, fin, callback) {
647
+ const endpoint = this._isServer ? "server" : "client";
648
+ if (!this._deflate) {
649
+ const key = `${endpoint}_max_window_bits`;
650
+ const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
651
+ this._deflate = zlib.createDeflateRaw({
652
+ ...this._options.zlibDeflateOptions,
653
+ windowBits
654
+ });
655
+ this._deflate[kTotalLength] = 0;
656
+ this._deflate[kBuffers] = [];
657
+ this._deflate.on("data", deflateOnData);
658
+ }
659
+ this._deflate[kCallback] = callback;
660
+ this._deflate.write(data);
661
+ this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
662
+ if (!this._deflate) return;
663
+ let data = bufferUtil.concat(this._deflate[kBuffers], this._deflate[kTotalLength]);
664
+ if (fin) data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);
665
+ this._deflate[kCallback] = null;
666
+ this._deflate[kTotalLength] = 0;
667
+ this._deflate[kBuffers] = [];
668
+ if (fin && this.params[`${endpoint}_no_context_takeover`]) this._deflate.reset();
669
+ callback(null, data);
670
+ });
671
+ }
672
+ };
673
+ module.exports = PerMessageDeflate;
674
+ /**
675
+ * The listener of the `zlib.DeflateRaw` stream `'data'` event.
676
+ *
677
+ * @param {Buffer} chunk A chunk of data
678
+ * @private
679
+ */
680
+ function deflateOnData(chunk) {
681
+ this[kBuffers].push(chunk);
682
+ this[kTotalLength] += chunk.length;
683
+ }
684
+ /**
685
+ * The listener of the `zlib.InflateRaw` stream `'data'` event.
686
+ *
687
+ * @param {Buffer} chunk A chunk of data
688
+ * @private
689
+ */
690
+ function inflateOnData(chunk) {
691
+ this[kTotalLength] += chunk.length;
692
+ if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) {
693
+ this[kBuffers].push(chunk);
694
+ return;
695
+ }
696
+ this[kError] = /* @__PURE__ */ new RangeError("Max payload size exceeded");
697
+ this[kError].code = "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH";
698
+ this[kError][kStatusCode] = 1009;
699
+ this.removeListener("data", inflateOnData);
700
+ this.reset();
701
+ }
702
+ /**
703
+ * The listener of the `zlib.InflateRaw` stream `'error'` event.
704
+ *
705
+ * @param {Error} err The emitted error
706
+ * @private
707
+ */
708
+ function inflateOnError(err) {
709
+ this[kPerMessageDeflate]._inflate = null;
710
+ if (this[kError]) {
711
+ this[kCallback](this[kError]);
712
+ return;
713
+ }
714
+ err[kStatusCode] = 1007;
715
+ this[kCallback](err);
716
+ }
717
+ }));
718
+ //#endregion
719
+ //#region ../../node_modules/ws/lib/validation.js
720
+ var require_validation = /* @__PURE__ */ __commonJSMin(((exports, module) => {
721
+ const { isUtf8 } = __require("buffer");
722
+ const { hasBlob } = require_constants();
723
+ const tokenChars = [
724
+ 0,
725
+ 0,
726
+ 0,
727
+ 0,
728
+ 0,
729
+ 0,
730
+ 0,
731
+ 0,
732
+ 0,
733
+ 0,
734
+ 0,
735
+ 0,
736
+ 0,
737
+ 0,
738
+ 0,
739
+ 0,
740
+ 0,
741
+ 0,
742
+ 0,
743
+ 0,
744
+ 0,
745
+ 0,
746
+ 0,
747
+ 0,
748
+ 0,
749
+ 0,
750
+ 0,
751
+ 0,
752
+ 0,
753
+ 0,
754
+ 0,
755
+ 0,
756
+ 0,
757
+ 1,
758
+ 0,
759
+ 1,
760
+ 1,
761
+ 1,
762
+ 1,
763
+ 1,
764
+ 0,
765
+ 0,
766
+ 1,
767
+ 1,
768
+ 0,
769
+ 1,
770
+ 1,
771
+ 0,
772
+ 1,
773
+ 1,
774
+ 1,
775
+ 1,
776
+ 1,
777
+ 1,
778
+ 1,
779
+ 1,
780
+ 1,
781
+ 1,
782
+ 0,
783
+ 0,
784
+ 0,
785
+ 0,
786
+ 0,
787
+ 0,
788
+ 0,
789
+ 1,
790
+ 1,
791
+ 1,
792
+ 1,
793
+ 1,
794
+ 1,
795
+ 1,
796
+ 1,
797
+ 1,
798
+ 1,
799
+ 1,
800
+ 1,
801
+ 1,
802
+ 1,
803
+ 1,
804
+ 1,
805
+ 1,
806
+ 1,
807
+ 1,
808
+ 1,
809
+ 1,
810
+ 1,
811
+ 1,
812
+ 1,
813
+ 1,
814
+ 1,
815
+ 0,
816
+ 0,
817
+ 0,
818
+ 1,
819
+ 1,
820
+ 1,
821
+ 1,
822
+ 1,
823
+ 1,
824
+ 1,
825
+ 1,
826
+ 1,
827
+ 1,
828
+ 1,
829
+ 1,
830
+ 1,
831
+ 1,
832
+ 1,
833
+ 1,
834
+ 1,
835
+ 1,
836
+ 1,
837
+ 1,
838
+ 1,
839
+ 1,
840
+ 1,
841
+ 1,
842
+ 1,
843
+ 1,
844
+ 1,
845
+ 1,
846
+ 1,
847
+ 0,
848
+ 1,
849
+ 0,
850
+ 1,
851
+ 0
852
+ ];
853
+ /**
854
+ * Checks if a status code is allowed in a close frame.
855
+ *
856
+ * @param {Number} code The status code
857
+ * @return {Boolean} `true` if the status code is valid, else `false`
858
+ * @public
859
+ */
860
+ function isValidStatusCode(code) {
861
+ return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999;
862
+ }
863
+ /**
864
+ * Checks if a given buffer contains only correct UTF-8.
865
+ * Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by
866
+ * Markus Kuhn.
867
+ *
868
+ * @param {Buffer} buf The buffer to check
869
+ * @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`
870
+ * @public
871
+ */
872
+ function _isValidUTF8(buf) {
873
+ const len = buf.length;
874
+ let i = 0;
875
+ while (i < len) if ((buf[i] & 128) === 0) i++;
876
+ else if ((buf[i] & 224) === 192) {
877
+ if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) return false;
878
+ i += 2;
879
+ } else if ((buf[i] & 240) === 224) {
880
+ if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || buf[i] === 237 && (buf[i + 1] & 224) === 160) return false;
881
+ i += 3;
882
+ } else if ((buf[i] & 248) === 240) {
883
+ if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) return false;
884
+ i += 4;
885
+ } else return false;
886
+ return true;
887
+ }
888
+ /**
889
+ * Determines whether a value is a `Blob`.
890
+ *
891
+ * @param {*} value The value to be tested
892
+ * @return {Boolean} `true` if `value` is a `Blob`, else `false`
893
+ * @private
894
+ */
895
+ function isBlob(value) {
896
+ return hasBlob && typeof value === "object" && typeof value.arrayBuffer === "function" && typeof value.type === "string" && typeof value.stream === "function" && (value[Symbol.toStringTag] === "Blob" || value[Symbol.toStringTag] === "File");
897
+ }
898
+ module.exports = {
899
+ isBlob,
900
+ isValidStatusCode,
901
+ isValidUTF8: _isValidUTF8,
902
+ tokenChars
903
+ };
904
+ if (isUtf8) module.exports.isValidUTF8 = function(buf) {
905
+ return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
906
+ };
907
+ else if (!process.env.WS_NO_UTF_8_VALIDATE) try {
908
+ const isValidUTF8 = __require("utf-8-validate");
909
+ module.exports.isValidUTF8 = function(buf) {
910
+ return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
911
+ };
912
+ } catch (e) {}
913
+ }));
914
+ //#endregion
915
+ //#region ../../node_modules/ws/lib/receiver.js
916
+ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
917
+ const { Writable } = __require("stream");
918
+ const PerMessageDeflate = require_permessage_deflate();
919
+ const { BINARY_TYPES, EMPTY_BUFFER, kStatusCode, kWebSocket } = require_constants();
920
+ const { concat, toArrayBuffer, unmask } = require_buffer_util();
921
+ const { isValidStatusCode, isValidUTF8 } = require_validation();
922
+ const FastBuffer = Buffer[Symbol.species];
923
+ const GET_INFO = 0;
924
+ const GET_PAYLOAD_LENGTH_16 = 1;
925
+ const GET_PAYLOAD_LENGTH_64 = 2;
926
+ const GET_MASK = 3;
927
+ const GET_DATA = 4;
928
+ const INFLATING = 5;
929
+ const DEFER_EVENT = 6;
930
+ /**
931
+ * HyBi Receiver implementation.
932
+ *
933
+ * @extends Writable
934
+ */
935
+ var Receiver = class extends Writable {
936
+ /**
937
+ * Creates a Receiver instance.
938
+ *
939
+ * @param {Object} [options] Options object
940
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
941
+ * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
942
+ * multiple times in the same tick
943
+ * @param {String} [options.binaryType=nodebuffer] The type for binary data
944
+ * @param {Object} [options.extensions] An object containing the negotiated
945
+ * extensions
946
+ * @param {Boolean} [options.isServer=false] Specifies whether to operate in
947
+ * client or server mode
948
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
949
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
950
+ * not to skip UTF-8 validation for text and close messages
951
+ */
952
+ constructor(options = {}) {
953
+ super();
954
+ this._allowSynchronousEvents = options.allowSynchronousEvents !== void 0 ? options.allowSynchronousEvents : true;
955
+ this._binaryType = options.binaryType || BINARY_TYPES[0];
956
+ this._extensions = options.extensions || {};
957
+ this._isServer = !!options.isServer;
958
+ this._maxPayload = options.maxPayload | 0;
959
+ this._skipUTF8Validation = !!options.skipUTF8Validation;
960
+ this[kWebSocket] = void 0;
961
+ this._bufferedBytes = 0;
962
+ this._buffers = [];
963
+ this._compressed = false;
964
+ this._payloadLength = 0;
965
+ this._mask = void 0;
966
+ this._fragmented = 0;
967
+ this._masked = false;
968
+ this._fin = false;
969
+ this._opcode = 0;
970
+ this._totalPayloadLength = 0;
971
+ this._messageLength = 0;
972
+ this._fragments = [];
973
+ this._errored = false;
974
+ this._loop = false;
975
+ this._state = GET_INFO;
976
+ }
977
+ /**
978
+ * Implements `Writable.prototype._write()`.
979
+ *
980
+ * @param {Buffer} chunk The chunk of data to write
981
+ * @param {String} encoding The character encoding of `chunk`
982
+ * @param {Function} cb Callback
983
+ * @private
984
+ */
985
+ _write(chunk, encoding, cb) {
986
+ if (this._opcode === 8 && this._state == GET_INFO) return cb();
987
+ this._bufferedBytes += chunk.length;
988
+ this._buffers.push(chunk);
989
+ this.startLoop(cb);
990
+ }
991
+ /**
992
+ * Consumes `n` bytes from the buffered data.
993
+ *
994
+ * @param {Number} n The number of bytes to consume
995
+ * @return {Buffer} The consumed bytes
996
+ * @private
997
+ */
998
+ consume(n) {
999
+ this._bufferedBytes -= n;
1000
+ if (n === this._buffers[0].length) return this._buffers.shift();
1001
+ if (n < this._buffers[0].length) {
1002
+ const buf = this._buffers[0];
1003
+ this._buffers[0] = new FastBuffer(buf.buffer, buf.byteOffset + n, buf.length - n);
1004
+ return new FastBuffer(buf.buffer, buf.byteOffset, n);
1005
+ }
1006
+ const dst = Buffer.allocUnsafe(n);
1007
+ do {
1008
+ const buf = this._buffers[0];
1009
+ const offset = dst.length - n;
1010
+ if (n >= buf.length) dst.set(this._buffers.shift(), offset);
1011
+ else {
1012
+ dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
1013
+ this._buffers[0] = new FastBuffer(buf.buffer, buf.byteOffset + n, buf.length - n);
1014
+ }
1015
+ n -= buf.length;
1016
+ } while (n > 0);
1017
+ return dst;
1018
+ }
1019
+ /**
1020
+ * Starts the parsing loop.
1021
+ *
1022
+ * @param {Function} cb Callback
1023
+ * @private
1024
+ */
1025
+ startLoop(cb) {
1026
+ this._loop = true;
1027
+ do
1028
+ switch (this._state) {
1029
+ case GET_INFO:
1030
+ this.getInfo(cb);
1031
+ break;
1032
+ case GET_PAYLOAD_LENGTH_16:
1033
+ this.getPayloadLength16(cb);
1034
+ break;
1035
+ case GET_PAYLOAD_LENGTH_64:
1036
+ this.getPayloadLength64(cb);
1037
+ break;
1038
+ case GET_MASK:
1039
+ this.getMask();
1040
+ break;
1041
+ case GET_DATA:
1042
+ this.getData(cb);
1043
+ break;
1044
+ case INFLATING:
1045
+ case DEFER_EVENT:
1046
+ this._loop = false;
1047
+ return;
1048
+ }
1049
+ while (this._loop);
1050
+ if (!this._errored) cb();
1051
+ }
1052
+ /**
1053
+ * Reads the first two bytes of a frame.
1054
+ *
1055
+ * @param {Function} cb Callback
1056
+ * @private
1057
+ */
1058
+ getInfo(cb) {
1059
+ if (this._bufferedBytes < 2) {
1060
+ this._loop = false;
1061
+ return;
1062
+ }
1063
+ const buf = this.consume(2);
1064
+ if ((buf[0] & 48) !== 0) {
1065
+ cb(this.createError(RangeError, "RSV2 and RSV3 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_2_3"));
1066
+ return;
1067
+ }
1068
+ const compressed = (buf[0] & 64) === 64;
1069
+ if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
1070
+ cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
1071
+ return;
1072
+ }
1073
+ this._fin = (buf[0] & 128) === 128;
1074
+ this._opcode = buf[0] & 15;
1075
+ this._payloadLength = buf[1] & 127;
1076
+ if (this._opcode === 0) {
1077
+ if (compressed) {
1078
+ cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
1079
+ return;
1080
+ }
1081
+ if (!this._fragmented) {
1082
+ cb(this.createError(RangeError, "invalid opcode 0", true, 1002, "WS_ERR_INVALID_OPCODE"));
1083
+ return;
1084
+ }
1085
+ this._opcode = this._fragmented;
1086
+ } else if (this._opcode === 1 || this._opcode === 2) {
1087
+ if (this._fragmented) {
1088
+ cb(this.createError(RangeError, `invalid opcode ${this._opcode}`, true, 1002, "WS_ERR_INVALID_OPCODE"));
1089
+ return;
1090
+ }
1091
+ this._compressed = compressed;
1092
+ } else if (this._opcode > 7 && this._opcode < 11) {
1093
+ if (!this._fin) {
1094
+ cb(this.createError(RangeError, "FIN must be set", true, 1002, "WS_ERR_EXPECTED_FIN"));
1095
+ return;
1096
+ }
1097
+ if (compressed) {
1098
+ cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
1099
+ return;
1100
+ }
1101
+ if (this._payloadLength > 125 || this._opcode === 8 && this._payloadLength === 1) {
1102
+ cb(this.createError(RangeError, `invalid payload length ${this._payloadLength}`, true, 1002, "WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH"));
1103
+ return;
1104
+ }
1105
+ } else {
1106
+ cb(this.createError(RangeError, `invalid opcode ${this._opcode}`, true, 1002, "WS_ERR_INVALID_OPCODE"));
1107
+ return;
1108
+ }
1109
+ if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
1110
+ this._masked = (buf[1] & 128) === 128;
1111
+ if (this._isServer) {
1112
+ if (!this._masked) {
1113
+ cb(this.createError(RangeError, "MASK must be set", true, 1002, "WS_ERR_EXPECTED_MASK"));
1114
+ return;
1115
+ }
1116
+ } else if (this._masked) {
1117
+ cb(this.createError(RangeError, "MASK must be clear", true, 1002, "WS_ERR_UNEXPECTED_MASK"));
1118
+ return;
1119
+ }
1120
+ if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
1121
+ else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
1122
+ else this.haveLength(cb);
1123
+ }
1124
+ /**
1125
+ * Gets extended payload length (7+16).
1126
+ *
1127
+ * @param {Function} cb Callback
1128
+ * @private
1129
+ */
1130
+ getPayloadLength16(cb) {
1131
+ if (this._bufferedBytes < 2) {
1132
+ this._loop = false;
1133
+ return;
1134
+ }
1135
+ this._payloadLength = this.consume(2).readUInt16BE(0);
1136
+ this.haveLength(cb);
1137
+ }
1138
+ /**
1139
+ * Gets extended payload length (7+64).
1140
+ *
1141
+ * @param {Function} cb Callback
1142
+ * @private
1143
+ */
1144
+ getPayloadLength64(cb) {
1145
+ if (this._bufferedBytes < 8) {
1146
+ this._loop = false;
1147
+ return;
1148
+ }
1149
+ const buf = this.consume(8);
1150
+ const num = buf.readUInt32BE(0);
1151
+ if (num > Math.pow(2, 21) - 1) {
1152
+ cb(this.createError(RangeError, "Unsupported WebSocket frame: payload length > 2^53 - 1", false, 1009, "WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH"));
1153
+ return;
1154
+ }
1155
+ this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
1156
+ this.haveLength(cb);
1157
+ }
1158
+ /**
1159
+ * Payload length has been read.
1160
+ *
1161
+ * @param {Function} cb Callback
1162
+ * @private
1163
+ */
1164
+ haveLength(cb) {
1165
+ if (this._payloadLength && this._opcode < 8) {
1166
+ this._totalPayloadLength += this._payloadLength;
1167
+ if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
1168
+ cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
1169
+ return;
1170
+ }
1171
+ }
1172
+ if (this._masked) this._state = GET_MASK;
1173
+ else this._state = GET_DATA;
1174
+ }
1175
+ /**
1176
+ * Reads mask bytes.
1177
+ *
1178
+ * @private
1179
+ */
1180
+ getMask() {
1181
+ if (this._bufferedBytes < 4) {
1182
+ this._loop = false;
1183
+ return;
1184
+ }
1185
+ this._mask = this.consume(4);
1186
+ this._state = GET_DATA;
1187
+ }
1188
+ /**
1189
+ * Reads data bytes.
1190
+ *
1191
+ * @param {Function} cb Callback
1192
+ * @private
1193
+ */
1194
+ getData(cb) {
1195
+ let data = EMPTY_BUFFER;
1196
+ if (this._payloadLength) {
1197
+ if (this._bufferedBytes < this._payloadLength) {
1198
+ this._loop = false;
1199
+ return;
1200
+ }
1201
+ data = this.consume(this._payloadLength);
1202
+ if (this._masked && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0) unmask(data, this._mask);
1203
+ }
1204
+ if (this._opcode > 7) {
1205
+ this.controlMessage(data, cb);
1206
+ return;
1207
+ }
1208
+ if (this._compressed) {
1209
+ this._state = INFLATING;
1210
+ this.decompress(data, cb);
1211
+ return;
1212
+ }
1213
+ if (data.length) {
1214
+ this._messageLength = this._totalPayloadLength;
1215
+ this._fragments.push(data);
1216
+ }
1217
+ this.dataMessage(cb);
1218
+ }
1219
+ /**
1220
+ * Decompresses data.
1221
+ *
1222
+ * @param {Buffer} data Compressed data
1223
+ * @param {Function} cb Callback
1224
+ * @private
1225
+ */
1226
+ decompress(data, cb) {
1227
+ this._extensions[PerMessageDeflate.extensionName].decompress(data, this._fin, (err, buf) => {
1228
+ if (err) return cb(err);
1229
+ if (buf.length) {
1230
+ this._messageLength += buf.length;
1231
+ if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
1232
+ cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
1233
+ return;
1234
+ }
1235
+ this._fragments.push(buf);
1236
+ }
1237
+ this.dataMessage(cb);
1238
+ if (this._state === GET_INFO) this.startLoop(cb);
1239
+ });
1240
+ }
1241
+ /**
1242
+ * Handles a data message.
1243
+ *
1244
+ * @param {Function} cb Callback
1245
+ * @private
1246
+ */
1247
+ dataMessage(cb) {
1248
+ if (!this._fin) {
1249
+ this._state = GET_INFO;
1250
+ return;
1251
+ }
1252
+ const messageLength = this._messageLength;
1253
+ const fragments = this._fragments;
1254
+ this._totalPayloadLength = 0;
1255
+ this._messageLength = 0;
1256
+ this._fragmented = 0;
1257
+ this._fragments = [];
1258
+ if (this._opcode === 2) {
1259
+ let data;
1260
+ if (this._binaryType === "nodebuffer") data = concat(fragments, messageLength);
1261
+ else if (this._binaryType === "arraybuffer") data = toArrayBuffer(concat(fragments, messageLength));
1262
+ else if (this._binaryType === "blob") data = new Blob(fragments);
1263
+ else data = fragments;
1264
+ if (this._allowSynchronousEvents) {
1265
+ this.emit("message", data, true);
1266
+ this._state = GET_INFO;
1267
+ } else {
1268
+ this._state = DEFER_EVENT;
1269
+ setImmediate(() => {
1270
+ this.emit("message", data, true);
1271
+ this._state = GET_INFO;
1272
+ this.startLoop(cb);
1273
+ });
1274
+ }
1275
+ } else {
1276
+ const buf = concat(fragments, messageLength);
1277
+ if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
1278
+ cb(this.createError(Error, "invalid UTF-8 sequence", true, 1007, "WS_ERR_INVALID_UTF8"));
1279
+ return;
1280
+ }
1281
+ if (this._state === INFLATING || this._allowSynchronousEvents) {
1282
+ this.emit("message", buf, false);
1283
+ this._state = GET_INFO;
1284
+ } else {
1285
+ this._state = DEFER_EVENT;
1286
+ setImmediate(() => {
1287
+ this.emit("message", buf, false);
1288
+ this._state = GET_INFO;
1289
+ this.startLoop(cb);
1290
+ });
1291
+ }
1292
+ }
1293
+ }
1294
+ /**
1295
+ * Handles a control message.
1296
+ *
1297
+ * @param {Buffer} data Data to handle
1298
+ * @return {(Error|RangeError|undefined)} A possible error
1299
+ * @private
1300
+ */
1301
+ controlMessage(data, cb) {
1302
+ if (this._opcode === 8) {
1303
+ if (data.length === 0) {
1304
+ this._loop = false;
1305
+ this.emit("conclude", 1005, EMPTY_BUFFER);
1306
+ this.end();
1307
+ } else {
1308
+ const code = data.readUInt16BE(0);
1309
+ if (!isValidStatusCode(code)) {
1310
+ cb(this.createError(RangeError, `invalid status code ${code}`, true, 1002, "WS_ERR_INVALID_CLOSE_CODE"));
1311
+ return;
1312
+ }
1313
+ const buf = new FastBuffer(data.buffer, data.byteOffset + 2, data.length - 2);
1314
+ if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
1315
+ cb(this.createError(Error, "invalid UTF-8 sequence", true, 1007, "WS_ERR_INVALID_UTF8"));
1316
+ return;
1317
+ }
1318
+ this._loop = false;
1319
+ this.emit("conclude", code, buf);
1320
+ this.end();
1321
+ }
1322
+ this._state = GET_INFO;
1323
+ return;
1324
+ }
1325
+ if (this._allowSynchronousEvents) {
1326
+ this.emit(this._opcode === 9 ? "ping" : "pong", data);
1327
+ this._state = GET_INFO;
1328
+ } else {
1329
+ this._state = DEFER_EVENT;
1330
+ setImmediate(() => {
1331
+ this.emit(this._opcode === 9 ? "ping" : "pong", data);
1332
+ this._state = GET_INFO;
1333
+ this.startLoop(cb);
1334
+ });
1335
+ }
1336
+ }
1337
+ /**
1338
+ * Builds an error object.
1339
+ *
1340
+ * @param {function(new:Error|RangeError)} ErrorCtor The error constructor
1341
+ * @param {String} message The error message
1342
+ * @param {Boolean} prefix Specifies whether or not to add a default prefix to
1343
+ * `message`
1344
+ * @param {Number} statusCode The status code
1345
+ * @param {String} errorCode The exposed error code
1346
+ * @return {(Error|RangeError)} The error
1347
+ * @private
1348
+ */
1349
+ createError(ErrorCtor, message, prefix, statusCode, errorCode) {
1350
+ this._loop = false;
1351
+ this._errored = true;
1352
+ const err = new ErrorCtor(prefix ? `Invalid WebSocket frame: ${message}` : message);
1353
+ Error.captureStackTrace(err, this.createError);
1354
+ err.code = errorCode;
1355
+ err[kStatusCode] = statusCode;
1356
+ return err;
1357
+ }
1358
+ };
1359
+ module.exports = Receiver;
1360
+ }));
1361
+ //#endregion
1362
+ //#region ../../node_modules/ws/lib/sender.js
1363
+ var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1364
+ const { Duplex: Duplex$3 } = __require("stream");
1365
+ const { randomFillSync } = __require("crypto");
1366
+ const { types: { isUint8Array } } = __require("util");
1367
+ const PerMessageDeflate = require_permessage_deflate();
1368
+ const { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
1369
+ const { isBlob, isValidStatusCode } = require_validation();
1370
+ const { mask: applyMask, toBuffer } = require_buffer_util();
1371
+ const kByteLength = Symbol("kByteLength");
1372
+ const maskBuffer = Buffer.alloc(4);
1373
+ const RANDOM_POOL_SIZE = 8 * 1024;
1374
+ let randomPool;
1375
+ let randomPoolPointer = RANDOM_POOL_SIZE;
1376
+ const DEFAULT = 0;
1377
+ const DEFLATING = 1;
1378
+ const GET_BLOB_DATA = 2;
1379
+ module.exports = class Sender {
1380
+ /**
1381
+ * Creates a Sender instance.
1382
+ *
1383
+ * @param {Duplex} socket The connection socket
1384
+ * @param {Object} [extensions] An object containing the negotiated extensions
1385
+ * @param {Function} [generateMask] The function used to generate the masking
1386
+ * key
1387
+ */
1388
+ constructor(socket, extensions, generateMask) {
1389
+ this._extensions = extensions || {};
1390
+ if (generateMask) {
1391
+ this._generateMask = generateMask;
1392
+ this._maskBuffer = Buffer.alloc(4);
1393
+ }
1394
+ this._socket = socket;
1395
+ this._firstFragment = true;
1396
+ this._compress = false;
1397
+ this._bufferedBytes = 0;
1398
+ this._queue = [];
1399
+ this._state = DEFAULT;
1400
+ this.onerror = NOOP;
1401
+ this[kWebSocket] = void 0;
1402
+ }
1403
+ /**
1404
+ * Frames a piece of data according to the HyBi WebSocket protocol.
1405
+ *
1406
+ * @param {(Buffer|String)} data The data to frame
1407
+ * @param {Object} options Options object
1408
+ * @param {Boolean} [options.fin=false] Specifies whether or not to set the
1409
+ * FIN bit
1410
+ * @param {Function} [options.generateMask] The function used to generate the
1411
+ * masking key
1412
+ * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1413
+ * `data`
1414
+ * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
1415
+ * key
1416
+ * @param {Number} options.opcode The opcode
1417
+ * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
1418
+ * modified
1419
+ * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
1420
+ * RSV1 bit
1421
+ * @return {(Buffer|String)[]} The framed data
1422
+ * @public
1423
+ */
1424
+ static frame(data, options) {
1425
+ let mask;
1426
+ let merge = false;
1427
+ let offset = 2;
1428
+ let skipMasking = false;
1429
+ if (options.mask) {
1430
+ mask = options.maskBuffer || maskBuffer;
1431
+ if (options.generateMask) options.generateMask(mask);
1432
+ else {
1433
+ if (randomPoolPointer === RANDOM_POOL_SIZE) {
1434
+ /* istanbul ignore else */
1435
+ if (randomPool === void 0) randomPool = Buffer.alloc(RANDOM_POOL_SIZE);
1436
+ randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);
1437
+ randomPoolPointer = 0;
1438
+ }
1439
+ mask[0] = randomPool[randomPoolPointer++];
1440
+ mask[1] = randomPool[randomPoolPointer++];
1441
+ mask[2] = randomPool[randomPoolPointer++];
1442
+ mask[3] = randomPool[randomPoolPointer++];
1443
+ }
1444
+ skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
1445
+ offset = 6;
1446
+ }
1447
+ let dataLength;
1448
+ if (typeof data === "string") if ((!options.mask || skipMasking) && options[kByteLength] !== void 0) dataLength = options[kByteLength];
1449
+ else {
1450
+ data = Buffer.from(data);
1451
+ dataLength = data.length;
1452
+ }
1453
+ else {
1454
+ dataLength = data.length;
1455
+ merge = options.mask && options.readOnly && !skipMasking;
1456
+ }
1457
+ let payloadLength = dataLength;
1458
+ if (dataLength >= 65536) {
1459
+ offset += 8;
1460
+ payloadLength = 127;
1461
+ } else if (dataLength > 125) {
1462
+ offset += 2;
1463
+ payloadLength = 126;
1464
+ }
1465
+ const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
1466
+ target[0] = options.fin ? options.opcode | 128 : options.opcode;
1467
+ if (options.rsv1) target[0] |= 64;
1468
+ target[1] = payloadLength;
1469
+ if (payloadLength === 126) target.writeUInt16BE(dataLength, 2);
1470
+ else if (payloadLength === 127) {
1471
+ target[2] = target[3] = 0;
1472
+ target.writeUIntBE(dataLength, 4, 6);
1473
+ }
1474
+ if (!options.mask) return [target, data];
1475
+ target[1] |= 128;
1476
+ target[offset - 4] = mask[0];
1477
+ target[offset - 3] = mask[1];
1478
+ target[offset - 2] = mask[2];
1479
+ target[offset - 1] = mask[3];
1480
+ if (skipMasking) return [target, data];
1481
+ if (merge) {
1482
+ applyMask(data, mask, target, offset, dataLength);
1483
+ return [target];
1484
+ }
1485
+ applyMask(data, mask, data, 0, dataLength);
1486
+ return [target, data];
1487
+ }
1488
+ /**
1489
+ * Sends a close message to the other peer.
1490
+ *
1491
+ * @param {Number} [code] The status code component of the body
1492
+ * @param {(String|Buffer)} [data] The message component of the body
1493
+ * @param {Boolean} [mask=false] Specifies whether or not to mask the message
1494
+ * @param {Function} [cb] Callback
1495
+ * @public
1496
+ */
1497
+ close(code, data, mask, cb) {
1498
+ let buf;
1499
+ if (code === void 0) buf = EMPTY_BUFFER;
1500
+ else if (typeof code !== "number" || !isValidStatusCode(code)) throw new TypeError("First argument must be a valid error code number");
1501
+ else if (data === void 0 || !data.length) {
1502
+ buf = Buffer.allocUnsafe(2);
1503
+ buf.writeUInt16BE(code, 0);
1504
+ } else {
1505
+ const length = Buffer.byteLength(data);
1506
+ if (length > 123) throw new RangeError("The message must not be greater than 123 bytes");
1507
+ buf = Buffer.allocUnsafe(2 + length);
1508
+ buf.writeUInt16BE(code, 0);
1509
+ if (typeof data === "string") buf.write(data, 2);
1510
+ else if (isUint8Array(data)) buf.set(data, 2);
1511
+ else throw new TypeError("Second argument must be a string or a Uint8Array");
1512
+ }
1513
+ const options = {
1514
+ [kByteLength]: buf.length,
1515
+ fin: true,
1516
+ generateMask: this._generateMask,
1517
+ mask,
1518
+ maskBuffer: this._maskBuffer,
1519
+ opcode: 8,
1520
+ readOnly: false,
1521
+ rsv1: false
1522
+ };
1523
+ if (this._state !== DEFAULT) this.enqueue([
1524
+ this.dispatch,
1525
+ buf,
1526
+ false,
1527
+ options,
1528
+ cb
1529
+ ]);
1530
+ else this.sendFrame(Sender.frame(buf, options), cb);
1531
+ }
1532
+ /**
1533
+ * Sends a ping message to the other peer.
1534
+ *
1535
+ * @param {*} data The message to send
1536
+ * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
1537
+ * @param {Function} [cb] Callback
1538
+ * @public
1539
+ */
1540
+ ping(data, mask, cb) {
1541
+ let byteLength;
1542
+ let readOnly;
1543
+ if (typeof data === "string") {
1544
+ byteLength = Buffer.byteLength(data);
1545
+ readOnly = false;
1546
+ } else if (isBlob(data)) {
1547
+ byteLength = data.size;
1548
+ readOnly = false;
1549
+ } else {
1550
+ data = toBuffer(data);
1551
+ byteLength = data.length;
1552
+ readOnly = toBuffer.readOnly;
1553
+ }
1554
+ if (byteLength > 125) throw new RangeError("The data size must not be greater than 125 bytes");
1555
+ const options = {
1556
+ [kByteLength]: byteLength,
1557
+ fin: true,
1558
+ generateMask: this._generateMask,
1559
+ mask,
1560
+ maskBuffer: this._maskBuffer,
1561
+ opcode: 9,
1562
+ readOnly,
1563
+ rsv1: false
1564
+ };
1565
+ if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1566
+ this.getBlobData,
1567
+ data,
1568
+ false,
1569
+ options,
1570
+ cb
1571
+ ]);
1572
+ else this.getBlobData(data, false, options, cb);
1573
+ else if (this._state !== DEFAULT) this.enqueue([
1574
+ this.dispatch,
1575
+ data,
1576
+ false,
1577
+ options,
1578
+ cb
1579
+ ]);
1580
+ else this.sendFrame(Sender.frame(data, options), cb);
1581
+ }
1582
+ /**
1583
+ * Sends a pong message to the other peer.
1584
+ *
1585
+ * @param {*} data The message to send
1586
+ * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
1587
+ * @param {Function} [cb] Callback
1588
+ * @public
1589
+ */
1590
+ pong(data, mask, cb) {
1591
+ let byteLength;
1592
+ let readOnly;
1593
+ if (typeof data === "string") {
1594
+ byteLength = Buffer.byteLength(data);
1595
+ readOnly = false;
1596
+ } else if (isBlob(data)) {
1597
+ byteLength = data.size;
1598
+ readOnly = false;
1599
+ } else {
1600
+ data = toBuffer(data);
1601
+ byteLength = data.length;
1602
+ readOnly = toBuffer.readOnly;
1603
+ }
1604
+ if (byteLength > 125) throw new RangeError("The data size must not be greater than 125 bytes");
1605
+ const options = {
1606
+ [kByteLength]: byteLength,
1607
+ fin: true,
1608
+ generateMask: this._generateMask,
1609
+ mask,
1610
+ maskBuffer: this._maskBuffer,
1611
+ opcode: 10,
1612
+ readOnly,
1613
+ rsv1: false
1614
+ };
1615
+ if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1616
+ this.getBlobData,
1617
+ data,
1618
+ false,
1619
+ options,
1620
+ cb
1621
+ ]);
1622
+ else this.getBlobData(data, false, options, cb);
1623
+ else if (this._state !== DEFAULT) this.enqueue([
1624
+ this.dispatch,
1625
+ data,
1626
+ false,
1627
+ options,
1628
+ cb
1629
+ ]);
1630
+ else this.sendFrame(Sender.frame(data, options), cb);
1631
+ }
1632
+ /**
1633
+ * Sends a data message to the other peer.
1634
+ *
1635
+ * @param {*} data The message to send
1636
+ * @param {Object} options Options object
1637
+ * @param {Boolean} [options.binary=false] Specifies whether `data` is binary
1638
+ * or text
1639
+ * @param {Boolean} [options.compress=false] Specifies whether or not to
1640
+ * compress `data`
1641
+ * @param {Boolean} [options.fin=false] Specifies whether the fragment is the
1642
+ * last one
1643
+ * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1644
+ * `data`
1645
+ * @param {Function} [cb] Callback
1646
+ * @public
1647
+ */
1648
+ send(data, options, cb) {
1649
+ const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1650
+ let opcode = options.binary ? 2 : 1;
1651
+ let rsv1 = options.compress;
1652
+ let byteLength;
1653
+ let readOnly;
1654
+ if (typeof data === "string") {
1655
+ byteLength = Buffer.byteLength(data);
1656
+ readOnly = false;
1657
+ } else if (isBlob(data)) {
1658
+ byteLength = data.size;
1659
+ readOnly = false;
1660
+ } else {
1661
+ data = toBuffer(data);
1662
+ byteLength = data.length;
1663
+ readOnly = toBuffer.readOnly;
1664
+ }
1665
+ if (this._firstFragment) {
1666
+ this._firstFragment = false;
1667
+ if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) rsv1 = byteLength >= perMessageDeflate._threshold;
1668
+ this._compress = rsv1;
1669
+ } else {
1670
+ rsv1 = false;
1671
+ opcode = 0;
1672
+ }
1673
+ if (options.fin) this._firstFragment = true;
1674
+ const opts = {
1675
+ [kByteLength]: byteLength,
1676
+ fin: options.fin,
1677
+ generateMask: this._generateMask,
1678
+ mask: options.mask,
1679
+ maskBuffer: this._maskBuffer,
1680
+ opcode,
1681
+ readOnly,
1682
+ rsv1
1683
+ };
1684
+ if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
1685
+ this.getBlobData,
1686
+ data,
1687
+ this._compress,
1688
+ opts,
1689
+ cb
1690
+ ]);
1691
+ else this.getBlobData(data, this._compress, opts, cb);
1692
+ else if (this._state !== DEFAULT) this.enqueue([
1693
+ this.dispatch,
1694
+ data,
1695
+ this._compress,
1696
+ opts,
1697
+ cb
1698
+ ]);
1699
+ else this.dispatch(data, this._compress, opts, cb);
1700
+ }
1701
+ /**
1702
+ * Gets the contents of a blob as binary data.
1703
+ *
1704
+ * @param {Blob} blob The blob
1705
+ * @param {Boolean} [compress=false] Specifies whether or not to compress
1706
+ * the data
1707
+ * @param {Object} options Options object
1708
+ * @param {Boolean} [options.fin=false] Specifies whether or not to set the
1709
+ * FIN bit
1710
+ * @param {Function} [options.generateMask] The function used to generate the
1711
+ * masking key
1712
+ * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1713
+ * `data`
1714
+ * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
1715
+ * key
1716
+ * @param {Number} options.opcode The opcode
1717
+ * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
1718
+ * modified
1719
+ * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
1720
+ * RSV1 bit
1721
+ * @param {Function} [cb] Callback
1722
+ * @private
1723
+ */
1724
+ getBlobData(blob, compress, options, cb) {
1725
+ this._bufferedBytes += options[kByteLength];
1726
+ this._state = GET_BLOB_DATA;
1727
+ blob.arrayBuffer().then((arrayBuffer) => {
1728
+ if (this._socket.destroyed) {
1729
+ const err = /* @__PURE__ */ new Error("The socket was closed while the blob was being read");
1730
+ process.nextTick(callCallbacks, this, err, cb);
1731
+ return;
1732
+ }
1733
+ this._bufferedBytes -= options[kByteLength];
1734
+ const data = toBuffer(arrayBuffer);
1735
+ if (!compress) {
1736
+ this._state = DEFAULT;
1737
+ this.sendFrame(Sender.frame(data, options), cb);
1738
+ this.dequeue();
1739
+ } else this.dispatch(data, compress, options, cb);
1740
+ }).catch((err) => {
1741
+ process.nextTick(onError, this, err, cb);
1742
+ });
1743
+ }
1744
+ /**
1745
+ * Dispatches a message.
1746
+ *
1747
+ * @param {(Buffer|String)} data The message to send
1748
+ * @param {Boolean} [compress=false] Specifies whether or not to compress
1749
+ * `data`
1750
+ * @param {Object} options Options object
1751
+ * @param {Boolean} [options.fin=false] Specifies whether or not to set the
1752
+ * FIN bit
1753
+ * @param {Function} [options.generateMask] The function used to generate the
1754
+ * masking key
1755
+ * @param {Boolean} [options.mask=false] Specifies whether or not to mask
1756
+ * `data`
1757
+ * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
1758
+ * key
1759
+ * @param {Number} options.opcode The opcode
1760
+ * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
1761
+ * modified
1762
+ * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
1763
+ * RSV1 bit
1764
+ * @param {Function} [cb] Callback
1765
+ * @private
1766
+ */
1767
+ dispatch(data, compress, options, cb) {
1768
+ if (!compress) {
1769
+ this.sendFrame(Sender.frame(data, options), cb);
1770
+ return;
1771
+ }
1772
+ const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1773
+ this._bufferedBytes += options[kByteLength];
1774
+ this._state = DEFLATING;
1775
+ perMessageDeflate.compress(data, options.fin, (_, buf) => {
1776
+ if (this._socket.destroyed) {
1777
+ const err = /* @__PURE__ */ new Error("The socket was closed while data was being compressed");
1778
+ callCallbacks(this, err, cb);
1779
+ return;
1780
+ }
1781
+ this._bufferedBytes -= options[kByteLength];
1782
+ this._state = DEFAULT;
1783
+ options.readOnly = false;
1784
+ this.sendFrame(Sender.frame(buf, options), cb);
1785
+ this.dequeue();
1786
+ });
1787
+ }
1788
+ /**
1789
+ * Executes queued send operations.
1790
+ *
1791
+ * @private
1792
+ */
1793
+ dequeue() {
1794
+ while (this._state === DEFAULT && this._queue.length) {
1795
+ const params = this._queue.shift();
1796
+ this._bufferedBytes -= params[3][kByteLength];
1797
+ Reflect.apply(params[0], this, params.slice(1));
1798
+ }
1799
+ }
1800
+ /**
1801
+ * Enqueues a send operation.
1802
+ *
1803
+ * @param {Array} params Send operation parameters.
1804
+ * @private
1805
+ */
1806
+ enqueue(params) {
1807
+ this._bufferedBytes += params[3][kByteLength];
1808
+ this._queue.push(params);
1809
+ }
1810
+ /**
1811
+ * Sends a frame.
1812
+ *
1813
+ * @param {(Buffer | String)[]} list The frame to send
1814
+ * @param {Function} [cb] Callback
1815
+ * @private
1816
+ */
1817
+ sendFrame(list, cb) {
1818
+ if (list.length === 2) {
1819
+ this._socket.cork();
1820
+ this._socket.write(list[0]);
1821
+ this._socket.write(list[1], cb);
1822
+ this._socket.uncork();
1823
+ } else this._socket.write(list[0], cb);
1824
+ }
1825
+ };
1826
+ /**
1827
+ * Calls queued callbacks with an error.
1828
+ *
1829
+ * @param {Sender} sender The `Sender` instance
1830
+ * @param {Error} err The error to call the callbacks with
1831
+ * @param {Function} [cb] The first callback
1832
+ * @private
1833
+ */
1834
+ function callCallbacks(sender, err, cb) {
1835
+ if (typeof cb === "function") cb(err);
1836
+ for (let i = 0; i < sender._queue.length; i++) {
1837
+ const params = sender._queue[i];
1838
+ const callback = params[params.length - 1];
1839
+ if (typeof callback === "function") callback(err);
1840
+ }
1841
+ }
1842
+ /**
1843
+ * Handles a `Sender` error.
1844
+ *
1845
+ * @param {Sender} sender The `Sender` instance
1846
+ * @param {Error} err The error
1847
+ * @param {Function} [cb] The first pending callback
1848
+ * @private
1849
+ */
1850
+ function onError(sender, err, cb) {
1851
+ callCallbacks(sender, err, cb);
1852
+ sender.onerror(err);
1853
+ }
1854
+ }));
1855
+ //#endregion
1856
+ //#region ../../node_modules/ws/lib/event-target.js
1857
+ var require_event_target = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1858
+ const { kForOnEventAttribute, kListener } = require_constants();
1859
+ const kCode = Symbol("kCode");
1860
+ const kData = Symbol("kData");
1861
+ const kError = Symbol("kError");
1862
+ const kMessage = Symbol("kMessage");
1863
+ const kReason = Symbol("kReason");
1864
+ const kTarget = Symbol("kTarget");
1865
+ const kType = Symbol("kType");
1866
+ const kWasClean = Symbol("kWasClean");
1867
+ /**
1868
+ * Class representing an event.
1869
+ */
1870
+ var Event = class {
1871
+ /**
1872
+ * Create a new `Event`.
1873
+ *
1874
+ * @param {String} type The name of the event
1875
+ * @throws {TypeError} If the `type` argument is not specified
1876
+ */
1877
+ constructor(type) {
1878
+ this[kTarget] = null;
1879
+ this[kType] = type;
1880
+ }
1881
+ /**
1882
+ * @type {*}
1883
+ */
1884
+ get target() {
1885
+ return this[kTarget];
1886
+ }
1887
+ /**
1888
+ * @type {String}
1889
+ */
1890
+ get type() {
1891
+ return this[kType];
1892
+ }
1893
+ };
1894
+ Object.defineProperty(Event.prototype, "target", { enumerable: true });
1895
+ Object.defineProperty(Event.prototype, "type", { enumerable: true });
1896
+ /**
1897
+ * Class representing a close event.
1898
+ *
1899
+ * @extends Event
1900
+ */
1901
+ var CloseEvent = class extends Event {
1902
+ /**
1903
+ * Create a new `CloseEvent`.
1904
+ *
1905
+ * @param {String} type The name of the event
1906
+ * @param {Object} [options] A dictionary object that allows for setting
1907
+ * attributes via object members of the same name
1908
+ * @param {Number} [options.code=0] The status code explaining why the
1909
+ * connection was closed
1910
+ * @param {String} [options.reason=''] A human-readable string explaining why
1911
+ * the connection was closed
1912
+ * @param {Boolean} [options.wasClean=false] Indicates whether or not the
1913
+ * connection was cleanly closed
1914
+ */
1915
+ constructor(type, options = {}) {
1916
+ super(type);
1917
+ this[kCode] = options.code === void 0 ? 0 : options.code;
1918
+ this[kReason] = options.reason === void 0 ? "" : options.reason;
1919
+ this[kWasClean] = options.wasClean === void 0 ? false : options.wasClean;
1920
+ }
1921
+ /**
1922
+ * @type {Number}
1923
+ */
1924
+ get code() {
1925
+ return this[kCode];
1926
+ }
1927
+ /**
1928
+ * @type {String}
1929
+ */
1930
+ get reason() {
1931
+ return this[kReason];
1932
+ }
1933
+ /**
1934
+ * @type {Boolean}
1935
+ */
1936
+ get wasClean() {
1937
+ return this[kWasClean];
1938
+ }
1939
+ };
1940
+ Object.defineProperty(CloseEvent.prototype, "code", { enumerable: true });
1941
+ Object.defineProperty(CloseEvent.prototype, "reason", { enumerable: true });
1942
+ Object.defineProperty(CloseEvent.prototype, "wasClean", { enumerable: true });
1943
+ /**
1944
+ * Class representing an error event.
1945
+ *
1946
+ * @extends Event
1947
+ */
1948
+ var ErrorEvent = class extends Event {
1949
+ /**
1950
+ * Create a new `ErrorEvent`.
1951
+ *
1952
+ * @param {String} type The name of the event
1953
+ * @param {Object} [options] A dictionary object that allows for setting
1954
+ * attributes via object members of the same name
1955
+ * @param {*} [options.error=null] The error that generated this event
1956
+ * @param {String} [options.message=''] The error message
1957
+ */
1958
+ constructor(type, options = {}) {
1959
+ super(type);
1960
+ this[kError] = options.error === void 0 ? null : options.error;
1961
+ this[kMessage] = options.message === void 0 ? "" : options.message;
1962
+ }
1963
+ /**
1964
+ * @type {*}
1965
+ */
1966
+ get error() {
1967
+ return this[kError];
1968
+ }
1969
+ /**
1970
+ * @type {String}
1971
+ */
1972
+ get message() {
1973
+ return this[kMessage];
1974
+ }
1975
+ };
1976
+ Object.defineProperty(ErrorEvent.prototype, "error", { enumerable: true });
1977
+ Object.defineProperty(ErrorEvent.prototype, "message", { enumerable: true });
1978
+ /**
1979
+ * Class representing a message event.
1980
+ *
1981
+ * @extends Event
1982
+ */
1983
+ var MessageEvent = class extends Event {
1984
+ /**
1985
+ * Create a new `MessageEvent`.
1986
+ *
1987
+ * @param {String} type The name of the event
1988
+ * @param {Object} [options] A dictionary object that allows for setting
1989
+ * attributes via object members of the same name
1990
+ * @param {*} [options.data=null] The message content
1991
+ */
1992
+ constructor(type, options = {}) {
1993
+ super(type);
1994
+ this[kData] = options.data === void 0 ? null : options.data;
1995
+ }
1996
+ /**
1997
+ * @type {*}
1998
+ */
1999
+ get data() {
2000
+ return this[kData];
2001
+ }
2002
+ };
2003
+ Object.defineProperty(MessageEvent.prototype, "data", { enumerable: true });
2004
+ module.exports = {
2005
+ CloseEvent,
2006
+ ErrorEvent,
2007
+ Event,
2008
+ EventTarget: {
2009
+ /**
2010
+ * Register an event listener.
2011
+ *
2012
+ * @param {String} type A string representing the event type to listen for
2013
+ * @param {(Function|Object)} handler The listener to add
2014
+ * @param {Object} [options] An options object specifies characteristics about
2015
+ * the event listener
2016
+ * @param {Boolean} [options.once=false] A `Boolean` indicating that the
2017
+ * listener should be invoked at most once after being added. If `true`,
2018
+ * the listener would be automatically removed when invoked.
2019
+ * @public
2020
+ */
2021
+ addEventListener(type, handler, options = {}) {
2022
+ for (const listener of this.listeners(type)) if (!options[kForOnEventAttribute] && listener[kListener] === handler && !listener[kForOnEventAttribute]) return;
2023
+ let wrapper;
2024
+ if (type === "message") wrapper = function onMessage(data, isBinary) {
2025
+ const event = new MessageEvent("message", { data: isBinary ? data : data.toString() });
2026
+ event[kTarget] = this;
2027
+ callListener(handler, this, event);
2028
+ };
2029
+ else if (type === "close") wrapper = function onClose(code, message) {
2030
+ const event = new CloseEvent("close", {
2031
+ code,
2032
+ reason: message.toString(),
2033
+ wasClean: this._closeFrameReceived && this._closeFrameSent
2034
+ });
2035
+ event[kTarget] = this;
2036
+ callListener(handler, this, event);
2037
+ };
2038
+ else if (type === "error") wrapper = function onError(error) {
2039
+ const event = new ErrorEvent("error", {
2040
+ error,
2041
+ message: error.message
2042
+ });
2043
+ event[kTarget] = this;
2044
+ callListener(handler, this, event);
2045
+ };
2046
+ else if (type === "open") wrapper = function onOpen() {
2047
+ const event = new Event("open");
2048
+ event[kTarget] = this;
2049
+ callListener(handler, this, event);
2050
+ };
2051
+ else return;
2052
+ wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
2053
+ wrapper[kListener] = handler;
2054
+ if (options.once) this.once(type, wrapper);
2055
+ else this.on(type, wrapper);
2056
+ },
2057
+ /**
2058
+ * Remove an event listener.
2059
+ *
2060
+ * @param {String} type A string representing the event type to remove
2061
+ * @param {(Function|Object)} handler The listener to remove
2062
+ * @public
2063
+ */
2064
+ removeEventListener(type, handler) {
2065
+ for (const listener of this.listeners(type)) if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
2066
+ this.removeListener(type, listener);
2067
+ break;
2068
+ }
2069
+ }
2070
+ },
2071
+ MessageEvent
2072
+ };
2073
+ /**
2074
+ * Call an event listener
2075
+ *
2076
+ * @param {(Function|Object)} listener The listener to call
2077
+ * @param {*} thisArg The value to use as `this`` when calling the listener
2078
+ * @param {Event} event The event to pass to the listener
2079
+ * @private
2080
+ */
2081
+ function callListener(listener, thisArg, event) {
2082
+ if (typeof listener === "object" && listener.handleEvent) listener.handleEvent.call(listener, event);
2083
+ else listener.call(thisArg, event);
2084
+ }
2085
+ }));
2086
+ //#endregion
2087
+ //#region ../../node_modules/ws/lib/extension.js
2088
+ var require_extension = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2089
+ const { tokenChars } = require_validation();
2090
+ /**
2091
+ * Adds an offer to the map of extension offers or a parameter to the map of
2092
+ * parameters.
2093
+ *
2094
+ * @param {Object} dest The map of extension offers or parameters
2095
+ * @param {String} name The extension or parameter name
2096
+ * @param {(Object|Boolean|String)} elem The extension parameters or the
2097
+ * parameter value
2098
+ * @private
2099
+ */
2100
+ function push(dest, name, elem) {
2101
+ if (dest[name] === void 0) dest[name] = [elem];
2102
+ else dest[name].push(elem);
2103
+ }
2104
+ /**
2105
+ * Parses the `Sec-WebSocket-Extensions` header into an object.
2106
+ *
2107
+ * @param {String} header The field value of the header
2108
+ * @return {Object} The parsed object
2109
+ * @public
2110
+ */
2111
+ function parse(header) {
2112
+ const offers = Object.create(null);
2113
+ let params = Object.create(null);
2114
+ let mustUnescape = false;
2115
+ let isEscaping = false;
2116
+ let inQuotes = false;
2117
+ let extensionName;
2118
+ let paramName;
2119
+ let start = -1;
2120
+ let code = -1;
2121
+ let end = -1;
2122
+ let i = 0;
2123
+ for (; i < header.length; i++) {
2124
+ code = header.charCodeAt(i);
2125
+ if (extensionName === void 0) if (end === -1 && tokenChars[code] === 1) {
2126
+ if (start === -1) start = i;
2127
+ } else if (i !== 0 && (code === 32 || code === 9)) {
2128
+ if (end === -1 && start !== -1) end = i;
2129
+ } else if (code === 59 || code === 44) {
2130
+ if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
2131
+ if (end === -1) end = i;
2132
+ const name = header.slice(start, end);
2133
+ if (code === 44) {
2134
+ push(offers, name, params);
2135
+ params = Object.create(null);
2136
+ } else extensionName = name;
2137
+ start = end = -1;
2138
+ } else throw new SyntaxError(`Unexpected character at index ${i}`);
2139
+ else if (paramName === void 0) if (end === -1 && tokenChars[code] === 1) {
2140
+ if (start === -1) start = i;
2141
+ } else if (code === 32 || code === 9) {
2142
+ if (end === -1 && start !== -1) end = i;
2143
+ } else if (code === 59 || code === 44) {
2144
+ if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
2145
+ if (end === -1) end = i;
2146
+ push(params, header.slice(start, end), true);
2147
+ if (code === 44) {
2148
+ push(offers, extensionName, params);
2149
+ params = Object.create(null);
2150
+ extensionName = void 0;
2151
+ }
2152
+ start = end = -1;
2153
+ } else if (code === 61 && start !== -1 && end === -1) {
2154
+ paramName = header.slice(start, i);
2155
+ start = end = -1;
2156
+ } else throw new SyntaxError(`Unexpected character at index ${i}`);
2157
+ else if (isEscaping) {
2158
+ if (tokenChars[code] !== 1) throw new SyntaxError(`Unexpected character at index ${i}`);
2159
+ if (start === -1) start = i;
2160
+ else if (!mustUnescape) mustUnescape = true;
2161
+ isEscaping = false;
2162
+ } else if (inQuotes) if (tokenChars[code] === 1) {
2163
+ if (start === -1) start = i;
2164
+ } else if (code === 34 && start !== -1) {
2165
+ inQuotes = false;
2166
+ end = i;
2167
+ } else if (code === 92) isEscaping = true;
2168
+ else throw new SyntaxError(`Unexpected character at index ${i}`);
2169
+ else if (code === 34 && header.charCodeAt(i - 1) === 61) inQuotes = true;
2170
+ else if (end === -1 && tokenChars[code] === 1) {
2171
+ if (start === -1) start = i;
2172
+ } else if (start !== -1 && (code === 32 || code === 9)) {
2173
+ if (end === -1) end = i;
2174
+ } else if (code === 59 || code === 44) {
2175
+ if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
2176
+ if (end === -1) end = i;
2177
+ let value = header.slice(start, end);
2178
+ if (mustUnescape) {
2179
+ value = value.replace(/\\/g, "");
2180
+ mustUnescape = false;
2181
+ }
2182
+ push(params, paramName, value);
2183
+ if (code === 44) {
2184
+ push(offers, extensionName, params);
2185
+ params = Object.create(null);
2186
+ extensionName = void 0;
2187
+ }
2188
+ paramName = void 0;
2189
+ start = end = -1;
2190
+ } else throw new SyntaxError(`Unexpected character at index ${i}`);
2191
+ }
2192
+ if (start === -1 || inQuotes || code === 32 || code === 9) throw new SyntaxError("Unexpected end of input");
2193
+ if (end === -1) end = i;
2194
+ const token = header.slice(start, end);
2195
+ if (extensionName === void 0) push(offers, token, params);
2196
+ else {
2197
+ if (paramName === void 0) push(params, token, true);
2198
+ else if (mustUnescape) push(params, paramName, token.replace(/\\/g, ""));
2199
+ else push(params, paramName, token);
2200
+ push(offers, extensionName, params);
2201
+ }
2202
+ return offers;
2203
+ }
2204
+ /**
2205
+ * Builds the `Sec-WebSocket-Extensions` header field value.
2206
+ *
2207
+ * @param {Object} extensions The map of extensions and parameters to format
2208
+ * @return {String} A string representing the given object
2209
+ * @public
2210
+ */
2211
+ function format(extensions) {
2212
+ return Object.keys(extensions).map((extension) => {
2213
+ let configurations = extensions[extension];
2214
+ if (!Array.isArray(configurations)) configurations = [configurations];
2215
+ return configurations.map((params) => {
2216
+ return [extension].concat(Object.keys(params).map((k) => {
2217
+ let values = params[k];
2218
+ if (!Array.isArray(values)) values = [values];
2219
+ return values.map((v) => v === true ? k : `${k}=${v}`).join("; ");
2220
+ })).join("; ");
2221
+ }).join(", ");
2222
+ }).join(", ");
2223
+ }
2224
+ module.exports = {
2225
+ format,
2226
+ parse
2227
+ };
2228
+ }));
2229
+ //#endregion
2230
+ //#region ../../node_modules/ws/lib/websocket.js
2231
+ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2232
+ const EventEmitter$1 = __require("events");
2233
+ const https = __require("https");
2234
+ const http$1 = __require("http");
2235
+ const net = __require("net");
2236
+ const tls = __require("tls");
2237
+ const { randomBytes, createHash: createHash$1 } = __require("crypto");
2238
+ const { Duplex: Duplex$2, Readable } = __require("stream");
2239
+ const { URL: URL$1 } = __require("url");
2240
+ const PerMessageDeflate = require_permessage_deflate();
2241
+ const Receiver = require_receiver();
2242
+ const Sender = require_sender();
2243
+ const { isBlob } = require_validation();
2244
+ const { BINARY_TYPES, CLOSE_TIMEOUT, EMPTY_BUFFER, GUID, kForOnEventAttribute, kListener, kStatusCode, kWebSocket, NOOP } = require_constants();
2245
+ const { EventTarget: { addEventListener, removeEventListener } } = require_event_target();
2246
+ const { format, parse } = require_extension();
2247
+ const { toBuffer } = require_buffer_util();
2248
+ const kAborted = Symbol("kAborted");
2249
+ const protocolVersions = [8, 13];
2250
+ const readyStates = [
2251
+ "CONNECTING",
2252
+ "OPEN",
2253
+ "CLOSING",
2254
+ "CLOSED"
2255
+ ];
2256
+ const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
2257
+ /**
2258
+ * Class representing a WebSocket.
2259
+ *
2260
+ * @extends EventEmitter
2261
+ */
2262
+ var WebSocket = class WebSocket extends EventEmitter$1 {
2263
+ /**
2264
+ * Create a new `WebSocket`.
2265
+ *
2266
+ * @param {(String|URL)} address The URL to which to connect
2267
+ * @param {(String|String[])} [protocols] The subprotocols
2268
+ * @param {Object} [options] Connection options
2269
+ */
2270
+ constructor(address, protocols, options) {
2271
+ super();
2272
+ this._binaryType = BINARY_TYPES[0];
2273
+ this._closeCode = 1006;
2274
+ this._closeFrameReceived = false;
2275
+ this._closeFrameSent = false;
2276
+ this._closeMessage = EMPTY_BUFFER;
2277
+ this._closeTimer = null;
2278
+ this._errorEmitted = false;
2279
+ this._extensions = {};
2280
+ this._paused = false;
2281
+ this._protocol = "";
2282
+ this._readyState = WebSocket.CONNECTING;
2283
+ this._receiver = null;
2284
+ this._sender = null;
2285
+ this._socket = null;
2286
+ if (address !== null) {
2287
+ this._bufferedAmount = 0;
2288
+ this._isServer = false;
2289
+ this._redirects = 0;
2290
+ if (protocols === void 0) protocols = [];
2291
+ else if (!Array.isArray(protocols)) if (typeof protocols === "object" && protocols !== null) {
2292
+ options = protocols;
2293
+ protocols = [];
2294
+ } else protocols = [protocols];
2295
+ initAsClient(this, address, protocols, options);
2296
+ } else {
2297
+ this._autoPong = options.autoPong;
2298
+ this._closeTimeout = options.closeTimeout;
2299
+ this._isServer = true;
2300
+ }
2301
+ }
2302
+ /**
2303
+ * For historical reasons, the custom "nodebuffer" type is used by the default
2304
+ * instead of "blob".
2305
+ *
2306
+ * @type {String}
2307
+ */
2308
+ get binaryType() {
2309
+ return this._binaryType;
2310
+ }
2311
+ set binaryType(type) {
2312
+ if (!BINARY_TYPES.includes(type)) return;
2313
+ this._binaryType = type;
2314
+ if (this._receiver) this._receiver._binaryType = type;
2315
+ }
2316
+ /**
2317
+ * @type {Number}
2318
+ */
2319
+ get bufferedAmount() {
2320
+ if (!this._socket) return this._bufferedAmount;
2321
+ return this._socket._writableState.length + this._sender._bufferedBytes;
2322
+ }
2323
+ /**
2324
+ * @type {String}
2325
+ */
2326
+ get extensions() {
2327
+ return Object.keys(this._extensions).join();
2328
+ }
2329
+ /**
2330
+ * @type {Boolean}
2331
+ */
2332
+ get isPaused() {
2333
+ return this._paused;
2334
+ }
2335
+ /**
2336
+ * @type {Function}
2337
+ */
2338
+ /* istanbul ignore next */
2339
+ get onclose() {
2340
+ return null;
2341
+ }
2342
+ /**
2343
+ * @type {Function}
2344
+ */
2345
+ /* istanbul ignore next */
2346
+ get onerror() {
2347
+ return null;
2348
+ }
2349
+ /**
2350
+ * @type {Function}
2351
+ */
2352
+ /* istanbul ignore next */
2353
+ get onopen() {
2354
+ return null;
2355
+ }
2356
+ /**
2357
+ * @type {Function}
2358
+ */
2359
+ /* istanbul ignore next */
2360
+ get onmessage() {
2361
+ return null;
2362
+ }
2363
+ /**
2364
+ * @type {String}
2365
+ */
2366
+ get protocol() {
2367
+ return this._protocol;
2368
+ }
2369
+ /**
2370
+ * @type {Number}
2371
+ */
2372
+ get readyState() {
2373
+ return this._readyState;
2374
+ }
2375
+ /**
2376
+ * @type {String}
2377
+ */
2378
+ get url() {
2379
+ return this._url;
2380
+ }
2381
+ /**
2382
+ * Set up the socket and the internal resources.
2383
+ *
2384
+ * @param {Duplex} socket The network socket between the server and client
2385
+ * @param {Buffer} head The first packet of the upgraded stream
2386
+ * @param {Object} options Options object
2387
+ * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether
2388
+ * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
2389
+ * multiple times in the same tick
2390
+ * @param {Function} [options.generateMask] The function used to generate the
2391
+ * masking key
2392
+ * @param {Number} [options.maxPayload=0] The maximum allowed message size
2393
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
2394
+ * not to skip UTF-8 validation for text and close messages
2395
+ * @private
2396
+ */
2397
+ setSocket(socket, head, options) {
2398
+ const receiver = new Receiver({
2399
+ allowSynchronousEvents: options.allowSynchronousEvents,
2400
+ binaryType: this.binaryType,
2401
+ extensions: this._extensions,
2402
+ isServer: this._isServer,
2403
+ maxPayload: options.maxPayload,
2404
+ skipUTF8Validation: options.skipUTF8Validation
2405
+ });
2406
+ const sender = new Sender(socket, this._extensions, options.generateMask);
2407
+ this._receiver = receiver;
2408
+ this._sender = sender;
2409
+ this._socket = socket;
2410
+ receiver[kWebSocket] = this;
2411
+ sender[kWebSocket] = this;
2412
+ socket[kWebSocket] = this;
2413
+ receiver.on("conclude", receiverOnConclude);
2414
+ receiver.on("drain", receiverOnDrain);
2415
+ receiver.on("error", receiverOnError);
2416
+ receiver.on("message", receiverOnMessage);
2417
+ receiver.on("ping", receiverOnPing);
2418
+ receiver.on("pong", receiverOnPong);
2419
+ sender.onerror = senderOnError;
2420
+ if (socket.setTimeout) socket.setTimeout(0);
2421
+ if (socket.setNoDelay) socket.setNoDelay();
2422
+ if (head.length > 0) socket.unshift(head);
2423
+ socket.on("close", socketOnClose);
2424
+ socket.on("data", socketOnData);
2425
+ socket.on("end", socketOnEnd);
2426
+ socket.on("error", socketOnError);
2427
+ this._readyState = WebSocket.OPEN;
2428
+ this.emit("open");
2429
+ }
2430
+ /**
2431
+ * Emit the `'close'` event.
2432
+ *
2433
+ * @private
2434
+ */
2435
+ emitClose() {
2436
+ if (!this._socket) {
2437
+ this._readyState = WebSocket.CLOSED;
2438
+ this.emit("close", this._closeCode, this._closeMessage);
2439
+ return;
2440
+ }
2441
+ if (this._extensions[PerMessageDeflate.extensionName]) this._extensions[PerMessageDeflate.extensionName].cleanup();
2442
+ this._receiver.removeAllListeners();
2443
+ this._readyState = WebSocket.CLOSED;
2444
+ this.emit("close", this._closeCode, this._closeMessage);
2445
+ }
2446
+ /**
2447
+ * Start a closing handshake.
2448
+ *
2449
+ * +----------+ +-----------+ +----------+
2450
+ * - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
2451
+ * | +----------+ +-----------+ +----------+ |
2452
+ * +----------+ +-----------+ |
2453
+ * CLOSING |ws.close()|<--|close frame|<--+-----+ CLOSING
2454
+ * +----------+ +-----------+ |
2455
+ * | | | +---+ |
2456
+ * +------------------------+-->|fin| - - - -
2457
+ * | +---+ | +---+
2458
+ * - - - - -|fin|<---------------------+
2459
+ * +---+
2460
+ *
2461
+ * @param {Number} [code] Status code explaining why the connection is closing
2462
+ * @param {(String|Buffer)} [data] The reason why the connection is
2463
+ * closing
2464
+ * @public
2465
+ */
2466
+ close(code, data) {
2467
+ if (this.readyState === WebSocket.CLOSED) return;
2468
+ if (this.readyState === WebSocket.CONNECTING) {
2469
+ abortHandshake(this, this._req, "WebSocket was closed before the connection was established");
2470
+ return;
2471
+ }
2472
+ if (this.readyState === WebSocket.CLOSING) {
2473
+ if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) this._socket.end();
2474
+ return;
2475
+ }
2476
+ this._readyState = WebSocket.CLOSING;
2477
+ this._sender.close(code, data, !this._isServer, (err) => {
2478
+ if (err) return;
2479
+ this._closeFrameSent = true;
2480
+ if (this._closeFrameReceived || this._receiver._writableState.errorEmitted) this._socket.end();
2481
+ });
2482
+ setCloseTimer(this);
2483
+ }
2484
+ /**
2485
+ * Pause the socket.
2486
+ *
2487
+ * @public
2488
+ */
2489
+ pause() {
2490
+ if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) return;
2491
+ this._paused = true;
2492
+ this._socket.pause();
2493
+ }
2494
+ /**
2495
+ * Send a ping.
2496
+ *
2497
+ * @param {*} [data] The data to send
2498
+ * @param {Boolean} [mask] Indicates whether or not to mask `data`
2499
+ * @param {Function} [cb] Callback which is executed when the ping is sent
2500
+ * @public
2501
+ */
2502
+ ping(data, mask, cb) {
2503
+ if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
2504
+ if (typeof data === "function") {
2505
+ cb = data;
2506
+ data = mask = void 0;
2507
+ } else if (typeof mask === "function") {
2508
+ cb = mask;
2509
+ mask = void 0;
2510
+ }
2511
+ if (typeof data === "number") data = data.toString();
2512
+ if (this.readyState !== WebSocket.OPEN) {
2513
+ sendAfterClose(this, data, cb);
2514
+ return;
2515
+ }
2516
+ if (mask === void 0) mask = !this._isServer;
2517
+ this._sender.ping(data || EMPTY_BUFFER, mask, cb);
2518
+ }
2519
+ /**
2520
+ * Send a pong.
2521
+ *
2522
+ * @param {*} [data] The data to send
2523
+ * @param {Boolean} [mask] Indicates whether or not to mask `data`
2524
+ * @param {Function} [cb] Callback which is executed when the pong is sent
2525
+ * @public
2526
+ */
2527
+ pong(data, mask, cb) {
2528
+ if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
2529
+ if (typeof data === "function") {
2530
+ cb = data;
2531
+ data = mask = void 0;
2532
+ } else if (typeof mask === "function") {
2533
+ cb = mask;
2534
+ mask = void 0;
2535
+ }
2536
+ if (typeof data === "number") data = data.toString();
2537
+ if (this.readyState !== WebSocket.OPEN) {
2538
+ sendAfterClose(this, data, cb);
2539
+ return;
2540
+ }
2541
+ if (mask === void 0) mask = !this._isServer;
2542
+ this._sender.pong(data || EMPTY_BUFFER, mask, cb);
2543
+ }
2544
+ /**
2545
+ * Resume the socket.
2546
+ *
2547
+ * @public
2548
+ */
2549
+ resume() {
2550
+ if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) return;
2551
+ this._paused = false;
2552
+ if (!this._receiver._writableState.needDrain) this._socket.resume();
2553
+ }
2554
+ /**
2555
+ * Send a data message.
2556
+ *
2557
+ * @param {*} data The message to send
2558
+ * @param {Object} [options] Options object
2559
+ * @param {Boolean} [options.binary] Specifies whether `data` is binary or
2560
+ * text
2561
+ * @param {Boolean} [options.compress] Specifies whether or not to compress
2562
+ * `data`
2563
+ * @param {Boolean} [options.fin=true] Specifies whether the fragment is the
2564
+ * last one
2565
+ * @param {Boolean} [options.mask] Specifies whether or not to mask `data`
2566
+ * @param {Function} [cb] Callback which is executed when data is written out
2567
+ * @public
2568
+ */
2569
+ send(data, options, cb) {
2570
+ if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
2571
+ if (typeof options === "function") {
2572
+ cb = options;
2573
+ options = {};
2574
+ }
2575
+ if (typeof data === "number") data = data.toString();
2576
+ if (this.readyState !== WebSocket.OPEN) {
2577
+ sendAfterClose(this, data, cb);
2578
+ return;
2579
+ }
2580
+ const opts = {
2581
+ binary: typeof data !== "string",
2582
+ mask: !this._isServer,
2583
+ compress: true,
2584
+ fin: true,
2585
+ ...options
2586
+ };
2587
+ if (!this._extensions[PerMessageDeflate.extensionName]) opts.compress = false;
2588
+ this._sender.send(data || EMPTY_BUFFER, opts, cb);
2589
+ }
2590
+ /**
2591
+ * Forcibly close the connection.
2592
+ *
2593
+ * @public
2594
+ */
2595
+ terminate() {
2596
+ if (this.readyState === WebSocket.CLOSED) return;
2597
+ if (this.readyState === WebSocket.CONNECTING) {
2598
+ abortHandshake(this, this._req, "WebSocket was closed before the connection was established");
2599
+ return;
2600
+ }
2601
+ if (this._socket) {
2602
+ this._readyState = WebSocket.CLOSING;
2603
+ this._socket.destroy();
2604
+ }
2605
+ }
2606
+ };
2607
+ /**
2608
+ * @constant {Number} CONNECTING
2609
+ * @memberof WebSocket
2610
+ */
2611
+ Object.defineProperty(WebSocket, "CONNECTING", {
2612
+ enumerable: true,
2613
+ value: readyStates.indexOf("CONNECTING")
2614
+ });
2615
+ /**
2616
+ * @constant {Number} CONNECTING
2617
+ * @memberof WebSocket.prototype
2618
+ */
2619
+ Object.defineProperty(WebSocket.prototype, "CONNECTING", {
2620
+ enumerable: true,
2621
+ value: readyStates.indexOf("CONNECTING")
2622
+ });
2623
+ /**
2624
+ * @constant {Number} OPEN
2625
+ * @memberof WebSocket
2626
+ */
2627
+ Object.defineProperty(WebSocket, "OPEN", {
2628
+ enumerable: true,
2629
+ value: readyStates.indexOf("OPEN")
2630
+ });
2631
+ /**
2632
+ * @constant {Number} OPEN
2633
+ * @memberof WebSocket.prototype
2634
+ */
2635
+ Object.defineProperty(WebSocket.prototype, "OPEN", {
2636
+ enumerable: true,
2637
+ value: readyStates.indexOf("OPEN")
2638
+ });
2639
+ /**
2640
+ * @constant {Number} CLOSING
2641
+ * @memberof WebSocket
2642
+ */
2643
+ Object.defineProperty(WebSocket, "CLOSING", {
2644
+ enumerable: true,
2645
+ value: readyStates.indexOf("CLOSING")
2646
+ });
2647
+ /**
2648
+ * @constant {Number} CLOSING
2649
+ * @memberof WebSocket.prototype
2650
+ */
2651
+ Object.defineProperty(WebSocket.prototype, "CLOSING", {
2652
+ enumerable: true,
2653
+ value: readyStates.indexOf("CLOSING")
2654
+ });
2655
+ /**
2656
+ * @constant {Number} CLOSED
2657
+ * @memberof WebSocket
2658
+ */
2659
+ Object.defineProperty(WebSocket, "CLOSED", {
2660
+ enumerable: true,
2661
+ value: readyStates.indexOf("CLOSED")
2662
+ });
2663
+ /**
2664
+ * @constant {Number} CLOSED
2665
+ * @memberof WebSocket.prototype
2666
+ */
2667
+ Object.defineProperty(WebSocket.prototype, "CLOSED", {
2668
+ enumerable: true,
2669
+ value: readyStates.indexOf("CLOSED")
2670
+ });
2671
+ [
2672
+ "binaryType",
2673
+ "bufferedAmount",
2674
+ "extensions",
2675
+ "isPaused",
2676
+ "protocol",
2677
+ "readyState",
2678
+ "url"
2679
+ ].forEach((property) => {
2680
+ Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
2681
+ });
2682
+ [
2683
+ "open",
2684
+ "error",
2685
+ "close",
2686
+ "message"
2687
+ ].forEach((method) => {
2688
+ Object.defineProperty(WebSocket.prototype, `on${method}`, {
2689
+ enumerable: true,
2690
+ get() {
2691
+ for (const listener of this.listeners(method)) if (listener[kForOnEventAttribute]) return listener[kListener];
2692
+ return null;
2693
+ },
2694
+ set(handler) {
2695
+ for (const listener of this.listeners(method)) if (listener[kForOnEventAttribute]) {
2696
+ this.removeListener(method, listener);
2697
+ break;
2698
+ }
2699
+ if (typeof handler !== "function") return;
2700
+ this.addEventListener(method, handler, { [kForOnEventAttribute]: true });
2701
+ }
2702
+ });
2703
+ });
2704
+ WebSocket.prototype.addEventListener = addEventListener;
2705
+ WebSocket.prototype.removeEventListener = removeEventListener;
2706
+ module.exports = WebSocket;
2707
+ /**
2708
+ * Initialize a WebSocket client.
2709
+ *
2710
+ * @param {WebSocket} websocket The client to initialize
2711
+ * @param {(String|URL)} address The URL to which to connect
2712
+ * @param {Array} protocols The subprotocols
2713
+ * @param {Object} [options] Connection options
2714
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any
2715
+ * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple
2716
+ * times in the same tick
2717
+ * @param {Boolean} [options.autoPong=true] Specifies whether or not to
2718
+ * automatically send a pong in response to a ping
2719
+ * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait
2720
+ * for the closing handshake to finish after `websocket.close()` is called
2721
+ * @param {Function} [options.finishRequest] A function which can be used to
2722
+ * customize the headers of each http request before it is sent
2723
+ * @param {Boolean} [options.followRedirects=false] Whether or not to follow
2724
+ * redirects
2725
+ * @param {Function} [options.generateMask] The function used to generate the
2726
+ * masking key
2727
+ * @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
2728
+ * handshake request
2729
+ * @param {Number} [options.maxPayload=104857600] The maximum allowed message
2730
+ * size
2731
+ * @param {Number} [options.maxRedirects=10] The maximum number of redirects
2732
+ * allowed
2733
+ * @param {String} [options.origin] Value of the `Origin` or
2734
+ * `Sec-WebSocket-Origin` header
2735
+ * @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
2736
+ * permessage-deflate
2737
+ * @param {Number} [options.protocolVersion=13] Value of the
2738
+ * `Sec-WebSocket-Version` header
2739
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
2740
+ * not to skip UTF-8 validation for text and close messages
2741
+ * @private
2742
+ */
2743
+ function initAsClient(websocket, address, protocols, options) {
2744
+ const opts = {
2745
+ allowSynchronousEvents: true,
2746
+ autoPong: true,
2747
+ closeTimeout: CLOSE_TIMEOUT,
2748
+ protocolVersion: protocolVersions[1],
2749
+ maxPayload: 100 * 1024 * 1024,
2750
+ skipUTF8Validation: false,
2751
+ perMessageDeflate: true,
2752
+ followRedirects: false,
2753
+ maxRedirects: 10,
2754
+ ...options,
2755
+ socketPath: void 0,
2756
+ hostname: void 0,
2757
+ protocol: void 0,
2758
+ timeout: void 0,
2759
+ method: "GET",
2760
+ host: void 0,
2761
+ path: void 0,
2762
+ port: void 0
2763
+ };
2764
+ websocket._autoPong = opts.autoPong;
2765
+ websocket._closeTimeout = opts.closeTimeout;
2766
+ if (!protocolVersions.includes(opts.protocolVersion)) throw new RangeError(`Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${protocolVersions.join(", ")})`);
2767
+ let parsedUrl;
2768
+ if (address instanceof URL$1) parsedUrl = address;
2769
+ else try {
2770
+ parsedUrl = new URL$1(address);
2771
+ } catch {
2772
+ throw new SyntaxError(`Invalid URL: ${address}`);
2773
+ }
2774
+ if (parsedUrl.protocol === "http:") parsedUrl.protocol = "ws:";
2775
+ else if (parsedUrl.protocol === "https:") parsedUrl.protocol = "wss:";
2776
+ websocket._url = parsedUrl.href;
2777
+ const isSecure = parsedUrl.protocol === "wss:";
2778
+ const isIpcUrl = parsedUrl.protocol === "ws+unix:";
2779
+ let invalidUrlMessage;
2780
+ if (parsedUrl.protocol !== "ws:" && !isSecure && !isIpcUrl) invalidUrlMessage = "The URL's protocol must be one of \"ws:\", \"wss:\", \"http:\", \"https:\", or \"ws+unix:\"";
2781
+ else if (isIpcUrl && !parsedUrl.pathname) invalidUrlMessage = "The URL's pathname is empty";
2782
+ else if (parsedUrl.hash) invalidUrlMessage = "The URL contains a fragment identifier";
2783
+ if (invalidUrlMessage) {
2784
+ const err = new SyntaxError(invalidUrlMessage);
2785
+ if (websocket._redirects === 0) throw err;
2786
+ else {
2787
+ emitErrorAndClose(websocket, err);
2788
+ return;
2789
+ }
2790
+ }
2791
+ const defaultPort = isSecure ? 443 : 80;
2792
+ const key = randomBytes(16).toString("base64");
2793
+ const request = isSecure ? https.request : http$1.request;
2794
+ const protocolSet = /* @__PURE__ */ new Set();
2795
+ let perMessageDeflate;
2796
+ opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
2797
+ opts.defaultPort = opts.defaultPort || defaultPort;
2798
+ opts.port = parsedUrl.port || defaultPort;
2799
+ opts.host = parsedUrl.hostname.startsWith("[") ? parsedUrl.hostname.slice(1, -1) : parsedUrl.hostname;
2800
+ opts.headers = {
2801
+ ...opts.headers,
2802
+ "Sec-WebSocket-Version": opts.protocolVersion,
2803
+ "Sec-WebSocket-Key": key,
2804
+ Connection: "Upgrade",
2805
+ Upgrade: "websocket"
2806
+ };
2807
+ opts.path = parsedUrl.pathname + parsedUrl.search;
2808
+ opts.timeout = opts.handshakeTimeout;
2809
+ if (opts.perMessageDeflate) {
2810
+ perMessageDeflate = new PerMessageDeflate({
2811
+ ...opts.perMessageDeflate,
2812
+ isServer: false,
2813
+ maxPayload: opts.maxPayload
2814
+ });
2815
+ opts.headers["Sec-WebSocket-Extensions"] = format({ [PerMessageDeflate.extensionName]: perMessageDeflate.offer() });
2816
+ }
2817
+ if (protocols.length) {
2818
+ for (const protocol of protocols) {
2819
+ if (typeof protocol !== "string" || !subprotocolRegex.test(protocol) || protocolSet.has(protocol)) throw new SyntaxError("An invalid or duplicated subprotocol was specified");
2820
+ protocolSet.add(protocol);
2821
+ }
2822
+ opts.headers["Sec-WebSocket-Protocol"] = protocols.join(",");
2823
+ }
2824
+ if (opts.origin) if (opts.protocolVersion < 13) opts.headers["Sec-WebSocket-Origin"] = opts.origin;
2825
+ else opts.headers.Origin = opts.origin;
2826
+ if (parsedUrl.username || parsedUrl.password) opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
2827
+ if (isIpcUrl) {
2828
+ const parts = opts.path.split(":");
2829
+ opts.socketPath = parts[0];
2830
+ opts.path = parts[1];
2831
+ }
2832
+ let req;
2833
+ if (opts.followRedirects) {
2834
+ if (websocket._redirects === 0) {
2835
+ websocket._originalIpc = isIpcUrl;
2836
+ websocket._originalSecure = isSecure;
2837
+ websocket._originalHostOrSocketPath = isIpcUrl ? opts.socketPath : parsedUrl.host;
2838
+ const headers = options && options.headers;
2839
+ options = {
2840
+ ...options,
2841
+ headers: {}
2842
+ };
2843
+ if (headers) for (const [key, value] of Object.entries(headers)) options.headers[key.toLowerCase()] = value;
2844
+ } else if (websocket.listenerCount("redirect") === 0) {
2845
+ const isSameHost = isIpcUrl ? websocket._originalIpc ? opts.socketPath === websocket._originalHostOrSocketPath : false : websocket._originalIpc ? false : parsedUrl.host === websocket._originalHostOrSocketPath;
2846
+ if (!isSameHost || websocket._originalSecure && !isSecure) {
2847
+ delete opts.headers.authorization;
2848
+ delete opts.headers.cookie;
2849
+ if (!isSameHost) delete opts.headers.host;
2850
+ opts.auth = void 0;
2851
+ }
2852
+ }
2853
+ if (opts.auth && !options.headers.authorization) options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
2854
+ req = websocket._req = request(opts);
2855
+ if (websocket._redirects) websocket.emit("redirect", websocket.url, req);
2856
+ } else req = websocket._req = request(opts);
2857
+ if (opts.timeout) req.on("timeout", () => {
2858
+ abortHandshake(websocket, req, "Opening handshake has timed out");
2859
+ });
2860
+ req.on("error", (err) => {
2861
+ if (req === null || req[kAborted]) return;
2862
+ req = websocket._req = null;
2863
+ emitErrorAndClose(websocket, err);
2864
+ });
2865
+ req.on("response", (res) => {
2866
+ const location = res.headers.location;
2867
+ const statusCode = res.statusCode;
2868
+ if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) {
2869
+ if (++websocket._redirects > opts.maxRedirects) {
2870
+ abortHandshake(websocket, req, "Maximum redirects exceeded");
2871
+ return;
2872
+ }
2873
+ req.abort();
2874
+ let addr;
2875
+ try {
2876
+ addr = new URL$1(location, address);
2877
+ } catch (e) {
2878
+ emitErrorAndClose(websocket, /* @__PURE__ */ new SyntaxError(`Invalid URL: ${location}`));
2879
+ return;
2880
+ }
2881
+ initAsClient(websocket, addr, protocols, options);
2882
+ } else if (!websocket.emit("unexpected-response", req, res)) abortHandshake(websocket, req, `Unexpected server response: ${res.statusCode}`);
2883
+ });
2884
+ req.on("upgrade", (res, socket, head) => {
2885
+ websocket.emit("upgrade", res);
2886
+ if (websocket.readyState !== WebSocket.CONNECTING) return;
2887
+ req = websocket._req = null;
2888
+ const upgrade = res.headers.upgrade;
2889
+ if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
2890
+ abortHandshake(websocket, socket, "Invalid Upgrade header");
2891
+ return;
2892
+ }
2893
+ const digest = createHash$1("sha1").update(key + GUID).digest("base64");
2894
+ if (res.headers["sec-websocket-accept"] !== digest) {
2895
+ abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2896
+ return;
2897
+ }
2898
+ const serverProt = res.headers["sec-websocket-protocol"];
2899
+ let protError;
2900
+ if (serverProt !== void 0) {
2901
+ if (!protocolSet.size) protError = "Server sent a subprotocol but none was requested";
2902
+ else if (!protocolSet.has(serverProt)) protError = "Server sent an invalid subprotocol";
2903
+ } else if (protocolSet.size) protError = "Server sent no subprotocol";
2904
+ if (protError) {
2905
+ abortHandshake(websocket, socket, protError);
2906
+ return;
2907
+ }
2908
+ if (serverProt) websocket._protocol = serverProt;
2909
+ const secWebSocketExtensions = res.headers["sec-websocket-extensions"];
2910
+ if (secWebSocketExtensions !== void 0) {
2911
+ if (!perMessageDeflate) {
2912
+ abortHandshake(websocket, socket, "Server sent a Sec-WebSocket-Extensions header but no extension was requested");
2913
+ return;
2914
+ }
2915
+ let extensions;
2916
+ try {
2917
+ extensions = parse(secWebSocketExtensions);
2918
+ } catch (err) {
2919
+ abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Extensions header");
2920
+ return;
2921
+ }
2922
+ const extensionNames = Object.keys(extensions);
2923
+ if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
2924
+ abortHandshake(websocket, socket, "Server indicated an extension that was not requested");
2925
+ return;
2926
+ }
2927
+ try {
2928
+ perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
2929
+ } catch (err) {
2930
+ abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Extensions header");
2931
+ return;
2932
+ }
2933
+ websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
2934
+ }
2935
+ websocket.setSocket(socket, head, {
2936
+ allowSynchronousEvents: opts.allowSynchronousEvents,
2937
+ generateMask: opts.generateMask,
2938
+ maxPayload: opts.maxPayload,
2939
+ skipUTF8Validation: opts.skipUTF8Validation
2940
+ });
2941
+ });
2942
+ if (opts.finishRequest) opts.finishRequest(req, websocket);
2943
+ else req.end();
2944
+ }
2945
+ /**
2946
+ * Emit the `'error'` and `'close'` events.
2947
+ *
2948
+ * @param {WebSocket} websocket The WebSocket instance
2949
+ * @param {Error} The error to emit
2950
+ * @private
2951
+ */
2952
+ function emitErrorAndClose(websocket, err) {
2953
+ websocket._readyState = WebSocket.CLOSING;
2954
+ websocket._errorEmitted = true;
2955
+ websocket.emit("error", err);
2956
+ websocket.emitClose();
2957
+ }
2958
+ /**
2959
+ * Create a `net.Socket` and initiate a connection.
2960
+ *
2961
+ * @param {Object} options Connection options
2962
+ * @return {net.Socket} The newly created socket used to start the connection
2963
+ * @private
2964
+ */
2965
+ function netConnect(options) {
2966
+ options.path = options.socketPath;
2967
+ return net.connect(options);
2968
+ }
2969
+ /**
2970
+ * Create a `tls.TLSSocket` and initiate a connection.
2971
+ *
2972
+ * @param {Object} options Connection options
2973
+ * @return {tls.TLSSocket} The newly created socket used to start the connection
2974
+ * @private
2975
+ */
2976
+ function tlsConnect(options) {
2977
+ options.path = void 0;
2978
+ if (!options.servername && options.servername !== "") options.servername = net.isIP(options.host) ? "" : options.host;
2979
+ return tls.connect(options);
2980
+ }
2981
+ /**
2982
+ * Abort the handshake and emit an error.
2983
+ *
2984
+ * @param {WebSocket} websocket The WebSocket instance
2985
+ * @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
2986
+ * abort or the socket to destroy
2987
+ * @param {String} message The error message
2988
+ * @private
2989
+ */
2990
+ function abortHandshake(websocket, stream, message) {
2991
+ websocket._readyState = WebSocket.CLOSING;
2992
+ const err = new Error(message);
2993
+ Error.captureStackTrace(err, abortHandshake);
2994
+ if (stream.setHeader) {
2995
+ stream[kAborted] = true;
2996
+ stream.abort();
2997
+ if (stream.socket && !stream.socket.destroyed) stream.socket.destroy();
2998
+ process.nextTick(emitErrorAndClose, websocket, err);
2999
+ } else {
3000
+ stream.destroy(err);
3001
+ stream.once("error", websocket.emit.bind(websocket, "error"));
3002
+ stream.once("close", websocket.emitClose.bind(websocket));
3003
+ }
3004
+ }
3005
+ /**
3006
+ * Handle cases where the `ping()`, `pong()`, or `send()` methods are called
3007
+ * when the `readyState` attribute is `CLOSING` or `CLOSED`.
3008
+ *
3009
+ * @param {WebSocket} websocket The WebSocket instance
3010
+ * @param {*} [data] The data to send
3011
+ * @param {Function} [cb] Callback
3012
+ * @private
3013
+ */
3014
+ function sendAfterClose(websocket, data, cb) {
3015
+ if (data) {
3016
+ const length = isBlob(data) ? data.size : toBuffer(data).length;
3017
+ if (websocket._socket) websocket._sender._bufferedBytes += length;
3018
+ else websocket._bufferedAmount += length;
3019
+ }
3020
+ if (cb) {
3021
+ const err = /* @__PURE__ */ new Error(`WebSocket is not open: readyState ${websocket.readyState} (${readyStates[websocket.readyState]})`);
3022
+ process.nextTick(cb, err);
3023
+ }
3024
+ }
3025
+ /**
3026
+ * The listener of the `Receiver` `'conclude'` event.
3027
+ *
3028
+ * @param {Number} code The status code
3029
+ * @param {Buffer} reason The reason for closing
3030
+ * @private
3031
+ */
3032
+ function receiverOnConclude(code, reason) {
3033
+ const websocket = this[kWebSocket];
3034
+ websocket._closeFrameReceived = true;
3035
+ websocket._closeMessage = reason;
3036
+ websocket._closeCode = code;
3037
+ if (websocket._socket[kWebSocket] === void 0) return;
3038
+ websocket._socket.removeListener("data", socketOnData);
3039
+ process.nextTick(resume, websocket._socket);
3040
+ if (code === 1005) websocket.close();
3041
+ else websocket.close(code, reason);
3042
+ }
3043
+ /**
3044
+ * The listener of the `Receiver` `'drain'` event.
3045
+ *
3046
+ * @private
3047
+ */
3048
+ function receiverOnDrain() {
3049
+ const websocket = this[kWebSocket];
3050
+ if (!websocket.isPaused) websocket._socket.resume();
3051
+ }
3052
+ /**
3053
+ * The listener of the `Receiver` `'error'` event.
3054
+ *
3055
+ * @param {(RangeError|Error)} err The emitted error
3056
+ * @private
3057
+ */
3058
+ function receiverOnError(err) {
3059
+ const websocket = this[kWebSocket];
3060
+ if (websocket._socket[kWebSocket] !== void 0) {
3061
+ websocket._socket.removeListener("data", socketOnData);
3062
+ process.nextTick(resume, websocket._socket);
3063
+ websocket.close(err[kStatusCode]);
3064
+ }
3065
+ if (!websocket._errorEmitted) {
3066
+ websocket._errorEmitted = true;
3067
+ websocket.emit("error", err);
3068
+ }
3069
+ }
3070
+ /**
3071
+ * The listener of the `Receiver` `'finish'` event.
3072
+ *
3073
+ * @private
3074
+ */
3075
+ function receiverOnFinish() {
3076
+ this[kWebSocket].emitClose();
3077
+ }
3078
+ /**
3079
+ * The listener of the `Receiver` `'message'` event.
3080
+ *
3081
+ * @param {Buffer|ArrayBuffer|Buffer[])} data The message
3082
+ * @param {Boolean} isBinary Specifies whether the message is binary or not
3083
+ * @private
3084
+ */
3085
+ function receiverOnMessage(data, isBinary) {
3086
+ this[kWebSocket].emit("message", data, isBinary);
3087
+ }
3088
+ /**
3089
+ * The listener of the `Receiver` `'ping'` event.
3090
+ *
3091
+ * @param {Buffer} data The data included in the ping frame
3092
+ * @private
3093
+ */
3094
+ function receiverOnPing(data) {
3095
+ const websocket = this[kWebSocket];
3096
+ if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP);
3097
+ websocket.emit("ping", data);
3098
+ }
3099
+ /**
3100
+ * The listener of the `Receiver` `'pong'` event.
3101
+ *
3102
+ * @param {Buffer} data The data included in the pong frame
3103
+ * @private
3104
+ */
3105
+ function receiverOnPong(data) {
3106
+ this[kWebSocket].emit("pong", data);
3107
+ }
3108
+ /**
3109
+ * Resume a readable stream
3110
+ *
3111
+ * @param {Readable} stream The readable stream
3112
+ * @private
3113
+ */
3114
+ function resume(stream) {
3115
+ stream.resume();
3116
+ }
3117
+ /**
3118
+ * The `Sender` error event handler.
3119
+ *
3120
+ * @param {Error} The error
3121
+ * @private
3122
+ */
3123
+ function senderOnError(err) {
3124
+ const websocket = this[kWebSocket];
3125
+ if (websocket.readyState === WebSocket.CLOSED) return;
3126
+ if (websocket.readyState === WebSocket.OPEN) {
3127
+ websocket._readyState = WebSocket.CLOSING;
3128
+ setCloseTimer(websocket);
3129
+ }
3130
+ this._socket.end();
3131
+ if (!websocket._errorEmitted) {
3132
+ websocket._errorEmitted = true;
3133
+ websocket.emit("error", err);
3134
+ }
3135
+ }
3136
+ /**
3137
+ * Set a timer to destroy the underlying raw socket of a WebSocket.
3138
+ *
3139
+ * @param {WebSocket} websocket The WebSocket instance
3140
+ * @private
3141
+ */
3142
+ function setCloseTimer(websocket) {
3143
+ websocket._closeTimer = setTimeout(websocket._socket.destroy.bind(websocket._socket), websocket._closeTimeout);
3144
+ }
3145
+ /**
3146
+ * The listener of the socket `'close'` event.
3147
+ *
3148
+ * @private
3149
+ */
3150
+ function socketOnClose() {
3151
+ const websocket = this[kWebSocket];
3152
+ this.removeListener("close", socketOnClose);
3153
+ this.removeListener("data", socketOnData);
3154
+ this.removeListener("end", socketOnEnd);
3155
+ websocket._readyState = WebSocket.CLOSING;
3156
+ if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
3157
+ const chunk = this.read(this._readableState.length);
3158
+ websocket._receiver.write(chunk);
3159
+ }
3160
+ websocket._receiver.end();
3161
+ this[kWebSocket] = void 0;
3162
+ clearTimeout(websocket._closeTimer);
3163
+ if (websocket._receiver._writableState.finished || websocket._receiver._writableState.errorEmitted) websocket.emitClose();
3164
+ else {
3165
+ websocket._receiver.on("error", receiverOnFinish);
3166
+ websocket._receiver.on("finish", receiverOnFinish);
3167
+ }
3168
+ }
3169
+ /**
3170
+ * The listener of the socket `'data'` event.
3171
+ *
3172
+ * @param {Buffer} chunk A chunk of data
3173
+ * @private
3174
+ */
3175
+ function socketOnData(chunk) {
3176
+ if (!this[kWebSocket]._receiver.write(chunk)) this.pause();
3177
+ }
3178
+ /**
3179
+ * The listener of the socket `'end'` event.
3180
+ *
3181
+ * @private
3182
+ */
3183
+ function socketOnEnd() {
3184
+ const websocket = this[kWebSocket];
3185
+ websocket._readyState = WebSocket.CLOSING;
3186
+ websocket._receiver.end();
3187
+ this.end();
3188
+ }
3189
+ /**
3190
+ * The listener of the socket `'error'` event.
3191
+ *
3192
+ * @private
3193
+ */
3194
+ function socketOnError() {
3195
+ const websocket = this[kWebSocket];
3196
+ this.removeListener("error", socketOnError);
3197
+ this.on("error", NOOP);
3198
+ if (websocket) {
3199
+ websocket._readyState = WebSocket.CLOSING;
3200
+ this.destroy();
3201
+ }
3202
+ }
3203
+ }));
3204
+ //#endregion
3205
+ //#region ../../node_modules/ws/lib/stream.js
3206
+ var require_stream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3207
+ require_websocket();
3208
+ const { Duplex: Duplex$1 } = __require("stream");
3209
+ /**
3210
+ * Emits the `'close'` event on a stream.
3211
+ *
3212
+ * @param {Duplex} stream The stream.
3213
+ * @private
3214
+ */
3215
+ function emitClose(stream) {
3216
+ stream.emit("close");
3217
+ }
3218
+ /**
3219
+ * The listener of the `'end'` event.
3220
+ *
3221
+ * @private
3222
+ */
3223
+ function duplexOnEnd() {
3224
+ if (!this.destroyed && this._writableState.finished) this.destroy();
3225
+ }
3226
+ /**
3227
+ * The listener of the `'error'` event.
3228
+ *
3229
+ * @param {Error} err The error
3230
+ * @private
3231
+ */
3232
+ function duplexOnError(err) {
3233
+ this.removeListener("error", duplexOnError);
3234
+ this.destroy();
3235
+ if (this.listenerCount("error") === 0) this.emit("error", err);
3236
+ }
3237
+ /**
3238
+ * Wraps a `WebSocket` in a duplex stream.
3239
+ *
3240
+ * @param {WebSocket} ws The `WebSocket` to wrap
3241
+ * @param {Object} [options] The options for the `Duplex` constructor
3242
+ * @return {Duplex} The duplex stream
3243
+ * @public
3244
+ */
3245
+ function createWebSocketStream(ws, options) {
3246
+ let terminateOnDestroy = true;
3247
+ const duplex = new Duplex$1({
3248
+ ...options,
3249
+ autoDestroy: false,
3250
+ emitClose: false,
3251
+ objectMode: false,
3252
+ writableObjectMode: false
3253
+ });
3254
+ ws.on("message", function message(msg, isBinary) {
3255
+ const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
3256
+ if (!duplex.push(data)) ws.pause();
3257
+ });
3258
+ ws.once("error", function error(err) {
3259
+ if (duplex.destroyed) return;
3260
+ terminateOnDestroy = false;
3261
+ duplex.destroy(err);
3262
+ });
3263
+ ws.once("close", function close() {
3264
+ if (duplex.destroyed) return;
3265
+ duplex.push(null);
3266
+ });
3267
+ duplex._destroy = function(err, callback) {
3268
+ if (ws.readyState === ws.CLOSED) {
3269
+ callback(err);
3270
+ process.nextTick(emitClose, duplex);
3271
+ return;
3272
+ }
3273
+ let called = false;
3274
+ ws.once("error", function error(err) {
3275
+ called = true;
3276
+ callback(err);
3277
+ });
3278
+ ws.once("close", function close() {
3279
+ if (!called) callback(err);
3280
+ process.nextTick(emitClose, duplex);
3281
+ });
3282
+ if (terminateOnDestroy) ws.terminate();
3283
+ };
3284
+ duplex._final = function(callback) {
3285
+ if (ws.readyState === ws.CONNECTING) {
3286
+ ws.once("open", function open() {
3287
+ duplex._final(callback);
3288
+ });
3289
+ return;
3290
+ }
3291
+ if (ws._socket === null) return;
3292
+ if (ws._socket._writableState.finished) {
3293
+ callback();
3294
+ if (duplex._readableState.endEmitted) duplex.destroy();
3295
+ } else {
3296
+ ws._socket.once("finish", function finish() {
3297
+ callback();
3298
+ });
3299
+ ws.close();
3300
+ }
3301
+ };
3302
+ duplex._read = function() {
3303
+ if (ws.isPaused) ws.resume();
3304
+ };
3305
+ duplex._write = function(chunk, encoding, callback) {
3306
+ if (ws.readyState === ws.CONNECTING) {
3307
+ ws.once("open", function open() {
3308
+ duplex._write(chunk, encoding, callback);
3309
+ });
3310
+ return;
3311
+ }
3312
+ ws.send(chunk, callback);
3313
+ };
3314
+ duplex.on("end", duplexOnEnd);
3315
+ duplex.on("error", duplexOnError);
3316
+ return duplex;
3317
+ }
3318
+ module.exports = createWebSocketStream;
3319
+ }));
3320
+ //#endregion
3321
+ //#region ../../node_modules/ws/lib/subprotocol.js
3322
+ var require_subprotocol = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3323
+ const { tokenChars } = require_validation();
3324
+ /**
3325
+ * Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
3326
+ *
3327
+ * @param {String} header The field value of the header
3328
+ * @return {Set} The subprotocol names
3329
+ * @public
3330
+ */
3331
+ function parse(header) {
3332
+ const protocols = /* @__PURE__ */ new Set();
3333
+ let start = -1;
3334
+ let end = -1;
3335
+ let i = 0;
3336
+ for (; i < header.length; i++) {
3337
+ const code = header.charCodeAt(i);
3338
+ if (end === -1 && tokenChars[code] === 1) {
3339
+ if (start === -1) start = i;
3340
+ } else if (i !== 0 && (code === 32 || code === 9)) {
3341
+ if (end === -1 && start !== -1) end = i;
3342
+ } else if (code === 44) {
3343
+ if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
3344
+ if (end === -1) end = i;
3345
+ const protocol = header.slice(start, end);
3346
+ if (protocols.has(protocol)) throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
3347
+ protocols.add(protocol);
3348
+ start = end = -1;
3349
+ } else throw new SyntaxError(`Unexpected character at index ${i}`);
3350
+ }
3351
+ if (start === -1 || end !== -1) throw new SyntaxError("Unexpected end of input");
3352
+ const protocol = header.slice(start, i);
3353
+ if (protocols.has(protocol)) throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
3354
+ protocols.add(protocol);
3355
+ return protocols;
3356
+ }
3357
+ module.exports = { parse };
3358
+ }));
3359
+ //#endregion
3360
+ //#region ../../node_modules/ws/lib/websocket-server.js
3361
+ var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3362
+ const EventEmitter = __require("events");
3363
+ const http = __require("http");
3364
+ const { Duplex } = __require("stream");
3365
+ const { createHash } = __require("crypto");
3366
+ const extension = require_extension();
3367
+ const PerMessageDeflate = require_permessage_deflate();
3368
+ const subprotocol = require_subprotocol();
3369
+ const WebSocket = require_websocket();
3370
+ const { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
3371
+ const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
3372
+ const RUNNING = 0;
3373
+ const CLOSING = 1;
3374
+ const CLOSED = 2;
3375
+ /**
3376
+ * Class representing a WebSocket server.
3377
+ *
3378
+ * @extends EventEmitter
3379
+ */
3380
+ var WebSocketServer = class extends EventEmitter {
3381
+ /**
3382
+ * Create a `WebSocketServer` instance.
3383
+ *
3384
+ * @param {Object} options Configuration options
3385
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
3386
+ * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
3387
+ * multiple times in the same tick
3388
+ * @param {Boolean} [options.autoPong=true] Specifies whether or not to
3389
+ * automatically send a pong in response to a ping
3390
+ * @param {Number} [options.backlog=511] The maximum length of the queue of
3391
+ * pending connections
3392
+ * @param {Boolean} [options.clientTracking=true] Specifies whether or not to
3393
+ * track clients
3394
+ * @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
3395
+ * wait for the closing handshake to finish after `websocket.close()` is
3396
+ * called
3397
+ * @param {Function} [options.handleProtocols] A hook to handle protocols
3398
+ * @param {String} [options.host] The hostname where to bind the server
3399
+ * @param {Number} [options.maxPayload=104857600] The maximum allowed message
3400
+ * size
3401
+ * @param {Boolean} [options.noServer=false] Enable no server mode
3402
+ * @param {String} [options.path] Accept only connections matching this path
3403
+ * @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
3404
+ * permessage-deflate
3405
+ * @param {Number} [options.port] The port where to bind the server
3406
+ * @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
3407
+ * server to use
3408
+ * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
3409
+ * not to skip UTF-8 validation for text and close messages
3410
+ * @param {Function} [options.verifyClient] A hook to reject connections
3411
+ * @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
3412
+ * class to use. It must be the `WebSocket` class or class that extends it
3413
+ * @param {Function} [callback] A listener for the `listening` event
3414
+ */
3415
+ constructor(options, callback) {
3416
+ super();
3417
+ options = {
3418
+ allowSynchronousEvents: true,
3419
+ autoPong: true,
3420
+ maxPayload: 100 * 1024 * 1024,
3421
+ skipUTF8Validation: false,
3422
+ perMessageDeflate: false,
3423
+ handleProtocols: null,
3424
+ clientTracking: true,
3425
+ closeTimeout: CLOSE_TIMEOUT,
3426
+ verifyClient: null,
3427
+ noServer: false,
3428
+ backlog: null,
3429
+ server: null,
3430
+ host: null,
3431
+ path: null,
3432
+ port: null,
3433
+ WebSocket,
3434
+ ...options
3435
+ };
3436
+ if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) throw new TypeError("One and only one of the \"port\", \"server\", or \"noServer\" options must be specified");
3437
+ if (options.port != null) {
3438
+ this._server = http.createServer((req, res) => {
3439
+ const body = http.STATUS_CODES[426];
3440
+ res.writeHead(426, {
3441
+ "Content-Length": body.length,
3442
+ "Content-Type": "text/plain"
3443
+ });
3444
+ res.end(body);
3445
+ });
3446
+ this._server.listen(options.port, options.host, options.backlog, callback);
3447
+ } else if (options.server) this._server = options.server;
3448
+ if (this._server) {
3449
+ const emitConnection = this.emit.bind(this, "connection");
3450
+ this._removeListeners = addListeners(this._server, {
3451
+ listening: this.emit.bind(this, "listening"),
3452
+ error: this.emit.bind(this, "error"),
3453
+ upgrade: (req, socket, head) => {
3454
+ this.handleUpgrade(req, socket, head, emitConnection);
3455
+ }
3456
+ });
3457
+ }
3458
+ if (options.perMessageDeflate === true) options.perMessageDeflate = {};
3459
+ if (options.clientTracking) {
3460
+ this.clients = /* @__PURE__ */ new Set();
3461
+ this._shouldEmitClose = false;
3462
+ }
3463
+ this.options = options;
3464
+ this._state = RUNNING;
3465
+ }
3466
+ /**
3467
+ * Returns the bound address, the address family name, and port of the server
3468
+ * as reported by the operating system if listening on an IP socket.
3469
+ * If the server is listening on a pipe or UNIX domain socket, the name is
3470
+ * returned as a string.
3471
+ *
3472
+ * @return {(Object|String|null)} The address of the server
3473
+ * @public
3474
+ */
3475
+ address() {
3476
+ if (this.options.noServer) throw new Error("The server is operating in \"noServer\" mode");
3477
+ if (!this._server) return null;
3478
+ return this._server.address();
3479
+ }
3480
+ /**
3481
+ * Stop the server from accepting new connections and emit the `'close'` event
3482
+ * when all existing connections are closed.
3483
+ *
3484
+ * @param {Function} [cb] A one-time listener for the `'close'` event
3485
+ * @public
3486
+ */
3487
+ close(cb) {
3488
+ if (this._state === CLOSED) {
3489
+ if (cb) this.once("close", () => {
3490
+ cb(/* @__PURE__ */ new Error("The server is not running"));
3491
+ });
3492
+ process.nextTick(emitClose, this);
3493
+ return;
3494
+ }
3495
+ if (cb) this.once("close", cb);
3496
+ if (this._state === CLOSING) return;
3497
+ this._state = CLOSING;
3498
+ if (this.options.noServer || this.options.server) {
3499
+ if (this._server) {
3500
+ this._removeListeners();
3501
+ this._removeListeners = this._server = null;
3502
+ }
3503
+ if (this.clients) if (!this.clients.size) process.nextTick(emitClose, this);
3504
+ else this._shouldEmitClose = true;
3505
+ else process.nextTick(emitClose, this);
3506
+ } else {
3507
+ const server = this._server;
3508
+ this._removeListeners();
3509
+ this._removeListeners = this._server = null;
3510
+ server.close(() => {
3511
+ emitClose(this);
3512
+ });
3513
+ }
3514
+ }
3515
+ /**
3516
+ * See if a given request should be handled by this server instance.
3517
+ *
3518
+ * @param {http.IncomingMessage} req Request object to inspect
3519
+ * @return {Boolean} `true` if the request is valid, else `false`
3520
+ * @public
3521
+ */
3522
+ shouldHandle(req) {
3523
+ if (this.options.path) {
3524
+ const index = req.url.indexOf("?");
3525
+ if ((index !== -1 ? req.url.slice(0, index) : req.url) !== this.options.path) return false;
3526
+ }
3527
+ return true;
3528
+ }
3529
+ /**
3530
+ * Handle a HTTP Upgrade request.
3531
+ *
3532
+ * @param {http.IncomingMessage} req The request object
3533
+ * @param {Duplex} socket The network socket between the server and client
3534
+ * @param {Buffer} head The first packet of the upgraded stream
3535
+ * @param {Function} cb Callback
3536
+ * @public
3537
+ */
3538
+ handleUpgrade(req, socket, head, cb) {
3539
+ socket.on("error", socketOnError);
3540
+ const key = req.headers["sec-websocket-key"];
3541
+ const upgrade = req.headers.upgrade;
3542
+ const version = +req.headers["sec-websocket-version"];
3543
+ if (req.method !== "GET") {
3544
+ abortHandshakeOrEmitwsClientError(this, req, socket, 405, "Invalid HTTP method");
3545
+ return;
3546
+ }
3547
+ if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
3548
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid Upgrade header");
3549
+ return;
3550
+ }
3551
+ if (key === void 0 || !keyRegex.test(key)) {
3552
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Missing or invalid Sec-WebSocket-Key header");
3553
+ return;
3554
+ }
3555
+ if (version !== 13 && version !== 8) {
3556
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Missing or invalid Sec-WebSocket-Version header", { "Sec-WebSocket-Version": "13, 8" });
3557
+ return;
3558
+ }
3559
+ if (!this.shouldHandle(req)) {
3560
+ abortHandshake(socket, 400);
3561
+ return;
3562
+ }
3563
+ const secWebSocketProtocol = req.headers["sec-websocket-protocol"];
3564
+ let protocols = /* @__PURE__ */ new Set();
3565
+ if (secWebSocketProtocol !== void 0) try {
3566
+ protocols = subprotocol.parse(secWebSocketProtocol);
3567
+ } catch (err) {
3568
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid Sec-WebSocket-Protocol header");
3569
+ return;
3570
+ }
3571
+ const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
3572
+ const extensions = {};
3573
+ if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
3574
+ const perMessageDeflate = new PerMessageDeflate({
3575
+ ...this.options.perMessageDeflate,
3576
+ isServer: true,
3577
+ maxPayload: this.options.maxPayload
3578
+ });
3579
+ try {
3580
+ const offers = extension.parse(secWebSocketExtensions);
3581
+ if (offers[PerMessageDeflate.extensionName]) {
3582
+ perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
3583
+ extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3584
+ }
3585
+ } catch (err) {
3586
+ abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid or unacceptable Sec-WebSocket-Extensions header");
3587
+ return;
3588
+ }
3589
+ }
3590
+ if (this.options.verifyClient) {
3591
+ const info = {
3592
+ origin: req.headers[`${version === 8 ? "sec-websocket-origin" : "origin"}`],
3593
+ secure: !!(req.socket.authorized || req.socket.encrypted),
3594
+ req
3595
+ };
3596
+ if (this.options.verifyClient.length === 2) {
3597
+ this.options.verifyClient(info, (verified, code, message, headers) => {
3598
+ if (!verified) return abortHandshake(socket, code || 401, message, headers);
3599
+ this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
3600
+ });
3601
+ return;
3602
+ }
3603
+ if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
3604
+ }
3605
+ this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
3606
+ }
3607
+ /**
3608
+ * Upgrade the connection to WebSocket.
3609
+ *
3610
+ * @param {Object} extensions The accepted extensions
3611
+ * @param {String} key The value of the `Sec-WebSocket-Key` header
3612
+ * @param {Set} protocols The subprotocols
3613
+ * @param {http.IncomingMessage} req The request object
3614
+ * @param {Duplex} socket The network socket between the server and client
3615
+ * @param {Buffer} head The first packet of the upgraded stream
3616
+ * @param {Function} cb Callback
3617
+ * @throws {Error} If called more than once with the same socket
3618
+ * @private
3619
+ */
3620
+ completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
3621
+ if (!socket.readable || !socket.writable) return socket.destroy();
3622
+ if (socket[kWebSocket]) throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");
3623
+ if (this._state > RUNNING) return abortHandshake(socket, 503);
3624
+ const headers = [
3625
+ "HTTP/1.1 101 Switching Protocols",
3626
+ "Upgrade: websocket",
3627
+ "Connection: Upgrade",
3628
+ `Sec-WebSocket-Accept: ${createHash("sha1").update(key + GUID).digest("base64")}`
3629
+ ];
3630
+ const ws = new this.options.WebSocket(null, void 0, this.options);
3631
+ if (protocols.size) {
3632
+ const protocol = this.options.handleProtocols ? this.options.handleProtocols(protocols, req) : protocols.values().next().value;
3633
+ if (protocol) {
3634
+ headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
3635
+ ws._protocol = protocol;
3636
+ }
3637
+ }
3638
+ if (extensions[PerMessageDeflate.extensionName]) {
3639
+ const params = extensions[PerMessageDeflate.extensionName].params;
3640
+ const value = extension.format({ [PerMessageDeflate.extensionName]: [params] });
3641
+ headers.push(`Sec-WebSocket-Extensions: ${value}`);
3642
+ ws._extensions = extensions;
3643
+ }
3644
+ this.emit("headers", headers, req);
3645
+ socket.write(headers.concat("\r\n").join("\r\n"));
3646
+ socket.removeListener("error", socketOnError);
3647
+ ws.setSocket(socket, head, {
3648
+ allowSynchronousEvents: this.options.allowSynchronousEvents,
3649
+ maxPayload: this.options.maxPayload,
3650
+ skipUTF8Validation: this.options.skipUTF8Validation
3651
+ });
3652
+ if (this.clients) {
3653
+ this.clients.add(ws);
3654
+ ws.on("close", () => {
3655
+ this.clients.delete(ws);
3656
+ if (this._shouldEmitClose && !this.clients.size) process.nextTick(emitClose, this);
3657
+ });
3658
+ }
3659
+ cb(ws, req);
3660
+ }
3661
+ };
3662
+ module.exports = WebSocketServer;
3663
+ /**
3664
+ * Add event listeners on an `EventEmitter` using a map of <event, listener>
3665
+ * pairs.
3666
+ *
3667
+ * @param {EventEmitter} server The event emitter
3668
+ * @param {Object.<String, Function>} map The listeners to add
3669
+ * @return {Function} A function that will remove the added listeners when
3670
+ * called
3671
+ * @private
3672
+ */
3673
+ function addListeners(server, map) {
3674
+ for (const event of Object.keys(map)) server.on(event, map[event]);
3675
+ return function removeListeners() {
3676
+ for (const event of Object.keys(map)) server.removeListener(event, map[event]);
3677
+ };
3678
+ }
3679
+ /**
3680
+ * Emit a `'close'` event on an `EventEmitter`.
3681
+ *
3682
+ * @param {EventEmitter} server The event emitter
3683
+ * @private
3684
+ */
3685
+ function emitClose(server) {
3686
+ server._state = CLOSED;
3687
+ server.emit("close");
3688
+ }
3689
+ /**
3690
+ * Handle socket errors.
3691
+ *
3692
+ * @private
3693
+ */
3694
+ function socketOnError() {
3695
+ this.destroy();
3696
+ }
3697
+ /**
3698
+ * Close the connection when preconditions are not fulfilled.
3699
+ *
3700
+ * @param {Duplex} socket The socket of the upgrade request
3701
+ * @param {Number} code The HTTP response status code
3702
+ * @param {String} [message] The HTTP response body
3703
+ * @param {Object} [headers] Additional HTTP response headers
3704
+ * @private
3705
+ */
3706
+ function abortHandshake(socket, code, message, headers) {
3707
+ message = message || http.STATUS_CODES[code];
3708
+ headers = {
3709
+ Connection: "close",
3710
+ "Content-Type": "text/html",
3711
+ "Content-Length": Buffer.byteLength(message),
3712
+ ...headers
3713
+ };
3714
+ socket.once("finish", socket.destroy);
3715
+ socket.end(`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message);
3716
+ }
3717
+ /**
3718
+ * Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
3719
+ * one listener for it, otherwise call `abortHandshake()`.
3720
+ *
3721
+ * @param {WebSocketServer} server The WebSocket server
3722
+ * @param {http.IncomingMessage} req The request object
3723
+ * @param {Duplex} socket The socket of the upgrade request
3724
+ * @param {Number} code The HTTP response status code
3725
+ * @param {String} message The HTTP response body
3726
+ * @param {Object} [headers] The HTTP response headers
3727
+ * @private
3728
+ */
3729
+ function abortHandshakeOrEmitwsClientError(server, req, socket, code, message, headers) {
3730
+ if (server.listenerCount("wsClientError")) {
3731
+ const err = new Error(message);
3732
+ Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
3733
+ server.emit("wsClientError", err, socket, req);
3734
+ } else abortHandshake(socket, code, message, headers);
3735
+ }
3736
+ }));
3737
+ //#endregion
3738
+ //#region ../../node_modules/ws/wrapper.mjs
3739
+ init_runtime();
3740
+ require_stream();
3741
+ require_extension();
3742
+ require_permessage_deflate();
3743
+ require_receiver();
3744
+ require_sender();
3745
+ require_subprotocol();
3746
+ var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
3747
+ require_websocket_server();
3748
+ var wrapper_default = import_websocket.default;
3749
+ //#endregion
3750
+ //#region src/client/ws-client.ts
3751
+ const DEFAULT_INITIAL_DELAY_MS = 1e3;
3752
+ const DEFAULT_MAX_DELAY_MS = 3e4;
3753
+ const DEFAULT_BACKOFF_MULTIPLIER = 2;
3754
+ function createMychatWsClient(params) {
3755
+ const { wsUrl, token, botSelfId, reconnect } = params;
3756
+ const initialDelay = reconnect?.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
3757
+ const maxDelay = reconnect?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
3758
+ const backoffMultiplier = reconnect?.backoffMultiplier ?? DEFAULT_BACKOFF_MULTIPLIER;
3759
+ let ws = null;
3760
+ let connected = false;
3761
+ let reconnectTimer = null;
3762
+ let currentDelay = initialDelay;
3763
+ const handlers = /* @__PURE__ */ new Set();
3764
+ function buildUrl() {
3765
+ const url = new URL(wsUrl);
3766
+ if (url.pathname.endsWith("/ws/bot")) {} else if (url.pathname.endsWith("/ws/")) url.pathname += "bot";
3767
+ else if (url.pathname.endsWith("/ws")) url.pathname += "/bot";
3768
+ else url.pathname = url.pathname.replace(/\/+$/, "") + "/ws/bot";
3769
+ return url.toString();
3770
+ }
3771
+ function scheduleReconnect() {
3772
+ if (reconnectTimer) return;
3773
+ console.log(`[mychat] WS will reconnect in ${currentDelay}ms`);
3774
+ reconnectTimer = setTimeout(() => {
3775
+ reconnectTimer = null;
3776
+ currentDelay = Math.min(currentDelay * backoffMultiplier, maxDelay);
3777
+ connect();
3778
+ }, currentDelay);
3779
+ }
3780
+ function connect() {
3781
+ if (ws) return;
3782
+ try {
3783
+ const url = buildUrl();
3784
+ console.log(`[mychat] WS connecting to ${url}`);
3785
+ ws = new wrapper_default(url, { headers: { Authorization: `Bearer ${token}` } });
3786
+ ws.on("open", () => {
3787
+ connected = true;
3788
+ currentDelay = initialDelay;
3789
+ console.log(`[mychat] WS connected botSelfId=${botSelfId ?? "none"}`);
3790
+ if (botSelfId) ws?.send(JSON.stringify({
3791
+ type: "subscribe",
3792
+ body: { subscribe: { conversationId: botSelfId } }
3793
+ }));
3794
+ });
3795
+ ws.on("message", (data) => {
3796
+ try {
3797
+ const message = JSON.parse(String(data));
3798
+ for (const handler of handlers) handler(message);
3799
+ } catch {}
3800
+ });
3801
+ ws.on("close", (code, reason) => {
3802
+ connected = false;
3803
+ ws = null;
3804
+ console.log(`[mychat] WS closed code=${code} reason=${reason?.toString() || "none"}`);
3805
+ scheduleReconnect();
3806
+ });
3807
+ ws.on("error", (err) => {
3808
+ const errMsg = err instanceof Error ? err.message : String(err);
3809
+ console.error(`[mychat] WS error: ${errMsg}`);
3810
+ });
3811
+ } catch (err) {
3812
+ const errMsg = err instanceof Error ? err.message : String(err);
3813
+ console.error(`[mychat] WS connect exception: ${errMsg}`);
3814
+ scheduleReconnect();
3815
+ }
3816
+ }
3817
+ function disconnect() {
3818
+ if (reconnectTimer) {
3819
+ clearTimeout(reconnectTimer);
3820
+ reconnectTimer = null;
3821
+ }
3822
+ connected = false;
3823
+ ws?.close();
3824
+ ws = null;
3825
+ }
3826
+ return {
3827
+ connect,
3828
+ disconnect,
3829
+ onMessage(handler) {
3830
+ handlers.add(handler);
3831
+ return () => {
3832
+ handlers.delete(handler);
3833
+ };
3834
+ },
3835
+ isConnected() {
3836
+ return connected;
3837
+ }
3838
+ };
3839
+ }
3840
+ //#endregion
3841
+ //#region src/logger.ts
3842
+ const PREFIX = "mychat";
3843
+ function mychatLogPrefix(feature) {
3844
+ return `[${PREFIX}:${feature}]`;
3845
+ }
3846
+ function getMychatLogger() {
3847
+ try {
3848
+ const logger = ((init_runtime(), __toCommonJS(runtime_exports)).tryGetMychatRuntime?.())?.logger;
3849
+ if (!logger) return null;
3850
+ return {
3851
+ debug: (m) => logger.debug?.(m),
3852
+ info: (m) => logger.info(m),
3853
+ warn: (m) => logger.warn(m),
3854
+ error: (m) => logger.error(m)
3855
+ };
3856
+ } catch {
3857
+ return null;
3858
+ }
3859
+ }
3860
+ //#endregion
3861
+ //#region src/monitor/listeners.ts
3862
+ function createMychatMessageListener(params) {
3863
+ const { handler } = params;
3864
+ return { handle(message) {
3865
+ handler.handleMessage(message).catch(() => {});
3866
+ } };
3867
+ }
3868
+ function createMychatReactionListener(params) {
3869
+ const logger = getMychatLogger();
3870
+ const prefix = mychatLogPrefix("reaction");
3871
+ return { handle(message) {
3872
+ const emoji = message.body?.reaction;
3873
+ const action = message.body?.action ?? "add";
3874
+ const target = message.body?.target;
3875
+ const senderId = message.from?.id;
3876
+ if (logger) logger.debug(`${prefix} ${action} ${emoji} on ${target} by ${senderId}`);
3877
+ } };
3878
+ }
3879
+ function createMychatTypingListener(params) {
3880
+ const logger = getMychatLogger();
3881
+ const prefix = mychatLogPrefix("typing");
3882
+ return { handle(message) {
3883
+ const senderId = message.from?.id;
3884
+ if (senderId === params.account.accountId) return;
3885
+ if (logger) logger.debug(`${prefix} from ${senderId}`);
3886
+ } };
3887
+ }
3888
+ //#endregion
3889
+ //#region src/monitor/provider.startup.ts
3890
+ function createMychatProviderClients(account, botSelfId) {
3891
+ return { wsClient: createMychatWsClient({
3892
+ wsUrl: account.wsUrl,
3893
+ token: account.token,
3894
+ botSelfId,
3895
+ reconnect: account.reconnect
3896
+ }) };
3897
+ }
3898
+ function registerMychatMonitorListeners(params) {
3899
+ const { account, handler, wsClient } = params;
3900
+ const messageListener = createMychatMessageListener({
3901
+ account,
3902
+ handler
3903
+ });
3904
+ const reactionListener = createMychatReactionListener({ account });
3905
+ const typingListener = createMychatTypingListener({ account });
3906
+ wsClient.onMessage((message) => {
3907
+ switch (message.type) {
3908
+ case "message":
3909
+ case "stream":
3910
+ messageListener.handle(message);
3911
+ break;
3912
+ case "reaction":
3913
+ reactionListener.handle(message);
3914
+ break;
3915
+ case "typing":
3916
+ typingListener.handle(message);
3917
+ break;
3918
+ default: break;
3919
+ }
3920
+ });
3921
+ }
3922
+ //#endregion
3923
+ //#region src/monitor/provider.lifecycle.ts
3924
+ const DEFAULT_READY_TIMEOUT_MS = 15e3;
3925
+ function createMychatProviderLifecycle(params) {
3926
+ const { httpClient, wsClient, readyTimeoutMs = DEFAULT_READY_TIMEOUT_MS } = params;
3927
+ const logger = getMychatLogger();
3928
+ const prefix = mychatLogPrefix("lifecycle");
3929
+ return {
3930
+ async waitForReady() {
3931
+ const start = Date.now();
3932
+ console.log(`[mychat:lifecycle] starting timeoutMs=${readyTimeoutMs}`);
3933
+ logger?.info(`${prefix} starting connection lifecycle timeoutMs=${readyTimeoutMs}`);
3934
+ wsClient.connect();
3935
+ let wsConnectedAt = null;
3936
+ let getSelfAttempts = 0;
3937
+ while (Date.now() - start < readyTimeoutMs) {
3938
+ if (wsClient.isConnected()) {
3939
+ if (!wsConnectedAt) {
3940
+ wsConnectedAt = Date.now();
3941
+ console.log(`[mychat:lifecycle] ws connected, fetching bot identity elapsedMs=${wsConnectedAt - start}`);
3942
+ logger?.info(`${prefix} ws connected, fetching bot identity elapsedMs=${wsConnectedAt - start}`);
3943
+ }
3944
+ getSelfAttempts++;
3945
+ const botSelf = await httpClient.getSelf();
3946
+ if (botSelf) {
3947
+ console.log(`[mychat:lifecycle] bot connected botId=${botSelf.botId} wsConnectMs=${wsConnectedAt - start} getSelfAttempts=${getSelfAttempts}`);
3948
+ logger?.info(`${prefix} bot connected botId=${botSelf.botId} wsConnectMs=${wsConnectedAt - start} getSelfAttempts=${getSelfAttempts}`);
3949
+ return botSelf;
3950
+ }
3951
+ console.warn(`[mychat:lifecycle] getSelf() failed attempt=${getSelfAttempts} elapsedMs=${Date.now() - start}`);
3952
+ logger?.warn(`${prefix} getSelf() failed attempt=${getSelfAttempts} elapsedMs=${Date.now() - start}`);
3953
+ }
3954
+ await new Promise((r) => setTimeout(r, 500));
3955
+ }
3956
+ const wsConnected = wsClient.isConnected();
3957
+ const elapsed = Date.now() - start;
3958
+ console.warn(`[mychat:lifecycle] ready timeout wsConnected=${wsConnected} wsConnectedAt=${wsConnectedAt ? wsConnectedAt - start : "never"} getSelfAttempts=${getSelfAttempts} elapsedMs=${elapsed} timeoutMs=${readyTimeoutMs}`);
3959
+ logger?.warn(`${prefix} ready timeout wsConnected=${wsConnected} wsConnectedAt=${wsConnectedAt ? wsConnectedAt - start : "never"} getSelfAttempts=${getSelfAttempts} elapsedMs=${elapsed} timeoutMs=${readyTimeoutMs}`);
3960
+ return null;
3961
+ },
3962
+ shutdown() {
3963
+ console.log(`[mychat:lifecycle] shutdown`);
3964
+ wsClient.disconnect();
3965
+ logger?.info(`${prefix} shutdown`);
3966
+ }
3967
+ };
3968
+ }
3969
+ //#endregion
3970
+ //#region src/monitor/inbound-dedupe.ts
3971
+ const MAX_CACHE_SIZE = 500;
3972
+ function createMychatInboundDedupe() {
3973
+ const seen = /* @__PURE__ */ new Set();
3974
+ return { isDuplicate(messageId) {
3975
+ if (seen.has(messageId)) return true;
3976
+ seen.add(messageId);
3977
+ if (seen.size > MAX_CACHE_SIZE) {
3978
+ const first = seen.values().next().value;
3979
+ if (first !== void 0) seen.delete(first);
3980
+ }
3981
+ return false;
3982
+ } };
3983
+ }
3984
+ //#endregion
3985
+ //#region src/monitor/message-text.ts
3986
+ /** Extract plain text from a WebSocket message body. */
3987
+ function extractMychatMessageText(message) {
3988
+ return message.body?.text ?? "";
3989
+ }
3990
+ /** Extract mention user IDs from a message. */
3991
+ function extractMentionUserIds(message) {
3992
+ return (message.body?.mentions ?? []).map((m) => m.userId).filter(Boolean);
3993
+ }
3994
+ /** Check if the bot is mentioned in a message. */
3995
+ function isBotMentioned(message, botUserId) {
3996
+ if (!botUserId) return false;
3997
+ if (extractMentionUserIds(message).includes(botUserId)) return true;
3998
+ return (message.body?.text ?? "").includes(`@${botUserId}`);
3999
+ }
4000
+ //#endregion
4001
+ //#region src/monitor/message-handler.preflight.ts
4002
+ /** Check if an inbound message should be accepted. */
4003
+ function mychatPreflight(message, account) {
4004
+ const senderId = message.from?.id;
4005
+ if (!senderId) return {
4006
+ allowed: false,
4007
+ reason: "no-sender"
4008
+ };
4009
+ if (senderId === account.accountId) return {
4010
+ allowed: false,
4011
+ reason: "self"
4012
+ };
4013
+ const toKind = message.to?.kind ?? "direct";
4014
+ if (toKind === "direct" || toKind === "channel") {
4015
+ if (account.dmPolicy === "disabled") return {
4016
+ allowed: false,
4017
+ reason: "dm-disabled"
4018
+ };
4019
+ if (account.dmPolicy === "allowlist" && account.allowFrom.length > 0) {
4020
+ if (!account.allowFrom.includes(senderId)) return {
4021
+ allowed: false,
4022
+ reason: "dm-not-allowed"
4023
+ };
4024
+ }
4025
+ }
4026
+ if (toKind === "group") {
4027
+ if (account.groupPolicy === "disabled") return {
4028
+ allowed: false,
4029
+ reason: "group-disabled"
4030
+ };
4031
+ if (account.groupPolicy === "allowlist" && account.groupAllowFrom.length > 0) {
4032
+ if (!account.groupAllowFrom.includes(senderId)) return {
4033
+ allowed: false,
4034
+ reason: "group-not-allowed"
4035
+ };
4036
+ }
4037
+ const groupId = message.to?.id;
4038
+ if (groupId && account.groups[groupId]) {
4039
+ const groupConfig = account.groups[groupId];
4040
+ if (groupConfig.enabled === false) return {
4041
+ allowed: false,
4042
+ reason: "group-disabled"
4043
+ };
4044
+ if (groupConfig.requireMention && !isBotMentioned(message, account.accountId)) return {
4045
+ allowed: false,
4046
+ reason: "mention-required"
4047
+ };
4048
+ }
4049
+ if (account.requireMention && !isBotMentioned(message, account.accountId)) return {
4050
+ allowed: false,
4051
+ reason: "mention-required"
4052
+ };
4053
+ }
4054
+ return { allowed: true };
4055
+ }
4056
+ //#endregion
4057
+ //#region src/monitor/inbound-context.ts
4058
+ function buildMychatInboundContext(message, account) {
4059
+ if (message.type !== "message" && message.type !== "stream") return null;
4060
+ const senderId = message.from?.id;
4061
+ if (!senderId) return null;
4062
+ const text = message.body?.text ?? "";
4063
+ const toKind = message.to?.kind ?? "direct";
4064
+ const chatType = toKind === "group" || toKind === "channel" ? "group" : "direct";
4065
+ return {
4066
+ messageId: message.id,
4067
+ text,
4068
+ senderId,
4069
+ senderName: message.from?.name,
4070
+ chatType,
4071
+ conversationId: message.to?.id ?? message.body?.conversationId,
4072
+ threadId: message.body?.parentId,
4073
+ replyTo: message.body?.parentId,
4074
+ mentions: message.body?.mentions ?? [],
4075
+ attachments: message.body?.attachments ?? [],
4076
+ timestamp: message.timestamp
4077
+ };
4078
+ }
4079
+ //#endregion
4080
+ //#region src/monitor/message-media.ts
4081
+ /** Resolve media attachments from an inbound message. */
4082
+ function resolveMychatMediaAttachments(message) {
4083
+ return (message.body?.attachments ?? []).filter((a) => a.url && (a.mimeType.startsWith("image/") || a.mimeType.startsWith("video/") || a.mimeType.startsWith("audio/") || a.mimeType === "application/pdf"));
4084
+ }
4085
+ //#endregion
4086
+ //#region src/monitor/message-handler.process.ts
4087
+ /** Process an inbound message into a structured result. */
4088
+ function processMychatInbound(message, account) {
4089
+ const context = buildMychatInboundContext(message, account);
4090
+ if (!context) return null;
4091
+ return {
4092
+ context,
4093
+ text: extractMychatMessageText(message),
4094
+ media: resolveMychatMediaAttachments(message)
4095
+ };
4096
+ }
4097
+ //#endregion
4098
+ //#region src/monitor/message-handler.ts
4099
+ function createMychatMessageHandler(params) {
4100
+ const { account, onInbound } = params;
4101
+ const dedupe = createMychatInboundDedupe();
4102
+ const logger = getMychatLogger();
4103
+ const prefix = mychatLogPrefix("handler");
4104
+ return { async handleMessage(message) {
4105
+ if (dedupe.isDuplicate(message.id)) return;
4106
+ const preflight = mychatPreflight(message, account);
4107
+ if (!preflight.allowed) {
4108
+ if (logger) logger.debug(`${prefix} message rejected reason=${preflight.reason} messageId=${message.id}`);
4109
+ return;
4110
+ }
4111
+ const result = processMychatInbound(message, account);
4112
+ if (!result) return;
4113
+ if (logger) logger.debug(`${prefix} message accepted messageId=${result.context.messageId} chatType=${result.context.chatType}`);
4114
+ await onInbound(result);
4115
+ } };
4116
+ }
4117
+ //#endregion
4118
+ //#region src/monitor/provider.ts
4119
+ async function monitorMychatProvider(ctx) {
4120
+ const { account, setStatus } = ctx;
4121
+ const logger = getMychatLogger();
4122
+ const prefix = mychatLogPrefix("provider");
4123
+ console.log(`[mychat:provider] starting accountId=${account.accountId}`);
4124
+ if (logger) logger.info(`${prefix} starting accountId=${account.accountId}`);
4125
+ setStatus({
4126
+ connected: false,
4127
+ mode: "websocket"
4128
+ });
4129
+ const httpClient = createMychatHttpClient({
4130
+ baseUrl: account.baseUrl,
4131
+ token: account.token
4132
+ });
4133
+ console.log(`[mychat:provider] fetching bot identity baseUrl=${account.baseUrl}`);
4134
+ if (logger) logger.info(`${prefix} fetching bot identity baseUrl=${account.baseUrl}`);
4135
+ const botSelf = await httpClient.getSelf();
4136
+ if (!botSelf) {
4137
+ const error = `MyChat provider failed to get bot identity (baseUrl=${account.baseUrl}, token=${account.token ? account.token.slice(0, 8) + "..." : "empty"})`;
4138
+ console.error(`[mychat:provider] ${error}`);
4139
+ if (logger) logger.error(`${prefix} ${error}`);
4140
+ setStatus({
4141
+ connected: false,
4142
+ lastError: error
4143
+ });
4144
+ throw new Error(error);
4145
+ }
4146
+ console.log(`[mychat:provider] got bot identity botId=${botSelf.botId} name=${botSelf.name ?? "unknown"}`);
4147
+ if (logger) logger.info(`${prefix} got bot identity botId=${botSelf.botId} name=${botSelf.name ?? "unknown"}`);
4148
+ const health = await httpClient.healthCheck();
4149
+ if (!health.ok) {
4150
+ console.warn(`[mychat:provider] health check failed latencyMs=${health.latencyMs} baseUrl=${account.baseUrl}`);
4151
+ if (logger) logger.warn(`${prefix} health check failed latencyMs=${health.latencyMs} baseUrl=${account.baseUrl}`);
4152
+ } else {
4153
+ console.log(`[mychat:provider] health check ok latencyMs=${health.latencyMs}`);
4154
+ if (logger) logger.info(`${prefix} health check ok latencyMs=${health.latencyMs}`);
4155
+ }
4156
+ const { wsClient } = createMychatProviderClients(account, botSelf.botId);
4157
+ const rt = ctx.runtime;
4158
+ rt.mychat = {
4159
+ accountId: account.accountId,
4160
+ httpClient,
4161
+ wsClient,
4162
+ botSelf
4163
+ };
4164
+ try {
4165
+ setMychatRuntime(ctx.runtime);
4166
+ } catch {}
4167
+ registerMychatMonitorListeners({
4168
+ account,
4169
+ handler: createMychatMessageHandler({
4170
+ account,
4171
+ onInbound: async (result) => {
4172
+ await dispatchMychatInbound(ctx, httpClient, result);
4173
+ }
4174
+ }),
4175
+ wsClient
4176
+ });
4177
+ console.log(`[mychat:provider] starting connection lifecycle`);
4178
+ const lifecycle = createMychatProviderLifecycle({
4179
+ httpClient,
4180
+ wsClient
4181
+ });
4182
+ const readyBotSelf = await lifecycle.waitForReady();
4183
+ if (!readyBotSelf) {
4184
+ const error = "MyChat provider failed to connect";
4185
+ console.error(`[mychat:provider] ${error}`);
4186
+ if (logger) logger.error(`${prefix} ${error}`);
4187
+ setStatus({
4188
+ connected: false,
4189
+ lastError: error
4190
+ });
4191
+ lifecycle.shutdown();
4192
+ throw new Error(error);
4193
+ }
4194
+ setStatus({
4195
+ connected: true,
4196
+ lastConnectedAt: Date.now(),
4197
+ lastEventAt: Date.now(),
4198
+ mode: "websocket",
4199
+ lastError: null
4200
+ });
4201
+ console.log(`[mychat:provider] bot connected botId=${readyBotSelf.botId}`);
4202
+ if (logger) logger.info(`${prefix} bot connected botId=${readyBotSelf.botId}`);
4203
+ ctx.abortSignal?.addEventListener("abort", () => {
4204
+ console.log(`[mychat:provider] abort signal received, shutting down`);
4205
+ if (logger) logger.info(`${prefix} abort signal received, shutting down`);
4206
+ lifecycle.shutdown();
4207
+ setStatus({ connected: false });
4208
+ });
4209
+ return { unsubscribe() {
4210
+ console.log(`[mychat:provider] unsubscribe called, shutting down`);
4211
+ lifecycle.shutdown();
4212
+ setStatus({ connected: false });
4213
+ } };
4214
+ }
4215
+ /** Dispatch an inbound message through the OpenClaw turn runtime. */
4216
+ async function dispatchMychatInbound(ctx, httpClient, result) {
4217
+ const { runtime, cfg, account } = ctx;
4218
+ const logger = getMychatLogger();
4219
+ const prefix = mychatLogPrefix("dispatch");
4220
+ const rt = runtime;
4221
+ if (!rt.channel?.turn?.run) {
4222
+ if (logger) logger.warn(`${prefix} channel turn runtime not available, skipping inbound`);
4223
+ return;
4224
+ }
4225
+ const conversationId = result.context.conversationId ?? result.context.senderId;
4226
+ const senderId = result.context.senderId;
4227
+ const chatType = result.context.chatType;
4228
+ const messageId = result.context.messageId;
4229
+ await rt.channel.turn.run({
4230
+ channel: "mychat",
4231
+ accountId: account.accountId,
4232
+ raw: result,
4233
+ adapter: {
4234
+ ingest: () => ({
4235
+ id: messageId,
4236
+ timestamp: result.context.timestamp,
4237
+ rawText: result.text,
4238
+ textForAgent: result.text,
4239
+ textForCommands: result.text,
4240
+ raw: result
4241
+ }),
4242
+ resolveTurn: (input) => {
4243
+ const msgCtx = rt.channel.turn.buildContext({
4244
+ channel: "mychat",
4245
+ accountId: account.accountId,
4246
+ timestamp: input.timestamp,
4247
+ from: `mychat:user:${senderId}`,
4248
+ sender: {
4249
+ id: senderId,
4250
+ name: result.context.senderName
4251
+ },
4252
+ conversation: {
4253
+ kind: chatType,
4254
+ id: conversationId,
4255
+ label: result.context.senderName ?? senderId,
4256
+ routePeer: {
4257
+ kind: chatType,
4258
+ id: conversationId
4259
+ }
4260
+ },
4261
+ route: {
4262
+ agentId: void 0,
4263
+ accountId: account.accountId
4264
+ },
4265
+ reply: {
4266
+ to: `mychat:${conversationId}`,
4267
+ originatingTo: `mychat:${conversationId}`
4268
+ },
4269
+ message: {
4270
+ rawBody: input.rawText,
4271
+ commandBody: input.textForCommands,
4272
+ bodyForAgent: input.textForAgent,
4273
+ envelopeFrom: result.context.senderName
4274
+ }
4275
+ });
4276
+ return {
4277
+ cfg,
4278
+ channel: "mychat",
4279
+ accountId: account.accountId,
4280
+ ctxPayload: msgCtx,
4281
+ dispatchReplyWithBufferedBlockDispatcher: rt.channel.reply.dispatchReplyWithBufferedBlockDispatcher,
4282
+ delivery: { deliver: async (payload) => {
4283
+ const text = payload.text?.trim();
4284
+ if (!text) return;
4285
+ const sendResult = await httpClient.sendMessage({
4286
+ to: conversationId,
4287
+ text,
4288
+ type: chatType === "group" ? "group" : "direct"
4289
+ });
4290
+ if (logger) logger.debug(`${prefix} reply sent to=${conversationId} ok=${Boolean(sendResult)}`);
4291
+ } }
4292
+ };
4293
+ }
4294
+ }
4295
+ });
4296
+ }
4297
+ //#endregion
4298
+ //#region src/shared.ts
4299
+ function createMychatPluginBase() {
4300
+ return {
4301
+ ...createChannelPluginBase({
4302
+ id: "mychat",
4303
+ setup: { applyAccountConfig(params) {
4304
+ return params.cfg;
4305
+ } },
4306
+ meta: {
4307
+ label: "MyChat",
4308
+ docsPath: "docs/plugins/mychat",
4309
+ blurb: "Connect to MyChat server via bot token",
4310
+ aliases: ["mychat"]
4311
+ },
4312
+ capabilities: {
4313
+ chatTypes: [
4314
+ "direct",
4315
+ "group",
4316
+ "thread"
4317
+ ],
4318
+ reactions: true,
4319
+ media: true,
4320
+ threads: true
4321
+ },
4322
+ reload: { configPrefixes: ["channels.mychat"] },
4323
+ configSchema: mychatConfigSchema,
4324
+ config: {
4325
+ listAccountIds(cfg) {
4326
+ return resolveMychatAccounts(cfg).map((a) => a.accountId);
4327
+ },
4328
+ resolveAccount(cfg, accountId) {
4329
+ return resolveMychatAccount({
4330
+ cfg,
4331
+ accountId
4332
+ });
4333
+ },
4334
+ async inspectAccount(account) {
4335
+ return {
4336
+ accountId: account.accountId,
4337
+ enabled: account.enabled,
4338
+ label: account.name ?? account.baseUrl
4339
+ };
4340
+ }
4341
+ }
4342
+ }),
4343
+ gateway: { async startAccount(ctx) {
4344
+ const setStatus = createAccountStatusSink({
4345
+ accountId: ctx.accountId,
4346
+ setStatus: ctx.setStatus
4347
+ });
4348
+ return { unsubscribe: (await monitorMychatProvider({
4349
+ cfg: ctx.cfg,
4350
+ runtime: ctx.runtime,
4351
+ account: ctx.account,
4352
+ setStatus,
4353
+ abortSignal: ctx.abortSignal,
4354
+ log: ctx.log
4355
+ })).unsubscribe };
4356
+ } }
4357
+ };
4358
+ }
4359
+ //#endregion
4360
+ //#region src/outbound/outbound-payload.ts
4361
+ async function sendMychatOutboundPayload(ctx, body) {
4362
+ const logger = getMychatLogger();
4363
+ const prefix = mychatLogPrefix("outbound");
4364
+ try {
4365
+ const result = await ctx.httpClient.sendMessage(body);
4366
+ if (result) {
4367
+ if (logger) logger.debug(`${prefix} message sent id=${result.id} status=${result.status}`);
4368
+ return {
4369
+ messageId: result.id,
4370
+ ok: true
4371
+ };
4372
+ }
4373
+ return { ok: false };
4374
+ } catch (error) {
4375
+ if (logger) logger.error(`${prefix} send failed error=${error instanceof Error ? error.message : String(error)}`);
4376
+ return { ok: false };
4377
+ }
4378
+ }
4379
+ //#endregion
4380
+ //#region src/outbound/outbound-send-context.ts
4381
+ function createMychatOutboundSendContext(params) {
4382
+ return {
4383
+ account: params.account,
4384
+ httpClient: params.httpClient
4385
+ };
4386
+ }
4387
+ //#endregion
4388
+ //#region src/outbound/outbound-adapter.ts
4389
+ init_runtime();
4390
+ const TEXT_CHUNK_LIMIT = 4e3;
4391
+ function chunkText(text, limit) {
4392
+ if (text.length <= limit) return [text];
4393
+ const chunks = [];
4394
+ let remaining = text;
4395
+ while (remaining.length > 0) {
4396
+ chunks.push(remaining.slice(0, limit));
4397
+ remaining = remaining.slice(limit);
4398
+ }
4399
+ return chunks;
4400
+ }
4401
+ const mychatOutbound = {
4402
+ deliveryMode: "direct",
4403
+ textChunkLimit: TEXT_CHUNK_LIMIT,
4404
+ async sendText(params) {
4405
+ const logger = getMychatLogger();
4406
+ const account = getMychatRuntime().mychat;
4407
+ if (!account) {
4408
+ if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
4409
+ return {
4410
+ channel: "mychat",
4411
+ messageId: "",
4412
+ ok: false
4413
+ };
4414
+ }
4415
+ const ctx = createMychatOutboundSendContext({
4416
+ account: params.account ?? {},
4417
+ httpClient: account.httpClient
4418
+ });
4419
+ const chunks = chunkText(params.text, TEXT_CHUNK_LIMIT);
4420
+ let lastOk = false;
4421
+ let lastMessageId = "";
4422
+ for (const chunk of chunks) {
4423
+ const result = await sendMychatOutboundPayload(ctx, {
4424
+ to: params.target ?? "",
4425
+ text: chunk,
4426
+ type: params.chatType,
4427
+ replyTo: params.replyTo
4428
+ });
4429
+ lastOk = result.ok;
4430
+ if (result.messageId) lastMessageId = result.messageId;
4431
+ }
4432
+ return {
4433
+ channel: "mychat",
4434
+ messageId: lastMessageId,
4435
+ ok: lastOk
4436
+ };
4437
+ },
4438
+ async sendMedia(params) {
4439
+ const logger = getMychatLogger();
4440
+ const account = getMychatRuntime().mychat;
4441
+ if (!account) {
4442
+ if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
4443
+ return {
4444
+ channel: "mychat",
4445
+ messageId: "",
4446
+ ok: false
4447
+ };
4448
+ }
4449
+ if (params.media) {
4450
+ const uploadResult = await account.httpClient.uploadFile({
4451
+ file: params.media,
4452
+ filename: params.filename ?? "upload",
4453
+ mimeType: params.mimeType ?? "application/octet-stream"
4454
+ });
4455
+ if (uploadResult) {
4456
+ const result = await sendMychatOutboundPayload(createMychatOutboundSendContext({
4457
+ account: params.account ?? {},
4458
+ httpClient: account.httpClient
4459
+ }), {
4460
+ to: params.target ?? "",
4461
+ text: params.text ?? "",
4462
+ type: params.chatType,
4463
+ attachments: [uploadResult.attachment]
4464
+ });
4465
+ return {
4466
+ channel: "mychat",
4467
+ messageId: result.messageId ?? "",
4468
+ ok: result.ok
4469
+ };
4470
+ }
4471
+ }
4472
+ return {
4473
+ channel: "mychat",
4474
+ messageId: "",
4475
+ ok: false
4476
+ };
4477
+ },
4478
+ async sendPayload(params) {
4479
+ const logger = getMychatLogger();
4480
+ const account = getMychatRuntime().mychat;
4481
+ if (!account) {
4482
+ if (logger) logger.error(`${mychatLogPrefix("outbound")} no runtime account`);
4483
+ return {
4484
+ channel: "mychat",
4485
+ messageId: "",
4486
+ ok: false
4487
+ };
4488
+ }
4489
+ const result = await sendMychatOutboundPayload(createMychatOutboundSendContext({
4490
+ account: params.account ?? {},
4491
+ httpClient: account.httpClient
4492
+ }), {
4493
+ to: params.target ?? "",
4494
+ text: params.text ?? "",
4495
+ type: params.chatType,
4496
+ replyTo: params.replyTo
4497
+ });
4498
+ return {
4499
+ channel: "mychat",
4500
+ messageId: result.messageId ?? "",
4501
+ ok: result.ok
4502
+ };
4503
+ }
4504
+ };
4505
+ //#endregion
4506
+ //#region src/channel.ts
4507
+ const mychatPlugin = createChatChannelPlugin({
4508
+ base: createMychatPluginBase(),
4509
+ security: { dm: {
4510
+ channelKey: "dmPolicy",
4511
+ resolvePolicy(account) {
4512
+ return account.dmPolicy ?? "open";
4513
+ },
4514
+ resolveAllowFrom(account) {
4515
+ return account.allowFrom;
4516
+ }
4517
+ } },
4518
+ pairing: { text: {
4519
+ idLabel: "MyChat Username",
4520
+ message: "To connect to MyChat, configure your bot token and server URL in the channel config.",
4521
+ notify() {}
4522
+ } },
4523
+ threading: {
4524
+ replyToMode: "always",
4525
+ resolveReplyTo(params) {
4526
+ return {
4527
+ threadId: params.inbound?.threadId ?? params.inbound?.conversationId,
4528
+ replyTo: params.inbound?.messageId
4529
+ };
4530
+ },
4531
+ sanitizeThreadName(name) {
4532
+ return name.slice(0, 100).replace(/[^\w\s-]/g, "");
4533
+ }
4534
+ },
4535
+ outbound: mychatOutbound
4536
+ });
4537
+ //#endregion
4538
+ export { mychatPlugin as t };