@@ -308,6 +308,83 @@ This pattern provides:
3083085 . ** Add router** to ` src/main/trpc/router.ts `
3093096 . ** Use in renderer** via ` trpcReact ` hooks
310310
311+ ## Events (tRPC Subscriptions)
312+
313+ For pushing real-time updates from main to renderer, use tRPC subscriptions with typed event emitters.
314+
315+ ### 1. Define Events in schemas.ts
316+
317+ Use a const object for event names and an interface for payloads:
318+
319+ ``` typescript
320+ // src/main/services/my-service/schemas.ts
321+ export const MyServiceEvent = {
322+ ItemCreated: " item-created" ,
323+ ItemDeleted: " item-deleted" ,
324+ } as const ;
325+
326+ export interface MyServiceEvents {
327+ [MyServiceEvent .ItemCreated ]: { id: string ; name: string };
328+ [MyServiceEvent .ItemDeleted ]: { id: string };
329+ }
330+ ```
331+
332+ ### 2. Extend TypedEventEmitter in Service
333+
334+ ``` typescript
335+ // src/main/services/my-service/service.ts
336+ import { TypedEventEmitter } from " ../../lib/typed-event-emitter" ;
337+ import { MyServiceEvent , type MyServiceEvents } from " ./schemas" ;
338+
339+ @injectable ()
340+ export class MyService extends TypedEventEmitter <MyServiceEvents > {
341+ async createItem(name : string ) {
342+ const item = { id: " 123" , name };
343+ // TypeScript enforces correct event name and payload shape
344+ this .emit (MyServiceEvent .ItemCreated , item );
345+ return item ;
346+ }
347+ }
348+ ```
349+
350+ ### 3. Create Subscriptions in Router
351+
352+ Use a helper to reduce boilerplate:
353+
354+ ``` typescript
355+ // src/main/trpc/routers/my-router.ts
356+ import { on } from " node:events" ;
357+ import { MyServiceEvent , type MyServiceEvents } from " ../../services/my-service/schemas" ;
358+
359+ function subscribe<K extends keyof MyServiceEvents >(event : K ) {
360+ return publicProcedure .subscription (async function * (opts ) {
361+ const service = getService ();
362+ for await (const [payload] of on (service , event , { signal: opts .signal })) {
363+ yield payload as MyServiceEvents [K ];
364+ }
365+ });
366+ }
367+
368+ export const myRouter = router ({
369+ // ... queries and mutations
370+ onItemCreated: subscribe (MyServiceEvent .ItemCreated ),
371+ onItemDeleted: subscribe (MyServiceEvent .ItemDeleted ),
372+ });
373+ ```
374+
375+ ### 4. Subscribe in Renderer
376+
377+ ``` typescript
378+ // React component
379+ trpcReact .my .onItemCreated .useSubscription (undefined , {
380+ enabled: true ,
381+ onData : (item ) => {
382+ // item is typed as { id: string; name: string }
383+ console .log (" Created:" , item );
384+ },
385+ });
386+ ```
387+
311388## Code Style
312389
313390See [ CLAUDE.md] ( ./CLAUDE.md ) for linting, formatting, and import conventions.
0 commit comments