Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/allow-requests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'electron-trpc': minor
---

Add an `allow` option to `createIPCHandler`, which decides whether to handle each request, e.g. based on `event.senderFrame.url`. Rejected requests fail with a `FORBIDDEN` error.
5 changes: 5 additions & 0 deletions .changeset/automatic-preload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'electron-trpc': major
---

Breaking change: `createIPCHandler` registers electron-trpc's preload script itself, on the default session or the sessions passed as `preload: { sessions }`, so apps no longer need one. Apps that expose electron-trpc from their own preload script pass `preload: false` and import `exposeElectronTRPC` from `electron-trpc/preload` instead of `electron-trpc/main`. If a build bundles electron-trpc without its preload script, `createIPCHandler` throws an error explaining how to expose it from your own preload script instead.
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch"
"updateInternalDependencies": "patch",
"ignore": ["examples/*", "www"]
}
5 changes: 5 additions & 0 deletions .changeset/disposable-ipc-handler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'electron-trpc': major
---

Breaking change: `createIPCHandler` returns a `Disposable` handler that serves every window, replacing the `windows` option and `attachWindow`/`detachWindow`. Requests are cancelled when their page navigates away, reloads, crashes or closes, and disposing the handler cancels everything still running.
5 changes: 5 additions & 0 deletions .changeset/ipc-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'electron-trpc': patch
---

Fix responses reaching the wrong client when a page has several (#201), types not resolving with `moduleResolution: bundler` (#229), `instanceof TRPCClientError` failing in the renderer (#209), and electron-trpc not working in sandboxed renderers (#116).
12 changes: 12 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mode": "exit",
"tag": "alpha",
"initialVersions": {
"examples/basic-react": "0.0.16",
"examples/basic-react-superjson": "0.0.16",
"examples/basic-vanilla-esm": "0.0.16",
"electron-trpc": "0.6.1",
"www": "0.0.1"
},
"changesets": []
}
5 changes: 5 additions & 0 deletions .changeset/trpc-11-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'electron-trpc': major
---

Breaking change: Requires tRPC 11 and Electron 35 or later. Pass transformers to `ipcLink` instead of the client, as with tRPC 11's other links.
2 changes: 1 addition & 1 deletion .envrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
use nix
use flake
14 changes: 5 additions & 9 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,14 @@ env:

jobs:
lint:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ['18.x']
os: [ubuntu-latest]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
node-version-file: .node_version
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: '18.x'
node-version-file: .node_version
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- uses: changesets/action@v1
Expand Down
41 changes: 20 additions & 21 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,37 @@ env:

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ['18.x']
os: [ubuntu-latest]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
node-version-file: .node_version
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm build:code
- run: pnpm test:ci
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v7

test-e2e:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ['18.x']
os: [ubuntu-latest]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
node-version-file: .node_version
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm build:code
# Electron downloads its binary on first use, so fetch it before configuring its sandbox.
- run: pnpm install:electron
- name: Set up Electron sandbox
run: |
for sandbox in examples/*/node_modules/electron/dist/chrome-sandbox; do
sudo chown root "$sandbox" && sudo chmod 4755 "$sandbox"
done
- run: xvfb-run --auto-servernum -- pnpm test:e2e
14 changes: 5 additions & 9 deletions .github/workflows/www.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ env:

jobs:
deploy:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: ['18.x']
os: [ubuntu-latest]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: actions/setup-node@v4
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
node-version-file: .node_version
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm docs:build
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ dist/
dist-electron/
docs/.vitepress/cache/
coverage/
packages/electron-trpc/renderer.d.ts
packages/electron-trpc/main.d.ts
test-results/
.webpack/
2 changes: 1 addition & 1 deletion .node_version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18
24
2 changes: 0 additions & 2 deletions .npmrc

This file was deleted.

2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
**/dist/
**/dist-electron/
**/.webpack/
**/coverage/

pnpm-lock.yaml
**/test-results/
78 changes: 54 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

## Installation

electron-trpc requires Electron 35 or later and tRPC 11.

```sh
# Using pnpm
pnpm add electron-trpc
Expand All @@ -36,46 +38,74 @@ npm install --save electron-trpc

## Basic Setup

1. Add your tRPC router to the Electron main process using `createIPCHandler`:
1. Add your tRPC router to the Electron main process using `createIPCHandler`. It also registers a preload script that exposes electron-trpc to your pages, so create it before loading any:

```ts
import { app } from 'electron';
import { app, BrowserWindow } from 'electron';
import { createIPCHandler } from 'electron-trpc/main';
import { router } from './api';

app.on('ready', () => {
const win = new BrowserWindow({
webPreferences: {
// Replace this path with the path to your preload file (see next step)
preload: 'path/to/preload.js',
},
});

createIPCHandler({ router, windows: [win] });
createIPCHandler({ router });

const win = new BrowserWindow();
win.loadFile('index.html');
});
```

2. Expose the IPC to the render process from the [preload file](https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts):
The handler serves every window and cleans up after pages that navigate away or close. It is `Disposable`, so `handler[Symbol.dispose]()` stops it and ends any running subscriptions.

2. When creating the client in the render process, use `ipcLink` instead of the HTTP or WebSocket links. If your router uses a transformer, pass it to the link:

```ts
import { exposeElectronTRPC } from 'electron-trpc/main';
import { createTRPCClient } from '@trpc/client';
import { ipcLink } from 'electron-trpc/renderer';
import superjson from 'superjson';
import type { AppRouter } from './api';

process.once('loaded', async () => {
exposeElectronTRPC();
export const client = createTRPCClient<AppRouter>({
links: [ipcLink({ transformer: superjson })],
});
```

> Note: `electron-trpc` depends on `contextIsolation` being enabled, which is the default.
3. Now you can use the client in your render process as you normally would, for example with `@trpc/tanstack-react-query`.

3. When creating the client in the render process, use the `ipcLink` (instead of the HTTP or batch HTTP links):
## Preload Script

```ts
import { createTRPCProxyClient } from '@trpc/client';
import { ipcLink } from 'electron-trpc/renderer';
`createIPCHandler` registers electron-trpc's preload script on Electron's default session, so your app doesn't need one. If your windows use other sessions, pass them instead:

export const client = createTRPCProxyClient({
links: [ipcLink()],
});
```
```ts
createIPCHandler({ router, preload: { sessions: [session.fromPartition('persist:app')] } });
```

The preload script is a file in the electron-trpc package, so your packaged app has to include it. That works without any setup when:

- Your app ships electron-trpc in `node_modules`, for example as a `dependency` packaged by electron-builder, whether or not your main process is bundled.
- Your main process is bundled with webpack, including electron-forge's webpack template. webpack copies the preload script into its output.

4. Now you can use the client in your render process as you normally would (e.g. using `@trpc/react`).
If your build bundles electron-trpc into the main process without including its preload script, `createIPCHandler` throws an error saying it couldn't find the preload script. This usually happens when electron-trpc is a `devDependency`, or when your packager only ships your bundled code. It usually only shows up in packaged apps, so test a packaged build of your app.

To fix it, expose electron-trpc from your own preload script. Pass `preload: false` and call `exposeElectronTRPC` there:

```ts
// main process
createIPCHandler({ router, preload: false });

// preload script
import { exposeElectronTRPC } from 'electron-trpc/preload';

exposeElectronTRPC();
```

> Note: `electron-trpc` depends on `contextIsolation` being enabled, which is the default.

## Restricting Access

The preload script exposes electron-trpc to every page in its sessions, including any remote content your app loads. To only handle requests from pages you trust, check the sender with `allow`. Requests it rejects fail with a `FORBIDDEN` error:

```ts
createIPCHandler({
router,
allow: (event) => new URL(event.senderFrame?.url ?? '').protocol === 'app:',
});
```
Loading
Loading