0.3.0 • Published 3 years ago

@josephuspaye/chrome-native-bridge v0.3.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

chrome-native-bridge

A utility for building Chrome native messaging hosts with Node.js. Allows you to write Node scripts that can communicate bi-directionally with extensions and apps in Chrome.

This project is part of #CreateWeekly, my attempt to create something new publicly every week in 2020.

Installation

npm install @josephuspaye/chrome-native-bridge --save

Usage

The following example shows a Node script that echoes all messages sent to it from Chrome. Note that it doesn't use console.log(), as that writes to stdout, which Chrome reads for messages.

import { ChromeNativeBridge } from '@josephuspaye/chrome-native-bridge';

const bridge = new ChromeNativeBridge(
  process.argv, // The arguments to the current process
  process.stdin, // The input stream that Chrome writes to
  process.stdout, // The output stream that Chrome reads from
  {
    onMessage(message) {
      // A message has been received, echo it
      // by sending it back
      bridge.emit(message);
    },

    onError(err) {
      // There's been an error parsing a received message.
      // Do something to handle it here...
    },

    onEnd() {
      // End of `process.stdin` detected: it's likely Chrome wants
      // to end the native host process, so we exit here
      process.exit();
    },
  }
);

// This is the origin of the caller, usually chrome-extension://[ID of allowed extension]
const origin = bridge.origin;

// This is the decimal handle value of the calling Chrome window. Available on Windows only.
const parentWindow = bridge.parentWindow;

Example

There is an example native host script and a corresponding Chrome extension at example/host and example/extension respectively. Together they implement a basic text chat to demonstrate communication over the native bridge.

You can view a demonstration GIF (2MB) here.

Get it

  • Clone this repo: git clone https://github.com/JosephusPaye/chrome-native-bridge.git
  • Change into the cloned directory: cd chrome-native-bridge
  • Install dependencies: yarn (or npm install)

Install it

Open the Extensions page in Chrome (navigate to chrome://extensions), enable developer mode, click "Load unpacked", and then select the example/extension folder in the repo.

When the extension is loaded, copy the generated ID, change it to the form chrome-extension://EXTENSION_ID_HERE/, and paste it into the example/host/manifest.json file, in the allowed_origins array.

Next, install the host manifest by running the following in a Command Prompt, from the repo root directory:

example\host\install.bat

Similarly, you can uninstall the host manifest by running example\host\uninstall.bat.

Note: the install and uninstall scripts are Windows only, but similar scripts can be written for Linux or macOS.

Run it

Start the example/host/chat.js script in Node, by running the following from the repo root directory.

node example/host/chat.js

This will start the native chat client and wait for a connection from the host script, which will be launched by Chrome when it connects.

In Chrome, navigate to chrome://apps and launch the Chrome Native Bridge example app, then click "Connect to chat". If the host manifest was installed correctly, Chrome will connect to the host, and a chat interface will be shown.

You can now exchanges messages between the Chrome app and the native chat.js script by typing in the input in Chrome, or the terminal running the chat.js script.

API

ChromeNativeBridge class

Implements the Chrome Native Messaging protocol and provides a nicer interface over stdin/stdout for communicating with a Chrome extension from a native Node script.

class ChromeNativeBridge<TSend = any, TReceive = any> {
  /**
   * Origin of the caller, usually chrome-extension://[ID of allowed extension]
   */
  origin: string;

  /**
   * The decimal handle value of the calling Chrome window. Available on Windows only.
   */
  parentWindow?: number;

  /**
   * Create a new bridge with the given input and output.
   *
   * @param args The arguments to the script. Use `process.argv`.
   * @param input A readable stream for getting messages from Chrome. Use `process.stdin`.
   * @param output A writable stream for sending message to Chrome. Use `process.stdout`.
   */
  constructor(
    args: string[],
    input: stream.Readable,
    output: stream.Writable,
    options: {
      onMessage: OnMessageHandler<TReceive>;
      onError: OnErrorHandler;
      onEnd: OnEndHandler;
      mirrorInputTo?: Writable;
      mirrorOutputTo?: Writable;
    }
  );

  /**
   * Send the given data to the output. The data will be passed to
   * `JSON.stringify()` for serialisation, and will throw an error
   * [if `JSON.stringify()` throws](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Exceptions).
   */
  emit(data: TSend): void;

  /**
   * Close the bridge.
   */
  close(): void;
}

Types

The following additional types are used by the ChromeNativeBridge class:

/**
 * An error that occurs while sending or receiving messages.
 */
type ChromeNativeBridgeError = Error & {
  type: 'SEND_ERROR' | 'RECEIVE_ERROR';
};

/**
 * Handle a new message.
 */
type OnMessageHandler<T> = (message: T) => void;

/**
 * Handle an error receiving data. The second parameter
 * is the raw string of the message that caused the error.
 */
type OnErrorHandler = (err: ChromeNativeBridgeError, data: string) => void;

/**
 * Handle the end of the input stream. This can be used to know when
 * to exit the native host process, since it seems like when Chrome
 * wants to end the process, it closes stdin, triggering its 'end'
 * event (at least from every case I've observed so far).
 */
export type OnEndHandler = () => void;

Related

Licence

MIT