summaryrefslogtreecommitdiff
path: root/node_modules/@discordjs/ws/dist/index.mjs
diff options
context:
space:
mode:
authorsowgro <tpoke.ferrari@gmail.com>2023-09-02 19:12:47 -0400
committersowgro <tpoke.ferrari@gmail.com>2023-09-02 19:12:47 -0400
commite4450c8417624b71d779cb4f41692538f9165e10 (patch)
treeb70826542223ecdf8a7a259f61b0a1abb8a217d8 /node_modules/@discordjs/ws/dist/index.mjs
downloadsowbot3-e4450c8417624b71d779cb4f41692538f9165e10.tar.gz
sowbot3-e4450c8417624b71d779cb4f41692538f9165e10.tar.bz2
sowbot3-e4450c8417624b71d779cb4f41692538f9165e10.zip
first commit
Diffstat (limited to 'node_modules/@discordjs/ws/dist/index.mjs')
-rw-r--r--node_modules/@discordjs/ws/dist/index.mjs1446
1 files changed, 1446 insertions, 0 deletions
diff --git a/node_modules/@discordjs/ws/dist/index.mjs b/node_modules/@discordjs/ws/dist/index.mjs
new file mode 100644
index 0000000..56e44e6
--- /dev/null
+++ b/node_modules/@discordjs/ws/dist/index.mjs
@@ -0,0 +1,1446 @@
+var __defProp = Object.defineProperty;
+var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
+var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
+}) : x)(function(x) {
+ if (typeof require !== "undefined")
+ return require.apply(this, arguments);
+ throw Error('Dynamic require of "' + x + '" is not supported');
+});
+
+// ../../node_modules/tsup/assets/esm_shims.js
+import { fileURLToPath } from "url";
+import path from "path";
+var getFilename = /* @__PURE__ */ __name(() => fileURLToPath(import.meta.url), "getFilename");
+var getDirname = /* @__PURE__ */ __name(() => path.dirname(getFilename()), "getDirname");
+var __dirname = /* @__PURE__ */ getDirname();
+
+// src/strategies/context/IContextFetchingStrategy.ts
+async function managerToFetchingStrategyOptions(manager) {
+ const {
+ buildIdentifyThrottler,
+ buildStrategy,
+ retrieveSessionInfo,
+ updateSessionInfo,
+ shardCount,
+ shardIds,
+ rest,
+ ...managerOptions
+ } = manager.options;
+ return {
+ ...managerOptions,
+ gatewayInformation: await manager.fetchGatewayInformation(),
+ shardCount: await manager.getShardCount()
+ };
+}
+__name(managerToFetchingStrategyOptions, "managerToFetchingStrategyOptions");
+
+// src/strategies/context/SimpleContextFetchingStrategy.ts
+var SimpleContextFetchingStrategy = class _SimpleContextFetchingStrategy {
+ constructor(manager, options) {
+ this.manager = manager;
+ this.options = options;
+ }
+ static {
+ __name(this, "SimpleContextFetchingStrategy");
+ }
+ // This strategy assumes every shard is running under the same process - therefore we need a single
+ // IdentifyThrottler per manager.
+ static throttlerCache = /* @__PURE__ */ new WeakMap();
+ static async ensureThrottler(manager) {
+ const throttler = _SimpleContextFetchingStrategy.throttlerCache.get(manager);
+ if (throttler) {
+ return throttler;
+ }
+ const newThrottler = await manager.options.buildIdentifyThrottler(manager);
+ _SimpleContextFetchingStrategy.throttlerCache.set(manager, newThrottler);
+ return newThrottler;
+ }
+ async retrieveSessionInfo(shardId) {
+ return this.manager.options.retrieveSessionInfo(shardId);
+ }
+ updateSessionInfo(shardId, sessionInfo) {
+ return this.manager.options.updateSessionInfo(shardId, sessionInfo);
+ }
+ async waitForIdentify(shardId, signal) {
+ const throttler = await _SimpleContextFetchingStrategy.ensureThrottler(this.manager);
+ await throttler.waitForIdentify(shardId, signal);
+ }
+};
+
+// src/strategies/context/WorkerContextFetchingStrategy.ts
+import { isMainThread, parentPort } from "node:worker_threads";
+import { Collection as Collection2 } from "@discordjs/collection";
+
+// src/strategies/sharding/WorkerShardingStrategy.ts
+import { once } from "node:events";
+import { join, isAbsolute, resolve } from "node:path";
+import { Worker } from "node:worker_threads";
+import { Collection } from "@discordjs/collection";
+var WorkerSendPayloadOp = /* @__PURE__ */ ((WorkerSendPayloadOp2) => {
+ WorkerSendPayloadOp2[WorkerSendPayloadOp2["Connect"] = 0] = "Connect";
+ WorkerSendPayloadOp2[WorkerSendPayloadOp2["Destroy"] = 1] = "Destroy";
+ WorkerSendPayloadOp2[WorkerSendPayloadOp2["Send"] = 2] = "Send";
+ WorkerSendPayloadOp2[WorkerSendPayloadOp2["SessionInfoResponse"] = 3] = "SessionInfoResponse";
+ WorkerSendPayloadOp2[WorkerSendPayloadOp2["ShardIdentifyResponse"] = 4] = "ShardIdentifyResponse";
+ WorkerSendPayloadOp2[WorkerSendPayloadOp2["FetchStatus"] = 5] = "FetchStatus";
+ return WorkerSendPayloadOp2;
+})(WorkerSendPayloadOp || {});
+var WorkerReceivePayloadOp = /* @__PURE__ */ ((WorkerReceivePayloadOp2) => {
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["Connected"] = 0] = "Connected";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["Destroyed"] = 1] = "Destroyed";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["Event"] = 2] = "Event";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["RetrieveSessionInfo"] = 3] = "RetrieveSessionInfo";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["UpdateSessionInfo"] = 4] = "UpdateSessionInfo";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["WaitForIdentify"] = 5] = "WaitForIdentify";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["FetchStatusResponse"] = 6] = "FetchStatusResponse";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["WorkerReady"] = 7] = "WorkerReady";
+ WorkerReceivePayloadOp2[WorkerReceivePayloadOp2["CancelIdentify"] = 8] = "CancelIdentify";
+ return WorkerReceivePayloadOp2;
+})(WorkerReceivePayloadOp || {});
+var WorkerShardingStrategy = class {
+ static {
+ __name(this, "WorkerShardingStrategy");
+ }
+ manager;
+ options;
+ #workers = [];
+ #workerByShardId = new Collection();
+ connectPromises = new Collection();
+ destroyPromises = new Collection();
+ fetchStatusPromises = new Collection();
+ waitForIdentifyControllers = new Collection();
+ throttler;
+ constructor(manager, options) {
+ this.manager = manager;
+ this.options = options;
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.spawn}
+ */
+ async spawn(shardIds) {
+ const shardsPerWorker = this.options.shardsPerWorker === "all" ? shardIds.length : this.options.shardsPerWorker;
+ const strategyOptions = await managerToFetchingStrategyOptions(this.manager);
+ const loops = Math.ceil(shardIds.length / shardsPerWorker);
+ const promises = [];
+ for (let idx = 0; idx < loops; idx++) {
+ const slice = shardIds.slice(idx * shardsPerWorker, (idx + 1) * shardsPerWorker);
+ const workerData2 = {
+ ...strategyOptions,
+ shardIds: slice
+ };
+ promises.push(this.setupWorker(workerData2));
+ }
+ await Promise.all(promises);
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.connect}
+ */
+ async connect() {
+ const promises = [];
+ for (const [shardId, worker] of this.#workerByShardId.entries()) {
+ const payload = {
+ op: 0 /* Connect */,
+ shardId
+ };
+ const promise = new Promise((resolve2) => this.connectPromises.set(shardId, resolve2));
+ worker.postMessage(payload);
+ promises.push(promise);
+ }
+ await Promise.all(promises);
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.destroy}
+ */
+ async destroy(options = {}) {
+ const promises = [];
+ for (const [shardId, worker] of this.#workerByShardId.entries()) {
+ const payload = {
+ op: 1 /* Destroy */,
+ shardId,
+ options
+ };
+ promises.push(
+ // eslint-disable-next-line no-promise-executor-return, promise/prefer-await-to-then
+ new Promise((resolve2) => this.destroyPromises.set(shardId, resolve2)).then(async () => worker.terminate())
+ );
+ worker.postMessage(payload);
+ }
+ this.#workers = [];
+ this.#workerByShardId.clear();
+ await Promise.all(promises);
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.send}
+ */
+ send(shardId, data) {
+ const worker = this.#workerByShardId.get(shardId);
+ if (!worker) {
+ throw new Error(`No worker found for shard ${shardId}`);
+ }
+ const payload = {
+ op: 2 /* Send */,
+ shardId,
+ payload: data
+ };
+ worker.postMessage(payload);
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.fetchStatus}
+ */
+ async fetchStatus() {
+ const statuses = new Collection();
+ for (const [shardId, worker] of this.#workerByShardId.entries()) {
+ const nonce = Math.random();
+ const payload = {
+ op: 5 /* FetchStatus */,
+ shardId,
+ nonce
+ };
+ const promise = new Promise((resolve2) => this.fetchStatusPromises.set(nonce, resolve2));
+ worker.postMessage(payload);
+ const status = await promise;
+ statuses.set(shardId, status);
+ }
+ return statuses;
+ }
+ async setupWorker(workerData2) {
+ const worker = new Worker(this.resolveWorkerPath(), { workerData: workerData2 });
+ await once(worker, "online");
+ await this.waitForWorkerReady(worker);
+ worker.on("error", (err) => {
+ throw err;
+ }).on("messageerror", (err) => {
+ throw err;
+ }).on("message", async (payload) => this.onMessage(worker, payload));
+ this.#workers.push(worker);
+ for (const shardId of workerData2.shardIds) {
+ this.#workerByShardId.set(shardId, worker);
+ }
+ }
+ resolveWorkerPath() {
+ const path2 = this.options.workerPath;
+ if (!path2) {
+ return join(__dirname, "defaultWorker.js");
+ }
+ if (isAbsolute(path2)) {
+ return path2;
+ }
+ if (/^\.\.?[/\\]/.test(path2)) {
+ return resolve(path2);
+ }
+ try {
+ return __require.resolve(path2);
+ } catch {
+ return resolve(path2);
+ }
+ }
+ async waitForWorkerReady(worker) {
+ return new Promise((resolve2) => {
+ const handler = /* @__PURE__ */ __name((payload) => {
+ if (payload.op === 7 /* WorkerReady */) {
+ resolve2();
+ worker.off("message", handler);
+ }
+ }, "handler");
+ worker.on("message", handler);
+ });
+ }
+ async onMessage(worker, payload) {
+ switch (payload.op) {
+ case 0 /* Connected */: {
+ this.connectPromises.get(payload.shardId)?.();
+ this.connectPromises.delete(payload.shardId);
+ break;
+ }
+ case 1 /* Destroyed */: {
+ this.destroyPromises.get(payload.shardId)?.();
+ this.destroyPromises.delete(payload.shardId);
+ break;
+ }
+ case 2 /* Event */: {
+ this.manager.emit(payload.event, { ...payload.data, shardId: payload.shardId });
+ break;
+ }
+ case 3 /* RetrieveSessionInfo */: {
+ const session = await this.manager.options.retrieveSessionInfo(payload.shardId);
+ const response = {
+ op: 3 /* SessionInfoResponse */,
+ nonce: payload.nonce,
+ session
+ };
+ worker.postMessage(response);
+ break;
+ }
+ case 4 /* UpdateSessionInfo */: {
+ await this.manager.options.updateSessionInfo(payload.shardId, payload.session);
+ break;
+ }
+ case 5 /* WaitForIdentify */: {
+ const throttler = await this.ensureThrottler();
+ try {
+ const controller = new AbortController();
+ this.waitForIdentifyControllers.set(payload.nonce, controller);
+ await throttler.waitForIdentify(payload.shardId, controller.signal);
+ } catch {
+ return;
+ }
+ const response = {
+ op: 4 /* ShardIdentifyResponse */,
+ nonce: payload.nonce,
+ ok: true
+ };
+ worker.postMessage(response);
+ break;
+ }
+ case 6 /* FetchStatusResponse */: {
+ this.fetchStatusPromises.get(payload.nonce)?.(payload.status);
+ this.fetchStatusPromises.delete(payload.nonce);
+ break;
+ }
+ case 7 /* WorkerReady */: {
+ break;
+ }
+ case 8 /* CancelIdentify */: {
+ this.waitForIdentifyControllers.get(payload.nonce)?.abort();
+ this.waitForIdentifyControllers.delete(payload.nonce);
+ const response = {
+ op: 4 /* ShardIdentifyResponse */,
+ nonce: payload.nonce,
+ ok: false
+ };
+ worker.postMessage(response);
+ break;
+ }
+ }
+ }
+ async ensureThrottler() {
+ this.throttler ??= await this.manager.options.buildIdentifyThrottler(this.manager);
+ return this.throttler;
+ }
+};
+
+// src/strategies/context/WorkerContextFetchingStrategy.ts
+var WorkerContextFetchingStrategy = class {
+ constructor(options) {
+ this.options = options;
+ if (isMainThread) {
+ throw new Error("Cannot instantiate WorkerContextFetchingStrategy on the main thread");
+ }
+ parentPort.on("message", (payload) => {
+ if (payload.op === 3 /* SessionInfoResponse */) {
+ this.sessionPromises.get(payload.nonce)?.(payload.session);
+ this.sessionPromises.delete(payload.nonce);
+ }
+ if (payload.op === 4 /* ShardIdentifyResponse */) {
+ const promise = this.waitForIdentifyPromises.get(payload.nonce);
+ if (payload.ok) {
+ promise?.resolve();
+ } else {
+ promise?.reject(promise.signal.reason);
+ }
+ this.waitForIdentifyPromises.delete(payload.nonce);
+ }
+ });
+ }
+ static {
+ __name(this, "WorkerContextFetchingStrategy");
+ }
+ sessionPromises = new Collection2();
+ waitForIdentifyPromises = new Collection2();
+ async retrieveSessionInfo(shardId) {
+ const nonce = Math.random();
+ const payload = {
+ op: 3 /* RetrieveSessionInfo */,
+ shardId,
+ nonce
+ };
+ const promise = new Promise((resolve2) => this.sessionPromises.set(nonce, resolve2));
+ parentPort.postMessage(payload);
+ return promise;
+ }
+ updateSessionInfo(shardId, sessionInfo) {
+ const payload = {
+ op: 4 /* UpdateSessionInfo */,
+ shardId,
+ session: sessionInfo
+ };
+ parentPort.postMessage(payload);
+ }
+ async waitForIdentify(shardId, signal) {
+ const nonce = Math.random();
+ const payload = {
+ op: 5 /* WaitForIdentify */,
+ nonce,
+ shardId
+ };
+ const promise = new Promise(
+ (resolve2, reject) => (
+ // eslint-disable-next-line no-promise-executor-return
+ this.waitForIdentifyPromises.set(nonce, { signal, resolve: resolve2, reject })
+ )
+ );
+ parentPort.postMessage(payload);
+ const listener = /* @__PURE__ */ __name(() => {
+ const payload2 = {
+ op: 8 /* CancelIdentify */,
+ nonce
+ };
+ parentPort.postMessage(payload2);
+ }, "listener");
+ signal.addEventListener("abort", listener);
+ try {
+ await promise;
+ } finally {
+ signal.removeEventListener("abort", listener);
+ }
+ }
+};
+
+// src/strategies/sharding/SimpleShardingStrategy.ts
+import { Collection as Collection6 } from "@discordjs/collection";
+
+// src/ws/WebSocketShard.ts
+import { Buffer as Buffer2 } from "node:buffer";
+import { once as once2 } from "node:events";
+import { clearInterval, clearTimeout, setInterval, setTimeout } from "node:timers";
+import { setTimeout as sleep2 } from "node:timers/promises";
+import { URLSearchParams } from "node:url";
+import { TextDecoder } from "node:util";
+import { inflate } from "node:zlib";
+import { Collection as Collection5 } from "@discordjs/collection";
+import { lazy as lazy2 } from "@discordjs/util";
+import { AsyncQueue as AsyncQueue2 } from "@sapphire/async-queue";
+import { AsyncEventEmitter } from "@vladfrangu/async_event_emitter";
+import {
+ GatewayCloseCodes,
+ GatewayDispatchEvents,
+ GatewayOpcodes as GatewayOpcodes2
+} from "discord-api-types/v10";
+import { WebSocket } from "ws";
+
+// src/utils/constants.ts
+import process from "node:process";
+import { Collection as Collection4 } from "@discordjs/collection";
+import { lazy } from "@discordjs/util";
+import { APIVersion, GatewayOpcodes } from "discord-api-types/v10";
+
+// src/throttling/SimpleIdentifyThrottler.ts
+import { setTimeout as sleep } from "node:timers/promises";
+import { Collection as Collection3 } from "@discordjs/collection";
+import { AsyncQueue } from "@sapphire/async-queue";
+var SimpleIdentifyThrottler = class {
+ constructor(maxConcurrency) {
+ this.maxConcurrency = maxConcurrency;
+ }
+ static {
+ __name(this, "SimpleIdentifyThrottler");
+ }
+ states = new Collection3();
+ /**
+ * {@inheritDoc IIdentifyThrottler.waitForIdentify}
+ */
+ async waitForIdentify(shardId, signal) {
+ const key = shardId % this.maxConcurrency;
+ const state = this.states.ensure(key, () => {
+ return {
+ queue: new AsyncQueue(),
+ resetsAt: Number.POSITIVE_INFINITY
+ };
+ });
+ await state.queue.wait({ signal });
+ try {
+ const diff = state.resetsAt - Date.now();
+ if (diff <= 5e3) {
+ const time = diff + Math.random() * 1500;
+ await sleep(time);
+ }
+ state.resetsAt = Date.now() + 5e3;
+ } finally {
+ state.queue.shift();
+ }
+ }
+};
+
+// src/utils/constants.ts
+var Encoding = /* @__PURE__ */ ((Encoding2) => {
+ Encoding2["JSON"] = "json";
+ return Encoding2;
+})(Encoding || {});
+var CompressionMethod = /* @__PURE__ */ ((CompressionMethod2) => {
+ CompressionMethod2["ZlibStream"] = "zlib-stream";
+ return CompressionMethod2;
+})(CompressionMethod || {});
+var DefaultDeviceProperty = `@discordjs/ws 1.0.1`;
+var getDefaultSessionStore = lazy(() => new Collection4());
+var DefaultWebSocketManagerOptions = {
+ async buildIdentifyThrottler(manager) {
+ const info = await manager.fetchGatewayInformation();
+ return new SimpleIdentifyThrottler(info.session_start_limit.max_concurrency);
+ },
+ buildStrategy: (manager) => new SimpleShardingStrategy(manager),
+ shardCount: null,
+ shardIds: null,
+ largeThreshold: null,
+ initialPresence: null,
+ identifyProperties: {
+ browser: DefaultDeviceProperty,
+ device: DefaultDeviceProperty,
+ os: process.platform
+ },
+ version: APIVersion,
+ encoding: "json" /* JSON */,
+ compression: null,
+ retrieveSessionInfo(shardId) {
+ const store = getDefaultSessionStore();
+ return store.get(shardId) ?? null;
+ },
+ updateSessionInfo(shardId, info) {
+ const store = getDefaultSessionStore();
+ if (info) {
+ store.set(shardId, info);
+ } else {
+ store.delete(shardId);
+ }
+ },
+ handshakeTimeout: 3e4,
+ helloTimeout: 6e4,
+ readyTimeout: 15e3
+};
+var ImportantGatewayOpcodes = /* @__PURE__ */ new Set([
+ GatewayOpcodes.Heartbeat,
+ GatewayOpcodes.Identify,
+ GatewayOpcodes.Resume
+]);
+function getInitialSendRateLimitState() {
+ return {
+ remaining: 120,
+ resetAt: Date.now() + 6e4
+ };
+}
+__name(getInitialSendRateLimitState, "getInitialSendRateLimitState");
+
+// src/ws/WebSocketShard.ts
+var getZlibSync = lazy2(async () => import("zlib-sync").then((mod) => mod.default).catch(() => null));
+var WebSocketShardEvents = /* @__PURE__ */ ((WebSocketShardEvents2) => {
+ WebSocketShardEvents2["Closed"] = "closed";
+ WebSocketShardEvents2["Debug"] = "debug";
+ WebSocketShardEvents2["Dispatch"] = "dispatch";
+ WebSocketShardEvents2["Error"] = "error";
+ WebSocketShardEvents2["HeartbeatComplete"] = "heartbeat";
+ WebSocketShardEvents2["Hello"] = "hello";
+ WebSocketShardEvents2["Ready"] = "ready";
+ WebSocketShardEvents2["Resumed"] = "resumed";
+ return WebSocketShardEvents2;
+})(WebSocketShardEvents || {});
+var WebSocketShardStatus = /* @__PURE__ */ ((WebSocketShardStatus2) => {
+ WebSocketShardStatus2[WebSocketShardStatus2["Idle"] = 0] = "Idle";
+ WebSocketShardStatus2[WebSocketShardStatus2["Connecting"] = 1] = "Connecting";
+ WebSocketShardStatus2[WebSocketShardStatus2["Resuming"] = 2] = "Resuming";
+ WebSocketShardStatus2[WebSocketShardStatus2["Ready"] = 3] = "Ready";
+ return WebSocketShardStatus2;
+})(WebSocketShardStatus || {});
+var WebSocketShardDestroyRecovery = /* @__PURE__ */ ((WebSocketShardDestroyRecovery2) => {
+ WebSocketShardDestroyRecovery2[WebSocketShardDestroyRecovery2["Reconnect"] = 0] = "Reconnect";
+ WebSocketShardDestroyRecovery2[WebSocketShardDestroyRecovery2["Resume"] = 1] = "Resume";
+ return WebSocketShardDestroyRecovery2;
+})(WebSocketShardDestroyRecovery || {});
+var CloseCodes = /* @__PURE__ */ ((CloseCodes2) => {
+ CloseCodes2[CloseCodes2["Normal"] = 1e3] = "Normal";
+ CloseCodes2[CloseCodes2["Resuming"] = 4200] = "Resuming";
+ return CloseCodes2;
+})(CloseCodes || {});
+var WebSocketConstructor = WebSocket;
+var WebSocketShard = class extends AsyncEventEmitter {
+ static {
+ __name(this, "WebSocketShard");
+ }
+ connection = null;
+ useIdentifyCompress = false;
+ inflate = null;
+ textDecoder = new TextDecoder();
+ replayedEvents = 0;
+ isAck = true;
+ sendRateLimitState = getInitialSendRateLimitState();
+ initialHeartbeatTimeoutController = null;
+ heartbeatInterval = null;
+ lastHeartbeatAt = -1;
+ // Indicates whether the shard has already resolved its original connect() call
+ initialConnectResolved = false;
+ // Indicates if we failed to connect to the ws url (ECONNREFUSED/ECONNRESET)
+ failedToConnectDueToNetworkError = false;
+ sendQueue = new AsyncQueue2();
+ timeoutAbortControllers = new Collection5();
+ strategy;
+ id;
+ #status = 0 /* Idle */;
+ get status() {
+ return this.#status;
+ }
+ constructor(strategy, id) {
+ super();
+ this.strategy = strategy;
+ this.id = id;
+ }
+ async connect() {
+ const controller = new AbortController();
+ let promise;
+ if (!this.initialConnectResolved) {
+ promise = Promise.race([
+ once2(this, "ready" /* Ready */, { signal: controller.signal }),
+ once2(this, "resumed" /* Resumed */, { signal: controller.signal })
+ ]);
+ }
+ void this.internalConnect();
+ try {
+ await promise;
+ } catch ({ error }) {
+ throw error;
+ } finally {
+ controller.abort();
+ }
+ this.initialConnectResolved = true;
+ }
+ async internalConnect() {
+ if (this.#status !== 0 /* Idle */) {
+ throw new Error("Tried to connect a shard that wasn't idle");
+ }
+ const { version: version2, encoding, compression } = this.strategy.options;
+ const params = new URLSearchParams({ v: version2, encoding });
+ if (compression) {
+ const zlib = await getZlibSync();
+ if (zlib) {
+ params.append("compress", compression);
+ this.inflate = new zlib.Inflate({
+ chunkSize: 65535,
+ to: "string"
+ });
+ } else if (!this.useIdentifyCompress) {
+ this.useIdentifyCompress = true;
+ console.warn(
+ "WebSocketShard: Compression is enabled but zlib-sync is not installed, falling back to identify compress"
+ );
+ }
+ }
+ const session = await this.strategy.retrieveSessionInfo(this.id);
+ const url = `${session?.resumeURL ?? this.strategy.options.gatewayInformation.url}?${params.toString()}`;
+ this.debug([`Connecting to ${url}`]);
+ const connection = new WebSocketConstructor(url, {
+ handshakeTimeout: this.strategy.options.handshakeTimeout ?? void 0
+ });
+ connection.binaryType = "arraybuffer";
+ connection.onmessage = (event) => {
+ void this.onMessage(event.data, event.data instanceof ArrayBuffer);
+ };
+ connection.onerror = (event) => {
+ this.onError(event.error);
+ };
+ connection.onclose = (event) => {
+ void this.onClose(event.code);
+ };
+ this.connection = connection;
+ this.#status = 1 /* Connecting */;
+ this.sendRateLimitState = getInitialSendRateLimitState();
+ const { ok } = await this.waitForEvent("hello" /* Hello */, this.strategy.options.helloTimeout);
+ if (!ok) {
+ return;
+ }
+ if (session?.shardCount === this.strategy.options.shardCount) {
+ await this.resume(session);
+ } else {
+ await this.identify();
+ }
+ }
+ async destroy(options = {}) {
+ if (this.#status === 0 /* Idle */) {
+ this.debug(["Tried to destroy a shard that was idle"]);
+ return;
+ }
+ if (!options.code) {
+ options.code = options.recover === 1 /* Resume */ ? 4200 /* Resuming */ : 1e3 /* Normal */;
+ }
+ this.debug([
+ "Destroying shard",
+ `Reason: ${options.reason ?? "none"}`,
+ `Code: ${options.code}`,
+ `Recover: ${options.recover === void 0 ? "none" : WebSocketShardDestroyRecovery[options.recover]}`
+ ]);
+ this.isAck = true;
+ if (this.heartbeatInterval) {
+ clearInterval(this.heartbeatInterval);
+ }
+ if (this.initialHeartbeatTimeoutController) {
+ this.initialHeartbeatTimeoutController.abort();
+ this.initialHeartbeatTimeoutController = null;
+ }
+ this.lastHeartbeatAt = -1;
+ for (const controller of this.timeoutAbortControllers.values()) {
+ controller.abort();
+ }
+ this.timeoutAbortControllers.clear();
+ this.failedToConnectDueToNetworkError = false;
+ if (options.recover !== 1 /* Resume */) {
+ await this.strategy.updateSessionInfo(this.id, null);
+ }
+ if (this.connection) {
+ this.connection.onmessage = null;
+ this.connection.onclose = null;
+ const shouldClose = this.connection.readyState === WebSocket.OPEN;
+ this.debug([
+ "Connection status during destroy",
+ `Needs closing: ${shouldClose}`,
+ `Ready state: ${this.connection.readyState}`
+ ]);
+ if (shouldClose) {
+ let outerResolve;
+ const promise = new Promise((resolve2) => {
+ outerResolve = resolve2;
+ });
+ this.connection.onclose = outerResolve;
+ this.connection.close(options.code, options.reason);
+ await promise;
+ this.emit("closed" /* Closed */, { code: options.code });
+ }
+ this.connection.onerror = null;
+ } else {
+ this.debug(["Destroying a shard that has no connection; please open an issue on GitHub"]);
+ }
+ this.#status = 0 /* Idle */;
+ if (options.recover !== void 0) {
+ await sleep2(500);
+ return this.internalConnect();
+ }
+ }
+ async waitForEvent(event, timeoutDuration) {
+ this.debug([`Waiting for event ${event} ${timeoutDuration ? `for ${timeoutDuration}ms` : "indefinitely"}`]);
+ const timeoutController = new AbortController();
+ const timeout = timeoutDuration ? setTimeout(() => timeoutController.abort(), timeoutDuration).unref() : null;
+ this.timeoutAbortControllers.set(event, timeoutController);
+ const closeController = new AbortController();
+ try {
+ const closed = await Promise.race([
+ once2(this, event, { signal: timeoutController.signal }).then(() => false),
+ once2(this, "closed" /* Closed */, { signal: closeController.signal }).then(() => true)
+ ]);
+ return { ok: !closed };
+ } catch {
+ void this.destroy({
+ code: 1e3 /* Normal */,
+ reason: "Something timed out or went wrong while waiting for an event",
+ recover: 0 /* Reconnect */
+ });
+ return { ok: false };
+ } finally {
+ if (timeout) {
+ clearTimeout(timeout);
+ }
+ this.timeoutAbortControllers.delete(event);
+ if (!closeController.signal.aborted) {
+ closeController.abort();
+ }
+ }
+ }
+ async send(payload) {
+ if (!this.connection) {
+ throw new Error("WebSocketShard wasn't connected");
+ }
+ if (this.#status !== 3 /* Ready */ && !ImportantGatewayOpcodes.has(payload.op)) {
+ this.debug(["Tried to send a non-crucial payload before the shard was ready, waiting"]);
+ try {
+ await once2(this, "ready" /* Ready */);
+ } catch {
+ return this.send(payload);
+ }
+ }
+ await this.sendQueue.wait();
+ if (--this.sendRateLimitState.remaining <= 0) {
+ const now = Date.now();
+ if (this.sendRateLimitState.resetAt > now) {
+ const sleepFor = this.sendRateLimitState.resetAt - now;
+ this.debug([`Was about to hit the send rate limit, sleeping for ${sleepFor}ms`]);
+ const controller = new AbortController();
+ const interrupted = await Promise.race([
+ sleep2(sleepFor).then(() => false),
+ once2(this, "closed" /* Closed */, { signal: controller.signal }).then(() => true)
+ ]);
+ if (interrupted) {
+ this.debug(["Connection closed while waiting for the send rate limit to reset, re-queueing payload"]);
+ this.sendQueue.shift();
+ return this.send(payload);
+ }
+ controller.abort();
+ }
+ this.sendRateLimitState = getInitialSendRateLimitState();
+ }
+ this.sendQueue.shift();
+ this.connection.send(JSON.stringify(payload));
+ }
+ async identify() {
+ this.debug(["Waiting for identify throttle"]);
+ const controller = new AbortController();
+ const closeHandler = /* @__PURE__ */ __name(() => {
+ controller.abort();
+ }, "closeHandler");
+ this.on("closed" /* Closed */, closeHandler);
+ try {
+ await this.strategy.waitForIdentify(this.id, controller.signal);
+ } catch {
+ if (controller.signal.aborted) {
+ this.debug(["Was waiting for an identify, but the shard closed in the meantime"]);
+ return;
+ }
+ this.debug([
+ "IContextFetchingStrategy#waitForIdentify threw an unknown error.",
+ "If you're using a custom strategy, this is probably nothing to worry about.",
+ "If you're not, please open an issue on GitHub."
+ ]);
+ await this.destroy({
+ reason: "Identify throttling logic failed",
+ recover: 1 /* Resume */
+ });
+ } finally {
+ this.off("closed" /* Closed */, closeHandler);
+ }
+ this.debug([
+ "Identifying",
+ `shard id: ${this.id.toString()}`,
+ `shard count: ${this.strategy.options.shardCount}`,
+ `intents: ${this.strategy.options.intents}`,
+ `compression: ${this.inflate ? "zlib-stream" : this.useIdentifyCompress ? "identify" : "none"}`
+ ]);
+ const d = {
+ token: this.strategy.options.token,
+ properties: this.strategy.options.identifyProperties,
+ intents: this.strategy.options.intents,
+ compress: this.useIdentifyCompress,
+ shard: [this.id, this.strategy.options.shardCount]
+ };
+ if (this.strategy.options.largeThreshold) {
+ d.large_threshold = this.strategy.options.largeThreshold;
+ }
+ if (this.strategy.options.initialPresence) {
+ d.presence = this.strategy.options.initialPresence;
+ }
+ await this.send({
+ op: GatewayOpcodes2.Identify,
+ d
+ });
+ await this.waitForEvent("ready" /* Ready */, this.strategy.options.readyTimeout);
+ }
+ async resume(session) {
+ this.debug([
+ "Resuming session",
+ `resume url: ${session.resumeURL}`,
+ `sequence: ${session.sequence}`,
+ `shard id: ${this.id.toString()}`
+ ]);
+ this.#status = 2 /* Resuming */;
+ this.replayedEvents = 0;
+ return this.send({
+ op: GatewayOpcodes2.Resume,
+ d: {
+ token: this.strategy.options.token,
+ seq: session.sequence,
+ session_id: session.sessionId
+ }
+ });
+ }
+ async heartbeat(requested = false) {
+ if (!this.isAck && !requested) {
+ return this.destroy({ reason: "Zombie connection", recover: 1 /* Resume */ });
+ }
+ const session = await this.strategy.retrieveSessionInfo(this.id);
+ await this.send({
+ op: GatewayOpcodes2.Heartbeat,
+ d: session?.sequence ?? null
+ });
+ this.lastHeartbeatAt = Date.now();
+ this.isAck = false;
+ }
+ async unpackMessage(data, isBinary) {
+ if (!isBinary) {
+ try {
+ return JSON.parse(data);
+ } catch {
+ return null;
+ }
+ }
+ const decompressable = new Uint8Array(data);
+ if (this.useIdentifyCompress) {
+ return new Promise((resolve2, reject) => {
+ inflate(decompressable, { chunkSize: 65535 }, (err, result) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+ resolve2(JSON.parse(this.textDecoder.decode(result)));
+ });
+ });
+ }
+ if (this.inflate) {
+ const l = decompressable.length;
+ const flush = l >= 4 && decompressable[l - 4] === 0 && decompressable[l - 3] === 0 && decompressable[l - 2] === 255 && decompressable[l - 1] === 255;
+ const zlib = await getZlibSync();
+ this.inflate.push(Buffer2.from(decompressable), flush ? zlib.Z_SYNC_FLUSH : zlib.Z_NO_FLUSH);
+ if (this.inflate.err) {
+ this.emit("error" /* Error */, {
+ error: new Error(`${this.inflate.err}${this.inflate.msg ? `: ${this.inflate.msg}` : ""}`)
+ });
+ }
+ if (!flush) {
+ return null;
+ }
+ const { result } = this.inflate;
+ if (!result) {
+ return null;
+ }
+ return JSON.parse(typeof result === "string" ? result : this.textDecoder.decode(result));
+ }
+ this.debug([
+ "Received a message we were unable to decompress",
+ `isBinary: ${isBinary.toString()}`,
+ `useIdentifyCompress: ${this.useIdentifyCompress.toString()}`,
+ `inflate: ${Boolean(this.inflate).toString()}`
+ ]);
+ return null;
+ }
+ async onMessage(data, isBinary) {
+ const payload = await this.unpackMessage(data, isBinary);
+ if (!payload) {
+ return;
+ }
+ switch (payload.op) {
+ case GatewayOpcodes2.Dispatch: {
+ if (this.#status === 2 /* Resuming */) {
+ this.replayedEvents++;
+ }
+ switch (payload.t) {
+ case GatewayDispatchEvents.Ready: {
+ this.#status = 3 /* Ready */;
+ const session2 = {
+ sequence: payload.s,
+ sessionId: payload.d.session_id,
+ shardId: this.id,
+ shardCount: this.strategy.options.shardCount,
+ resumeURL: payload.d.resume_gateway_url
+ };
+ await this.strategy.updateSessionInfo(this.id, session2);
+ this.emit("ready" /* Ready */, { data: payload.d });
+ break;
+ }
+ case GatewayDispatchEvents.Resumed: {
+ this.#status = 3 /* Ready */;
+ this.debug([`Resumed and replayed ${this.replayedEvents} events`]);
+ this.emit("resumed" /* Resumed */);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ const session = await this.strategy.retrieveSessionInfo(this.id);
+ if (session) {
+ if (payload.s > session.sequence) {
+ await this.strategy.updateSessionInfo(this.id, { ...session, sequence: payload.s });
+ }
+ } else {
+ this.debug([
+ `Received a ${payload.t} event but no session is available. Session information cannot be re-constructed in this state without a full reconnect`
+ ]);
+ }
+ this.emit("dispatch" /* Dispatch */, { data: payload });
+ break;
+ }
+ case GatewayOpcodes2.Heartbeat: {
+ await this.heartbeat(true);
+ break;
+ }
+ case GatewayOpcodes2.Reconnect: {
+ await this.destroy({
+ reason: "Told to reconnect by Discord",
+ recover: 1 /* Resume */
+ });
+ break;
+ }
+ case GatewayOpcodes2.InvalidSession: {
+ this.debug([`Invalid session; will attempt to resume: ${payload.d.toString()}`]);
+ const session = await this.strategy.retrieveSessionInfo(this.id);
+ if (payload.d && session) {
+ await this.resume(session);
+ } else {
+ await this.destroy({
+ reason: "Invalid session",
+ recover: 0 /* Reconnect */
+ });
+ }
+ break;
+ }
+ case GatewayOpcodes2.Hello: {
+ this.emit("hello" /* Hello */);
+ const jitter = Math.random();
+ const firstWait = Math.floor(payload.d.heartbeat_interval * jitter);
+ this.debug([`Preparing first heartbeat of the connection with a jitter of ${jitter}; waiting ${firstWait}ms`]);
+ try {
+ const controller = new AbortController();
+ this.initialHeartbeatTimeoutController = controller;
+ await sleep2(firstWait, void 0, { signal: controller.signal });
+ } catch {
+ this.debug(["Cancelled initial heartbeat due to #destroy being called"]);
+ return;
+ } finally {
+ this.initialHeartbeatTimeoutController = null;
+ }
+ await this.heartbeat();
+ this.debug([`First heartbeat sent, starting to beat every ${payload.d.heartbeat_interval}ms`]);
+ this.heartbeatInterval = setInterval(() => void this.heartbeat(), payload.d.heartbeat_interval);
+ break;
+ }
+ case GatewayOpcodes2.HeartbeatAck: {
+ this.isAck = true;
+ const ackAt = Date.now();
+ this.emit("heartbeat" /* HeartbeatComplete */, {
+ ackAt,
+ heartbeatAt: this.lastHeartbeatAt,
+ latency: ackAt - this.lastHeartbeatAt
+ });
+ break;
+ }
+ }
+ }
+ onError(error) {
+ if ("code" in error && ["ECONNRESET", "ECONNREFUSED"].includes(error.code)) {
+ this.debug(["Failed to connect to the gateway URL specified due to a network error"]);
+ this.failedToConnectDueToNetworkError = true;
+ return;
+ }
+ this.emit("error" /* Error */, { error });
+ }
+ async onClose(code) {
+ this.emit("closed" /* Closed */, { code });
+ switch (code) {
+ case 1e3 /* Normal */: {
+ return this.destroy({
+ code,
+ reason: "Got disconnected by Discord",
+ recover: 0 /* Reconnect */
+ });
+ }
+ case 4200 /* Resuming */: {
+ break;
+ }
+ case GatewayCloseCodes.UnknownError: {
+ this.debug([`An unknown error occurred: ${code}`]);
+ return this.destroy({ code, recover: 1 /* Resume */ });
+ }
+ case GatewayCloseCodes.UnknownOpcode: {
+ this.debug(["An invalid opcode was sent to Discord."]);
+ return this.destroy({ code, recover: 1 /* Resume */ });
+ }
+ case GatewayCloseCodes.DecodeError: {
+ this.debug(["An invalid payload was sent to Discord."]);
+ return this.destroy({ code, recover: 1 /* Resume */ });
+ }
+ case GatewayCloseCodes.NotAuthenticated: {
+ this.debug(["A request was somehow sent before the identify/resume payload."]);
+ return this.destroy({ code, recover: 0 /* Reconnect */ });
+ }
+ case GatewayCloseCodes.AuthenticationFailed: {
+ this.emit("error" /* Error */, {
+ error: new Error("Authentication failed")
+ });
+ return this.destroy({ code });
+ }
+ case GatewayCloseCodes.AlreadyAuthenticated: {
+ this.debug(["More than one auth payload was sent."]);
+ return this.destroy({ code, recover: 0 /* Reconnect */ });
+ }
+ case GatewayCloseCodes.InvalidSeq: {
+ this.debug(["An invalid sequence was sent."]);
+ return this.destroy({ code, recover: 0 /* Reconnect */ });
+ }
+ case GatewayCloseCodes.RateLimited: {
+ this.debug(["The WebSocket rate limit has been hit, this should never happen"]);
+ return this.destroy({ code, recover: 0 /* Reconnect */ });
+ }
+ case GatewayCloseCodes.SessionTimedOut: {
+ this.debug(["Session timed out."]);
+ return this.destroy({ code, recover: 1 /* Resume */ });
+ }
+ case GatewayCloseCodes.InvalidShard: {
+ this.emit("error" /* Error */, {
+ error: new Error("Invalid shard")
+ });
+ return this.destroy({ code });
+ }
+ case GatewayCloseCodes.ShardingRequired: {
+ this.emit("error" /* Error */, {
+ error: new Error("Sharding is required")
+ });
+ return this.destroy({ code });
+ }
+ case GatewayCloseCodes.InvalidAPIVersion: {
+ this.emit("error" /* Error */, {
+ error: new Error("Used an invalid API version")
+ });
+ return this.destroy({ code });
+ }
+ case GatewayCloseCodes.InvalidIntents: {
+ this.emit("error" /* Error */, {
+ error: new Error("Used invalid intents")
+ });
+ return this.destroy({ code });
+ }
+ case GatewayCloseCodes.DisallowedIntents: {
+ this.emit("error" /* Error */, {
+ error: new Error("Used disallowed intents")
+ });
+ return this.destroy({ code });
+ }
+ default: {
+ this.debug([
+ `The gateway closed with an unexpected code ${code}, attempting to ${this.failedToConnectDueToNetworkError ? "reconnect" : "resume"}.`
+ ]);
+ return this.destroy({
+ code,
+ recover: this.failedToConnectDueToNetworkError ? 0 /* Reconnect */ : 1 /* Resume */
+ });
+ }
+ }
+ }
+ debug(messages) {
+ const message = `${messages[0]}${messages.length > 1 ? `
+${messages.slice(1).map((m) => ` ${m}`).join("\n")}` : ""}`;
+ this.emit("debug" /* Debug */, { message });
+ }
+};
+
+// src/strategies/sharding/SimpleShardingStrategy.ts
+var SimpleShardingStrategy = class {
+ static {
+ __name(this, "SimpleShardingStrategy");
+ }
+ manager;
+ shards = new Collection6();
+ constructor(manager) {
+ this.manager = manager;
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.spawn}
+ */
+ async spawn(shardIds) {
+ const strategyOptions = await managerToFetchingStrategyOptions(this.manager);
+ for (const shardId of shardIds) {
+ const strategy = new SimpleContextFetchingStrategy(this.manager, strategyOptions);
+ const shard = new WebSocketShard(strategy, shardId);
+ for (const event of Object.values(WebSocketShardEvents)) {
+ shard.on(event, (payload) => this.manager.emit(event, { ...payload, shardId }));
+ }
+ this.shards.set(shardId, shard);
+ }
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.connect}
+ */
+ async connect() {
+ const promises = [];
+ for (const shard of this.shards.values()) {
+ promises.push(shard.connect());
+ }
+ await Promise.all(promises);
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.destroy}
+ */
+ async destroy(options) {
+ const promises = [];
+ for (const shard of this.shards.values()) {
+ promises.push(shard.destroy(options));
+ }
+ await Promise.all(promises);
+ this.shards.clear();
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.send}
+ */
+ async send(shardId, payload) {
+ const shard = this.shards.get(shardId);
+ if (!shard) {
+ throw new RangeError(`Shard ${shardId} not found`);
+ }
+ return shard.send(payload);
+ }
+ /**
+ * {@inheritDoc IShardingStrategy.fetchStatus}
+ */
+ async fetchStatus() {
+ return this.shards.mapValues((shard) => shard.status);
+ }
+};
+
+// src/utils/WorkerBootstrapper.ts
+import { isMainThread as isMainThread2, parentPort as parentPort2, workerData } from "node:worker_threads";
+import { Collection as Collection7 } from "@discordjs/collection";
+var WorkerBootstrapper = class {
+ static {
+ __name(this, "WorkerBootstrapper");
+ }
+ /**
+ * The data passed to the worker thread
+ */
+ data = workerData;
+ /**
+ * The shards that are managed by this worker
+ */
+ shards = new Collection7();
+ constructor() {
+ if (isMainThread2) {
+ throw new Error("Expected WorkerBootstrap to not be used within the main thread");
+ }
+ }
+ /**
+ * Helper method to initiate a shard's connection process
+ */
+ async connect(shardId) {
+ const shard = this.shards.get(shardId);
+ if (!shard) {
+ throw new RangeError(`Shard ${shardId} does not exist`);
+ }
+ await shard.connect();
+ }
+ /**
+ * Helper method to destroy a shard
+ */
+ async destroy(shardId, options) {
+ const shard = this.shards.get(shardId);
+ if (!shard) {
+ throw new RangeError(`Shard ${shardId} does not exist`);
+ }
+ await shard.destroy(options);
+ }
+ /**
+ * Helper method to attach event listeners to the parentPort
+ */
+ setupThreadEvents() {
+ parentPort2.on("messageerror", (err) => {
+ throw err;
+ }).on("message", async (payload) => {
+ switch (payload.op) {
+ case 0 /* Connect */: {
+ await this.connect(payload.shardId);
+ const response = {
+ op: 0 /* Connected */,
+ shardId: payload.shardId
+ };
+ parentPort2.postMessage(response);
+ break;
+ }
+ case 1 /* Destroy */: {
+ await this.destroy(payload.shardId, payload.options);
+ const response = {
+ op: 1 /* Destroyed */,
+ shardId: payload.shardId
+ };
+ parentPort2.postMessage(response);
+ break;
+ }
+ case 2 /* Send */: {
+ const shard = this.shards.get(payload.shardId);
+ if (!shard) {
+ throw new RangeError(`Shard ${payload.shardId} does not exist`);
+ }
+ await shard.send(payload.payload);
+ break;
+ }
+ case 3 /* SessionInfoResponse */: {
+ break;
+ }
+ case 4 /* ShardIdentifyResponse */: {
+ break;
+ }
+ case 5 /* FetchStatus */: {
+ const shard = this.shards.get(payload.shardId);
+ if (!shard) {
+ throw new Error(`Shard ${payload.shardId} does not exist`);
+ }
+ const response = {
+ op: 6 /* FetchStatusResponse */,
+ status: shard.status,
+ nonce: payload.nonce
+ };
+ parentPort2.postMessage(response);
+ break;
+ }
+ }
+ });
+ }
+ /**
+ * Bootstraps the worker thread with the provided options
+ */
+ async bootstrap(options = {}) {
+ for (const shardId of this.data.shardIds) {
+ const shard = new WebSocketShard(new WorkerContextFetchingStrategy(this.data), shardId);
+ for (const event of options.forwardEvents ?? Object.values(WebSocketShardEvents)) {
+ shard.on(event, (data) => {
+ const payload = {
+ op: 2 /* Event */,
+ event,
+ data,
+ shardId
+ };
+ parentPort2.postMessage(payload);
+ });
+ }
+ await options.shardCallback?.(shard);
+ this.shards.set(shardId, shard);
+ }
+ this.setupThreadEvents();
+ const message = {
+ op: 7 /* WorkerReady */
+ };
+ parentPort2.postMessage(message);
+ }
+};
+
+// src/ws/WebSocketManager.ts
+import { range } from "@discordjs/util";
+import { AsyncEventEmitter as AsyncEventEmitter2 } from "@vladfrangu/async_event_emitter";
+import {
+ Routes
+} from "discord-api-types/v10";
+var WebSocketManager = class extends AsyncEventEmitter2 {
+ static {
+ __name(this, "WebSocketManager");
+ }
+ /**
+ * The options being used by this manager
+ */
+ options;
+ /**
+ * Internal cache for a GET /gateway/bot result
+ */
+ gatewayInformation = null;
+ /**
+ * Internal cache for the shard ids
+ */
+ shardIds = null;
+ /**
+ * Strategy used to manage shards
+ *
+ * @defaultValue `SimpleShardingStrategy`
+ */
+ strategy;
+ constructor(options) {
+ super();
+ this.options = { ...DefaultWebSocketManagerOptions, ...options };
+ this.strategy = this.options.buildStrategy(this);
+ }
+ /**
+ * Fetches the gateway information from Discord - or returns it from cache if available
+ *
+ * @param force - Whether to ignore the cache and force a fresh fetch
+ */
+ async fetchGatewayInformation(force = false) {
+ if (this.gatewayInformation) {
+ if (this.gatewayInformation.expiresAt <= Date.now()) {
+ this.gatewayInformation = null;
+ } else if (!force) {
+ return this.gatewayInformation.data;
+ }
+ }
+ const data = await this.options.rest.get(Routes.gatewayBot());
+ this.gatewayInformation = { data, expiresAt: Date.now() + (data.session_start_limit.reset_after || 5e3) };
+ return this.gatewayInformation.data;
+ }
+ /**
+ * Updates your total shard count on-the-fly, spawning shards as needed
+ *
+ * @param shardCount - The new shard count to use
+ */
+ async updateShardCount(shardCount) {
+ await this.strategy.destroy({ reason: "User is adjusting their shards" });
+ this.options.shardCount = shardCount;
+ const shardIds = await this.getShardIds(true);
+ await this.strategy.spawn(shardIds);
+ return this;
+ }
+ /**
+ * Yields the total number of shards across for your bot, accounting for Discord recommendations
+ */
+ async getShardCount() {
+ if (this.options.shardCount) {
+ return this.options.shardCount;
+ }
+ const shardIds = await this.getShardIds();
+ return Math.max(...shardIds) + 1;
+ }
+ /**
+ * Yields the ids of the shards this manager should manage
+ */
+ async getShardIds(force = false) {
+ if (this.shardIds && !force) {
+ return this.shardIds;
+ }
+ let shardIds;
+ if (this.options.shardIds) {
+ if (Array.isArray(this.options.shardIds)) {
+ shardIds = this.options.shardIds;
+ } else {
+ const { start, end } = this.options.shardIds;
+ shardIds = [...range({ start, end: end + 1 })];
+ }
+ } else {
+ const data = await this.fetchGatewayInformation();
+ shardIds = [...range(this.options.shardCount ?? data.shards)];
+ }
+ this.shardIds = shardIds;
+ return shardIds;
+ }
+ async connect() {
+ const shardCount = await this.getShardCount();
+ const data = await this.fetchGatewayInformation();
+ if (data.session_start_limit.remaining < shardCount) {
+ throw new Error(
+ `Not enough sessions remaining to spawn ${shardCount} shards; only ${data.session_start_limit.remaining} remaining; resets at ${new Date(Date.now() + data.session_start_limit.reset_after).toISOString()}`
+ );
+ }
+ await this.updateShardCount(shardCount);
+ await this.strategy.connect();
+ }
+ destroy(options) {
+ return this.strategy.destroy(options);
+ }
+ send(shardId, payload) {
+ return this.strategy.send(shardId, payload);
+ }
+ fetchStatus() {
+ return this.strategy.fetchStatus();
+ }
+};
+
+// src/index.ts
+var version = "1.0.1";
+export {
+ CloseCodes,
+ CompressionMethod,
+ DefaultDeviceProperty,
+ DefaultWebSocketManagerOptions,
+ Encoding,
+ ImportantGatewayOpcodes,
+ SimpleContextFetchingStrategy,
+ SimpleIdentifyThrottler,
+ SimpleShardingStrategy,
+ WebSocketManager,
+ WebSocketShard,
+ WebSocketShardDestroyRecovery,
+ WebSocketShardEvents,
+ WebSocketShardStatus,
+ WorkerBootstrapper,
+ WorkerContextFetchingStrategy,
+ WorkerReceivePayloadOp,
+ WorkerSendPayloadOp,
+ WorkerShardingStrategy,
+ getInitialSendRateLimitState,
+ managerToFetchingStrategyOptions,
+ version
+};
+//# sourceMappingURL=index.mjs.map \ No newline at end of file