mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
refactor: use eslint-config-neon for packages. (#8579)
Co-authored-by: Noel <buechler.noel@outlook.com>
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/consistent-type-imports */
|
||||
import { REST } from '@discordjs/rest';
|
||||
import { MockAgent, Interceptable } from 'undici';
|
||||
import { MockAgent, type Interceptable } from 'undici';
|
||||
import { beforeEach, test, vi, expect } from 'vitest';
|
||||
import {
|
||||
managerToFetchingStrategyOptions,
|
||||
WorkerContextFetchingStrategy,
|
||||
WorkerRecievePayload,
|
||||
WorkerSendPayload,
|
||||
WebSocketManager,
|
||||
WorkerSendPayloadOp,
|
||||
WorkerRecievePayloadOp,
|
||||
} from '../../src';
|
||||
type WorkerRecievePayload,
|
||||
type WorkerSendPayload,
|
||||
} from '../../src/index.js';
|
||||
|
||||
let mockAgent: MockAgent;
|
||||
let mockPool: Interceptable;
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/* eslint-disable id-length */
|
||||
import { setImmediate } from 'node:timers';
|
||||
import { REST } from '@discordjs/rest';
|
||||
import {
|
||||
GatewayDispatchEvents,
|
||||
GatewayDispatchPayload,
|
||||
GatewayOpcodes,
|
||||
GatewaySendPayload,
|
||||
type GatewayDispatchPayload,
|
||||
type GatewaySendPayload,
|
||||
} from 'discord-api-types/v10';
|
||||
import { MockAgent, Interceptable } from 'undici';
|
||||
import { MockAgent, type Interceptable } from 'undici';
|
||||
import { beforeEach, test, vi, expect, afterEach } from 'vitest';
|
||||
import {
|
||||
WorkerRecievePayload,
|
||||
WorkerSendPayload,
|
||||
WebSocketManager,
|
||||
WorkerSendPayloadOp,
|
||||
WorkerRecievePayloadOp,
|
||||
WorkerShardingStrategy,
|
||||
WebSocketShardEvents,
|
||||
SessionInfo,
|
||||
} from '../../src';
|
||||
type WorkerRecievePayload,
|
||||
type WorkerSendPayload,
|
||||
type SessionInfo,
|
||||
} from '../../src/index.js';
|
||||
|
||||
let mockAgent: MockAgent;
|
||||
let mockPool: Interceptable;
|
||||
@@ -43,6 +45,7 @@ const sessionInfo: SessionInfo = {
|
||||
};
|
||||
|
||||
vi.mock('node:worker_threads', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
const { EventEmitter }: typeof import('node:events') = await vi.importActual('node:events');
|
||||
class MockWorker extends EventEmitter {
|
||||
public constructor(...args: any[]) {
|
||||
@@ -54,6 +57,7 @@ vi.mock('node:worker_threads', async () => {
|
||||
}
|
||||
|
||||
public postMessage(message: WorkerSendPayload) {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (message.op) {
|
||||
case WorkerSendPayloadOp.Connect: {
|
||||
const response: WorkerRecievePayload = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { setTimeout as sleep } from 'node:timers/promises';
|
||||
import { expect, Mock, test, vi } from 'vitest';
|
||||
import { IdentifyThrottler, WebSocketManager } from '../../src';
|
||||
import { expect, test, vi, type Mock } from 'vitest';
|
||||
import { IdentifyThrottler, type WebSocketManager } from '../../src/index.js';
|
||||
|
||||
vi.mock('node:timers/promises', () => ({
|
||||
setTimeout: vi.fn(),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { REST } from '@discordjs/rest';
|
||||
import { APIGatewayBotInfo, GatewayOpcodes, GatewaySendPayload } from 'discord-api-types/v10';
|
||||
import { MockAgent, Interceptable } from 'undici';
|
||||
import { GatewayOpcodes, type APIGatewayBotInfo, type GatewaySendPayload } from 'discord-api-types/v10';
|
||||
import { MockAgent, type Interceptable } from 'undici';
|
||||
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { IShardingStrategy, WebSocketManager } from '../../src';
|
||||
import { WebSocketManager, type IShardingStrategy } from '../../src/index.js';
|
||||
|
||||
vi.useFakeTimers();
|
||||
|
||||
@@ -80,7 +80,7 @@ test('fetch gateway information', async () => {
|
||||
})
|
||||
.reply(fetch);
|
||||
|
||||
NOW.mockReturnValue(Infinity);
|
||||
NOW.mockReturnValue(Number.POSITIVE_INFINITY);
|
||||
const cacheExpired = await manager.fetchGatewayInformation();
|
||||
expect(cacheExpired).toEqual(data);
|
||||
expect(fetch).toHaveBeenCalledOnce();
|
||||
@@ -171,8 +171,11 @@ test('it handles passing in both shardIds and shardCount', async () => {
|
||||
test('strategies', async () => {
|
||||
class MockStrategy implements IShardingStrategy {
|
||||
public spawn = vi.fn();
|
||||
|
||||
public connect = vi.fn();
|
||||
|
||||
public destroy = vi.fn();
|
||||
|
||||
public send = vi.fn();
|
||||
}
|
||||
|
||||
@@ -219,6 +222,7 @@ test('strategies', async () => {
|
||||
await manager.destroy(destroyOptions);
|
||||
expect(strategy.destroy).toHaveBeenCalledWith(destroyOptions);
|
||||
|
||||
// eslint-disable-next-line id-length
|
||||
const send: GatewaySendPayload = { op: GatewayOpcodes.RequestGuildMembers, d: { guild_id: '1234', limit: 0 } };
|
||||
await manager.send(0, send);
|
||||
expect(strategy.send).toHaveBeenCalledWith(0, send);
|
||||
|
||||
@@ -66,16 +66,10 @@
|
||||
"@favware/cliff-jumper": "^1.8.7",
|
||||
"@microsoft/api-extractor": "^7.29.5",
|
||||
"@types/node": "^16.11.56",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
||||
"@typescript-eslint/parser": "^5.36.1",
|
||||
"@vitest/coverage-c8": "^0.22.1",
|
||||
"downlevel-dts": "^0.10.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-marine": "^9.4.1",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-import-resolver-typescript": "^3.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"eslint-config-neon": "^0.1.23",
|
||||
"mock-socket": "^9.1.5",
|
||||
"prettier": "^2.7.1",
|
||||
"rollup-plugin-typescript2": "^0.33.0",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export * from './strategies/context/IContextFetchingStrategy';
|
||||
export * from './strategies/context/SimpleContextFetchingStrategy';
|
||||
export * from './strategies/context/WorkerContextFetchingStrategy';
|
||||
export * from './strategies/context/IContextFetchingStrategy.js';
|
||||
export * from './strategies/context/SimpleContextFetchingStrategy.js';
|
||||
export * from './strategies/context/WorkerContextFetchingStrategy.js';
|
||||
|
||||
export * from './strategies/sharding/IShardingStrategy';
|
||||
export * from './strategies/sharding/SimpleShardingStrategy';
|
||||
export * from './strategies/sharding/WorkerShardingStrategy';
|
||||
export * from './strategies/sharding/IShardingStrategy.js';
|
||||
export * from './strategies/sharding/SimpleShardingStrategy.js';
|
||||
export * from './strategies/sharding/WorkerShardingStrategy.js';
|
||||
|
||||
export * from './utils/constants';
|
||||
export * from './utils/IdentifyThrottler';
|
||||
export * from './utils/utils';
|
||||
export * from './utils/constants.js';
|
||||
export * from './utils/IdentifyThrottler.js';
|
||||
export * from './utils/utils.js';
|
||||
|
||||
export * from './ws/WebSocketManager';
|
||||
export * from './ws/WebSocketShard';
|
||||
export * from './ws/WebSocketManager.js';
|
||||
export * from './ws/WebSocketShard.js';
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { SessionInfo, WebSocketManager, WebSocketManagerOptions } from '../
|
||||
export interface FetchingStrategyOptions
|
||||
extends Omit<
|
||||
WebSocketManagerOptions,
|
||||
'retrieveSessionInfo' | 'updateSessionInfo' | 'shardCount' | 'shardIds' | 'rest'
|
||||
'rest' | 'retrieveSessionInfo' | 'shardCount' | 'shardIds' | 'updateSessionInfo'
|
||||
> {
|
||||
readonly gatewayInformation: APIGatewayBotInfo;
|
||||
readonly shardCount: number;
|
||||
@@ -16,11 +16,12 @@ export interface FetchingStrategyOptions
|
||||
*/
|
||||
export interface IContextFetchingStrategy {
|
||||
readonly options: FetchingStrategyOptions;
|
||||
retrieveSessionInfo: (shardId: number) => Awaitable<SessionInfo | null>;
|
||||
updateSessionInfo: (shardId: number, sessionInfo: SessionInfo | null) => Awaitable<void>;
|
||||
retrieveSessionInfo(shardId: number): Awaitable<SessionInfo | null>;
|
||||
updateSessionInfo(shardId: number, sessionInfo: SessionInfo | null): Awaitable<void>;
|
||||
}
|
||||
|
||||
export async function managerToFetchingStrategyOptions(manager: WebSocketManager): Promise<FetchingStrategyOptions> {
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const { retrieveSessionInfo, updateSessionInfo, shardCount, shardIds, rest, ...managerOptions } = manager.options;
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FetchingStrategyOptions, IContextFetchingStrategy } from './IContextFetchingStrategy';
|
||||
import type { SessionInfo, WebSocketManager } from '../../ws/WebSocketManager';
|
||||
import type { SessionInfo, WebSocketManager } from '../../ws/WebSocketManager.js';
|
||||
import type { FetchingStrategyOptions, IContextFetchingStrategy } from './IContextFetchingStrategy.js';
|
||||
|
||||
export class SimpleContextFetchingStrategy implements IContextFetchingStrategy {
|
||||
public constructor(private readonly manager: WebSocketManager, public readonly options: FetchingStrategyOptions) {}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { isMainThread, parentPort } from 'node:worker_threads';
|
||||
import { Collection } from '@discordjs/collection';
|
||||
import type { FetchingStrategyOptions, IContextFetchingStrategy } from './IContextFetchingStrategy';
|
||||
import type { SessionInfo } from '../../ws/WebSocketManager';
|
||||
import type { SessionInfo } from '../../ws/WebSocketManager.js';
|
||||
import {
|
||||
WorkerRecievePayload,
|
||||
WorkerRecievePayloadOp,
|
||||
WorkerSendPayload,
|
||||
WorkerSendPayloadOp,
|
||||
} from '../sharding/WorkerShardingStrategy';
|
||||
type WorkerRecievePayload,
|
||||
type WorkerSendPayload,
|
||||
} from '../sharding/WorkerShardingStrategy.js';
|
||||
import type { FetchingStrategyOptions, IContextFetchingStrategy } from './IContextFetchingStrategy.js';
|
||||
|
||||
export class WorkerContextFetchingStrategy implements IContextFetchingStrategy {
|
||||
private readonly sessionPromises = new Collection<number, (session: SessionInfo | null) => void>();
|
||||
@@ -33,7 +33,9 @@ export class WorkerContextFetchingStrategy implements IContextFetchingStrategy {
|
||||
shardId,
|
||||
nonce,
|
||||
};
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
const promise = new Promise<SessionInfo | null>((resolve) => this.sessionPromises.set(nonce, resolve));
|
||||
// eslint-disable-next-line unicorn/require-post-message-target-origin
|
||||
parentPort!.postMessage(payload);
|
||||
return promise;
|
||||
}
|
||||
@@ -44,6 +46,7 @@ export class WorkerContextFetchingStrategy implements IContextFetchingStrategy {
|
||||
shardId,
|
||||
session: sessionInfo,
|
||||
};
|
||||
// eslint-disable-next-line unicorn/require-post-message-target-origin
|
||||
parentPort!.postMessage(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,20 +6,20 @@ import type { WebSocketShardDestroyOptions } from '../../ws/WebSocketShard';
|
||||
* Strategies responsible for spawning, initializing connections, destroying shards, and relaying events
|
||||
*/
|
||||
export interface IShardingStrategy {
|
||||
/**
|
||||
* Spawns all the shards
|
||||
*/
|
||||
spawn: (shardIds: number[]) => Awaitable<void>;
|
||||
/**
|
||||
* Initializes all the shards
|
||||
*/
|
||||
connect: () => Awaitable<void>;
|
||||
connect(): Awaitable<void>;
|
||||
/**
|
||||
* Destroys all the shards
|
||||
*/
|
||||
destroy: (options?: Omit<WebSocketShardDestroyOptions, 'recover'>) => Awaitable<void>;
|
||||
destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>): Awaitable<void>;
|
||||
/**
|
||||
* Sends a payload to a shard
|
||||
*/
|
||||
send: (shardId: number, payload: GatewaySendPayload) => Awaitable<void>;
|
||||
send(shardId: number, payload: GatewaySendPayload): Awaitable<void>;
|
||||
/**
|
||||
* Spawns all the shards
|
||||
*/
|
||||
spawn(shardIds: number[]): Awaitable<void>;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Collection } from '@discordjs/collection';
|
||||
import type { GatewaySendPayload } from 'discord-api-types/v10';
|
||||
import type { IShardingStrategy } from './IShardingStrategy';
|
||||
import { IdentifyThrottler } from '../../utils/IdentifyThrottler';
|
||||
import { IdentifyThrottler } from '../../utils/IdentifyThrottler.js';
|
||||
import type { WebSocketManager } from '../../ws/WebSocketManager';
|
||||
import { WebSocketShard, WebSocketShardDestroyOptions, WebSocketShardEvents } from '../../ws/WebSocketShard';
|
||||
import { managerToFetchingStrategyOptions } from '../context/IContextFetchingStrategy';
|
||||
import { SimpleContextFetchingStrategy } from '../context/SimpleContextFetchingStrategy';
|
||||
import { WebSocketShard, WebSocketShardEvents, type WebSocketShardDestroyOptions } from '../../ws/WebSocketShard.js';
|
||||
import { managerToFetchingStrategyOptions } from '../context/IContextFetchingStrategy.js';
|
||||
import { SimpleContextFetchingStrategy } from '../context/SimpleContextFetchingStrategy.js';
|
||||
import type { IShardingStrategy } from './IShardingStrategy.js';
|
||||
|
||||
/**
|
||||
* Simple strategy that just spawns shards in the current process
|
||||
*/
|
||||
export class SimpleShardingStrategy implements IShardingStrategy {
|
||||
private readonly manager: WebSocketManager;
|
||||
|
||||
private readonly shards = new Collection<number, WebSocketShard>();
|
||||
|
||||
private readonly throttler: IdentifyThrottler;
|
||||
@@ -30,9 +31,10 @@ export class SimpleShardingStrategy implements IShardingStrategy {
|
||||
const strategy = new SimpleContextFetchingStrategy(this.manager, strategyOptions);
|
||||
const shard = new WebSocketShard(strategy, shardId);
|
||||
for (const event of Object.values(WebSocketShardEvents)) {
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error: Intentional
|
||||
shard.on(event, (payload) => this.manager.emit(event, { ...payload, shardId }));
|
||||
}
|
||||
|
||||
this.shards.set(shardId, shard);
|
||||
}
|
||||
}
|
||||
@@ -68,7 +70,7 @@ export class SimpleShardingStrategy implements IShardingStrategy {
|
||||
/**
|
||||
* {@inheritDoc IShardingStrategy.send}
|
||||
*/
|
||||
public send(shardId: number, payload: GatewaySendPayload) {
|
||||
public async send(shardId: number, payload: GatewaySendPayload) {
|
||||
const shard = this.shards.get(shardId);
|
||||
if (!shard) throw new Error(`Shard ${shardId} not found`);
|
||||
return shard.send(payload);
|
||||
|
||||
@@ -3,11 +3,11 @@ import { join } from 'node:path';
|
||||
import { Worker } from 'node:worker_threads';
|
||||
import { Collection } from '@discordjs/collection';
|
||||
import type { GatewaySendPayload } from 'discord-api-types/v10';
|
||||
import type { IShardingStrategy } from './IShardingStrategy';
|
||||
import { IdentifyThrottler } from '../../utils/IdentifyThrottler';
|
||||
import { IdentifyThrottler } from '../../utils/IdentifyThrottler.js';
|
||||
import type { SessionInfo, WebSocketManager } from '../../ws/WebSocketManager';
|
||||
import type { WebSocketShardDestroyOptions, WebSocketShardEvents } from '../../ws/WebSocketShard';
|
||||
import { FetchingStrategyOptions, managerToFetchingStrategyOptions } from '../context/IContextFetchingStrategy';
|
||||
import { managerToFetchingStrategyOptions, type FetchingStrategyOptions } from '../context/IContextFetchingStrategy.js';
|
||||
import type { IShardingStrategy } from './IShardingStrategy.js';
|
||||
|
||||
export interface WorkerData extends FetchingStrategyOptions {
|
||||
shardIds: number[];
|
||||
@@ -21,10 +21,10 @@ export enum WorkerSendPayloadOp {
|
||||
}
|
||||
|
||||
export type WorkerSendPayload =
|
||||
| { nonce: number; op: WorkerSendPayloadOp.SessionInfoResponse; session: SessionInfo | null }
|
||||
| { op: WorkerSendPayloadOp.Connect; shardId: number }
|
||||
| { op: WorkerSendPayloadOp.Destroy; shardId: number; options?: WebSocketShardDestroyOptions }
|
||||
| { op: WorkerSendPayloadOp.Send; shardId: number; payload: GatewaySendPayload }
|
||||
| { op: WorkerSendPayloadOp.SessionInfoResponse; nonce: number; session: SessionInfo | null };
|
||||
| { op: WorkerSendPayloadOp.Destroy; options?: WebSocketShardDestroyOptions; shardId: number }
|
||||
| { op: WorkerSendPayloadOp.Send; payload: GatewaySendPayload; shardId: number };
|
||||
|
||||
export enum WorkerRecievePayloadOp {
|
||||
Connected,
|
||||
@@ -35,12 +35,12 @@ export enum WorkerRecievePayloadOp {
|
||||
}
|
||||
|
||||
export type WorkerRecievePayload =
|
||||
// Can't seem to get a type-safe union based off of the event, so I'm sadly leaving data as any for now
|
||||
| { data: any; event: WebSocketShardEvents; op: WorkerRecievePayloadOp.Event; shardId: number }
|
||||
| { nonce: number; op: WorkerRecievePayloadOp.RetrieveSessionInfo; shardId: number }
|
||||
| { op: WorkerRecievePayloadOp.Connected; shardId: number }
|
||||
| { op: WorkerRecievePayloadOp.Destroyed; shardId: number }
|
||||
// Can't seem to get a type-safe union based off of the event, so I'm sadly leaving data as any for now
|
||||
| { op: WorkerRecievePayloadOp.Event; shardId: number; event: WebSocketShardEvents; data: any }
|
||||
| { op: WorkerRecievePayloadOp.RetrieveSessionInfo; shardId: number; nonce: number }
|
||||
| { op: WorkerRecievePayloadOp.UpdateSessionInfo; shardId: number; session: SessionInfo | null };
|
||||
| { op: WorkerRecievePayloadOp.UpdateSessionInfo; session: SessionInfo | null; shardId: number };
|
||||
|
||||
/**
|
||||
* Options for a {@link WorkerShardingStrategy}
|
||||
@@ -57,12 +57,15 @@ export interface WorkerShardingStrategyOptions {
|
||||
*/
|
||||
export class WorkerShardingStrategy implements IShardingStrategy {
|
||||
private readonly manager: WebSocketManager;
|
||||
|
||||
private readonly options: WorkerShardingStrategyOptions;
|
||||
|
||||
#workers: Worker[] = [];
|
||||
|
||||
readonly #workerByShardId = new Collection<number, Worker>();
|
||||
|
||||
private readonly connectPromises = new Collection<number, () => void>();
|
||||
|
||||
private readonly destroyPromises = new Collection<number, () => void>();
|
||||
|
||||
private readonly throttler: IdentifyThrottler;
|
||||
@@ -98,7 +101,7 @@ export class WorkerShardingStrategy implements IShardingStrategy {
|
||||
throw err;
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
.on('message', (payload: WorkerRecievePayload) => this.onMessage(worker, payload));
|
||||
.on('message', async (payload: WorkerRecievePayload) => this.onMessage(worker, payload));
|
||||
|
||||
this.#workers.push(worker);
|
||||
for (const shardId of slice) {
|
||||
@@ -123,7 +126,9 @@ export class WorkerShardingStrategy implements IShardingStrategy {
|
||||
shardId,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
const promise = new Promise<void>((resolve) => this.connectPromises.set(shardId, resolve));
|
||||
// eslint-disable-next-line unicorn/require-post-message-target-origin
|
||||
worker.postMessage(payload);
|
||||
promises.push(promise);
|
||||
}
|
||||
@@ -145,8 +150,10 @@ export class WorkerShardingStrategy implements IShardingStrategy {
|
||||
};
|
||||
|
||||
promises.push(
|
||||
new Promise<void>((resolve) => this.destroyPromises.set(shardId, resolve)).then(() => worker.terminate()),
|
||||
// eslint-disable-next-line no-promise-executor-return, promise/prefer-await-to-then
|
||||
new Promise<void>((resolve) => this.destroyPromises.set(shardId, resolve)).then(async () => worker.terminate()),
|
||||
);
|
||||
// eslint-disable-next-line unicorn/require-post-message-target-origin
|
||||
worker.postMessage(payload);
|
||||
}
|
||||
|
||||
@@ -170,10 +177,12 @@ export class WorkerShardingStrategy implements IShardingStrategy {
|
||||
shardId,
|
||||
payload: data,
|
||||
};
|
||||
// eslint-disable-next-line unicorn/require-post-message-target-origin
|
||||
worker.postMessage(payload);
|
||||
}
|
||||
|
||||
private async onMessage(worker: Worker, payload: WorkerRecievePayload) {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (payload.op) {
|
||||
case WorkerRecievePayloadOp.Connected: {
|
||||
const resolve = this.connectPromises.get(payload.shardId)!;
|
||||
@@ -202,6 +211,7 @@ export class WorkerShardingStrategy implements IShardingStrategy {
|
||||
nonce: payload.nonce,
|
||||
session,
|
||||
};
|
||||
// eslint-disable-next-line unicorn/require-post-message-target-origin
|
||||
worker.postMessage(response);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/* eslint-disable unicorn/require-post-message-target-origin */
|
||||
import { isMainThread, workerData, parentPort } from 'node:worker_threads';
|
||||
import { Collection } from '@discordjs/collection';
|
||||
import { WebSocketShard, WebSocketShardEvents, type WebSocketShardDestroyOptions } from '../../ws/WebSocketShard.js';
|
||||
import { WorkerContextFetchingStrategy } from '../context/WorkerContextFetchingStrategy.js';
|
||||
import {
|
||||
WorkerData,
|
||||
WorkerRecievePayload,
|
||||
WorkerRecievePayloadOp,
|
||||
WorkerSendPayload,
|
||||
WorkerSendPayloadOp,
|
||||
} from './WorkerShardingStrategy';
|
||||
import { WebSocketShard, WebSocketShardDestroyOptions, WebSocketShardEvents } from '../../ws/WebSocketShard';
|
||||
import { WorkerContextFetchingStrategy } from '../context/WorkerContextFetchingStrategy';
|
||||
type WorkerData,
|
||||
type WorkerRecievePayload,
|
||||
type WorkerSendPayload,
|
||||
} from './WorkerShardingStrategy.js';
|
||||
|
||||
if (isMainThread) {
|
||||
throw new Error('Expected worker script to not be ran within the main thread');
|
||||
@@ -22,6 +23,7 @@ async function connect(shardId: number) {
|
||||
if (!shard) {
|
||||
throw new Error(`Shard ${shardId} does not exist`);
|
||||
}
|
||||
|
||||
await shard.connect();
|
||||
}
|
||||
|
||||
@@ -30,13 +32,14 @@ async function destroy(shardId: number, options?: WebSocketShardDestroyOptions)
|
||||
if (!shard) {
|
||||
throw new Error(`Shard ${shardId} does not exist`);
|
||||
}
|
||||
|
||||
await shard.destroy(options);
|
||||
}
|
||||
|
||||
for (const shardId of data.shardIds) {
|
||||
const shard = new WebSocketShard(new WorkerContextFetchingStrategy(data), shardId);
|
||||
for (const event of Object.values(WebSocketShardEvents)) {
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error: Event types incompatible
|
||||
shard.on(event, (data) => {
|
||||
const payload: WorkerRecievePayload = {
|
||||
op: WorkerRecievePayloadOp.Event,
|
||||
@@ -47,6 +50,7 @@ for (const shardId of data.shardIds) {
|
||||
parentPort!.postMessage(payload);
|
||||
});
|
||||
}
|
||||
|
||||
shards.set(shardId, shard);
|
||||
}
|
||||
|
||||
@@ -56,6 +60,7 @@ parentPort!
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
.on('message', async (payload: WorkerSendPayload) => {
|
||||
// eslint-disable-next-line default-case
|
||||
switch (payload.op) {
|
||||
case WorkerSendPayloadOp.Connect: {
|
||||
await connect(payload.shardId);
|
||||
@@ -73,6 +78,7 @@ parentPort!
|
||||
op: WorkerRecievePayloadOp.Destroyed,
|
||||
shardId: payload.shardId,
|
||||
};
|
||||
|
||||
parentPort!.postMessage(response);
|
||||
break;
|
||||
}
|
||||
@@ -82,6 +88,7 @@ parentPort!
|
||||
if (!shard) {
|
||||
throw new Error(`Shard ${payload.shardId} does not exist`);
|
||||
}
|
||||
|
||||
await shard.send(payload.payload);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { WebSocketManager } from '../ws/WebSocketManager';
|
||||
export class IdentifyThrottler {
|
||||
private identifyState = {
|
||||
remaining: 0,
|
||||
resetsAt: Infinity,
|
||||
resetsAt: Number.POSITIVE_INFINITY,
|
||||
};
|
||||
|
||||
public constructor(private readonly manager: WebSocketManager) {}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { Collection } from '@discordjs/collection';
|
||||
import { APIVersion, GatewayOpcodes } from 'discord-api-types/v10';
|
||||
import { lazy } from './utils';
|
||||
import type { OptionalWebSocketManagerOptions, SessionInfo } from '../ws/WebSocketManager';
|
||||
import type { OptionalWebSocketManagerOptions, SessionInfo } from '../ws/WebSocketManager.js';
|
||||
import { lazy } from './utils.js';
|
||||
|
||||
/**
|
||||
* Valid encoding types
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ShardRange } from '../ws/WebSocketManager';
|
||||
|
||||
export type Awaitable<T> = T | Promise<T>;
|
||||
export type Awaitable<T> = Promise<T> | T;
|
||||
|
||||
/**
|
||||
* Yields the numbers in the given range as an array
|
||||
@@ -11,7 +11,7 @@ export type Awaitable<T> = T | Promise<T>;
|
||||
* ```
|
||||
*/
|
||||
export function range({ start, end }: ShardRange): number[] {
|
||||
return Array.from({ length: end - start + 1 }, (_, i) => i + start);
|
||||
return Array.from({ length: end - start + 1 }, (_, index) => index + start);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import type { REST } from '@discordjs/rest';
|
||||
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
||||
import {
|
||||
APIGatewayBotInfo,
|
||||
GatewayIdentifyProperties,
|
||||
GatewayPresenceUpdateData,
|
||||
RESTGetAPIGatewayBotResult,
|
||||
GatewayIntentBits,
|
||||
Routes,
|
||||
GatewaySendPayload,
|
||||
type APIGatewayBotInfo,
|
||||
type GatewayIdentifyProperties,
|
||||
type GatewayPresenceUpdateData,
|
||||
type RESTGetAPIGatewayBotResult,
|
||||
type GatewayIntentBits,
|
||||
type GatewaySendPayload,
|
||||
} from 'discord-api-types/v10';
|
||||
import type { WebSocketShardDestroyOptions, WebSocketShardEventsMap } from './WebSocketShard';
|
||||
import type { IShardingStrategy } from '../strategies/sharding/IShardingStrategy';
|
||||
import { SimpleShardingStrategy } from '../strategies/sharding/SimpleShardingStrategy';
|
||||
import { CompressionMethod, DefaultWebSocketManagerOptions, Encoding } from '../utils/constants';
|
||||
import { Awaitable, range } from '../utils/utils';
|
||||
import { SimpleShardingStrategy } from '../strategies/sharding/SimpleShardingStrategy.js';
|
||||
import { DefaultWebSocketManagerOptions, type CompressionMethod, type Encoding } from '../utils/constants.js';
|
||||
import { range, type Awaitable } from '../utils/utils.js';
|
||||
import type { WebSocketShardDestroyOptions, WebSocketShardEventsMap } from './WebSocketShard.js';
|
||||
|
||||
/**
|
||||
* Represents a range of shard ids
|
||||
*/
|
||||
export interface ShardRange {
|
||||
start: number;
|
||||
end: number;
|
||||
start: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,35 +28,31 @@ export interface ShardRange {
|
||||
*/
|
||||
export interface SessionInfo {
|
||||
/**
|
||||
* Session id for this shard
|
||||
* URL to use when resuming
|
||||
*/
|
||||
sessionId: string;
|
||||
resumeURL: string;
|
||||
/**
|
||||
* The sequence number of the last message sent by the shard
|
||||
*/
|
||||
sequence: number;
|
||||
/**
|
||||
* The id of the shard
|
||||
* Session id for this shard
|
||||
*/
|
||||
shardId: number;
|
||||
sessionId: string;
|
||||
/**
|
||||
* The total number of shards at the time of this shard identifying
|
||||
*/
|
||||
shardCount: number;
|
||||
/**
|
||||
* URL to use when resuming
|
||||
* The id of the shard
|
||||
*/
|
||||
resumeURL: string;
|
||||
shardId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required options for the WebSocketManager
|
||||
*/
|
||||
export interface RequiredWebSocketManagerOptions {
|
||||
/**
|
||||
* The token to use for identifying with the gateway
|
||||
*/
|
||||
token: string;
|
||||
/**
|
||||
* The intents to request
|
||||
*/
|
||||
@@ -65,12 +61,67 @@ export interface RequiredWebSocketManagerOptions {
|
||||
* The REST instance to use for fetching gateway information
|
||||
*/
|
||||
rest: REST;
|
||||
/**
|
||||
* The token to use for identifying with the gateway
|
||||
*/
|
||||
token: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional additional configuration for the WebSocketManager
|
||||
*/
|
||||
export interface OptionalWebSocketManagerOptions {
|
||||
/**
|
||||
* The compression method to use
|
||||
*
|
||||
* @defaultValue `null` (no compression)
|
||||
*/
|
||||
compression: CompressionMethod | null;
|
||||
/**
|
||||
* The encoding to use
|
||||
*
|
||||
* @defaultValue `'json'`
|
||||
*/
|
||||
encoding: Encoding;
|
||||
/**
|
||||
* How long to wait for a shard to connect before giving up
|
||||
*/
|
||||
handshakeTimeout: number | null;
|
||||
/**
|
||||
* How long to wait for a shard's HELLO packet before giving up
|
||||
*/
|
||||
helloTimeout: number | null;
|
||||
/**
|
||||
* Properties to send to the gateway when identifying
|
||||
*/
|
||||
identifyProperties: GatewayIdentifyProperties;
|
||||
/**
|
||||
* Initial presence data to send to the gateway when identifying
|
||||
*/
|
||||
initialPresence: GatewayPresenceUpdateData | null;
|
||||
/**
|
||||
* Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list
|
||||
*/
|
||||
largeThreshold: number | null;
|
||||
/**
|
||||
* How long to wait for a shard's READY packet before giving up
|
||||
*/
|
||||
readyTimeout: number | null;
|
||||
/**
|
||||
* Function used to retrieve session information (and attempt to resume) for a given shard
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const manager = new WebSocketManager({
|
||||
* async retrieveSessionInfo(shardId): Awaitable<SessionInfo | null> {
|
||||
* // Fetch this info from redis or similar
|
||||
* return { sessionId: string, sequence: number };
|
||||
* // Return null if no information is found
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
retrieveSessionInfo(shardId: number): Awaitable<SessionInfo | null>;
|
||||
/**
|
||||
* The total number of shards across all WebsocketManagers you intend to instantiate.
|
||||
* Use `null` to use Discord's recommended shard count
|
||||
@@ -86,7 +137,6 @@ export interface OptionalWebSocketManagerOptions {
|
||||
* shardIds: [1, 3, 7], // spawns shard 1, 3, and 7, nothing else
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const manager = new WebSocketManager({
|
||||
@@ -99,66 +149,18 @@ export interface OptionalWebSocketManagerOptions {
|
||||
*/
|
||||
shardIds: number[] | ShardRange | null;
|
||||
/**
|
||||
* Value between 50 and 250, total number of members where the gateway will stop sending offline members in the guild member list
|
||||
* Function used to store session information for a given shard
|
||||
*/
|
||||
largeThreshold: number | null;
|
||||
/**
|
||||
* Initial presence data to send to the gateway when identifying
|
||||
*/
|
||||
initialPresence: GatewayPresenceUpdateData | null;
|
||||
/**
|
||||
* Properties to send to the gateway when identifying
|
||||
*/
|
||||
identifyProperties: GatewayIdentifyProperties;
|
||||
updateSessionInfo(shardId: number, sessionInfo: SessionInfo | null): Awaitable<void>;
|
||||
/**
|
||||
* The gateway version to use
|
||||
*
|
||||
* @defaultValue `'10'`
|
||||
*/
|
||||
version: string;
|
||||
/**
|
||||
* The encoding to use
|
||||
* @defaultValue `'json'`
|
||||
*/
|
||||
encoding: Encoding;
|
||||
/**
|
||||
* The compression method to use
|
||||
* @defaultValue `null` (no compression)
|
||||
*/
|
||||
compression: CompressionMethod | null;
|
||||
/**
|
||||
* Function used to retrieve session information (and attempt to resume) for a given shard
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const manager = new WebSocketManager({
|
||||
* async retrieveSessionInfo(shardId): Awaitable<SessionInfo | null> {
|
||||
* // Fetch this info from redis or similar
|
||||
* return { sessionId: string, sequence: number };
|
||||
* // Return null if no information is found
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
retrieveSessionInfo: (shardId: number) => Awaitable<SessionInfo | null>;
|
||||
/**
|
||||
* Function used to store session information for a given shard
|
||||
*/
|
||||
updateSessionInfo: (shardId: number, sessionInfo: SessionInfo | null) => Awaitable<void>;
|
||||
/**
|
||||
* How long to wait for a shard to connect before giving up
|
||||
*/
|
||||
handshakeTimeout: number | null;
|
||||
/**
|
||||
* How long to wait for a shard's HELLO packet before giving up
|
||||
*/
|
||||
helloTimeout: number | null;
|
||||
/**
|
||||
* How long to wait for a shard's READY packet before giving up
|
||||
*/
|
||||
readyTimeout: number | null;
|
||||
}
|
||||
|
||||
export type WebSocketManagerOptions = RequiredWebSocketManagerOptions & OptionalWebSocketManagerOptions;
|
||||
export type WebSocketManagerOptions = OptionalWebSocketManagerOptions & RequiredWebSocketManagerOptions;
|
||||
|
||||
export type ManagerShardEventsMap = {
|
||||
[K in keyof WebSocketShardEventsMap]: [
|
||||
@@ -187,11 +189,12 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
|
||||
|
||||
/**
|
||||
* Strategy used to manage shards
|
||||
*
|
||||
* @defaultValue `SimpleManagerToShardStrategy`
|
||||
*/
|
||||
private strategy: IShardingStrategy = new SimpleShardingStrategy(this);
|
||||
|
||||
public constructor(options: RequiredWebSocketManagerOptions & Partial<OptionalWebSocketManagerOptions>) {
|
||||
public constructor(options: Partial<OptionalWebSocketManagerOptions> & RequiredWebSocketManagerOptions) {
|
||||
super();
|
||||
this.options = { ...DefaultWebSocketManagerOptions, ...options };
|
||||
}
|
||||
@@ -203,6 +206,7 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public async fetchGatewayInformation(force = false) {
|
||||
@@ -222,6 +226,7 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
|
||||
|
||||
/**
|
||||
* Updates your total shard count on-the-fly, spawning shards as needed
|
||||
*
|
||||
* @param shardCount - The new shard count to use
|
||||
*/
|
||||
public async updateShardCount(shardCount: number | null) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* eslint-disable id-length */
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { once } from 'node:events';
|
||||
import { setTimeout } from 'node:timers';
|
||||
import { setTimeout, clearInterval, clearTimeout, setInterval } from 'node:timers';
|
||||
import { setTimeout as sleep } from 'node:timers/promises';
|
||||
import { URLSearchParams } from 'node:url';
|
||||
import { TextDecoder } from 'node:util';
|
||||
import { inflate } from 'node:zlib';
|
||||
import { Collection } from '@discordjs/collection';
|
||||
@@ -9,27 +12,28 @@ import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
|
||||
import {
|
||||
GatewayCloseCodes,
|
||||
GatewayDispatchEvents,
|
||||
GatewayDispatchPayload,
|
||||
GatewayIdentifyData,
|
||||
GatewayOpcodes,
|
||||
GatewayReceivePayload,
|
||||
GatewaySendPayload,
|
||||
type GatewayDispatchPayload,
|
||||
type GatewayIdentifyData,
|
||||
type GatewayReceivePayload,
|
||||
type GatewaySendPayload,
|
||||
} from 'discord-api-types/v10';
|
||||
import { RawData, WebSocket } from 'ws';
|
||||
import { WebSocket, type RawData } from 'ws';
|
||||
import type { Inflate } from 'zlib-sync';
|
||||
import type { SessionInfo } from './WebSocketManager';
|
||||
import type { IContextFetchingStrategy } from '../strategies/context/IContextFetchingStrategy';
|
||||
import { ImportantGatewayOpcodes } from '../utils/constants';
|
||||
import { lazy } from '../utils/utils';
|
||||
import { ImportantGatewayOpcodes } from '../utils/constants.js';
|
||||
import { lazy } from '../utils/utils.js';
|
||||
import type { SessionInfo } from './WebSocketManager.js';
|
||||
|
||||
const getZlibSync = lazy(() => import('zlib-sync').then((mod) => mod.default).catch(() => null));
|
||||
// eslint-disable-next-line promise/prefer-await-to-then
|
||||
const getZlibSync = lazy(async () => import('zlib-sync').then((mod) => mod.default).catch(() => null));
|
||||
|
||||
export enum WebSocketShardEvents {
|
||||
Debug = 'debug',
|
||||
Dispatch = 'dispatch',
|
||||
Hello = 'hello',
|
||||
Ready = 'ready',
|
||||
Resumed = 'resumed',
|
||||
Dispatch = 'dispatch',
|
||||
}
|
||||
|
||||
export enum WebSocketShardStatus {
|
||||
@@ -54,14 +58,14 @@ export type WebSocketShardEventsMap = {
|
||||
};
|
||||
|
||||
export interface WebSocketShardDestroyOptions {
|
||||
reason?: string;
|
||||
code?: number;
|
||||
reason?: string;
|
||||
recover?: WebSocketShardDestroyRecovery;
|
||||
}
|
||||
|
||||
export enum CloseCodes {
|
||||
Normal = 1000,
|
||||
Resuming = 4200,
|
||||
Normal = 1_000,
|
||||
Resuming = 4_200,
|
||||
}
|
||||
|
||||
export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
@@ -72,6 +76,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
private useIdentifyCompress = false;
|
||||
|
||||
private inflate: Inflate | null = null;
|
||||
|
||||
private readonly textDecoder = new TextDecoder();
|
||||
|
||||
private status: WebSocketShardStatus = WebSocketShardStatus.Idle;
|
||||
@@ -86,6 +91,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
};
|
||||
|
||||
private heartbeatInterval: NodeJS.Timer | null = null;
|
||||
|
||||
private lastHeartbeatAt = -1;
|
||||
|
||||
private session: SessionInfo | null = null;
|
||||
@@ -114,7 +120,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
if (zlib) {
|
||||
params.append('compress', compression);
|
||||
this.inflate = new zlib.Inflate({
|
||||
chunkSize: 65535,
|
||||
chunkSize: 65_535,
|
||||
to: 'string',
|
||||
});
|
||||
} else if (!this.useIdentifyCompress) {
|
||||
@@ -173,6 +179,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
if (this.heartbeatInterval) {
|
||||
clearInterval(this.heartbeatInterval);
|
||||
}
|
||||
|
||||
this.lastHeartbeatAt = -1;
|
||||
|
||||
// Clear session state if applicable
|
||||
@@ -202,6 +209,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
this.status = WebSocketShardStatus.Idle;
|
||||
|
||||
if (options.recover !== undefined) {
|
||||
// eslint-disable-next-line consistent-return
|
||||
return this.connect();
|
||||
}
|
||||
}
|
||||
@@ -213,6 +221,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
if (timeout) {
|
||||
this.timeouts.set(event, timeout);
|
||||
}
|
||||
|
||||
await once(this, event, { signal: controller.signal });
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
@@ -279,7 +288,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
this.status = WebSocketShardStatus.Ready;
|
||||
}
|
||||
|
||||
private resume(session: SessionInfo) {
|
||||
private async resume(session: SessionInfo) {
|
||||
this.debug(['Resuming session']);
|
||||
this.status = WebSocketShardStatus.Resuming;
|
||||
this.replayedEvents = 0;
|
||||
@@ -293,6 +302,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
private async heartbeat(requested = false) {
|
||||
if (!this.isAck && !requested) {
|
||||
return this.destroy({ reason: 'Zombie connection', recover: WebSocketShardDestroyRecovery.Resume });
|
||||
@@ -307,7 +317,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
this.isAck = false;
|
||||
}
|
||||
|
||||
private async unpackMessage(data: Buffer | ArrayBuffer, isBinary: boolean): Promise<GatewayReceivePayload | null> {
|
||||
private async unpackMessage(data: ArrayBuffer | Buffer, isBinary: boolean): Promise<GatewayReceivePayload | null> {
|
||||
const decompressable = new Uint8Array(data);
|
||||
|
||||
// Deal with no compression
|
||||
@@ -318,9 +328,10 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
// Deal with identify compress
|
||||
if (this.useIdentifyCompress) {
|
||||
return new Promise((resolve, reject) => {
|
||||
inflate(decompressable, { chunkSize: 65535 }, (err, result) => {
|
||||
inflate(decompressable, { chunkSize: 65_535 }, (err, result) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(JSON.parse(this.textDecoder.decode(result)) as GatewayReceivePayload);
|
||||
@@ -368,11 +379,12 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
}
|
||||
|
||||
private async onMessage(data: RawData, isBinary: boolean) {
|
||||
const payload = await this.unpackMessage(data as Buffer | ArrayBuffer, isBinary);
|
||||
const payload = await this.unpackMessage(data as ArrayBuffer | Buffer, isBinary);
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line default-case
|
||||
switch (payload.op) {
|
||||
case GatewayOpcodes.Dispatch: {
|
||||
if (this.status === WebSocketShardStatus.Ready || this.status === WebSocketShardStatus.Resuming) {
|
||||
@@ -411,11 +423,9 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.session) {
|
||||
if (payload.s > this.session.sequence) {
|
||||
this.session.sequence = payload.s;
|
||||
await this.strategy.updateSessionInfo(this.id, this.session);
|
||||
}
|
||||
if (this.session && payload.s > this.session.sequence) {
|
||||
this.session.sequence = payload.s;
|
||||
await this.strategy.updateSessionInfo(this.id, this.session);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -447,6 +457,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
recover: WebSocketShardDestroyRecovery.Reconnect,
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -469,6 +480,7 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
||||
this.emit('error', err);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
private async onClose(code: number) {
|
||||
switch (code) {
|
||||
case CloseCodes.Normal: {
|
||||
|
||||
Reference in New Issue
Block a user