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

simple-electron-ipc

v2.0.1

Published

an easy way to use electron ipc, get a response via promise, event support progress feedback

Downloads

30

Readme

📝 Table of Contents

Features

  • Simple API: on emit and off are all you need
  • Responsible: emit will return a promise with the response from the other side
  • Progress-able: get response with progress easily
  • Tinny: less than 4kb gzipped(even smaller with tree-shaking), no external dependencies required
  • Consistency: same api every where
  • Typescript support: this utility is written in typescript, has type definition inborn

It also has a browser version that can simplify cross window / js context messaging, check Duplex-Message for more details.

Install

using yarn

yarn add simple-electron-ipc

or npm

npm install simple-electron-ipc -S

Example

The following example shows you how to use it to to communicate between electron app's main process and renderer process.

in main process:

import { MainMessageHub } from 'simple-electron-ipc'

const mainMessageHub = new MainMessageHub()

// listen getUserToken and download from all renderer processes
mainMessageHub.on('*', {
  getUserToken: (a, b) => Math.random().toString(36) + a + b,

  // download with progress support
  download: (msg) => {
    return new Promise((resolve, reject) => {
      let hiCount = 0
      const tid = setInterval(() => {
        if (hiCount >= 100) {
          clearInterval(tid)
          return resolve('done')
        }
        msg.onprogress({count: hiCount += 10})
      }, 200)
    })
  }
}
// mainWindow should be an instance of BrowserWindow, use mainWindow.webContents to get WebContents object
mainMessageHub.on(mainWindow.webContents, 'some-method', () => {...})

mainMessageHub.emit(mainWindow.webContents, 'generate-watermark', 'arg1', 'arg2')
  .then(base64OfPng => {...})
  .catch(e => console.log(e))

in renderer process:

import { RendererMessageHub } from 'simple-electron-ipc'

const rendererMessageHub = new RendererMessageHub()

rendererMessageHub.on('generate-watermark', async (arg1, arg2) => {
  ...
})

rendererMessageHub.emit('download', {
  onprogress(p) {
    console.log('progress', p)
  }
}).then(res => console.log(res))

Usage

MainMessageHub & RendererMessageHub

Before use this lib to communicate to each other, you need to create instances with MainMessageHub for main process & RendererMessageHub for renderer process.

// in main process
import { MainMessageHub } from 'simple-electron-ipc'

const mainMessageHub = new MainMessageHub(options?: IElectronMessageHubOptions)

// in renderer process
import { RendererMessageHub } from 'simple-electron-ipc'

const rendererMessageHub = new RendererMessageHub(options?: IElectronMessageHubOptions)


interface IElectronMessageHubOptions {
  /** ipc channel name used under the hood, default: message-hub */
  channelName?: string
}

Tips:

in most cases, you only need one instance in main process, you can use

MainMessageHub.shared/RendererMessageHub.shared instead of new an instance.
e.g.:
MainMessageHub.shared.on(webContent, 'xxx', () => {...})
RendererMessageHub.shared.emit('xxx').then((res) => {...})

If you change channelName when creating instances, main and renderers should use the same channel name.

You may need to change webPreferences when create BrowserWindow, so that you can import simple-electron-ipc in renderer process:

import { BrowserWindow } from "electron";
const mainWindow = new BrowserWindow({
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
      enableRemoteModule: true,
    },
    // other configurations
    ...
  });

The usage of MainMessageHub and RendererMessageHub have subtle differences, because the main process can send messages to multi renderers, but a renderer process can only send messages to the main process.

emit

Send a message to peer, invoking methodName registered on the peer via on with all its arguments args:

// in main process
//    if you got a BrowserWindow instance, use browserWindow.webContents to get WebContents
mainMessageHub.emit<ResponseType = unknown>(peer: WebContents, method: string, ...args: any[]) => Promise<ResponseType>

// in renderer process, no need to specify the peer, the peer is default to the main process
rendererMessageHub.emit<ResponseType = unknown>(method: string, ...args: any[]) => Promise<ResponseType>

e.g.

// in main process
const mainWindow = new BrowserWindow({....})
mainMessageHub
  .emit(mainWindow.webContents, 'some-method', 'arg1', 'arg2')
  .then(res => console.log('success', res))
  .catch(err => console.warn('error', err))

// in renderer process
rendererMessageHub
  .emit('stop-download')
  .then(res => console.log('success', res))
  .catch(err => console.warn('error', err))

Notice:

  1. look into Error when you catch an error
  2. omit args if no arguments are required, e.g rendererMessageHub.emit('some-method')
  3. you may need to handle the promise returned by emit if some lint warning unhandled promise(or floating promise)

on

Listen messages sent from peer, it has following forms:

// in main process
// register(listen)) one handler for methodName when message received from renderer
//  * means all renderers, same as below
mainMessageHub.on(peer: WebContents | '*', methodName: string, handler: Function)
// register(listen)) multi handlers
mainMessageHub.on(peer: WebContents | '*', handlerMap: Record<string, Function>)
// register only one handler to deal with all messages from renderer
mainMessageHub.on(peer: WebContents | '*', singleHandler: Function)


// in renderer process
// register(listen)) one handler for methodName when message received from main process
rendererMessageHub.on(methodName: string, handler: Function)
// register(listen)) multi handlers
rendererMessageHub.on(handlerMap: Record<string, Function>)
// register only one handler to deal with all messages from process
rendererMessageHub.on(singleHandler: Function)

e.g.

// in main process
const mainWindow = new BrowserWindow({....})
// listen multi messages from mainWindow  by passing a handler map
mainMessageHub.on(mainWindow.webContents, {
  hi (name) {
    console.log(`name ${name}`)
    // response by return
    return `hi ${name}`
  },
  'some-method': function (a, b) {
    ...
  }
})
// listen 'get-token' from all renderers
mainMessageHub.on('*', 'get-token', () => Math.random().toString(36).slice(2) )


// in renderer process
rendererMessageHub.on('async-add', async function (a, b) {
  return new Promise((resolve, reject) => {
    resolve(a + b)
  })
})

rendererMessageHub.on({
  'method1': function () {...},
  'method2': function (a, b, c) {...}
})

// listen all messages from  main process with one handler
anotherWindowRendererMessageHub.on((methodName, ...args) => {
  ...
})

Notice:

  1. you should only listen a message once, it will override existing listener when do it again
  2. for mainMessageHub: the specified callback will be called if you listen same methodName in specified peer and *

progress

If you need progress feedback when peer handling you requests, you can do it by setting the first argument as an object and has a function property named onprogress when emit messages, and call onprogress in on on the peer's side.

e.g.

// in main process
// listen download from all renderer processes
mainMessageHub.on('*', {
  // download with progress support
  download: (msg) => {
    return new Promise((resolve, reject) => {
      let hiCount = 0
      const tid = setInterval(() => {
        if (hiCount >= 100) {
          clearInterval(tid)
          return resolve('done')
        }
        // send feedback by calling onprogress if it exists
        msg && msg.onprogress && msg.onprogress({count: hiCount += 10})
      }, 200)
    })
  }
}

// in renderer process
rendererMessageHub.emit('download', {
 onprogress(p) {console.log('progress: ' + p.count)}
}).then(e => {
 console.log('success: ', e)
}).catch(err => {
  console.log('error: ' + err)
})

off

Remove message handlers, if methodName presented, remove methodName's listener, or remove the whole peer's listener

// in main process
mainMessageHub.off(peer: WebContents | '*', methodName?: string)


// in renderer process
rendererMessageHub.off(methodName?: string)

destroy

Destroy instance: remove all message handlers and references of objects. Any invoking of destroyed instance's methods will throw an exception

// in main process
mainMessageHub.destroy()


// in renderer process
rendererMessageHub.destroy()

Error

when you catch an error from emit, it conforms the following structure IError

/** error object could be caught via emit().catch(err) */
interface IError {
  /** none-zero error code */
  code: EErrorCode
  /** error message */
  message: string
  /** error object if it could pass through via the message channel underground*/
  error?: Error
}

/** enum of error code */
enum EErrorCode {
  /** handler on other side encounter an error  */
  HANDLER_EXEC_ERROR = 1,
  /** peer not found */
  PEER_NOT_FOUND = 2,
  /** method not found in peer */
  METHOD_NOT_FOUND = 3,
  /** message has invalid content, can't be sent  */
  INVALID_MESSAGE = 4,
  /** other unspecified error */
  UNKNOWN = 5,
}

Debug

You can enable debug mode by setting process.env.NODE_ENV to any value other than production, like development, it will log some debug info to the console.