Protocol-registry
Registers protocols like:- yourapp:// or myapp:// etc. to open your nodejs app from different browsers.
This is meant to be used in command-line tools and scripts, not in the browser.
Why?
- Actively maintained.
- Handles Cross Platform.
- Supports WSL paths to Windows apps.
- Handles multi-line commands.
- Works on electron.
Install
$ npm install protocol-registry
Usage
const path = require("path");
const ProtocolRegistry = require("protocol-registry");
console.log("Registering...");
// Registers the Protocol
ProtocolRegistry.register({
protocol: "testproto", // sets protocol for your command , testproto://**
command: `node ${path.join(__dirname, "./tester.js")} $_URL_`, // this will be executed with a extra argument %url from which it was initiated
override: true, // Use this with caution as it will destroy all previous Registrations on this protocol
terminal: true, // Use this to run your command inside a terminal
script: false,
scriptName: 'my-custom-script-name' // Custom script name.
}).then(async () => {
console.log("Successfully registered");
});
Note : Refrain from using query to get data from the url, click here to view some alternatives.
Alternative 1
Instead you can use routing params to get the data from the url, described in the example below:
Original Way : testproto://test?a=b&b=c
Must use : testproto://test/-a/b/-b/c
As it is more CLI friendly.
Example :
const url = "testProto://-l/Hindi";
const { ArgumentParser } = require("argparse");
const { version } = require("./package.json");
const protocols = ["testProto", "test"];
const defaultProtocol = "testProto";
const parser = new ArgumentParser({
description: "Example",
});
parser.add_argument("-v", "--version", { action: "version", version });
parser.add_argument("mode", {
type: String,
choices: protocols,
nargs: "?",
default: defaultProtocol,
});
parser.add_argument("-l", "--lang", {
help: "Choose the language.",
choices: ["Hindi", "English"],
});
const data = parser.parse_args(url.split(/:\/\/|[\/]/g));
console.log(JSON.stringify(data, null, 4));
// {
// "mode": "testProto",
// "lang": "Hindi"
// }
Alternative 2
Use can use base64 encryption to transmit data and decode it there.
const encode = (str) => Buffer.from(str).toString('base64');
const decode = (str) => Buffer.from(str, 'base64').toString();
const protocol = 'testproto://';
const encoded = encode(JSON.stringify({ mode: 'testProto', lang: 'Hindi' }));
const url = `${protocol}${encoded}`;
console.log(url);
// testproto://eyJtb2RlIjoidGVzdFByb3RvIiwibGFuZyI6IkhpbmRpIn0=
const data = url.split('://')[1];
const decoded = JSON.parse(decode(data));
console.log(decoded);
// { mode: 'testProto', lang: 'Hindi' }
On Electron :
On electron, you can use the protocol-registry to open your app through custom protocols.
Note : Electron's built-in app.setAsDefaultProtocolClient
is recommended to be used in production but as it has some issues in development you can use ProtocolRegistry.register
instead while developing.
const dev = require("electron-is-dev");
const ProtocolRegistry = require("protocol-registry")
if (dev) {
ProtocolRegistry
.register({
protocol: "testproto",
command: `"${process.execPath}" "${path.resolve(
process.argv[1]
)}" $_URL_`,
override: true,
script: true,
terminal: dev,
})
.then(() => console.log("Successfully registered"))
.catch(console.error);
} else {
if (!app.isDefaultProtocolClient('testproto')) {
app.setAsDefaultProtocolClient('testproto');
}
}
API
At present it supports :
register(options, cb(err))
Options are mentioned in the above example, more details below. If a valid callback is provided then it returns cb(err) Otherwise it returns a promise.
Example
const path = require("path");
const ProtocolRegistry = require("protocol-registry");
// Registers the Protocol
ProtocolRegistry.register({
protocol: "testproto",
command: `node ${path.join(__dirname, "./tester.js")} $_URL_`,
terminal: true,
})
.then(() => {
// do something
})
.catch((e) => {
// do something
});
// Above will run tester.js when testproto://** is called as
// node .../tester.js testproto://**
// you can further parse the url to run in different modes
// As override is not passed true it will throw an error is protocol already exists
ProtocolRegistry.register(
{
protocol: "testproto",
command: `node ${path.join(__dirname, "./tester.js")} $_URL_`,
terminal: true,
},
(err) => {
if (err) {
// do something
}
}
);
// Example with callback
ProtocolRegistry.register(
{
protocol: "testproto",
command: `node ${path.join(__dirname, "./tester.js")} $_URL_`,
terminal: false, // Terminal is set to false
},
(err) => {
if (err) {
// do something
}
}
);
// The above code will run your command in background
// You wont be able to see any logs
// But if your program launches any UI / webpage / file will be visible
const commands = `cd path/to/destination
ls
node ${path.join(__dirname, "./tester.js")} $_URL_
`;
ProtocolRegistry.register(
{
protocol: "testproto",
command: commands,
terminal: true, // Terminal is set to false
script: true, // This will save your commands in a script file and execute it when the protocol is hit.
scriptName: 'my-custom-script-name' // This is the name of the script file that will be created if script option is set true.
},
(err) => {
if (err) {
// do something
}
}
);
// the above code will save your commands to a script file
// and execute it when ever required
// use this for multiline commands
checkifExists(protocol)
Checks if the provided protocol already exists or not. Returns a Promise which resolves in true or false.
Example
const path = require("path");
const ProtocolRegistry = require("protocol-registry");
// Registers the Protocol
ProtocolRegistry.checkifExists("testproto")
.then((res) => {
console.log(res); // true or false
// do something
})
.catch((e) => {
// do something
});
// Above snippet will check it already some app uses the given protocol or not
options
Register function accept the below mentioned option
name | types | default | details |
---|---|---|---|
protocol | String (required) | NA | Only alphabets allowed. Your command will be executed when any url starting with this protocol is opened i.e. "myapp://test","testproto://abcd?mode=dev", etc. And please make sure that the protocol is unique to your application. |
command | String (required) | NA | This command will be executed when the protocol is called. $_URL_ mentioned anywhere in your command will be replaced by the url by which it is initiated. |
override | Boolean | false | If this is not true, then you will get an error that protocol is already being used. So, first check if the protocol exist or not then take action accordingly (Refrain from using it). |
terminal | Boolean | false | If this is set true, then first a terminal is opened and then your command is executed inside it.otherwise your command is executed in background and no logs appear but if your program launches any UI / webpage / file, it will be visible. |
script | Boolean | false | If this is set true, then your command is saved in a script and that script is executed. This option is recommended if you are using multi-line commands or your command uses any kind of quotes. |
scriptName | String | ${protocol} |
This is the name of the script file that will be created if script option is set true. |
Supported platforms
-
Windows
- OS - Supported -
linux
- OS - Supported -
MacOS
- OS - Supported with some anomalies mentioned below.
MacOS Anomalies
terminal: false
In MacOS if you don't launch the terminal it will run your command without logging in.
Thus you need to use absolute address of each command in your command string.
Example
Suppose you want to run :
$ node /path/to/index.js
Then first you need to find the path of node using the command below in terminal :
$ type node
> node is /usr/local/bin/node
Then replace the address of node in original command. So your final command will be :
$ /usr/local/bin/node /path/to/index.js
To check if your program is running in MacOS you can use the code below:
if (process.platform === "darwin") {
// running in MacOS do some thing
}
To run shell commands such as "type node" using nodeJS
please check the ShellJS documentation
Contributors:
Credits goes to these people: ✨