@matterbridge/thread 3.10.13-dev-20261005-49e0dbd → 3.10.13-dev-20261008-55d98fb

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.
@@ -11,11 +11,13 @@ export declare class ThreadsManager {
11
11
  private readonly boundMsgHandler;
12
12
  private interval;
13
13
  private intervalMs;
14
+ private readonly coreDirectory;
14
15
  private threads;
15
16
  private terminateWorkers;
16
- constructor(intervalMs?: number);
17
+ constructor(coreDirectory: string, intervalMs?: number);
17
18
  destroy(): void;
18
19
  private msgHandler;
20
+ private sendShutdown;
19
21
  private terminateExitedWorkers;
20
22
  private intervalHandler;
21
23
  runThread(name: string, workerData?: WorkerData, argv?: string[], env?: NodeJS.ProcessEnv, execArgv?: string[], pipedOutput?: boolean): Worker;
@@ -2,7 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath, pathToFileURL } from 'node:url';
4
4
  import { Worker } from 'node:worker_threads';
5
- import { hasParameter } from '@matterbridge/utils/cli';
5
+ import { hasAnyParameter, hasParameter } from '@matterbridge/utils/cli';
6
6
  import { getErrorMessage } from '@matterbridge/utils/error';
7
7
  import { logModuleLoaded } from '@matterbridge/utils/loader';
8
8
  import { fireAndForget } from '@matterbridge/utils/wait';
@@ -19,6 +19,7 @@ export class ThreadsManager {
19
19
  boundMsgHandler = this.msgHandler.bind(this);
20
20
  interval;
21
21
  intervalMs;
22
+ coreDirectory;
22
23
  threads = [
23
24
  { name: 'CheckUpdates', path: 'workerCheckUpdates.js', type: 'worker' },
24
25
  { name: 'SystemCheck', path: 'workerSystemCheck.js', type: 'worker' },
@@ -26,12 +27,15 @@ export class ThreadsManager {
26
27
  { name: 'SpawnCommand', path: 'workerSpawnCommand.js', type: 'worker' },
27
28
  { name: 'ArchiveCommand', path: 'workerArchiveCommand.js', type: 'worker' },
28
29
  { name: 'DockerVersion', path: 'workerDockerVersion.js', type: 'worker' },
30
+ { name: 'Backend', path: 'threadBackend.js', type: 'thread' },
31
+ { name: 'RootNode', path: 'threadRootNode.js', type: 'thread', multiple: true },
32
+ { name: 'Tracker', path: 'threadTracker.js', type: 'thread', multiple: true, startup: true, shutdown: true },
29
33
  ];
30
34
  terminateWorkers = new Set();
31
- constructor(intervalMs = 60_000) {
32
- this.debug = hasParameter('debug') || hasParameter('verbose') || hasParameter('debug-threads') || hasParameter('verbose-threads');
33
- this.verbose = hasParameter('verbose') || hasParameter('verbose-threads');
34
- this.tracker = hasParameter('tracker') || hasParameter('tracker-threads');
35
+ constructor(coreDirectory, intervalMs = 60_000) {
36
+ this.debug = hasAnyParameter('debug', 'verbose', 'debug-threads', 'verbose-threads');
37
+ this.verbose = hasAnyParameter('verbose', 'verbose-threads');
38
+ this.tracker = hasAnyParameter('tracker', 'tracker-threads');
35
39
  this.log = new AnsiLogger({
36
40
  logName: 'ThreadsManager',
37
41
  logNameColor: MAGENTA,
@@ -43,6 +47,7 @@ export class ThreadsManager {
43
47
  this.server = new BroadcastServer('manager', this.log);
44
48
  this.server.on('broadcast_message', this.boundMsgHandler);
45
49
  this.intervalMs = intervalMs;
50
+ this.coreDirectory = coreDirectory;
46
51
  this.interval = setInterval(this.intervalHandler.bind(this), this.intervalMs);
47
52
  if (this.verbose)
48
53
  this.log.notice(`ThreadsManager initialized. Listening for broadcast messages...`);
@@ -50,6 +55,7 @@ export class ThreadsManager {
50
55
  destroy() {
51
56
  clearInterval(this.interval);
52
57
  this.terminateExitedWorkers();
58
+ this.sendShutdown(this.threads.filter((t) => t.shutdown));
53
59
  this.server.off('broadcast_message', this.boundMsgHandler);
54
60
  this.server.close();
55
61
  if (this.verbose)
@@ -80,12 +86,31 @@ export class ThreadsManager {
80
86
  this.server.respond({ ...msg, result: { success: false } });
81
87
  }
82
88
  break;
89
+ case 'manager_shutdown': {
90
+ const threads = msg.params.thread === undefined ? this.threads : this.threads.filter((t) => t.name === msg.params.thread);
91
+ if (threads.length === 0)
92
+ this.log.warn(`Failed to shut down thread ${CYAN}${msg.params.thread}${wr}: thread not found`);
93
+ this.sendShutdown(threads);
94
+ this.server.respond({ ...msg, result: { success: threads.length > 0 } });
95
+ break;
96
+ }
83
97
  default:
84
98
  if (this.verbose)
85
99
  this.log.debug(`Unknown broadcast request ${CYAN}${msg.type}${db} from ${CYAN}${msg.src}${db}`);
86
100
  }
87
101
  }
88
102
  }
103
+ sendShutdown(threads) {
104
+ for (const thread of threads) {
105
+ for (const info of [thread, ...(thread.instances ?? [])]) {
106
+ if (!info.worker)
107
+ continue;
108
+ const shutdownMessage = { type: 'shutdown', threadName: thread.name, threadId: info.worker.threadId };
109
+ info.worker.postMessage(shutdownMessage);
110
+ this.log.debug(`Thread ${thread.name} sent shutdown message to thread id ${info.worker.threadId}`);
111
+ }
112
+ }
113
+ }
89
114
  terminateExitedWorkers() {
90
115
  if (this.terminateWorkers.size > 0) {
91
116
  this.log.debug(`Terminating ${this.terminateWorkers.size} workers that have exited...`);
@@ -103,16 +128,26 @@ export class ThreadsManager {
103
128
  }
104
129
  intervalHandler() {
105
130
  for (const thread of this.threads) {
106
- this.log.debug(`Thread ${thread.name} running: ${thread.worker ? 'yes' : 'no'}, lastSeen: ${thread.lastSeen ? new Date(thread.lastSeen).toISOString() : 'never'}, runs: ${thread.runCount ?? 0}, errors: ${thread.errorCount ?? 0}`);
131
+ if (!thread.multiple) {
132
+ this.log.debug(`Thread ${thread.name} running: ${thread.worker ? 'yes' : 'no'}, threadId: ${thread.worker?.threadId ?? 'none'}, lastSeen: ${thread.lastSeen ? new Date(thread.lastSeen).toISOString() : 'never'}, runs: ${thread.runCount ?? 0}, errors: ${thread.errorCount ?? 0}`);
133
+ continue;
134
+ }
135
+ const instances = thread.instances ?? [];
136
+ this.log.debug(`Thread ${thread.name} instances: ${instances.length}, running: ${instances.filter((instance) => instance.worker).length}, runs: ${thread.runCount ?? 0}, errors: ${thread.errorCount ?? 0}`);
137
+ instances.forEach((instance, index) => {
138
+ this.log.debug(`- instance ${index} running: ${instance.worker ? 'yes' : 'no'}, threadId: ${instance.worker?.threadId ?? 'none'}, lastSeen: ${instance.lastSeen ? new Date(instance.lastSeen).toISOString() : 'never'}, lastStarted: ${instance.lastStarted ? new Date(instance.lastStarted).toISOString() : 'never'}, lastStopped: ${instance.lastStopped ? new Date(instance.lastStopped).toISOString() : 'never'}, lastDuration: ${instance.lastDuration ?? 'none'} ms`);
139
+ });
107
140
  }
108
141
  this.terminateExitedWorkers();
109
142
  for (const thread of this.threads) {
110
- if (thread.worker && Date.now() - (thread.lastSeen ?? 0) > this.intervalMs) {
111
- const msg = { type: 'ping', threadId: thread.worker.threadId, threadName: thread.name };
112
- thread.worker.postMessage(msg);
113
- }
114
- if (thread.worker && Date.now() - (thread.lastSeen ?? 0) > this.intervalMs * 2) {
115
- this.log.warn(`Thread ${CYAN}${thread.name}${db} has not been seen for more than ${this.intervalMs * 2} ms. It may be unresponsive.`);
143
+ for (const info of [thread, ...(thread.instances ?? [])]) {
144
+ if (info.worker && Date.now() - (info.lastSeen ?? 0) > this.intervalMs) {
145
+ const msg = { type: 'ping', threadId: info.worker.threadId, threadName: thread.name };
146
+ info.worker.postMessage(msg);
147
+ }
148
+ if (info.worker && Date.now() - (info.lastSeen ?? 0) > this.intervalMs * 2) {
149
+ this.log.warn(`Thread ${CYAN}${thread.name}${db} has not been seen for more than ${this.intervalMs * 2} ms. It may be unresponsive.`);
150
+ }
116
151
  }
117
152
  }
118
153
  }
@@ -121,76 +156,100 @@ export class ThreadsManager {
121
156
  if (!threadInfo) {
122
157
  throw new Error(`Thread ${name} not found`);
123
158
  }
124
- if (threadInfo.worker) {
159
+ if (threadInfo.worker && !threadInfo.multiple) {
125
160
  throw new Error(`Thread ${name} is already running with thread ID ${threadInfo.worker.threadId}`);
126
161
  }
127
162
  const path = this.resolvePath(threadInfo.path);
128
163
  if (!fs.existsSync(path)) {
129
164
  throw new Error(`Thread ${name} file not found at path ${path}`);
130
165
  }
131
- this.log.debug(`Starting thread ${threadInfo.name} from path ${path} type ${threadInfo.type}...`);
166
+ this.log.debug(`Starting thread ${threadInfo.name} from path ${path} type ${threadInfo.type}${threadInfo.multiple ? ` instances: ${threadInfo.instances?.length}` : ''}...`);
132
167
  threadInfo.lastStarted = undefined;
133
168
  threadInfo.lastStopped = undefined;
134
169
  threadInfo.lastDuration = undefined;
135
- threadInfo.worker = this.createESMWorker(threadInfo.name, path, { ...workerData, debug: this.debug, verbose: this.verbose, logLevel: this.log.logLevel }, argv, env, execArgv, pipedOutput);
136
- const worker = threadInfo.worker;
170
+ const worker = this.createESMWorker(threadInfo.name, path, { ...workerData, type: threadInfo.type, debug: this.debug, verbose: this.verbose, tracker: this.tracker, logLevel: this.log.logLevel }, argv, env, execArgv, pipedOutput);
171
+ if (threadInfo.multiple) {
172
+ threadInfo.instances ??= [];
173
+ threadInfo.instances.push({ worker });
174
+ }
175
+ else
176
+ threadInfo.worker = worker;
137
177
  worker.once('online', () => {
138
178
  const now = Date.now();
179
+ const info = [threadInfo, ...(threadInfo.instances ?? [])].find((info) => info.worker === worker);
180
+ if (info) {
181
+ info.lastSeen = now;
182
+ info.lastStarted = now;
183
+ }
139
184
  this.log.debug(`Thread ${threadInfo.name} is online at ${new Date(now).toISOString()}`);
140
185
  });
141
186
  worker.once('exit', () => {
142
187
  const now = Date.now();
143
- if (threadInfo.worker === worker) {
144
- threadInfo.lastSeen = now;
145
- threadInfo.lastStopped = now;
146
- threadInfo.lastDuration = Math.max(0, now - (threadInfo.lastStarted ?? now));
147
- threadInfo.worker = undefined;
188
+ const info = [threadInfo, ...(threadInfo.instances ?? [])].find((info) => info.worker === worker);
189
+ if (info) {
190
+ info.lastSeen = now;
191
+ info.lastStopped = now;
192
+ info.lastDuration = Math.max(0, now - (info.lastStarted ?? now));
193
+ info.worker = undefined;
148
194
  }
149
- this.log.debug(`Thread ${threadInfo.name} has exited at ${new Date(now).toISOString()}`);
195
+ this.log.debug(`Thread ${threadInfo.name} has exited at ${new Date(now).toISOString()} after ${info?.lastDuration ?? 0} ms`);
150
196
  this.terminateWorkers.delete(worker);
151
197
  });
152
198
  worker.on('message', (message) => {
153
199
  const now = Date.now();
154
- threadInfo.lastSeen = now;
200
+ const info = [threadInfo, ...(threadInfo.instances ?? [])].find((info) => info.worker === worker);
201
+ if (info) {
202
+ info.lastSeen = now;
203
+ }
155
204
  if (this.verbose)
156
205
  this.log.debug(`Thread ${threadInfo.name} sent a message at ${new Date(now).toISOString()}: ${debugStringify(message)}`);
157
- if (message.type === 'log') {
158
- AnsiLogger.create({ logName: threadInfo.name, logNameColor: MAGENTA, logTimestampFormat: 4, logLevel: this.log.logLevel }).log(message.logLevel, message.message);
159
- }
160
- else if (message.type === 'init') {
161
- threadInfo.lastStarted = now;
206
+ if (message.type === 'init') {
162
207
  threadInfo.runCount = (threadInfo.runCount ?? 0) + 1;
163
208
  this.log.debug(`Thread ${threadInfo.name} is online started at ${new Date(now).toISOString()} with thread id ${worker.threadId}`);
209
+ if (threadInfo.startup) {
210
+ const startupMessage = { type: 'startup', threadName: threadInfo.name, threadId: worker.threadId };
211
+ worker.postMessage(startupMessage);
212
+ this.log.debug(`Thread ${threadInfo.name} sent startup message to thread id ${worker.threadId}`);
213
+ }
214
+ }
215
+ else if (message.type === 'pong') {
216
+ this.log.debug(`Thread ${threadInfo.name} received a pong at ${new Date(now).toISOString()}`);
217
+ }
218
+ else if (message.type === 'log') {
219
+ AnsiLogger.create({ logName: threadInfo.name, logNameColor: MAGENTA, logTimestampFormat: 4, logLevel: this.log.logLevel }).log(message.logLevel, message.message);
164
220
  }
165
221
  else if (message.type === 'exit') {
166
- threadInfo.lastStopped = now;
167
- threadInfo.lastDuration = Math.max(0, now - (threadInfo.lastStarted ?? now));
168
222
  if (!message.success) {
169
223
  threadInfo.errorCount = (threadInfo.errorCount ?? 0) + 1;
170
224
  }
171
225
  this.terminateWorkers.add(worker);
172
- threadInfo.worker = undefined;
173
- this.log.debug(`Thread ${threadInfo.name} has exited at ${new Date(now).toISOString()} with thread id ${worker.threadId} after running for ${threadInfo.lastDuration} ms`);
226
+ this.log.debug(`Thread ${threadInfo.name} has exited at ${new Date(now).toISOString()} with thread id ${worker.threadId}`);
174
227
  }
175
228
  });
176
229
  worker.on('messageerror', () => {
177
230
  const now = Date.now();
178
- threadInfo.lastSeen = now;
179
- threadInfo.errorCount = (threadInfo.errorCount ?? 0) + 1;
231
+ const info = [threadInfo, ...(threadInfo.instances ?? [])].find((info) => info.worker === worker);
232
+ if (info) {
233
+ info.lastSeen = now;
234
+ threadInfo.errorCount = (threadInfo.errorCount ?? 0) + 1;
235
+ }
180
236
  this.log.error(`Thread ${threadInfo.name} encountered a message error at ${new Date(now).toISOString()}`);
181
237
  });
182
238
  worker.once('error', (error) => {
183
239
  const now = Date.now();
184
- threadInfo.lastSeen = now;
185
- threadInfo.lastStopped = now;
186
- threadInfo.lastDuration = Math.max(0, now - (threadInfo.lastStarted ?? now));
187
- threadInfo.errorCount = (threadInfo.errorCount ?? 0) + 1;
188
- threadInfo.worker = undefined;
240
+ const info = [threadInfo, ...(threadInfo.instances ?? [])].find((info) => info.worker === worker);
241
+ if (info) {
242
+ info.lastSeen = now;
243
+ info.lastStopped = now;
244
+ info.lastDuration = Math.max(0, now - (info.lastStarted ?? now));
245
+ threadInfo.errorCount = (threadInfo.errorCount ?? 0) + 1;
246
+ info.worker = undefined;
247
+ }
189
248
  this.terminateWorkers.add(worker);
190
- this.log.error(`Thread ${threadInfo.name} encountered an error at ${new Date(now).toISOString()} after running for ${threadInfo.lastDuration} ms: ${getErrorMessage(error)}`);
249
+ this.log.error(`Thread ${threadInfo.name} encountered an error at ${new Date(now).toISOString()} after running for ${info?.lastDuration} ms: ${getErrorMessage(error)}`);
191
250
  });
192
251
  this.log.debug(`Started thread ${threadInfo.name} from path ${path} type ${threadInfo.type} with thread id ${worker.threadId}`);
193
- return threadInfo.worker;
252
+ return worker;
194
253
  }
195
254
  async runInMainThread(name, workerData = null) {
196
255
  const threadInfo = this.threads.find((t) => t.name === name);
@@ -199,22 +258,30 @@ export class ThreadsManager {
199
258
  }
200
259
  this.log.debug(`Running thread ${threadInfo.name} in the main thread...`);
201
260
  let success = false;
202
- const workerWrapper = (await import(this.resolvePath(threadInfo.path))).default;
203
- if (workerWrapper && typeof workerWrapper === 'object' && workerWrapper.name === name && workerWrapper.callback && typeof workerWrapper.callback === 'function') {
204
- workerWrapper.workerData = workerData;
205
- success = await workerWrapper.callback(workerWrapper);
206
- workerWrapper.destroy(success);
261
+ const threadsWrapper = (await import(this.resolvePath(threadInfo.path))).default;
262
+ if (threadsWrapper && typeof threadsWrapper === 'object' && threadsWrapper.name === name && threadsWrapper.entrypoint && typeof threadsWrapper.entrypoint === 'function') {
263
+ threadsWrapper.workerData = workerData ? { ...workerData, type: threadInfo.type } : null;
264
+ try {
265
+ success = await threadsWrapper.entrypoint(threadsWrapper);
266
+ }
267
+ finally {
268
+ if (!success || threadInfo.type !== 'thread')
269
+ threadsWrapper.destroy(success);
270
+ }
207
271
  }
208
272
  this.log.debug(`Finished running thread ${threadInfo.name} in the main thread.`);
209
273
  return success;
210
274
  }
211
275
  resolvePath(fileName) {
212
- const currentModuleDirectory = path.dirname(fileURLToPath(import.meta.url));
276
+ const threadDirectory = path.dirname(fileURLToPath(import.meta.url));
213
277
  const candidates = [
214
- path.join(currentModuleDirectory, fileName),
215
- path.join(currentModuleDirectory, '..', 'dist', fileName),
216
- path.join(currentModuleDirectory, '..', 'src', fileName.replace(/\.js$/, '.ts')),
217
- path.join(currentModuleDirectory, 'workers', fileName),
278
+ path.join(threadDirectory, fileName),
279
+ path.join(threadDirectory, '..', 'dist', fileName),
280
+ path.join(threadDirectory, '..', 'src', fileName.replace(/\.js$/, '.ts')),
281
+ path.join(threadDirectory, 'workers', fileName),
282
+ path.join(this.coreDirectory, 'runners', fileName),
283
+ path.join(this.coreDirectory, '..', 'dist', 'runners', fileName),
284
+ path.join(this.coreDirectory, '..', 'src', 'runners', fileName.replace(/\.js$/, '.ts')),
218
285
  ];
219
286
  for (const candidate of candidates) {
220
287
  if (fs.existsSync(candidate))
@@ -226,7 +293,7 @@ export class ThreadsManager {
226
293
  createESMWorker(name, relativePath, workerData, argv, env, execArgv, pipedOutput = false) {
227
294
  const fileURL = pathToFileURL(path.resolve(relativePath));
228
295
  const options = {
229
- workerData: { ...workerData, threadName: name, debug: this.debug, verbose: this.verbose, logLevel: this.log.logLevel, tracker: this.tracker },
296
+ workerData: { ...workerData, threadName: name, debug: this.debug, verbose: this.verbose, tracker: this.tracker, logLevel: this.log.logLevel },
230
297
  name,
231
298
  argv: argv ?? process.argv.slice(2),
232
299
  env: env ?? process.env,
@@ -1,10 +1,15 @@
1
+ import { EventEmitter } from 'node:events';
1
2
  import type { ParentPortMessage, ThreadNames, WorkerData } from '@matterbridge/types';
2
3
  import type { Tracker } from '@matterbridge/utils/tracker';
3
4
  import { AnsiLogger, LogLevel } from 'node-ansi-logger';
4
5
  import { BroadcastServer } from './broadcastServer.js';
5
- export declare class WorkerWrapper {
6
+ export interface ThreadsWrapperEvents {
7
+ startup: [];
8
+ shutdown: [];
9
+ }
10
+ export declare class ThreadsWrapper extends EventEmitter<ThreadsWrapperEvents> {
6
11
  name: ThreadNames;
7
- callback: (worker: WorkerWrapper) => Promise<boolean>;
12
+ entrypoint: (worker: ThreadsWrapper) => Promise<boolean>;
8
13
  debug: boolean;
9
14
  verbose: boolean;
10
15
  useTracker: boolean;
@@ -15,7 +20,7 @@ export declare class WorkerWrapper {
15
20
  private destroyed;
16
21
  private readonly boundUnhandledRejectionHandler;
17
22
  private readonly boundUncaughtExceptionHandler;
18
- constructor(name: ThreadNames, callback: (worker: WorkerWrapper) => Promise<boolean>);
23
+ constructor(name: ThreadNames, entrypoint: (worker: ThreadsWrapper) => Promise<boolean>);
19
24
  destroy(success: boolean): void;
20
25
  private handleUnhandledRejection;
21
26
  private handleUncaughtException;
@@ -1,18 +1,19 @@
1
+ import { EventEmitter } from 'node:events';
1
2
  import { isMainThread, parentPort, threadId, workerData } from 'node:worker_threads';
2
3
  import { getErrorMessage, inspectError } from '@matterbridge/utils';
3
- import { hasParameter } from '@matterbridge/utils/cli';
4
+ import { hasAnyParameter } from '@matterbridge/utils/cli';
4
5
  import { formatBytes } from '@matterbridge/utils/format';
5
6
  import { logModuleLoaded } from '@matterbridge/utils/loader';
6
7
  import { AnsiLogger, debugStringify, MAGENTA } from 'node-ansi-logger';
7
8
  import { BroadcastServer } from './broadcastServer.js';
8
9
  import { ThreadsManager } from './threadsManager.js';
9
- logModuleLoaded('WorkerWrapper');
10
- export class WorkerWrapper {
10
+ logModuleLoaded('ThreadsWrapper');
11
+ export class ThreadsWrapper extends EventEmitter {
11
12
  name;
12
- callback;
13
- debug = hasParameter('debug') || hasParameter('verbose') || hasParameter('debug-threads') || hasParameter('verbose-threads');
14
- verbose = hasParameter('verbose') || hasParameter('verbose-threads');
15
- useTracker = hasParameter('tracker') || hasParameter('tracker-threads');
13
+ entrypoint;
14
+ debug = hasAnyParameter('debug', 'verbose', 'debug-threads', 'verbose-threads');
15
+ verbose = hasAnyParameter('verbose', 'verbose-threads');
16
+ useTracker = hasAnyParameter('tracker', 'tracker-threads');
16
17
  log;
17
18
  server;
18
19
  workerData = workerData;
@@ -24,33 +25,33 @@ export class WorkerWrapper {
24
25
  boundUncaughtExceptionHandler = (error) => {
25
26
  this.handleUncaughtException(error);
26
27
  };
27
- constructor(name, callback) {
28
+ constructor(name, entrypoint) {
29
+ super();
28
30
  this.name = name;
29
- this.callback = callback;
31
+ this.entrypoint = entrypoint;
30
32
  if (this.workerData) {
31
33
  this.debug = this.workerData.debug ?? this.debug;
32
34
  this.verbose = this.workerData.verbose ?? this.verbose;
33
35
  this.useTracker = this.workerData.tracker ?? this.useTracker;
34
36
  }
37
+ this.log = new AnsiLogger({
38
+ logName: this.name,
39
+ logNameColor: MAGENTA,
40
+ logTimestampFormat: 4,
41
+ logLevel: this.debug ? "debug" : "info",
42
+ });
35
43
  if (this.useTracker) {
36
44
  void import('@matterbridge/utils/tracker')
37
45
  .then(({ Tracker }) => {
38
- this.tracker = new Tracker(`Thread${this.name}`, this.debug, this.verbose);
46
+ this.tracker = new Tracker(`Thread${this.name}`, this.debug || this.useTracker, this.verbose, this.useTracker);
39
47
  this.tracker.start();
40
48
  return;
41
49
  })
42
50
  .catch((err) => {
43
- if (this.debug)
44
- console.error(`WorkerWrapper ${this.name}: failed to load Tracker ${getErrorMessage(err)}`);
51
+ this.safeParentLog("error", `ThreadsWrapper ${this.name}: failed to load Tracker ${getErrorMessage(err)}`);
45
52
  return;
46
53
  });
47
54
  }
48
- this.log = new AnsiLogger({
49
- logName: this.name,
50
- logNameColor: MAGENTA,
51
- logTimestampFormat: 4,
52
- logLevel: this.debug ? "debug" : "info",
53
- });
54
55
  this.server = new BroadcastServer('matterbridge', this.log);
55
56
  if (!isMainThread) {
56
57
  process.on('unhandledRejection', this.boundUnhandledRejectionHandler);
@@ -66,8 +67,11 @@ export class WorkerWrapper {
66
67
  this.parentPost({ type: 'pong', threadId, threadName: this.name });
67
68
  this.parentLog(this.name, "debug", `Worker ${this.name}:${threadId} sent pong message type to parent: ${debugStringify(message)}`);
68
69
  break;
69
- case 'pong':
70
- this.parentLog(this.name, "debug", `Worker ${this.name}:${threadId} received pong message type from parent: ${debugStringify(message)}`);
70
+ case 'startup':
71
+ setImmediate(() => this.emit('startup'));
72
+ break;
73
+ case 'shutdown':
74
+ setImmediate(() => this.emit('shutdown'));
71
75
  break;
72
76
  default:
73
77
  this.parentLog(this.name, "warn", `Worker ${this.name}:${threadId} received unknown message type from parent: ${debugStringify(message)}`);
@@ -90,13 +94,14 @@ export class WorkerWrapper {
90
94
  void (async () => {
91
95
  let success = false;
92
96
  try {
93
- success = await callback(this);
97
+ success = await entrypoint(this);
94
98
  }
95
99
  catch (err) {
96
- inspectError(this.log, `Worker ${this.name} callback failed`, err);
100
+ inspectError(this.log, `Worker ${this.name} entrypoint failed`, err);
97
101
  }
98
102
  finally {
99
- this.destroy(success);
103
+ if (!success || this.workerData?.type !== 'thread')
104
+ this.destroy(success);
100
105
  }
101
106
  })();
102
107
  });
@@ -1,3 +1,3 @@
1
- import { WorkerWrapper } from './workerWrapper.js';
2
- declare const _default: WorkerWrapper;
1
+ import { ThreadsWrapper } from './threadsWrapper.js';
2
+ declare const _default: ThreadsWrapper;
3
3
  export default _default;
@@ -1,9 +1,9 @@
1
1
  import { isArchiveWorkerData } from '@matterbridge/types';
2
2
  import { logModuleLoaded } from '@matterbridge/utils/loader';
3
- import { WorkerWrapper } from './workerWrapper.js';
3
+ import { ThreadsWrapper } from './threadsWrapper.js';
4
4
  import { createZip, readZip, unZip } from './zipjs.js';
5
5
  logModuleLoaded('ArchiveCommand', '\u001B[35m');
6
- export default new WorkerWrapper('ArchiveCommand', async (worker) => {
6
+ export default new ThreadsWrapper('ArchiveCommand', async (worker) => {
7
7
  if (!isArchiveWorkerData(worker.workerData)) {
8
8
  worker.logger("error", `ArchiveCommand invalid parameters`);
9
9
  return false;
@@ -1,3 +1,3 @@
1
- import { WorkerWrapper } from './workerWrapper.js';
2
- declare const _default: WorkerWrapper;
1
+ import { ThreadsWrapper } from './threadsWrapper.js';
2
+ declare const _default: ThreadsWrapper;
3
3
  export default _default;
@@ -1,9 +1,9 @@
1
1
  import { inspectError } from '@matterbridge/utils/error';
2
2
  import { logModuleLoaded } from '@matterbridge/utils/loader';
3
3
  import { checkUpdates } from './checkUpdates.js';
4
- import { WorkerWrapper } from './workerWrapper.js';
4
+ import { ThreadsWrapper } from './threadsWrapper.js';
5
5
  logModuleLoaded('CheckUpdates', '\u001B[35m');
6
- export default new WorkerWrapper('CheckUpdates', async (worker) => {
6
+ export default new ThreadsWrapper('CheckUpdates', async (worker) => {
7
7
  worker.logger("info", `Starting check updates...`);
8
8
  let success = false;
9
9
  try {
@@ -1,3 +1,3 @@
1
- import { WorkerWrapper } from './workerWrapper.js';
2
- declare const _default: WorkerWrapper;
1
+ import { ThreadsWrapper } from './threadsWrapper.js';
2
+ declare const _default: ThreadsWrapper;
3
3
  export default _default;
@@ -3,9 +3,9 @@ import { inspectError } from '@matterbridge/utils/error';
3
3
  import { logModuleLoaded } from '@matterbridge/utils/loader';
4
4
  import { debugStringify } from 'node-ansi-logger';
5
5
  import { getDockerVersion, takeDockerVersionWarning } from './dockerVersion.js';
6
- import { WorkerWrapper } from './workerWrapper.js';
6
+ import { ThreadsWrapper } from './threadsWrapper.js';
7
7
  logModuleLoaded('DockerVersion', '\u001B[35m');
8
- export default new WorkerWrapper('DockerVersion', async (worker) => {
8
+ export default new ThreadsWrapper('DockerVersion', async (worker) => {
9
9
  worker.logger("info", `Starting docker version check...`);
10
10
  let success = false;
11
11
  let dockerBuildConfig;
@@ -1,3 +1,3 @@
1
- import { WorkerWrapper } from './workerWrapper.js';
2
- declare const _default: WorkerWrapper;
1
+ import { ThreadsWrapper } from './threadsWrapper.js';
2
+ declare const _default: ThreadsWrapper;
3
3
  export default _default;
@@ -1,9 +1,9 @@
1
1
  import { inspectError } from '@matterbridge/utils/error';
2
2
  import { logModuleLoaded } from '@matterbridge/utils/loader';
3
3
  import { getGlobalNodeModules } from '@matterbridge/utils/npm-prefix';
4
- import { WorkerWrapper } from './workerWrapper.js';
4
+ import { ThreadsWrapper } from './threadsWrapper.js';
5
5
  logModuleLoaded('GlobalPrefix', '\u001B[35m');
6
- export default new WorkerWrapper('GlobalPrefix', async (worker) => {
6
+ export default new ThreadsWrapper('GlobalPrefix', async (worker) => {
7
7
  let prefix;
8
8
  worker.logger("info", `Starting global prefix check...`);
9
9
  let success = false;
@@ -1,3 +1,3 @@
1
- import { WorkerWrapper } from './workerWrapper.js';
2
- declare const _default: WorkerWrapper;
1
+ import { ThreadsWrapper } from './threadsWrapper.js';
2
+ declare const _default: ThreadsWrapper;
3
3
  export default _default;
@@ -1,9 +1,9 @@
1
1
  import { isSpawnWorkerData } from '@matterbridge/types';
2
2
  import { logModuleLoaded } from '@matterbridge/utils/loader';
3
3
  import { spawnCommand } from './spawnCommand.js';
4
- import { WorkerWrapper } from './workerWrapper.js';
4
+ import { ThreadsWrapper } from './threadsWrapper.js';
5
5
  logModuleLoaded('SpawnCommand', '\u001B[35m');
6
- export default new WorkerWrapper('SpawnCommand', async (worker) => {
6
+ export default new ThreadsWrapper('SpawnCommand', async (worker) => {
7
7
  if (!isSpawnWorkerData(worker.workerData)) {
8
8
  worker.logger("error", `SpawnCommand invalid parameters`);
9
9
  return false;
@@ -1,3 +1,3 @@
1
- import { WorkerWrapper } from './workerWrapper.js';
2
- declare const _default: WorkerWrapper;
1
+ import { ThreadsWrapper } from './threadsWrapper.js';
2
+ declare const _default: ThreadsWrapper;
3
3
  export default _default;
@@ -3,9 +3,9 @@ import { getBunLatestVersion, getBunVersion, isBun } from '@matterbridge/utils/b
3
3
  import { inspectError } from '@matterbridge/utils/error';
4
4
  import { logModuleLoaded } from '@matterbridge/utils/loader';
5
5
  import { excludedInterfaceNamePattern } from '@matterbridge/utils/network';
6
- import { WorkerWrapper } from './workerWrapper.js';
6
+ import { ThreadsWrapper } from './threadsWrapper.js';
7
7
  logModuleLoaded('SystemCheck', '\u001B[35m');
8
- export default new WorkerWrapper('SystemCheck', async (worker) => {
8
+ export default new ThreadsWrapper('SystemCheck', async (worker) => {
9
9
  worker.logger("info", `Starting system check...`);
10
10
  let success = false;
11
11
  try {
package/dist/zipjs.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { Uint8ArrayReader, Uint8ArrayWriter, ZipReader, ZipWriter } from '@zip.js/zip.js';
3
+ import { configure, Uint8ArrayReader, Uint8ArrayWriter, ZipReader, ZipWriter } from '@zip.js/zip.js';
4
4
  import { AnsiLogger } from 'node-ansi-logger';
5
+ configure({ useWebWorkers: false });
5
6
  export async function createZip(zipPath, sourcePaths) {
6
7
  if (sourcePaths.length === 0)
7
8
  throw new Error('No files or directories provided to createZip.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/thread",
3
- "version": "3.10.13-dev-20261005-49e0dbd",
3
+ "version": "3.10.13-dev-20261008-55d98fb",
4
4
  "description": "Matterbridge thread library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -57,6 +57,10 @@
57
57
  "./manager": {
58
58
  "types": "./dist/threadsManager.d.ts",
59
59
  "import": "./dist/threadsManager.js"
60
+ },
61
+ "./wrapper": {
62
+ "types": "./dist/threadsWrapper.d.ts",
63
+ "import": "./dist/threadsWrapper.js"
60
64
  }
61
65
  },
62
66
  "engines": {
@@ -75,8 +79,9 @@
75
79
  "skipTsconfig": true
76
80
  },
77
81
  "dependencies": {
78
- "@matterbridge/types": "3.10.13-dev-20261005-49e0dbd",
79
- "@matterbridge/utils": "3.10.13-dev-20261005-49e0dbd",
82
+ "@matter/main": "0.17.9",
83
+ "@matterbridge/types": "3.10.13-dev-20261008-55d98fb",
84
+ "@matterbridge/utils": "3.10.13-dev-20261008-55d98fb",
80
85
  "@zip.js/zip.js": "2.22.0",
81
86
  "node-ansi-logger": "3.3.1"
82
87
  }