feat(audit): Batch 24 — one validate-and-coerce schema layer for settings (CC9)

zod field schemas in settingsSchema.ts replace setSettings' bespoke
per-key switch: parse → skip-on-fail → store, side effects (secret
encryption, launch-at-startup sync) kept in settings.ts. sanitizeOptions
extracted to settingsOptions.ts to break the import cycle; backup import
flows through the same schema. Field-by-field parity pinned in
test/settingsSchema.test.ts (15 tests); full suite unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 12:41:00 -04:00
parent 6abed2064d
commit 8f6b2a761b
7 changed files with 330 additions and 197 deletions
+12 -4
View File
@@ -2018,13 +2018,21 @@ split — but that style is unenforced and several "do the same thing two ways"
renderer's `logError` (M29) failures to the same file sink — closing the "sink" half M29/R6 deferred here.
`errorlog.ts` stays as user-facing download-failure data. The user-facing **toast** half ties to the
global status surface (UI25/UX9) and lands there.*
- [ ] **CC9 — Four validation styles.** Type-guard predicates (`isValid*` in validation.ts),
- [x] **CC9 — Four validation styles.** Type-guard predicates (`isValid*` in validation.ts),
coerce-with-fallback (`sanitizeOptions`, `templates.sanitize`), an imperative per-key `switch`
(`setSettings`, M24), and `throw` (`assertHttpUrl`). **Standard:** one schema layer (e.g. `zod`) that
both *validates and coerces*; `setSettings` runs values through the same schema instead of a bespoke switch.
*Deferred to 1.x (per the release gate — the CC consolidations are explicitly post-1.0): adopting a schema
layer (`zod`) is a new runtime dependency plus a rewrite of `validation.ts` + `setSettings` + backup import.
High-value but a sweeping change best done as its own reviewed PR, not unattended; standard recorded.*
*Fixed (Batch 24): `zod` adopted; every Settings key is a schema in
[settingsSchema.ts](src/main/settingsSchema.ts) whose parsed output is exactly what gets persisted —
numbers coerce+clamp, strings trim, enums allowlist, folders/templates run the shared validation.ts
refinements, `downloadOptions` sanitizes (via the extracted
[settingsOptions.ts](src/main/settingsOptions.ts)). `setSettings`'s 130-line switch is now a 15-line loop:
parse → skip-on-fail → store, with the two genuine side effects (secret encryption at rest, the
launch-at-startup registry sync) kept beside their module state. Backup import flows through the same
schema (it calls setSettings). Parity with the old switch is pinned field-by-field in
[test/settingsSchema.test.ts](test/settingsSchema.test.ts); the whole suite passes unchanged. The jsonStore
row guards deliberately stay type-guard predicates (hot read path filtering untrusted disk rows — no
coercion wanted), documented in the schema header.*
- [ ] **CC10 — Mixed serialization/persistence.** `electron-store` (settings) **and** hand-rolled
pretty-JSON files (history/errorlog/templates/sources/media-items, M1) for the same job, plus
yt-dlp-mandated formats (Netscape cookies, plaintext archive) and base64 `enc:v1:` secrets.