@babylonjs/inspector 9.1.0 → 9.2.0

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