Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/issue-145.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'electron-trpc': patch
---

Fix `handleIPCMessage` only sending replies to Electron's main frame.

pr: 146
33 changes: 16 additions & 17 deletions packages/electron-trpc/src/main/__tests__/handleIPCMessage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@ import * as trpc from '@trpc/server';
import { observable } from '@trpc/server/observable';
import { EventEmitter } from 'events';
import { handleIPCMessage } from '../handleIPCMessage';
import { IpcMainInvokeEvent } from 'electron';
import { IpcMainEvent } from 'electron';

interface MockEvent {
reply: MockedFunction<any>;
sender: {
isDestroyed: () => boolean;
on: (event: string, cb: () => void) => void;
send: MockedFunction<any>;
};
}
const makeEvent = (event: MockEvent) =>
event as unknown as IpcMainInvokeEvent & { sender: { send: MockedFunction<any> } };
const makeEvent = (event: MockEvent) => event as unknown as IpcMainEvent & Pick<MockEvent, 'reply'>;

const ee = new EventEmitter();

Expand Down Expand Up @@ -44,10 +43,10 @@ const testRouter = t.router({
describe('api', () => {
test('handles queries', async () => {
const event = makeEvent({
reply: vi.fn(),
sender: {
isDestroyed: () => false,
on: () => {},
send: vi.fn(),
},
});

Expand All @@ -69,8 +68,8 @@ describe('api', () => {
subscriptions: new Map(),
});

expect(event.sender.send).toHaveBeenCalledOnce();
expect(event.sender.send.mock.lastCall[1]).toMatchObject({
expect(event.reply).toHaveBeenCalledOnce();
expect(event.reply.mock.lastCall![1]).toMatchObject({
id: 1,
result: {
data: {
Expand All @@ -83,10 +82,10 @@ describe('api', () => {

test('does not respond if sender is gone', async () => {
const event = makeEvent({
reply: vi.fn(),
sender: {
isDestroyed: () => true,
on: () => {},
send: vi.fn(),
},
});

Expand All @@ -108,15 +107,15 @@ describe('api', () => {
subscriptions: new Map(),
});

expect(event.sender.send).not.toHaveBeenCalled();
expect(event.reply).not.toHaveBeenCalled();
});

test('handles subscriptions', async () => {
const event = makeEvent({
reply: vi.fn(),
sender: {
isDestroyed: () => false,
on: () => {},
send: vi.fn(),
},
});

Expand All @@ -138,12 +137,12 @@ describe('api', () => {
event,
});

expect(event.sender.send).not.toHaveBeenCalled();
expect(event.reply).not.toHaveBeenCalled();

ee.emit('test');

expect(event.sender.send).toHaveBeenCalledOnce();
expect(event.sender.send.mock.lastCall[1]).toMatchObject({
expect(event.reply).toHaveBeenCalledOnce();
expect(event.reply.mock.lastCall![1]).toMatchObject({
id: 1,
result: {
data: 'test response',
Expand All @@ -153,10 +152,10 @@ describe('api', () => {

test('subscription responds using custom serializer', async () => {
const event = makeEvent({
reply: vi.fn(),
sender: {
isDestroyed: () => false,
on: () => {},
send: vi.fn(),
},
});

Expand Down Expand Up @@ -203,12 +202,12 @@ describe('api', () => {
event,
});

expect(event.sender.send).not.toHaveBeenCalled();
expect(event.reply).not.toHaveBeenCalled();

ee.emit('test');

expect(event.sender.send).toHaveBeenCalledOnce();
expect(event.sender.send.mock.lastCall[1]).toMatchObject({
expect(event.reply).toHaveBeenCalledOnce();
expect(event.reply.mock.lastCall![1]).toMatchObject({
id: 1,
result: {
type: 'data',
Expand Down
6 changes: 3 additions & 3 deletions packages/electron-trpc/src/main/createIPCHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { AnyRouter, inferRouterContext } from '@trpc/server';
import { ipcMain } from 'electron';
import type { BrowserWindow, IpcMainInvokeEvent } from 'electron';
import type { BrowserWindow, IpcMainEvent } from 'electron';
import { handleIPCMessage } from './handleIPCMessage';
import { CreateContextOptions } from './types';
import { ELECTRON_TRPC_CHANNEL } from '../constants';
Expand All @@ -9,7 +9,7 @@ import { Unsubscribable } from '@trpc/server/observable';

type Awaitable<T> = T | Promise<T>;

const getInternalId = (event: IpcMainInvokeEvent, request: ETRPCRequest) => {
const getInternalId = (event: IpcMainEvent, request: ETRPCRequest) => {
const messageId = request.method === 'request' ? request.operation.id : request.id;
return `${event.sender.id}-${event.senderFrame.routingId}:${messageId}`;
};
Expand All @@ -29,7 +29,7 @@ class IPCHandler<TRouter extends AnyRouter> {
}) {
windows.forEach((win) => this.attachWindow(win));

ipcMain.on(ELECTRON_TRPC_CHANNEL, (event: IpcMainInvokeEvent, request: ETRPCRequest) => {
ipcMain.on(ELECTRON_TRPC_CHANNEL, (event: IpcMainEvent, request: ETRPCRequest) => {
handleIPCMessage({
router,
createContext,
Expand Down
6 changes: 3 additions & 3 deletions packages/electron-trpc/src/main/handleIPCMessage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { callProcedure, TRPCError } from '@trpc/server';
import type { AnyRouter, inferRouterContext } from '@trpc/server';
import type { TRPCResponseMessage } from '@trpc/server/rpc';
import type { IpcMainInvokeEvent } from 'electron';
import type { IpcMainEvent } from 'electron';
import { isObservable, Unsubscribable } from '@trpc/server/observable';
import { CreateContextOptions } from './types';
import { getTRPCErrorFromUnknown, transformTRPCResponseItem } from './utils';
Expand All @@ -20,7 +20,7 @@ export async function handleIPCMessage<TRouter extends AnyRouter>({
createContext?: (opts: CreateContextOptions) => Promise<inferRouterContext<TRouter>>;
internalId: string;
message: ETRPCRequest;
event: IpcMainInvokeEvent;
event: IpcMainEvent;
subscriptions: Map<string, Unsubscribable>;
}) {
if (message.method === 'subscription.stop') {
Expand All @@ -43,7 +43,7 @@ export async function handleIPCMessage<TRouter extends AnyRouter>({

const respond = (response: TRPCResponseMessage) => {
if (event.sender.isDestroyed()) return;
event.sender.send(ELECTRON_TRPC_CHANNEL, transformTRPCResponseItem(router, response));
event.reply(ELECTRON_TRPC_CHANNEL, transformTRPCResponseItem(router, response));
};

try {
Expand Down