seyfert 3.1.3-dev-15068780757.0 → 3.1.3-dev-15087248840.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,15 @@
1
- import { type DeepPartial, type MakeRequired, type When } from '../common';
1
+ import { type Awaitable, type DeepPartial, type MakeRequired, type When } from '../common';
2
2
  import { EventHandler } from '../events';
3
3
  import type { GatewayDispatchPayload } from '../types';
4
4
  import { Shard, type ShardManagerOptions, type WorkerData } from '../websocket';
5
- import type { WorkerMessages, WorkerShardInfo } from '../websocket/discord/worker';
5
+ import type { ClientHeartbeaterMessages, WorkerMessages, WorkerShardInfo } from '../websocket/discord/worker';
6
6
  import type { ManagerMessages, ManagerSpawnShards } from '../websocket/discord/workermanager';
7
7
  import type { BaseClientOptions, ServicesOptions, StartOptions } from './base';
8
8
  import { BaseClient } from './base';
9
9
  import type { Client, ClientOptions } from './client';
10
10
  import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
11
11
  import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
12
+ import type { WorkerHeartbeaterMessages } from '../websocket/discord/heartbeater';
12
13
  import type { ShardData } from '../websocket/discord/shared';
13
14
  import { Collectors } from './collectors';
14
15
  import { type ClientUserStructure } from './transformers';
@@ -35,8 +36,8 @@ export declare class WorkerClient<Ready extends boolean = boolean> extends BaseC
35
36
  get workerData(): WorkerData;
36
37
  start(options?: Omit<DeepPartial<StartOptions>, 'httpConnection' | 'token' | 'connection'>): Promise<void>;
37
38
  loadEvents(dir?: string): Promise<void>;
38
- postMessage(body: WorkerMessages): unknown;
39
- handleManagerMessages(data: ManagerMessages): Promise<any>;
39
+ postMessage(body: WorkerMessages | ClientHeartbeaterMessages): unknown;
40
+ handleManagerMessages(data: ManagerMessages | WorkerHeartbeaterMessages): Promise<any>;
40
41
  calculateShardId(guildId: string): number;
41
42
  private generateNonce;
42
43
  private generateSendPromise;
@@ -51,8 +52,8 @@ export interface WorkerClientOptions extends BaseClientOptions {
51
52
  commands?: NonNullable<Client['options']>['commands'];
52
53
  handlePayload?: ShardManagerOptions['handlePayload'];
53
54
  gateway?: ClientOptions['gateway'];
54
- postMessage?: (body: unknown) => unknown;
55
+ postMessage?: (body: unknown) => Awaitable<unknown>;
55
56
  /** can have perfomance issues in big bots if the client sends every event, specially in startup (false by default) */
56
57
  sendPayloadToParent?: boolean;
57
- handleManagerMessages?(message: ManagerMessages): any;
58
+ handleManagerMessages?(message: ManagerMessages | WorkerHeartbeaterMessages): Awaitable<unknown>;
58
59
  }
@@ -131,6 +131,12 @@ class WorkerClient extends base_1.BaseClient {
131
131
  }
132
132
  async handleManagerMessages(data) {
133
133
  switch (data.type) {
134
+ case 'HEARTBEAT':
135
+ this.postMessage({
136
+ type: 'ACK_HEARTBEAT',
137
+ workerId: workerData.workerId,
138
+ });
139
+ break;
134
140
  case 'CACHE_RESULT':
135
141
  if (this.cache.adapter instanceof cache_1.WorkerAdapter && this.cache.adapter.promises.has(data.nonce)) {
136
142
  const cacheData = this.cache.adapter.promises.get(data.nonce);
@@ -0,0 +1,17 @@
1
+ import type { Awaitable } from '../../common';
2
+ export type WorkerHeartbeaterMessages = SendHeartbeat;
3
+ export type CreateHeartbeaterMessage<T extends string, D extends object = object> = {
4
+ type: T;
5
+ } & D;
6
+ export type SendHeartbeat = CreateHeartbeaterMessage<'HEARTBEAT'>;
7
+ export declare class Heartbeater {
8
+ sendMethod: (workerId: number, data: WorkerHeartbeaterMessages) => Awaitable<void>;
9
+ interval: number;
10
+ store: Map<number, {
11
+ ack: boolean;
12
+ interval: NodeJS.Timeout;
13
+ }>;
14
+ constructor(sendMethod: (workerId: number, data: WorkerHeartbeaterMessages) => Awaitable<void>, interval: number);
15
+ register(workerId: number, recreate: (workerId: number) => Awaitable<void>): void;
16
+ acknowledge(workerId: number): void;
17
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Heartbeater = void 0;
4
+ class Heartbeater {
5
+ sendMethod;
6
+ interval;
7
+ store = new Map();
8
+ constructor(sendMethod, interval) {
9
+ this.sendMethod = sendMethod;
10
+ this.interval = interval;
11
+ }
12
+ register(workerId, recreate) {
13
+ if (this.interval <= 0)
14
+ return;
15
+ this.store.set(workerId, {
16
+ ack: true,
17
+ interval: setInterval(() => {
18
+ const heartbeat = this.store.get(workerId);
19
+ if (!heartbeat.ack) {
20
+ heartbeat.ack = true;
21
+ return recreate(workerId);
22
+ }
23
+ heartbeat.ack = false;
24
+ this.sendMethod(workerId, { type: 'HEARTBEAT' });
25
+ }, this.interval),
26
+ });
27
+ }
28
+ acknowledge(workerId) {
29
+ const heartbeat = this.store.get(workerId);
30
+ if (!heartbeat)
31
+ return;
32
+ heartbeat.ack = true;
33
+ }
34
+ }
35
+ exports.Heartbeater = Heartbeater;
@@ -55,6 +55,8 @@ export interface WorkerManagerOptions extends Omit<ShardManagerOptions, 'handleP
55
55
  */
56
56
  shardsPerWorker?: number;
57
57
  workerProxy?: boolean;
58
+ /** @default 15000 */
59
+ heartbeaterInterval?: number;
58
60
  path: string;
59
61
  handlePayload?(shardId: number, workerId: number, packet: GatewayDispatchPayload): any;
60
62
  handleWorkerMessage?(message: WorkerMessages): any;
@@ -68,7 +68,9 @@ export type CustomWorkerClientMessages = {
68
68
  workerId: number;
69
69
  } & Identify<CustomWorkerClientEvents[K] extends never ? {} : CustomWorkerClientEvents[K]>>;
70
70
  };
71
- export type WorkerMessages = {
71
+ export type ClientHeartbeaterMessages = ACKHeartbeat;
72
+ export type ACKHeartbeat = CreateWorkerMessage<'ACK_HEARTBEAT'>;
73
+ export type WorkerMessages = ClientHeartbeaterMessages | {
72
74
  [K in BaseWorkerMessage['type']]: Identify<Extract<BaseWorkerMessage, {
73
75
  type: K;
74
76
  }>>;
@@ -5,6 +5,7 @@ import { type Adapter } from '../../cache';
5
5
  import { type Identify, type PickPartial } from '../../common';
6
6
  import type { GatewayPresenceUpdateData, GatewaySendPayload } from '../../types';
7
7
  import { ConnectQueue } from '../structures/timeout';
8
+ import { Heartbeater, type WorkerHeartbeaterMessages } from './heartbeater';
8
9
  import type { ShardOptions, WorkerData, WorkerManagerOptions } from './shared';
9
10
  import type { WorkerInfo, WorkerMessages, WorkerShardInfo } from './worker';
10
11
  export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerThreadsWorker | {
@@ -31,6 +32,7 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerTh
31
32
  rest: ApiHandler;
32
33
  reshardingWorkerQueue: (() => void)[];
33
34
  private _info?;
35
+ heartbeater: Heartbeater;
34
36
  constructor(options: Omit<PickPartial<WorkerManagerOptions, 'token' | 'intents' | 'info' | 'handlePayload' | 'handleWorkerMessage'>, 'resharding'> & {
35
37
  resharding?: PickPartial<NonNullable<WorkerManagerOptions['resharding']>, 'getInfo'>;
36
38
  });
@@ -52,8 +54,8 @@ export declare class WorkerManager extends Map<number, (ClusterWorker | WorkerTh
52
54
  }): Promise<number>;
53
55
  calculateShardId(guildId: string): number;
54
56
  calculateWorkerId(shardId: number): number;
55
- postMessage(id: number, body: ManagerMessages): void;
56
- prepareWorkers(shards: number[][], resharding?: boolean): void;
57
+ postMessage(id: number, body: ManagerMessages | WorkerHeartbeaterMessages): void;
58
+ prepareWorkers(shards: number[][], rawResharding?: boolean): void;
57
59
  createWorker(workerData: WorkerData): ClusterWorker | WorkerThreadsWorker | ({
58
60
  ready?: boolean;
59
61
  } & {
@@ -13,6 +13,7 @@ const common_1 = require("../../common");
13
13
  const constants_1 = require("../constants");
14
14
  const structures_1 = require("../structures");
15
15
  const timeout_1 = require("../structures/timeout");
16
+ const heartbeater_1 = require("./heartbeater");
16
17
  class WorkerManager extends Map {
17
18
  static prepareSpaces(options, logger) {
18
19
  logger?.info('Preparing buckets');
@@ -35,6 +36,7 @@ class WorkerManager extends Map {
35
36
  rest;
36
37
  reshardingWorkerQueue = [];
37
38
  _info;
39
+ heartbeater;
38
40
  constructor(options) {
39
41
  super();
40
42
  this.options = options;
@@ -46,6 +48,7 @@ class WorkerManager extends Map {
46
48
  return oldFn(message);
47
49
  };
48
50
  }
51
+ this.heartbeater = new heartbeater_1.Heartbeater(this.postMessage.bind(this), options.heartbeaterInterval ?? 15e3);
49
52
  }
50
53
  setCache(adapter) {
51
54
  this.cacheAdapter = adapter;
@@ -101,7 +104,8 @@ class WorkerManager extends Map {
101
104
  return this.debugger?.error(`Worker ${id} does not exists.`);
102
105
  switch (this.options.mode) {
103
106
  case 'clusters':
104
- worker.send(body);
107
+ if (worker.isConnected())
108
+ worker.send(body);
105
109
  break;
106
110
  case 'threads':
107
111
  worker.postMessage(body);
@@ -111,33 +115,40 @@ class WorkerManager extends Map {
111
115
  break;
112
116
  }
113
117
  }
114
- prepareWorkers(shards, resharding = false) {
118
+ prepareWorkers(shards, rawResharding = false) {
115
119
  const worker_threads = (0, common_1.lazyLoadPackage)('node:worker_threads');
116
120
  if (!worker_threads)
117
121
  throw new Error('Cannot prepare workers without worker_threads.');
118
122
  for (let i = 0; i < shards.length; i++) {
123
+ const registerWorker = (resharding) => {
124
+ const worker = this.createWorker({
125
+ path: this.options.path,
126
+ debug: this.options.debug,
127
+ token: this.options.token,
128
+ shards: shards[i],
129
+ intents: this.options.intents,
130
+ workerId: i,
131
+ workerProxy: this.options.workerProxy,
132
+ totalShards: resharding ? this._info.shards : this.totalShards,
133
+ mode: this.options.mode,
134
+ resharding,
135
+ totalWorkers: shards.length,
136
+ info: {
137
+ ...this.options.info,
138
+ shards: this.totalShards,
139
+ },
140
+ compress: this.options.compress,
141
+ });
142
+ this.set(i, worker);
143
+ };
119
144
  const workerExists = this.has(i);
120
- if (resharding || !workerExists) {
121
- this[resharding ? 'reshardingWorkerQueue' : 'workerQueue'].push(() => {
122
- const worker = this.createWorker({
123
- path: this.options.path,
124
- debug: this.options.debug,
125
- token: this.options.token,
126
- shards: shards[i],
127
- intents: this.options.intents,
128
- workerId: i,
129
- workerProxy: this.options.workerProxy,
130
- totalShards: resharding ? this._info.shards : this.totalShards,
131
- mode: this.options.mode,
132
- resharding,
133
- totalWorkers: shards.length,
134
- info: {
135
- ...this.options.info,
136
- shards: this.totalShards,
137
- },
138
- compress: this.options.compress,
145
+ if (rawResharding || !workerExists) {
146
+ this[rawResharding ? 'reshardingWorkerQueue' : 'workerQueue'].push(() => {
147
+ registerWorker(rawResharding);
148
+ this.heartbeater.register(i, () => {
149
+ this.delete(i);
150
+ registerWorker(false);
139
151
  });
140
- this.set(i, worker);
141
152
  });
142
153
  }
143
154
  }
@@ -170,6 +181,9 @@ class WorkerManager extends Map {
170
181
  env,
171
182
  });
172
183
  worker.on('message', data => this.handleWorkerMessage(data));
184
+ worker.on('error', err => {
185
+ this.debugger?.error(`[Worker #${workerData.workerId}]`, err);
186
+ });
173
187
  return worker;
174
188
  }
175
189
  case 'clusters': {
@@ -204,6 +218,9 @@ class WorkerManager extends Map {
204
218
  }
205
219
  async handleWorkerMessage(message) {
206
220
  switch (message.type) {
221
+ case 'ACK_HEARTBEAT':
222
+ this.heartbeater.acknowledge(message.workerId);
223
+ break;
207
224
  case 'WORKER_READY_RESHARDING':
208
225
  {
209
226
  this.get(message.workerId).resharded = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seyfert",
3
- "version": "3.1.3-dev-15068780757.0",
3
+ "version": "3.1.3-dev-15087248840.0",
4
4
  "description": "The most advanced framework for discord bots",
5
5
  "main": "./lib/index.js",
6
6
  "module": "./lib/index.js",