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

node-red-embedded-start

v1.0.1

Published

Enables waiting for flows to have started for embedded Node-RED applications

Downloads

123

Readme

Build Status npm npm david-dm david-dm

node-red-embedded-start

When running Node-RED as an embedded application, the method to start Node-RED (RED.start()) returns (more precisely, the promise it returns resolves) before the runtime API is ready to be interacted with, resulting in obscure internal Node-RED errors (typically, TypeError being thrown) if one calls certain runtime or admin API method too early. This module allows waiting deterministically for the runtime API for flows to be ready.

Use cases

If your primary need is to interact programmatically with the flows API of an embedded Node-RED instance once it starts up, then this module is for you.

One use case likely faced by many developers (such as myself) of nodes for Node-RED is running automatic testing for their node(s). Ideally such testing can include testing discovery, loading, and running of nodes by a live embedded Node-RED instance. Usually, one of the first steps in getting a node to "run" (i.e., Node-RED invoking the constructor for the node type) is to add it to a flow, whether one that exists or one that you create from scratch programmatically.

Another use case would be to programmatically control which nodes are available in the palette, by disabling after startup those that are undesired or unneeded. See node-red/node-red#1221.

Installation

$ npm install --save node-red-embedded-start

If your use case is automatic testing of a Node-RED node, then you will only need this for development and not in production, and hence use --save-dev then instead of --save:

$ npm install --save-dev node-red-embedded-start

Note that the module does not itself have a dependency declared on node-red (except for development, because it is used for testing itself). To use it, you will have to have either a Node-RED instance (usually called RED), or the node-red module you use for your embedded application needs to be installed in a place where this module can find it (meaning either global, or in the same or a higher node_modules/ directory).

Usage

There are two general ways to use this module. One is to insert it into the promise chain from RED.start():

var embeddedStart = require('node-red-embedded-start');
RED.start().then(embeddedStart(RED)).then((result) => {
    // result is whatever RED.start() resolved to
    // RED.node.getFlows() etc are now ready to use
}).catch((err) => {
    if (/^timed out/.test(err.message)) {
        // embeddedStart() timed out
        // the value that RED.start() resolved to is available as err.result
    }
});

The other way is to inject it into RED.start():

var RED = require('node-red');
var embeddedStart = require('node-red-embedded-start');
embeddedStart.inject(RED);

// then use RED.start() just as you would normally
RED.start().then((result) => {
    // RED.node.getFlow() etc are now ready to use
}).catch((err) => {
    // same as above example
});

In either case, the promise returned by the function settles in one of the following three ways (the "result value" is optional and should normally be the value to which RED.start() resolves):

  1. If the flows API is ready already, the promise resolves immediately with the result value passed in.
  2. When the nodes-started event fires, the promise resolves with the result value passed in.
  3. If the timeout is reached before the nodes-started event fires, the promise rejects with an error. If a result value different from undefined is passed in, it is passed through as the result property of the error.

Usage & API details

Generate a wait function: embeddedStart([RED[, timeout]])

var waitFunc = embeddedStart(RED, timeout);

RED.start().then(waitFunc).then(() => {
    // do whatever
});

Usually, because the wait function will only be used once, one will write the call directly into the promise chain:

RED.start().then(embeddedStart(RED, 5000)).then((result) => {
    // do whatever
});
  • RED is the embedded Node-RED instance.
  • timeout is the time in milliseconds after which waiting for the nodes-started event should time out. Optional, default is 30 seconds.
  • If both parameters are omitted, RED will be loaded from the node-red module, and hence if that fails, the call will fail.

The returned function takes one optional parameter, the value that the returned promise should resolve to. Normally, this should be the value to which RED.start() resolves, so that it is passed through.

Call a wait function: embeddedStart([RED, timeout,] result)

RED.start().then((result) => embeddedStart(RED, undefined, result)).then(() => {
    // do whatever
});
  • RED is the embedded Node-RED instance.
  • timeout is the time in milliseconds after which waiting for the nodes-started event should time out. Defaults to 30 seconds if undefined.
  • If only the result parameter is provided, RED will be loaded from the node-red module, and hence if that fails, the call will fail.

Inject a wait function: embeddedStart.inject([RED [, timeout]])

embeddedStart.inject(RED, timeout);

RED.start().then((result) => {
    // do whatever
});
  • RED is the embedded Node-RED instance.
  • timeout is the time in milliseconds after which waiting for the nodes-started event should time out. Optional, default is 30 seconds.
  • If both parameters are omitted, RED will be loaded from the node-red module, and hence if that fails, the call will fail.
  • The injected function will pass through any arguments passed to RED.start(), and also value to which the original method resolves to.

Motivation

Calling the Node-RED runtime flow API methods such as RED.nodes.addFlow() after RED.start() resolves results in a TypeError. RED.nodes.getFlows() returns null, not a valid data structure. One solution could be to wait for some time and hope for the best. Although a couple of seconds will often suffice, the time needed will depend on processor speed, storage speed, number of flows, number of nodes in those flows, and various other factors. A wait-and-hope-for-the-best solution seems therefore unsatisfactory, and nevertheless also requires code to implement.

This issue has been brought to the attention of the Node-RED developers (see Node-RED issues #1168 and #902), but they responded that the behaviour is intentional and will thus not be fixed until the behavior of an embedded application is fully defined and implemented.

This module allows waiting instead for a certain event (nodes-started) to fire. The event fires at a point in time during the Node-RED startup when the code driving the flow API is initialized and thus ready for interaction.

Caveats

  • If you run Node-RED standalone rather than embedded, the problem does not apply in practice, and hence this module will have no benefits for you.
  • The method for waiting implemented here will only work while the nodes-started event fires and at the right timing. Presumably, the tests should indicate when that's no longer the case.
  • Due to the highly asynchronous nature of the Node-RED startup process, the flow API being ready does not (and should not) mean that any other components have fully completed their initialization as well. For example, the constructors of nodes may themselves have launched initialization tasks asynchronously, and there is no way of knowing in which stage these are. See Node-RED issue #698 for discussion.

License

Available under the MIT License.