refactor(app): migrate main + preload to TypeScript with typed seams (Phase 5d-3c-ii)
Convert the app's main and preload source from .js to .ts (git mv preserves history), adding types where they carry value: the core-consumption seam (BuildIndexOptions/BuildIndexResult, FileInfo), the indexer service/worker factories, and the preload IPC bridge. Module-to-module specifiers use the real .ts extension (mirroring scripts/, since Node type-stripping does not rewrite .js->.ts); the worker's runtime path stays indexer-worker.js because that is the built output. Toolchain: - Add app/tsconfig.json: strict but noImplicitAny:false (the app orchestrates the already-strict core; annotating every SQLite-handle helper is low-value churn) + allowImportingTsExtensions (safe under noEmit). - Add @types/better-sqlite3 for the injected binding. - electron.vite.config.ts inputs -> .ts; refresh the stale CommonJS comment. - typecheck script runs root + app projects. Root tsconfig excludes the app-importing tests (app-*.test.mjs, recap-capture-query.test.mjs) so the lenient app files are not dragged into the strict root program; the app source is covered by app/tsconfig.json instead. See docs/adr/0005. Verified: npm run typecheck (root + app) clean; suite 121/121; electron-vite build emits all 6 main entries + preload with no .ts/node:sqlite residue in the bundles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
60d47a852e
commit
01a390fa10
@@ -0,0 +1,81 @@
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Worker } from 'node:worker_threads';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
interface WorkerMessage {
|
||||
id: number;
|
||||
result?: unknown;
|
||||
error?: { message: string; stack?: string };
|
||||
}
|
||||
|
||||
interface PendingBuild {
|
||||
resolve: (value: unknown) => void;
|
||||
reject: (error: Error) => void;
|
||||
}
|
||||
|
||||
interface WorkerBuildIndexOptions {
|
||||
workerPath?: string;
|
||||
WorkerImpl?: typeof Worker;
|
||||
}
|
||||
|
||||
function createWorkerBuildIndex({
|
||||
// indexer-worker.js is the built worker output emitted next to this module.
|
||||
workerPath = path.join(__dirname, 'indexer-worker.js'),
|
||||
WorkerImpl = Worker,
|
||||
}: WorkerBuildIndexOptions = {}) {
|
||||
let worker: Worker | null = null;
|
||||
let nextId = 1;
|
||||
const pending = new Map<number, PendingBuild>();
|
||||
|
||||
const rejectPending = (error: Error) => {
|
||||
for (const { reject } of pending.values()) reject(error);
|
||||
pending.clear();
|
||||
};
|
||||
|
||||
const ensureWorker = (): Worker => {
|
||||
if (worker) return worker;
|
||||
const active = new WorkerImpl(workerPath, { type: 'module' } as ConstructorParameters<typeof Worker>[1]);
|
||||
worker = active;
|
||||
active.on('message', (message: WorkerMessage) => {
|
||||
const current = pending.get(message.id);
|
||||
if (!current) return;
|
||||
pending.delete(message.id);
|
||||
if (message.error) {
|
||||
const error = new Error(message.error.message);
|
||||
error.stack = message.error.stack;
|
||||
current.reject(error);
|
||||
} else {
|
||||
current.resolve(message.result);
|
||||
}
|
||||
});
|
||||
active.on('error', (error: Error) => {
|
||||
rejectPending(error);
|
||||
worker = null;
|
||||
});
|
||||
active.on('exit', (code: number) => {
|
||||
if (pending.size) rejectPending(new Error(`Indexer worker exited with code ${code}`));
|
||||
worker = null;
|
||||
});
|
||||
return active;
|
||||
};
|
||||
|
||||
const buildIndex = (args: Record<string, unknown> = {}) => new Promise((resolve, reject) => {
|
||||
const id = nextId++;
|
||||
pending.set(id, { resolve, reject });
|
||||
ensureWorker().postMessage({ id, args });
|
||||
});
|
||||
|
||||
const stop = () => {
|
||||
const current = worker;
|
||||
worker = null;
|
||||
const termination = current?.terminate ? Promise.resolve(current.terminate()) : Promise.resolve();
|
||||
rejectPending(new Error('Indexer worker stopped'));
|
||||
return termination;
|
||||
};
|
||||
|
||||
return { buildIndex, stop };
|
||||
}
|
||||
|
||||
export { createWorkerBuildIndex };
|
||||
Reference in New Issue
Block a user