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-common-ipc-dsabanin

v4.9.2

Published

A safe IPC bus for applications built on Node or Electron.

Downloads

5

Readme

electron-common-ipc

An IPC (Inter-Process Communication) bus for applications built on Node or Electron.

This bus offers a EventEmitter-like API for exchanging data between any processes (Node, Electron Master, Electron Renderer/s).

  • Node to Node,
  • Node to Electron (Master and Renderer processes),
  • Electon to Node,
  • Electron to Electron.

Node needs a "BusBroker" in charge to commute messages to right listeners Electron needs an additional broker : "BusBridge" in charge to commute messages between renderers (WebPages), Master and Node.

A WebPage is then able to dialog with a node process and vice-versa.

Features

  • EventEmitter oriented API
  • Works with Electron sandboxed renderer process
  • Support for Electron renderer affinity (several webpages hosted in the same renderer process)
  • Remote calls/events and pending messages management with Services

Installation

npm install electron-common-ipc

Dependencies

  • https://github.com/emmkimme/socket-serializer
  • http://electron.atom.io/
  • http://nodejs.org/

Technical Overview

Objective

Electron's processes

Usage

// Load modules
const ipcBusModule = require("electron-common-ipc");
const electronApp = require('electron').app;

// Configuration
const ipcBusPath = 50494;
// const ipcBusPath = '/myfavorite/path';
// const ipcBusPath = 'localhost:49152';

// In Electron, listen app ready event (not needed in pure Node)
electronApp.on('ready', function () {
    // Create broker
    const ipcBusBroker = ipcBusModule.CreateIpcBusBroker(ipcBusPath);
    // Start broker
    ipcBusBroker.start()
        .then((msg) => {
            console.log('IpcBusBroker started');

            // In Electron, start bridge ensuring connection between renderer/s and master processes (not needed in pure Node)
            const ipcBusBridge = ipcBusModule.CreateIpcBusBridge(ipcBusPath);
            ipcBusBridge.start()
                .then((msg) => {
                    console.log('IpcBusBridge started');

                    // Create clients
                    const ipcBusClient1 = ipcBusModule.CreateIpcBusClient(ipcBusPath);
                    const ipcBusClient2 = ipcBusModule.CreateIpcBusClient(ipcBusPath);
                    Promise.all([ipcBusClient1.connect({ peerName: 'client1' }), ipcBusClient2.connect({ peerName: 'client2' })])
                        .then((msg) => {
                            // Chatting on channel 'greeting'
                            ipcBusClient1.addListener('greeting', (ipcBusEvent, greetingMsg) => {
                                if (ipcBusEvent.request) {
                                    ipcBusEvent.request.resolve('thanks to you, dear #' + ipcBusEvent.sender.name);
                                }
                                else {
                                    ipcBusClient1.send('greeting-reply', 'thanks to all listeners')
                                }
                                console.log(ipcBusClient1.peer.name + ' received ' + ipcBusEvent.channel + ':' + greetingMsg);
                            });

                            ipcBusClient2.addListener('greeting', (ipcBusEvent, greetingMsg) => {
                                if (ipcBusEvent.request) {
                                    ipcBusEvent.request.resolve('thanks to you, dear #' + ipcBusEvent.sender.name);
                                }
                                else {
                                    ipcBusClient2.send('greeting-reply', 'thanks to all listeners')
                                }
                                console.log(ipcBusClient2.peer.name + ' received ' + ipcBusEvent.channel + ':' + greetingMsg);
                            });

                            ipcBusClient1.addListener('greeting-reply', (ipcBusEvent, greetingReplyMsg) => {
                                console.log(greetingReplyMsg);
                                console.log(ipcBusClient1.peer.name + ' received ' + ipcBusEvent.channel + ':' + greetingReplyMsg);
                            });

                            ipcBusClient2.send('greeting', 'hello everyone!');

                            ipcBusClient2.request('greeting', 0, 'hello partner!')
                                .then((ipcBusRequestResponse) => {
                                    console.log(JSON.stringify(ipcBusRequestResponse.event.sender) + ' replied ' + ipcBusRequestResponse.payload);
                                })
                                .catch((err) => {
                                    console.log('I have no friend :-(');
                                });

                            ipcBusClient1.request('greeting', 1000, 'hello partner, please answer within 1sec!')
                                .then((ipcBusRequestResponse) => {
                                    console.log(JSON.stringify(ipcBusRequestResponse.event.sender) + ' replied ' + ipcBusRequestResponse.payload);
                                })
                                .catch((err) => {
                                    console.log('I have no friend :-(');
                                });
                        });
                });
        });
});

Common options

Some interfaces are sharing the same kind of options. In order to be consistent in term of behavior and naming we have common interfaces for options.

export interface IpcTimeoutOptions {
    timeoutDelay?: number;
}
  • timeoutDelay = number (milliseconds)

  • timeoutDelay [0 | undefined | null] : use a default timeout of 2000ms

  • timeoutDelay < 0 : an infinite waiting

  • timeoutDelay > 0 : wait for the expected time

A timeoutdelay below zero leads to an infinite waiting.

export interface IpcSocketBufferingOptions {
    socketBuffer?: number;
} 
  • socketBuffer [0 | undefined | null] : message is serialized in small pieces written immediatly to the socket
  • socketBuffer < 0 : message is serialized in objects (number, string, buffer...), each object is written immediatly to the socket
  • socketBuffer > 0 : message is serialized in objects, objects are written only in one shot when reaching a size of socketBuffer in bytes.

IpcBusBroker

Dispatching of Node messages is managed by a broker. You can have only one single Broker for the whole application. The broker can be instanciated in a node process or in the master process (not in renderer processes). For performance purpose, it is better to instanciate the broker in an independent node process.

Interface

interface IpcBusBroker {
    start(options?: IpcBusBroker.StartOptions): Promise<string>;
    stop(): void;
    queryState(): Object;
    isServiceAvailable(serviceName: string): boolean;
}

Initialization of the Broker (in a node process)

const ipcBusModule = require("electron-common-ipc");
const ipcBusBroker = ipcBusModule.CreateIpcBusBroker([busPath]);

The require() call loads the module and CreateIpcBusBroker setups the broker with the busPath. If busPath is not specified, the framework tries to get it from the command line with switch --bus-path.

Example with busPath set by code

// Socket path
const ipcBusBroker = ipcBusModule.CreateIpcBusBroker('/my-ipc-bus-path');
// Port number
const ipcBusBroker = ipcBusModule.CreateIpcBusBroker(58666);

Example with busPath set by command line:

electron.exe --bus-path=58666
const ipcBusBroker = ipcBusModule.CreateIpcBusBroker();

Methods

start(options?: IpcBusBroker.StartOptions) : Promise < string >

ipcBusBroker.start() 
    .then((msg) => console.log(msg))
    .catch((err) => console.log(err))

Starts the broker dispatcher. If succeeded the value of msg is 'started' (do not rely on it, subject to change). If failed (timeout or any other internal error), err contains the error message.

stop()

ipcBusBroker.stop() 

queryState() - for debugging purpose only

var queryState = ipcBusBroker.queryState() 

Returns the list of pair <channel, peer> subscriptions. Format may change from one version to another. This information can be retrieved from an IpcBusClient through the channel : /electron-common-ipc/queryState

isServiceAvailable(serviceName): boolean

  • serviceName: string
ipcBusBroker.isServiceAvailable('mySettings') 

Test if a service is started. This information can be retrieved from an IpcBusClient through the channel : /electron-common-ipc/serviceAvailable

IpcBusBridge

Dispatching of Renderer messages is managed by a bridge. You can have only one single bridge for the whole application. The bridge must be instanciated in the master process only. Without this bridge, Renderer and Node processes are not able to dialog.

Interface

interface IpcBusBridge {
    start(options?: IpcBusBridge.StartOptions): Promise<string>;
    stop(): void;
}

Initialization of the Bridge (in the master process)

const ipcBusModule = require("electron-common-ipc");
const ipcBusBridge = ipcBusModule.CreateIpcBusBridge([busPath]);

The require() call loads the module and CreateIpcBusBridge setups the bridge with the busPath. If busPath is not specified, the framework tries to get it from the command line with switch --bus-path.

Example with busPath set by code

// Socket path
const ipcBusBridge = ipcBusModule.CreateIpcBusBridge('/my-ipc-bus-path');
// Port number
const ipcBusBridge = ipcBusModule.CreateIpcBusBridge(58666);

Example with busPath set by command line:

electron.exe --bus-path=58666
const ipcBusBridge = ipcBusModule.CreateIpcBusBridge();

Methods

start(options?: IpcBusBridge.StartOptions) : Promise < string >

ipcBusBridge.start() 
    .then((msg) => console.log(msg))
    .catch((err) => console.log(err))

Starts the bridge dispatcher. If succeeded the value of msg is 'started' (do not rely on it, subject to change). If failed (timeout or any other internal error), err contains the error message.

stop()

ipcBusBridge.stop() 

IpcBusClient

The IpcBusClient is an instance of the EventEmitter class.

When you register a callback to a specified channel. Each time a message is received on this channel, the callback is called. The callback must follow the IpcBusListener signature (see below).

Only one IpcBusClient per Process/Renderer is created. If you ask for more, the same instance will be returned.

Interface

interface IpcBusClient extends events.EventEmitter {
    readonly peerName: string;

    connect(options?: IpcBusClient.ConnectOptions): Promise<string>;
    close(): void;

    send(channel: string, ...args: any[]): void;
    request(channel: string, timeoutDelay: number, ...args: any[]): Promise<IpcBusRequestResponse>;

    // EventEmitter overriden API
    addListener(channel: string, listener: IpcBusListener): this;
    removeListener(channel: string, listener: IpcBusListener): this;
    on(channel: string, listener: IpcBusListener): this;
    once(channel: string, listener: IpcBusListener): this;
    off(channel: string, listener: IpcBusListener): this;

    // Added in Node 6...
    prependListener(channel: string, listener: IpcBusListener): this;
    prependOnceListener(channel: string, listener: IpcBusListener): this;
}

Initialization in the Main/Browser Node process

const ipcBusModule = require("electron-common-ipc");
const ipcBus = ipcBusModule.CreateIpcBusClient([busPath]);

The require() call loads the module. CreateIpcBus setups the client with the busPath that was used to start the broker. If busPath is not specified, the framework tries to get it from the command line with switch --bus-path.

Example with busPath set by code:

const ipcBus = ipcBusModule.CreateIpcBusClient('/my-ipc-bus-path');

Example with busPath set by command line:

electron.exe --bus-path='/my-ipc-bus-path'
const ipcBus = ipcBusModule.CreateIpcBusClient();

Initialization in a Node single process

Example with busPath set by code:

const ipcBus = ipcBusModule.CreateIpcBusClient('/my-ipc-bus-path');

Example with busPath set by command line:

electron.exe --bus-path='/my-ipc-bus-path'
const ipcBus = ipcBusModule.CreateIpcBusClient();

Initialization in a Renderer process (either sandboxed or not)

const ipcBusModule = require("electron-common-ipc");
const ipcBus = ipcBusModule.CreateIpcBusClient();

NOTE: If the renderer is running in sandboxed mode, the above code must be run from the BrowserWindow's preload script (browserify -o BundledBrowserWindowPreload.js -x electron BrowserWindowPreload.js). Otherwise, the Electron's ipcRenderer is not accessible and the client cannot work. Use the code below to make the client accessible to the the Web page scripts.

window.ipcBus = require('electron-common-ipc').CreateIpcBusClient();

Property

peer

For debugging purpose, each IpcBusClient is identified by a peer.

type IpcBusProcessType = 'browser' | 'renderer' | 'node';

interface IpcBusProcess {
    type: IpcBusProcessType;
    pid: number;
    rid?: number;
    wcid?: number;
}

interface IpcBusPeer {
    id: number;
    name: string;
    process: IpcBusProcess;
}

IpcBusPeer contains :

  • an uniq read-only id
  • the name of the peer, this name can be changed during the connection (IpcBusClient.connect)
  • the process context of the peer.

IpcBusProcess contains :

  • type : type of the process : 'browser' | 'renderer' | 'node'
  • pid : id of the process which hosts the the peer
  • rid : client id of the renderer process where the peer is hosted (equivalent to the value of --renderer-client-id option in the commandline of the renderer process), only valid for a 'renderer' process
  • wcid : webContents id of the peer, only valid for a 'renderer' process

For a 'renderer' process the pid depends on the version of Electron.

Electron < 1.7.1 : pid = wcid

Connectivity Methods

connect(options?: IpcBusClient.ConnectOptions) : Promise < string >

Basic usage

ipcBus.connect().then((eventName) => console.log("Connected to Ipc bus !"))

Provide a timeout

ipcBus.connect({ timeoutDelay: 2000 }).then((eventName) => console.log("Connected to Ipc bus !"))

Provide a peer name

ipcBus.connect({ peerName: 'client2' }).then((eventName) => console.log("Connected to Ipc bus !"))

Provide all options

ipcBus.connect({ peerName: 'client2', timeoutDelay: 2000 }).then((eventName) => console.log("Connected to Ipc bus !"))

For a bus in a renderer, it fails if the Bridge is not started else it fails if the Broker is not started. Most of the functions below will fail if the connection is not established (you have to wait for the connect promise). A timeoutdelay below zero leads to an infinite waiting.

close()

ipcBus.close()

addListener(channel, listener)

  • channel: string
  • listener: IpcBusListener

Listens to channel, when a new message arrives listener would be called with listener(event, args...).

NOTE: on, prependListener, once and prependOnceListener methods are supported as well

removeListener(channel, listener)

  • channel: string
  • listener: IpcBusListener

Removes the specified listener from the listeners array for the specified channel.

NOTE: off method is supported as well

removeAllListeners([channel])

channel: String (optional)

Removes all listeners, or those of the specified channel.

IpcBusListener(event, ...args) callback

  • event: IpcBusEvent
  • ...args: any[]): void

The first parameter of the callback is always an event which contains the channel and the origin of the message (sender).

function HelloHandler(ipcBusEvent, content) {
    console.log("Received '" + content + "' on channel '" + ipcBusEvent.channel +"' from #" + ipcBusEvent.sender.peerName)
}
ipcBus.on("Hello!", HelloHandler)

Posting Methods

send(channel [, ...args])

  • channel: string
  • ...args: any[]

Sends a message asynchronously via channel, you can also send arbitrary arguments. Arguments will be serialized in JSON internally and hence no functions or prototype chain will be included.

ipcBus.send("Hello!", { name: "My age !"}, "is", 10)

request(channel: string, timeoutDelay: number, ...args: any[]): Promise

  • channel: string
  • timeoutDelay = timeoutDelay: number (milliseconds)
  • ...args: any[]

Sends a request message on specified channel. The returned Promise is settled when a result is available. The parameter timeoutDelay defines how much time we're waiting for the response. A timeoutdelay below zero leads to an infinite waiting.

The Promise provides an IpcBusRequestResponse object:

interface IpcBusRequestResponse {
    event: IpcBusEvent;
    payload?: Object | string;
    err?: string;
}
ipcBus.request("compute", 0, "2*PI*9")
    .then(ipcBusRequestResponse) {
        console.log("channel = " + ipcBusRequestResponse.event.channel + ", response = " + ipcBusRequestResponse.payload + ", from = " + ipcBusRequestResponse.event.sender.peerName);
     }
     .catch(ipcBusRequestResponse) {
        console.log("err = " + ipcBusRequestResponse.err);
     }

With timeout

ipcBus.request("compute", 2000, "2*PI*9")
...

IpcBusEvent object

interface IpcBusEvent {
    channel: string;
    sender: IpcBusSender {
        peerName: string;
    };
    request?: IpcBusRequest {
        resolve(payload: Object | string): void;
        reject(err: string): void;
    };
}

The event object passed to the listener has the following properties:

event.channel: string

channel delivering the message

event.sender.peerName: string

peerName of the sender

event.request [optional]: IpcBusRequest

If present, the message is a request. Listener can resolve the request by calling event.request.resolve() with the response or can reject the request by calling event.request.reject() with an error message.

IpcBusService

The IpcBusService creates an IPC endpoint that can be requested via remote calls and send events.

Interface

interface IpcBusService {
    start(): void;
    stop(): void;
    registerCallHandler(name: string, handler: IpcBusServiceCallHandler): void;
    sendEvent(eventName: string, ...args: any[]): void;
}

IpcBusServiceCall

Message sent to a service to execute a remote call.

interface IpcBusServiceCall {
    handlerName: string;
    args: any[];
}

IpcBusServiceCallHandler

Prototype of a method that will be executed to handle a service's call.

interface IpcBusServiceCallHandler {
    (call: IpcBusServiceCall, request: IpcBusRequest): void;
}

Creation (without an outer implementation)

const ipcBusModule = require("electron-common-ipc");
...
// ipcBusClient is a connected instance of IpcBusClient
const ipcMyService = ipcBusModule.CreateIpcBusService(ipcBusClient, 'myService');

Creation (with an outer instance)

const ipcBusModule = require("electron-common-ipc");
...
const myOuterServiceInstance = {};
myOuterServiceInstance.test = () => { return 'This is a test'; };
...
// ipcBusClient is a connected instance of IpcBusClient
const ipcMyService = ipcBusModule.CreateIpcBusService(ipcBusClient, 'myService', myOuterServiceInstance);

NOTE : This constructor will automatically register all methods of myOuterServiceImpl as call handlers using registerCallHandler().

Methods

start(): void

This makes the service to listen and serve incoming remote calls. The service also sends the IPCBUS_SERVICE_EVENT_START event. NOTE : If an outerrouter service's instance has been specified at construction time, start() will overload its This method will overload

stop(): void

This makes the service to stop listen and serve incoming remote calls. The service also sends the IPCBUS_SERVICE_EVENT_STOP event.

registerCallHandler(name, handler): void

  • name: string
  • handler: IpcBusServiceCallHandler This sets the function that will be executed to serve the specified remote call. As this is run in the context of a promise, the function must call either request.resolve() or request.reject() to fulfill the promise.
ipcMyService.registerCallHandler('getCurrentTime', (event, call) => {
    if (event.request) {
            try {                        {
                request.resolve(new Date().getTime());
            } catch(e) {
                request.reject(e);
            }
        });
    }

sendEvent(name, ...args): void

  • name: string
  • args: any[] This sends a service event message.
ipcMyService.sendEvent('timeChanged', new Date().getTime());

IpcBusServiceProxy

The IpcBusServiceProxy creates an IPC endpoint that can be used to execute calls on a service and listen its events.

Interface

interface IpcBusServiceProxy extends events.EventEmitter {
    readonly isStarted: boolean;

    getStatus(): Promise<ServiceStatus>;
    call<T>(handlerName: string, ...args: any[]): Promise<T>;
    getWrapper<T>(): T;
    connect<T>(timeoutDelay?: number): Promise<T>

IpcBusServiceEvent

Message sent to a service's proxy to trigger the code associated to this event.

interface IpcBusServiceEvent {
    eventName: string;
    args: any[];
}

IpcBusServiceEventHandler

Prototype of a method that will be executed to handle a service's call.

interface IpcBusServiceEventHandler {
    (event: IpcBusServiceEvent): void;
}

Creation

const ipcBusModule = require("electron-common-ipc");
...
// ipcBusClient is a connected instance of IpcBusClient
const ipcMyServiceProxy = ipcBusModule.CreateIpcBusServiceProxy(ipcBusClient, 'myService', 2000); // 2000 ms for call timeout (default is 1000 ms)

Properties

isAvailable: boolean

Availability of the associated service (available means that the service is started).

Methods

checkAvailability(): Promise< boolean >

This asynchronously requests the service availability to the Broker.

ipcMyServiceProxy.checkAvailability()
        .then(
            (availability) => console.log(`MyService availability = ${availability}`),
            (err) => console.log(`Failed to get MyService availability (${err})`));

call(handlerName: string, timeout: number, ...args: any[]): Promise< T >

  • handlerName: string
  • timeout: number
  • args: any[] This sends a service event message.
ipcMyServiceProxy.call('getCurrentTime')
        .then(
            (currentTime) => console.log(`Current Time = ${currentTime}`),
            (err) => console.log(`Failed to get current time : ${err}`));

EventEmitter interface

This allow to handle events emitted by remote RPC service. Please refers to the EventEmitter class documentation for more information.

  • addListener(event: string, listener: IpcBusServiceEventHandler): this;
  • removeListener(event: string, listener: IpcBusServiceEventHandler): this;
  • on(event: string, listener: IpcBusServiceEventHandler): this;
  • once(event: string, listener: IpcBusServiceEventHandler): this;
  • off(event: string, listener: IpcBusServiceEventHandler): this;
  • removeAllListeners(event?: string): this;
  • prependListener(event: string, listener: IpcBusServiceEventHandler): this;
  • prependOnceListener(event: string, listener: IpcBusServiceEventHandler): this;

The wrapper implements EventEmitter as well. If the interface of the service emits an event it will be receiced by the wrapper of the proxy.

Test application

The test-app folder contains all sources of the testing application.

NOTE: This folder is not packaged by NPM.

To build the application:

cd examples
cd test-app
npm install
npm run build

To run the application:

npm run start

To run the application in sandboxed mode:

npm run start-sandboxed

Possible enhancements

  • Support several brokers each with its own buspath in order to distribute the traffic load.
  • Add an optional spy for debugging purpose

MIT License

Copyright (c) 2017 Emmanuel Kimmerlin and Michael Vasseur

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.