npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

electron-ipc-extended

v1.0.0

Published

Provides cross-process type safety and clear event organization for Electron apps

Downloads

2

Readme

electron-ipc-extended

electron-ipc-extended is a typed-ipc wrapper for Electron (it's possible that more functionality will be added in the future, hence the name).

Installation

npm install electron-ipc-extended --save

Initialization

Initialize the IPC wrappers by passing Electron's IPC module and generic type arguments for own type definitions and type definitions from the other side.

main.ts


import { ipcMain } from 'electron';
import { createMainIpc } from 'electron-ipc-extended';
import type { RendererIpcActions } from './renderer';

export interface MainIpcActions {
  events: {
    // ...
  }

  commands: {
    // ...
  }

  calls: {
    // ...
  }

  // Note that there's no need to define an empty object for an action type
  // if there are no actions of this type.
}

const ipc = createMainIpc<MainIpcActions, RendererIpcActions>(ipcMain);

renderer.ts

import { ipcRenderer } from 'electron';
import { createRendererIpc } from 'electron-ipc-extended';
import type { MainIpcActions } from './main';

export interface RendererIpcActions {
  events: {
    // ...
  }

  calls: {
    // ...
  }

  // Note that currently commands are not supported in the renderer
}
const ipc = createRendererIpc<RendererIpcActions, MainIpcActions>(ipcRenderer);

Actions

There are 3 IPC action types available:

  • Events
  • Commands
  • Calls (unawaitable commands)

Events

An event is a plain IPC message that is dispatched from one source to many targets. Events are defined by the module that emits them. Events are sent with the send method and listened to with the on method.

main.ts

import { BrowserWindow, ipcMain } from 'electron';
import { createMainIpc } from 'electron-ipc-extended';
import type { RendererIpcActions } from './renderer';

export interface MainIpcActions {
  events: {
    'window/resize': [width: number, height: number]
  }
}

const ipc = createMainIpc<MainIpcActions, RendererIpcActions>(ipcMain);
const win = new BrowserWindow();

win.on('resize', () => {
  const { width, height } = win.getBounds();
  ipc.send(win.webContents, 'window/resize', width, height);
});

renderer.ts

import { ipcRenderer } from 'electron';
import { createRendererIpc } from 'electron-ipc-extended';
import type { MainIpcActions } from './main';

export interface RendererIpcActions { }

const ipc = createRendererIpc<RendererIpcActions, MainIpcActions>(ipcRenderer);

ipc.on('window/resize', (e, width, height) => {
  console.log(`Window resized: ${width} x ${height}`);
});

Commands

A command is handled in the target and can be invoked from multiple sources. Commands are defined by the module that handles them. Commands are invoked with the invoke method and handled with the handle method, invoke returns a Promise of the handler's return value.

main.ts

import { BrowserWindow, ipcMain } from 'electron';
import { createMainIpc } from 'electron-ipc-extended';
import type { RendererIpcActions } from './renderer';

export interface MainIpcActions {
  commands: {
    'window/isAlwaysOnTop': { params: [], returnVal: boolean }
  }
}

const ipc = createMainIpc<MainIpcActions, RendererIpcActions>(ipcMain);
const win = new BrowserWindow();

ipc.handle('window/isAlwaysOnTop', win.isAlwaysOnTop);

renderer.ts

import { ipcRenderer } from 'electron';
import { createRendererIpc } from 'electron-ipc-extended';
import type { MainIpcActions } from './main';

export interface RendererIpcActions { }

const ipc = createRendererIpc<RendererIpcActions, MainIpcActions>(ipcRenderer);

function isWindowAlwaysOnTop(): Promise<boolean> {
  return ipc.invoke('window/isAlwaysOnTop');
}

Note: Currently only the main process can receive commands (only the renderer can invoke them) - you can use calls instead (they are not awaitable though).

Calls

A call is a plain IPC message that is received in the target and can be called from many sources. Calls are defined by the module that receives them. Calls are called with the call method and received with the receive method.

From the runtime perspective, they are identical to events. Call handling functions are just aliases for event handling functions. However, from type and definitions perspective, calls are used in the same way as commands.

main.ts

import { BrowserWindow, ipcMain } from 'electron';
import { createMainIpc } from 'electron-ipc-extended';
import type { RendererIpcActions } from './renderer';

export interface MainIpcActions { }

const win = new BrowserWindow();

const ipc = createMainIpc<MainIpcActions, RendererIpcActions>(ipcMain);

ipc.call(win.webContents, 'menus/open', 'main-menu');

renderer.ts

import { ipcRenderer } from 'electron';
import { createRendererIpc } from 'electron-ipc-extended';
import type { MainIpcActions } from './main';

export interface RendererIpcActions {
  calls: {
    'menus/open': [menuId: string],
  }
}

const ipc = createRendererIpc<RendererIpcActions, MainIpcActions>(ipcRenderer);

ipc.receive('menus/open', (e, menuId) => {
  console.log(`Opening menu ${menuId}`);
});

Defining actions in a modular way

You will likely want to define actions for each module but the library accepts one type. You can use type intersection to join them (extending interfaces will not work - it will not be a deep merge).

export type MainIpcActions = ModuleAIpcActions & ModuleBIpcActions;
const ipc = createMainIpc<MainIpcActions, RendererIpcActions>(ipcMain);

Since there's a single IPC interface for all app modules, it is recommended to prefix action names with the name of the module, i.e. menus/open rather than openMenu

View Full Example

Possible future upgrades

No promises made, these are the possible future features in the order of priority:

  • Typed communication between main process and a Node.js child process
  • (typed) Communication between a Node.js child process and a renderer (indirect via main)
  • Invokeable commands for all channels
  • Passing uncaught errors thrown in command handlers to the other side, with custom error object re-hydration