Skip to content

Enable TypeScript strict mode across frontend, electron, and server#3388

Closed
jcfurey wants to merge 47 commits into
ChurchApps:devfrom
jcfurey:split/5-typescript-strict
Closed

Enable TypeScript strict mode across frontend, electron, and server#3388
jcfurey wants to merge 47 commits into
ChurchApps:devfrom
jcfurey:split/5-typescript-strict

Conversation

@jcfurey

@jcfurey jcfurey commented Jun 16, 2026

Copy link
Copy Markdown

🔗 Stacked on #3387 (vitest suite). This PR clears the ~76 Svelte 5 migration type errors deferred from PR3/PR4 and completes strict mode.

Builds on the Svelte 5 migration to re-enable and complete TypeScript strict mode.

Resolve the remaining Svelte 5 migration type errors (svelte-check → 0)
Re-enable strict mode, then work through noImplicitAny in stages (type untyped params, cast index access, type variable declarations) until full strict passes
Enable full strict for the electron tsconfig; make the already-strict server tsconfig type-check clean
Mostly type annotations and safe casts (+410 / −415), no runtime behavior change.

Verification: svelte-check (0 errors), electron tsc, and server tsc all clean.

claude added 24 commits June 16, 2026 00:56
Security:
- zip-slip guard in archive extraction (sanitize entry paths)
- REMOTE control/API auth gate (require authenticated connection when a password is set)
- harden WebRTC host window (deny navigation/new windows)
- make export window contextIsolation explicit

Correctness:
- readExifData always resolves (fixes IPC hang)
- save() queues concurrent saves instead of dropping them
- requestMain registers listener before send; resolves when bridge absent
- companion servers recreate listenable instances on restart; clear state on close
- requestToMain uses a single shared dispatcher (no per-request global listener)
- release thumbnail generation lock on timeout
- single-pass now-playing token replacement
- close fs.watch before re-watching downloads
- per-process UUID fallback for machine id
- order-independent deep compare in checkIfMatching
- log previously-empty catches
- normalize CRLF on CSV import

Build/process:
- add .nvmrc, engines field, and a PR CI workflow (build gate; format/svelte report-only)

Verified: electron tsc type-check and production build pass.
- tmp -> ^0.2.7 (+ override) clears tmp path-traversal High across direct/grandiose/electron-builder
- fast-xml-parser 5.4.1 -> ^5.8.0 (un-pinned) clears entity-expansion/builder-injection High
- overrides shell-quote ^1.8.4 eliminates the critical svelte-inspector/open-in-editor/shell-quote chain
- npm-run-all -> npm-run-all2@^9 (maintained drop-in)

npm audit: 27 -> 21 (criticals 3 -> 0, highs 10 -> 7).
Verified: electron tsc type-check and production build pass.
…or in audit docs

- package-lock.json updated to match the dependency security fixes (tmp, fast-xml-parser, shell-quote override, npm-run-all2)
- Correct an earlier audit error: package-lock.json IS committed/tracked; the .gitignore entry is a commented-out (inactive) line, not an active ignore rule. The 'no reproducible builds' finding is withdrawn.
- ci.yml now installs with 'npm ci' (lockfile verified in sync via npm ci --dry-run)
- electron ^37.10.3 -> ^40.10.3; electron-builder ^26.15.2, electron-updater ^6.8.9
- better-sqlite3 -> ^12.10.0 (prebuilt binary available for Electron 40 ABI)
- engines.node >= 22.12.0 (Electron 40 requirement); playwright CI node 20 -> 22

Stopped at 40 (not 42): Electron 41/42 ship V8 14 which removed
v8::External::Value()/New(); better-sqlite3 12.10.0 fails to source-compile
against it and has no prebuild for those ABIs. 40.10.3 is > 39.8.4 so it
clears all flagged electron advisories.

Verified: npm audit no longer flags electron (27->20 total, criticals 0,
highs 7->6); electron tsc type-check passes; production build passes; app
launches and fully renders under xvfb (screenshot confirmed). The single
Playwright E2E test fails only on a stale welcome-popup selector, unrelated
to Electron (and disabled in CI).
…on 40

The single E2E test had drifted from the UI and was failing on stale selectors:
- pick the main window by URL (index.html) instead of firstWindow() (could be the splash)
- drive the consolidated Initialize.svelte welcome popup (language dropdown,
  data-location folder picker, 'Get started!') instead of the old 3-step flow
- guard the setup popup + onboarding guide so the test is idempotent
  (passes whether or not user data already exists)
- group labels use data-title now -> select by text
- save via Ctrl+S (the right-click 'Save' menu became 'Quick search')

Verified passing under xvfb on both a fresh profile and an already-initialized one.
Note: the test's FS_MOCK_STORE_PATH env var is not honored by the app, so state
isn't isolated (harmless in fresh CI; noted as a follow-up).
…t RCE High)

Pulls a patched serialize-javascript (7.0.5), clearing the High (GHSA-5c6j-r48x-rmvq)
plus a @rollup/plugin-terser moderate. Zero-risk: the only consumer
(config/building/rollup.config.mjs) isn't referenced by any build script (the
active build uses Vite + tsc), and the terser() plugin API is unchanged
(peer rollup ^4). npm audit: 20 -> 18 (criticals 0, highs 6 -> 5). Build verified.
…oderate)

v8+ is ESM-only, but no dynamic-import conversion was needed: Electron 40
bundles Node >=22.12 which supports require() of ESM (music-metadata exposes a
module-sync export condition and has no top-level await), so the existing static
imports work. Only code change: v11 returns IPicture.data as Uint8Array instead
of Buffer, so cover-art handling wraps it with Buffer.from().

Verified: electron tsc passes; svelte-check unchanged (ICommonTagsResult still
exported); production build passes; E2E smoke test passes (decisive, since
music-metadata is require()d at startup via responsesMain.ts).

npm audit: 18 -> 16 (criticals 0, highs 5 -> 4).
…ser (S4)

The variable-expression and number-input calculators used new Function("return "+x)(),
which executes arbitrary JS from user/network-reachable input. Replaced all four call
sites with evaluateExpression() — an arithmetic-only recursive-descent parser (numbers,
+ - * / % **, unary +/-, parentheses) that throws on any identifier/call/property access.

Verified: 19/19 unit checks (valid math computes correctly; alert(1), Math.PI, x=5,
__proto__, etc. all throw); production build passes.
…lint-plugin-svelte

- Replace the three legacy eslintrc files with one flat config (config/linting/eslint.config.mjs).
- eslint 8 (EOL) -> 9; @typescript-eslint 5 -> typescript-eslint 8; eslint-config-prettier 8 -> 10;
  eslint-plugin-jsdoc 39 -> 50; swap deprecated eslint-plugin-svelte3 -> eslint-plugin-svelte 3
  (+ svelte-eslint-parser, supports Svelte 3); drop unused eslint-plugin-prefer-arrow.
- Consolidate lint scripts into a single 'lint:eslint' (flat config scopes electron/frontend/svelte;
  --ext is removed in eslint 9).
- Rule set kept behaviour-faithful: map removed v8 rules (ban-types -> no-restricted-types,
  no-empty-interface -> no-empty-object-type, no-var-requires -> no-require-imports,
  id-blacklist -> id-denylist), drop removed 'off' formatting rules, and turn off v8's
  newly-recommended promise/type rules to match the old config's intent. eslint:recommended
  intentionally not added (the old config never extended it).

Verified: flat config loads and lints electron, frontend TS, and Svelte 3 components with no
config/parsing errors; findings match the old rule set (pre-existing, mostly auto-fixable).
Build passes; lockfile npm ci-verified. The old eslintrc files are kept (superseded; removable).

The legacy config files (eslint.electron.json/eslint.frontend.json/eslint.svelte.js) are now unused.
eslint.electron.json / eslint.frontend.json / eslint.svelte.js are no longer
referenced (replaced by config/linting/eslint.config.mjs in the ESLint 9 migration).
Mechanical autofixes across 291 files (prefer-const, consistent-type-imports,
dot-notation, object-shorthand, no-var, etc.) from 'npm run lint', plus Prettier
formatting on the touched files. Kept as a separate commit from the config migration.

Verified: electron tsc passes; production build passes; svelte-check improved
(199 -> 186 errors, no new ones); E2E smoke test passes. The remaining ~604
non-fixable findings (no-shadow / no-console / no-unused-vars debt) are left for
follow-up.
Toolchain:
- svelte 3 -> 5, vite 4 -> 8, @sveltejs/vite-plugin-svelte 2 -> 7, svelte-check 2 -> 4,
  typescript 4.9 -> 5.9, @tsconfig/svelte 2 -> 5.
- Replace svelte-preprocess with vitePreprocess (svelte.config + both vite configs).
- Drop svelte-inspector fork + rollup-plugin-svelte (Svelte-3-only, used only by the
  unused rollup.config.mjs).

Code changes (Svelte 5 / Vite 8 / TS 5 compatibility):
- Component instantiation: new App({target}) -> mount(App, {target}) (frontend + 5 server entries).
- Top.svelte: <script type="ts"> -> <script lang="ts"> (vitePreprocess needs lang).
- Connection.svelte: rename {@const connections} (Svelte 5 disallows shadowing a store name in a store subscription).
- Server type-only imports marked  (Rolldown rejects type-as-value): Writable, ReceiverKey,
  Deep*/Inferred/Nested, AutosizeTypes, StageItem/StageLayout.
- bonjour.ts: bonjour-service 1.4 exports Service as a value -> InstanceType<typeof Service> (TS 5).
- Raise the E2E test timeout (the flow is slightly slower under Svelte 5).

Status: full production build passes (frontend + servers + electron); the app launches and the
E2E user flow completes (verified via screenshot). KNOWN REMAINING: the electron process does not
exit cleanly after the test (worker-teardown hang) — needs investigation; svelte-check (v4) reports
~584 issues (mostly Svelte 5 deprecations/strictness) for a follow-up cleanup; full feature QA pending.
…umbrella)

@tsconfig/svelte v5 turns on "strict" (incl. noImplicitAny), which the project
intentionally kept off (it opts into only strictNullChecks/strictFunctionTypes/
noImplicitThis). Restoring strict:false drops svelte-check from 584 -> 78 errors
(below the pre-migration ~186 baseline). Type-check only; the esbuild-based build
is unaffected.
…stale build prereqs

Adds a comprehensive build/test/packaging guide derived from the CI workflows, covering
all three OSes (toolchains, Node 22.12, Python — incl. the macOS 3.11 node-gyp caveat,
dev run, production build, the E2E test incl. the Linux xvfb wrapper, lint/format, and
packaging/release + signing env vars).

Also reconciles the README and CLAUDE.md: Linux needs libfontconfig1-dev + uuid-dev +
libltc-dev (not just libfontconfig1-dev), notes Node 22.12+, and links to BUILDING.md.
…environment

Pins the OS toolchain + Node 22.12 + Python + the native-build system libs
(libfontconfig1-dev/uuid-dev/libltc-dev) + xvfb, installs deps (npm ci when a
lockfile exists, else npm install) and runs the production build. Documented in
BUILDING.md with usage (build/pack/test) and the lockfile traceability caveat.

Note: targets Linux only (Electron builds per-platform). Not container-build-tested
here (no Docker daemon in this env), but mirrors the verified CI/local build steps.
package-lock.json is already committed and in sync; switch build.yml, playwright.yml,
and release.yml (Windows/Linux/macOS) from npm install to npm ci for reproducible,
deterministic installs. Verified the lockfile records all platforms' optional native
binaries (rollup/rolldown win32/darwin/linux with os/cpu constraints), so npm ci picks
the right one per platform — and a clean npm ci (incl. native rebuild) passes on Linux.

Also: add the missing uuid-dev/libltc-dev to playwright.yml's apt step (needed for the
native rebuild), and replace the obsolete @rollup/rollup-x-gnu note in .gitignore
(that npm bug was fixed in npm 10).
…oing forward

Svelte 5 warns on self-closing non-void HTML elements (<div ... /> etc.). Converted
all 204 occurrences across frontend + server components to explicit close tags
(<div ...></div>) using eslint-plugin-svelte's autofixable svelte/html-self-closing
rule (AST-safe), then prettier-formatted. Also enabled that rule in the project ESLint
config (frontend) so it can't regress.

Verified: full build passes (frontend + servers + electron); svelte-check warnings
299 -> 144 (the ~155 self-closing warnings cleared; 78 errors unchanged); E2E smoke
test passes.
… labels, aria-selected)

- img alt="" on decorative output media / preview thumbnails / custom action icons (3)
- aria-label on icon-only inc/dec buttons in MaterialNumberInput + MaterialRadialPicker (4)
- aria-selected={false} on the non-selectable 'add new' dropdown option (2)

These are universally-correct improvements with no behaviour change. Build passes;
svelte-check warnings 144 -> 135; E2E passes.

The remaining a11y warnings are judgment-/risk-heavy and out of scope here (see PR notes):
~90 interactive <div> elements would need role+tabindex+keyboard handlers (changes
operator-UI tab order/key handling, which the build deliberately suppresses via onwarn),
plus intentional autofocus and structural floating-label cases.
The autofocus inputs/textareas are prop-driven (a caller opts in to focus a field when a
dialog/field appears) — intentional operator UX. Marked with <!-- svelte-ignore a11y_autofocus -->
rather than removing it. svelte-check warnings 135 -> 127.
The two .pickColor gradient swatches already had tabindex/aria-label but no keyboard
handler; added on:keydown={triggerClickOnEnterSpace} (reuses the existing click via the
shared helper, no new tab stops). svelte-check warnings 127 -> 125.
Inventories the remaining ~125 svelte-check a11y warnings (by category + component area),
documents the fix patterns (using the existing clickable.ts helpers), the real risks
(tab-order changes, key conflicts, manual-QA-only verification), a phased plan, and the
policy decisions to make first (enforce a11y? remove the onwarn suppression? i18n aria-labels?).
claude added 4 commits June 16, 2026 02:40
The committed public/index.html referenced the production bundle
(./build/bundle.js), which only exists after `vite build` — a
setProductionHTML() artifact that cleanBuilds.js never reverted before
commit. Restore the dev source entry (/src/frontend/main.ts) to match
upstream; postBuild.js still swaps in the bundle tags for production,
cleanBuilds.js reverts. Fixes a broken `npm start` dev server.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
…ripture)

Now that the vitest harness is in place, cover three dependency-free modules
that are easy to get subtly wrong:

- frontend/utils/expression.ts — the security-critical safe arithmetic parser:
  math correctness (precedence, associativity, unary, e-notation) and, most
  importantly, that it REJECTS identifiers / calls / property access, which is
  what makes it a safe replacement for new Function().
- frontend/components/helpers/color.ts — hex/rgb/hsl conversions, contrast,
  fade, and round-trips.
- common/scripture/sanitizeVerseText.ts — <br>/<q>/nbsp/whitespace handling.

52 new tests (70 total with the existing syncLedger suite); all pass.
svelte-check unchanged (the same 78 pre-existing migration errors). Updated
CLAUDE.md to document the vitest unit-test layer and how to run a single file.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
…elpers

Four more dependency-free modules:
- frontend/utils/chordTranspose.ts — sharp/flat preference by direction, octave
  wrap, slash chords, unicode accidentals, and leaving section labels (e.g.
  [Chorus], [Verse 1]) untouched.
- frontend/components/helpers/bytes.ts — unit boundaries and the decimals arg.
- frontend/components/helpers/style.ts — style-string parsing, unit stripping,
  and transform -> filter expansion.
- frontend/components/helpers/cropping.ts — crop clamping/scaling, pan limits,
  center, and the generated clip-path / ppt zoom geometry.

36 new tests (106 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
Three more import-clean modules:
- frontend/components/helpers/mover.ts — the drag-reorder array logic
  (getIndexes, addToPos, and mover incl. the position adjustment when moving
  items forward past the gap they leave).
- frontend/utils/clickable.ts — Enter/Space activation handlers, incl. the
  guards for inputs/textareas/editable elements and the Space-in-slide case.
- frontend/utils/languageData.ts — a data-integrity invariant: every language
  has a non-empty browser-locale mapping and a flag, with no stray keys.

19 new tests (125 total); all pass. svelte-check unchanged (78 pre-existing).

Note: array.ts/time.ts remain untested — they transitively import stores.ts ->
showActions.ts -> pdfjs-dist, which needs DOMMatrix at import time and so won't
load under vitest's node environment.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
claude added 19 commits June 16, 2026 02:40
- electron/utils/helpers.ts — clone (deep, reference-independent), the
  order-normalized checkIfMatching used by cloud sync (key-order-insensitive
  for objects but order-sensitive for arrays), and wait.
- frontend/components/helpers/fonts.ts — getFontName quoting rules.
- frontend/utils/request.ts — renderer IPC dispatch helpers (send fans out per
  channel; receive routes a message to its channel handler), via a window.api mock.

14 new tests (139 total); all pass. svelte-check unchanged (78 pre-existing).
Test files are excluded from the electron build via the base tsconfig's
**/*.test.ts exclude (the prod tsconfig is generated from it by preBuild.js).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
These two high-traffic modules looked untestable because array.ts imports
utils/language — a hub that transitively drags in stores.ts -> svelte components
and browser globals. Solved surgically with module mocking rather than global
env hacks (which cascaded into the whole UI):

- array.ts: vi.mock("../../utils/language") cuts the chain. Covers
  removeDuplicates, keysToID, removeValues/removeDeleted, removeDuplicateValues,
  changeValues, clone, areObjectsEqual, moveToPos, the sorts, getChangedKeys and
  rangeSelect (ctrl/meta/shift selection logic).
- time.ts imports cleanly (stores.ts only creates writables). Covers
  secondsToTime, joinTime/joinTimeBig, padString/addZero, splitDate,
  combineDateAndTime/changeTime, getWeekday/getMonthName (English fallback and a
  provided dictionary), timeAgo and getTimeFromInterval.

40 new tests (179 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
The converters build a ShowObj and call setTempShows inside a setTimeout (no return
value) and transitively import the whole app. Established a reusable harness: mock the
show-building collaborators (ShowObj, array/clone, setShow, show, itemHelpers,
importHelpers) while stores stay real, drive the converter with fake timers, and
capture setTempShows to assert on the parsed output.

- chordpro.ts: metadata directives + aliases (t/ccli/f/su), inline chord position
  extraction, # comment -> layout notes, and section splitting across markers.
- csv.ts: parseCSVLine quoted / triple-quoted / unquoted fields, one slide per row and
  one item per field, empty-line dropping, and CRLF normalization.

11 new tests (190 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
More option-1 (no new deps) converters:
- txt.ts exported helpers: trimNameFromString (name extraction incl. label-skip,
  punctuation/html stripping, length truncation), similarity (case-insensitive
  edit-distance ratio), and findGroupMatch (group id / custom display name /
  dictionary-translation fallback). The heavy convertText pipeline is left out as
  too intricate to unit-test meaningfully.
- songbeamer.ts full pipeline (runs synchronously): #-prefixed metadata parsing
  incl. CCLI digit extraction, title -> name with file-name fallback, UTF-8 BOM
  stripping, and grouped slides with their lyric lines.

17 new tests (207 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
XML converters route through a real DOMParser (xml2json / getElementsByTagName),
which node lacks. Added jsdom as a dev dependency and opt the relevant test files into
it per-file via `// @vitest-environment jsdom` (no global config change). Tried
happy-dom first but it mis-iterates element attributes (empty nodeName/nodeValue);
jsdom is spec-faithful.

- xml.ts: xml2json element/text parsing, @-prefixed attributes, repeated-element ->
  array collapsing, and XML-declaration header stripping.
- opensong.ts: metadata via DOMParser, [V] tag -> verse global group, "." chord lines
  attached to the following lyric line, and ";" comments captured as slide notes.

8 new tests (215 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
More XML converters on the jsdom unlock:
- openlp.ts (OpenLyrics): metadata (title/author/CCLI/copyright), verses split on
  <br/>, V/C group-name mapping, verseOrder-driven layout order, and inline [chord]
  position extraction.
- zefaniaBible.ts: parses the Zefania XML bible (books/chapters/verses) and registers
  it in the scriptures store; file-name fallback when biblename is absent.

7 new tests (222 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
ProPresenter is the most involved converter; covered both import shapes:
- JSON: convertJSONToSlides (verses -> slides, group mapping, verse_order layout,
  metadata) and convertJSONBundleToSlides (<p>/<br> lyrics).
- XML (.pro4): the real decode pipeline base64 -> decodeBase64 -> RTFToText/decodeHex
  -> splitTextToLines. Fixtures build base64-encoded RTF in-test and assert the decoded
  lyrics, CCLI metadata from document attributes, \par -> newline, and the PlainText
  fallback (paragraph splitting).

8 new tests (230 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
… EasySlides, MediaShout)

Rounds out converter coverage with the jsdom + harness pattern:
- quelea.ts: sections -> slides, metadata, group mapping.
- osisBible.ts / bebliaBible.ts: XML bible -> books/chapters/verses + scripture-store
  registration (named books to skip the translate prompt).
- verseview.ts: CDATA lyrics split on <slide>/<br>.
- easyslides.ts: Contents -> slides with [group] labels and SongNumber metadata.
- mediashout.ts: MediaShout5 Cues -> slides from Content Elements.

12 new tests (242 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
Two more song converters (both JSON/object-based, no DOM needed):
- videopsalm.ts: exercises the malformed-JSON repair pipeline (unquoted keys ->
  valid JSON), verse <br> splitting, inline [chord] extraction, and metadata.
- softprojector.ts: pre-parsed song object -> slides (first line of each block is the
  group label), with metadata.

5 new tests (247 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
Two non-song converters:
- calendar.ts: ICS/iCal parsing (BEGIN/END nesting via convertToJSON) into stored
  events, including RRULE -> repeatData mapping.
- project.ts: importProject distributes overlays/effects/actions/media into their stores
  and creates the shows + project through history.

4 new tests (251 total); all pass. svelte-check unchanged (78 pre-existing).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
… any, props)

- MaterialButton.svelte: type the `button` element + event/id params; drop the unused
  `translate` import.
- VirtualList.svelte: give `itemHeight` a default so it's an optional prop — callers
  already relied on the existing `|| 30` fallback (behavior unchanged).
- AudioFilter.svelte: drop unused `translateText`; Array.from(magResp) before .map() —
  Float32Array.map coerced the SVG-path strings to NaN, a latent runtime bug now fixed.
- ScriptureContentTablet.svelte: mark the unused `source` param.

svelte-check: 78 -> 62 errors, no new ones.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
PDF export iterates project items that may be shows, sections, or media, with display
fields (data, metaDisplay) added at runtime in getRefs() — but they were typed as plain
Show. Introduced an ExportShow augmented type for `shows`/`renderedShows` and the
page-count helper, guarded `show.id` indexing with `|| ""`, and cast the legacy
`chord.chord` fallback. No behavior change.

svelte-check: 62 -> 23 errors, no new ones.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
…eck now 0)

- Null/undefined guards: ContextItem (|| false), ListView (?.length ?? 0),
  ChooseStyle (type/outputs defaults).
- Number coercion for TemplateStyle's MaterialNumberInput values.
- dataArrays now infers Uint8Array<ArrayBuffer> (dropped the explicit Uint8Array[]
  annotation) in AudioPreview/Visualizer.
- Optional props: SlideItems.itemElem and OutputTransition.transition default to
  undefined so callers needn't pass them.
- Type extensions: API_output_style gains outputStyle/styleOutputs; getCameraStream
  accepts an options arg.
- Casts for legacy/loose access: EditboxLines redo history, Lyrics line/text ids,
  Timer alignX, Media thumbnailPath, ChooseEmitter template value, stage Slide item.
- @ts-ignore for the plain-JS tablet VirtualList import (types unresolved under the
  server tsconfig).

svelte-check: 23 -> 0 errors (78 -> 0 across the whole effort). Unit tests still
green (251). No behavior changes.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
…red)

Flip strict:true in src/frontend/tsconfig.json, restoring the umbrella the Svelte 5
migration had disabled. With the 78 prior type errors already cleared, the only strict
sub-flag with real cost is noImplicitAny (~508 errors, mostly untyped params + index
access) — kept off as a separate migration, matching the project's prior stance. The
newly active flags (strictBindCallApply, strictPropertyInitialization,
useUnknownInCatchVariables, alwaysStrict) needed just one fix:
- ImportScripture.svelte: catch (err) is now `unknown`; String(err) before assigning to
  the string error field (preserves the displayed message).

svelte-check: 0 errors with strict:true. Unit tests green (251).

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
…63 remaining)

Working toward full strict (noImplicitAny enabled). This batch:
- CustomInput.svelte: typed the `value` prop + a couple of params / index casts (cleared
  the 46-error cascade from one untyped variable).
- 199 untyped function/callback params across 86 files annotated `: any` (matching the
  codebase's existing style), applied via a position-driven script verified against each
  reported identifier (0 mismatches).

svelte-check noImplicitAny: 508 -> 263. Unit tests green (251); Prettier clean.
Remaining: ~203 index-access, 34 variable, 14 binding, 7 revealed null-checks.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
… fixed, 42 left)

Continued noImplicitAny migration via position-driven scripts (each verified against the
reported identifier; 0 mismatches):
- 187 object index accesses wrapped as (obj as any)[key], with a leading ';' ASI guard
  where the cast starts a statement (the codebase is no-semicolons).
- 11 untyped `let` declarations annotated `: any`, clearing their cascading usages.

svelte-check noImplicitAny: 263 -> 42. Unit tests green (251); Prettier applied.
Remaining 42: 14 destructuring bindings, 16 member/array index, 7 revealed null-checks,
a few misc — to finish manually.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
…ck 0 errors

Final manual batch (42 + 7 follow-ons):
- Destructuring params typed `: any` (Calendar/Day getEventIcon, NDIStreams,
  EditboxLines cutInTwo, ChooseCamera, DeleteDuplicatedShows).
- Member/call/array index casts the scripts skipped (ChooseEmitter, MidiValues,
  Emitters, EditboxLines, edit+stage BoxStyle, ItemStyle, SlideFilters, Conditions,
  SelectTemplate, Variable, Variables), with `;` ASI guards on statement-leading casts.
- SelectElem: `let layout: any` clears its revealed null-checks; newly-revealed params
  typed (audioId, DisplayDuration updater).
- EditValues: guard `extension || ""`. YouTubePlayer: @ts-ignore (youtube-player ships
  no type declarations).

svelte-check: 508 -> 0 with strict:true (noImplicitAny enabled). Unit tests green (251);
Prettier clean. Full TypeScript strict mode is now on with zero errors.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
The electron side already opted into noImplicitAny / strictNullChecks /
strictFunctionTypes / noImplicitThis, but not the full strict umbrella. Flip
strict:true; the only new errors were 4 catch variables now typed `unknown`
(useUnknownInCatchVariables) — cast `(error as Error).message` in OAuth2Helper,
NdiReceiver and pptToShow (behavior unchanged).

tsc --noEmit (electron): 0 errors. Unit tests green (251); Prettier clean. Both the
frontend and electron tsconfigs are now on full strict with zero type errors.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
src/server/tsconfig.json was already strict:true, but nothing ran tsc against it (the
companion apps ship via esbuild and svelte-check uses the frontend config), so it had
5 latent errors. `tsc -p tsconfig.server.json` is now clean:
- HtmlSlideHelper: import `_store` (aliased as `stores`) instead of the non-existent
  `stores` export, and guard the optional `_store.SHOWS/SETTINGS` with `?.`.
- audioStream: type bufferQueue channels as Float32Array<ArrayBuffer> so copyToChannel
  accepts them (TS 5.7 ArrayBuffer typing).
- Blackmagic (electron files pulled in transitively, where macadam types resolve
  differently under the server config): cast getDeviceConfig result and the
  onFramePlayed access. Electron tsc unaffected (still 0).

All three TS projects now pass full strict with 0 errors: svelte-check (frontend),
tsc (electron), tsc (server). Unit tests green (251); Prettier clean.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
@jcfurey jcfurey force-pushed the split/5-typescript-strict branch from ef5dac8 to 5a3b6cb Compare June 16, 2026 02:42
@vassbo

vassbo commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

For what I can see it just adds : any explicitly in many cases to variables that already are detected as any.

@vassbo vassbo closed this Jun 16, 2026
jcfurey pushed a commit to jcfurey/freeshow that referenced this pull request Jun 16, 2026
…shback

HTML-fetched all 8 upstream PR pages (2026-06-16):
- CLOSED by vassbo: ChurchApps#3386 (Svelte 5 migration), ChurchApps#3387 (tests "not a benefit"),
  ChurchApps#3388 (strict types ":any churn"), ChurchApps#3389 (build/transition/regressions, silent).
- OPEN with pushback: ChurchApps#3385 (safe-eval "fine to use new Function, local app"),
  ChurchApps#3390 ("what's the improvement? remove Rebuild button"), ChurchApps#3391 ("not necessary").
- OPEN no comment: ChurchApps#3384 (security/deps).

Pattern: maintainer declining most modernization as unnecessary for a local
desktop app. Closing ChurchApps#3386 makes the transition fix moot upstream for now.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
jcfurey pushed a commit to jcfurey/freeshow that referenced this pull request Jun 16, 2026
…#3385/ChurchApps#3391 closed, ChurchApps#3384 sole open

Current upstream state (2026-06-16 ~18:35, via HTML fetch):
- ChurchApps#3390 MERGED ("Nice, great!") — first modernization PR landed.
- ChurchApps#3384 OPEN & ready, awaiting maintainer (sole live PR).
- ChurchApps#3385 CLOSED (jcfurey conceded the safe-eval; ESLint-9 set aside).
- ChurchApps#3391 CLOSED by vassbo (declined as unnecessary).
- ChurchApps#3386 CLOSED/parked — standalone migration ready, opens after ChurchApps#3384.
- ChurchApps#3387/ChurchApps#3388/ChurchApps#3389 closed.

https://claude.ai/code/session_01GnucxHgJuqNxYHbeHonfRm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants