├── .gitignore
├── README.md
├── app
├── background.js
├── main.css
├── main.html
├── main.js
└── manifest.json
├── host
├── com.my_company.my_application-win.json
├── com.my_company.my_application.json
├── my_host.bat
├── my_host.js
├── node.exe
├── register.bat
└── register.sh
├── index.js
├── json2msg.js
├── msg2json.js
├── package-lock.json
├── package.json
├── test.js
├── test.json
└── test.msg
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chrome Native Messaging for Node.js
2 |
3 | Transform streams for writing Chrome App [native messaging][1] hosts in Node.js.
4 |
5 | [1]: https://developer.chrome.com/extensions/messaging#native-messaging
6 |
7 | ## Install
8 |
9 | ```
10 | npm i -S chrome-native-messaging
11 | ```
12 |
13 | ## API
14 |
15 | The module exports `Input`, `Output`, and `Transform` transform streams.
16 |
17 | `Input` streams transform bytes to objects.
18 |
19 | `Output` streams transform objects to bytes.
20 |
21 | Use `Transform` to easily create custom object-to-object transform streams.
22 |
23 | ```
24 | var nativeMessage = require('chrome-native-messaging');
25 |
26 | process.stdin
27 | .pipe(new nativeMessage.Input())
28 | .pipe(new nativeMessage.Transform(function(msg, push, done) {
29 | var reply = getReplyFor(msg); // Implemented elsewhere by you.
30 | push(reply); // Push as many replies as you like.
31 | done(); // Call when done pushing replies.
32 | }))
33 | .pipe(new nativeMessage.Output())
34 | .pipe(process.stdout)
35 | ;
36 | ```
37 |
38 | ## Example
39 |
40 | The `app` directory contains a sample Chrome App.
41 |
42 | The `host` directory contains a native messaging host that you can send
43 | messages to.
44 |
45 | Go to the Chrome Extensions page (chrome://extensions/), hit "Load unpacked
46 | extension...", and select this project's `app` directory.
47 |
48 | SUPER IMPORTANT: Find the ID your app received when you loaded it and copy it
49 | to the host manifest in the `host` directory.
50 |
51 | SUPER IMPORTANT ON MACS: The path to the host must be absolute. Make sure the
52 | "path" property in the manifest is correct (it's probably not unless you're me).
53 |
54 | Install the host manifest:
55 |
56 | ```
57 | sudo host/register.sh
58 | ```
59 |
60 | On Windows (Run as Administrator):
61 |
62 | ```
63 | host\register.bat
64 | ```
65 |
66 | Open a new tab and hit Apps in the upper left. Launch the example app and send
67 | yourself messages.
68 |
69 | ## Testing
70 |
71 | Run `npm test` for the unit tests.
72 |
73 | `json2msg.js` is a script that can convert lines of JSON into native messages.
74 | Use it to send messages to your host to see how it responds.
75 |
76 | Pipe the output of your host to `msg2json.js` to see what its output looks like.
77 |
78 | ```
79 | ./json2msg.js < test.json | ./host/my_host.js | ./msg2json.js
80 | ```
81 |
82 | On Windows:
83 |
84 | ```
85 | node json2msg.js < test.json | node host\my_host.js | node msg2json.js
86 | ```
87 |
88 | ## Logging
89 |
90 | Enabling logging in Chrome can help find problems finding the manifest.
91 |
92 | Quit Chrome first.
93 |
94 | ```
95 | open -a Google\ Chrome --args --enable-logging --v=1
96 | ```
97 |
98 | On Windows:
99 |
100 | ```
101 | start chrome --enable-logging --v=1
102 | ```
103 |
104 | View the log like this:
105 |
106 | ```
107 | less ~/Library/Application\ Support/Google/Chrome/chrome_debug.log
108 | ```
109 |
110 | On Windows:
111 |
112 | ```
113 | type "C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\chrome_debug.log"
114 | ```
115 |
116 | More info: http://www.chromium.org/for-testers/enable-logging
117 |
--------------------------------------------------------------------------------
/app/background.js:
--------------------------------------------------------------------------------
1 | chrome.app.runtime.onLaunched.addListener(function() {
2 | chrome.app.window.create('main.html', {
3 | bounds: {
4 | width: 640,
5 | height: 480
6 | }
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/app/main.css:
--------------------------------------------------------------------------------
1 | html {
2 | overflow: auto;
3 | }
4 |
--------------------------------------------------------------------------------
/app/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Chrome Native Messaging Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Examples:
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/main.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | var application = 'com.my_company.my_application';
4 | var port = null;
5 |
6 | document.getElementById('connect').addEventListener('click', function() {
7 | log('chrome.runtime.connectNative')
8 |
9 | port = chrome.runtime.connectNative(application);
10 |
11 | port.onMessage.addListener(log);
12 |
13 | port.onDisconnect.addListener(function(e) {
14 | log('unexpected disconnect');
15 |
16 | port = null;
17 | });
18 | });
19 |
20 | document.getElementById('disconnect').addEventListener('click', function() {
21 | log('port.disconnect');
22 | port.disconnect(); // this doesn't seem to trigger the onDisconnect event
23 | port = null;
24 | });
25 |
26 | var examples = {
27 | ping: { ping: 'pong' },
28 | readdir: { readdir: '/' },
29 | subscribe: { subscribe: 'time' },
30 | unsubscribe: { unsubscribe: 'time' }
31 | };
32 |
33 | Array.prototype.slice.call(document.querySelectorAll('[data-example]')).forEach(function(example) {
34 | example.addEventListener('click', function() {
35 | document.getElementById('msg').value = JSON.stringify(examples[example.dataset.example]);
36 | });
37 | });
38 |
39 | document.getElementById('send').addEventListener('click', function() {
40 | var json = document.getElementById('msg').value;
41 | var msg;
42 |
43 | try {
44 | msg = JSON.parse(json);
45 | } catch (err) {
46 | return log('invalid JSON: ' + json);
47 | }
48 |
49 | if (port) {
50 | log('port.postMessage');
51 | port.postMessage(msg);
52 | } else {
53 | log('chrome.runtime.sendNativeMessage');
54 | chrome.runtime.sendNativeMessage(application, msg, log);
55 | }
56 | });
57 |
58 | document.getElementById('clear').addEventListener('click', function() {
59 | document.getElementById('log').innerHTML = '';
60 | });
61 |
62 | function log(msg) {
63 | console.log(msg);
64 |
65 | var e = document.createElement('pre');
66 | e.appendChild(document.createTextNode(typeof msg === 'object' ? JSON.stringify(msg) : msg));
67 | document.getElementById('log').appendChild(e);
68 | }
69 |
70 | })();
71 |
--------------------------------------------------------------------------------
/app/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvyGmcOkw4cTnhO0bgl3fQLAdv1jZp8T1ZHYI+4d8FgwwVKLYWE+pAuJ/0LrI69ibed4Nnnw5YleB1xCpI+mzB56xfXWboKp6lljevKqWJ5TpJk/Vam3kSSoZwpmJRXnzmcM3qKpL6viUhTfwGmQO6WVTsN4YCx+KWXv97IyF6yDTgd6hwFsvCZY2n1ADgurrQkE6AcJ3kK4xZ14jaHllXEdFcqwh0+Am5qLcIJ1cNo5iFD35exXsjwdQbmpt8sEk5f95pK5FEEbJFmOTguu2fOZycqIoTgoDrbbhT5k9TUogZaN5Lup0Iwh0Cv60i4C1f7IdPrxHuaYmYCfoUezXnQIDAQAB",
4 | "name": "Chrome Native Messaging Example",
5 | "version": "1.0",
6 | "description": "Send a message to a native application.",
7 | "app": {
8 | "background": {
9 | "scripts": [ "background.js" ]
10 | }
11 | },
12 | "permissions": [
13 | "nativeMessaging"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/host/com.my_company.my_application-win.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.my_company.my_application",
3 | "description": "My Application",
4 | "path": "my_host.bat",
5 | "type": "stdio",
6 | "allowed_origins": [
7 | "chrome-extension://bmfbcejdknlknpncfpeloejonjoledha/"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/host/com.my_company.my_application.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.my_company.my_application",
3 | "description": "My Application",
4 | "path": "/Users/jason/code/chrome-native-messaging/host/my_host.js",
5 | "type": "stdio",
6 | "allowed_origins": [
7 | "chrome-extension://bmfbcejdknlknpncfpeloejonjoledha/"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/host/my_host.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | set LOG=C:\Users\Jason\Desktop\log.txt
4 |
5 | time /t >> %LOG%
6 |
7 | "%~dp0node.exe" "%~dp0my_host.js" %* 2>> %LOG%
8 |
9 | echo %errorlevel% >> %LOG%
10 |
--------------------------------------------------------------------------------
/host/my_host.js:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/node
2 |
3 | // Might be good to use an explicit path to node on the shebang line in case
4 | // it isn't in PATH when launched by Chrome.
5 |
6 | var fs = require('fs');
7 |
8 | var nativeMessage = require('../index');
9 |
10 | var input = new nativeMessage.Input();
11 | var transform = new nativeMessage.Transform(messageHandler);
12 | var output = new nativeMessage.Output();
13 |
14 | process.stdin
15 | .pipe(input)
16 | .pipe(transform)
17 | .pipe(output)
18 | .pipe(process.stdout)
19 | ;
20 |
21 | var subscriptions = {};
22 |
23 | var timer = setInterval(function() {
24 | if (subscriptions.time) {
25 | output.write({ time: new Date().toISOString() });
26 | }
27 | }, 1000);
28 |
29 | input.on('end', function() {
30 | clearInterval(timer);
31 | });
32 |
33 | function messageHandler(msg, push, done) {
34 | if (msg.readdir) {
35 | fs.readdir(msg.readdir, function(err, files) {
36 | if (err) {
37 | push({ error: err.message || err });
38 | } else {
39 | files.forEach(function(file) {
40 | push({ file: file });
41 | });
42 | }
43 |
44 | done();
45 | });
46 | } else if (msg.subscribe) {
47 | subscriptions[msg.subscribe] = true;
48 | push({ subscribed: msg.subscribe });
49 | done();
50 | } else if (msg.unsubscribe) {
51 | delete subscriptions[msg.unsubscribe];
52 | push({ unsubscribed: msg.unsubscribe });
53 | done();
54 | } else {
55 | // Just echo the message:
56 | push(msg);
57 | done();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/host/node.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jdiamond/chrome-native-messaging/6859c7cba2b17d6898b05f69912bc0a507ff986d/host/node.exe
--------------------------------------------------------------------------------
/host/register.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | call :isAdmin
4 |
5 | if %errorlevel% == 0 (
6 | goto :run
7 | ) else (
8 | echo Error: Run as administrator.
9 | )
10 |
11 | exit /b
12 |
13 | :isAdmin
14 | fsutil dirty query %systemdrive% >nul
15 | exit /b
16 |
17 | :run
18 |
19 | reg add HKLM\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application /f /ve /t REG_SZ /d %~dp0com.my_company.my_application-win.json
20 |
21 | pause
22 |
--------------------------------------------------------------------------------
/host/register.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd $(dirname $0)
4 |
5 | mkdir -p /Library/Google/Chrome/NativeMessagingHosts
6 | cp com.my_company.my_application.json /Library/Google/Chrome/NativeMessagingHosts
7 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // chrome-native-messaging module
2 | //
3 | // Defines three Transform streams:
4 | //
5 | // - Input - transform native messages to JavaScript objects
6 | // - Output - transform JavaScript objects to native messages
7 | // - Transform - transform message objects to reply objects
8 | // - Debug - transform JavaScript objects to lines of JSON (for debugging, obviously)
9 |
10 | var stream = require('stream');
11 | var util = require('util');
12 |
13 | function Input() {
14 | stream.Transform.call(this);
15 |
16 | // Transform bytes...
17 | this._writableState.objectMode = false;
18 | // ...into objects.
19 | this._readableState.objectMode = true;
20 |
21 | // Unparsed data.
22 | this.buf = typeof Buffer.alloc === 'function' ? Buffer.alloc(0) : new Buffer(0);
23 | // Parsed length.
24 | this.len = null;
25 | }
26 |
27 | util.inherits(Input, stream.Transform);
28 |
29 | Input.prototype._transform = function(chunk, encoding, done) {
30 | // Save this chunk.
31 | this.buf = Buffer.concat([ this.buf, chunk ]);
32 |
33 | var self = this;
34 |
35 | function parseBuf() {
36 | // Do we have a length yet?
37 | if (typeof self.len !== 'number') {
38 | // Nope. Do we have enough bytes for the length?
39 | if (self.buf.length >= 4) {
40 | // Yep. Parse the bytes.
41 | self.len = self.buf.readUInt32LE(0);
42 | // Remove the length bytes from the buffer.
43 | self.buf = self.buf.slice(4);
44 | }
45 | }
46 |
47 | // Do we have a length yet? (We may have just parsed it.)
48 | if (typeof self.len === 'number') {
49 | // Yep. Do we have enough bytes for the message?
50 | if (self.buf.length >= self.len) {
51 | // Yep. Slice off the bytes we need.
52 | var message = self.buf.slice(0, self.len);
53 | // Remove the bytes for the message from the buffer.
54 | self.buf = self.buf.slice(self.len);
55 | // Clear the length so we know we need to parse it again.
56 | self.len = null;
57 | // Parse the message bytes.
58 | var obj = JSON.parse(message.toString());
59 | // Enqueue it for reading.
60 | self.push(obj);
61 | // We could have more messages in the buffer so check again.
62 | parseBuf();
63 | }
64 | }
65 | }
66 |
67 | // Check for a parsable buffer (both length and message).
68 | parseBuf();
69 |
70 | // We're done.
71 | done();
72 | };
73 |
74 | function Output() {
75 | stream.Transform.call(this);
76 |
77 | this._writableState.objectMode = true;
78 | this._readableState.objectMode = false;
79 | }
80 |
81 | util.inherits(Output, stream.Transform);
82 |
83 | Output.prototype._transform = function(chunk, encoding, done) {
84 | var len = typeof Buffer.alloc === 'function' ? Buffer.alloc(4) : new Buffer(4);
85 | var buf = typeof Buffer.from === 'function'
86 | ? Buffer.from(JSON.stringify(chunk))
87 | : new Buffer(JSON.stringify(chunk));
88 |
89 | len.writeUInt32LE(buf.length, 0);
90 |
91 | this.push(Buffer.concat([len, buf]));
92 |
93 | done();
94 | };
95 |
96 | function Transform(handler) {
97 | stream.Transform.call(this);
98 |
99 | this._writableState.objectMode = true;
100 | this._readableState.objectMode = true;
101 |
102 | this.handler = handler;
103 | }
104 |
105 | util.inherits(Transform, stream.Transform);
106 |
107 | Transform.prototype._transform = function(msg, encoding, done) {
108 | this.handler(msg, this.push.bind(this), done);
109 | };
110 |
111 | function Debug() {
112 | stream.Transform.call(this);
113 |
114 | this._writableState.objectMode = true;
115 | this._readableState.objectMode = false;
116 | }
117 |
118 | util.inherits(Debug, stream.Transform);
119 |
120 | Debug.prototype._transform = function(chunk, encoding, done) {
121 | this.push(JSON.stringify(chunk) + '\n');
122 |
123 | done();
124 | };
125 |
126 | exports.Input = Input;
127 | exports.Output = Output;
128 | exports.Transform = Transform;
129 | exports.Debug = Debug;
130 |
--------------------------------------------------------------------------------
/json2msg.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var fs = require('fs');
4 | var readline = require('readline');
5 |
6 | var nativeMessage = require('./index');
7 |
8 | var output = new nativeMessage.Output();
9 |
10 | readline.createInterface({
11 | input: process.stdin,
12 | output: output,
13 | terminal: false
14 | }).on('line', function(line) {
15 | output.write(JSON.parse(line));
16 | });
17 |
18 | output.pipe(process.stdout);
19 |
--------------------------------------------------------------------------------
/msg2json.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var nativeMessage = require('./index');
4 |
5 | process.stdin
6 | .pipe(new nativeMessage.Input())
7 | .pipe(new nativeMessage.Debug())
8 | .pipe(process.stdout)
9 | ;
10 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chrome-native-messaging",
3 | "version": "0.2.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "balanced-match": {
8 | "version": "1.0.0",
9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
11 | "dev": true
12 | },
13 | "brace-expansion": {
14 | "version": "1.1.11",
15 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
16 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
17 | "dev": true,
18 | "requires": {
19 | "balanced-match": "1.0.0",
20 | "concat-map": "0.0.1"
21 | }
22 | },
23 | "concat-map": {
24 | "version": "0.0.1",
25 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
26 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
27 | "dev": true
28 | },
29 | "deep-equal": {
30 | "version": "1.1.1",
31 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
32 | "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
33 | "dev": true,
34 | "requires": {
35 | "is-arguments": "1.0.4",
36 | "is-date-object": "1.0.2",
37 | "is-regex": "1.0.5",
38 | "object-is": "1.0.2",
39 | "object-keys": "1.1.1",
40 | "regexp.prototype.flags": "1.3.0"
41 | }
42 | },
43 | "define-properties": {
44 | "version": "1.1.3",
45 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
46 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
47 | "dev": true,
48 | "requires": {
49 | "object-keys": "1.1.1"
50 | }
51 | },
52 | "defined": {
53 | "version": "1.0.0",
54 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
55 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
56 | "dev": true
57 | },
58 | "dotignore": {
59 | "version": "0.1.2",
60 | "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz",
61 | "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==",
62 | "dev": true,
63 | "requires": {
64 | "minimatch": "3.0.4"
65 | }
66 | },
67 | "es-abstract": {
68 | "version": "1.17.4",
69 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
70 | "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
71 | "dev": true,
72 | "requires": {
73 | "es-to-primitive": "1.2.1",
74 | "function-bind": "1.1.1",
75 | "has": "1.0.3",
76 | "has-symbols": "1.0.1",
77 | "is-callable": "1.1.5",
78 | "is-regex": "1.0.5",
79 | "object-inspect": "1.7.0",
80 | "object-keys": "1.1.1",
81 | "object.assign": "4.1.0",
82 | "string.prototype.trimleft": "2.1.1",
83 | "string.prototype.trimright": "2.1.1"
84 | }
85 | },
86 | "es-to-primitive": {
87 | "version": "1.2.1",
88 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
89 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
90 | "dev": true,
91 | "requires": {
92 | "is-callable": "1.1.5",
93 | "is-date-object": "1.0.2",
94 | "is-symbol": "1.0.3"
95 | }
96 | },
97 | "for-each": {
98 | "version": "0.3.3",
99 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
100 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
101 | "dev": true,
102 | "requires": {
103 | "is-callable": "1.1.5"
104 | }
105 | },
106 | "fs.realpath": {
107 | "version": "1.0.0",
108 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
109 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
110 | "dev": true
111 | },
112 | "function-bind": {
113 | "version": "1.1.1",
114 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
115 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
116 | "dev": true
117 | },
118 | "glob": {
119 | "version": "7.1.6",
120 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
121 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
122 | "dev": true,
123 | "requires": {
124 | "fs.realpath": "1.0.0",
125 | "inflight": "1.0.6",
126 | "inherits": "2.0.4",
127 | "minimatch": "3.0.4",
128 | "once": "1.4.0",
129 | "path-is-absolute": "1.0.1"
130 | }
131 | },
132 | "has": {
133 | "version": "1.0.3",
134 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
135 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
136 | "dev": true,
137 | "requires": {
138 | "function-bind": "1.1.1"
139 | }
140 | },
141 | "has-symbols": {
142 | "version": "1.0.1",
143 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
144 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
145 | "dev": true
146 | },
147 | "inflight": {
148 | "version": "1.0.6",
149 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
150 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
151 | "dev": true,
152 | "requires": {
153 | "once": "1.4.0",
154 | "wrappy": "1.0.2"
155 | }
156 | },
157 | "inherits": {
158 | "version": "2.0.4",
159 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
160 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
161 | "dev": true
162 | },
163 | "is-arguments": {
164 | "version": "1.0.4",
165 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
166 | "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
167 | "dev": true
168 | },
169 | "is-callable": {
170 | "version": "1.1.5",
171 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
172 | "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
173 | "dev": true
174 | },
175 | "is-date-object": {
176 | "version": "1.0.2",
177 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
178 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
179 | "dev": true
180 | },
181 | "is-regex": {
182 | "version": "1.0.5",
183 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
184 | "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
185 | "dev": true,
186 | "requires": {
187 | "has": "1.0.3"
188 | }
189 | },
190 | "is-symbol": {
191 | "version": "1.0.3",
192 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
193 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
194 | "dev": true,
195 | "requires": {
196 | "has-symbols": "1.0.1"
197 | }
198 | },
199 | "minimatch": {
200 | "version": "3.0.4",
201 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
202 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
203 | "dev": true,
204 | "requires": {
205 | "brace-expansion": "1.1.11"
206 | }
207 | },
208 | "minimist": {
209 | "version": "1.2.0",
210 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
211 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
212 | "dev": true
213 | },
214 | "object-inspect": {
215 | "version": "1.7.0",
216 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
217 | "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
218 | "dev": true
219 | },
220 | "object-is": {
221 | "version": "1.0.2",
222 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
223 | "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==",
224 | "dev": true
225 | },
226 | "object-keys": {
227 | "version": "1.1.1",
228 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
229 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
230 | "dev": true
231 | },
232 | "object.assign": {
233 | "version": "4.1.0",
234 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
235 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
236 | "dev": true,
237 | "requires": {
238 | "define-properties": "1.1.3",
239 | "function-bind": "1.1.1",
240 | "has-symbols": "1.0.1",
241 | "object-keys": "1.1.1"
242 | }
243 | },
244 | "once": {
245 | "version": "1.4.0",
246 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
247 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
248 | "dev": true,
249 | "requires": {
250 | "wrappy": "1.0.2"
251 | }
252 | },
253 | "path-is-absolute": {
254 | "version": "1.0.1",
255 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
256 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
257 | "dev": true
258 | },
259 | "path-parse": {
260 | "version": "1.0.6",
261 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
262 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
263 | "dev": true
264 | },
265 | "regexp.prototype.flags": {
266 | "version": "1.3.0",
267 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
268 | "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
269 | "dev": true,
270 | "requires": {
271 | "define-properties": "1.1.3",
272 | "es-abstract": "1.17.4"
273 | }
274 | },
275 | "resolve": {
276 | "version": "1.14.2",
277 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz",
278 | "integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==",
279 | "dev": true,
280 | "requires": {
281 | "path-parse": "1.0.6"
282 | }
283 | },
284 | "resumer": {
285 | "version": "0.0.0",
286 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz",
287 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=",
288 | "dev": true,
289 | "requires": {
290 | "through": "2.3.8"
291 | }
292 | },
293 | "string.prototype.trim": {
294 | "version": "1.2.1",
295 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz",
296 | "integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==",
297 | "dev": true,
298 | "requires": {
299 | "define-properties": "1.1.3",
300 | "es-abstract": "1.17.4",
301 | "function-bind": "1.1.1"
302 | }
303 | },
304 | "string.prototype.trimleft": {
305 | "version": "2.1.1",
306 | "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
307 | "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
308 | "dev": true,
309 | "requires": {
310 | "define-properties": "1.1.3",
311 | "function-bind": "1.1.1"
312 | }
313 | },
314 | "string.prototype.trimright": {
315 | "version": "2.1.1",
316 | "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
317 | "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
318 | "dev": true,
319 | "requires": {
320 | "define-properties": "1.1.3",
321 | "function-bind": "1.1.1"
322 | }
323 | },
324 | "tape": {
325 | "version": "4.13.0",
326 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.13.0.tgz",
327 | "integrity": "sha512-J/hvA+GJnuWJ0Sj8Z0dmu3JgMNU+MmusvkCT7+SN4/2TklW18FNCp/UuHIEhPZwHfy4sXfKYgC7kypKg4umbOw==",
328 | "dev": true,
329 | "requires": {
330 | "deep-equal": "1.1.1",
331 | "defined": "1.0.0",
332 | "dotignore": "0.1.2",
333 | "for-each": "0.3.3",
334 | "function-bind": "1.1.1",
335 | "glob": "7.1.6",
336 | "has": "1.0.3",
337 | "inherits": "2.0.4",
338 | "is-regex": "1.0.5",
339 | "minimist": "1.2.0",
340 | "object-inspect": "1.7.0",
341 | "resolve": "1.14.2",
342 | "resumer": "0.0.0",
343 | "string.prototype.trim": "1.2.1",
344 | "through": "2.3.8"
345 | }
346 | },
347 | "through": {
348 | "version": "2.3.8",
349 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
350 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
351 | "dev": true
352 | },
353 | "wrappy": {
354 | "version": "1.0.2",
355 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
356 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
357 | "dev": true
358 | }
359 | }
360 | }
361 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chrome-native-messaging",
3 | "version": "0.2.0",
4 | "author": "Jason Diamond ",
5 | "licenses": [
6 | {
7 | "type": "MIT",
8 | "url": "http://jdiamond.mit-license.org/"
9 | }
10 | ],
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/jdiamond/chrome-native-messaging.git"
14 | },
15 | "description": "Transform streams for writing Chrome App native messaging hosts in Node.js",
16 | "keywords": [
17 | "chrome-app",
18 | "native-messaging"
19 | ],
20 | "main": "index.js",
21 | "dependencies": {},
22 | "devDependencies": {
23 | "tape": "^4.13.0"
24 | },
25 | "scripts": {
26 | "test": "node test.js"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var test = require('tape');
4 |
5 | var nativeMessaging = require('./index');
6 |
7 | test('Input', function(t) {
8 | var buf =typeof Buffer.alloc === 'function' ? Buffer.alloc(17) : new Buffer(17);
9 | buf.writeUInt32LE(13, 0);
10 | buf.write('{"foo":"bar"}', 4);
11 |
12 | var input = new nativeMessaging.Input();
13 |
14 | input.once('readable', function() {
15 | var obj = input.read();
16 | t.equal(obj.foo, 'bar');
17 | t.end();
18 | });
19 |
20 | input.end(buf);
21 | });
22 |
23 | test('Output', function(t) {
24 | var output = new nativeMessaging.Output();
25 |
26 | output.once('readable', function() {
27 | var buf = output.read();
28 | t.equal(buf.readUInt32LE(0), 13);
29 | t.equal(buf.slice(4).toString(), '{"foo":"bar"}');
30 | t.end();
31 | });
32 |
33 | output.end({ foo: 'bar' });
34 | });
35 |
36 | test('Transform', function(t) {
37 | var transform = new nativeMessaging.Transform(function(msg, push, done) {
38 | push({ output: msg.input.toUpperCase() });
39 | done();
40 | });
41 |
42 | transform.once('readable', function() {
43 | var obj = transform.read();
44 | t.equal(obj.output, 'data'.toUpperCase());
45 | t.end();
46 | });
47 |
48 | transform.end({ input: 'data' });
49 | });
50 |
--------------------------------------------------------------------------------
/test.json:
--------------------------------------------------------------------------------
1 | { "msg": 1 }
2 | { "msg": 2 }
3 | { "readdir": "/" }
4 | { "msg": 3 }
5 | { "msg": 4 }
6 |
--------------------------------------------------------------------------------
/test.msg:
--------------------------------------------------------------------------------
1 | {"msg":1} {"msg":2} {"readdir":"/"} {"msg":3} {"msg":4}
--------------------------------------------------------------------------------