indomitable
Advanced tools
| export { } |
| "use strict"; | ||
| var __create = Object.create; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __getProtoOf = Object.getPrototypeOf; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
| // If the importer is in node compatibility mode or this is not an ESM | ||
| // file that has been converted to a CommonJS file using a Babel- | ||
| // compatible transform (i.e. "__esModule" has not been set), then set | ||
| // "default" to the CommonJS "module.exports" for node compatibility. | ||
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
| mod | ||
| )); | ||
| // src/strategy/Thread.ts | ||
| var import_ws2 = require("@discordjs/ws"); | ||
| var import_worker_threads2 = require("worker_threads"); | ||
| // src/strategy/IndomitableFetchingStrategy.ts | ||
| var import_ws = require("@discordjs/ws"); | ||
| // src/Util.ts | ||
| var import_node_https = __toESM(require("https")); | ||
| var EnvProcessData = { | ||
| clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0), | ||
| clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0), | ||
| shardIds: (process.env.INDOMITABLE_SHARDS || "").split(" ").map(Number), | ||
| shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0) | ||
| }; | ||
| // src/strategy/IndomitableFetchingStrategy.ts | ||
| var IndomitableFetchingStrategy = class { | ||
| constructor(ipc2, options2) { | ||
| this.ipc = ipc2; | ||
| this.options = options2; | ||
| } | ||
| async retrieveSessionInfo(shardId) { | ||
| const content = { | ||
| op: "retrieveSession" /* RETRIEVE_SESSION */, | ||
| event: import_ws.WebSocketShardEvents.Ready, | ||
| data: { shardId }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| return await this.ipc.send({ content, repliable: true }); | ||
| } | ||
| async updateSessionInfo(shardId, sessionInfo) { | ||
| const content = { | ||
| op: "updateSession" /* UPDATE_SESSION */, | ||
| event: import_ws.WebSocketShardEvents.Ready, | ||
| data: { shardId, sessionInfo }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| await this.ipc.send({ content }); | ||
| } | ||
| async waitForIdentify(shardId, signal) { | ||
| const content = { | ||
| op: "requestIdentify" /* REQUEST_IDENTIFY */, | ||
| event: import_ws.WebSocketShardEvents.Ready, | ||
| data: { shardId }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| const listener = () => this.abortIdentify(shardId); | ||
| try { | ||
| signal.addEventListener("abort", listener); | ||
| await this.ipc.send({ content, repliable: true }).catch(() => null); | ||
| } finally { | ||
| signal.removeEventListener("abort", listener); | ||
| } | ||
| } | ||
| abortIdentify(shardId) { | ||
| const content = { | ||
| op: "cancelIdentify" /* CANCEL_IDENTIFY */, | ||
| event: import_ws.WebSocketShardEvents.Ready, | ||
| data: { shardId }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| this.ipc.send({ content, repliable: false }).catch(() => null); | ||
| } | ||
| }; | ||
| // src/ipc/ThreadStrategyWorker.ts | ||
| var import_node_events = __toESM(require("events")); | ||
| // src/ipc/BaseIpc.ts | ||
| var import_crypto = require("crypto"); | ||
| var BaseIpc = class { | ||
| constructor(manager) { | ||
| this.manager = manager; | ||
| this.promises = /* @__PURE__ */ new Map(); | ||
| } | ||
| /** | ||
| * Number of promises pending to be resolved | ||
| */ | ||
| get pendingPromises() { | ||
| return this.promises.size; | ||
| } | ||
| /** | ||
| * Rejects all the pending promises | ||
| */ | ||
| flushPromises(reason) { | ||
| const error = new Error(reason); | ||
| for (const promise of this.promises.values()) { | ||
| if (promise.controller) { | ||
| promise.controller.signal.removeEventListener("abort", promise.controller.listener); | ||
| } | ||
| promise.reject(error); | ||
| } | ||
| this.promises.clear(); | ||
| } | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| if (!this.available()) { | ||
| this.manager.emit("debug" /* DEBUG */, "IPC tried to send a message, but the ipc communication is not yet ready"); | ||
| return resolve(void 0); | ||
| } | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? (0, import_crypto.randomUUID)() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| this.sendData(data); | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| } | ||
| /** | ||
| * Taps into message event of worker or primary process to handle ipc communication | ||
| * @internal | ||
| */ | ||
| async handleRawResponse(data, errorCallback) { | ||
| try { | ||
| this.manager.emit("raw" /* RAW */, data); | ||
| if (!data.internal) | ||
| return; | ||
| switch (data.type) { | ||
| case "message" /* MESSAGE */: | ||
| return await this.handleUnparsedMessage(data); | ||
| case "response" /* RESPONSE */: | ||
| case "error" /* ERROR */: | ||
| return this.handlePromise(data); | ||
| } | ||
| } catch (error) { | ||
| errorCallback(error); | ||
| } | ||
| } | ||
| waitForPromise(options2) { | ||
| let controller; | ||
| if (options2.signal) { | ||
| const listener = () => { | ||
| this.promises.delete(options2.id); | ||
| options2.reject(new Error("This operation is aborted")); | ||
| }; | ||
| controller = { | ||
| listener, | ||
| signal: options2.signal | ||
| }; | ||
| controller.signal.addEventListener("abort", listener); | ||
| } | ||
| this.promises.set(options2.id, { resolve: options2.resolve, reject: options2.reject, controller }); | ||
| } | ||
| handlePromise(data) { | ||
| const id = data.id; | ||
| const promise = this.promises.get(id); | ||
| if (!promise) | ||
| return; | ||
| this.promises.delete(id); | ||
| if (promise.controller) { | ||
| promise.controller.signal.removeEventListener("abort", promise.controller.listener); | ||
| } | ||
| if (data.type === "error" /* ERROR */) { | ||
| const content = data.content; | ||
| const error = new Error(content.reason); | ||
| error.stack = content.stack; | ||
| error.name = content.name; | ||
| promise.reject(error); | ||
| return; | ||
| } | ||
| promise.resolve(data.content); | ||
| } | ||
| async handleUnparsedMessage(data) { | ||
| const reply = (content) => { | ||
| if (!data.id) | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content, | ||
| internal: true, | ||
| type: "response" /* RESPONSE */ | ||
| }; | ||
| this.sendData(response); | ||
| }; | ||
| const message = { | ||
| repliable: !!data.id, | ||
| content: data.content, | ||
| reply | ||
| }; | ||
| if (!data.content.internal) | ||
| return this.emitMessage(message); | ||
| try { | ||
| await this.handleMessage(message); | ||
| } catch (error) { | ||
| if (!message.repliable) | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content: { | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }, | ||
| internal: true, | ||
| type: "error" /* ERROR */ | ||
| }; | ||
| this.sendData(response); | ||
| } | ||
| } | ||
| emitMessage(message) { | ||
| this.manager.emit("message" /* MESSAGE */, message); | ||
| } | ||
| }; | ||
| // src/ipc/ThreadStrategyWorker.ts | ||
| var import_worker_threads = require("worker_threads"); | ||
| var ThreadStrategyWorker = class extends BaseIpc { | ||
| constructor() { | ||
| super(new import_node_events.default()); | ||
| import_worker_threads.parentPort.on("message", (message) => this.handleRawResponse(message, () => null)); | ||
| } | ||
| build(shard2) { | ||
| if (!this.shard) | ||
| this.shard = shard2; | ||
| } | ||
| available() { | ||
| return !!import_worker_threads.parentPort; | ||
| } | ||
| sendData(data) { | ||
| return import_worker_threads.parentPort.postMessage(data); | ||
| } | ||
| async handleMessage(message) { | ||
| const content = message.content; | ||
| if (!this.shard) | ||
| throw new Error("Shard isn't initialized yet"); | ||
| switch (content.op) { | ||
| case "connect" /* CONNECT */: | ||
| await this.shard.connect(); | ||
| message.reply(null); | ||
| break; | ||
| case "destroy" /* DESTROY */: | ||
| await this.shard.destroy(content.data || {}); | ||
| message.reply(null); | ||
| break; | ||
| case "send" /* SEND */: | ||
| await this.shard.send(content.data || {}); | ||
| message.reply(null); | ||
| break; | ||
| case "reconnect" /* RECONNECT */: | ||
| await this.shard.destroy(content.data); | ||
| message.reply(null); | ||
| break; | ||
| case "status" /* STATUS */: | ||
| message.reply(this.shard.status); | ||
| break; | ||
| } | ||
| } | ||
| }; | ||
| // src/strategy/Thread.ts | ||
| var options = import_worker_threads2.workerData; | ||
| var ipc = new ThreadStrategyWorker(); | ||
| var strategy = new IndomitableFetchingStrategy(ipc, options); | ||
| var shard = new import_ws2.WebSocketShard(strategy, options.shardId); | ||
| ipc.build(shard); | ||
| for (const event of Object.values(import_ws2.WebSocketShardEvents)) { | ||
| shard.on(event, (data) => { | ||
| const content = { | ||
| op: "shardEvent" /* SHARD_EVENT */, | ||
| event, | ||
| data, | ||
| shardId: shard.id, | ||
| internal: true | ||
| }; | ||
| ipc.send({ content }).catch(() => null); | ||
| }); | ||
| } | ||
| //# sourceMappingURL=Thread.js.map |
| {"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 { BaseIpc } from './BaseIpc';\nimport { MainStrategyData, MainStrategyOps, Message, RawIpcMessage } from '../Util';\nimport { parentPort } from 'worker_threads';\nimport { WebSocketShard } from '@discordjs/ws';\n\nexport class ThreadStrategyWorker extends BaseIpc {\n private shard: WebSocketShard|undefined;\n constructor() {\n // @ts-expect-error: Indomitable will not be used in the thread process\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 { 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;\n protected readonly promises: Map<string, InternalPromise>;\n protected constructor(manager: Indomitable) {\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"]} |
| // src/strategy/Thread.ts | ||
| import { WebSocketShard, WebSocketShardEvents as WebSocketShardEvents2 } from "@discordjs/ws"; | ||
| import { workerData } from "worker_threads"; | ||
| // src/strategy/IndomitableFetchingStrategy.ts | ||
| import { WebSocketShardEvents } from "@discordjs/ws"; | ||
| // src/Util.ts | ||
| var EnvProcessData = { | ||
| clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0), | ||
| clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0), | ||
| shardIds: (process.env.INDOMITABLE_SHARDS || "").split(" ").map(Number), | ||
| shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0) | ||
| }; | ||
| // src/strategy/IndomitableFetchingStrategy.ts | ||
| var IndomitableFetchingStrategy = class { | ||
| constructor(ipc2, options2) { | ||
| this.ipc = ipc2; | ||
| this.options = options2; | ||
| } | ||
| async retrieveSessionInfo(shardId) { | ||
| const content = { | ||
| op: "retrieveSession" /* RETRIEVE_SESSION */, | ||
| event: WebSocketShardEvents.Ready, | ||
| data: { shardId }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| return await this.ipc.send({ content, repliable: true }); | ||
| } | ||
| async updateSessionInfo(shardId, sessionInfo) { | ||
| const content = { | ||
| op: "updateSession" /* UPDATE_SESSION */, | ||
| event: WebSocketShardEvents.Ready, | ||
| data: { shardId, sessionInfo }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| await this.ipc.send({ content }); | ||
| } | ||
| async waitForIdentify(shardId, signal) { | ||
| const content = { | ||
| op: "requestIdentify" /* REQUEST_IDENTIFY */, | ||
| event: WebSocketShardEvents.Ready, | ||
| data: { shardId }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| const listener = () => this.abortIdentify(shardId); | ||
| try { | ||
| signal.addEventListener("abort", listener); | ||
| await this.ipc.send({ content, repliable: true }).catch(() => null); | ||
| } finally { | ||
| signal.removeEventListener("abort", listener); | ||
| } | ||
| } | ||
| abortIdentify(shardId) { | ||
| const content = { | ||
| op: "cancelIdentify" /* CANCEL_IDENTIFY */, | ||
| event: WebSocketShardEvents.Ready, | ||
| data: { shardId }, | ||
| shardId, | ||
| internal: true | ||
| }; | ||
| this.ipc.send({ content, repliable: false }).catch(() => null); | ||
| } | ||
| }; | ||
| // src/ipc/ThreadStrategyWorker.ts | ||
| import EventEmitter from "events"; | ||
| // src/ipc/BaseIpc.ts | ||
| import { randomUUID } from "crypto"; | ||
| var BaseIpc = class { | ||
| constructor(manager) { | ||
| this.manager = manager; | ||
| this.promises = /* @__PURE__ */ new Map(); | ||
| } | ||
| /** | ||
| * Number of promises pending to be resolved | ||
| */ | ||
| get pendingPromises() { | ||
| return this.promises.size; | ||
| } | ||
| /** | ||
| * Rejects all the pending promises | ||
| */ | ||
| flushPromises(reason) { | ||
| const error = new Error(reason); | ||
| for (const promise of this.promises.values()) { | ||
| if (promise.controller) { | ||
| promise.controller.signal.removeEventListener("abort", promise.controller.listener); | ||
| } | ||
| promise.reject(error); | ||
| } | ||
| this.promises.clear(); | ||
| } | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| if (!this.available()) { | ||
| this.manager.emit("debug" /* DEBUG */, "IPC tried to send a message, but the ipc communication is not yet ready"); | ||
| return resolve(void 0); | ||
| } | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? randomUUID() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| this.sendData(data); | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| } | ||
| /** | ||
| * Taps into message event of worker or primary process to handle ipc communication | ||
| * @internal | ||
| */ | ||
| async handleRawResponse(data, errorCallback) { | ||
| try { | ||
| this.manager.emit("raw" /* RAW */, data); | ||
| if (!data.internal) | ||
| return; | ||
| switch (data.type) { | ||
| case "message" /* MESSAGE */: | ||
| return await this.handleUnparsedMessage(data); | ||
| case "response" /* RESPONSE */: | ||
| case "error" /* ERROR */: | ||
| return this.handlePromise(data); | ||
| } | ||
| } catch (error) { | ||
| errorCallback(error); | ||
| } | ||
| } | ||
| waitForPromise(options2) { | ||
| let controller; | ||
| if (options2.signal) { | ||
| const listener = () => { | ||
| this.promises.delete(options2.id); | ||
| options2.reject(new Error("This operation is aborted")); | ||
| }; | ||
| controller = { | ||
| listener, | ||
| signal: options2.signal | ||
| }; | ||
| controller.signal.addEventListener("abort", listener); | ||
| } | ||
| this.promises.set(options2.id, { resolve: options2.resolve, reject: options2.reject, controller }); | ||
| } | ||
| handlePromise(data) { | ||
| const id = data.id; | ||
| const promise = this.promises.get(id); | ||
| if (!promise) | ||
| return; | ||
| this.promises.delete(id); | ||
| if (promise.controller) { | ||
| promise.controller.signal.removeEventListener("abort", promise.controller.listener); | ||
| } | ||
| if (data.type === "error" /* ERROR */) { | ||
| const content = data.content; | ||
| const error = new Error(content.reason); | ||
| error.stack = content.stack; | ||
| error.name = content.name; | ||
| promise.reject(error); | ||
| return; | ||
| } | ||
| promise.resolve(data.content); | ||
| } | ||
| async handleUnparsedMessage(data) { | ||
| const reply = (content) => { | ||
| if (!data.id) | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content, | ||
| internal: true, | ||
| type: "response" /* RESPONSE */ | ||
| }; | ||
| this.sendData(response); | ||
| }; | ||
| const message = { | ||
| repliable: !!data.id, | ||
| content: data.content, | ||
| reply | ||
| }; | ||
| if (!data.content.internal) | ||
| return this.emitMessage(message); | ||
| try { | ||
| await this.handleMessage(message); | ||
| } catch (error) { | ||
| if (!message.repliable) | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content: { | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }, | ||
| internal: true, | ||
| type: "error" /* ERROR */ | ||
| }; | ||
| this.sendData(response); | ||
| } | ||
| } | ||
| emitMessage(message) { | ||
| this.manager.emit("message" /* MESSAGE */, message); | ||
| } | ||
| }; | ||
| // src/ipc/ThreadStrategyWorker.ts | ||
| import { parentPort } from "worker_threads"; | ||
| var ThreadStrategyWorker = class extends BaseIpc { | ||
| constructor() { | ||
| super(new EventEmitter()); | ||
| parentPort.on("message", (message) => this.handleRawResponse(message, () => null)); | ||
| } | ||
| build(shard2) { | ||
| if (!this.shard) | ||
| this.shard = shard2; | ||
| } | ||
| available() { | ||
| return !!parentPort; | ||
| } | ||
| sendData(data) { | ||
| return parentPort.postMessage(data); | ||
| } | ||
| async handleMessage(message) { | ||
| const content = message.content; | ||
| if (!this.shard) | ||
| throw new Error("Shard isn't initialized yet"); | ||
| switch (content.op) { | ||
| case "connect" /* CONNECT */: | ||
| await this.shard.connect(); | ||
| message.reply(null); | ||
| break; | ||
| case "destroy" /* DESTROY */: | ||
| await this.shard.destroy(content.data || {}); | ||
| message.reply(null); | ||
| break; | ||
| case "send" /* SEND */: | ||
| await this.shard.send(content.data || {}); | ||
| message.reply(null); | ||
| break; | ||
| case "reconnect" /* RECONNECT */: | ||
| await this.shard.destroy(content.data); | ||
| message.reply(null); | ||
| break; | ||
| case "status" /* STATUS */: | ||
| message.reply(this.shard.status); | ||
| break; | ||
| } | ||
| } | ||
| }; | ||
| // src/strategy/Thread.ts | ||
| var options = workerData; | ||
| var ipc = new ThreadStrategyWorker(); | ||
| var strategy = new IndomitableFetchingStrategy(ipc, options); | ||
| var shard = new WebSocketShard(strategy, options.shardId); | ||
| ipc.build(shard); | ||
| for (const event of Object.values(WebSocketShardEvents2)) { | ||
| shard.on(event, (data) => { | ||
| const content = { | ||
| op: "shardEvent" /* SHARD_EVENT */, | ||
| event, | ||
| data, | ||
| shardId: shard.id, | ||
| internal: true | ||
| }; | ||
| ipc.send({ content }).catch(() => null); | ||
| }); | ||
| } | ||
| //# sourceMappingURL=Thread.mjs.map |
| {"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 { BaseIpc } from './BaseIpc';\nimport { MainStrategyData, MainStrategyOps, Message, RawIpcMessage } from '../Util';\nimport { parentPort } from 'worker_threads';\nimport { WebSocketShard } from '@discordjs/ws';\n\nexport class ThreadStrategyWorker extends BaseIpc {\n private shard: WebSocketShard|undefined;\n constructor() {\n // @ts-expect-error: Indomitable will not be used in the thread process\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 { 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;\n protected readonly promises: Map<string, InternalPromise>;\n protected constructor(manager: Indomitable) {\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"]} |
+126
-47
| import { ClientOptions, Client } from 'discord.js'; | ||
| import { Worker as Worker$1, ClusterSettings } from 'node:cluster'; | ||
| import { Worker, ClusterSettings } from 'node:cluster'; | ||
| import EventEmitter from 'node:events'; | ||
| import { Serializable } from 'node:child_process'; | ||
| import { RequestOptions } from 'node:https'; | ||
| import { WebSocketShardEvents } from '@discordjs/ws'; | ||
@@ -25,5 +26,34 @@ /** | ||
| /** | ||
| * Events for internal use | ||
| * Hoisted Environmental Variable for ease of fetching | ||
| */ | ||
| declare enum ClientEvents { | ||
| 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", | ||
@@ -36,8 +66,14 @@ RESTART = "restart", | ||
| SESSION_INFO = "sessionInfo", | ||
| PING = "ping" | ||
| } | ||
| /** | ||
| * Events for internal use | ||
| */ | ||
| declare enum ClientEvents { | ||
| READY = "ready", | ||
| PING = "ping", | ||
| SHARD_READY = "shardReady", | ||
| SHARD_RECONNECT = "shardReconnect", | ||
| SHARD_RESUME = "shardResume", | ||
| SHARD_DISCONNECT = "shardDisconnect" | ||
| SHARD_DISCONNECT = "shardDisconnect", | ||
| ERROR = "ERROR" | ||
| } | ||
@@ -66,8 +102,35 @@ /** | ||
| MESSAGE = "message", | ||
| RESPONSE = "response" | ||
| 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 InternalEvents { | ||
| interface InternalOpsData { | ||
| op: InternalOps; | ||
| data: any; | ||
| internal: true; | ||
| } | ||
| /** | ||
| * Data structure representing an internal discord.js event | ||
| */ | ||
| interface ClientEventData { | ||
| op: ClientEvents; | ||
@@ -80,5 +143,3 @@ data: any; | ||
| */ | ||
| interface InternalError { | ||
| internal: true; | ||
| error: true; | ||
| interface IpcErrorData { | ||
| name: string; | ||
@@ -189,13 +250,20 @@ reason: string; | ||
| 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_InternalError = InternalError; | ||
| type Util_InternalEvents = InternalEvents; | ||
| 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; | ||
@@ -208,2 +276,5 @@ type Util_Message = Message; | ||
| type Util_SessionObject = SessionObject; | ||
| type Util_ThreadStrategyData = ThreadStrategyData; | ||
| type Util_ThreadStrategyOps = ThreadStrategyOps; | ||
| declare const Util_ThreadStrategyOps: typeof ThreadStrategyOps; | ||
| type Util_Transportable = Transportable; | ||
@@ -214,11 +285,16 @@ declare namespace Util { | ||
| 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_InternalError as InternalError, | ||
| Util_InternalEvents as InternalEvents, | ||
| 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, | ||
@@ -230,2 +306,4 @@ Util_Message as Message, | ||
| Util_SessionObject as SessionObject, | ||
| Util_ThreadStrategyData as ThreadStrategyData, | ||
| Util_ThreadStrategyOps as ThreadStrategyOps, | ||
| Util_Transportable as Transportable, | ||
@@ -251,2 +329,7 @@ }; | ||
| /** | ||
| * 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 | ||
@@ -258,3 +341,7 @@ * @internal | ||
| private handlePromise; | ||
| protected abstract handleMessage(data: RawIpcMessage): Promise<boolean | void> | boolean | void; | ||
| private handleUnparsedMessage; | ||
| protected emitMessage(message: Message): void; | ||
| protected abstract available(): boolean; | ||
| protected abstract sendData(data: RawIpcMessage): void; | ||
| protected abstract handleMessage(message: Message): Promise<void>; | ||
| } | ||
@@ -265,11 +352,8 @@ | ||
| */ | ||
| declare class Main extends BaseIpc { | ||
| declare class MainWorker extends BaseIpc { | ||
| readonly cluster: ClusterManager; | ||
| constructor(cluster: ClusterManager); | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable: Transportable): Promise<unknown | undefined>; | ||
| protected handleMessage(data: RawIpcMessage): Promise<boolean | void>; | ||
| protected available(): boolean; | ||
| protected sendData(data: RawIpcMessage): void; | ||
| protected handleMessage(message: Message): Promise<void>; | ||
| } | ||
@@ -291,3 +375,3 @@ | ||
| readonly id: number; | ||
| readonly ipc: Main; | ||
| readonly ipc: MainWorker; | ||
| shards: number[]; | ||
@@ -297,3 +381,3 @@ started: boolean; | ||
| readyAt: number; | ||
| worker?: Worker$1; | ||
| worker?: Worker; | ||
| /** | ||
@@ -564,19 +648,19 @@ * @param options.id ClusterId of this Cluster Manager being created | ||
| /** | ||
| * Worker ipc class. Only initialized at worker process | ||
| * Basic worker ipc class, basic child process ipc handler | ||
| */ | ||
| declare class Worker extends BaseIpc { | ||
| declare class BaseWorker extends BaseIpc { | ||
| constructor(manager: Indomitable); | ||
| 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; | ||
| private built; | ||
| constructor(shard: ShardClientUtil, manager: Indomitable); | ||
| /** | ||
| * Builds the pre-initialized worker ipc | ||
| * @internal | ||
| */ | ||
| build(): void; | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable: Transportable): Promise<unknown | undefined>; | ||
| protected handleMessage(data: RawIpcMessage): boolean | void; | ||
| protected emitMessage(message: Message): void; | ||
| protected handleMessage(message: Message): Promise<void>; | ||
| } | ||
@@ -597,4 +681,4 @@ | ||
| declare class ShardClientUtil extends EventEmitter { | ||
| client?: Client; | ||
| readonly ipc: Worker; | ||
| client: Client; | ||
| readonly ipc: ClientWorker; | ||
| readonly clusterId: number; | ||
@@ -604,9 +688,4 @@ readonly clusterCount: number; | ||
| readonly shardCount: number; | ||
| constructor(manager: Indomitable); | ||
| constructor(client: Client, manager: Indomitable); | ||
| /** | ||
| * Builds the pre-initialized shard client util | ||
| * @internal | ||
| */ | ||
| build(client: Client): void; | ||
| /** | ||
| * Gets the current ipc delay | ||
@@ -649,2 +728,2 @@ * @returns A promise that resolves to delay in nanoseconds | ||
| export { ClusterManager, ClusterManagerOptions, Indomitable, IndomitableOptions, Main, PartialInternalEvents, ReconfigureOptions, ShardClient, ShardClientUtil, ShardEventData, Util as Utils, Worker }; | ||
| export { BaseIpc, BaseWorker, ClientWorker, ClusterManager, ClusterManagerOptions, ConcurrencyManager, Indomitable, IndomitableOptions, MainWorker, PartialInternalEvents, ReconfigureOptions, ShardClient, ShardClientUtil, ShardEventData, Util as Utils }; |
+387
-286
@@ -33,15 +33,15 @@ "use strict"; | ||
| __export(Indomitable_exports, { | ||
| BaseIpc: () => BaseIpc, | ||
| BaseWorker: () => BaseWorker, | ||
| ClientWorker: () => ClientWorker, | ||
| ClusterManager: () => ClusterManager, | ||
| ConcurrencyManager: () => ConcurrencyManager, | ||
| Indomitable: () => Indomitable, | ||
| Main: () => Main, | ||
| MainWorker: () => MainWorker, | ||
| ShardClient: () => ShardClient, | ||
| ShardClientUtil: () => ShardClientUtil, | ||
| Utils: () => Util_exports, | ||
| Worker: () => Worker | ||
| Utils: () => Util_exports | ||
| }); | ||
| module.exports = __toCommonJS(Indomitable_exports); | ||
| // src/client/ShardClient.ts | ||
| var import_ws = require("@discordjs/ws"); | ||
| // src/Util.ts | ||
@@ -53,19 +53,48 @@ var Util_exports = {}; | ||
| Delay: () => Delay, | ||
| EnvProcessData: () => EnvProcessData, | ||
| Fetch: () => Fetch, | ||
| FetchSessions: () => FetchSessions, | ||
| InternalOps: () => InternalOps, | ||
| LibraryEvents: () => LibraryEvents, | ||
| MainStrategyOps: () => MainStrategyOps, | ||
| MakeAbortableRequest: () => MakeAbortableRequest, | ||
| RawIpcMessageType: () => RawIpcMessageType | ||
| RawIpcMessageType: () => RawIpcMessageType, | ||
| ThreadStrategyOps: () => ThreadStrategyOps | ||
| }); | ||
| var import_node_https = __toESM(require("https")); | ||
| var EnvProcessData = { | ||
| clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0), | ||
| clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0), | ||
| shardIds: (process.env.INDOMITABLE_SHARDS || "").split(" ").map(Number), | ||
| shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0) | ||
| }; | ||
| var MainStrategyOps = /* @__PURE__ */ ((MainStrategyOps2) => { | ||
| MainStrategyOps2["CONNECT"] = "connect"; | ||
| MainStrategyOps2["DESTROY"] = "destroy"; | ||
| MainStrategyOps2["SEND"] = "send"; | ||
| MainStrategyOps2["STATUS"] = "status"; | ||
| MainStrategyOps2["RECONNECT"] = "reconnect"; | ||
| return MainStrategyOps2; | ||
| })(MainStrategyOps || {}); | ||
| var ThreadStrategyOps = /* @__PURE__ */ ((ThreadStrategyOps2) => { | ||
| ThreadStrategyOps2["REQUEST_IDENTIFY"] = "requestIdentify"; | ||
| ThreadStrategyOps2["CANCEL_IDENTIFY"] = "cancelIdentify"; | ||
| ThreadStrategyOps2["SHARD_EVENT"] = "shardEvent"; | ||
| ThreadStrategyOps2["RETRIEVE_SESSION"] = "retrieveSession"; | ||
| ThreadStrategyOps2["UPDATE_SESSION"] = "updateSession"; | ||
| return ThreadStrategyOps2; | ||
| })(ThreadStrategyOps || {}); | ||
| var InternalOps = /* @__PURE__ */ ((InternalOps2) => { | ||
| InternalOps2["EVAL"] = "eval"; | ||
| InternalOps2["RESTART"] = "restart"; | ||
| InternalOps2["RESTART_ALL"] = "restartAll"; | ||
| InternalOps2["DESTROY_CLIENT"] = "destroyClient"; | ||
| InternalOps2["REQUEST_IDENTIFY"] = "requestIdentify"; | ||
| InternalOps2["CANCEL_IDENTIFY"] = "cancelIdentify"; | ||
| InternalOps2["SESSION_INFO"] = "sessionInfo"; | ||
| InternalOps2["PING"] = "ping"; | ||
| return InternalOps2; | ||
| })(InternalOps || {}); | ||
| var ClientEvents = /* @__PURE__ */ ((ClientEvents2) => { | ||
| ClientEvents2["EVAL"] = "eval"; | ||
| ClientEvents2["RESTART"] = "restart"; | ||
| ClientEvents2["RESTART_ALL"] = "restartAll"; | ||
| ClientEvents2["DESTROY_CLIENT"] = "destroyClient"; | ||
| ClientEvents2["REQUEST_IDENTIFY"] = "requestIdentify"; | ||
| ClientEvents2["CANCEL_IDENTIFY"] = "cancelIdentify"; | ||
| ClientEvents2["SESSION_INFO"] = "sessionInfo"; | ||
| ClientEvents2["READY"] = "ready"; | ||
| ClientEvents2["PING"] = "ping"; | ||
| ClientEvents2["SHARD_READY"] = "shardReady"; | ||
@@ -75,2 +104,3 @@ ClientEvents2["SHARD_RECONNECT"] = "shardReconnect"; | ||
| ClientEvents2["SHARD_DISCONNECT"] = "shardDisconnect"; | ||
| ClientEvents2["ERROR"] = "ERROR"; | ||
| return ClientEvents2; | ||
@@ -96,2 +126,3 @@ })(ClientEvents || {}); | ||
| RawIpcMessageType2["RESPONSE"] = "response"; | ||
| RawIpcMessageType2["ERROR"] = "error"; | ||
| return RawIpcMessageType2; | ||
@@ -145,48 +176,14 @@ })(RawIpcMessageType || {}); | ||
| // src/concurrency/ConcurrencyClient.ts | ||
| var ConcurrencyClient = class { | ||
| constructor(shard) { | ||
| this.shard = shard; | ||
| } | ||
| /** | ||
| * Method to try and acquire a lock for identify | ||
| */ | ||
| async waitForIdentify(shardId, signal) { | ||
| const content = { | ||
| op: "requestIdentify" /* REQUEST_IDENTIFY */, | ||
| data: { shardId }, | ||
| internal: true | ||
| }; | ||
| const listener = () => this.abortIdentify(shardId); | ||
| try { | ||
| signal.addEventListener("abort", listener); | ||
| await this.shard.send({ content, repliable: true }); | ||
| } catch (error) { | ||
| if (error.message.includes("aborted the identify")) | ||
| throw error; | ||
| } finally { | ||
| signal.removeEventListener("abort", listener); | ||
| } | ||
| } | ||
| /** | ||
| * Aborts an acquire lock request | ||
| */ | ||
| abortIdentify(shardId) { | ||
| const content = { | ||
| op: "cancelIdentify" /* CANCEL_IDENTIFY */, | ||
| data: { shardId }, | ||
| internal: true | ||
| }; | ||
| this.shard.send({ content, repliable: false }).catch(() => null); | ||
| } | ||
| }; | ||
| // src/strategy/IndomitableStrategy.ts | ||
| var import_node_path = require("path"); | ||
| var import_node_events2 = require("events"); | ||
| var import_node_worker_threads = require("worker_threads"); | ||
| var import_ws = require("@discordjs/ws"); | ||
| var import_discord = require("discord.js"); | ||
| // src/client/ShardClientUtil.ts | ||
| // src/ipc/MainStrategyWorker.ts | ||
| var import_node_events = __toESM(require("events")); | ||
| var import_timers = require("timers"); | ||
| // src/ipc/Worker.ts | ||
| // src/ipc/BaseIpc.ts | ||
| var import_crypto = require("crypto"); | ||
| // src/ipc/BaseIpc.ts | ||
| var BaseIpc = class { | ||
@@ -217,2 +214,26 @@ constructor(manager) { | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| if (!this.available()) { | ||
| this.manager.emit("debug" /* DEBUG */, "IPC tried to send a message, but the ipc communication is not yet ready"); | ||
| return resolve(void 0); | ||
| } | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? (0, import_crypto.randomUUID)() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| this.sendData(data); | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| } | ||
| /** | ||
| * Taps into message event of worker or primary process to handle ipc communication | ||
@@ -228,4 +249,5 @@ * @internal | ||
| case "message" /* MESSAGE */: | ||
| return await this.handleMessage(data); | ||
| return await this.handleUnparsedMessage(data); | ||
| case "response" /* RESPONSE */: | ||
| case "error" /* ERROR */: | ||
| return this.handlePromise(data); | ||
@@ -261,57 +283,13 @@ } | ||
| } | ||
| if (data.content?.internal && data.content?.error) { | ||
| const error = new Error(data.content.reason || "Unknown error reason"); | ||
| error.stack = data.content.stack; | ||
| error.name = data.content.name; | ||
| return promise.reject(error); | ||
| if (data.type === "error" /* ERROR */) { | ||
| const content = data.content; | ||
| const error = new Error(content.reason); | ||
| error.stack = content.stack; | ||
| error.name = content.name; | ||
| promise.reject(error); | ||
| return; | ||
| } | ||
| promise.resolve(data.content); | ||
| } | ||
| }; | ||
| // src/ipc/Worker.ts | ||
| var Worker = class extends BaseIpc { | ||
| constructor(shard, manager) { | ||
| super(manager); | ||
| this.shard = shard; | ||
| this.built = false; | ||
| } | ||
| /** | ||
| * Builds the pre-initialized worker ipc | ||
| * @internal | ||
| */ | ||
| build() { | ||
| if (this.built) | ||
| return; | ||
| this.built = true; | ||
| process.on( | ||
| "message", | ||
| (data) => this.handleRawResponse(data, (error) => this.shard.client.emit("error" /* ERROR */, error)) | ||
| ); | ||
| } | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? (0, import_crypto.randomUUID)() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| try { | ||
| process.send(data); | ||
| } catch (error) { | ||
| return reject(error); | ||
| } | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| } | ||
| handleMessage(data) { | ||
| async handleUnparsedMessage(data) { | ||
| const reply = (content) => { | ||
@@ -326,3 +304,3 @@ if (!data.id) | ||
| }; | ||
| process.send(response); | ||
| this.sendData(response); | ||
| }; | ||
@@ -334,47 +312,223 @@ const message = { | ||
| }; | ||
| if (!message.content.internal) | ||
| return this.shard.emit("message" /* MESSAGE */, message); | ||
| if (!data.content.internal) | ||
| return this.emitMessage(message); | ||
| try { | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "eval" /* EVAL */: | ||
| message.reply(this.shard.client._eval(content.data)); | ||
| break; | ||
| case "destroyClient" /* DESTROY_CLIENT */: | ||
| this.shard.client.destroy(); | ||
| message.reply(null); | ||
| } | ||
| await this.handleMessage(message); | ||
| } catch (error) { | ||
| if (!message.repliable) | ||
| throw error; | ||
| message.reply({ | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content: { | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }, | ||
| internal: true, | ||
| error: true, | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }); | ||
| type: "error" /* ERROR */ | ||
| }; | ||
| this.sendData(response); | ||
| } | ||
| } | ||
| emitMessage(message) { | ||
| this.manager.emit("message" /* MESSAGE */, message); | ||
| } | ||
| }; | ||
| // src/ipc/MainStrategyWorker.ts | ||
| var MainStrategyWorker = class extends BaseIpc { | ||
| constructor(id, thread, strategy) { | ||
| super(new import_node_events.default()); | ||
| this.id = id; | ||
| this.thread = thread; | ||
| this.strategy = strategy; | ||
| } | ||
| available() { | ||
| return true; | ||
| } | ||
| sendData(data) { | ||
| return this.thread.postMessage(data); | ||
| } | ||
| async handleMessage(message) { | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "shardEvent" /* SHARD_EVENT */: | ||
| this.strategy.manager.emit(content.event, { ...content.data, shardId: content.shardId }); | ||
| break; | ||
| case "requestIdentify" /* REQUEST_IDENTIFY */: { | ||
| const request = { | ||
| op: "requestIdentify" /* REQUEST_IDENTIFY */, | ||
| data: { shardId: content.data.shardId }, | ||
| internal: true | ||
| }; | ||
| await this.strategy.ipc.send({ content: request, repliable: true }); | ||
| message.reply(null); | ||
| break; | ||
| } | ||
| case "cancelIdentify" /* CANCEL_IDENTIFY */: { | ||
| const request = { | ||
| op: "cancelIdentify" /* CANCEL_IDENTIFY */, | ||
| data: { shardId: content.data.shardId }, | ||
| internal: true | ||
| }; | ||
| await this.strategy.ipc.send({ content: request }); | ||
| message.reply(null); | ||
| break; | ||
| } | ||
| case "retrieveSession" /* RETRIEVE_SESSION */: { | ||
| const session = await this.strategy.manager.options.retrieveSessionInfo(content.data.shardId); | ||
| message.reply(session); | ||
| break; | ||
| } | ||
| case "updateSession" /* UPDATE_SESSION */: | ||
| await this.strategy.manager.options.updateSessionInfo(content.data.shardId, content.data.sessionInfo); | ||
| break; | ||
| } | ||
| } | ||
| }; | ||
| // src/strategy/IndomitableStrategy.ts | ||
| var IndomitableStrategy = class { | ||
| constructor(manager, ipc) { | ||
| this.manager = manager; | ||
| this.ipc = ipc; | ||
| this.workers = new import_discord.Collection(); | ||
| } | ||
| async spawn(shardIds) { | ||
| const strategyOptions = await (0, import_ws.managerToFetchingStrategyOptions)(this.manager); | ||
| const promises = shardIds.map((shardId) => this.createWorker(shardId, { ...strategyOptions, shardId })); | ||
| await Promise.all(promises); | ||
| } | ||
| async connect() { | ||
| const promises = []; | ||
| for (const worker of this.workers.values()) { | ||
| const content = { | ||
| op: "connect" /* CONNECT */, | ||
| data: {}, | ||
| internal: true | ||
| }; | ||
| promises.push(worker.ipc.send({ content, repliable: true })); | ||
| } | ||
| await Promise.all(promises); | ||
| } | ||
| async destroy(data = {}) { | ||
| const promises = []; | ||
| for (const worker of this.workers.values()) { | ||
| const content = { | ||
| op: "destroy" /* DESTROY */, | ||
| data, | ||
| internal: true | ||
| }; | ||
| promises.push(worker.ipc.send({ content, repliable: true })); | ||
| } | ||
| await Promise.all(promises); | ||
| } | ||
| async reconnect(shardId) { | ||
| const worker = this.workers.get(shardId); | ||
| if (!worker) | ||
| throw new Error(`No worker found for shard #${shardId}`); | ||
| const content = { | ||
| op: "reconnect" /* RECONNECT */, | ||
| data: { recovery: import_ws.WebSocketShardDestroyRecovery.Reconnect }, | ||
| internal: true | ||
| }; | ||
| await worker.ipc.send({ content, repliable: true }); | ||
| } | ||
| async send(shardId, data) { | ||
| const worker = this.workers.get(shardId); | ||
| if (!worker) | ||
| throw new Error(`No worker found for shard #${shardId}`); | ||
| const content = { | ||
| op: "send" /* SEND */, | ||
| data, | ||
| internal: true | ||
| }; | ||
| await worker.ipc.send({ content, repliable: true }); | ||
| } | ||
| async fetchStatus() { | ||
| const collection = new import_discord.Collection(); | ||
| const promises = this.workers.map(async (worker, id) => { | ||
| const content = { | ||
| op: "status" /* STATUS */, | ||
| data: {}, | ||
| internal: true | ||
| }; | ||
| const status = await worker.ipc.send({ content, repliable: true }); | ||
| collection.set(id, status); | ||
| }); | ||
| await Promise.all(promises); | ||
| return collection; | ||
| } | ||
| async createWorker(shardId, workerData) { | ||
| const thread = new import_node_worker_threads.Worker((0, import_node_path.join)(__dirname, "src/strategy/", "Thread.js"), { workerData }); | ||
| await (0, import_node_events2.once)(thread, "online"); | ||
| const ipc = new MainStrategyWorker(shardId, thread, this); | ||
| thread.on("error", (error) => { | ||
| throw error; | ||
| }).on("message", (message) => ipc.handleRawResponse(message, () => null)); | ||
| this.workers.set(shardId, { thread, ipc }); | ||
| return thread; | ||
| } | ||
| }; | ||
| // src/client/ShardClientUtil.ts | ||
| var ShardClientUtil = class extends import_node_events.default { | ||
| var import_node_events3 = __toESM(require("events")); | ||
| var import_timers = require("timers"); | ||
| // src/ipc/BaseWorker.ts | ||
| var BaseWorker = class extends BaseIpc { | ||
| constructor(manager) { | ||
| super(manager); | ||
| process.on( | ||
| "message", | ||
| (data) => this.handleRawResponse(data, () => null) | ||
| ); | ||
| } | ||
| available() { | ||
| return !!process.send; | ||
| } | ||
| sendData(data) { | ||
| process.send(data); | ||
| } | ||
| handleMessage(message) { | ||
| return Promise.resolve(); | ||
| } | ||
| }; | ||
| // src/ipc/ClientWorker.ts | ||
| var internalOpsValues = Object.values(InternalOps); | ||
| var ClientWorker = class extends BaseWorker { | ||
| constructor(shard, manager) { | ||
| super(manager); | ||
| this.shard = shard; | ||
| } | ||
| emitMessage(message) { | ||
| this.shard.emit("message" /* MESSAGE */, message); | ||
| } | ||
| handleMessage(message) { | ||
| if (!internalOpsValues.includes(message.content.op)) | ||
| return Promise.resolve(); | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "eval" /* EVAL */: | ||
| message.reply(this.shard.client._eval(content.data)); | ||
| break; | ||
| case "destroyClient" /* DESTROY_CLIENT */: | ||
| this.shard.client.destroy(); | ||
| message.reply(null); | ||
| } | ||
| return Promise.resolve(); | ||
| } | ||
| }; | ||
| // src/client/ShardClientUtil.ts | ||
| var ShardClientUtil = class extends import_node_events3.default { | ||
| constructor(client, manager) { | ||
| super(); | ||
| this.ipc = new Worker(this, manager); | ||
| this.clusterId = Number(process.env.INDOMITABLE_CLUSTER); | ||
| this.clusterCount = Number(process.env.INDOMITABLE_CLUSTER_TOTAL); | ||
| this.shardIds = process.env.INDOMITABLE_SHARDS.split(" ").map(Number); | ||
| this.shardCount = Number(process.env.INDOMITABLE_SHARDS_TOTAL); | ||
| } | ||
| /** | ||
| * Builds the pre-initialized shard client util | ||
| * @internal | ||
| */ | ||
| build(client) { | ||
| if (this.client) | ||
| return; | ||
| this.client = client; | ||
| this.ipc.build(); | ||
| this.ipc = new ClientWorker(this, manager); | ||
| this.clusterId = EnvProcessData.clusterId; | ||
| this.clusterCount = EnvProcessData.clusterCount; | ||
| this.shardIds = EnvProcessData.shardIds; | ||
| this.shardCount = EnvProcessData.shardCount; | ||
| } | ||
@@ -461,7 +615,8 @@ /** | ||
| } | ||
| return await this.ipc.send(transportable).finally(() => { | ||
| if (!abortableData) | ||
| return; | ||
| (0, import_timers.clearTimeout)(abortableData.timeout); | ||
| }); | ||
| try { | ||
| return await this.ipc.send(transportable); | ||
| } finally { | ||
| if (abortableData) | ||
| (0, import_timers.clearTimeout)(abortableData.timeout); | ||
| } | ||
| } | ||
@@ -474,28 +629,13 @@ }; | ||
| this.manager = manager; | ||
| const shardClientUtil = new ShardClientUtil(manager); | ||
| const concurrencyClient = new ConcurrencyClient(shardClientUtil); | ||
| const clientOptions = manager.clientOptions || {}; | ||
| clientOptions.shards = shardClientUtil.shardIds; | ||
| clientOptions.shardCount = shardClientUtil.shardCount; | ||
| clientOptions.shards = EnvProcessData.shardIds; | ||
| clientOptions.shardCount = EnvProcessData.shardCount; | ||
| if (manager.handleConcurrency) { | ||
| if (!clientOptions.ws) | ||
| clientOptions.ws = {}; | ||
| if (!clientOptions.ws.buildStrategy) { | ||
| clientOptions.ws.buildStrategy = (websocketManager) => { | ||
| websocketManager.options.buildIdentifyThrottler = () => Promise.resolve(concurrencyClient); | ||
| return new import_ws.SimpleShardingStrategy(websocketManager); | ||
| }; | ||
| } else { | ||
| const clone = Function(clientOptions.ws.buildStrategy.toString()); | ||
| clientOptions.ws.buildStrategy = (websocketManager) => { | ||
| websocketManager.options.buildIdentifyThrottler = () => Promise.resolve(concurrencyClient); | ||
| return clone(websocketManager); | ||
| }; | ||
| } | ||
| clientOptions.ws.buildStrategy = (ws) => new IndomitableStrategy(ws, new BaseWorker(manager)); | ||
| } | ||
| const client = new manager.client(clientOptions); | ||
| shardClientUtil.build(client); | ||
| client.shard = shardClientUtil; | ||
| this.client = client; | ||
| this.clusterId = Number(process.env.INDOMITABLE_CLUSTER); | ||
| this.client = new manager.client(clientOptions); | ||
| this.client.shard = new ShardClientUtil(this.client, manager); | ||
| this.clusterId = Number(EnvProcessData.clusterId); | ||
| } | ||
@@ -513,64 +653,56 @@ async start(token) { | ||
| const content = { ...partial, internal: true }; | ||
| shardClientUtil.send({ content, repliable: false }).catch((error) => this.client.emit("error" /* ERROR */, error)); | ||
| shardClientUtil.send({ content, repliable: false }).catch((error) => this.client.emit("ERROR" /* ERROR */, error)); | ||
| } | ||
| }; | ||
| // src/ipc/Main.ts | ||
| var import_crypto2 = require("crypto"); | ||
| var Main = class extends BaseIpc { | ||
| constructor(cluster) { | ||
| super(cluster.manager); | ||
| this.cluster = cluster; | ||
| // src/concurrency/ConcurrencyManager.ts | ||
| var import_ws2 = require("@discordjs/ws"); | ||
| var ConcurrencyManager = class { | ||
| constructor(concurrency) { | ||
| this.throttler = new import_ws2.SimpleIdentifyThrottler(concurrency); | ||
| this.signals = /* @__PURE__ */ new Map(); | ||
| } | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| * Method to try and acquire a lock for identify | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| if (!this.cluster.worker) { | ||
| this.manager.emit("debug" /* DEBUG */, `Tried to send message to cluster ${this.cluster.id} but this worker is yet to be available`); | ||
| return resolve(void 0); | ||
| async waitForIdentify(shardId) { | ||
| try { | ||
| let abort = this.signals.get(shardId); | ||
| if (!abort) { | ||
| abort = new AbortController(); | ||
| this.signals.set(shardId, abort); | ||
| } | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? (0, import_crypto2.randomUUID)() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| this.cluster.worker.send(data); | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| await this.throttler.waitForIdentify(shardId, abort.signal); | ||
| } finally { | ||
| this.signals.delete(shardId); | ||
| } | ||
| } | ||
| async handleMessage(data) { | ||
| const reply = (content) => { | ||
| if (!data.id) | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content, | ||
| internal: true, | ||
| type: "response" /* RESPONSE */ | ||
| }; | ||
| this.cluster.worker.send(response); | ||
| }; | ||
| const message = { | ||
| repliable: !!data.id, | ||
| content: data.content, | ||
| reply | ||
| }; | ||
| if (!message.content.internal) | ||
| return this.manager.emit("message" /* MESSAGE */, message); | ||
| try { | ||
| /** | ||
| * Aborts an acquire lock request | ||
| */ | ||
| abortIdentify(shardId) { | ||
| const signal = this.signals.get(shardId); | ||
| signal?.abort(`Shard ${shardId} aborted the identify request`); | ||
| } | ||
| }; | ||
| // src/ipc/MainWorker.ts | ||
| var internalOpsValues2 = Object.values(InternalOps); | ||
| var clientEventsValues = Object.values(ClientEvents); | ||
| var MainWorker = class extends BaseIpc { | ||
| constructor(cluster) { | ||
| super(cluster.manager); | ||
| this.cluster = cluster; | ||
| } | ||
| available() { | ||
| return !!this.cluster.worker; | ||
| } | ||
| sendData(data) { | ||
| this.cluster.worker?.send(data); | ||
| } | ||
| async handleMessage(message) { | ||
| this.manager.emit("debug" /* DEBUG */, `Received internal message. op: ${message.content.op} | data: ${JSON.stringify(message.content.data || {})}`); | ||
| if (internalOpsValues2.includes(message.content.op)) { | ||
| const content = message.content; | ||
| this.manager.emit("debug" /* DEBUG */, `Received internal message. op: ${content.op} | data: ${JSON.stringify(content.data || {})}`); | ||
| switch (content.op) { | ||
| case "ready" /* READY */: { | ||
| this.manager.emit("clientReady" /* CLIENT_READY */, content.data); | ||
| break; | ||
| } | ||
| case "ping" /* PING */: { | ||
@@ -582,7 +714,7 @@ const end = process.hrtime.bigint().toString(); | ||
| case "eval" /* EVAL */: { | ||
| const data2 = await this.manager.broadcast({ | ||
| const data = await this.manager.broadcast({ | ||
| content, | ||
| repliable: true | ||
| }); | ||
| message.reply(data2); | ||
| message.reply(data); | ||
| break; | ||
@@ -609,2 +741,9 @@ } | ||
| break; | ||
| } | ||
| } else if (clientEventsValues.includes(message.content.op)) { | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "ready" /* READY */: | ||
| this.manager.emit("clientReady" /* CLIENT_READY */, content.data); | ||
| break; | ||
| case "shardReady" /* SHARD_READY */: | ||
@@ -622,12 +761,2 @@ this.manager.emit("shardReady" /* SHARD_READY */, content.data); | ||
| } | ||
| } catch (error) { | ||
| if (!message.repliable) | ||
| throw error; | ||
| message.reply({ | ||
| internal: true, | ||
| error: true, | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }); | ||
| } | ||
@@ -650,3 +779,3 @@ } | ||
| this.shards = options.shards; | ||
| this.ipc = new Main(this); | ||
| this.ipc = new MainWorker(this); | ||
| this.started = false; | ||
@@ -747,40 +876,7 @@ this.started = false; | ||
| var import_node_cluster2 = __toESM(require("cluster")); | ||
| var import_node_events2 = __toESM(require("events")); | ||
| var import_node_events4 = __toESM(require("events")); | ||
| var import_node_os = __toESM(require("os")); | ||
| var import_timers3 = require("timers"); | ||
| // src/concurrency/ConcurrencyManager.ts | ||
| var import_ws2 = require("@discordjs/ws"); | ||
| var ConcurrencyManager = class { | ||
| constructor(concurrency) { | ||
| this.throttler = new import_ws2.SimpleIdentifyThrottler(concurrency); | ||
| this.signals = /* @__PURE__ */ new Map(); | ||
| } | ||
| var Indomitable = class extends import_node_events4.default { | ||
| /** | ||
| * Method to try and acquire a lock for identify | ||
| */ | ||
| async waitForIdentify(shardId) { | ||
| try { | ||
| let abort = this.signals.get(shardId); | ||
| if (!abort) { | ||
| abort = new AbortController(); | ||
| this.signals.set(shardId, abort); | ||
| } | ||
| await this.throttler.waitForIdentify(shardId, abort.signal); | ||
| } finally { | ||
| this.signals.delete(shardId); | ||
| } | ||
| } | ||
| /** | ||
| * Aborts an acquire lock request | ||
| */ | ||
| abortIdentify(shardId) { | ||
| const signal = this.signals.get(shardId); | ||
| signal?.abort(`Shard ${shardId} aborted the identify request`); | ||
| } | ||
| }; | ||
| // src/Indomitable.ts | ||
| var Indomitable = class extends import_node_events2.default { | ||
| /** | ||
| * @param [options.clusterCount=auto] The amount of clusters to spawn. Expects a number or 'auto' | ||
@@ -913,7 +1009,8 @@ * @param [options.shardCount=auto] The number of shards to create. Expects a number or 'auto' | ||
| } | ||
| return await cluster.ipc.send(transportable).finally(() => { | ||
| if (!abortableData) | ||
| return; | ||
| (0, import_timers3.clearTimeout)(abortableData.timeout); | ||
| }); | ||
| try { | ||
| return await cluster.ipc.send(transportable); | ||
| } finally { | ||
| if (abortableData) | ||
| (0, import_timers3.clearTimeout)(abortableData.timeout); | ||
| } | ||
| } | ||
@@ -930,10 +1027,11 @@ /** | ||
| } | ||
| const results = await Promise.all([...this.clusters.values()].map((cluster) => cluster.ipc.send(transportable))).finally(() => { | ||
| if (!abortableData) | ||
| return; | ||
| (0, import_timers3.clearTimeout)(abortableData.timeout); | ||
| }); | ||
| if (!transportable.repliable) | ||
| return void 0; | ||
| return results; | ||
| try { | ||
| const results = await Promise.all([...this.clusters.values()].map((cluster) => cluster.ipc.send(transportable))); | ||
| if (!transportable.repliable) | ||
| return void 0; | ||
| return results; | ||
| } finally { | ||
| if (abortableData) | ||
| (0, import_timers3.clearTimeout)(abortableData.timeout); | ||
| } | ||
| } | ||
@@ -1039,10 +1137,13 @@ /** | ||
| 0 && (module.exports = { | ||
| BaseIpc, | ||
| BaseWorker, | ||
| ClientWorker, | ||
| ClusterManager, | ||
| ConcurrencyManager, | ||
| Indomitable, | ||
| Main, | ||
| MainWorker, | ||
| ShardClient, | ||
| ShardClientUtil, | ||
| Utils, | ||
| Worker | ||
| Utils | ||
| }); | ||
| //# sourceMappingURL=index.js.map |
+390
-282
@@ -7,4 +7,8 @@ var __defProp = Object.defineProperty; | ||
| // src/client/ShardClient.ts | ||
| import { SimpleShardingStrategy } from "@discordjs/ws"; | ||
| // node_modules/tsup/assets/esm_shims.js | ||
| import { fileURLToPath } from "url"; | ||
| import path from "path"; | ||
| var getFilename = () => fileURLToPath(import.meta.url); | ||
| var getDirname = () => path.dirname(getFilename()); | ||
| var __dirname = /* @__PURE__ */ getDirname(); | ||
@@ -17,19 +21,48 @@ // src/Util.ts | ||
| Delay: () => Delay, | ||
| EnvProcessData: () => EnvProcessData, | ||
| Fetch: () => Fetch, | ||
| FetchSessions: () => FetchSessions, | ||
| InternalOps: () => InternalOps, | ||
| LibraryEvents: () => LibraryEvents, | ||
| MainStrategyOps: () => MainStrategyOps, | ||
| MakeAbortableRequest: () => MakeAbortableRequest, | ||
| RawIpcMessageType: () => RawIpcMessageType | ||
| RawIpcMessageType: () => RawIpcMessageType, | ||
| ThreadStrategyOps: () => ThreadStrategyOps | ||
| }); | ||
| import Https from "https"; | ||
| var EnvProcessData = { | ||
| clusterId: Number(process.env.INDOMITABLE_CLUSTER || 0), | ||
| clusterCount: Number(process.env.INDOMITABLE_CLUSTER_TOTAL || 0), | ||
| shardIds: (process.env.INDOMITABLE_SHARDS || "").split(" ").map(Number), | ||
| shardCount: Number(process.env.INDOMITABLE_SHARDS_TOTAL || 0) | ||
| }; | ||
| var MainStrategyOps = /* @__PURE__ */ ((MainStrategyOps2) => { | ||
| MainStrategyOps2["CONNECT"] = "connect"; | ||
| MainStrategyOps2["DESTROY"] = "destroy"; | ||
| MainStrategyOps2["SEND"] = "send"; | ||
| MainStrategyOps2["STATUS"] = "status"; | ||
| MainStrategyOps2["RECONNECT"] = "reconnect"; | ||
| return MainStrategyOps2; | ||
| })(MainStrategyOps || {}); | ||
| var ThreadStrategyOps = /* @__PURE__ */ ((ThreadStrategyOps2) => { | ||
| ThreadStrategyOps2["REQUEST_IDENTIFY"] = "requestIdentify"; | ||
| ThreadStrategyOps2["CANCEL_IDENTIFY"] = "cancelIdentify"; | ||
| ThreadStrategyOps2["SHARD_EVENT"] = "shardEvent"; | ||
| ThreadStrategyOps2["RETRIEVE_SESSION"] = "retrieveSession"; | ||
| ThreadStrategyOps2["UPDATE_SESSION"] = "updateSession"; | ||
| return ThreadStrategyOps2; | ||
| })(ThreadStrategyOps || {}); | ||
| var InternalOps = /* @__PURE__ */ ((InternalOps2) => { | ||
| InternalOps2["EVAL"] = "eval"; | ||
| InternalOps2["RESTART"] = "restart"; | ||
| InternalOps2["RESTART_ALL"] = "restartAll"; | ||
| InternalOps2["DESTROY_CLIENT"] = "destroyClient"; | ||
| InternalOps2["REQUEST_IDENTIFY"] = "requestIdentify"; | ||
| InternalOps2["CANCEL_IDENTIFY"] = "cancelIdentify"; | ||
| InternalOps2["SESSION_INFO"] = "sessionInfo"; | ||
| InternalOps2["PING"] = "ping"; | ||
| return InternalOps2; | ||
| })(InternalOps || {}); | ||
| var ClientEvents = /* @__PURE__ */ ((ClientEvents2) => { | ||
| ClientEvents2["EVAL"] = "eval"; | ||
| ClientEvents2["RESTART"] = "restart"; | ||
| ClientEvents2["RESTART_ALL"] = "restartAll"; | ||
| ClientEvents2["DESTROY_CLIENT"] = "destroyClient"; | ||
| ClientEvents2["REQUEST_IDENTIFY"] = "requestIdentify"; | ||
| ClientEvents2["CANCEL_IDENTIFY"] = "cancelIdentify"; | ||
| ClientEvents2["SESSION_INFO"] = "sessionInfo"; | ||
| ClientEvents2["READY"] = "ready"; | ||
| ClientEvents2["PING"] = "ping"; | ||
| ClientEvents2["SHARD_READY"] = "shardReady"; | ||
@@ -39,2 +72,3 @@ ClientEvents2["SHARD_RECONNECT"] = "shardReconnect"; | ||
| ClientEvents2["SHARD_DISCONNECT"] = "shardDisconnect"; | ||
| ClientEvents2["ERROR"] = "ERROR"; | ||
| return ClientEvents2; | ||
@@ -60,2 +94,3 @@ })(ClientEvents || {}); | ||
| RawIpcMessageType2["RESPONSE"] = "response"; | ||
| RawIpcMessageType2["ERROR"] = "error"; | ||
| return RawIpcMessageType2; | ||
@@ -109,48 +144,17 @@ })(RawIpcMessageType || {}); | ||
| // src/concurrency/ConcurrencyClient.ts | ||
| var ConcurrencyClient = class { | ||
| constructor(shard) { | ||
| this.shard = shard; | ||
| } | ||
| /** | ||
| * Method to try and acquire a lock for identify | ||
| */ | ||
| async waitForIdentify(shardId, signal) { | ||
| const content = { | ||
| op: "requestIdentify" /* REQUEST_IDENTIFY */, | ||
| data: { shardId }, | ||
| internal: true | ||
| }; | ||
| const listener = () => this.abortIdentify(shardId); | ||
| try { | ||
| signal.addEventListener("abort", listener); | ||
| await this.shard.send({ content, repliable: true }); | ||
| } catch (error) { | ||
| if (error.message.includes("aborted the identify")) | ||
| throw error; | ||
| } finally { | ||
| signal.removeEventListener("abort", listener); | ||
| } | ||
| } | ||
| /** | ||
| * Aborts an acquire lock request | ||
| */ | ||
| abortIdentify(shardId) { | ||
| const content = { | ||
| op: "cancelIdentify" /* CANCEL_IDENTIFY */, | ||
| data: { shardId }, | ||
| internal: true | ||
| }; | ||
| this.shard.send({ content, repliable: false }).catch(() => null); | ||
| } | ||
| }; | ||
| // src/strategy/IndomitableStrategy.ts | ||
| import { join } from "path"; | ||
| import { once } from "events"; | ||
| import { Worker } from "worker_threads"; | ||
| import { | ||
| managerToFetchingStrategyOptions, | ||
| WebSocketShardDestroyRecovery | ||
| } from "@discordjs/ws"; | ||
| import { Collection } from "discord.js"; | ||
| // src/client/ShardClientUtil.ts | ||
| // src/ipc/MainStrategyWorker.ts | ||
| import EventEmitter from "events"; | ||
| import { clearTimeout } from "timers"; | ||
| // src/ipc/Worker.ts | ||
| // src/ipc/BaseIpc.ts | ||
| import { randomUUID } from "crypto"; | ||
| // src/ipc/BaseIpc.ts | ||
| var BaseIpc = class { | ||
@@ -181,2 +185,26 @@ constructor(manager) { | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| if (!this.available()) { | ||
| this.manager.emit("debug" /* DEBUG */, "IPC tried to send a message, but the ipc communication is not yet ready"); | ||
| return resolve(void 0); | ||
| } | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? randomUUID() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| this.sendData(data); | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| } | ||
| /** | ||
| * Taps into message event of worker or primary process to handle ipc communication | ||
@@ -192,4 +220,5 @@ * @internal | ||
| case "message" /* MESSAGE */: | ||
| return await this.handleMessage(data); | ||
| return await this.handleUnparsedMessage(data); | ||
| case "response" /* RESPONSE */: | ||
| case "error" /* ERROR */: | ||
| return this.handlePromise(data); | ||
@@ -225,57 +254,13 @@ } | ||
| } | ||
| if (data.content?.internal && data.content?.error) { | ||
| const error = new Error(data.content.reason || "Unknown error reason"); | ||
| error.stack = data.content.stack; | ||
| error.name = data.content.name; | ||
| return promise.reject(error); | ||
| if (data.type === "error" /* ERROR */) { | ||
| const content = data.content; | ||
| const error = new Error(content.reason); | ||
| error.stack = content.stack; | ||
| error.name = content.name; | ||
| promise.reject(error); | ||
| return; | ||
| } | ||
| promise.resolve(data.content); | ||
| } | ||
| }; | ||
| // src/ipc/Worker.ts | ||
| var Worker = class extends BaseIpc { | ||
| constructor(shard, manager) { | ||
| super(manager); | ||
| this.shard = shard; | ||
| this.built = false; | ||
| } | ||
| /** | ||
| * Builds the pre-initialized worker ipc | ||
| * @internal | ||
| */ | ||
| build() { | ||
| if (this.built) | ||
| return; | ||
| this.built = true; | ||
| process.on( | ||
| "message", | ||
| (data) => this.handleRawResponse(data, (error) => this.shard.client.emit("error" /* ERROR */, error)) | ||
| ); | ||
| } | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? randomUUID() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| try { | ||
| process.send(data); | ||
| } catch (error) { | ||
| return reject(error); | ||
| } | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| } | ||
| handleMessage(data) { | ||
| async handleUnparsedMessage(data) { | ||
| const reply = (content) => { | ||
@@ -290,3 +275,3 @@ if (!data.id) | ||
| }; | ||
| process.send(response); | ||
| this.sendData(response); | ||
| }; | ||
@@ -298,47 +283,223 @@ const message = { | ||
| }; | ||
| if (!message.content.internal) | ||
| return this.shard.emit("message" /* MESSAGE */, message); | ||
| if (!data.content.internal) | ||
| return this.emitMessage(message); | ||
| try { | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "eval" /* EVAL */: | ||
| message.reply(this.shard.client._eval(content.data)); | ||
| break; | ||
| case "destroyClient" /* DESTROY_CLIENT */: | ||
| this.shard.client.destroy(); | ||
| message.reply(null); | ||
| } | ||
| await this.handleMessage(message); | ||
| } catch (error) { | ||
| if (!message.repliable) | ||
| throw error; | ||
| message.reply({ | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content: { | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }, | ||
| internal: true, | ||
| error: true, | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }); | ||
| type: "error" /* ERROR */ | ||
| }; | ||
| this.sendData(response); | ||
| } | ||
| } | ||
| emitMessage(message) { | ||
| this.manager.emit("message" /* MESSAGE */, message); | ||
| } | ||
| }; | ||
| // src/ipc/MainStrategyWorker.ts | ||
| var MainStrategyWorker = class extends BaseIpc { | ||
| constructor(id, thread, strategy) { | ||
| super(new EventEmitter()); | ||
| this.id = id; | ||
| this.thread = thread; | ||
| this.strategy = strategy; | ||
| } | ||
| available() { | ||
| return true; | ||
| } | ||
| sendData(data) { | ||
| return this.thread.postMessage(data); | ||
| } | ||
| async handleMessage(message) { | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "shardEvent" /* SHARD_EVENT */: | ||
| this.strategy.manager.emit(content.event, { ...content.data, shardId: content.shardId }); | ||
| break; | ||
| case "requestIdentify" /* REQUEST_IDENTIFY */: { | ||
| const request = { | ||
| op: "requestIdentify" /* REQUEST_IDENTIFY */, | ||
| data: { shardId: content.data.shardId }, | ||
| internal: true | ||
| }; | ||
| await this.strategy.ipc.send({ content: request, repliable: true }); | ||
| message.reply(null); | ||
| break; | ||
| } | ||
| case "cancelIdentify" /* CANCEL_IDENTIFY */: { | ||
| const request = { | ||
| op: "cancelIdentify" /* CANCEL_IDENTIFY */, | ||
| data: { shardId: content.data.shardId }, | ||
| internal: true | ||
| }; | ||
| await this.strategy.ipc.send({ content: request }); | ||
| message.reply(null); | ||
| break; | ||
| } | ||
| case "retrieveSession" /* RETRIEVE_SESSION */: { | ||
| const session = await this.strategy.manager.options.retrieveSessionInfo(content.data.shardId); | ||
| message.reply(session); | ||
| break; | ||
| } | ||
| case "updateSession" /* UPDATE_SESSION */: | ||
| await this.strategy.manager.options.updateSessionInfo(content.data.shardId, content.data.sessionInfo); | ||
| break; | ||
| } | ||
| } | ||
| }; | ||
| // src/strategy/IndomitableStrategy.ts | ||
| var IndomitableStrategy = class { | ||
| constructor(manager, ipc) { | ||
| this.manager = manager; | ||
| this.ipc = ipc; | ||
| this.workers = new Collection(); | ||
| } | ||
| async spawn(shardIds) { | ||
| const strategyOptions = await managerToFetchingStrategyOptions(this.manager); | ||
| const promises = shardIds.map((shardId) => this.createWorker(shardId, { ...strategyOptions, shardId })); | ||
| await Promise.all(promises); | ||
| } | ||
| async connect() { | ||
| const promises = []; | ||
| for (const worker of this.workers.values()) { | ||
| const content = { | ||
| op: "connect" /* CONNECT */, | ||
| data: {}, | ||
| internal: true | ||
| }; | ||
| promises.push(worker.ipc.send({ content, repliable: true })); | ||
| } | ||
| await Promise.all(promises); | ||
| } | ||
| async destroy(data = {}) { | ||
| const promises = []; | ||
| for (const worker of this.workers.values()) { | ||
| const content = { | ||
| op: "destroy" /* DESTROY */, | ||
| data, | ||
| internal: true | ||
| }; | ||
| promises.push(worker.ipc.send({ content, repliable: true })); | ||
| } | ||
| await Promise.all(promises); | ||
| } | ||
| async reconnect(shardId) { | ||
| const worker = this.workers.get(shardId); | ||
| if (!worker) | ||
| throw new Error(`No worker found for shard #${shardId}`); | ||
| const content = { | ||
| op: "reconnect" /* RECONNECT */, | ||
| data: { recovery: WebSocketShardDestroyRecovery.Reconnect }, | ||
| internal: true | ||
| }; | ||
| await worker.ipc.send({ content, repliable: true }); | ||
| } | ||
| async send(shardId, data) { | ||
| const worker = this.workers.get(shardId); | ||
| if (!worker) | ||
| throw new Error(`No worker found for shard #${shardId}`); | ||
| const content = { | ||
| op: "send" /* SEND */, | ||
| data, | ||
| internal: true | ||
| }; | ||
| await worker.ipc.send({ content, repliable: true }); | ||
| } | ||
| async fetchStatus() { | ||
| const collection = new Collection(); | ||
| const promises = this.workers.map(async (worker, id) => { | ||
| const content = { | ||
| op: "status" /* STATUS */, | ||
| data: {}, | ||
| internal: true | ||
| }; | ||
| const status = await worker.ipc.send({ content, repliable: true }); | ||
| collection.set(id, status); | ||
| }); | ||
| await Promise.all(promises); | ||
| return collection; | ||
| } | ||
| async createWorker(shardId, workerData) { | ||
| const thread = new Worker(join(__dirname, "src/strategy/", "Thread.js"), { workerData }); | ||
| await once(thread, "online"); | ||
| const ipc = new MainStrategyWorker(shardId, thread, this); | ||
| thread.on("error", (error) => { | ||
| throw error; | ||
| }).on("message", (message) => ipc.handleRawResponse(message, () => null)); | ||
| this.workers.set(shardId, { thread, ipc }); | ||
| return thread; | ||
| } | ||
| }; | ||
| // src/client/ShardClientUtil.ts | ||
| var ShardClientUtil = class extends EventEmitter { | ||
| import EventEmitter2 from "events"; | ||
| import { clearTimeout } from "timers"; | ||
| // src/ipc/BaseWorker.ts | ||
| var BaseWorker = class extends BaseIpc { | ||
| constructor(manager) { | ||
| super(manager); | ||
| process.on( | ||
| "message", | ||
| (data) => this.handleRawResponse(data, () => null) | ||
| ); | ||
| } | ||
| available() { | ||
| return !!process.send; | ||
| } | ||
| sendData(data) { | ||
| process.send(data); | ||
| } | ||
| handleMessage(message) { | ||
| return Promise.resolve(); | ||
| } | ||
| }; | ||
| // src/ipc/ClientWorker.ts | ||
| var internalOpsValues = Object.values(InternalOps); | ||
| var ClientWorker = class extends BaseWorker { | ||
| constructor(shard, manager) { | ||
| super(manager); | ||
| this.shard = shard; | ||
| } | ||
| emitMessage(message) { | ||
| this.shard.emit("message" /* MESSAGE */, message); | ||
| } | ||
| handleMessage(message) { | ||
| if (!internalOpsValues.includes(message.content.op)) | ||
| return Promise.resolve(); | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "eval" /* EVAL */: | ||
| message.reply(this.shard.client._eval(content.data)); | ||
| break; | ||
| case "destroyClient" /* DESTROY_CLIENT */: | ||
| this.shard.client.destroy(); | ||
| message.reply(null); | ||
| } | ||
| return Promise.resolve(); | ||
| } | ||
| }; | ||
| // src/client/ShardClientUtil.ts | ||
| var ShardClientUtil = class extends EventEmitter2 { | ||
| constructor(client, manager) { | ||
| super(); | ||
| this.ipc = new Worker(this, manager); | ||
| this.clusterId = Number(process.env.INDOMITABLE_CLUSTER); | ||
| this.clusterCount = Number(process.env.INDOMITABLE_CLUSTER_TOTAL); | ||
| this.shardIds = process.env.INDOMITABLE_SHARDS.split(" ").map(Number); | ||
| this.shardCount = Number(process.env.INDOMITABLE_SHARDS_TOTAL); | ||
| } | ||
| /** | ||
| * Builds the pre-initialized shard client util | ||
| * @internal | ||
| */ | ||
| build(client) { | ||
| if (this.client) | ||
| return; | ||
| this.client = client; | ||
| this.ipc.build(); | ||
| this.ipc = new ClientWorker(this, manager); | ||
| this.clusterId = EnvProcessData.clusterId; | ||
| this.clusterCount = EnvProcessData.clusterCount; | ||
| this.shardIds = EnvProcessData.shardIds; | ||
| this.shardCount = EnvProcessData.shardCount; | ||
| } | ||
@@ -425,7 +586,8 @@ /** | ||
| } | ||
| return await this.ipc.send(transportable).finally(() => { | ||
| if (!abortableData) | ||
| return; | ||
| clearTimeout(abortableData.timeout); | ||
| }); | ||
| try { | ||
| return await this.ipc.send(transportable); | ||
| } finally { | ||
| if (abortableData) | ||
| clearTimeout(abortableData.timeout); | ||
| } | ||
| } | ||
@@ -438,28 +600,13 @@ }; | ||
| this.manager = manager; | ||
| const shardClientUtil = new ShardClientUtil(manager); | ||
| const concurrencyClient = new ConcurrencyClient(shardClientUtil); | ||
| const clientOptions = manager.clientOptions || {}; | ||
| clientOptions.shards = shardClientUtil.shardIds; | ||
| clientOptions.shardCount = shardClientUtil.shardCount; | ||
| clientOptions.shards = EnvProcessData.shardIds; | ||
| clientOptions.shardCount = EnvProcessData.shardCount; | ||
| if (manager.handleConcurrency) { | ||
| if (!clientOptions.ws) | ||
| clientOptions.ws = {}; | ||
| if (!clientOptions.ws.buildStrategy) { | ||
| clientOptions.ws.buildStrategy = (websocketManager) => { | ||
| websocketManager.options.buildIdentifyThrottler = () => Promise.resolve(concurrencyClient); | ||
| return new SimpleShardingStrategy(websocketManager); | ||
| }; | ||
| } else { | ||
| const clone = Function(clientOptions.ws.buildStrategy.toString()); | ||
| clientOptions.ws.buildStrategy = (websocketManager) => { | ||
| websocketManager.options.buildIdentifyThrottler = () => Promise.resolve(concurrencyClient); | ||
| return clone(websocketManager); | ||
| }; | ||
| } | ||
| clientOptions.ws.buildStrategy = (ws) => new IndomitableStrategy(ws, new BaseWorker(manager)); | ||
| } | ||
| const client = new manager.client(clientOptions); | ||
| shardClientUtil.build(client); | ||
| client.shard = shardClientUtil; | ||
| this.client = client; | ||
| this.clusterId = Number(process.env.INDOMITABLE_CLUSTER); | ||
| this.client = new manager.client(clientOptions); | ||
| this.client.shard = new ShardClientUtil(this.client, manager); | ||
| this.clusterId = Number(EnvProcessData.clusterId); | ||
| } | ||
@@ -477,64 +624,56 @@ async start(token) { | ||
| const content = { ...partial, internal: true }; | ||
| shardClientUtil.send({ content, repliable: false }).catch((error) => this.client.emit("error" /* ERROR */, error)); | ||
| shardClientUtil.send({ content, repliable: false }).catch((error) => this.client.emit("ERROR" /* ERROR */, error)); | ||
| } | ||
| }; | ||
| // src/ipc/Main.ts | ||
| import { randomUUID as randomUUID2 } from "crypto"; | ||
| var Main = class extends BaseIpc { | ||
| constructor(cluster) { | ||
| super(cluster.manager); | ||
| this.cluster = cluster; | ||
| // src/concurrency/ConcurrencyManager.ts | ||
| import { SimpleIdentifyThrottler } from "@discordjs/ws"; | ||
| var ConcurrencyManager = class { | ||
| constructor(concurrency) { | ||
| this.throttler = new SimpleIdentifyThrottler(concurrency); | ||
| this.signals = /* @__PURE__ */ new Map(); | ||
| } | ||
| /** | ||
| * Raw send method without abort controller handling | ||
| * @param transportable Data to send | ||
| * Method to try and acquire a lock for identify | ||
| */ | ||
| send(transportable) { | ||
| return new Promise((resolve, reject) => { | ||
| if (!this.cluster.worker) { | ||
| this.manager.emit("debug" /* DEBUG */, `Tried to send message to cluster ${this.cluster.id} but this worker is yet to be available`); | ||
| return resolve(void 0); | ||
| async waitForIdentify(shardId) { | ||
| try { | ||
| let abort = this.signals.get(shardId); | ||
| if (!abort) { | ||
| abort = new AbortController(); | ||
| this.signals.set(shardId, abort); | ||
| } | ||
| const repliable = transportable.repliable || false; | ||
| const id = repliable ? randomUUID2() : null; | ||
| const data = { | ||
| id, | ||
| content: transportable.content, | ||
| internal: true, | ||
| type: "message" /* MESSAGE */ | ||
| }; | ||
| this.cluster.worker.send(data); | ||
| if (!id) | ||
| return resolve(void 0); | ||
| this.waitForPromise({ id, resolve, reject, signal: transportable.signal }); | ||
| }); | ||
| await this.throttler.waitForIdentify(shardId, abort.signal); | ||
| } finally { | ||
| this.signals.delete(shardId); | ||
| } | ||
| } | ||
| async handleMessage(data) { | ||
| const reply = (content) => { | ||
| if (!data.id) | ||
| return; | ||
| const response = { | ||
| id: data.id, | ||
| content, | ||
| internal: true, | ||
| type: "response" /* RESPONSE */ | ||
| }; | ||
| this.cluster.worker.send(response); | ||
| }; | ||
| const message = { | ||
| repliable: !!data.id, | ||
| content: data.content, | ||
| reply | ||
| }; | ||
| if (!message.content.internal) | ||
| return this.manager.emit("message" /* MESSAGE */, message); | ||
| try { | ||
| /** | ||
| * Aborts an acquire lock request | ||
| */ | ||
| abortIdentify(shardId) { | ||
| const signal = this.signals.get(shardId); | ||
| signal?.abort(`Shard ${shardId} aborted the identify request`); | ||
| } | ||
| }; | ||
| // src/ipc/MainWorker.ts | ||
| var internalOpsValues2 = Object.values(InternalOps); | ||
| var clientEventsValues = Object.values(ClientEvents); | ||
| var MainWorker = class extends BaseIpc { | ||
| constructor(cluster) { | ||
| super(cluster.manager); | ||
| this.cluster = cluster; | ||
| } | ||
| available() { | ||
| return !!this.cluster.worker; | ||
| } | ||
| sendData(data) { | ||
| this.cluster.worker?.send(data); | ||
| } | ||
| async handleMessage(message) { | ||
| this.manager.emit("debug" /* DEBUG */, `Received internal message. op: ${message.content.op} | data: ${JSON.stringify(message.content.data || {})}`); | ||
| if (internalOpsValues2.includes(message.content.op)) { | ||
| const content = message.content; | ||
| this.manager.emit("debug" /* DEBUG */, `Received internal message. op: ${content.op} | data: ${JSON.stringify(content.data || {})}`); | ||
| switch (content.op) { | ||
| case "ready" /* READY */: { | ||
| this.manager.emit("clientReady" /* CLIENT_READY */, content.data); | ||
| break; | ||
| } | ||
| case "ping" /* PING */: { | ||
@@ -546,7 +685,7 @@ const end = process.hrtime.bigint().toString(); | ||
| case "eval" /* EVAL */: { | ||
| const data2 = await this.manager.broadcast({ | ||
| const data = await this.manager.broadcast({ | ||
| content, | ||
| repliable: true | ||
| }); | ||
| message.reply(data2); | ||
| message.reply(data); | ||
| break; | ||
@@ -573,2 +712,9 @@ } | ||
| break; | ||
| } | ||
| } else if (clientEventsValues.includes(message.content.op)) { | ||
| const content = message.content; | ||
| switch (content.op) { | ||
| case "ready" /* READY */: | ||
| this.manager.emit("clientReady" /* CLIENT_READY */, content.data); | ||
| break; | ||
| case "shardReady" /* SHARD_READY */: | ||
@@ -586,12 +732,2 @@ this.manager.emit("shardReady" /* SHARD_READY */, content.data); | ||
| } | ||
| } catch (error) { | ||
| if (!message.repliable) | ||
| throw error; | ||
| message.reply({ | ||
| internal: true, | ||
| error: true, | ||
| name: error.name, | ||
| reason: error.reason, | ||
| stack: error.stack | ||
| }); | ||
| } | ||
@@ -614,3 +750,3 @@ } | ||
| this.shards = options.shards; | ||
| this.ipc = new Main(this); | ||
| this.ipc = new MainWorker(this); | ||
| this.started = false; | ||
@@ -711,40 +847,7 @@ this.started = false; | ||
| import Cluster2 from "cluster"; | ||
| import EventEmitter2 from "events"; | ||
| import EventEmitter3 from "events"; | ||
| import Os from "os"; | ||
| import { clearTimeout as clearTimeout3 } from "timers"; | ||
| // src/concurrency/ConcurrencyManager.ts | ||
| import { SimpleIdentifyThrottler } from "@discordjs/ws"; | ||
| var ConcurrencyManager = class { | ||
| constructor(concurrency) { | ||
| this.throttler = new SimpleIdentifyThrottler(concurrency); | ||
| this.signals = /* @__PURE__ */ new Map(); | ||
| } | ||
| var Indomitable = class extends EventEmitter3 { | ||
| /** | ||
| * Method to try and acquire a lock for identify | ||
| */ | ||
| async waitForIdentify(shardId) { | ||
| try { | ||
| let abort = this.signals.get(shardId); | ||
| if (!abort) { | ||
| abort = new AbortController(); | ||
| this.signals.set(shardId, abort); | ||
| } | ||
| await this.throttler.waitForIdentify(shardId, abort.signal); | ||
| } finally { | ||
| this.signals.delete(shardId); | ||
| } | ||
| } | ||
| /** | ||
| * Aborts an acquire lock request | ||
| */ | ||
| abortIdentify(shardId) { | ||
| const signal = this.signals.get(shardId); | ||
| signal?.abort(`Shard ${shardId} aborted the identify request`); | ||
| } | ||
| }; | ||
| // src/Indomitable.ts | ||
| var Indomitable = class extends EventEmitter2 { | ||
| /** | ||
| * @param [options.clusterCount=auto] The amount of clusters to spawn. Expects a number or 'auto' | ||
@@ -877,7 +980,8 @@ * @param [options.shardCount=auto] The number of shards to create. Expects a number or 'auto' | ||
| } | ||
| return await cluster.ipc.send(transportable).finally(() => { | ||
| if (!abortableData) | ||
| return; | ||
| clearTimeout3(abortableData.timeout); | ||
| }); | ||
| try { | ||
| return await cluster.ipc.send(transportable); | ||
| } finally { | ||
| if (abortableData) | ||
| clearTimeout3(abortableData.timeout); | ||
| } | ||
| } | ||
@@ -894,10 +998,11 @@ /** | ||
| } | ||
| const results = await Promise.all([...this.clusters.values()].map((cluster) => cluster.ipc.send(transportable))).finally(() => { | ||
| if (!abortableData) | ||
| return; | ||
| clearTimeout3(abortableData.timeout); | ||
| }); | ||
| if (!transportable.repliable) | ||
| return void 0; | ||
| return results; | ||
| try { | ||
| const results = await Promise.all([...this.clusters.values()].map((cluster) => cluster.ipc.send(transportable))); | ||
| if (!transportable.repliable) | ||
| return void 0; | ||
| return results; | ||
| } finally { | ||
| if (abortableData) | ||
| clearTimeout3(abortableData.timeout); | ||
| } | ||
| } | ||
@@ -1002,10 +1107,13 @@ /** | ||
| export { | ||
| BaseIpc, | ||
| BaseWorker, | ||
| ClientWorker, | ||
| ClusterManager, | ||
| ConcurrencyManager, | ||
| Indomitable, | ||
| Main, | ||
| MainWorker, | ||
| ShardClient, | ||
| ShardClientUtil, | ||
| Util_exports as Utils, | ||
| Worker | ||
| Util_exports as Utils | ||
| }; | ||
| //# sourceMappingURL=index.mjs.map |
+2
-1
| { | ||
| "name": "indomitable", | ||
| "version": "3.0.4", | ||
| "version": "4.0.0", | ||
| "description": "A lightweight (the actual ship is heavy though), performant, powerful & no dependency sharder for Discord.JS", | ||
@@ -20,2 +20,3 @@ "main": "dist/index.js", | ||
| "build:docs": "typedoc --theme default --readme README.md --out docs/ --entryPointStrategy expand src/.", | ||
| "lint": "eslint . --ext .ts --fix", | ||
| "prepare": "npm run build:ts" | ||
@@ -22,0 +23,0 @@ }, |
+7
-7
@@ -22,9 +22,9 @@ ## Indomitable | ||
| * Concurrency Support | ||
| * Shard Concurrency Support | ||
| * Promisified IPC (Bi-directional) | ||
| * Easy to use promised based IPC | ||
| * No dependencies (v2 onwards) | ||
| * No dependencies | ||
| * Very cute (and lazy like the Kaiju Princess! *If you know, you know*) | ||
| * Very cute (and lazy) | ||
@@ -116,3 +116,3 @@ ## Used in prod by the ff: | ||
| }); | ||
| // Worker Process (your client most likely) | ||
| // ClientWorker Process (your client most likely) | ||
| client.shard.send({ content: { op: 'something' } }) | ||
@@ -132,3 +132,3 @@ .catch(console.error); | ||
| }); | ||
| // Worker Process (your client most likely) | ||
| // ClientWorker Process (your client most likely) | ||
| client.shard.send({ content: { op: 'something' }, repliable: true }) | ||
@@ -157,3 +157,3 @@ .then(console.log) | ||
| // Worker Process (your client most likely) | ||
| // ClientWorker Process (your client most likely) | ||
| client.shard.on('message', message => { | ||
@@ -160,0 +160,0 @@ if (message.content.op === 'something') { |
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance 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
343763
36.99%13
62.5%3465
32.86%7
-12.5%21
40%