diff --git a/.changeset/issue-145.md b/.changeset/issue-145.md new file mode 100644 index 00000000..fb1dc332 --- /dev/null +++ b/.changeset/issue-145.md @@ -0,0 +1,7 @@ +--- +'electron-trpc': patch +--- + +Fix `handleIPCMessage` only sending replies to Electron's main frame. + +pr: 146 diff --git a/packages/electron-trpc/src/main/__tests__/handleIPCMessage.test.ts b/packages/electron-trpc/src/main/__tests__/handleIPCMessage.test.ts index 3b7aafb1..a6b02e82 100644 --- a/packages/electron-trpc/src/main/__tests__/handleIPCMessage.test.ts +++ b/packages/electron-trpc/src/main/__tests__/handleIPCMessage.test.ts @@ -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; sender: { isDestroyed: () => boolean; on: (event: string, cb: () => void) => void; - send: MockedFunction; }; } -const makeEvent = (event: MockEvent) => - event as unknown as IpcMainInvokeEvent & { sender: { send: MockedFunction } }; +const makeEvent = (event: MockEvent) => event as unknown as IpcMainEvent & Pick; const ee = new EventEmitter(); @@ -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(), }, }); @@ -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: { @@ -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(), }, }); @@ -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(), }, }); @@ -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', @@ -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(), }, }); @@ -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', diff --git a/packages/electron-trpc/src/main/createIPCHandler.ts b/packages/electron-trpc/src/main/createIPCHandler.ts index 67801f44..658b52e7 100644 --- a/packages/electron-trpc/src/main/createIPCHandler.ts +++ b/packages/electron-trpc/src/main/createIPCHandler.ts @@ -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'; @@ -9,7 +9,7 @@ import { Unsubscribable } from '@trpc/server/observable'; type Awaitable = T | Promise; -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}`; }; @@ -29,7 +29,7 @@ class IPCHandler { }) { 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, diff --git a/packages/electron-trpc/src/main/handleIPCMessage.ts b/packages/electron-trpc/src/main/handleIPCMessage.ts index 325807e8..d7347b1e 100644 --- a/packages/electron-trpc/src/main/handleIPCMessage.ts +++ b/packages/electron-trpc/src/main/handleIPCMessage.ts @@ -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'; @@ -20,7 +20,7 @@ export async function handleIPCMessage({ createContext?: (opts: CreateContextOptions) => Promise>; internalId: string; message: ETRPCRequest; - event: IpcMainInvokeEvent; + event: IpcMainEvent; subscriptions: Map; }) { if (message.method === 'subscription.stop') { @@ -43,7 +43,7 @@ export async function handleIPCMessage({ 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 {