@discordjs/ws 1.1.1 → 1.1.2-dev.1717373416-94cc02a25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,7 +50,10 @@ const manager = new WebSocketManager({
50
50
  intents: 0, // for no intents
51
51
  rest,
52
52
  // uncomment if you have zlib-sync installed and want to use compression
53
- // compression: CompressionMethod.ZlibStream,
53
+ // compression: CompressionMethod.ZlibSync,
54
+
55
+ // alternatively, we support compression using node's native `node:zlib` module:
56
+ // compression: CompressionMethod.ZlibNative,
54
57
  });
55
58
 
56
59
  manager.on(WebSocketShardEvents.Dispatch, (event) => {
@@ -141,7 +141,6 @@ var import_node_timers = require("timers");
141
141
  var import_promises2 = require("timers/promises");
142
142
  var import_node_url = require("url");
143
143
  var import_node_util = require("util");
144
- var import_node_zlib = require("zlib");
145
144
  var import_collection6 = require("@discordjs/collection");
146
145
  var import_util2 = require("@discordjs/util");
147
146
  var import_async_queue2 = require("@sapphire/async-queue");
@@ -292,8 +291,17 @@ var SimpleIdentifyThrottler = class {
292
291
  };
293
292
 
294
293
  // src/utils/constants.ts
294
+ var CompressionMethod = /* @__PURE__ */ ((CompressionMethod2) => {
295
+ CompressionMethod2[CompressionMethod2["ZlibNative"] = 0] = "ZlibNative";
296
+ CompressionMethod2[CompressionMethod2["ZlibSync"] = 1] = "ZlibSync";
297
+ return CompressionMethod2;
298
+ })(CompressionMethod || {});
295
299
  var DefaultDeviceProperty = `@discordjs/ws [VI]{{inject}}[/VI]`;
296
300
  var getDefaultSessionStore = (0, import_util.lazy)(() => new import_collection5.Collection());
301
+ var CompressionParameterMap = {
302
+ [0 /* ZlibNative */]: "zlib-stream",
303
+ [1 /* ZlibSync */]: "zlib-stream"
304
+ };
297
305
  var DefaultWebSocketManagerOptions = {
298
306
  async buildIdentifyThrottler(manager) {
299
307
  const info = await manager.fetchGatewayInformation();
@@ -312,6 +320,7 @@ var DefaultWebSocketManagerOptions = {
312
320
  version: import_v10.APIVersion,
313
321
  encoding: "json" /* JSON */,
314
322
  compression: null,
323
+ useIdentifyCompression: false,
315
324
  retrieveSessionInfo(shardId) {
316
325
  const store = getDefaultSessionStore();
317
326
  return store.get(shardId) ?? null;
@@ -343,6 +352,7 @@ __name(getInitialSendRateLimitState, "getInitialSendRateLimitState");
343
352
 
344
353
  // src/ws/WebSocketShard.ts
345
354
  var getZlibSync = (0, import_util2.lazy)(async () => import("zlib-sync").then((mod) => mod.default).catch(() => null));
355
+ var getNativeZlib = (0, import_util2.lazy)(async () => import("zlib").then((mod) => mod).catch(() => null));
346
356
  var WebSocketShardEvents = /* @__PURE__ */ ((WebSocketShardEvents2) => {
347
357
  WebSocketShardEvents2["Closed"] = "closed";
348
358
  WebSocketShardEvents2["Debug"] = "debug";
@@ -365,8 +375,8 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
365
375
  __name(this, "WebSocketShard");
366
376
  }
367
377
  connection = null;
368
- useIdentifyCompress = false;
369
- inflate = null;
378
+ nativeInflate = null;
379
+ zLibSyncInflate = null;
370
380
  textDecoder = new import_node_util.TextDecoder();
371
381
  replayedEvents = 0;
372
382
  isAck = true;
@@ -383,6 +393,16 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
383
393
  strategy;
384
394
  id;
385
395
  #status = 0 /* Idle */;
396
+ identifyCompressionEnabled = false;
397
+ /**
398
+ * @privateRemarks
399
+ *
400
+ * This is needed because `this.strategy.options.compression` is not an actual reflection of the compression method
401
+ * used, but rather the compression method that the user wants to use. This is because the libraries could just be missing.
402
+ */
403
+ get transportCompressionEnabled() {
404
+ return this.strategy.options.compression !== null && (this.nativeInflate ?? this.zLibSyncInflate) !== null;
405
+ }
386
406
  get status() {
387
407
  return this.#status;
388
408
  }
@@ -414,21 +434,53 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
414
434
  if (this.#status !== 0 /* Idle */) {
415
435
  throw new Error("Tried to connect a shard that wasn't idle");
416
436
  }
417
- const { version, encoding, compression } = this.strategy.options;
437
+ const { version, encoding, compression, useIdentifyCompression } = this.strategy.options;
438
+ this.identifyCompressionEnabled = useIdentifyCompression;
418
439
  const params = new import_node_url.URLSearchParams({ v: version, encoding });
419
- if (compression) {
420
- const zlib = await getZlibSync();
421
- if (zlib) {
422
- params.append("compress", compression);
423
- this.inflate = new zlib.Inflate({
424
- chunkSize: 65535,
425
- to: "string"
426
- });
427
- } else if (!this.useIdentifyCompress) {
428
- this.useIdentifyCompress = true;
429
- console.warn(
430
- "WebSocketShard: Compression is enabled but zlib-sync is not installed, falling back to identify compress"
431
- );
440
+ if (compression !== null) {
441
+ if (useIdentifyCompression) {
442
+ console.warn("WebSocketShard: transport compression is enabled, disabling identify compression");
443
+ this.identifyCompressionEnabled = false;
444
+ }
445
+ params.append("compress", CompressionParameterMap[compression]);
446
+ switch (compression) {
447
+ case 0 /* ZlibNative */: {
448
+ const zlib = await getNativeZlib();
449
+ if (zlib) {
450
+ const inflate = zlib.createInflate({
451
+ chunkSize: 65535,
452
+ flush: zlib.constants.Z_SYNC_FLUSH
453
+ });
454
+ inflate.on("error", (error) => {
455
+ this.emit("error" /* Error */, { error });
456
+ });
457
+ this.nativeInflate = inflate;
458
+ } else {
459
+ console.warn("WebSocketShard: Compression is set to native but node:zlib is not available.");
460
+ params.delete("compress");
461
+ }
462
+ break;
463
+ }
464
+ case 1 /* ZlibSync */: {
465
+ const zlib = await getZlibSync();
466
+ if (zlib) {
467
+ this.zLibSyncInflate = new zlib.Inflate({
468
+ chunkSize: 65535,
469
+ to: "string"
470
+ });
471
+ } else {
472
+ console.warn("WebSocketShard: Compression is set to zlib-sync, but it is not installed.");
473
+ params.delete("compress");
474
+ }
475
+ break;
476
+ }
477
+ }
478
+ }
479
+ if (this.identifyCompressionEnabled) {
480
+ const zlib = await getNativeZlib();
481
+ if (!zlib) {
482
+ console.warn("WebSocketShard: Identify compression is enabled, but node:zlib is not available.");
483
+ this.identifyCompressionEnabled = false;
432
484
  }
433
485
  }
434
486
  const session = await this.strategy.retrieveSessionInfo(this.id);
@@ -622,24 +674,25 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
622
674
  `shard id: ${this.id.toString()}`,
623
675
  `shard count: ${this.strategy.options.shardCount}`,
624
676
  `intents: ${this.strategy.options.intents}`,
625
- `compression: ${this.inflate ? "zlib-stream" : this.useIdentifyCompress ? "identify" : "none"}`
677
+ `compression: ${this.transportCompressionEnabled ? CompressionParameterMap[this.strategy.options.compression] : this.identifyCompressionEnabled ? "identify" : "none"}`
626
678
  ]);
627
- const d = {
679
+ const data = {
628
680
  token: this.strategy.options.token,
629
681
  properties: this.strategy.options.identifyProperties,
630
682
  intents: this.strategy.options.intents,
631
- compress: this.useIdentifyCompress,
683
+ compress: this.identifyCompressionEnabled,
632
684
  shard: [this.id, this.strategy.options.shardCount]
633
685
  };
634
686
  if (this.strategy.options.largeThreshold) {
635
- d.large_threshold = this.strategy.options.largeThreshold;
687
+ data.large_threshold = this.strategy.options.largeThreshold;
636
688
  }
637
689
  if (this.strategy.options.initialPresence) {
638
- d.presence = this.strategy.options.initialPresence;
690
+ data.presence = this.strategy.options.initialPresence;
639
691
  }
640
692
  await this.send({
641
693
  op: import_v102.GatewayOpcodes.Identify,
642
- d
694
+ // eslint-disable-next-line id-length
695
+ d: data
643
696
  });
644
697
  await this.waitForEvent("ready" /* Ready */, this.strategy.options.readyTimeout);
645
698
  }
@@ -654,6 +707,7 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
654
707
  this.replayedEvents = 0;
655
708
  return this.send({
656
709
  op: import_v102.GatewayOpcodes.Resume,
710
+ // eslint-disable-next-line id-length
657
711
  d: {
658
712
  token: this.strategy.options.token,
659
713
  seq: session.sequence,
@@ -668,11 +722,18 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
668
722
  const session = await this.strategy.retrieveSessionInfo(this.id);
669
723
  await this.send({
670
724
  op: import_v102.GatewayOpcodes.Heartbeat,
725
+ // eslint-disable-next-line id-length
671
726
  d: session?.sequence ?? null
672
727
  });
673
728
  this.lastHeartbeatAt = Date.now();
674
729
  this.isAck = false;
675
730
  }
731
+ parseInflateResult(result) {
732
+ if (!result) {
733
+ return null;
734
+ }
735
+ return JSON.parse(typeof result === "string" ? result : this.textDecoder.decode(result));
736
+ }
676
737
  async unpackMessage(data, isBinary) {
677
738
  if (!isBinary) {
678
739
  try {
@@ -682,9 +743,10 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
682
743
  }
683
744
  }
684
745
  const decompressable = new Uint8Array(data);
685
- if (this.useIdentifyCompress) {
686
- return new Promise((resolve2, reject) => {
687
- (0, import_node_zlib.inflate)(decompressable, { chunkSize: 65535 }, (err, result) => {
746
+ if (this.identifyCompressionEnabled) {
747
+ return new Promise(async (resolve2, reject) => {
748
+ const zlib = await getNativeZlib();
749
+ zlib.inflate(decompressable, { chunkSize: 65535 }, (err, result) => {
688
750
  if (err) {
689
751
  reject(err);
690
752
  return;
@@ -693,30 +755,37 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
693
755
  });
694
756
  });
695
757
  }
696
- if (this.inflate) {
697
- const l = decompressable.length;
698
- const flush = l >= 4 && decompressable[l - 4] === 0 && decompressable[l - 3] === 0 && decompressable[l - 2] === 255 && decompressable[l - 1] === 255;
699
- const zlib = await getZlibSync();
700
- this.inflate.push(import_node_buffer.Buffer.from(decompressable), flush ? zlib.Z_SYNC_FLUSH : zlib.Z_NO_FLUSH);
701
- if (this.inflate.err) {
702
- this.emit("error" /* Error */, {
703
- error: new Error(`${this.inflate.err}${this.inflate.msg ? `: ${this.inflate.msg}` : ""}`)
704
- });
705
- }
706
- if (!flush) {
707
- return null;
708
- }
709
- const { result } = this.inflate;
710
- if (!result) {
711
- return null;
758
+ if (this.transportCompressionEnabled) {
759
+ const flush = decompressable.length >= 4 && decompressable.at(-4) === 0 && decompressable.at(-3) === 0 && decompressable.at(-2) === 255 && decompressable.at(-1) === 255;
760
+ if (this.nativeInflate) {
761
+ this.nativeInflate.write(decompressable, "binary");
762
+ if (!flush) {
763
+ return null;
764
+ }
765
+ const [result] = await (0, import_node_events2.once)(this.nativeInflate, "data");
766
+ return this.parseInflateResult(result);
767
+ } else if (this.zLibSyncInflate) {
768
+ const zLibSync = await getZlibSync();
769
+ this.zLibSyncInflate.push(import_node_buffer.Buffer.from(decompressable), flush ? zLibSync.Z_SYNC_FLUSH : zLibSync.Z_NO_FLUSH);
770
+ if (this.zLibSyncInflate.err) {
771
+ this.emit("error" /* Error */, {
772
+ error: new Error(
773
+ `${this.zLibSyncInflate.err}${this.zLibSyncInflate.msg ? `: ${this.zLibSyncInflate.msg}` : ""}`
774
+ )
775
+ });
776
+ }
777
+ if (!flush) {
778
+ return null;
779
+ }
780
+ const { result } = this.zLibSyncInflate;
781
+ return this.parseInflateResult(result);
712
782
  }
713
- return JSON.parse(typeof result === "string" ? result : this.textDecoder.decode(result));
714
783
  }
715
784
  this.debug([
716
785
  "Received a message we were unable to decompress",
717
786
  `isBinary: ${isBinary.toString()}`,
718
- `useIdentifyCompress: ${this.useIdentifyCompress.toString()}`,
719
- `inflate: ${Boolean(this.inflate).toString()}`
787
+ `identifyCompressionEnabled: ${this.identifyCompressionEnabled.toString()}`,
788
+ `inflate: ${this.transportCompressionEnabled ? CompressionMethod[this.strategy.options.compression] : "none"}`
720
789
  ]);
721
790
  return null;
722
791
  }
@@ -925,7 +994,7 @@ var WebSocketShard = class extends import_async_event_emitter.AsyncEventEmitter
925
994
  }
926
995
  debug(messages) {
927
996
  const message = `${messages[0]}${messages.length > 1 ? `
928
- ${messages.slice(1).map((m) => ` ${m}`).join("\n")}` : ""}`;
997
+ ${messages.slice(1).map((message2) => ` ${message2}`).join("\n")}` : ""}`;
929
998
  this.emit("debug" /* Debug */, { message });
930
999
  }
931
1000
  };