fix(preload): bundle the preload so it loads under Electron's sandbox - #29
Merged
Conversation
Since ef10eda the preload imports CHANNELS from ./ipc-types, which tsc compiles to require("./ipc-types"). Electron sandboxes the renderer by default, and a sandboxed preload can only require Electron's own builtins — never a relative file. So the preload threw on load, contextBridge never ran, window.electronAPI was undefined, and the renderer crashed on its first property access. The window came up blank. Nothing caught it: the type-checker is happy, the unit tests never boot Electron, and `npm run pack` builds the app without launching it. Bundle the preload with esbuild as an IIFE instead, so it reaches the sandbox self-contained. tsc still type-checks it via tsconfig.main.json, so the single-source-of-truth IPC contract that ef10eda introduced is kept intact — this only changes how the file is emitted. No released version is affected; v1.4.0 and earlier predate the refactor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
killerwolf
force-pushed
the
fix/preload-sandbox-bundle
branch
from
September 7, 2026 13:41
999641a to
11a433c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The app on
mainopens to a blank windowrefactor(ipc): centralize the main/preload IPC contract(ef10eda) added a relative import to the preload:tsccompiles that torequire("./ipc-types"). Electron sandboxes the renderer by default, and a sandboxed preload can only require Electron's own builtins — never a relative file.So the preload throws on load →
contextBridgenever runs →window.electronAPIisundefined→ the renderer crashes on its first property access (onUpdateStatus). The window comes up empty.Reproduced against the real, untouched
dist/main.jsonmain:No released version is affected — v1.4.0 and earlier predate the refactor. But the next tag cut from
mainwould have shipped this.Why CI was green
The type-checker is happy (the import is valid TypeScript), the unit tests never boot Electron, and
npm run packbuilds the app without launching it. The PR that introduced this passed all checks.The fix
Bundle the preload with esbuild as an IIFE, so it arrives in the sandbox self-contained with no runtime
requireof local files.--format=iifespecifically: the CJS output referencesmodule, which a sandboxed preload also doesn't provide.tscstill type-checkselectron/preload.tsthroughtsconfig.main.json, so the single-source-of-truth IPC contract from ef10eda is kept intact — this only changes how the file is emitted.esbuildwas already present transitively via Vite; this promotes it to an explicit devDependency rather than relying on a transitive one.Verification
From a clean
rm -rf dist dist-react && npm run build:lint, format, typecheck and all 54 tests pass.
Follow-up worth doing separately
A smoke test that boots Electron and asserts
window.electronAPIis defined would close this gap permanently. Not included here to keep the fix minimal.🤖 Generated with Claude Code