jsonnull / electron-trpc Goto Github PK
View Code? Open in Web Editor NEWBuild type-safe Electron inter-process communication using tRPC
Home Page: https://www.electron-trpc.dev
License: MIT License
Build type-safe Electron inter-process communication using tRPC
Home Page: https://www.electron-trpc.dev
License: MIT License
I would like to add a route that toggles DevTools and in order to do that I need access to the BrowserWindow that the request was made from. It doesn't look like there is a way for this, which is kind of strange as I'm seeing loads of use-cases where you would need the instance of the BrowserWindow. Or have I missed something?
EDIT: Found the createContext
property :)
I just noticed that the npm page doesn't have the necessary details for the electron-trpc
package since the README is in the root and I haven't included additional meta in the published package's package.json.
In the beta versions of trpc v10, @trpc/react@next
has a peer pin on beta 20 instead of beta 25 for the other @trpc
packages. In order to stay on @next
for all these packages, I'm leaving strict peer dependencies off for the project.
When trpc v10 releases, let's get back to strict peer deps.
I believe it is coming from here due to win
being destroyed:
Here's the full error:
respond: (response) => _event.sender.send(ELECTRON_TRPC_CHANNEL, response)
rather than send to all windows
otherwise it would have problem when using with multiple windows and there is duplicate id used and this happens frequently
Since Electron 20, the sandbox
option of window webPreferences
has been set to true by default. This means that preload scripts can only use a small subset of the Electron and Node APIs. Specifically, require
is polyfilled with a version that will only allow requiring a small subset of node core modules.
This causes a problem for this lib, since we need to require electron-trpc/main
in the preload script, and then call exposeElectronTRPC()
.
Disabling the sandbox of course resolves this issue, but at the cost of losing the security benefits of sandboxing.
After a quick think about this I have two potential solutions:
contextBridge
code. This should be a documentation only change, perhaps based on a sample vite-plugin-electron
config.ELECTRON_TRPC_CHANNEL
and make it up to the user to configure an appropriate and consistent channel across main, preload, and renderer. Then supply a copy/paste snippet for the preload file that only uses the contextBridge
API. It would be helpful to export the RendererGlobalElectronTRPC
type so consumers could at least match up with some sort of package API.There may be other options I haven't considered.
Thanks for creating such a useful lib!
At first, that entire code works if I copy/paste it into my app. When I tried to use the npm package I faced with that issue:
I am using electron-react-boilerplate
I think it is caused because I am using import { ipcLink } from 'electron-trpc';
. When I am trying to use import { ipcLink } from 'electron-trpc/renderer';
I am getting:
Module not found: Error: Package path ./renderer is not exported from package [...]/node_modules/electron-trpc
I'm consulting a query that does not have parameters this way:
trpc.endpoint.useQuery(undefined);
And it is never responding.
My current workaround is to send an empty object like this:
trpc.endpoint.useQuery({});
Is there a way to get a more detailed stack trace for an error that occurs during an electron-trpc call? For example, a Zod error results in this:
Matches.tsx:267 Uncaught TRPCClientError: [
{
"code": "invalid_type",
"expected": "object",
"received": "null",
"path": [],
"message": "Expected object, received null"
}
]
at _y.from (renderer.mjs:60:10)
at Object.next (renderer.mjs:487:23)
at V.M (renderer.mjs:469:21)
at renderer.mjs:447:21
There's no line numbers in the stack trace that indicate where the error actually came from.
I have a use case that involves using iframes
and tRPC to communicate between each frame and the main thread. After spending some time debugging it seems that handleIPCMessage
replies to the sender using event.sender.send
which will always reply to the main frame.
To send an asynchronous message back to the sender, you can use event.reply(...). This helper method will automatically handle messages coming from frames that aren't the main frame (e.g. iframes) whereas event.sender.send(...) will always send to the main frame.
I think the solution here is pretty simple. handleIPCMessage
should be able to use event.reply
to respond to incoming events as stated by the Electron docs. This would also involve updating the types on the event
parameters to use ipcMainEvent
instead of IpcMainInvokeEvent
.
Current implementation
import { IpcMainInvokeEvent } from 'electron'
export async function handleIPCMessage<TRouter extends AnyRouter>({
router,
createContext,
internalId,
message,
event,
subscriptions,
}: {
router: TRouter;
createContext?: (opts: CreateContextOptions) => Promise<inferRouterContext<TRouter>>;
internalId: string;
message: ETRPCRequest;
event: IpcMainInvokeEvent; // OLD
subscriptions: Map<string, Unsubscribable>;
}) {
// ...
}
Suggested change
import { IpcMainEvent } from 'electron'
export async function handleIPCMessage<TRouter extends AnyRouter>({
router,
createContext,
internalId,
message,
event,
subscriptions,
}: {
router: TRouter;
createContext?: (opts: CreateContextOptions) => Promise<inferRouterContext<TRouter>>;
internalId: string;
message: ETRPCRequest;
event: IpcMainEvent; // NEW
subscriptions: Map<string, Unsubscribable>;
}) {
// ...
}
Would be more than happy to make a PR with the suggested changes above.
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates are currently rate-limited. Click on a checkbox below to force their creation now.
@tanstack/react-query
, superjson
, zod
)These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
@changesets/changelog-github
, @changesets/cli
, @playwright/test
, @tanstack/react-query
, @types/debug
, @types/node
, @types/react
, @types/react-dom
, @vitejs/plugin-react
, @vitest/coverage-v8
, dts-bundle-generator
, superjson
, typescript
, unocss
, vite
, vite-plugin-commonjs-externals
, vite-plugin-electron
, vitepress
, vitest
, vue
, zod
)@trpc/client
, @trpc/react-query
, @trpc/server
)@tanstack/react-query
, superjson
)@tanstack/react-query
, @vitest/coverage-v8
, dts-bundle-generator
, prettier
, superjson
, vite
, vitest
).github/workflows/lint.yml
actions/checkout v3
pnpm/action-setup v2
actions/setup-node v3
.github/workflows/release.yml
actions/checkout v3
pnpm/action-setup v2
actions/setup-node v3
changesets/action v1
actions/checkout v3
pnpm/action-setup v2
actions/setup-node v3
changesets/action v1
.github/workflows/test.yml
actions/checkout v3
pnpm/action-setup v2
actions/setup-node v3
codecov/codecov-action v3
actions/checkout v3
pnpm/action-setup v2
actions/setup-node v3
examples/basic-react-superjson/package.json
@tanstack/react-query ^4.29.14
@trpc/client 10.33.1
@trpc/react-query 10.33.1
@trpc/server 10.33.1
electron ^25.1.1
react ^18.2.0
react-dom ^18.2.0
superjson ^1.12.3
zod ^3.21.4
@types/node ^20.3.1
@types/react ^18.2.13
@types/react-dom ^18.2.6
@vitejs/plugin-react ^4.0.0
vite ^4.3.9
vite-plugin-electron ^0.12.0
examples/basic-react/package.json
@tanstack/react-query ^4.29.14
@trpc/client 10.33.1
@trpc/react-query 10.33.1
@trpc/server 10.33.1
electron ^25.1.1
react ^18.2.0
react-dom ^18.2.0
zod ^3.21.4
@types/node ^20.3.1
@types/react ^18.2.13
@types/react-dom ^18.2.6
@vitejs/plugin-react ^4.0.0
vite ^4.3.9
vite-plugin-electron ^0.12.0
package.json
@changesets/changelog-github ^0.4.8
@changesets/cli ^2.26.1
@playwright/test ^1.35.1
prettier ^2.8.8
typescript ^5.1.3
unocss ^0.54.0
vite ^4.3.9
vitepress 1.0.0-beta.7
vue ^3.3.4
node >=18
pnpm >=8
pnpm 8.6.11
packages/electron-trpc/package.json
debug ^4.3.4
@tanstack/react-query ^4.29.14
@trpc/client 10.33.1
@trpc/server 10.33.1
@types/debug ^4.1.8
@types/node ^20.3.1
@vitest/coverage-v8 ^0.34.0
builtin-modules ^3.3.0
dts-bundle-generator ^8.0.1
electron ^25.1.1
react ^18.2.0
react-dom ^18.2.0
superjson ^1.12.3
vite ^4.3.9
vite-plugin-commonjs-externals ^0.1.3
vitest ^0.34.0
zod ^3.21.4
@trpc/client >10.0.0
@trpc/server >10.0.0
electron >19.0.0
Currently createContext
must always return a Promise. Instead it should return an Awaitable.
I noticed a crash when using this lib with superjson
. The stack trace indicated that superjson.deserialize
was being called with the input being undefined.
A little digging and console logging led me to notice that this was happening for subscription.stop
methods (which are being emitted here: https://github.com/jsonnull/electron-trpc/blob/main/packages/electron-trpc/src/renderer/ipcLink.ts#L77-L80) because they don't have an input
property.
Thus when the handleIPCOperation
function picks this up on the main thread, it is calling deserialize
on an empty object (here: https://github.com/jsonnull/electron-trpc/blob/main/packages/electron-trpc/src/main/handleIPCOperation.ts#L23).
Seems like this function needs to look for the presence of operation.input
before calling deserialize
, or else filter by the operation method if it is a stable API.
One of the breaking changes involves transformers, the error manifests itself on client side as:
Uncaught TypeError: Cannot read properties of undefined (reading 'serialize')
With this line being at fault:
I've forked the repository and resolved the compatibility issues with the next version of TRPC: mat-sz@994bd41
Since this is an unstable version of TRPC, I'm not submitting this as a pull request. If you'd like me to submit that in the future when 11.x.x becomes stable, please let me know; otherwise I'll continue maintaining this as a fork.
Hi there!
As discussed on Twitter, I'd like to help move this package to support v10 of tRPC.
I've gotten it to work on my starter that ties together vite, electron, tRPC, and Prisma.
Currently, that uses a bunch of copy-pasted internal tRPC source code.
I saw you PRd trpc/trpc#2397 for v9, so something similar for v10 is probably needed if we want to avoid rewriting a bunch of tRPCs sourcecode.
I can PR a proof-of-concept at the end of the week if you're interested!
Or wait until v10 is out of beta, let me know!
When I'm trying to run my jest tests I got this error:
Test suite failed to run
Configuration error:
Could not locate module electron-trpc/renderer mapped as:
node_modules/electron-trpc/dist/renderer.
Please check your configuration for these entries:
{
"moduleNameMapper": {
"/^electron\-trpc\/renderer$/": "node_modules/electron-trpc/dist/renderer"
},
"resolver": undefined
}
2 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3 | import { render, RenderOptions } from '@testing-library/react';
> 4 | import { ipcLink } from 'electron-trpc/renderer';
| ^
5 | import { ReactElement, useState } from 'react';
6 | import { MemoryRouter } from 'react-router-dom';
7 |
at createNoMappedModuleFoundError (../../node_modules/jest-resolve/build/resolver.js:759:17)
at Object.<anonymous> (test-utils/TestWrapper.tsx:4:1)
Any ideas on how to resole this issue?
If is use superjson
as transformer, this procedure:
open: publicProcedure.subscription(async () => {
return observable<{ open: boolean }>((emit) => {
const setFileopenVisible = (data: boolean) => {
console.log("setFileopenVisible", data);
emit.next({ open: data });
};
ee.on("setFileopenVisible", setFileopenVisible);
return () => {
ee.off("setFileopenVisible", setFileopenVisible);
};
})
}),
returns undefined when called here:
trpc.ui.openFileVisible.open.useSubscription(undefined, {
onData: (data) => {
console.log("data", data);
},
});
The data is properly returned, if I remove the transformer from the api and the client.
Using electron-trpc version 0.4.2.
electron-trpc/src/renderer/ipcLink.ts
Line 1 in ef9df5e
seperate line for
import type { TRPCLink } from "@trpc/client";
Hi,
For some reason all the imports in the README fail.
// All of the following give "Cannot find module..." error
import { createIPCHandler } from "electron-trpc/main";
import { exposeElectronTRPC } from "electron-trpc/main";
import { ipcLink } from "electron-trpc/renderer";
Importing from /dist
or /src
compiles, but fails at runtime with the same error.
I'm using version 0.2.1 of electron-trpc.
Using your example repo, I noticed that subscriptions do not clean up. Eventually I get the following error:
[1] (node:22811) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 output listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit
[1] (Use `Electron --trace-warnings ...` to show where the warning was created)
[1] (node:22811) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 destroyed listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit
[1] subscribed
Hi,
In my application I have two windows with different type of routers. For both windows I'm trying to register IPC handlers using createIPCHandler({router: router1, windows: [win1]})
and createIPCHandler({router: router2, windows: [win2]})
.
When I try to invoke some query from win1, I'm getting the following error:
TRPCClientError: No "query"-procedure on path "getSomething"
I assume it happens because both routers replies to the same query simultaneously which brings to race condition. In my case, router2
is trying to handle a query that can be handled only by router1
.
It would be nice to check event.source
inside ipcMain.on message handler and process only those messages which come from BrowserWindow passed in windows
property of createIPCHandler
call.
Sometimes during development it's necessary to fully reload the page. I created this little subscription procedure to see whether there is a risk for memory leaks. And yes, there is. Check the code below:
let count = 0;
export const testSubscription = publicProcedure.subscription(async () => {
return observable<string>(() => {
count ++;
console.log(count);
return () => {
count --;
};
});
});
Whenever this procedure is being subscribed to, count
will increment or decrement to keep track of amount of active subscriptions. However, if you do a page reload, there is no cleanup process being made and count
keeps increasing.
This is a discussion around what the roadmap to v1.0.0 looks like. I really enjoy this library and believe it's the best way to work with ipc
communication. I would like to contribute more to this project but don't know what the roadmap looks like.
Was hoping we could start a discussion specifically with @jsonnull around what work needs to be done to push this to v1.0.0. More than happy to help with planning, implementation, etc...
Thanks in advance for all the work on this project thus far and looking forward to future discussions!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.