indomitable
Advanced tools
+1
-1
@@ -951,3 +951,3 @@ "use strict"; | ||
| signal.addEventListener("abort", listener); | ||
| await this.ipc.send({ content, repliable: true }).catch(() => null); | ||
| await this.ipc.send({ content, repliable: true }); | ||
| } finally { | ||
@@ -954,0 +954,0 @@ signal.removeEventListener("abort", listener); |
+1
-1
@@ -918,3 +918,3 @@ var __defProp = Object.defineProperty; | ||
| signal.addEventListener("abort", listener); | ||
| await this.ipc.send({ content, repliable: true }).catch(() => null); | ||
| await this.ipc.send({ content, repliable: true }); | ||
| } finally { | ||
@@ -921,0 +921,0 @@ signal.removeEventListener("abort", listener); |
@@ -78,3 +78,3 @@ "use strict"; | ||
| signal.addEventListener("abort", listener); | ||
| await this.ipc.send({ content, repliable: true }).catch(() => null); | ||
| await this.ipc.send({ content, repliable: true }); | ||
| } finally { | ||
@@ -81,0 +81,0 @@ signal.removeEventListener("abort", listener); |
@@ -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\n .send({ content, repliable: true })\n .catch(() => null);\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"]} |
@@ -53,3 +53,3 @@ // src/strategy/Thread.ts | ||
| signal.addEventListener("abort", listener); | ||
| await this.ipc.send({ content, repliable: true }).catch(() => null); | ||
| await this.ipc.send({ content, repliable: true }); | ||
| } finally { | ||
@@ -56,0 +56,0 @@ signal.removeEventListener("abort", listener); |
@@ -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\n .send({ content, repliable: true })\n .catch(() => null);\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"]} |
+2
-2
| { | ||
| "name": "indomitable", | ||
| "version": "4.0.2", | ||
| "version": "4.0.3", | ||
| "description": "A lightweight (the actual ship is heavy though), performant, powerful & no dependency sharder for Discord.JS", | ||
@@ -48,3 +48,3 @@ "main": "dist/index.js", | ||
| "discord.js": ">=14.10.x", | ||
| "@discordjs/ws": ">=0.8.3" | ||
| "@discordjs/ws": ">=1.0.0" | ||
| }, | ||
@@ -51,0 +51,0 @@ "devDependencies": { |
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
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
367686
-0.1%