New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

indomitable

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

indomitable - npm Package Compare versions

Comparing version
3.0.4
to
4.0.0
+2
dist/src/strategy/Thread.d.ts
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 };

@@ -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

@@ -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
{
"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 @@ },

@@ -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