fix: types & workerClient#resumeShard

This commit is contained in:
MARCROCK22 2024-11-10 21:51:28 +00:00
parent 103dcc53f3
commit e4233e6a40
11 changed files with 124 additions and 92 deletions

View File

@ -1,5 +1,5 @@
import { type UUID, randomUUID } from 'node:crypto'; import { type UUID, randomUUID } from 'node:crypto';
import { type Awaitable, Logger, delay, lazyLoadPackage, snowflakeToTimestamp } from '../common'; import { type Awaitable, BASE_HOST, Logger, delay, lazyLoadPackage, snowflakeToTimestamp } from '../common';
import type { WorkerData } from '../websocket'; import type { WorkerData } from '../websocket';
import type { WorkerSendApiRequest } from '../websocket/discord/worker'; import type { WorkerSendApiRequest } from '../websocket/discord/worker';
import { CDNRouter, Router } from './Router'; import { CDNRouter, Router } from './Router';
@ -39,16 +39,12 @@ export class ApiHandler {
constructor(options: ApiHandlerOptions) { constructor(options: ApiHandlerOptions) {
this.options = { this.options = {
baseUrl: 'api/v10', baseUrl: 'api/v10',
domain: 'https://discord.com', domain: BASE_HOST,
type: 'Bot', type: 'Bot',
...options, ...options,
userAgent: DefaultUserAgent, userAgent: DefaultUserAgent,
}; };
if (options.debug) { if (options.debug) this.debug = true;
this.debugger = new Logger({
name: '[API]',
});
}
const worker_threads = lazyLoadPackage<typeof import('node:worker_threads')>('node:worker_threads'); const worker_threads = lazyLoadPackage<typeof import('node:worker_threads')>('node:worker_threads');
@ -61,6 +57,14 @@ export class ApiHandler {
} }
} }
set debug(active: boolean) {
this.debugger = active
? new Logger({
name: '[API]',
})
: undefined;
}
get proxy() { get proxy() {
return (this._proxy_ ??= new Router(this).createProxy()); return (this._proxy_ ??= new Router(this).createProxy());
} }

View File

@ -59,8 +59,8 @@ import type { LocaleString, RESTPostAPIChannelMessageJSONBody } from '../types';
import type { MessageStructure } from './transformers'; import type { MessageStructure } from './transformers';
export class BaseClient { export class BaseClient {
rest!: ApiHandler; rest = new ApiHandler({ token: 'INVALID' });
cache!: Cache; cache = new Cache(0, new MemoryAdapter());
applications = new ApplicationShorter(this); applications = new ApplicationShorter(this);
users = new UsersShorter(this); users = new UsersShorter(this);
@ -194,6 +194,7 @@ export class BaseClient {
setServices({ rest, cache, langs, middlewares, handleCommand }: ServicesOptions) { setServices({ rest, cache, langs, middlewares, handleCommand }: ServicesOptions) {
if (rest) { if (rest) {
rest.onRatelimit ??= this.rest.onRatelimit?.bind(rest);
this.rest = rest; this.rest = rest;
} }
if (cache) { if (cache) {
@ -213,7 +214,7 @@ export class BaseClient {
'users', 'users',
'voiceStates', 'voiceStates',
]; ];
let disabledCache: Partial<Record<keyof Cache['disabledCache'], boolean>> = this.cache?.disabledCache ?? {}; let disabledCache: Partial<Record<keyof Cache['disabledCache'], boolean>> = this.cache.disabledCache;
if (typeof cache.disabledCache === 'boolean') { if (typeof cache.disabledCache === 'boolean') {
for (const i of caches) { for (const i of caches) {
@ -227,12 +228,7 @@ export class BaseClient {
disabledCache = cache.disabledCache; disabledCache = cache.disabledCache;
} }
this.cache = new Cache( this.cache = new Cache(this.cache.intents, cache.adapter ?? this.cache.adapter, disabledCache, this);
this.cache?.intents ?? 0,
cache?.adapter ?? this.cache?.adapter ?? new MemoryAdapter(),
disabledCache,
this,
);
} }
if (middlewares) { if (middlewares) {
this.middlewares = middlewares; this.middlewares = middlewares;
@ -270,22 +266,12 @@ export class BaseClient {
const { token: tokenRC, debug } = await this.getRC(); const { token: tokenRC, debug } = await this.getRC();
const token = options?.token ?? tokenRC; const token = options?.token ?? tokenRC;
BaseClient.assertString(token, 'token is not a string');
if (!this.rest) { if (this.rest.options.token === 'INVALID') this.rest.options.token = token;
BaseClient.assertString(token, 'token is not a string'); this.rest.debug = debug;
this.rest = new ApiHandler({
token,
baseUrl: 'api/v10',
domain: 'https://discord.com',
debug,
});
}
if (this.cache) { this.cache.__setClient(this);
this.cache.__setClient(this);
} else {
this.cache = new Cache(0, new MemoryAdapter(), {}, this);
}
if (!this.handleCommand) this.handleCommand = new HandleCommand(this); if (!this.handleCommand) this.handleCommand = new HandleCommand(this);
@ -310,7 +296,6 @@ export class BaseClient {
} }
private syncCachePath(cachePath: string) { private syncCachePath(cachePath: string) {
this.logger.debug('Syncing commands cache');
return promises.writeFile( return promises.writeFile(
cachePath, cachePath,
JSON.stringify( JSON.stringify(

View File

@ -94,6 +94,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
const { token: tokenRC, intents: intentsRC, debug: debugRC } = await this.getRC<InternalRuntimeConfig>(); const { token: tokenRC, intents: intentsRC, debug: debugRC } = await this.getRC<InternalRuntimeConfig>();
const token = options?.token ?? tokenRC; const token = options?.token ?? tokenRC;
const intents = options?.connection?.intents ?? intentsRC; const intents = options?.connection?.intents ?? intentsRC;
this.cache.intents = intents;
if (!this.gateway) { if (!this.gateway) {
BaseClient.assertString(token, 'token is not a string'); BaseClient.assertString(token, 'token is not a string');
@ -123,8 +124,6 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
}); });
} }
this.cache.intents = this.gateway.options.intents;
if (execute) { if (execute) {
await this.execute(options.connection); await this.execute(options.connection);
} else { } else {
@ -138,7 +137,6 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
this.collectors.run('RAW', packet, this), this.collectors.run('RAW', packet, this),
]); //ignore promise ]); //ignore promise
switch (packet.t) { switch (packet.t) {
// Cases where we must obtain the old data before updating
case 'GUILD_MEMBER_UPDATE': case 'GUILD_MEMBER_UPDATE':
{ {
if (!this.memberUpdateHandler.check(packet.d)) { if (!this.memberUpdateHandler.check(packet.d)) {

View File

@ -1,7 +1,7 @@
import { type UUID, randomUUID } from 'node:crypto'; import { type UUID, randomUUID } from 'node:crypto';
import { ApiHandler, Logger } from '..'; import { ApiHandler, Logger } from '..';
import { WorkerAdapter } from '../cache'; import { WorkerAdapter } from '../cache';
import { type DeepPartial, LogLevels, type When, hasIntent, lazyLoadPackage } from '../common'; import { type DeepPartial, LogLevels, type MakeRequired, type When, hasIntent, lazyLoadPackage } from '../common';
import { EventHandler } from '../events'; import { EventHandler } from '../events';
import type { GatewayDispatchPayload, GatewaySendPayload } from '../types'; import type { GatewayDispatchPayload, GatewaySendPayload } from '../types';
import { Shard, type ShardManagerOptions, type WorkerData, properties } from '../websocket'; import { Shard, type ShardManagerOptions, type WorkerData, properties } from '../websocket';
@ -23,13 +23,14 @@ import type {
WorkerStart, WorkerStart,
WorkerStartResharding, WorkerStartResharding,
} from '../websocket/discord/worker'; } from '../websocket/discord/worker';
import type { ManagerMessages } from '../websocket/discord/workermanager'; import type { ManagerMessages, ManagerSpawnShards } from '../websocket/discord/workermanager';
import type { BaseClientOptions, ServicesOptions, StartOptions } from './base'; import type { BaseClientOptions, ServicesOptions, StartOptions } from './base';
import { BaseClient } from './base'; import { BaseClient } from './base';
import type { Client, ClientOptions } from './client'; import type { Client, ClientOptions } from './client';
import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate'; import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate'; import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
import type { ShardData } from '../websocket/discord/shared';
import { Collectors } from './collectors'; import { Collectors } from './collectors';
import { type ClientUserStructure, Transformers } from './transformers'; import { type ClientUserStructure, Transformers } from './transformers';
@ -40,7 +41,7 @@ try {
debug: process.env.SEYFERT_WORKER_DEBUG === 'true', debug: process.env.SEYFERT_WORKER_DEBUG === 'true',
intents: Number(process.env.SEYFERT_WORKER_INTENTS), intents: Number(process.env.SEYFERT_WORKER_INTENTS),
path: process.env.SEYFERT_WORKER_PATH!, path: process.env.SEYFERT_WORKER_PATH!,
shards: process.env.SEYFERT_WORKER_SHARDS!.split(',').map(id => Number(id)), shards: JSON.parse(process.env.SEYFERT_WORKER_SHARDS!),
token: process.env.SEYFERT_WORKER_TOKEN!, token: process.env.SEYFERT_WORKER_TOKEN!,
workerId: Number(process.env.SEYFERT_WORKER_WORKERID), workerId: Number(process.env.SEYFERT_WORKER_WORKERID),
workerProxy: process.env.SEYFERT_WORKER_WORKERPROXY === 'true', workerProxy: process.env.SEYFERT_WORKER_WORKERPROXY === 'true',
@ -48,6 +49,8 @@ try {
mode: process.env.SEYFERT_WORKER_MODE as 'custom' | 'threads' | 'clusters', mode: process.env.SEYFERT_WORKER_MODE as 'custom' | 'threads' | 'clusters',
resharding: process.env.SEYFERT_WORKER_RESHARDING === 'true', resharding: process.env.SEYFERT_WORKER_RESHARDING === 'true',
totalWorkers: Number(process.env.SEYFERT_WORKER_TOTALWORKERS), totalWorkers: Number(process.env.SEYFERT_WORKER_TOTALWORKERS),
info: JSON.parse(process.env.SEYFERT_WORKER_INFO!),
compress: process.env.SEYFERT_WORKER_COMPRESS === 'true',
} satisfies WorkerData; } satisfies WorkerData;
} catch { } catch {
// //
@ -155,6 +158,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
}), }),
}); });
} }
this.cache.intents = workerData.intents;
this.postMessage({ this.postMessage({
type: workerData.resharding ? 'WORKER_START_RESHARDING' : 'WORKER_START', type: workerData.resharding ? 'WORKER_START_RESHARDING' : 'WORKER_START',
workerId: workerData.workerId, workerId: workerData.workerId,
@ -164,7 +168,6 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
} }
await super.start(options); await super.start(options);
await this.loadEvents(options.eventsDir); await this.loadEvents(options.eventsDir);
this.cache.intents = workerData.intents;
} }
async loadEvents(dir?: string) { async loadEvents(dir?: string) {
@ -295,9 +298,6 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
break; break;
case 'SPAWN_SHARDS': case 'SPAWN_SHARDS':
{ {
const onPacket = this.onPacket.bind(this);
const handlePayload = this.options?.handlePayload?.bind(this);
const self = this;
for (const id of workerData.shards) { for (const id of workerData.shards) {
const existsShard = this.shards.has(id); const existsShard = this.shards.has(id);
if (existsShard) { if (existsShard) {
@ -305,28 +305,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
continue; continue;
} }
const shard = new Shard(id, { const shard = this.createShard(id, data);
token: workerData.token,
intents: workerData.intents,
info: data.info,
compress: data.compress,
debugger: this.debugger,
properties: {
...properties,
...this.options.gateway?.properties,
},
async handlePayload(shardId, payload) {
await handlePayload?.(shardId, payload);
await onPacket(payload, shardId);
if (self.options.sendPayloadToParent)
self.postMessage({
workerId: workerData.workerId,
shardId,
type: 'RECEIVE_PAYLOAD',
payload,
} satisfies WorkerReceivePayload);
},
});
this.shards.set(id, shard); this.shards.set(id, shard);
this.postMessage({ this.postMessage({
type: 'CONNECT_QUEUE', type: 'CONNECT_QUEUE',
@ -380,7 +359,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
let result: unknown; let result: unknown;
try { try {
result = await eval(` result = await eval(`
(${data.func})(this) (${data.func})(this, ${data.vars})
`); `);
} catch (e) { } catch (e) {
result = e; result = e;
@ -455,7 +434,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
}); });
} }
tellWorker<R>(workerId: number, func: (_: this) => R) { tellWorker<R, V extends Record<string, unknown>>(workerId: number, func: (_: this, vars: V) => R, vars: V) {
const nonce = this.generateNonce(); const nonce = this.generateNonce();
this.postMessage({ this.postMessage({
type: 'EVAL_TO_WORKER', type: 'EVAL_TO_WORKER',
@ -463,25 +442,71 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
toWorkerId: workerId, toWorkerId: workerId,
workerId: workerData.workerId, workerId: workerData.workerId,
nonce, nonce,
vars: JSON.stringify(vars),
} satisfies WorkerSendToWorkerEval); } satisfies WorkerSendToWorkerEval);
return this.generateSendPromise<R>(nonce); return this.generateSendPromise<R>(nonce);
} }
tellWorkers<R>(func: (_: this) => R) { tellWorkers<R, V extends Record<string, unknown>>(func: (_: this, vars: V) => R, vars: V) {
const promises: Promise<R>[] = []; const promises: Promise<R>[] = [];
for (let i = 0; i < workerData.totalWorkers; i++) { for (let i = 0; i < workerData.totalWorkers; i++) {
promises.push(this.tellWorker(i, func)); promises.push(this.tellWorker(i, func, vars));
} }
return Promise.all(promises); return Promise.all(promises);
} }
createShard(id: number, data: Pick<ManagerSpawnShards, 'info' | 'compress'>) {
const onPacket = this.onPacket.bind(this);
const handlePayload = this.options?.handlePayload?.bind(this);
const self = this;
const shard = new Shard(id, {
token: workerData.token,
intents: workerData.intents,
info: data.info,
compress: data.compress,
debugger: this.debugger,
properties: {
...properties,
...this.options.gateway?.properties,
},
async handlePayload(shardId, payload) {
await handlePayload?.(shardId, payload);
await onPacket(payload, shardId);
if (self.options.sendPayloadToParent)
self.postMessage({
workerId: workerData.workerId,
shardId,
type: 'RECEIVE_PAYLOAD',
payload,
} satisfies WorkerReceivePayload);
},
});
return shard;
}
async resumeShard(shardId: number, shardData: MakeRequired<ShardData>) {
const exists = (await this.tellWorkers((r, vars) => r.shards.has(vars.shardId), { shardId })).some(x => x);
if (exists) throw new Error('Cannot override existing shard');
const shard = this.createShard(shardId, {
info: this.workerData.info,
compress: this.workerData.compress,
});
shard.data = shardData;
this.shards.set(shardId, shard);
return this.postMessage({
workerId: this.workerId,
shardId,
type: 'CONNECT_QUEUE',
});
}
protected async onPacket(packet: GatewayDispatchPayload, shardId: number) { protected async onPacket(packet: GatewayDispatchPayload, shardId: number) {
Promise.allSettled([ Promise.allSettled([
this.events?.runEvent('RAW', this, packet, shardId, false), this.events?.runEvent('RAW', this, packet, shardId, false),
this.collectors.run('RAW', packet, this), this.collectors.run('RAW', packet, this),
]); //ignore promise ]); //ignore promise
switch (packet.t) { switch (packet.t) {
//// Cases where we must obtain the old data before updating
case 'GUILD_MEMBER_UPDATE': case 'GUILD_MEMBER_UPDATE':
{ {
if (!this.memberUpdateHandler.check(packet.d)) { if (!this.memberUpdateHandler.check(packet.d)) {

View File

@ -466,16 +466,18 @@ export class CommandHandler extends BaseHandler {
stablishContextCommandDefaults(commandInstance: InstanceType<HandleableCommand>): ContextMenuCommand | false { stablishContextCommandDefaults(commandInstance: InstanceType<HandleableCommand>): ContextMenuCommand | false {
if (!(commandInstance instanceof ContextMenuCommand)) return false; if (!(commandInstance instanceof ContextMenuCommand)) return false;
commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun; commandInstance.onAfterRun ??= this.client.options.commands?.defaults?.onAfterRun;
//@ts-expect-error magic.
commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail; if (this.client.options.commands?.defaults?.onBotPermissionsFail)
//@ts-expect-error magic. commandInstance.onBotPermissionsFail ??= this.client.options.commands?.defaults?.onBotPermissionsFail;
commandInstance.onInternalError ??= this.client.options.commands?.defaults?.onInternalError;
//@ts-expect-error magic. if (this.client.options.commands?.defaults?.onInternalError)
commandInstance.onMiddlewaresError ??= this.client.options.commands?.defaults?.onMiddlewaresError; commandInstance.onInternalError ??= this.client.options.commands.defaults.onInternalError;
//@ts-expect-error magic.
commandInstance.onPermissionsFail ??= this.client.options.commands?.defaults?.onPermissionsFail; if (this.client.options.commands?.defaults?.onMiddlewaresError)
//@ts-expect-error magic. commandInstance.onMiddlewaresError ??= this.client.options.commands.defaults.onMiddlewaresError;
commandInstance.onRunError ??= this.client.options.commands?.defaults?.onRunError;
if (this.client.options.commands?.defaults?.onRunError)
commandInstance.onRunError ??= this.client.options.commands.defaults.onRunError;
return commandInstance; return commandInstance;
} }

View File

@ -9,6 +9,7 @@ import type {
ModalCreateBodyRequest, ModalCreateBodyRequest,
UnionToTuple, UnionToTuple,
} from '../common'; } from '../common';
import type { Interaction } from '../structures/Interaction';
import { MessageFlags } from '../types'; import { MessageFlags } from '../types';
export interface ModalContext extends BaseContext, ExtendContext {} export interface ModalContext extends BaseContext, ExtendContext {}
@ -101,7 +102,7 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
return this.interaction.deleteResponse(); return this.interaction.deleteResponse();
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest): ReturnType<Interaction['modal']> {
//@ts-expect-error //@ts-expect-error
return this.interaction.modal(body); return this.interaction.modal(body);
} }

View File

@ -224,21 +224,21 @@ export class EventHandler extends BaseHandler {
async runCustom<T extends CustomEventsKeys>(name: T, ...args: ResolveEventRunParams<T>) { async runCustom<T extends CustomEventsKeys>(name: T, ...args: ResolveEventRunParams<T>) {
const Event = this.values[name]; const Event = this.values[name];
if (!Event) { if (!Event) {
// @ts-expect-error working with non-existent types is hard // @ts-expect-error
return this.client.collectors.run(name, args, this.client); return this.client.collectors.run(name, args, this.client);
} }
try { try {
if (Event.data.once && Event.fired) { if (Event.data.once && Event.fired) {
// @ts-expect-error working with non-existent types is hard // @ts-expect-error
return this.client.collectors.run(name, args, this.client); return this.client.collectors.run(name, args, this.client);
} }
Event.fired = true; Event.fired = true;
this.logger.debug(`executed a custom event [${name}]`, Event.data.once ? 'once' : ''); this.logger.debug(`executed a custom event [${name}]`, Event.data.once ? 'once' : '');
await Promise.all([ await Promise.all([
// @ts-expect-error working with non-existent types is hard // @ts-expect-error
Event.run(...args, this.client), Event.run(...args, this.client),
// @ts-expect-error working with non-existent types is hard // @ts-expect-error
this.client.collectors.run(name, args, this.client), this.client.collectors.run(name, args, this.client),
]); ]);
} catch (e) { } catch (e) {

View File

@ -105,8 +105,8 @@ export class Shard {
this.debugger?.debug(`[Shard #${this.id}] Connecting to ${this.currentGatewayURL}`); this.debugger?.debug(`[Shard #${this.id}] Connecting to ${this.currentGatewayURL}`);
// @ts-expect-error @types/bun cause erros in compile // @ts-expect-error Use native websocket when using Bun
// biome-ignore lint/correctness/noUndeclaredVariables: /\ bun lol // biome-ignore lint/correctness/noUndeclaredVariables: /\
this.websocket = new BaseSocket(typeof Bun === 'undefined' ? 'ws' : 'bun', this.currentGatewayURL); this.websocket = new BaseSocket(typeof Bun === 'undefined' ? 'ws' : 'bun', this.currentGatewayURL);
this.websocket.onmessage = ({ data }: { data: string | Buffer }) => { this.websocket.onmessage = ({ data }: { data: string | Buffer }) => {

View File

@ -140,6 +140,8 @@ export interface WorkerData {
workerId: number; workerId: number;
debug: boolean; debug: boolean;
workerProxy: boolean; workerProxy: boolean;
info: APIGatewayBotInfo;
compress: boolean;
__USING_WATCHER__?: boolean; __USING_WATCHER__?: boolean;
resharding: boolean; resharding: boolean;
} }

View File

@ -81,6 +81,7 @@ export type WorkerSendToWorkerEval = CreateWorkerMessage<
{ {
func: string; func: string;
nonce: string; nonce: string;
vars: string;
toWorkerId: number; toWorkerId: number;
} }
>; >;

View File

@ -4,7 +4,7 @@ import type { Worker as WorkerThreadsWorker } from 'node:worker_threads';
import { ApiHandler, Logger, type UsingClient, type WorkerClient } from '../..'; import { ApiHandler, Logger, type UsingClient, type WorkerClient } from '../..';
import { type Adapter, MemoryAdapter } from '../../cache'; import { type Adapter, MemoryAdapter } from '../../cache';
import { BaseClient, type InternalRuntimeConfig } from '../../client/base'; import { BaseClient, type InternalRuntimeConfig } from '../../client/base';
import { type MakePartial, MergeOptions, lazyLoadPackage } from '../../common'; import { BASE_HOST, type MakePartial, MergeOptions, lazyLoadPackage } from '../../common';
import type { GatewayPresenceUpdateData, GatewaySendPayload, RESTGetAPIGatewayBotResult } from '../../types'; import type { GatewayPresenceUpdateData, GatewaySendPayload, RESTGetAPIGatewayBotResult } from '../../types';
import { WorkerManagerDefaults, properties } from '../constants'; import { WorkerManagerDefaults, properties } from '../constants';
import { DynamicBucket } from '../structures'; import { DynamicBucket } from '../structures';
@ -169,6 +169,11 @@ export class WorkerManager extends Map<
mode: this.options.mode, mode: this.options.mode,
resharding, resharding,
totalWorkers: shards.length, totalWorkers: shards.length,
info: {
...this.options.info,
shards: this.totalShards,
},
compress: this.options.compress,
}); });
this.set(i, worker); this.set(i, worker);
}); });
@ -193,7 +198,8 @@ export class WorkerManager extends Map<
}; };
if (workerData.resharding) env.SEYFERT_WORKER_RESHARDING = 'true'; if (workerData.resharding) env.SEYFERT_WORKER_RESHARDING = 'true';
for (const i in workerData) { for (const i in workerData) {
env[`SEYFERT_WORKER_${i.toUpperCase()}`] = workerData[i as keyof WorkerData]; const data = workerData[i as keyof WorkerData];
env[`SEYFERT_WORKER_${i.toUpperCase()}`] = typeof data === 'object' && data ? JSON.stringify(data) : data;
} }
switch (this.options.mode) { switch (this.options.mode) {
case 'threads': { case 'threads': {
@ -425,6 +431,7 @@ export class WorkerManager extends Map<
func: message.func, func: message.func,
type: 'EXECUTE_EVAL_TO_WORKER', type: 'EXECUTE_EVAL_TO_WORKER',
toWorkerId: message.toWorkerId, toWorkerId: message.toWorkerId,
vars: message.vars,
} satisfies ManagerExecuteEvalToWorker); } satisfies ManagerExecuteEvalToWorker);
this.generateSendPromise(nonce, 'Eval timeout').then(val => this.generateSendPromise(nonce, 'Eval timeout').then(val =>
this.postMessage(message.workerId, { this.postMessage(message.workerId, {
@ -503,20 +510,25 @@ export class WorkerManager extends Map<
return this.generateSendPromise<WorkerInfo>(nonce, 'Get worker info timeout'); return this.generateSendPromise<WorkerInfo>(nonce, 'Get worker info timeout');
} }
tellWorker<R>(workerId: number, func: (_: WorkerClient & UsingClient) => R) { tellWorker<R, V extends Record<string, unknown>>(
workerId: number,
func: (_: WorkerClient & UsingClient, vars: V) => R,
vars: V,
) {
const nonce = this.generateNonce(); const nonce = this.generateNonce();
this.postMessage(workerId, { this.postMessage(workerId, {
type: 'EXECUTE_EVAL', type: 'EXECUTE_EVAL',
func: func.toString(), func: func.toString(),
nonce, nonce,
vars: JSON.stringify(vars),
} satisfies ManagerExecuteEval); } satisfies ManagerExecuteEval);
return this.generateSendPromise<R>(nonce); return this.generateSendPromise<R>(nonce);
} }
tellWorkers<R>(func: (_: WorkerClient & UsingClient) => R) { tellWorkers<R, V extends Record<string, unknown>>(func: (_: WorkerClient & UsingClient, vars: V) => R, vars: V) {
const promises: Promise<R>[] = []; const promises: Promise<R>[] = [];
for (const i of this.keys()) { for (const i of this.keys()) {
promises.push(this.tellWorker(i, func)); promises.push(this.tellWorker(i, func, vars));
} }
return Promise.all(promises); return Promise.all(promises);
} }
@ -530,7 +542,7 @@ export class WorkerManager extends Map<
this.rest ??= new ApiHandler({ this.rest ??= new ApiHandler({
token: this.options.token, token: this.options.token,
baseUrl: 'api/v10', baseUrl: 'api/v10',
domain: 'https://discord.com', domain: BASE_HOST,
debug: this.options.debug, debug: this.options.debug,
}); });
this.options.info ??= await this.rest.proxy.gateway.bot.get(); this.options.info ??= await this.rest.proxy.gateway.bot.get();
@ -562,7 +574,7 @@ export class WorkerManager extends Map<
}, },
this.debugger, this.debugger,
); );
await this.prepareWorkers(spaces); this.prepareWorkers(spaces);
// Start workers queue // Start workers queue
this.workerQueue.shift()!(); this.workerQueue.shift()!();
await this.startResharding(); await this.startResharding();
@ -645,6 +657,7 @@ export type ManagerExecuteEvalToWorker = CreateManagerMessage<
{ {
func: string; func: string;
nonce: string; nonce: string;
vars: string;
toWorkerId: number; toWorkerId: number;
} }
>; >;
@ -653,6 +666,7 @@ export type ManagerExecuteEval = CreateManagerMessage<
'EXECUTE_EVAL', 'EXECUTE_EVAL',
{ {
func: string; func: string;
vars: string;
nonce: string; nonce: string;
} }
>; >;