SnowLuma v2.4.2 source snapshot
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
This commit is contained in:
commit
fa9c73363d
1367 files changed
+355735
No files matched your search
@@ -0,0 +1,115 @@
|
||||
import { errorMessage } from '@snowluma/common/errors';
|
||||
import { createLogger, type Logger } from '@snowluma/common/logger';
|
||||
import { watch, type FSWatcher } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { loginDbPaths, qqDataRootsForPid } from './paths';
|
||||
|
||||
export function watchAccountChangesForPids(
|
||||
pids: readonly number[],
|
||||
onChange: () => void,
|
||||
log: Logger = createLogger('QqAccount'),
|
||||
): () => void {
|
||||
const roots = new Set<string>();
|
||||
|
||||
for (const pid of pids) {
|
||||
try {
|
||||
for (const root of qqDataRootsForPid(pid)) {
|
||||
roots.add(root);
|
||||
}
|
||||
} catch (error) {
|
||||
log.warn('account watch root failed: PID=%d err=%s', pid, errorMessage(error));
|
||||
}
|
||||
}
|
||||
|
||||
return watchAccountFiles([...roots].flatMap(loginDbPaths), onChange, log);
|
||||
}
|
||||
|
||||
/** Watch directories so replacing login.db or recreating its WAL keeps working. */
|
||||
export function watchAccountFiles(
|
||||
dbPaths: readonly string[],
|
||||
onChange: () => void,
|
||||
log: Logger = createLogger('QqAccount'),
|
||||
): () => void {
|
||||
const watchers = new Map<string, FSWatcher>();
|
||||
let timer: NodeJS.Timeout | undefined;
|
||||
|
||||
const changed = () => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
timer = undefined;
|
||||
onChange();
|
||||
}, 250);
|
||||
timer.unref();
|
||||
};
|
||||
|
||||
const observe = (dbPath: string) => {
|
||||
watchers.get(dbPath)?.close();
|
||||
watchers.delete(dbPath);
|
||||
const dbDirectory = path.dirname(dbPath);
|
||||
let directory = dbDirectory;
|
||||
|
||||
for (;;) {
|
||||
const target = path.relative(directory, dbPath).split(path.sep)[0]!;
|
||||
|
||||
try {
|
||||
const watcher = watch(directory, { persistent: false }, (_event, filename) => {
|
||||
if (
|
||||
watchers.get(dbPath) !== watcher ||
|
||||
(filename !== null &&
|
||||
filename !== target &&
|
||||
!(directory === dbDirectory && filename === `${target}-wal`))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// QQ may create its data directories after process discovery.
|
||||
if (directory !== dbDirectory) {
|
||||
observe(dbPath);
|
||||
}
|
||||
|
||||
changed();
|
||||
});
|
||||
watcher.on('error', (error) => {
|
||||
if (watchers.get(dbPath) !== watcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
watcher.close();
|
||||
watchers.delete(dbPath);
|
||||
log.warn('account watch failed: path=%s err=%s', dbPath, errorMessage(error));
|
||||
});
|
||||
watchers.set(dbPath, watcher);
|
||||
return;
|
||||
} catch (error) {
|
||||
const parent = path.dirname(directory);
|
||||
|
||||
if (
|
||||
error instanceof Error &&
|
||||
'code' in error &&
|
||||
error.code === 'ENOENT' &&
|
||||
parent !== directory
|
||||
) {
|
||||
directory = parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
log.warn('account watch failed: path=%s err=%s', dbPath, errorMessage(error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const dbPath of new Set(dbPaths)) {
|
||||
observe(dbPath);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
|
||||
for (const watcher of watchers.values()) {
|
||||
watcher.close();
|
||||
}
|
||||
|
||||
watchers.clear();
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user