├── LICENSE ├── README.md ├── background.js ├── manifest.json ├── nm_nodejs.js └── nm_nodejs.json /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Node.js Native Messaging host 2 | 3 | Installation and usage on Chrome and Chromium 4 | 5 | 1. Navigate to `chrome://extensions`. 6 | 2. Toggle `Developer mode`. 7 | 3. Click `Load unpacked`. 8 | 4. Select `native-messaging-nodejs` folder. 9 | 5. Note the generated extension ID. 10 | 6. Open `nm_nodejs.json` in a text editor, set `"path"` to absolute path of `nm_nodejs.js` and `chrome-extension:///` using ID from 5 in `"allowed_origins"` array. 11 | 7. Copy the file to Chrome or Chromium configuration folder, e.g., Chromium on \*nix `~/.config/chromium/NativeMessagingHosts`; Chrome dev channel on \*nix `~/.config/google-chrome-unstable/NativeMessagingHosts`. 12 | 8. Make sure `node` executable and `nm_nodejs.js` are executable. 13 | 9. To test click `service worker` link in panel of unpacked extension which is DevTools for `background.js` in MV3 `ServiceWorker`, observe echo'ed message from Node.js Native Messaging host. To disconnect run `port.disconnect()`. 14 | 15 | The Native Messaging host echoes back the message passed. 16 | 17 | For differences between OS and browser implementations see [Chrome incompatibilities](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Chrome_incompatibilities#native_messaging). 18 | 19 | # License 20 | Do What the Fuck You Want to Public License [WTFPLv2](http://www.wtfpl.net/about/) 21 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | globalThis.name = chrome.runtime.getManifest().short_name; 2 | 3 | globalThis.port = chrome.runtime.connectNative(globalThis.name); 4 | port.onMessage.addListener((message) => console.log(message)); 5 | port.onDisconnect.addListener((p) => console.log(chrome.runtime.lastError)); 6 | port.postMessage(new Array(209715)); 7 | 8 | chrome.runtime.onInstalled.addListener((reason) => { 9 | console.log(reason); 10 | }); 11 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nm-node", 3 | "short_name": "nm_nodejs", 4 | "version": "1.0", 5 | "description": "Node.js Native Messaging host", 6 | "manifest_version": 3, 7 | "permissions": ["nativeMessaging"], 8 | "background": { 9 | "service_worker": "background.js", 10 | "type": "module" 11 | }, 12 | "action": {}, 13 | "homepage_url": "https://github.com/guest271314/native-messaging-nodejs" 14 | } 15 | -------------------------------------------------------------------------------- /nm_nodejs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S /path/to/node 2 | // Node.js Native Messaging host 3 | // guest271314, 10-9-2022 4 | import process from "node:process"; 5 | const buffer = new ArrayBuffer(0, { 6 | maxByteLength: 1024 ** 2 7 | }); 8 | const view = new DataView(buffer); 9 | const encoder = new TextEncoder(); 10 | const readable = process.stdin; 11 | const writable = new WritableStream({ 12 | write(value) { 13 | process.stdout.write(value); 14 | } 15 | }); 16 | 17 | const { 18 | exit 19 | } = process; 20 | 21 | function encodeMessage(message) { 22 | return encoder.encode(JSON.stringify(message)); 23 | } 24 | 25 | async function* getMessage() { 26 | let messageLength = 0; 27 | let readOffset = 0; 28 | for await (let message of readable) { 29 | if (buffer.byteLength === 0 && messageLength === 0) { 30 | buffer.resize(4); 31 | for (let i = 0; i < 4; i++) { 32 | view.setUint8(i, message[i]); 33 | } 34 | messageLength = view.getUint32(0, true); 35 | message = message.subarray(4); 36 | buffer.resize(0); 37 | } 38 | buffer.resize(buffer.byteLength + message.length); 39 | for (let i = 0; i < message.length; i++, readOffset++) { 40 | view.setUint8(readOffset, message[i]); 41 | } 42 | if (buffer.byteLength === messageLength) { 43 | yield new Uint8Array(buffer); 44 | messageLength = 0; 45 | readOffset = 0; 46 | buffer.resize(0); 47 | } 48 | } 49 | } 50 | 51 | async function sendMessage(message) { 52 | await new Blob([ 53 | new Uint8Array(new Uint32Array([message.length]).buffer), 54 | message, 55 | ]) 56 | .stream() 57 | .pipeTo(writable, { 58 | preventClose: true 59 | }); 60 | } 61 | 62 | try { 63 | for await (const message of getMessage()) { 64 | await sendMessage(message); 65 | } 66 | } catch (e) { 67 | exit(); 68 | } 69 | 70 | export { 71 | encodeMessage, 72 | exit, 73 | getMessage, 74 | readable, 75 | sendMessage, 76 | writable, 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /nm_nodejs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nm_nodejs", 3 | "description": "Node.js Native Messaging Host", 4 | "path": "", 5 | "type": "stdio", 6 | "allowed_origins": [] 7 | } 8 | --------------------------------------------------------------------------------