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

js-host

v0.12.0

Published

Configurable JavaScript host

Downloads

19

Readme

js-host

Build Status

Provides a configurable JavaScript host which exposes functions to network requests. Intended to provide the low-level bindings for other languages to access a JavaScript environment.

There are a variety of projects offering execution of JavaScript (ExecJS et al), but their performance tends to lag as they typically spawn new environments on every call.

Using a persistent JavaScript environment enables massive performance improvements as we can persist state across calls and avoid the overhead of spawning environments.

Installation

npm install js-host

Basic usage

Create a file host.config.js which will contain the configuration for the host.

module.exports = {
  functions: {
    hello_world: function(data, cb) {
      cb(null, 'Hello, World!');
    }
  }
};

Start the host with:

node_modules/.bin/js-host host.config.js

And call hello_world by sending a POST request to http://127.0.0.1:9009/function/hello_world.

Documentation

CLI usage

A js-host file is placed into node_modules/.bin which allows you to interact with library at a high-level. Most of the interactions will require you to specify a path to a config file.

You can start a host process by invoking js-host with a config file. For example

node_modules/.bin/js-host host.config.js

The following arguments are accepted:

|    | Alias | Description | | :----- | :---- | :---------- | | -c | --config | Read in the config file and output its generated config as JSON | | -j | --json | Once the process has started, output its generated config as JSON | | -p | --port | Override the config's port and run the process at the number specified | | -l | --logfile | Adds a file transport to the process's logger which will write to the specified file | | -m | --manager | Run a manager process, rather than a host | | -d | --detached | Run in a detached process | | -v | --version | Output the package's version | | -h | --help | Output help text |

If you want run a host with an interactive debugger, you should start the host with Node's debugger. For example

node debug node_modules/.bin/js-host host.config.js

Place a debugger statement where you want to block the process and inspect the environment.

Config files

Config files are simply JS files which export an object, for example:

module.exports = {
  port: 8080,
  functions: {
    my_func: function(data, cb) {
      // ...
    }
  }
};

Config objects may possess the following properties:

functions: a key/value object with names -> functions.

address: the address that the host will listen at. Defaults to '127.0.0.1'.

port: the port number that the host will listen at. Defaults to 9009.

requestDataLimit: the maximum size allowed for a request's body. Defaults to '10mb'.

logger: an object which will be used instead of the default logger. The object must provide a similar API to JavaScript's console object, eg: it must provide functions named log, error, info, etc. Defaults to null.

disconnectTimeout: the number of milliseconds that a manager will wait before stopping a host without any open connections. Defaults to 5 * 1000 // 5 seconds.

If you want to pass configuration to a function, you can add extra properties to the config object, and then access them in your function via the this binding. For example

module.exports = {
  functions: {
    some_func: function(data, cb) {
      if (this.host.config.production) {
        // ...
      } else {
        // ...
      }
    }
  }
  production: true
};

If you want to use environment-specific config files, you can import a default file and then override or add settings specific to that environment. For example

var _ = require('lodash');
// Import your default config
var defaultConfig = require('./host.config');

module.exports = _.defaults({
  // Override the logger with one specific to this environment
  logger: // ...
}, defaultConfig);

Functions

The functions definition of a host config is a simple map which enables network requests to be passed to functions. When a request is matched to a function, the function is called with two arguments:

  • data is an object generated from deserializing any data sent in the request's body.
  • cb is a function which enables you to indicate that your function has either completed successfully, or encountered an error. Following the argument pattern typical in function-based languages, cb assumes that the first argument indicates an error, and the second argument indicates success.

Sending a success response

Once your function has completed successfully, you should pass a value to cb as the second argument. For example:

cb(null, {status: 'success'});

If the value provided as the second argument is falsey - null, false, undefined, 0 - the host will assume that a mistake was made in your code, and will return an error response.

Note: the value of the second argument is sent back to the caller as a text response. If the value is an object, it will be serialized to JSON. All non-object types are coerced to strings.

Handling errors

You should try to gracefully handle any errors encountered as uncaught errors may cause the host to assume the worst and exit immediately. If you encounter an error condition, pass an Error object to cb, and let the host handle it. If you need to execute code that may throw errors, use a try/catch to pass the error to the host and then exit your function. For example:

function(data, cb) {
  try {
    dangerousFunc();
  } catch(err) {
    return cb(err);
  }
  cb(null, 'ok');
};

Note: if you use a try/catch statement, remember to exit the function with return when sending errors back.

If you encounter a condition deserving of an error, always provide Error objects rather than strings. For example:

// Bad
cb('Something bad happened');

// Good
cb(new Error('Something bad happened'));

By using Error objects, the host is able to provide an accurate stack trace, indicating the source of the error.

Accessing the host from a function

Functions can access the host via this.host. For example:

// Write to the host's logs
function(data, cb) {
  this.host.logger.info('Something happened');
  this.host.logger.warn('Something bad might happen');
  this.host.logger.error('Something bad happened');
};

Note: the this binding will not be passed along to other functions that you may call. You need to either pass values explicitly or pass the this binding along. For example:

function(data, cb) {
  this.host.logger.info('Started my function');
  
  // Pass values explicitly
  logData(this.host.logger, data);
  
  // Create a new function which uses the outer `this` binding
  someAsyncFunc(function(err, res) {
    if (err) {
      this.host.logger.error('Something bad happened');
      return cb(err);
    }
    cb(null, res);
  }.bind(this));
}

function logData(logger, data) {
  logger.info('So much data...', data);
};

Note: the this binding of your function is generated per-request, hence any values that you may add to the this object will not be passed along to other requests.

Logging

By default, js-host instances will only write their logs to stdout and stderr. If you want the log output streamed to files, you are strongly recommended to define the logger property in your config file.

Interally, js-host uses winston as the default logger. If you define the logger property of your config, js-host will use your logger instead of its own. For example

var winston = require('winston');

module.exports = {
  // ...
  logger: new winston.Logger({
    transports: [
      // Write to stdout and stderr
      new (winston.transports.Console)(),
      // Write to a file as well
      new (winston.transports.File)({ filename: 'somefile.log' })
    ]
  });
};

Calling functions via the network

Functions are exposed to POST requests at the /function/<name> endpoint.

To send data: set the request's content-type to application/json and pass JSON as the request's body.

If the function indicated success, a 200 response will be returned with the function's output as the response's text.

If the function returned an error condition, a 500 response will be returned, with a stack trace as the response's text.

Endpoints

Hosts and managers communicate via HTTP and provide a number of endpoints that perform particular functions.

Hosts use the following endpoints

| Method | Endpoint | Description | | :----- | :------- | :---------- | | GET | /status | Returns a JSON object describing the host and environment | | POST | /function/<name> | Passes the request's body to the function matched to <name> and returns its output |

Managers use the following endpoints

| Method | Endpoint | Description | | :----- | :------- | :---------- | | GET | /status | Returns a JSON object describing the manager and environment | | POST | /manager/stop | Causes the manager to stop every host and then stop its own process. Returns text | | POST | /host/start | Starts a host. Returns a JSON object describing the host. * | | POST | /host/stop | Stops a host. Returns a JSON object describing the host. * | | POST | /host/restart | Restarts a host. Returns a JSON object describing the host. * | | POST | /host/status | Provides information about a host. Returns a JSON object indicating if the host is running and all information about it. * | | POST | /host/connect | Opens a new connection to a host. Returns a JSON object containing the connection identifier and information about the host. * | | POST | /host/disconnect | Closes a connection to a host. Returns a JSON object indicating if/when the host will be stopped. Requires a connection identifier as a connection property. * |