@matterbridge/thread 3.9.3-dev-20260629-7c47082 → 3.9.3-dev-20260630-9b4b673

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,7 +1,7 @@
1
1
  import type { ApiPlugin, SharedMatterbridge } from '@matterbridge/types';
2
2
  import { AnsiLogger } from 'node-ansi-logger';
3
- import { BroadcastServer } from './broadcastServer.js';
4
- export declare function checkUpdates(matterbridge: SharedMatterbridge): Promise<void>;
3
+ import type { BroadcastServer } from './broadcastServer.js';
4
+ export declare function checkUpdates(matterbridge: SharedMatterbridge, server: BroadcastServer): Promise<void>;
5
5
  export declare function checkUpdatesAndLog(matterbridge: SharedMatterbridge, log: AnsiLogger, server: BroadcastServer): Promise<void>;
6
6
  export declare function getMatterbridgeLatestVersion(matterbridge: SharedMatterbridge, log: AnsiLogger, server: BroadcastServer): Promise<string | undefined>;
7
7
  export declare function getMatterbridgeDevVersion(matterbridge: SharedMatterbridge, log: AnsiLogger, server: BroadcastServer): Promise<string | undefined>;
@@ -4,33 +4,28 @@ import { getErrorMessage } from '@matterbridge/utils/error';
4
4
  import { logModuleLoaded } from '@matterbridge/utils/loader';
5
5
  import { isValidString } from '@matterbridge/utils/validate';
6
6
  import { AnsiLogger, db, debugStringify, nt, wr } from 'node-ansi-logger';
7
- import { BroadcastServer } from './broadcastServer.js';
8
7
  logModuleLoaded('CheckUpdates');
9
8
  let noUpdatesAvailable = true;
10
- export async function checkUpdates(matterbridge) {
9
+ export async function checkUpdates(matterbridge, server) {
11
10
  const log = new AnsiLogger({ logName: 'MatterbridgeUpdates', logTimestampFormat: 4, logLevel: matterbridge.logLevel });
12
- const server = new BroadcastServer('updates', log);
13
11
  noUpdatesAvailable = true;
14
- const checkUpdatePromise = checkUpdatesAndLog(matterbridge, log, server);
15
- const latestVersionPromise = getMatterbridgeLatestVersion(matterbridge, log, server);
16
- const devVersionPromise = getMatterbridgeDevVersion(matterbridge, log, server);
17
- const pluginsVersionPromises = [];
18
- const pluginsDevVersionPromises = [];
12
+ let plugins = [];
19
13
  try {
20
- const plugins = (await server.fetch({ type: 'plugins_apipluginarray', src: server.name, dst: 'plugins' }, 1000)).result.plugins;
21
- for (const plugin of plugins) {
22
- pluginsVersionPromises.push(getPluginLatestVersion(log, server, plugin));
23
- pluginsDevVersionPromises.push(getPluginDevVersion(log, server, plugin));
24
- }
14
+ plugins = (await server.fetch({ type: 'plugins_apipluginarray', src: server.name, dst: 'plugins' }, 1000)).result.plugins;
25
15
  }
26
16
  catch (error) {
27
17
  log.debug(`Error fetching plugins for update check: ${getErrorMessage(error)}`);
28
18
  }
29
- await Promise.all([checkUpdatePromise, latestVersionPromise, devVersionPromise, ...pluginsVersionPromises, ...pluginsDevVersionPromises]);
19
+ await checkUpdatesAndLog(matterbridge, log, server);
20
+ await getMatterbridgeLatestVersion(matterbridge, log, server);
21
+ await getMatterbridgeDevVersion(matterbridge, log, server);
22
+ for (const plugin of plugins) {
23
+ await getPluginLatestVersion(log, server, plugin);
24
+ await getPluginDevVersion(log, server, plugin);
25
+ }
30
26
  if (noUpdatesAvailable) {
31
27
  server.request({ type: 'frontend_snackbarmessage', src: server.name, dst: 'frontend', params: { message: 'No updates available', timeout: 5, severity: 'info' } });
32
28
  }
33
- server.close();
34
29
  }
35
30
  export async function checkUpdatesAndLog(matterbridge, log, server) {
36
31
  const { getGitHubUpdate } = await import('@matterbridge/utils/github-version');
@@ -32,6 +32,7 @@ export async function spawnCommand(command, args, packageCommand, packageName) {
32
32
  (command === 'npm' || command === 'bun') &&
33
33
  !hasParameter('docker') &&
34
34
  !hasParameter('nosudo') &&
35
+ !isBun() &&
35
36
  !process.env.PATH?.includes('/.nvm/versions/node/'))) {
36
37
  args.unshift(command === 'npm' && isBun() ? 'bun' : command);
37
38
  command = 'sudo';
@@ -12,9 +12,11 @@ export declare class ThreadsManager {
12
12
  private interval;
13
13
  private intervalMs;
14
14
  private threads;
15
+ private terminateWorkers;
15
16
  constructor(intervalMs?: number);
16
17
  destroy(): void;
17
18
  private msgHandler;
19
+ private terminateExitedWorkers;
18
20
  private intervalHandler;
19
21
  runThread(name: string, workerData?: WorkerData, argv?: string[], env?: NodeJS.ProcessEnv, execArgv?: string[], pipedOutput?: boolean): Worker;
20
22
  runInMainThread(name: string, workerData?: WorkerData | null): Promise<boolean>;
@@ -5,6 +5,7 @@ import { Worker } from 'node:worker_threads';
5
5
  import { hasParameter } from '@matterbridge/utils/cli';
6
6
  import { getErrorMessage } from '@matterbridge/utils/error';
7
7
  import { logModuleLoaded } from '@matterbridge/utils/loader';
8
+ import { fireAndForget } from '@matterbridge/utils/wait';
8
9
  import { AnsiLogger, CYAN, db, debugStringify, ft, MAGENTA, wr } from 'node-ansi-logger';
9
10
  import { BroadcastServer } from './broadcastServer.js';
10
11
  logModuleLoaded('ThreadsManager');
@@ -26,6 +27,7 @@ export class ThreadsManager {
26
27
  { name: 'ArchiveCommand', path: 'workerArchiveCommand.js', type: 'worker' },
27
28
  { name: 'DockerVersion', path: 'workerDockerVersion.js', type: 'worker' },
28
29
  ];
30
+ terminateWorkers = new Set();
29
31
  constructor(intervalMs = 60_000) {
30
32
  this.debug = hasParameter('debug') || hasParameter('verbose') || hasParameter('debug-threads') || hasParameter('verbose-threads');
31
33
  this.verbose = hasParameter('verbose') || hasParameter('verbose-threads');
@@ -47,6 +49,7 @@ export class ThreadsManager {
47
49
  }
48
50
  destroy() {
49
51
  clearInterval(this.interval);
52
+ this.terminateExitedWorkers();
50
53
  this.server.off('broadcast_message', this.boundMsgHandler);
51
54
  this.server.close();
52
55
  if (this.verbose)
@@ -83,10 +86,26 @@ export class ThreadsManager {
83
86
  }
84
87
  }
85
88
  }
89
+ terminateExitedWorkers() {
90
+ if (this.terminateWorkers.size > 0) {
91
+ this.log.debug(`Terminating ${this.terminateWorkers.size} workers that have exited...`);
92
+ for (const worker of this.terminateWorkers) {
93
+ try {
94
+ this.log.debug(`Terminating worker with thread id ${worker.threadId}...`);
95
+ fireAndForget(worker.terminate(), this.log, `Failed to terminate worker with thread id ${worker.threadId}`);
96
+ }
97
+ catch (error) {
98
+ this.log.error(`Failed to terminate worker with thread id ${worker.threadId}: ${getErrorMessage(error)}`);
99
+ }
100
+ }
101
+ this.terminateWorkers.clear();
102
+ }
103
+ }
86
104
  intervalHandler() {
87
105
  for (const thread of this.threads) {
88
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}`);
89
107
  }
108
+ this.terminateExitedWorkers();
90
109
  for (const thread of this.threads) {
91
110
  if (thread.worker && Date.now() - (thread.lastSeen ?? 0) > this.intervalMs) {
92
111
  const msg = { type: 'ping', threadId: thread.worker.threadId, threadName: thread.name };
@@ -128,6 +147,7 @@ export class ThreadsManager {
128
147
  threadInfo.worker = undefined;
129
148
  }
130
149
  this.log.debug(`Thread ${threadInfo.name} has exited at ${new Date(now).toISOString()}`);
150
+ this.terminateWorkers.delete(worker);
131
151
  });
132
152
  worker.on('message', (message) => {
133
153
  const now = Date.now();
@@ -148,6 +168,7 @@ export class ThreadsManager {
148
168
  if (!message.success) {
149
169
  threadInfo.errorCount = (threadInfo.errorCount ?? 0) + 1;
150
170
  }
171
+ this.terminateWorkers.add(worker);
151
172
  threadInfo.worker = undefined;
152
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`);
153
174
  }
@@ -165,6 +186,7 @@ export class ThreadsManager {
165
186
  threadInfo.lastDuration = Math.max(0, now - (threadInfo.lastStarted ?? now));
166
187
  threadInfo.errorCount = (threadInfo.errorCount ?? 0) + 1;
167
188
  threadInfo.worker = undefined;
189
+ this.terminateWorkers.add(worker);
168
190
  this.log.error(`Thread ${threadInfo.name} encountered an error at ${new Date(now).toISOString()} after running for ${threadInfo.lastDuration} ms: ${getErrorMessage(error)}`);
169
191
  });
170
192
  this.log.debug(`Started thread ${threadInfo.name} from path ${path} type ${threadInfo.type} with thread id ${worker.threadId}`);
@@ -191,6 +213,7 @@ export class ThreadsManager {
191
213
  const candidates = [
192
214
  path.join(currentModuleDirectory, fileName),
193
215
  path.join(currentModuleDirectory, '..', 'dist', fileName),
216
+ path.join(currentModuleDirectory, '..', 'src', fileName.replace(/\.js$/, '.ts')),
194
217
  path.join(currentModuleDirectory, 'workers', fileName),
195
218
  ];
196
219
  for (const candidate of candidates) {
@@ -8,7 +8,7 @@ export default new WorkerWrapper('CheckUpdates', async (worker) => {
8
8
  let success = false;
9
9
  try {
10
10
  const shared = (await worker.server.fetch({ type: 'matterbridge_shared', src: `matterbridge`, dst: 'matterbridge' }, 5000)).result.data;
11
- await checkUpdates(shared);
11
+ await checkUpdates(shared, worker.server);
12
12
  worker.logger("info", `Check updates succeeded`);
13
13
  success = true;
14
14
  }
@@ -124,7 +124,6 @@ export class WorkerWrapper {
124
124
  }
125
125
  try {
126
126
  parentPort.close();
127
- process.exit(success ? 0 : 1);
128
127
  }
129
128
  catch (error) {
130
129
  this.log.error(`Worker ${this.name}:${threadId} failed to close parentPort: ${getErrorMessage(error)}`);
@@ -146,6 +145,7 @@ export class WorkerWrapper {
146
145
  this.destroy(false);
147
146
  }
148
147
  safeParentLog(level, message) {
148
+ this.log.error(`Worker ${this.name}: ${message}`);
149
149
  if (this.destroyed)
150
150
  return;
151
151
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matterbridge/thread",
3
- "version": "3.9.3-dev-20260629-7c47082",
3
+ "version": "3.9.3-dev-20260630-9b4b673",
4
4
  "description": "Matterbridge thread library",
5
5
  "author": "https://github.com/Luligu",
6
6
  "homepage": "https://matterbridge.io/",
@@ -69,8 +69,8 @@
69
69
  "CHANGELOG.md"
70
70
  ],
71
71
  "dependencies": {
72
- "@matterbridge/types": "3.9.3-dev-20260629-7c47082",
73
- "@matterbridge/utils": "3.9.3-dev-20260629-7c47082",
72
+ "@matterbridge/types": "3.9.3-dev-20260630-9b4b673",
73
+ "@matterbridge/utils": "3.9.3-dev-20260630-9b4b673",
74
74
  "@zip.js/zip.js": "2.8.26",
75
75
  "node-ansi-logger": "3.3.0"
76
76
  }