indomitable
Advanced tools
+743
| import { ClientOptions, Client } from 'discord.js'; | ||
| import { Worker, ClusterSettings } from 'node:cluster'; | ||
| import EventEmitter from 'node:events'; | ||
| import { Serializable } from 'node:child_process'; | ||
| import { RequestOptions } from 'node:https'; | ||
| import { WebSocketShardEvents, FetchingStrategyOptions, IShardingStrategy, WebSocketManager, WebSocketShardDestroyOptions, WebSocketShardStatus, WebSocketShard, IContextFetchingStrategy, SessionInfo } from '@discordjs/ws'; | ||
| import { Worker as Worker$1, ResourceLimits } from 'node:worker_threads'; | ||
| import { Collection } from '@discordjs/collection'; | ||
| /** | ||
| * A wrapper for @discordjs/ws to work exclusively with Indomitable's dynamic concurrency with support for abort controller | ||
| */ | ||
| declare class ConcurrencyManager { | ||
| private readonly throttler; | ||
| private readonly signals; | ||
| constructor(concurrency: number); | ||
| /** | ||
| * Method to try and acquire a lock for identify | ||
| */ | ||
| waitForIdentify(shardId: number): Promise<void>; | ||
| /** | ||
| * Aborts an acquire lock request | ||
| */ | ||
| abortIdentify(shardId: number): void; | ||
| } | ||
| /** | ||
| * Hoisted Environmental Variable for ease of fetching | ||
| */ | ||
| declare const EnvProcessData: { | ||
| clusterId: number; | ||
| clusterCount: number; | ||
| shardIds: number[]; | ||
| shardCount: number; | ||
| }; | ||
| /** | ||
| * Internal operation codes for the cluster -> thread | ||
| */ | ||
| declare enum MainStrategyOps { | ||
| CONNECT = "connect", | ||
| DESTROY = "destroy", | ||
| SEND = "send", | ||
| STATUS = "status", | ||
| RECONNECT = "reconnect" | ||
| } | ||
| /** | ||
| * Internal operation codes for the thread <- cluster | ||
| */ | ||
| declare enum ThreadStrategyOps { | ||
| REQUEST_IDENTIFY = "requestIdentify", | ||
| CANCEL_IDENTIFY = "cancelIdentify", | ||
| SHARD_EVENT = "shardEvent", | ||
| RETRIEVE_SESSION = "retrieveSession", | ||
| UPDATE_SESSION = "updateSession" | ||
| } | ||
| /** | ||
| * Internal operation codes | ||
| */ | ||
| declare enum InternalOps { | ||
| EVAL = "eval", | ||
| RESTART = "restart", | ||
| RESTART_ALL = "restartAll", | ||
| DESTROY_CLIENT = "destroyClient", | ||
| REQUEST_IDENTIFY = "requestIdentify", | ||
| CANCEL_IDENTIFY = "cancelIdentify", | ||
| SESSION_INFO = "sessionInfo", | ||
| PING = "ping" | ||
| } | ||
| /** | ||
| * Events for internal use | ||
| */ | ||
| declare enum ClientEvents { | ||
| READY = "ready", | ||
| SHARD_READY = "shardReady", | ||
| SHARD_RECONNECT = "shardReconnect", | ||
| SHARD_RESUME = "shardResume", | ||
| SHARD_DISCONNECT = "shardDisconnect", | ||
| ERROR = "ERROR" | ||
| } | ||
| /** | ||
| * Events emitted by Indomitable | ||
| */ | ||
| declare enum LibraryEvents { | ||
| DEBUG = "debug", | ||
| MESSAGE = "message", | ||
| ERROR = "error", | ||
| WORKER_FORK = "workerFork", | ||
| WORKER_READY = "workerReady", | ||
| WORKER_EXIT = "workerExit", | ||
| SHARD_READY = "shardReady", | ||
| SHARD_RECONNECT = "shardReconnect", | ||
| SHARD_RESUME = "shardResume", | ||
| SHARD_DISCONNECT = "shardDisconnect", | ||
| CLIENT_READY = "clientReady", | ||
| RAW = "raw" | ||
| } | ||
| /** | ||
| * Type for raw ipc message | ||
| */ | ||
| declare enum RawIpcMessageType { | ||
| MESSAGE = "message", | ||
| RESPONSE = "response", | ||
| ERROR = "error" | ||
| } | ||
| /** | ||
| * Type for raw ipc messages of cluster -> thread | ||
| */ | ||
| interface MainStrategyData { | ||
| op: MainStrategyOps; | ||
| data: any; | ||
| internal: true; | ||
| } | ||
| /** | ||
| * Type for raw ipc messages of cluster <- thread | ||
| */ | ||
| interface ThreadStrategyData { | ||
| op: ThreadStrategyOps; | ||
| event: WebSocketShardEvents; | ||
| data: any; | ||
| shardId: number; | ||
| internal: true; | ||
| } | ||
| /** | ||
| * Data structure representing an internal event | ||
| */ | ||
| interface InternalOpsData { | ||
| op: InternalOps; | ||
| data: any; | ||
| internal: true; | ||
| } | ||
| /** | ||
| * Data structure representing an internal discord.js event | ||
| */ | ||
| interface ClientEventData { | ||
| op: ClientEvents; | ||
| data: any; | ||
| internal: true; | ||
| } | ||
| /** | ||
| * Data structure representing an internal error | ||
| */ | ||
| interface IpcErrorData { | ||
| name: string; | ||
| reason: string; | ||
| stack: string; | ||
| } | ||
| /** | ||
| * Data structure representing IPC data | ||
| */ | ||
| interface Transportable { | ||
| content: any; | ||
| repliable?: boolean; | ||
| signal?: AbortSignal; | ||
| } | ||
| /** | ||
| * Data structure representing an internal abort data | ||
| */ | ||
| interface InternalAbortSignal { | ||
| listener: () => void; | ||
| signal: AbortSignal; | ||
| } | ||
| interface SavePromiseOptions { | ||
| id: string; | ||
| resolve: (data: unknown) => void; | ||
| reject: (reason: unknown) => void; | ||
| signal?: AbortSignal | undefined; | ||
| } | ||
| /** | ||
| * Data structure representing a generated abort controller instance | ||
| */ | ||
| interface AbortableData { | ||
| controller: AbortController; | ||
| timeout: NodeJS.Timeout; | ||
| } | ||
| /** | ||
| * Internal promise data tracking | ||
| */ | ||
| interface InternalPromise { | ||
| resolve: Function; | ||
| reject: Function; | ||
| controller?: InternalAbortSignal; | ||
| } | ||
| /** | ||
| * Data structure representing internal IPC data | ||
| */ | ||
| interface RawIpcMessage { | ||
| id: string | null; | ||
| content: any; | ||
| internal: true; | ||
| type: RawIpcMessageType; | ||
| } | ||
| /** | ||
| * Data structure representing an IPC message | ||
| */ | ||
| interface Message { | ||
| reply: (data: any) => void; | ||
| content: any; | ||
| repliable: boolean; | ||
| } | ||
| /** | ||
| * Data structure representing a Discord session | ||
| */ | ||
| interface SessionObject { | ||
| url: string; | ||
| shards: number; | ||
| session_start_limit: { | ||
| total: number; | ||
| remaining: number; | ||
| reset_after: number; | ||
| max_concurrency: number; | ||
| }; | ||
| } | ||
| /** | ||
| * Wrapper function for fetching data using HTTP | ||
| * @param url URL of resource to fetch | ||
| * @param options RequestOptions to modify behavior | ||
| * @returns A promise containing data fetched, or an error | ||
| */ | ||
| declare function Fetch(url: string | URL, options: RequestOptions): Promise<any>; | ||
| /** | ||
| * Fetch sessions from discord | ||
| * @param token Bot token | ||
| * @returns A promise containing a session object | ||
| */ | ||
| declare function FetchSessions(token: string): Promise<SessionObject>; | ||
| /** | ||
| * Modify an array to contain the specified amount of chunks | ||
| * @param original An array of data | ||
| * @param chunks The amount of chunks to transform into | ||
| * @returns A modified array | ||
| */ | ||
| declare function Chunk(original: any[], chunks: number): any[]; | ||
| /** | ||
| * Wait for a specific amount of time (timeout) | ||
| * @param ms Time to wait in milliseconds | ||
| * @returns A promise that resolves in x seconds | ||
| */ | ||
| declare function Delay(ms: number): Promise<void>; | ||
| /** | ||
| * Creates an abortable request with controller and timeout | ||
| * @param delay Time before an abort error throws | ||
| * @returns An abortable data with controller and timeout | ||
| */ | ||
| declare function MakeAbortableRequest(delay: number): AbortableData; | ||
| type Util_AbortableData = AbortableData; | ||
| declare const Util_Chunk: typeof Chunk; | ||
| type Util_ClientEventData = ClientEventData; | ||
| type Util_ClientEvents = ClientEvents; | ||
| declare const Util_ClientEvents: typeof ClientEvents; | ||
| declare const Util_Delay: typeof Delay; | ||
| declare const Util_EnvProcessData: typeof EnvProcessData; | ||
| declare const Util_Fetch: typeof Fetch; | ||
| declare const Util_FetchSessions: typeof FetchSessions; | ||
| type Util_InternalAbortSignal = InternalAbortSignal; | ||
| type Util_InternalOps = InternalOps; | ||
| declare const Util_InternalOps: typeof InternalOps; | ||
| type Util_InternalOpsData = InternalOpsData; | ||
| type Util_InternalPromise = InternalPromise; | ||
| type Util_IpcErrorData = IpcErrorData; | ||
| type Util_LibraryEvents = LibraryEvents; | ||
| declare const Util_LibraryEvents: typeof LibraryEvents; | ||
| type Util_MainStrategyData = MainStrategyData; | ||
| type Util_MainStrategyOps = MainStrategyOps; | ||
| declare const Util_MainStrategyOps: typeof MainStrategyOps; | ||
| declare const Util_MakeAbortableRequest: typeof MakeAbortableRequest; | ||
| type Util_Message = Message; | ||
| type Util_RawIpcMessage = RawIpcMessage; | ||
| type Util_RawIpcMessageType = RawIpcMessageType; | ||
| declare const Util_RawIpcMessageType: typeof RawIpcMessageType; | ||
| type Util_SavePromiseOptions = SavePromiseOptions; | ||
| type Util_SessionObject = SessionObject; | ||
| type Util_ThreadStrategyData = ThreadStrategyData; | ||
| type Util_ThreadStrategyOps = ThreadStrategyOps; | ||
| declare const Util_ThreadStrategyOps: typeof ThreadStrategyOps; | ||
| type Util_Transportable = Transportable; | ||
| declare namespace Util { | ||
| export { type Util_AbortableData as AbortableData, Util_Chunk as Chunk, type Util_ClientEventData as ClientEventData, Util_ClientEvents as ClientEvents, Util_Delay as Delay, Util_EnvProcessData as EnvProcessData, Util_Fetch as Fetch, Util_FetchSessions as FetchSessions, type Util_InternalAbortSignal as InternalAbortSignal, Util_InternalOps as InternalOps, type Util_InternalOpsData as InternalOpsData, type Util_InternalPromise as InternalPromise, type Util_IpcErrorData as IpcErrorData, Util_LibraryEvents as LibraryEvents, type Util_MainStrategyData as MainStrategyData, Util_MainStrategyOps as MainStrategyOps, Util_MakeAbortableRequest as MakeAbortableRequest, type Util_Message as Message, type Util_RawIpcMessage as RawIpcMessage, Util_RawIpcMessageType as RawIpcMessageType, type Util_SavePromiseOptions as SavePromiseOptions, type Util_SessionObject as SessionObject, type Util_ThreadStrategyData as ThreadStrategyData, Util_ThreadStrategyOps as ThreadStrategyOps, type Util_Transportable as Transportable }; | ||
| } | ||
| /** | ||
| * Base class where primary and worker ipc inherits | ||
| */ | ||
| declare abstract class BaseIpc { | ||
| readonly manager: Indomitable | EventEmitter; | ||
| protected readonly promises: Map<string, InternalPromise>; | ||
| protected constructor(manager: Indomitable | EventEmitter); | ||
| /** | ||
| * Number of promises pending to be resolved | ||
| */ | ||
| get pendingPromises(): number; | ||
| /** | ||
| * Rejects all the pending promises | ||
| */ | ||
| flushPromises(reason: string): void; | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable: Transportable): Promise<unknown | undefined>; | ||
| /** | ||
| * Taps into message event of worker or primary process to handle ipc communication | ||
| * @internal | ||
| */ | ||
| handleRawResponse(data: Serializable, errorCallback: (error: unknown) => any): Promise<boolean | void>; | ||
| protected waitForPromise(options: SavePromiseOptions): void; | ||
| private handlePromise; | ||
| private handleUnparsedMessage; | ||
| protected emitMessage(message: Message): void; | ||
| protected abstract available(): boolean; | ||
| protected abstract sendData(data: RawIpcMessage): void; | ||
| protected abstract handleMessage(message: Message): Promise<void>; | ||
| } | ||
| /** | ||
| * Primary ipc class. Only initialized at main process | ||
| */ | ||
| declare class MainWorker extends BaseIpc { | ||
| readonly cluster: ClusterManager; | ||
| constructor(cluster: ClusterManager); | ||
| protected available(): boolean; | ||
| protected sendData(data: RawIpcMessage): void; | ||
| protected handleMessage(message: Message): Promise<void>; | ||
| } | ||
| /** | ||
| * Options for child processes | ||
| */ | ||
| interface ClusterManagerOptions { | ||
| id: number; | ||
| shards: number[]; | ||
| manager: Indomitable; | ||
| } | ||
| /** | ||
| * A class to manage a cluster | ||
| */ | ||
| declare class ClusterManager { | ||
| readonly manager: Indomitable; | ||
| readonly id: number; | ||
| readonly ipc: MainWorker; | ||
| shards: number[]; | ||
| started: boolean; | ||
| ready: boolean; | ||
| readyAt: number; | ||
| worker?: Worker; | ||
| /** | ||
| * @param options.id ClusterId of this Cluster Manager being created | ||
| * @param options.shards An array of numbers representing the shards that this cluster controls | ||
| * @param options.manager Indomitable instance that spawned this cluster | ||
| */ | ||
| constructor(options: ClusterManagerOptions); | ||
| /** | ||
| * Destroy associated worker process | ||
| * @param signal Process exit signal | ||
| */ | ||
| destroy(signal?: string): void; | ||
| /** | ||
| * Respawn associated worker process | ||
| * @param delay Time to wait before restarting worker process | ||
| */ | ||
| respawn(delay?: number): Promise<void>; | ||
| /** | ||
| * Spawn a worker process | ||
| */ | ||
| spawn(): Promise<void>; | ||
| /** | ||
| * Remove all listeners on attached worker process and free from memory | ||
| */ | ||
| private cleanup; | ||
| /** | ||
| * Waits for this cluster to be ready | ||
| * @returns A promise that resolves to void | ||
| * @internal | ||
| */ | ||
| private wait; | ||
| } | ||
| /** | ||
| * Options to control Indomitable behavior | ||
| */ | ||
| interface IndomitableOptions { | ||
| clusterCount?: number | 'auto'; | ||
| shardCount?: number | 'auto'; | ||
| clientOptions?: ClientOptions; | ||
| clusterSettings?: ClusterSettings; | ||
| ipcTimeout?: number; | ||
| spawnTimeout?: number; | ||
| spawnDelay?: number; | ||
| autoRestart?: boolean; | ||
| waitForReady?: boolean; | ||
| handleConcurrency?: boolean; | ||
| client: typeof Client; | ||
| token: string; | ||
| } | ||
| interface ReconfigureOptions { | ||
| clusters?: number; | ||
| shards?: number; | ||
| } | ||
| interface ShardEventData { | ||
| clusterId: number; | ||
| shardId?: number; | ||
| replayed?: number; | ||
| event?: CloseEvent; | ||
| } | ||
| declare interface Indomitable { | ||
| /** | ||
| * Emitted when data useful for debugging is produced | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'debug', listener: (message: string) => void): this; | ||
| /** | ||
| * Emitted when an IPC message is received | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'message', listener: (message: Message | unknown) => void): this; | ||
| /** | ||
| * Emitted when an error occurs | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'error', listener: (error: unknown) => void): this; | ||
| /** | ||
| * Emitted when a new worker process is forked | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'workerFork', listener: (cluster: ClusterManager) => void): this; | ||
| /** | ||
| * Emitted when a worker process is ready | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'workerReady', listener: (cluster: ClusterManager) => void): this; | ||
| /** | ||
| * Emitted when a worker process exits | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'workerExit', listener: (code: number | null, signal: string | null, cluster: ClusterManager) => void): this; | ||
| /** | ||
| * Emitted when a Discord.js shard is ready | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'shardReady', listener: (event: ShardEventData) => void): this; | ||
| /** | ||
| * Emitted when a Discord.js shard is reconnecting | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'shardReconnect', listener: (event: ShardEventData) => void): this; | ||
| /** | ||
| * Emitted when a Discord.js shard resumes | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'shardResume', listener: (event: ShardEventData) => void): this; | ||
| /** | ||
| * Emitted when a Discord.js shard disconnects | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'shardDisconnect', listener: (event: ShardEventData) => void): this; | ||
| /** | ||
| * Emitted when a Discord.js client is ready | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'clientReady', listener: (event: ShardEventData) => void): this; | ||
| /** | ||
| * Emitted on every ipc message the handler receives | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'raw', listener: (event: unknown) => void): this; | ||
| once(event: 'debug', listener: (message: string) => void): this; | ||
| once(event: 'message', listener: (message: Message | unknown) => void): this; | ||
| once(event: 'error', listener: (error: unknown) => void): this; | ||
| once(event: 'workerFork', listener: (cluster: ClusterManager) => void): this; | ||
| once(event: 'workerReady', listener: (cluster: ClusterManager) => void): this; | ||
| once(event: 'workerExit', listener: (code: number | null, signal: string | null, cluster: ClusterManager) => void): this; | ||
| once(event: 'shardReady', listener: (event: ShardEventData) => void): this; | ||
| once(event: 'shardReconnect', listener: (event: ShardEventData) => void): this; | ||
| once(event: 'shardResume', listener: (event: ShardEventData) => void): this; | ||
| once(event: 'shardDisconnect', listener: (event: ShardEventData) => void): this; | ||
| once(event: 'clientReady', listener: (event: ShardEventData) => void): this; | ||
| once(event: 'raw', listener: (event: unknown) => void): this; | ||
| off(event: 'debug', listener: (message: string) => void): this; | ||
| off(event: 'message', listener: (message: Message | unknown) => void): this; | ||
| off(event: 'error', listener: (error: unknown) => void): this; | ||
| off(event: 'workerFork', listener: (cluster: ClusterManager) => void): this; | ||
| off(event: 'workerReady', listener: (cluster: ClusterManager) => void): this; | ||
| off(event: 'workerExit', listener: (code: number | null, signal: string | null, cluster: ClusterManager) => void): this; | ||
| off(event: 'shardReady', listener: (event: ShardEventData) => void): this; | ||
| off(event: 'shardReconnect', listener: (event: ShardEventData) => void): this; | ||
| off(event: 'shardResume', listener: (event: ShardEventData) => void): this; | ||
| off(event: 'shardDisconnect', listener: (event: ShardEventData) => void): this; | ||
| off(event: 'clientReady', listener: (event: ShardEventData) => void): this; | ||
| off(event: 'raw', listener: (event: unknown) => void): this; | ||
| } | ||
| /** | ||
| * The main Indomitable class, exposing all functionality. | ||
| */ | ||
| declare class Indomitable extends EventEmitter { | ||
| clusterCount: number | 'auto'; | ||
| shardCount: number | 'auto'; | ||
| cachedSession?: SessionObject; | ||
| concurrencyManager?: ConcurrencyManager; | ||
| readonly clientOptions: ClientOptions; | ||
| readonly clusterSettings: ClusterSettings; | ||
| readonly ipcTimeout: number; | ||
| readonly spawnTimeout: number; | ||
| readonly spawnDelay: number; | ||
| readonly autoRestart: boolean; | ||
| readonly waitForReady: boolean; | ||
| readonly handleConcurrency: boolean; | ||
| readonly client: typeof Client; | ||
| readonly clusters: Map<number, ClusterManager>; | ||
| private readonly spawnQueue; | ||
| private readonly token; | ||
| private busy; | ||
| /** | ||
| * @param [options.clusterCount=auto] The amount of clusters to spawn. Expects a number or 'auto' | ||
| * @param [options.shardCount=auto] The number of shards to create. Expects a number or 'auto' | ||
| * @param [options.clientOptions] Options for the Discord.js client | ||
| * @param [options.clusterSettings] Options for the forked process | ||
| * @param [options.ipcTimeout] Time to wait before an ipc request aborts | ||
| * @param [options.spawnTimeout] Time to wait before reporting a failed child process spawn | ||
| * @param [options.spawnDelay] Time to wait before spawning another child process | ||
| * @param [options.autoRestart] Whether to automatically restart shards that have been killed unintentionally | ||
| * @param [options.waitForReady] Whether to wait for clusters to be ready before spawning a new one | ||
| * @param [options.handleConcurrency] Whether you want to handle concurrency properly. Enabling this may result into more stable connection | ||
| * @param [options.client] A Discord.js client class or a modified Discord.js client class | ||
| * @param options.token Discord bot token | ||
| */ | ||
| constructor(options: IndomitableOptions); | ||
| /** | ||
| * Checks the internal private flag if Indomitable is busy | ||
| * @returns Number of clusters in queue | ||
| */ | ||
| get isBusy(): boolean; | ||
| /** | ||
| * Gets how many clusters are waiting to be spawned | ||
| * @returns Number of clusters in queue | ||
| */ | ||
| get inSpawnQueueCount(): number; | ||
| /** | ||
| * Gets the current session info of the bot token Indomitable currently handles | ||
| * @returns Session Info | ||
| */ | ||
| fetchSessions(force?: boolean): Promise<SessionObject>; | ||
| /** | ||
| * Spawn a new ShardClient if this instance is a child process, or start a new cluster and IPC server if this instance is the primary process | ||
| * @returns A promise that resolves to void | ||
| */ | ||
| spawn(): Promise<void>; | ||
| /** | ||
| * Restart specified cluster if this instance is the primary process | ||
| * @param clusterId Id of cluster to restart | ||
| * @returns A promise that resolves to void | ||
| */ | ||
| restart(clusterId: number): Promise<void>; | ||
| /** | ||
| * Restart all clusters if this instance is the primary process | ||
| * @returns A promise that resolves to void | ||
| */ | ||
| restartAll(): Promise<void>; | ||
| /** | ||
| * Sends a message to a specific cluster | ||
| * @returns A promise that resolves to undefined or an unknown value depending on how you reply to it | ||
| */ | ||
| send(id: number, transportable: Transportable): Promise<unknown | undefined>; | ||
| /** | ||
| * Sends a message on all clusters | ||
| * @returns An array of promise that resolves to undefined or an unknown value depending on how you reply to it | ||
| */ | ||
| broadcast(transportable: Transportable): Promise<unknown[] | undefined>; | ||
| /** | ||
| * Reconfigures to launch more shards / clusters without killing the existing processes if possible to avoid big downtimes | ||
| * @remarks Never execute restart() or restartAll() during this process or else you will double restart that cluster / all clusters | ||
| * @returns A promise that resolves to void | ||
| */ | ||
| reconfigure(options?: ReconfigureOptions): Promise<void>; | ||
| /** | ||
| * Adds a cluster to spawn queue | ||
| * @internal | ||
| */ | ||
| addToSpawnQueue(...clusters: ClusterManager[]): Promise<void>; | ||
| /** | ||
| * Destroys the client on a cluster | ||
| * @internal | ||
| */ | ||
| private destroyClusterClient; | ||
| /** | ||
| * Processes the cluster queue | ||
| * @internal | ||
| */ | ||
| private processQueue; | ||
| } | ||
| interface PartialInternalEvents { | ||
| op: ClientEvents; | ||
| data: { | ||
| clusterId: number; | ||
| shardId?: number; | ||
| replayed?: number; | ||
| event?: CloseEvent; | ||
| ipcId?: string; | ||
| }; | ||
| } | ||
| declare class ShardClient { | ||
| readonly manager: Indomitable; | ||
| readonly client: Client; | ||
| readonly clusterId: number; | ||
| constructor(manager: Indomitable); | ||
| start(token: string): Promise<void>; | ||
| private send; | ||
| } | ||
| /** | ||
| * Basic worker ipc class, basic child process ipc handler | ||
| */ | ||
| declare class BaseWorker extends BaseIpc { | ||
| constructor(manager?: Indomitable | EventEmitter); | ||
| protected available(): boolean; | ||
| protected sendData(data: RawIpcMessage): void; | ||
| protected handleMessage(message: Message): Promise<void>; | ||
| } | ||
| /** | ||
| * Extended worker ipc class, shard client util ipc class | ||
| */ | ||
| declare class ClientWorker extends BaseWorker { | ||
| readonly shard: ShardClientUtil; | ||
| constructor(shard: ShardClientUtil, manager: Indomitable); | ||
| protected emitMessage(message: Message): void; | ||
| protected handleMessage(message: Message): Promise<void>; | ||
| } | ||
| declare interface ShardClientUtil { | ||
| /** | ||
| * Emitted when an IPC message from parent process is received | ||
| * @eventProperty | ||
| */ | ||
| on(event: 'message', listener: (message: Message | unknown) => void): this; | ||
| once(event: 'message', listener: (message: Message | unknown) => void): this; | ||
| off(event: 'message', listener: (message: Message | unknown) => void): this; | ||
| } | ||
| /** | ||
| * A class that replaces d.js stock shard client util. The class is built similar to it with minor changes | ||
| */ | ||
| declare class ShardClientUtil extends EventEmitter { | ||
| client: Client; | ||
| readonly ipc: ClientWorker; | ||
| readonly clusterId: number; | ||
| readonly clusterCount: number; | ||
| readonly shardIds: number[]; | ||
| readonly shardCount: number; | ||
| constructor(client: Client, manager: Indomitable); | ||
| /** | ||
| * Gets the current ipc delay | ||
| * @returns A promise that resolves to delay in nanoseconds | ||
| */ | ||
| ping(): Promise<number>; | ||
| /** | ||
| * Evaluates a script or function on all clusters in the context of the client | ||
| * @returns A promise that resolves to an array of code results | ||
| */ | ||
| broadcastEval(script: Function, context?: any): Promise<unknown[]>; | ||
| /** | ||
| * Fetches a client property value on all clusters | ||
| * @returns A promise that resolves to an array of code results | ||
| */ | ||
| fetchClientValues(prop: string): Promise<unknown[]>; | ||
| /** | ||
| * Gets the cached session info or fetches an updated session info | ||
| * @param update If you want to fetch and update the cached session info | ||
| * @returns A session object | ||
| */ | ||
| fetchSessions(update?: boolean): Promise<SessionObject>; | ||
| /** | ||
| * Restarts the given cluster from the clusterId given | ||
| * @returns A promise that resolves to void | ||
| */ | ||
| restart(clusterId: number): Promise<undefined>; | ||
| /** | ||
| * Restarts all the clusters Indomitable handles sequentially | ||
| * @returns A promise that resolves to void | ||
| */ | ||
| restartAll(): Promise<undefined>; | ||
| /** | ||
| * Sends a message to primary process | ||
| * @returns A promise that resolves to void or a repliable object | ||
| */ | ||
| send(transportable: Transportable): Promise<unknown | undefined>; | ||
| } | ||
| interface WorkerData extends FetchingStrategyOptions { | ||
| shardId: number; | ||
| } | ||
| interface IndomitableWorker { | ||
| thread: Worker$1; | ||
| ipc: MainStrategyWorker; | ||
| } | ||
| interface IndomitableStrategyOptions { | ||
| path?: string; | ||
| resourceLimits?: ResourceLimits; | ||
| } | ||
| declare class IndomitableStrategy implements IShardingStrategy { | ||
| readonly manager: WebSocketManager; | ||
| readonly ipc: BaseWorker; | ||
| readonly workers: Collection<number, IndomitableWorker>; | ||
| private readonly options; | ||
| constructor(manager: WebSocketManager, ipc: BaseWorker, options?: IndomitableStrategyOptions); | ||
| spawn(shardIds: number[]): Promise<void>; | ||
| connect(): Promise<void>; | ||
| destroy(data?: Omit<WebSocketShardDestroyOptions, 'recover'>): Promise<void>; | ||
| reconnect(shardId: number): Promise<void>; | ||
| send(shardId: number, data: any): Promise<void>; | ||
| fetchStatus(): Promise<Collection<number, WebSocketShardStatus>>; | ||
| private createWorker; | ||
| } | ||
| declare class MainStrategyWorker extends BaseIpc { | ||
| readonly id: number; | ||
| readonly thread: Worker$1; | ||
| readonly strategy: IndomitableStrategy; | ||
| constructor(id: number, thread: Worker$1, strategy: IndomitableStrategy); | ||
| protected available(): boolean; | ||
| protected sendData(data: RawIpcMessage): void; | ||
| protected handleMessage(message: Message): Promise<void>; | ||
| } | ||
| declare class ThreadStrategyWorker extends BaseIpc { | ||
| private shard; | ||
| constructor(); | ||
| build(shard: WebSocketShard): void; | ||
| protected available(): boolean; | ||
| protected sendData(data: RawIpcMessage): void; | ||
| protected handleMessage(message: Message): Promise<void>; | ||
| } | ||
| declare class IndomitableFetchingStrategy implements IContextFetchingStrategy { | ||
| private readonly ipc; | ||
| readonly options: FetchingStrategyOptions; | ||
| constructor(ipc: ThreadStrategyWorker, options: FetchingStrategyOptions); | ||
| retrieveSessionInfo(shardId: number): Promise<SessionInfo | null>; | ||
| updateSessionInfo(shardId: number, sessionInfo: SessionInfo | null): Promise<void>; | ||
| waitForIdentify(shardId: number, signal: AbortSignal): Promise<void>; | ||
| private abortIdentify; | ||
| } | ||
| export { BaseIpc, BaseWorker, ClientWorker, ClusterManager, type ClusterManagerOptions, ConcurrencyManager, Indomitable, IndomitableFetchingStrategy, type IndomitableOptions, IndomitableStrategy, type IndomitableStrategyOptions, type IndomitableWorker, MainStrategyWorker, MainWorker, type PartialInternalEvents, type ReconfigureOptions, ShardClient, ShardClientUtil, type ShardEventData, ThreadStrategyWorker, Util as Utils, type WorkerData }; |
| export { } |
+4
-29
@@ -1,2 +0,2 @@ | ||
| import { ClientOptions, Client, Collection } from 'discord.js'; | ||
| import { ClientOptions, Client } from 'discord.js'; | ||
| import { Worker, ClusterSettings } from 'node:cluster'; | ||
@@ -8,2 +8,3 @@ import EventEmitter from 'node:events'; | ||
| import { Worker as Worker$1, ResourceLimits } from 'node:worker_threads'; | ||
| import { Collection } from '@discordjs/collection'; | ||
@@ -278,29 +279,3 @@ /** | ||
| declare namespace Util { | ||
| export { | ||
| Util_AbortableData as AbortableData, | ||
| Util_Chunk as Chunk, | ||
| Util_ClientEventData as ClientEventData, | ||
| Util_ClientEvents as ClientEvents, | ||
| Util_Delay as Delay, | ||
| Util_EnvProcessData as EnvProcessData, | ||
| Util_Fetch as Fetch, | ||
| Util_FetchSessions as FetchSessions, | ||
| Util_InternalAbortSignal as InternalAbortSignal, | ||
| Util_InternalOps as InternalOps, | ||
| Util_InternalOpsData as InternalOpsData, | ||
| Util_InternalPromise as InternalPromise, | ||
| Util_IpcErrorData as IpcErrorData, | ||
| Util_LibraryEvents as LibraryEvents, | ||
| Util_MainStrategyData as MainStrategyData, | ||
| Util_MainStrategyOps as MainStrategyOps, | ||
| Util_MakeAbortableRequest as MakeAbortableRequest, | ||
| Util_Message as Message, | ||
| Util_RawIpcMessage as RawIpcMessage, | ||
| Util_RawIpcMessageType as RawIpcMessageType, | ||
| Util_SavePromiseOptions as SavePromiseOptions, | ||
| Util_SessionObject as SessionObject, | ||
| Util_ThreadStrategyData as ThreadStrategyData, | ||
| Util_ThreadStrategyOps as ThreadStrategyOps, | ||
| Util_Transportable as Transportable, | ||
| }; | ||
| export { type Util_AbortableData as AbortableData, Util_Chunk as Chunk, type Util_ClientEventData as ClientEventData, Util_ClientEvents as ClientEvents, Util_Delay as Delay, Util_EnvProcessData as EnvProcessData, Util_Fetch as Fetch, Util_FetchSessions as FetchSessions, type Util_InternalAbortSignal as InternalAbortSignal, Util_InternalOps as InternalOps, type Util_InternalOpsData as InternalOpsData, type Util_InternalPromise as InternalPromise, type Util_IpcErrorData as IpcErrorData, Util_LibraryEvents as LibraryEvents, type Util_MainStrategyData as MainStrategyData, Util_MainStrategyOps as MainStrategyOps, Util_MakeAbortableRequest as MakeAbortableRequest, type Util_Message as Message, type Util_RawIpcMessage as RawIpcMessage, Util_RawIpcMessageType as RawIpcMessageType, type Util_SavePromiseOptions as SavePromiseOptions, type Util_SessionObject as SessionObject, type Util_ThreadStrategyData as ThreadStrategyData, Util_ThreadStrategyOps as ThreadStrategyOps, type Util_Transportable as Transportable }; | ||
| } | ||
@@ -770,2 +745,2 @@ | ||
| export { BaseIpc, BaseWorker, ClientWorker, ClusterManager, ClusterManagerOptions, ConcurrencyManager, Indomitable, IndomitableFetchingStrategy, IndomitableOptions, IndomitableStrategy, IndomitableStrategyOptions, IndomitableWorker, MainStrategyWorker, MainWorker, PartialInternalEvents, ReconfigureOptions, ShardClient, ShardClientUtil, ShardEventData, ThreadStrategyWorker, Util as Utils, WorkerData }; | ||
| export { BaseIpc, BaseWorker, ClientWorker, ClusterManager, type ClusterManagerOptions, ConcurrencyManager, Indomitable, IndomitableFetchingStrategy, type IndomitableOptions, IndomitableStrategy, type IndomitableStrategyOptions, type IndomitableWorker, MainStrategyWorker, MainWorker, type PartialInternalEvents, type ReconfigureOptions, ShardClient, ShardClientUtil, type ShardEventData, ThreadStrategyWorker, Util as Utils, type WorkerData }; |
+3
-3
@@ -181,3 +181,3 @@ "use strict"; | ||
| var import_ws = require("@discordjs/ws"); | ||
| var import_discord = require("discord.js"); | ||
| var import_collection = require("@discordjs/collection"); | ||
@@ -390,3 +390,3 @@ // src/ipc/MainStrategyWorker.ts | ||
| this.ipc = ipc; | ||
| this.workers = new import_discord.Collection(); | ||
| this.workers = new import_collection.Collection(); | ||
| this.options = options; | ||
@@ -446,3 +446,3 @@ } | ||
| async fetchStatus() { | ||
| const collection = new import_discord.Collection(); | ||
| const collection = new import_collection.Collection(); | ||
| const promises = this.workers.map(async (worker, id) => { | ||
@@ -449,0 +449,0 @@ const content = { |
+1
-1
@@ -148,3 +148,3 @@ var __defProp = Object.defineProperty; | ||
| } from "@discordjs/ws"; | ||
| import { Collection } from "discord.js"; | ||
| import { Collection } from "@discordjs/collection"; | ||
@@ -151,0 +151,0 @@ // src/ipc/MainStrategyWorker.ts |
| export { } | ||
| export { } |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../../src/strategy/Thread.ts","../../../src/strategy/IndomitableFetchingStrategy.ts","../../../src/Util.ts","../../../src/ipc/ThreadStrategyWorker.ts","../../../src/ipc/BaseIpc.ts"],"sourcesContent":["import { WebSocketShard, WebSocketShardEvents } from '@discordjs/ws';\nimport { workerData } from 'worker_threads';\nimport { WorkerData } from './IndomitableStrategy';\nimport { IndomitableFetchingStrategy } from './IndomitableFetchingStrategy';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nconst options = workerData as WorkerData;\n\nconst ipc = new ThreadStrategyWorker();\nconst strategy = new IndomitableFetchingStrategy(ipc, options);\nconst shard = new WebSocketShard(strategy, options.shardId);\n\nipc.build(shard);\n\nfor (const event of Object.values(WebSocketShardEvents)) {\n // @ts-expect-error: unknown fix\n shard.on(event, data => {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.SHARD_EVENT,\n event,\n data,\n shardId: shard.id,\n internal: true\n };\n ipc.send({ content })\n .catch(() => null);\n });\n}\n\n\n\n\n\n","import { FetchingStrategyOptions, IContextFetchingStrategy, SessionInfo, WebSocketShardEvents } from '@discordjs/ws';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nexport class IndomitableFetchingStrategy implements IContextFetchingStrategy {\n private readonly ipc: ThreadStrategyWorker;\n public readonly options: FetchingStrategyOptions;\n constructor(ipc: ThreadStrategyWorker, options: FetchingStrategyOptions) {\n this.ipc = ipc;\n this.options = options;\n }\n\n public async retrieveSessionInfo(shardId: number): Promise<SessionInfo | null> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.RETRIEVE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n return await this.ipc.send({ content, repliable: true }) as SessionInfo;\n }\n\n public async updateSessionInfo(shardId: number, sessionInfo: SessionInfo | null): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.UPDATE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId, sessionInfo },\n shardId: shardId,\n internal: true\n };\n await this.ipc.send({ content });\n }\n\n public async waitForIdentify(shardId: number, signal: AbortSignal): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.REQUEST_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n const listener = () => this.abortIdentify(shardId);\n try {\n signal.addEventListener('abort', listener);\n await this.ipc.send({ content, repliable: true });\n } finally {\n signal.removeEventListener('abort', listener);\n }\n }\n\n private abortIdentify(shardId: number): void {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.CANCEL_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n this.ipc\n .send({ content, repliable: false })\n .catch(() => null);\n }\n}\n","import Https, { RequestOptions } from 'node:https';\nimport { WebSocketShardEvents } from '@discordjs/ws';\n\n/**\n * Hoisted Environmental Variable for ease of fetching\n */\nexport const EnvProcessData = {\n clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0),\n clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0),\n shardIds: (process.env.INDOMITABLE_SHARDS || '').split(' ').map(Number),\n shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0)\n};\n\n/**\n * Internal operation codes for the cluster -> thread\n */\nexport enum MainStrategyOps {\n CONNECT = 'connect',\n DESTROY = 'destroy',\n SEND = 'send',\n STATUS = 'status',\n RECONNECT = 'reconnect'\n}\n\n/**\n * Internal operation codes for the thread <- cluster\n */\nexport enum ThreadStrategyOps {\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SHARD_EVENT = 'shardEvent',\n RETRIEVE_SESSION = 'retrieveSession',\n UPDATE_SESSION = 'updateSession'\n}\n\n/**\n * Internal operation codes\n */\nexport enum InternalOps {\n EVAL = 'eval',\n RESTART = 'restart',\n RESTART_ALL = 'restartAll',\n DESTROY_CLIENT = 'destroyClient',\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SESSION_INFO = 'sessionInfo',\n PING = 'ping'\n}\n\n/**\n * Events for internal use\n */\nexport enum ClientEvents {\n READY = 'ready',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n ERROR = 'ERROR'\n}\n\n/**\n * Events emitted by Indomitable\n */\nexport enum LibraryEvents {\n DEBUG = 'debug',\n MESSAGE = 'message',\n ERROR = 'error',\n WORKER_FORK = 'workerFork',\n WORKER_READY = 'workerReady',\n WORKER_EXIT = 'workerExit',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n CLIENT_READY = 'clientReady',\n RAW = 'raw'\n}\n\n/**\n * Type for raw ipc message\n */\nexport enum RawIpcMessageType {\n MESSAGE = 'message',\n RESPONSE = 'response',\n ERROR = 'error'\n}\n\n/**\n * Type for raw ipc messages of cluster -> thread\n */\nexport interface MainStrategyData {\n op: MainStrategyOps,\n data: any,\n internal: true\n}\n\n/**\n * Type for raw ipc messages of cluster <- thread\n */\nexport interface ThreadStrategyData {\n op: ThreadStrategyOps,\n event: WebSocketShardEvents,\n data: any,\n shardId: number,\n internal: true\n}\n\n/**\n * Data structure representing an internal event\n */\nexport interface InternalOpsData {\n op: InternalOps,\n data: any,\n internal: true\n}\n\n/**\n * Data structure representing an internal discord.js event\n */\nexport interface ClientEventData {\n op: ClientEvents,\n data: any,\n internal: true,\n}\n\n/**\n * Data structure representing an internal error\n */\nexport interface IpcErrorData {\n name: string;\n reason: string;\n stack: string;\n}\n\n/**\n * Data structure representing IPC data\n */\nexport interface Transportable {\n content: any;\n repliable?: boolean;\n signal?: AbortSignal\n}\n\n/**\n * Data structure representing an internal abort data\n */\nexport interface InternalAbortSignal {\n listener: () => void,\n signal: AbortSignal\n}\n\nexport interface SavePromiseOptions {\n id: string;\n resolve: (data: unknown) => void;\n reject: (reason: unknown) => void;\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Data structure representing a generated abort controller instance\n */\nexport interface AbortableData {\n controller: AbortController;\n timeout: NodeJS.Timeout;\n}\n\n/**\n * Internal promise data tracking\n */\nexport interface InternalPromise {\n resolve: Function;\n reject: Function;\n controller?: InternalAbortSignal;\n}\n\n/**\n * Data structure representing internal IPC data\n */\nexport interface RawIpcMessage {\n id: string|null;\n content: any;\n internal: true;\n type: RawIpcMessageType\n}\n\n/**\n * Data structure representing an IPC message\n */\nexport interface Message {\n reply: (data: any) => void;\n content: any;\n repliable: boolean;\n}\n\n/**\n * Data structure representing a Discord session\n */\nexport interface SessionObject {\n\turl: string;\n\tshards: number;\n\tsession_start_limit: {\n\t\ttotal: number;\n\t\tremaining: number;\n\t\treset_after: number;\n max_concurrency: number;\n\t};\n}\n\n/**\n * Wrapper function for fetching data using HTTP\n * @param url URL of resource to fetch\n * @param options RequestOptions to modify behavior\n * @returns A promise containing data fetched, or an error\n */\nexport function Fetch(url: string|URL, options: RequestOptions): Promise<any> {\n return new Promise((resolve, reject) => {\n const request = Https.request(url, options, response => {\n const chunks: any[] = [];\n response.on('data', chunk => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n const code = response.statusCode ?? 500;\n const body = chunks.join('');\n if (code >= 200 && code <= 299)\n resolve(body);\n else\n reject(new Error(`Response received is not ok, Status Code: ${response.statusCode}, body: ${body}`));\n });\n });\n request.on('error', reject);\n request.end();\n });\n}\n\n/**\n * Fetch sessions from discord\n * @param token Bot token\n * @returns A promise containing a session object\n */\nexport async function FetchSessions(token: string): Promise<SessionObject> {\n const url = new URL('https://discord.com/api/v10/gateway/bot');\n const data = await Fetch(url, {\n method: 'GET',\n headers: { authorization: `Bot ${token}` }\n });\n return JSON.parse(data);\n}\n\n/**\n * Modify an array to contain the specified amount of chunks\n * @param original An array of data\n * @param chunks The amount of chunks to transform into\n * @returns A modified array\n */\nexport function Chunk(original: any[], chunks: number): any[] {\n const array = [];\n for (let i = 0; i < original.length; i += chunks)\n array.push(original.slice(i , i + chunks));\n return array;\n}\n\n/**\n * Wait for a specific amount of time (timeout)\n * @param ms Time to wait in milliseconds\n * @returns A promise that resolves in x seconds\n */\nexport function Delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(() => resolve(), ms));\n}\n\n/**\n * Creates an abortable request with controller and timeout\n * @param delay Time before an abort error throws\n * @returns An abortable data with controller and timeout\n */\nexport function MakeAbortableRequest(delay: number): AbortableData {\n const controller = new AbortController();\n const seconds = Math.round(delay / 1000);\n const timeout = setTimeout(\n () => controller.abort(new Error(`The request has been aborted in ${seconds} second(s)`)),\n delay\n );\n return { controller, timeout };\n}\n","import EventEmitter from 'node:events';\nimport { parentPort } from 'worker_threads';\nimport { WebSocketShard } from '@discordjs/ws';\nimport { BaseIpc } from './BaseIpc';\nimport {\n MainStrategyData,\n MainStrategyOps,\n Message,\n RawIpcMessage,\n} from '../Util';\n\nexport class ThreadStrategyWorker extends BaseIpc {\n private shard: WebSocketShard|undefined;\n constructor() {\n super(new EventEmitter());\n parentPort!.on('message', message => this.handleRawResponse(message, () => null));\n }\n\n public build(shard: WebSocketShard): void {\n if (!this.shard) this.shard = shard;\n }\n\n protected available(): boolean {\n return !!parentPort;\n }\n\n protected sendData(data: RawIpcMessage) {\n return parentPort!.postMessage(data);\n }\n\n protected async handleMessage(message: Message): Promise<void> {\n const content = message.content as MainStrategyData;\n if (!this.shard) throw new Error('Shard isn\\'t initialized yet');\n switch(content.op) {\n case MainStrategyOps.CONNECT:\n await this.shard.connect();\n message.reply(null);\n break;\n case MainStrategyOps.DESTROY:\n await this.shard.destroy(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.SEND:\n await this.shard.send(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.RECONNECT:\n await this.shard.destroy(content.data);\n message.reply(null);\n break;\n case MainStrategyOps.STATUS:\n message.reply(this.shard.status);\n break;\n }\n }\n}\n","import EventEmitter from 'node:events';\nimport { Serializable } from 'node:child_process';\nimport { randomUUID } from 'crypto';\nimport { Indomitable } from '../Indomitable.js';\nimport {\n InternalAbortSignal,\n InternalPromise,\n IpcErrorData,\n LibraryEvents,\n Message,\n RawIpcMessage,\n RawIpcMessageType,\n SavePromiseOptions,\n Transportable\n} from '../Util.js';\n\n/**\n * Base class where primary and worker ipc inherits\n */\nexport abstract class BaseIpc {\n public readonly manager: Indomitable|EventEmitter;\n protected readonly promises: Map<string, InternalPromise>;\n protected constructor(manager: Indomitable|EventEmitter) {\n this.manager = manager;\n this.promises = new Map();\n }\n\n /**\n * Number of promises pending to be resolved\n */\n public get pendingPromises(): number {\n return this.promises.size;\n }\n\n /**\n * Rejects all the pending promises\n */\n public flushPromises(reason: string): void {\n const error = new Error(reason);\n for (const promise of this.promises.values()) {\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n promise.reject(error);\n }\n this.promises.clear();\n }\n\n /**\n * Raw send method without abort controller handling\n * @param transportable Data to send\n */\n public send(transportable: Transportable): Promise<unknown|undefined> {\n return new Promise((resolve, reject) => {\n if (!this.available()) {\n this.manager.emit(LibraryEvents.DEBUG, 'IPC tried to send a message, but the ipc communication is not yet ready');\n return resolve(undefined);\n }\n const repliable = transportable.repliable || false;\n const id = repliable ? randomUUID() : null;\n const data: RawIpcMessage = {\n id,\n content: transportable.content,\n internal: true,\n type: RawIpcMessageType.MESSAGE\n };\n this.sendData(data);\n if (!id) return resolve(undefined);\n this.waitForPromise({ id, resolve, reject, signal: transportable.signal });\n });\n }\n\n /**\n * Taps into message event of worker or primary process to handle ipc communication\n * @internal\n */\n public async handleRawResponse(data: Serializable, errorCallback: (error: unknown) => any): Promise<boolean|void> {\n try {\n this.manager.emit(LibraryEvents.RAW, data);\n if (!(data as any).internal) return;\n switch((data as RawIpcMessage).type) {\n case RawIpcMessageType.MESSAGE:\n return await this.handleUnparsedMessage(data as RawIpcMessage);\n case RawIpcMessageType.RESPONSE:\n case RawIpcMessageType.ERROR:\n return this.handlePromise(data as RawIpcMessage);\n }\n } catch (error: unknown) {\n errorCallback(error);\n }\n }\n\n protected waitForPromise(options: SavePromiseOptions): void {\n let controller: InternalAbortSignal|undefined;\n if (options.signal) {\n const listener = () => {\n this.promises.delete(options.id);\n options.reject(new Error('This operation is aborted'));\n };\n controller = {\n listener,\n signal: options.signal\n };\n controller.signal.addEventListener('abort', listener);\n }\n this.promises.set(options.id, { resolve: options.resolve, reject: options.reject, controller } as InternalPromise);\n }\n\n private handlePromise(data: RawIpcMessage): void {\n const id = data.id as string;\n const promise = this.promises.get(id);\n if (!promise) return;\n this.promises.delete(id);\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n if (data.type === RawIpcMessageType.ERROR) {\n const content = data.content as IpcErrorData;\n const error = new Error(content.reason);\n error.stack = content.stack;\n error.name = content.name;\n promise.reject(error);\n return;\n }\n promise.resolve(data.content);\n }\n\n private async handleUnparsedMessage(data: RawIpcMessage): Promise<void> {\n const reply = (content: any) => {\n if (!data.id) return;\n const response: RawIpcMessage = {\n id: data.id,\n content,\n internal: true,\n type: RawIpcMessageType.RESPONSE\n };\n this.sendData(response);\n };\n const message: Message = {\n repliable: !!data.id,\n content: data.content,\n reply\n };\n if (!data.content.internal)\n return this.emitMessage(message);\n try {\n await this.handleMessage(message);\n } catch (error: any) {\n if (!message.repliable) return;\n const response: RawIpcMessage = {\n id: data.id,\n content: {\n name: error.name,\n reason: error.reason,\n stack: error.stack\n },\n internal: true,\n type: RawIpcMessageType.ERROR\n };\n this.sendData(response);\n }\n }\n\n protected emitMessage(message: Message): void {\n this.manager.emit(LibraryEvents.MESSAGE, message);\n }\n\n protected abstract available(): boolean;\n protected abstract sendData(data: RawIpcMessage): void;\n protected abstract handleMessage(message: Message): Promise<void>;\n}\n"],"mappings":"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","names":["import_ws","import_worker_threads","ipc","options","options","EventEmitter","shard"]} | ||
| {"version":3,"sources":["../../../src/strategy/Thread.ts","../../../src/strategy/IndomitableFetchingStrategy.ts","../../../src/Util.ts","../../../src/ipc/ThreadStrategyWorker.ts","../../../src/ipc/BaseIpc.ts"],"sourcesContent":["import { WebSocketShard, WebSocketShardEvents } from '@discordjs/ws';\nimport { workerData } from 'worker_threads';\nimport { WorkerData } from './IndomitableStrategy';\nimport { IndomitableFetchingStrategy } from './IndomitableFetchingStrategy';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nconst options = workerData as WorkerData;\n\nconst ipc = new ThreadStrategyWorker();\nconst strategy = new IndomitableFetchingStrategy(ipc, options);\nconst shard = new WebSocketShard(strategy, options.shardId);\n\nipc.build(shard);\n\nfor (const event of Object.values(WebSocketShardEvents)) {\n // @ts-expect-error: unknown fix\n shard.on(event, data => {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.SHARD_EVENT,\n event,\n data,\n shardId: shard.id,\n internal: true\n };\n ipc.send({ content })\n .catch(() => null);\n });\n}\n\n\n\n\n\n","import { FetchingStrategyOptions, IContextFetchingStrategy, SessionInfo, WebSocketShardEvents } from '@discordjs/ws';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nexport class IndomitableFetchingStrategy implements IContextFetchingStrategy {\n private readonly ipc: ThreadStrategyWorker;\n public readonly options: FetchingStrategyOptions;\n constructor(ipc: ThreadStrategyWorker, options: FetchingStrategyOptions) {\n this.ipc = ipc;\n this.options = options;\n }\n\n public async retrieveSessionInfo(shardId: number): Promise<SessionInfo | null> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.RETRIEVE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n return await this.ipc.send({ content, repliable: true }) as SessionInfo;\n }\n\n public async updateSessionInfo(shardId: number, sessionInfo: SessionInfo | null): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.UPDATE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId, sessionInfo },\n shardId: shardId,\n internal: true\n };\n await this.ipc.send({ content });\n }\n\n public async waitForIdentify(shardId: number, signal: AbortSignal): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.REQUEST_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n const listener = () => this.abortIdentify(shardId);\n try {\n signal.addEventListener('abort', listener);\n await this.ipc.send({ content, repliable: true });\n } finally {\n signal.removeEventListener('abort', listener);\n }\n }\n\n private abortIdentify(shardId: number): void {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.CANCEL_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n this.ipc\n .send({ content, repliable: false })\n .catch(() => null);\n }\n}\n","import Https, { RequestOptions } from 'node:https';\nimport { WebSocketShardEvents } from '@discordjs/ws';\n\n/**\n * Hoisted Environmental Variable for ease of fetching\n */\nexport const EnvProcessData = {\n clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0),\n clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0),\n shardIds: (process.env.INDOMITABLE_SHARDS || '').split(' ').map(Number),\n shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0)\n};\n\n/**\n * Internal operation codes for the cluster -> thread\n */\nexport enum MainStrategyOps {\n CONNECT = 'connect',\n DESTROY = 'destroy',\n SEND = 'send',\n STATUS = 'status',\n RECONNECT = 'reconnect'\n}\n\n/**\n * Internal operation codes for the thread <- cluster\n */\nexport enum ThreadStrategyOps {\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SHARD_EVENT = 'shardEvent',\n RETRIEVE_SESSION = 'retrieveSession',\n UPDATE_SESSION = 'updateSession'\n}\n\n/**\n * Internal operation codes\n */\nexport enum InternalOps {\n EVAL = 'eval',\n RESTART = 'restart',\n RESTART_ALL = 'restartAll',\n DESTROY_CLIENT = 'destroyClient',\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SESSION_INFO = 'sessionInfo',\n PING = 'ping'\n}\n\n/**\n * Events for internal use\n */\nexport enum ClientEvents {\n READY = 'ready',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n ERROR = 'ERROR'\n}\n\n/**\n * Events emitted by Indomitable\n */\nexport enum LibraryEvents {\n DEBUG = 'debug',\n MESSAGE = 'message',\n ERROR = 'error',\n WORKER_FORK = 'workerFork',\n WORKER_READY = 'workerReady',\n WORKER_EXIT = 'workerExit',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n CLIENT_READY = 'clientReady',\n RAW = 'raw'\n}\n\n/**\n * Type for raw ipc message\n */\nexport enum RawIpcMessageType {\n MESSAGE = 'message',\n RESPONSE = 'response',\n ERROR = 'error'\n}\n\n/**\n * Type for raw ipc messages of cluster -> thread\n */\nexport interface MainStrategyData {\n op: MainStrategyOps,\n data: any,\n internal: true\n}\n\n/**\n * Type for raw ipc messages of cluster <- thread\n */\nexport interface ThreadStrategyData {\n op: ThreadStrategyOps,\n event: WebSocketShardEvents,\n data: any,\n shardId: number,\n internal: true\n}\n\n/**\n * Data structure representing an internal event\n */\nexport interface InternalOpsData {\n op: InternalOps,\n data: any,\n internal: true\n}\n\n/**\n * Data structure representing an internal discord.js event\n */\nexport interface ClientEventData {\n op: ClientEvents,\n data: any,\n internal: true,\n}\n\n/**\n * Data structure representing an internal error\n */\nexport interface IpcErrorData {\n name: string;\n reason: string;\n stack: string;\n}\n\n/**\n * Data structure representing IPC data\n */\nexport interface Transportable {\n content: any;\n repliable?: boolean;\n signal?: AbortSignal\n}\n\n/**\n * Data structure representing an internal abort data\n */\nexport interface InternalAbortSignal {\n listener: () => void,\n signal: AbortSignal\n}\n\nexport interface SavePromiseOptions {\n id: string;\n resolve: (data: unknown) => void;\n reject: (reason: unknown) => void;\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Data structure representing a generated abort controller instance\n */\nexport interface AbortableData {\n controller: AbortController;\n timeout: NodeJS.Timeout;\n}\n\n/**\n * Internal promise data tracking\n */\nexport interface InternalPromise {\n resolve: Function;\n reject: Function;\n controller?: InternalAbortSignal;\n}\n\n/**\n * Data structure representing internal IPC data\n */\nexport interface RawIpcMessage {\n id: string|null;\n content: any;\n internal: true;\n type: RawIpcMessageType\n}\n\n/**\n * Data structure representing an IPC message\n */\nexport interface Message {\n reply: (data: any) => void;\n content: any;\n repliable: boolean;\n}\n\n/**\n * Data structure representing a Discord session\n */\nexport interface SessionObject {\n\turl: string;\n\tshards: number;\n\tsession_start_limit: {\n\t\ttotal: number;\n\t\tremaining: number;\n\t\treset_after: number;\n max_concurrency: number;\n\t};\n}\n\n/**\n * Wrapper function for fetching data using HTTP\n * @param url URL of resource to fetch\n * @param options RequestOptions to modify behavior\n * @returns A promise containing data fetched, or an error\n */\nexport function Fetch(url: string|URL, options: RequestOptions): Promise<any> {\n return new Promise((resolve, reject) => {\n const request = Https.request(url, options, response => {\n const chunks: any[] = [];\n response.on('data', chunk => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n const code = response.statusCode ?? 500;\n const body = chunks.join('');\n if (code >= 200 && code <= 299)\n resolve(body);\n else\n reject(new Error(`Response received is not ok, Status Code: ${response.statusCode}, body: ${body}`));\n });\n });\n request.on('error', reject);\n request.end();\n });\n}\n\n/**\n * Fetch sessions from discord\n * @param token Bot token\n * @returns A promise containing a session object\n */\nexport async function FetchSessions(token: string): Promise<SessionObject> {\n const url = new URL('https://discord.com/api/v10/gateway/bot');\n const data = await Fetch(url, {\n method: 'GET',\n headers: { authorization: `Bot ${token}` }\n });\n return JSON.parse(data);\n}\n\n/**\n * Modify an array to contain the specified amount of chunks\n * @param original An array of data\n * @param chunks The amount of chunks to transform into\n * @returns A modified array\n */\nexport function Chunk(original: any[], chunks: number): any[] {\n const array = [];\n for (let i = 0; i < original.length; i += chunks)\n array.push(original.slice(i , i + chunks));\n return array;\n}\n\n/**\n * Wait for a specific amount of time (timeout)\n * @param ms Time to wait in milliseconds\n * @returns A promise that resolves in x seconds\n */\nexport function Delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(() => resolve(), ms));\n}\n\n/**\n * Creates an abortable request with controller and timeout\n * @param delay Time before an abort error throws\n * @returns An abortable data with controller and timeout\n */\nexport function MakeAbortableRequest(delay: number): AbortableData {\n const controller = new AbortController();\n const seconds = Math.round(delay / 1000);\n const timeout = setTimeout(\n () => controller.abort(new Error(`The request has been aborted in ${seconds} second(s)`)),\n delay\n );\n return { controller, timeout };\n}\n","import EventEmitter from 'node:events';\nimport { parentPort } from 'worker_threads';\nimport { WebSocketShard } from '@discordjs/ws';\nimport { BaseIpc } from './BaseIpc';\nimport {\n MainStrategyData,\n MainStrategyOps,\n Message,\n RawIpcMessage,\n} from '../Util';\n\nexport class ThreadStrategyWorker extends BaseIpc {\n private shard: WebSocketShard|undefined;\n constructor() {\n super(new EventEmitter());\n parentPort!.on('message', message => this.handleRawResponse(message, () => null));\n }\n\n public build(shard: WebSocketShard): void {\n if (!this.shard) this.shard = shard;\n }\n\n protected available(): boolean {\n return !!parentPort;\n }\n\n protected sendData(data: RawIpcMessage) {\n return parentPort!.postMessage(data);\n }\n\n protected async handleMessage(message: Message): Promise<void> {\n const content = message.content as MainStrategyData;\n if (!this.shard) throw new Error('Shard isn\\'t initialized yet');\n switch(content.op) {\n case MainStrategyOps.CONNECT:\n await this.shard.connect();\n message.reply(null);\n break;\n case MainStrategyOps.DESTROY:\n await this.shard.destroy(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.SEND:\n await this.shard.send(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.RECONNECT:\n await this.shard.destroy(content.data);\n message.reply(null);\n break;\n case MainStrategyOps.STATUS:\n message.reply(this.shard.status);\n break;\n }\n }\n}\n","import EventEmitter from 'node:events';\nimport { Serializable } from 'node:child_process';\nimport { randomUUID } from 'crypto';\nimport { Indomitable } from '../Indomitable.js';\nimport {\n InternalAbortSignal,\n InternalPromise,\n IpcErrorData,\n LibraryEvents,\n Message,\n RawIpcMessage,\n RawIpcMessageType,\n SavePromiseOptions,\n Transportable\n} from '../Util.js';\n\n/**\n * Base class where primary and worker ipc inherits\n */\nexport abstract class BaseIpc {\n public readonly manager: Indomitable|EventEmitter;\n protected readonly promises: Map<string, InternalPromise>;\n protected constructor(manager: Indomitable|EventEmitter) {\n this.manager = manager;\n this.promises = new Map();\n }\n\n /**\n * Number of promises pending to be resolved\n */\n public get pendingPromises(): number {\n return this.promises.size;\n }\n\n /**\n * Rejects all the pending promises\n */\n public flushPromises(reason: string): void {\n const error = new Error(reason);\n for (const promise of this.promises.values()) {\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n promise.reject(error);\n }\n this.promises.clear();\n }\n\n /**\n * Raw send method without abort controller handling\n * @param transportable Data to send\n */\n public send(transportable: Transportable): Promise<unknown|undefined> {\n return new Promise((resolve, reject) => {\n if (!this.available()) {\n this.manager.emit(LibraryEvents.DEBUG, 'IPC tried to send a message, but the ipc communication is not yet ready');\n return resolve(undefined);\n }\n const repliable = transportable.repliable || false;\n const id = repliable ? randomUUID() : null;\n const data: RawIpcMessage = {\n id,\n content: transportable.content,\n internal: true,\n type: RawIpcMessageType.MESSAGE\n };\n this.sendData(data);\n if (!id) return resolve(undefined);\n this.waitForPromise({ id, resolve, reject, signal: transportable.signal });\n });\n }\n\n /**\n * Taps into message event of worker or primary process to handle ipc communication\n * @internal\n */\n public async handleRawResponse(data: Serializable, errorCallback: (error: unknown) => any): Promise<boolean|void> {\n try {\n this.manager.emit(LibraryEvents.RAW, data);\n if (!(data as any).internal) return;\n switch((data as RawIpcMessage).type) {\n case RawIpcMessageType.MESSAGE:\n return await this.handleUnparsedMessage(data as RawIpcMessage);\n case RawIpcMessageType.RESPONSE:\n case RawIpcMessageType.ERROR:\n return this.handlePromise(data as RawIpcMessage);\n }\n } catch (error: unknown) {\n errorCallback(error);\n }\n }\n\n protected waitForPromise(options: SavePromiseOptions): void {\n let controller: InternalAbortSignal|undefined;\n if (options.signal) {\n const listener = () => {\n this.promises.delete(options.id);\n options.reject(new Error('This operation is aborted'));\n };\n controller = {\n listener,\n signal: options.signal\n };\n controller.signal.addEventListener('abort', listener);\n }\n this.promises.set(options.id, { resolve: options.resolve, reject: options.reject, controller } as InternalPromise);\n }\n\n private handlePromise(data: RawIpcMessage): void {\n const id = data.id as string;\n const promise = this.promises.get(id);\n if (!promise) return;\n this.promises.delete(id);\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n if (data.type === RawIpcMessageType.ERROR) {\n const content = data.content as IpcErrorData;\n const error = new Error(content.reason);\n error.stack = content.stack;\n error.name = content.name;\n promise.reject(error);\n return;\n }\n promise.resolve(data.content);\n }\n\n private async handleUnparsedMessage(data: RawIpcMessage): Promise<void> {\n const reply = (content: any) => {\n if (!data.id) return;\n const response: RawIpcMessage = {\n id: data.id,\n content,\n internal: true,\n type: RawIpcMessageType.RESPONSE\n };\n this.sendData(response);\n };\n const message: Message = {\n repliable: !!data.id,\n content: data.content,\n reply\n };\n if (!data.content.internal)\n return this.emitMessage(message);\n try {\n await this.handleMessage(message);\n } catch (error: any) {\n if (!message.repliable) return;\n const response: RawIpcMessage = {\n id: data.id,\n content: {\n name: error.name,\n reason: error.reason,\n stack: error.stack\n },\n internal: true,\n type: RawIpcMessageType.ERROR\n };\n this.sendData(response);\n }\n }\n\n protected emitMessage(message: Message): void {\n this.manager.emit(LibraryEvents.MESSAGE, message);\n }\n\n protected abstract available(): boolean;\n protected abstract sendData(data: RawIpcMessage): void;\n protected abstract handleMessage(message: Message): Promise<void>;\n}\n"],"mappings":"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","names":["import_ws","import_worker_threads","ipc","options","options","EventEmitter","shard"]} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../../src/strategy/Thread.ts","../../../src/strategy/IndomitableFetchingStrategy.ts","../../../src/Util.ts","../../../src/ipc/ThreadStrategyWorker.ts","../../../src/ipc/BaseIpc.ts"],"sourcesContent":["import { WebSocketShard, WebSocketShardEvents } from '@discordjs/ws';\nimport { workerData } from 'worker_threads';\nimport { WorkerData } from './IndomitableStrategy';\nimport { IndomitableFetchingStrategy } from './IndomitableFetchingStrategy';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nconst options = workerData as WorkerData;\n\nconst ipc = new ThreadStrategyWorker();\nconst strategy = new IndomitableFetchingStrategy(ipc, options);\nconst shard = new WebSocketShard(strategy, options.shardId);\n\nipc.build(shard);\n\nfor (const event of Object.values(WebSocketShardEvents)) {\n // @ts-expect-error: unknown fix\n shard.on(event, data => {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.SHARD_EVENT,\n event,\n data,\n shardId: shard.id,\n internal: true\n };\n ipc.send({ content })\n .catch(() => null);\n });\n}\n\n\n\n\n\n","import { FetchingStrategyOptions, IContextFetchingStrategy, SessionInfo, WebSocketShardEvents } from '@discordjs/ws';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nexport class IndomitableFetchingStrategy implements IContextFetchingStrategy {\n private readonly ipc: ThreadStrategyWorker;\n public readonly options: FetchingStrategyOptions;\n constructor(ipc: ThreadStrategyWorker, options: FetchingStrategyOptions) {\n this.ipc = ipc;\n this.options = options;\n }\n\n public async retrieveSessionInfo(shardId: number): Promise<SessionInfo | null> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.RETRIEVE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n return await this.ipc.send({ content, repliable: true }) as SessionInfo;\n }\n\n public async updateSessionInfo(shardId: number, sessionInfo: SessionInfo | null): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.UPDATE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId, sessionInfo },\n shardId: shardId,\n internal: true\n };\n await this.ipc.send({ content });\n }\n\n public async waitForIdentify(shardId: number, signal: AbortSignal): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.REQUEST_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n const listener = () => this.abortIdentify(shardId);\n try {\n signal.addEventListener('abort', listener);\n await this.ipc.send({ content, repliable: true });\n } finally {\n signal.removeEventListener('abort', listener);\n }\n }\n\n private abortIdentify(shardId: number): void {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.CANCEL_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n this.ipc\n .send({ content, repliable: false })\n .catch(() => null);\n }\n}\n","import Https, { RequestOptions } from 'node:https';\nimport { WebSocketShardEvents } from '@discordjs/ws';\n\n/**\n * Hoisted Environmental Variable for ease of fetching\n */\nexport const EnvProcessData = {\n clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0),\n clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0),\n shardIds: (process.env.INDOMITABLE_SHARDS || '').split(' ').map(Number),\n shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0)\n};\n\n/**\n * Internal operation codes for the cluster -> thread\n */\nexport enum MainStrategyOps {\n CONNECT = 'connect',\n DESTROY = 'destroy',\n SEND = 'send',\n STATUS = 'status',\n RECONNECT = 'reconnect'\n}\n\n/**\n * Internal operation codes for the thread <- cluster\n */\nexport enum ThreadStrategyOps {\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SHARD_EVENT = 'shardEvent',\n RETRIEVE_SESSION = 'retrieveSession',\n UPDATE_SESSION = 'updateSession'\n}\n\n/**\n * Internal operation codes\n */\nexport enum InternalOps {\n EVAL = 'eval',\n RESTART = 'restart',\n RESTART_ALL = 'restartAll',\n DESTROY_CLIENT = 'destroyClient',\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SESSION_INFO = 'sessionInfo',\n PING = 'ping'\n}\n\n/**\n * Events for internal use\n */\nexport enum ClientEvents {\n READY = 'ready',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n ERROR = 'ERROR'\n}\n\n/**\n * Events emitted by Indomitable\n */\nexport enum LibraryEvents {\n DEBUG = 'debug',\n MESSAGE = 'message',\n ERROR = 'error',\n WORKER_FORK = 'workerFork',\n WORKER_READY = 'workerReady',\n WORKER_EXIT = 'workerExit',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n CLIENT_READY = 'clientReady',\n RAW = 'raw'\n}\n\n/**\n * Type for raw ipc message\n */\nexport enum RawIpcMessageType {\n MESSAGE = 'message',\n RESPONSE = 'response',\n ERROR = 'error'\n}\n\n/**\n * Type for raw ipc messages of cluster -> thread\n */\nexport interface MainStrategyData {\n op: MainStrategyOps,\n data: any,\n internal: true\n}\n\n/**\n * Type for raw ipc messages of cluster <- thread\n */\nexport interface ThreadStrategyData {\n op: ThreadStrategyOps,\n event: WebSocketShardEvents,\n data: any,\n shardId: number,\n internal: true\n}\n\n/**\n * Data structure representing an internal event\n */\nexport interface InternalOpsData {\n op: InternalOps,\n data: any,\n internal: true\n}\n\n/**\n * Data structure representing an internal discord.js event\n */\nexport interface ClientEventData {\n op: ClientEvents,\n data: any,\n internal: true,\n}\n\n/**\n * Data structure representing an internal error\n */\nexport interface IpcErrorData {\n name: string;\n reason: string;\n stack: string;\n}\n\n/**\n * Data structure representing IPC data\n */\nexport interface Transportable {\n content: any;\n repliable?: boolean;\n signal?: AbortSignal\n}\n\n/**\n * Data structure representing an internal abort data\n */\nexport interface InternalAbortSignal {\n listener: () => void,\n signal: AbortSignal\n}\n\nexport interface SavePromiseOptions {\n id: string;\n resolve: (data: unknown) => void;\n reject: (reason: unknown) => void;\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Data structure representing a generated abort controller instance\n */\nexport interface AbortableData {\n controller: AbortController;\n timeout: NodeJS.Timeout;\n}\n\n/**\n * Internal promise data tracking\n */\nexport interface InternalPromise {\n resolve: Function;\n reject: Function;\n controller?: InternalAbortSignal;\n}\n\n/**\n * Data structure representing internal IPC data\n */\nexport interface RawIpcMessage {\n id: string|null;\n content: any;\n internal: true;\n type: RawIpcMessageType\n}\n\n/**\n * Data structure representing an IPC message\n */\nexport interface Message {\n reply: (data: any) => void;\n content: any;\n repliable: boolean;\n}\n\n/**\n * Data structure representing a Discord session\n */\nexport interface SessionObject {\n\turl: string;\n\tshards: number;\n\tsession_start_limit: {\n\t\ttotal: number;\n\t\tremaining: number;\n\t\treset_after: number;\n max_concurrency: number;\n\t};\n}\n\n/**\n * Wrapper function for fetching data using HTTP\n * @param url URL of resource to fetch\n * @param options RequestOptions to modify behavior\n * @returns A promise containing data fetched, or an error\n */\nexport function Fetch(url: string|URL, options: RequestOptions): Promise<any> {\n return new Promise((resolve, reject) => {\n const request = Https.request(url, options, response => {\n const chunks: any[] = [];\n response.on('data', chunk => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n const code = response.statusCode ?? 500;\n const body = chunks.join('');\n if (code >= 200 && code <= 299)\n resolve(body);\n else\n reject(new Error(`Response received is not ok, Status Code: ${response.statusCode}, body: ${body}`));\n });\n });\n request.on('error', reject);\n request.end();\n });\n}\n\n/**\n * Fetch sessions from discord\n * @param token Bot token\n * @returns A promise containing a session object\n */\nexport async function FetchSessions(token: string): Promise<SessionObject> {\n const url = new URL('https://discord.com/api/v10/gateway/bot');\n const data = await Fetch(url, {\n method: 'GET',\n headers: { authorization: `Bot ${token}` }\n });\n return JSON.parse(data);\n}\n\n/**\n * Modify an array to contain the specified amount of chunks\n * @param original An array of data\n * @param chunks The amount of chunks to transform into\n * @returns A modified array\n */\nexport function Chunk(original: any[], chunks: number): any[] {\n const array = [];\n for (let i = 0; i < original.length; i += chunks)\n array.push(original.slice(i , i + chunks));\n return array;\n}\n\n/**\n * Wait for a specific amount of time (timeout)\n * @param ms Time to wait in milliseconds\n * @returns A promise that resolves in x seconds\n */\nexport function Delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(() => resolve(), ms));\n}\n\n/**\n * Creates an abortable request with controller and timeout\n * @param delay Time before an abort error throws\n * @returns An abortable data with controller and timeout\n */\nexport function MakeAbortableRequest(delay: number): AbortableData {\n const controller = new AbortController();\n const seconds = Math.round(delay / 1000);\n const timeout = setTimeout(\n () => controller.abort(new Error(`The request has been aborted in ${seconds} second(s)`)),\n delay\n );\n return { controller, timeout };\n}\n","import EventEmitter from 'node:events';\nimport { parentPort } from 'worker_threads';\nimport { WebSocketShard } from '@discordjs/ws';\nimport { BaseIpc } from './BaseIpc';\nimport {\n MainStrategyData,\n MainStrategyOps,\n Message,\n RawIpcMessage,\n} from '../Util';\n\nexport class ThreadStrategyWorker extends BaseIpc {\n private shard: WebSocketShard|undefined;\n constructor() {\n super(new EventEmitter());\n parentPort!.on('message', message => this.handleRawResponse(message, () => null));\n }\n\n public build(shard: WebSocketShard): void {\n if (!this.shard) this.shard = shard;\n }\n\n protected available(): boolean {\n return !!parentPort;\n }\n\n protected sendData(data: RawIpcMessage) {\n return parentPort!.postMessage(data);\n }\n\n protected async handleMessage(message: Message): Promise<void> {\n const content = message.content as MainStrategyData;\n if (!this.shard) throw new Error('Shard isn\\'t initialized yet');\n switch(content.op) {\n case MainStrategyOps.CONNECT:\n await this.shard.connect();\n message.reply(null);\n break;\n case MainStrategyOps.DESTROY:\n await this.shard.destroy(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.SEND:\n await this.shard.send(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.RECONNECT:\n await this.shard.destroy(content.data);\n message.reply(null);\n break;\n case MainStrategyOps.STATUS:\n message.reply(this.shard.status);\n break;\n }\n }\n}\n","import EventEmitter from 'node:events';\nimport { Serializable } from 'node:child_process';\nimport { randomUUID } from 'crypto';\nimport { Indomitable } from '../Indomitable.js';\nimport {\n InternalAbortSignal,\n InternalPromise,\n IpcErrorData,\n LibraryEvents,\n Message,\n RawIpcMessage,\n RawIpcMessageType,\n SavePromiseOptions,\n Transportable\n} from '../Util.js';\n\n/**\n * Base class where primary and worker ipc inherits\n */\nexport abstract class BaseIpc {\n public readonly manager: Indomitable|EventEmitter;\n protected readonly promises: Map<string, InternalPromise>;\n protected constructor(manager: Indomitable|EventEmitter) {\n this.manager = manager;\n this.promises = new Map();\n }\n\n /**\n * Number of promises pending to be resolved\n */\n public get pendingPromises(): number {\n return this.promises.size;\n }\n\n /**\n * Rejects all the pending promises\n */\n public flushPromises(reason: string): void {\n const error = new Error(reason);\n for (const promise of this.promises.values()) {\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n promise.reject(error);\n }\n this.promises.clear();\n }\n\n /**\n * Raw send method without abort controller handling\n * @param transportable Data to send\n */\n public send(transportable: Transportable): Promise<unknown|undefined> {\n return new Promise((resolve, reject) => {\n if (!this.available()) {\n this.manager.emit(LibraryEvents.DEBUG, 'IPC tried to send a message, but the ipc communication is not yet ready');\n return resolve(undefined);\n }\n const repliable = transportable.repliable || false;\n const id = repliable ? randomUUID() : null;\n const data: RawIpcMessage = {\n id,\n content: transportable.content,\n internal: true,\n type: RawIpcMessageType.MESSAGE\n };\n this.sendData(data);\n if (!id) return resolve(undefined);\n this.waitForPromise({ id, resolve, reject, signal: transportable.signal });\n });\n }\n\n /**\n * Taps into message event of worker or primary process to handle ipc communication\n * @internal\n */\n public async handleRawResponse(data: Serializable, errorCallback: (error: unknown) => any): Promise<boolean|void> {\n try {\n this.manager.emit(LibraryEvents.RAW, data);\n if (!(data as any).internal) return;\n switch((data as RawIpcMessage).type) {\n case RawIpcMessageType.MESSAGE:\n return await this.handleUnparsedMessage(data as RawIpcMessage);\n case RawIpcMessageType.RESPONSE:\n case RawIpcMessageType.ERROR:\n return this.handlePromise(data as RawIpcMessage);\n }\n } catch (error: unknown) {\n errorCallback(error);\n }\n }\n\n protected waitForPromise(options: SavePromiseOptions): void {\n let controller: InternalAbortSignal|undefined;\n if (options.signal) {\n const listener = () => {\n this.promises.delete(options.id);\n options.reject(new Error('This operation is aborted'));\n };\n controller = {\n listener,\n signal: options.signal\n };\n controller.signal.addEventListener('abort', listener);\n }\n this.promises.set(options.id, { resolve: options.resolve, reject: options.reject, controller } as InternalPromise);\n }\n\n private handlePromise(data: RawIpcMessage): void {\n const id = data.id as string;\n const promise = this.promises.get(id);\n if (!promise) return;\n this.promises.delete(id);\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n if (data.type === RawIpcMessageType.ERROR) {\n const content = data.content as IpcErrorData;\n const error = new Error(content.reason);\n error.stack = content.stack;\n error.name = content.name;\n promise.reject(error);\n return;\n }\n promise.resolve(data.content);\n }\n\n private async handleUnparsedMessage(data: RawIpcMessage): Promise<void> {\n const reply = (content: any) => {\n if (!data.id) return;\n const response: RawIpcMessage = {\n id: data.id,\n content,\n internal: true,\n type: RawIpcMessageType.RESPONSE\n };\n this.sendData(response);\n };\n const message: Message = {\n repliable: !!data.id,\n content: data.content,\n reply\n };\n if (!data.content.internal)\n return this.emitMessage(message);\n try {\n await this.handleMessage(message);\n } catch (error: any) {\n if (!message.repliable) return;\n const response: RawIpcMessage = {\n id: data.id,\n content: {\n name: error.name,\n reason: error.reason,\n stack: error.stack\n },\n internal: true,\n type: RawIpcMessageType.ERROR\n };\n this.sendData(response);\n }\n }\n\n protected emitMessage(message: Message): void {\n this.manager.emit(LibraryEvents.MESSAGE, message);\n }\n\n protected abstract available(): boolean;\n protected abstract sendData(data: RawIpcMessage): void;\n protected abstract handleMessage(message: Message): Promise<void>;\n}\n"],"mappings":"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","names":["WebSocketShardEvents","ipc","options","options","shard","WebSocketShardEvents"]} | ||
| {"version":3,"sources":["../../../src/strategy/Thread.ts","../../../src/strategy/IndomitableFetchingStrategy.ts","../../../src/Util.ts","../../../src/ipc/ThreadStrategyWorker.ts","../../../src/ipc/BaseIpc.ts"],"sourcesContent":["import { WebSocketShard, WebSocketShardEvents } from '@discordjs/ws';\nimport { workerData } from 'worker_threads';\nimport { WorkerData } from './IndomitableStrategy';\nimport { IndomitableFetchingStrategy } from './IndomitableFetchingStrategy';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nconst options = workerData as WorkerData;\n\nconst ipc = new ThreadStrategyWorker();\nconst strategy = new IndomitableFetchingStrategy(ipc, options);\nconst shard = new WebSocketShard(strategy, options.shardId);\n\nipc.build(shard);\n\nfor (const event of Object.values(WebSocketShardEvents)) {\n // @ts-expect-error: unknown fix\n shard.on(event, data => {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.SHARD_EVENT,\n event,\n data,\n shardId: shard.id,\n internal: true\n };\n ipc.send({ content })\n .catch(() => null);\n });\n}\n\n\n\n\n\n","import { FetchingStrategyOptions, IContextFetchingStrategy, SessionInfo, WebSocketShardEvents } from '@discordjs/ws';\nimport { ThreadStrategyWorker } from '../ipc/ThreadStrategyWorker';\nimport { ThreadStrategyData, ThreadStrategyOps } from '../Util';\n\nexport class IndomitableFetchingStrategy implements IContextFetchingStrategy {\n private readonly ipc: ThreadStrategyWorker;\n public readonly options: FetchingStrategyOptions;\n constructor(ipc: ThreadStrategyWorker, options: FetchingStrategyOptions) {\n this.ipc = ipc;\n this.options = options;\n }\n\n public async retrieveSessionInfo(shardId: number): Promise<SessionInfo | null> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.RETRIEVE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n return await this.ipc.send({ content, repliable: true }) as SessionInfo;\n }\n\n public async updateSessionInfo(shardId: number, sessionInfo: SessionInfo | null): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.UPDATE_SESSION,\n event: WebSocketShardEvents.Ready,\n data: { shardId, sessionInfo },\n shardId: shardId,\n internal: true\n };\n await this.ipc.send({ content });\n }\n\n public async waitForIdentify(shardId: number, signal: AbortSignal): Promise<void> {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.REQUEST_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n const listener = () => this.abortIdentify(shardId);\n try {\n signal.addEventListener('abort', listener);\n await this.ipc.send({ content, repliable: true });\n } finally {\n signal.removeEventListener('abort', listener);\n }\n }\n\n private abortIdentify(shardId: number): void {\n const content: ThreadStrategyData = {\n op: ThreadStrategyOps.CANCEL_IDENTIFY,\n event: WebSocketShardEvents.Ready,\n data: { shardId },\n shardId: shardId,\n internal: true\n };\n this.ipc\n .send({ content, repliable: false })\n .catch(() => null);\n }\n}\n","import Https, { RequestOptions } from 'node:https';\nimport { WebSocketShardEvents } from '@discordjs/ws';\n\n/**\n * Hoisted Environmental Variable for ease of fetching\n */\nexport const EnvProcessData = {\n clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0),\n clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0),\n shardIds: (process.env.INDOMITABLE_SHARDS || '').split(' ').map(Number),\n shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0)\n};\n\n/**\n * Internal operation codes for the cluster -> thread\n */\nexport enum MainStrategyOps {\n CONNECT = 'connect',\n DESTROY = 'destroy',\n SEND = 'send',\n STATUS = 'status',\n RECONNECT = 'reconnect'\n}\n\n/**\n * Internal operation codes for the thread <- cluster\n */\nexport enum ThreadStrategyOps {\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SHARD_EVENT = 'shardEvent',\n RETRIEVE_SESSION = 'retrieveSession',\n UPDATE_SESSION = 'updateSession'\n}\n\n/**\n * Internal operation codes\n */\nexport enum InternalOps {\n EVAL = 'eval',\n RESTART = 'restart',\n RESTART_ALL = 'restartAll',\n DESTROY_CLIENT = 'destroyClient',\n REQUEST_IDENTIFY = 'requestIdentify',\n CANCEL_IDENTIFY = 'cancelIdentify',\n SESSION_INFO = 'sessionInfo',\n PING = 'ping'\n}\n\n/**\n * Events for internal use\n */\nexport enum ClientEvents {\n READY = 'ready',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n ERROR = 'ERROR'\n}\n\n/**\n * Events emitted by Indomitable\n */\nexport enum LibraryEvents {\n DEBUG = 'debug',\n MESSAGE = 'message',\n ERROR = 'error',\n WORKER_FORK = 'workerFork',\n WORKER_READY = 'workerReady',\n WORKER_EXIT = 'workerExit',\n SHARD_READY = 'shardReady',\n SHARD_RECONNECT = 'shardReconnect',\n SHARD_RESUME = 'shardResume',\n SHARD_DISCONNECT = 'shardDisconnect',\n CLIENT_READY = 'clientReady',\n RAW = 'raw'\n}\n\n/**\n * Type for raw ipc message\n */\nexport enum RawIpcMessageType {\n MESSAGE = 'message',\n RESPONSE = 'response',\n ERROR = 'error'\n}\n\n/**\n * Type for raw ipc messages of cluster -> thread\n */\nexport interface MainStrategyData {\n op: MainStrategyOps,\n data: any,\n internal: true\n}\n\n/**\n * Type for raw ipc messages of cluster <- thread\n */\nexport interface ThreadStrategyData {\n op: ThreadStrategyOps,\n event: WebSocketShardEvents,\n data: any,\n shardId: number,\n internal: true\n}\n\n/**\n * Data structure representing an internal event\n */\nexport interface InternalOpsData {\n op: InternalOps,\n data: any,\n internal: true\n}\n\n/**\n * Data structure representing an internal discord.js event\n */\nexport interface ClientEventData {\n op: ClientEvents,\n data: any,\n internal: true,\n}\n\n/**\n * Data structure representing an internal error\n */\nexport interface IpcErrorData {\n name: string;\n reason: string;\n stack: string;\n}\n\n/**\n * Data structure representing IPC data\n */\nexport interface Transportable {\n content: any;\n repliable?: boolean;\n signal?: AbortSignal\n}\n\n/**\n * Data structure representing an internal abort data\n */\nexport interface InternalAbortSignal {\n listener: () => void,\n signal: AbortSignal\n}\n\nexport interface SavePromiseOptions {\n id: string;\n resolve: (data: unknown) => void;\n reject: (reason: unknown) => void;\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Data structure representing a generated abort controller instance\n */\nexport interface AbortableData {\n controller: AbortController;\n timeout: NodeJS.Timeout;\n}\n\n/**\n * Internal promise data tracking\n */\nexport interface InternalPromise {\n resolve: Function;\n reject: Function;\n controller?: InternalAbortSignal;\n}\n\n/**\n * Data structure representing internal IPC data\n */\nexport interface RawIpcMessage {\n id: string|null;\n content: any;\n internal: true;\n type: RawIpcMessageType\n}\n\n/**\n * Data structure representing an IPC message\n */\nexport interface Message {\n reply: (data: any) => void;\n content: any;\n repliable: boolean;\n}\n\n/**\n * Data structure representing a Discord session\n */\nexport interface SessionObject {\n\turl: string;\n\tshards: number;\n\tsession_start_limit: {\n\t\ttotal: number;\n\t\tremaining: number;\n\t\treset_after: number;\n max_concurrency: number;\n\t};\n}\n\n/**\n * Wrapper function for fetching data using HTTP\n * @param url URL of resource to fetch\n * @param options RequestOptions to modify behavior\n * @returns A promise containing data fetched, or an error\n */\nexport function Fetch(url: string|URL, options: RequestOptions): Promise<any> {\n return new Promise((resolve, reject) => {\n const request = Https.request(url, options, response => {\n const chunks: any[] = [];\n response.on('data', chunk => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () => {\n const code = response.statusCode ?? 500;\n const body = chunks.join('');\n if (code >= 200 && code <= 299)\n resolve(body);\n else\n reject(new Error(`Response received is not ok, Status Code: ${response.statusCode}, body: ${body}`));\n });\n });\n request.on('error', reject);\n request.end();\n });\n}\n\n/**\n * Fetch sessions from discord\n * @param token Bot token\n * @returns A promise containing a session object\n */\nexport async function FetchSessions(token: string): Promise<SessionObject> {\n const url = new URL('https://discord.com/api/v10/gateway/bot');\n const data = await Fetch(url, {\n method: 'GET',\n headers: { authorization: `Bot ${token}` }\n });\n return JSON.parse(data);\n}\n\n/**\n * Modify an array to contain the specified amount of chunks\n * @param original An array of data\n * @param chunks The amount of chunks to transform into\n * @returns A modified array\n */\nexport function Chunk(original: any[], chunks: number): any[] {\n const array = [];\n for (let i = 0; i < original.length; i += chunks)\n array.push(original.slice(i , i + chunks));\n return array;\n}\n\n/**\n * Wait for a specific amount of time (timeout)\n * @param ms Time to wait in milliseconds\n * @returns A promise that resolves in x seconds\n */\nexport function Delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(() => resolve(), ms));\n}\n\n/**\n * Creates an abortable request with controller and timeout\n * @param delay Time before an abort error throws\n * @returns An abortable data with controller and timeout\n */\nexport function MakeAbortableRequest(delay: number): AbortableData {\n const controller = new AbortController();\n const seconds = Math.round(delay / 1000);\n const timeout = setTimeout(\n () => controller.abort(new Error(`The request has been aborted in ${seconds} second(s)`)),\n delay\n );\n return { controller, timeout };\n}\n","import EventEmitter from 'node:events';\nimport { parentPort } from 'worker_threads';\nimport { WebSocketShard } from '@discordjs/ws';\nimport { BaseIpc } from './BaseIpc';\nimport {\n MainStrategyData,\n MainStrategyOps,\n Message,\n RawIpcMessage,\n} from '../Util';\n\nexport class ThreadStrategyWorker extends BaseIpc {\n private shard: WebSocketShard|undefined;\n constructor() {\n super(new EventEmitter());\n parentPort!.on('message', message => this.handleRawResponse(message, () => null));\n }\n\n public build(shard: WebSocketShard): void {\n if (!this.shard) this.shard = shard;\n }\n\n protected available(): boolean {\n return !!parentPort;\n }\n\n protected sendData(data: RawIpcMessage) {\n return parentPort!.postMessage(data);\n }\n\n protected async handleMessage(message: Message): Promise<void> {\n const content = message.content as MainStrategyData;\n if (!this.shard) throw new Error('Shard isn\\'t initialized yet');\n switch(content.op) {\n case MainStrategyOps.CONNECT:\n await this.shard.connect();\n message.reply(null);\n break;\n case MainStrategyOps.DESTROY:\n await this.shard.destroy(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.SEND:\n await this.shard.send(content.data || {});\n message.reply(null);\n break;\n case MainStrategyOps.RECONNECT:\n await this.shard.destroy(content.data);\n message.reply(null);\n break;\n case MainStrategyOps.STATUS:\n message.reply(this.shard.status);\n break;\n }\n }\n}\n","import EventEmitter from 'node:events';\nimport { Serializable } from 'node:child_process';\nimport { randomUUID } from 'crypto';\nimport { Indomitable } from '../Indomitable.js';\nimport {\n InternalAbortSignal,\n InternalPromise,\n IpcErrorData,\n LibraryEvents,\n Message,\n RawIpcMessage,\n RawIpcMessageType,\n SavePromiseOptions,\n Transportable\n} from '../Util.js';\n\n/**\n * Base class where primary and worker ipc inherits\n */\nexport abstract class BaseIpc {\n public readonly manager: Indomitable|EventEmitter;\n protected readonly promises: Map<string, InternalPromise>;\n protected constructor(manager: Indomitable|EventEmitter) {\n this.manager = manager;\n this.promises = new Map();\n }\n\n /**\n * Number of promises pending to be resolved\n */\n public get pendingPromises(): number {\n return this.promises.size;\n }\n\n /**\n * Rejects all the pending promises\n */\n public flushPromises(reason: string): void {\n const error = new Error(reason);\n for (const promise of this.promises.values()) {\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n promise.reject(error);\n }\n this.promises.clear();\n }\n\n /**\n * Raw send method without abort controller handling\n * @param transportable Data to send\n */\n public send(transportable: Transportable): Promise<unknown|undefined> {\n return new Promise((resolve, reject) => {\n if (!this.available()) {\n this.manager.emit(LibraryEvents.DEBUG, 'IPC tried to send a message, but the ipc communication is not yet ready');\n return resolve(undefined);\n }\n const repliable = transportable.repliable || false;\n const id = repliable ? randomUUID() : null;\n const data: RawIpcMessage = {\n id,\n content: transportable.content,\n internal: true,\n type: RawIpcMessageType.MESSAGE\n };\n this.sendData(data);\n if (!id) return resolve(undefined);\n this.waitForPromise({ id, resolve, reject, signal: transportable.signal });\n });\n }\n\n /**\n * Taps into message event of worker or primary process to handle ipc communication\n * @internal\n */\n public async handleRawResponse(data: Serializable, errorCallback: (error: unknown) => any): Promise<boolean|void> {\n try {\n this.manager.emit(LibraryEvents.RAW, data);\n if (!(data as any).internal) return;\n switch((data as RawIpcMessage).type) {\n case RawIpcMessageType.MESSAGE:\n return await this.handleUnparsedMessage(data as RawIpcMessage);\n case RawIpcMessageType.RESPONSE:\n case RawIpcMessageType.ERROR:\n return this.handlePromise(data as RawIpcMessage);\n }\n } catch (error: unknown) {\n errorCallback(error);\n }\n }\n\n protected waitForPromise(options: SavePromiseOptions): void {\n let controller: InternalAbortSignal|undefined;\n if (options.signal) {\n const listener = () => {\n this.promises.delete(options.id);\n options.reject(new Error('This operation is aborted'));\n };\n controller = {\n listener,\n signal: options.signal\n };\n controller.signal.addEventListener('abort', listener);\n }\n this.promises.set(options.id, { resolve: options.resolve, reject: options.reject, controller } as InternalPromise);\n }\n\n private handlePromise(data: RawIpcMessage): void {\n const id = data.id as string;\n const promise = this.promises.get(id);\n if (!promise) return;\n this.promises.delete(id);\n if (promise.controller) {\n promise.controller.signal.removeEventListener('abort', promise.controller.listener);\n }\n if (data.type === RawIpcMessageType.ERROR) {\n const content = data.content as IpcErrorData;\n const error = new Error(content.reason);\n error.stack = content.stack;\n error.name = content.name;\n promise.reject(error);\n return;\n }\n promise.resolve(data.content);\n }\n\n private async handleUnparsedMessage(data: RawIpcMessage): Promise<void> {\n const reply = (content: any) => {\n if (!data.id) return;\n const response: RawIpcMessage = {\n id: data.id,\n content,\n internal: true,\n type: RawIpcMessageType.RESPONSE\n };\n this.sendData(response);\n };\n const message: Message = {\n repliable: !!data.id,\n content: data.content,\n reply\n };\n if (!data.content.internal)\n return this.emitMessage(message);\n try {\n await this.handleMessage(message);\n } catch (error: any) {\n if (!message.repliable) return;\n const response: RawIpcMessage = {\n id: data.id,\n content: {\n name: error.name,\n reason: error.reason,\n stack: error.stack\n },\n internal: true,\n type: RawIpcMessageType.ERROR\n };\n this.sendData(response);\n }\n }\n\n protected emitMessage(message: Message): void {\n this.manager.emit(LibraryEvents.MESSAGE, message);\n }\n\n protected abstract available(): boolean;\n protected abstract sendData(data: RawIpcMessage): void;\n protected abstract handleMessage(message: Message): Promise<void>;\n}\n"],"mappings":"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","names":["WebSocketShardEvents","ipc","options","options","shard","WebSocketShardEvents"]} |
+16
-13
| { | ||
| "name": "indomitable", | ||
| "version": "4.0.3", | ||
| "description": "A lightweight (the actual ship is heavy though), performant, powerful & no dependency sharder for Discord.JS", | ||
| "version": "4.1.0", | ||
| "description": "A lightweight (the actual ship is heavy though), performant, and powerful sharder for Discord.JS", | ||
| "main": "dist/index.js", | ||
@@ -47,16 +47,19 @@ "module": "dist/index.mjs", | ||
| "peerDependencies": { | ||
| "discord.js": ">=14.10.x", | ||
| "@discordjs/ws": ">=1.0.0" | ||
| "@discordjs/ws": ">=1.0.x", | ||
| "discord.js": ">=14.14.x" | ||
| }, | ||
| "devDependencies": { | ||
| "@augu/eslint-config": "^4.0.1", | ||
| "@types/node": "^20.3.1", | ||
| "@types/ws": "^8.5.5", | ||
| "@typescript-eslint/eslint-plugin": "^5.60.0", | ||
| "@typescript-eslint/parser": "^5.60.0", | ||
| "eslint": "^8.43.0", | ||
| "tsup": "^7.0.0", | ||
| "typedoc": "^0.24.8", | ||
| "typescript": "^5.1.3" | ||
| "@augu/eslint-config": "^5.0.0", | ||
| "@types/node": "^20.10.5", | ||
| "@types/ws": "^8.5.10", | ||
| "@typescript-eslint/eslint-plugin": "^6.15.0", | ||
| "@typescript-eslint/parser": "^6.15.0", | ||
| "eslint": "^8.56.0", | ||
| "tsup": "^8.0.1", | ||
| "typedoc": "^0.25.4", | ||
| "typescript": "^5.3.3" | ||
| }, | ||
| "dependencies": { | ||
| "@discordjs/collection": "^2.0.0" | ||
| } | ||
| } |
+144
-132
| ## Indomitable | ||
| > A lightweight (the actual ship is heavy though), performant, powerful & no dependency sharder for Discord.JS | ||
| > A lightweight (the actual ship is heavy though), performant, and powerful sharder for Discord.JS | ||
| <p align="center"> | ||
@@ -10,21 +11,19 @@ <img src="https://cdn.donmai.us/original/9b/cf/__indomitable_azur_lane_drawn_by_kincora__9bcf19b2f822ce75ea707e5047882d6a.png"> | ||
| * Supports Discord.JS `v13` and `v14` | ||
| ## Features | ||
| * Fast | ||
| - Fast | ||
| * Lightweight | ||
| - Lightweight | ||
| * Reliable | ||
| - Reliable | ||
| * ESM & CommonJS supported | ||
| - ESM & CommonJS supported | ||
| * Shard Concurrency Support | ||
| - Shard Concurrency Support | ||
| * Easy to use promised based IPC | ||
| - Easy to use promised based IPC | ||
| * No dependencies | ||
| - No dependencies | ||
| * Very cute (and lazy) | ||
| - Very cute (and lazy) | ||
@@ -35,3 +34,3 @@ ## Used in prod by the ff: | ||
| > Kashima (https://kashima.saya.moe/) | ||
| > Kashima (https://kashima.saya.moe/) | ||
@@ -42,7 +41,7 @@ > Or add your own! | ||
| * Stable | ||
| - Stable | ||
| > `npm install indomitable` | ||
| * Dev | ||
| - Dev | ||
@@ -53,3 +52,3 @@ > `npm install https://github.com/Deivu/Indomitable.git#master` | ||
| 🔗 https://deivu.github.io/Indomitable/index.html | ||
| 🔗 https://indomitable.shipgirl.moe/ | ||
@@ -61,9 +60,12 @@ > Don't forget to read my "Notes" below! | ||
| > Basic usage | ||
| ```js | ||
| const { Indomitable } = require('indomitable'); | ||
| const { Client } = require('discord.js'); | ||
| const token = 'your_token'; | ||
| const { Indomitable } = require("indomitable"); | ||
| const { Client } = require("discord.js"); | ||
| const token = "your_token"; | ||
| const manager = new Indomitable({ client: Client, token }) | ||
| .on('error', console.error); | ||
| const manager = new Indomitable({ client: Client, token }).on( | ||
| "error", | ||
| console.error | ||
| ); | ||
@@ -74,26 +76,27 @@ manager.spawn(); | ||
| > Basic usage with more Indomitable Options | ||
| ```js | ||
| const { Indomitable } = require('indomitable'); | ||
| const { Client } = require('discord.js'); | ||
| const token = 'your_token'; | ||
| const { Indomitable } = require("indomitable"); | ||
| const { Client } = require("discord.js"); | ||
| const token = "your_token"; | ||
| const options = { | ||
| // Processes to run | ||
| clusterCount: 2, | ||
| // Websocket shards to run | ||
| shardCount: 8, | ||
| // Discord.JS options | ||
| clientOptions: { | ||
| intents: [1 << 0] // Bitwise for GUILD intent only | ||
| }, | ||
| // Auto restart processes that have been killed | ||
| autoRestart: true, // This defaults to false by default unless you specify it | ||
| // Your Discord.JS client | ||
| client: Client, | ||
| // Your bot token | ||
| token | ||
| } | ||
| // Processes to run | ||
| clusterCount: 2, | ||
| // Websocket shards to run | ||
| shardCount: 8, | ||
| // Discord.js options | ||
| clientOptions: { | ||
| intents: [1 << 0], // Bitwise for GUILD intent only | ||
| }, | ||
| // Auto restart processes that have been killed | ||
| // This defaults to false by default unless you specify it | ||
| autoRestart: true, | ||
| // Discord.js client | ||
| client: Client, | ||
| // Your bot token | ||
| token, | ||
| }; | ||
| const manager = new Indomitable(options) | ||
| .on('error', console.error); | ||
| const manager = new Indomitable(options).on("error", console.error); | ||
@@ -104,2 +107,3 @@ manager.spawn(); | ||
| > Broadcasteval as one way to get data across shards | ||
| ```js | ||
@@ -110,81 +114,88 @@ // Saya's note: | ||
| client.shard | ||
| .broadcastEval(client => client.guilds.cache.size) | ||
| .then(console.log); | ||
| .broadcastEval((client) => client.guilds.cache.size) | ||
| .then(console.log); | ||
| ``` | ||
| > Example of a very basic ipc communication (non repliable) | ||
| ```js | ||
| // Primary Process | ||
| indomitable.on('message', message => { | ||
| if (message.content.op === 'something') { | ||
| doSomething(); | ||
| } | ||
| indomitable.on("message", (message) => { | ||
| if (message.content.op === "something") { | ||
| doSomething(); | ||
| } | ||
| }); | ||
| // ClientWorker Process (your client most likely) | ||
| client.shard.send({ content: { op: 'something' } }) | ||
| .catch(console.error); | ||
| client.shard.send({ content: { op: "something" } }).catch(console.error); | ||
| ``` | ||
| > Example of a very basic ipc communication (repliable) | ||
| ```js | ||
| // Primary Process | ||
| indomitable.on('message', message => { | ||
| if (message.content.op === 'something') { | ||
| if (!message.repliable) return; // check if the message is repliable just incase, though it won't error even it is not | ||
| const someValue = doSomething(); | ||
| message.reply(someValue); | ||
| } | ||
| indomitable.on("message", (message) => { | ||
| if (message.content.op === "something") { | ||
| if (!message.repliable) return; // check if the message is repliable just incase, though it won't error even it is not | ||
| const someValue = doSomething(); | ||
| message.reply(someValue); | ||
| } | ||
| }); | ||
| // ClientWorker Process (your client most likely) | ||
| client.shard.send({ content: { op: 'something' }, repliable: true }) | ||
| .then(console.log) | ||
| .catch(console.error); | ||
| client.shard | ||
| .send({ content: { op: "something" }, repliable: true }) | ||
| .then(console.log) | ||
| .catch(console.error); | ||
| ``` | ||
| > You could also do it reversely (main process asking data from clusters instead of clusters asking main process) | ||
| ```js | ||
| // Primary Process | ||
| // send to specific cluster | ||
| indomitable.send(0, { content: { op: 'nya' } }) | ||
| .catch(console.error); | ||
| // send to specific cluster | ||
| indomitable.send(0, { content: { op: "nya" } }).catch(console.error); | ||
| // send to specific cluster with repliable | ||
| indomitable.send(0, { content: { op: 'something' }, repliable: true }) | ||
| .then(console.log) | ||
| .catch(console.error); | ||
| indomitable | ||
| .send(0, { content: { op: "something" }, repliable: true }) | ||
| .then(console.log) | ||
| .catch(console.error); | ||
| // broadcast to all clusters | ||
| indomitable.broadcast({ content: { op: 'meow' } }) | ||
| .catch(console.error); | ||
| indomitable.broadcast({ content: { op: "meow" } }).catch(console.error); | ||
| // broadcast to all clusters with repliable is possible as well | ||
| indomitable.broadcast({ content: { op: 'meow' }, repliable: true }) | ||
| .then(console.log) | ||
| .catch(console.error); | ||
| indomitable | ||
| .broadcast({ content: { op: "meow" }, repliable: true }) | ||
| .then(console.log) | ||
| .catch(console.error); | ||
| // ClientWorker Process (your client most likely) | ||
| client.shard.on('message', message => { | ||
| if (message.content.op === 'something') { | ||
| if (!message.repliable) return; | ||
| const someValue = doSomething(); | ||
| message.reply(someValue); | ||
| } | ||
| if (message.content.op === 'nya') { | ||
| doSomething(); | ||
| } | ||
| if (message.content.op === 'meow') { | ||
| if (!message.repliable) return; | ||
| message.reply('nya'); | ||
| } | ||
| }) | ||
| client.shard.on("message", (message) => { | ||
| if (message.content.op === "something") { | ||
| if (!message.repliable) return; | ||
| const someValue = doSomething(); | ||
| message.reply(someValue); | ||
| } | ||
| if (message.content.op === "nya") { | ||
| doSomething(); | ||
| } | ||
| if (message.content.op === "meow") { | ||
| if (!message.repliable) return; | ||
| message.reply("nya"); | ||
| } | ||
| }); | ||
| ``` | ||
| > Reconfiguring Indomitable on the go, to launch more clusters or more shard, or more on both | ||
| ```js | ||
| // Reconfigure to launch more shards based on Discord Recommendation without spawning more clusters | ||
| indomitable.reconfigure() | ||
| .then(() => console.log('Done, Indomitable is reconfigured')); | ||
| indomitable | ||
| .reconfigure() | ||
| .then(() => console.log("Done, Indomitable is reconfigured")); | ||
| // Reconfigure to launch more clusters based on your value, but leave the shards based on Discord's recommendation | ||
| indomitable.reconfigure({ clusters: 8 }) | ||
| .then(() => console.log('Done, Indomitable is reconfigured')); | ||
| indomitable | ||
| .reconfigure({ clusters: 8 }) | ||
| .then(() => console.log("Done, Indomitable is reconfigured")); | ||
| // Reconfigure to launch more clusters or shards based on your values | ||
| indomitable.reconfigure({ clusters: 8, shards: 8 }) | ||
| .then(() => console.log('Done, Indomitable is reconfigured')); | ||
| indomitable | ||
| .reconfigure({ clusters: 8, shards: 8 }) | ||
| .then(() => console.log("Done, Indomitable is reconfigured")); | ||
| // Do not run restart() or restartAll() while this is running. It will cause your cluster / clusters to restart twice. | ||
@@ -197,29 +208,29 @@ if (!indomitable.isBusy) indomitable.restartAll(); | ||
| > Enabling concurrency handling (across all your shards) (Read Notes Below) | ||
| ```js | ||
| const { Indomitable } = require('indomitable'); | ||
| const { Client } = require('discord.js'); | ||
| const token = 'your_token'; | ||
| const { Indomitable } = require("indomitable"); | ||
| const { Client } = require("discord.js"); | ||
| const token = "your_token"; | ||
| const options = { | ||
| // Processes to run | ||
| clusterCount: 2, | ||
| // Websocket shards to run | ||
| shardCount: 8, | ||
| // Discord.JS options | ||
| clientOptions: { | ||
| intents: [1 << 0] // Bitwise for GUILD intent only | ||
| }, | ||
| // Auto restart processes that have been killed | ||
| // This defaults to false by default unless you specify it | ||
| autoRestart: true, | ||
| // Enable max concurrency handling | ||
| handleConcurrency: true, | ||
| // Your Discord.JS client | ||
| client: Client, | ||
| // Your bot token | ||
| token | ||
| } | ||
| // Processes to run | ||
| clusterCount: 2, | ||
| // Websocket shards to run | ||
| shardCount: 8, | ||
| // Discord.js options | ||
| clientOptions: { | ||
| intents: [1 << 0], // Bitwise for GUILD intent only | ||
| }, | ||
| // Auto restart processes that have been killed | ||
| // This defaults to false by default unless you specify it | ||
| autoRestart: true, | ||
| // Enable max concurrency handling | ||
| handleConcurrency: true, | ||
| // Your Discord.js client | ||
| client: Client, | ||
| // Your bot token | ||
| token, | ||
| }; | ||
| const manager = new Indomitable(options) | ||
| .on('error', console.error); | ||
| const manager = new Indomitable(options).on("error", console.error); | ||
@@ -229,15 +240,15 @@ manager.spawn(); | ||
| * Indomitable's concurrency handling will work in **any shard # and cluster #** regardless | ||
| - Indomitable's concurrency handling will work in **any shard # and cluster #** regardless | ||
| * Pair this with **waitForReady** disabled, and you will get amazingly fast boot times, specially for those who have access to big bot sharding. | ||
| - Pair this with **waitForReady** disabled, and you will get amazingly fast boot times, specially for those who have access to big bot sharding. | ||
| * Reduces your identify calls by following global max_concurrency identifies resulting into better connection quality | ||
| - Reduces your identify calls by following global max_concurrency identifies resulting into better connection quality | ||
| ### Notes | ||
| * You don't need to call `client.login('token');` yourself, Indomitable will call it for you. | ||
| - You don't need to call `client.login('token');` yourself, Indomitable will call it for you. | ||
| * Extended clients that extend from discord.js client will work, as long as you use `client.login('token');` to get your bot running | ||
| - Extended clients that extend from discord.js client will work, as long as you use `client.login('token');` to get your bot running | ||
| * Your Discord.JS Client ShardClientUtil is replaced with Indomitable's ShardClientUtil. Refer to our docs for documentation 🔗 https://deivu.github.io/Indomitable/classes/client_ShardClientUtil.ShardClientUtil.html | ||
| - Your Discord.JS Client ShardClientUtil is replaced with Indomitable's ShardClientUtil. Refer to our docs for documentation 🔗 https://deivu.github.io/Indomitable/classes/client_ShardClientUtil.ShardClientUtil.html | ||
@@ -249,17 +260,18 @@ ### Other Links | ||
| ### Indomitable Options | ||
| | Option | Type | Description | Required | Default | | ||
| |--------------------|------------------|------------------------------------------------------------------------------|----------|----------------------------------------------------| | ||
| | clusterCount | number or 'auto' | How many clusters we should spawn | No | 'auto' | | ||
| | shardCount | number or 'auto' | How many websocket shards we should make | No | 'auto' | | ||
| | clientOptions | Object | Discord.JS Client Options | No | {} | | ||
| | clusterSettings | Object | Options for the forked process | No | {} | | ||
| | ipcTimeout | number | Timeout before we fail a request | No | 30000 | | ||
| | spawnTimeout | number | Timeout before we fail a cluster spawn | No | 60000 (multiplied by clusterShardCount internally) | | ||
| | spawnDelay | number | Time to wait before spawning a new cluster | No | 5000 | | ||
| | autoRestart | boolean | If you want to auto restart the shards that have been killed unintentionally | No | false | | ||
| | waitForReady | boolean | If you want to wait for cluster ready before spawning another cluster | No | true | | ||
| | handleConcurrency | boolean | Whether you want to handle shard concurrency properly | No | false | | ||
| | client | Client | Your Discord.JS non modified OR modified client | Yes | None | | ||
| | token | string | The token of your bot | Yes | None | | ||
| ### Made with ❤ by `@sayanyan` (https://github.com/Deivu/) | ||
| | Option | Type | Description | Required | Default | | ||
| | ----------------- | ---------------- | ---------------------------------------------------------------------------- | -------- | -------------------------------------------------- | | ||
| | clusterCount | number or 'auto' | How many clusters we should spawn | No | 'auto' | | ||
| | shardCount | number or 'auto' | How many websocket shards we should make | No | 'auto' | | ||
| | clientOptions | Object | Discord.JS Client Options | No | {} | | ||
| | clusterSettings | Object | Options for the forked process | No | {} | | ||
| | ipcTimeout | number | Timeout before we fail a request | No | 30000 | | ||
| | spawnTimeout | number | Timeout before we fail a cluster spawn | No | 60000 (multiplied by clusterShardCount internally) | | ||
| | spawnDelay | number | Time to wait before spawning a new cluster | No | 5000 | | ||
| | autoRestart | boolean | If you want to auto restart the shards that have been killed unintentionally | No | false | | ||
| | waitForReady | boolean | If you want to wait for cluster ready before spawning another cluster | No | true | | ||
| | handleConcurrency | boolean | Whether you want to handle shard concurrency properly | No | false | | ||
| | client | Client | Your Discord.JS non modified OR modified client | Yes | None | | ||
| | token | string | The token of your bot | Yes | None | | ||
| ### Made with ❤ by `@ichimakase` (https://github.com/Deivu/) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
394338
7.25%15
15.38%267
4.71%3
50%3718
-0.59%30
15.38%9
28.57%+ Added