Repository navigation
electron-trpc 1.0: tRPC 11, disposable IPC handler, automatic preload - #231
Merged
Merged
Conversation
This reverts commit 4e177da.
- Replace shell.nix with a flake (Node 24, pnpm 12) and `use flake`. - Drop corepack from scripts and move pnpm settings into pnpm-workspace.yaml. - Build with tsdown instead of four vite configs and dts-bundle-generator, emitting per-format type declarations listed first in `exports`. - Upgrade to TypeScript 7, Vitest 5, Electron 45 and Playwright 1.64, and remove unused dev dependencies. - Skip IPC messages and navigations whose frame is already gone, now that Electron types `senderFrame` and navigation `frame` as nullable. - Run CI on Node 24 with current action versions, and set up the Electron sandbox for every example.
Electron 45 no longer downloads its binary on install, so fetch it with `pnpm install:electron` before running e2e tests or configuring its sandbox in CI, instead of letting parallel test workers race to download it. The dev shell gives nix-ld the libraries that binary loads, along with a matching dynamic linker, so e2e tests run the same Electron locally as in CI.
The main process tracks everything a renderer has in flight in a tree of disposable scopes: the handler, each webContents, each renderer-side client and each operation. Disposing a scope aborts what is below it, so requests end when their page navigates away, reloads, crashes or closes, or when the handler itself is disposed. - Support tRPC 11, including async generator subscriptions, tracked events and cancellation through the procedure's signal, and require Electron 35. - Identify renderer-side clients, so pages with several clients get the right responses (#201). - Register operations before creating context, so a request stopped early can't leave a subscription running. - Clean up on committed cross-document navigations only, matching frames by frame tree node instead of by a routing id prefix. - Only listen for responses in the renderer while requests are in flight. - Register the preload script with `registerPreloadScript`, on configurable sessions, and unregister it when the handler is disposed. - Typecheck main, renderer and tests separately, and test both halves against each other through a fake Electron. - Update the examples to tRPC 11, React 19 and Vite 8.
The preload script exposes electron-trpc to every page in its sessions, so apps that load content they don't trust need a way to refuse its requests. `allow` is checked for every request, and rejected requests fail with a `FORBIDDEN` error rather than never settling.
Exit pre-release mode, and rewrite the changesets for people upgrading from 0.7.1, who never had the alpha's `autoRegisterPreload` option.
… missing. Packaged webpack apps, like electron-forge's, ship only the webpack output, so the preload script was never found and renderers silently had no `electronTRPC`. A `webpack` export condition gives webpack the ES module build, whose `new URL(..., import.meta.url)` reference makes webpack copy the script into its output. `createIPCHandler` now uses the first location where the script exists, and otherwise throws an error explaining how to expose electron-trpc from the app's own preload script, which the README documents along with when it's needed. The basic-webpack example runs its e2e test from a copy of only the webpack output, as a packaged app would.
A page could send anything over the electron-trpc channel, and reading fields off a message like `null` threw in the main process, before `allow` could reject the sender. Check a message's shape before handling it.
Electron has no event for an iframe being removed, so its requests kept running. Stop a client once it has something to send to a frame that's gone, and sweep up clients of gone frames whenever their window sends a message or creates a frame.
`frameToken` only exists from Electron 38, so on 35-37 every frame in a process compared equal, and a frame could stop a request from another frame that used the same client id. A frame tree node id identifies one frame on every supported version, so compare that instead.
Merged
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.
electron-trpc 1.0 supports tRPC 11 and Electron 35 or later, and rebuilds the IPC handler around disposable scopes. It replaces the
nextbranch's 1.0 alphas: this branch includesnext's history and exits pre-release mode, so merging it and then the Version Packages PR releases1.0.0aslatest.Breaking changes
^11.10.0) and Electron 35 or later. Transformers are passed toipcLink({ transformer })instead of the client, as with tRPC 11's other links.createIPCHandlerregisters electron-trpc's preload script itself, so apps no longer need one. Apps with their own preload script passpreload: falseand importexposeElectronTRPCfromelectron-trpc/preload.createIPCHandlerreturns aDisposablehandler that serves every window, replacing thewindowsoption andattachWindow/detachWindow.Architecture
The main process tracks each renderer's requests in a tree of disposable scopes: the handler, each webContents, each renderer-side client and each request. Every request gets an
AbortController, which tRPC 11 passes to the procedure, so disposing a scope cancels everything below it. Requests end when their page navigates away, reloads, crashes or closes, or when the handler is disposed.Also new
allow(event)rejects requests by sender with aFORBIDDENerror, for apps that load content they don't trust.preload: { sessions }registers the preload script on sessions other than the default.createIPCHandlerthrows an error explaining the fix. webpack builds, including electron-forge's, copy the script automatically.Fixed
moduleResolution: bundler(Error when used with moduleResolution: Bundler #229).instanceof TRPCClientErrorfailing in the renderer (Unable to use instanceof TRPCClientError #209).tRPC 11 support also covers #184 and #227.
Tooling
A nix flake (Node 24, pnpm 12) replaces
shell.nix, the library builds with tsdown, and the toolchain moves to TypeScript 7, Vitest 5, Electron 45 and Playwright 1.64. CI runs on Node 24. The examples move to tRPC 11, React 19 and Vite 8, and a new webpack example joins them.Testing
basic-reactchecks that a reload leaves exactly one subscription in the main process, andbasic-webpackruns from a copy of only its webpack output, as a packaged app would.asarand run against this build.