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:
tommy0103
2026-07-09 16:25:59 +08:00
co-authored by Claude Opus 4.8
parent 60d47a852e
commit 01a390fa10
21 changed files with 299 additions and 151 deletions
+24 -3
View File
@@ -27,11 +27,32 @@ decisions within this:
(ADR-0003) remains for the skill artifact; the app does not need it.
- **better-sqlite3 stays the app's binding**, externalized (not bundled) and
unpacked from the asar.
- **The app main + preload source is TypeScript with types at its seams**, but
under a *deliberately more lenient* project than the runtime core. `app/tsconfig.json`
keeps `strict` on yet sets `noImplicitAny: false`, because the app mostly
orchestrates the already-strictly-typed core (`scripts/`), and annotating every
internal SQLite-handle helper would be high-cost, low-value churn. Types are
added where they matter: the core-consumption seam (`BuildIndexOptions`/
`BuildIndexResult`, `FileInfo`), the service/worker factories, and the IPC
bridge. Module-to-module specifiers use the real `.ts` extension (mirroring
`scripts/`, since Node's type-stripping does not rewrite `.js``.ts`), which
needs `allowImportingTsExtensions` (safe under the project's `noEmit`); the
worker's *runtime* path stays `indexer-worker.js` because that is the built
output. `@types/better-sqlite3` is a devDependency for the injected binding.
**Two-tier typechecking.** `npm run typecheck` runs the root project (`scripts/` +
`tests/`, fully strict including `noImplicitAny`) and then the app project. The
root project **excludes the app-importing tests** (`tests/app-*.test.mjs`,
`tests/recap-capture-query.test.mjs`): those tests import app source, which would
otherwise drag the lenient app files into the strict root program and fail on
implicit `any`. The app source is instead covered by `app/tsconfig.json`, so
nothing loses type coverage — the strict core and the lenient app are checked by
the project that owns each, and never mixed.
**Consequences.** The app is restructured into `src/{main,preload,renderer}` with
`electron.vite.config.ts`; each main module is a build input so CommonJS-style
require resolution and the indexer worker (`{ type: 'module' }`) resolve at
runtime. `npm run dev` is `electron-vite dev`. Tests that loaded app modules moved
`electron.vite.config.ts`; each main module is a build input so relative imports
between them and the indexer worker (`{ type: 'module' }`) resolve at runtime.
`npm run dev` is `electron-vite dev`. Tests that loaded app modules moved
to ESM imports, and `app-main-settings` was rewritten from CJS `Module._load`
mocking to `node:test` `mock.module` (needs `--experimental-test-module-mocks`).
A future contributor may be tempted to make the preload ESM or disable the