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 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-red-builder

v1.4.0

Published

Core runtime and CLI for Node-RED Builder - develop Node-RED nodes with Hot Reload and IDE-friendly JSDoc types

Readme

node-red-builder

🌐 English | Русский

npm license

CLI and runtime framework for building Node-RED nodes. Exposes the nrb binary and provides base classes for node development.


CLI

The nrb binary is available after installing the package. It is typically invoked via npm scripts (see generated package.json below) or with bunx nrb.

nrb init [projectDir] [--example]

Scaffolds a new project in the current directory or in projectDir if specified. Skips files that already exist.

By default, it creates a clean project structure. Use the --example flag to also generate an example node.

Generates:

  • package.json with scripts and empty node-red.nodes
  • tsconfig.json for TypeScript and JSDoc type checking
  • node-red-builder.config.js
  • .gitignore

With --example, it also generates:

  • src/nodes/example/runtime.js, ui.js, template.html
  • src/locales/en-US/example.json
  • docs/en-US/nodes/example.md

The prefix is automatically inferred from the directory name or the root package.json name if in a monorepo. Prefixes like node-red-contrib- and node-red- are stripped.

This command is called internally by create-node-red.


nrb add [name] [--type <t>] [--config-node <n>] [--skip-config]

Adds a new node to an existing project. If type is config and name is omitted, it defaults to config.

  • <name> — Kebab-case name of the node (e.g., my-device).
  • --typenode (default) or config.
  • --config-node — Pre-select a config node to link with (skips prompt).
  • --skip-config — Do not link with any config node (skips prompt).

The tool automatically detects existing configuration nodes and offers to link the new node to one of them. When linked, the generator automatically adds JSDoc imports and correct @extends parameters to runtime.js for full type safety in the editor.

Creates:

  • src/nodes/<name>/runtime.js
  • src/nodes/<name>/ui.js
  • src/nodes/<name>/template.html
  • src/locales/en-US/<name>.json
  • docs/en-US/nodes/<name>.md (for functional nodes only)

Also adds the entry to package.json:

"node-red": {
    "nodes": { "<name>": "dist/nodes/<name>.js" }
}
bunx nrb add temperature-sensor
bunx nrb add --type config

nrb build

For each directory found in src/nodes/:

  1. Bundles runtime.js with Bun (Node.js target, minified). Packages listed in dependencies are marked as external.
  2. Bundles ui.js for the browser and assembles it together with template.html into a single dist/nodes/<n>.html.
  3. Copies icons from src/nodes/<n>/icons/ if the directory exists.
  4. Copies locale files: src/locales/<lang>/<n>.jsondist/nodes/locales/<lang>/<n>.json
  5. Converts help docs: docs/<lang>/nodes/<n>.mddist/nodes/locales/<lang>/<n>.html (H1 is stripped; remaining headings shift down one level)

nrb dev [--port <number>]

Runs nrb build, starts Node-RED, then watches src/, docs/, src/locales/, and config files for changes. On any change, rebuilds and restarts Node-RED (debounced at 300 ms).

The port is read from node-red-builder.config.js or defaults to 3000. Use the --port flag to override.


nrb start [--port <number>]

Sets up the dev environment and starts Node-RED without building or watching.

The port is read from node-red-builder.config.js or defaults to 3000. Use the --port flag to override.

On SIGINT / SIGTERM:

  • Credentials are saved to .cred.json
  • Flows saved in the Node-RED library are moved to examples/

Dev Environment

Both nrb dev and nrb start use .dev/ as the Node-RED user directory. On each start:

  1. Creates .dev/ if absent.
  2. Symlinks examples/.dev/lib/flows (if it exists), making example flows available in the Node-RED library.
  3. Copies .cred.json.dev/flows_cred.json.
  4. On first run (no .dev/flows.json yet), generates it by merging all examples/*.json files, deduplicating tab IDs.
  5. Symlinks the current package into .dev/node_modules/<name>.

Configuration

node-red-builder.config.js (ESM, default export):

export default {
    prefix: 'my',
    port: 3000,
    srcDir: 'src',
    distDir: 'dist',
    docsDir: 'docs',
    localesDir: 'src/locales',
    palette: {
        color: '#a6bbcf', // default node color
    },
};

All paths are resolved relative to process.cwd(). The palette.color is used as a template variable __COLOR__ when generating new nodes. The file is optional; defaults are used if absent.


Generated package.json

nrb init generates the following package.json:

{
    "name": "node-red-contrib-<prefix>",
    "version": "1.0.0",
    "type": "module",
    "scripts": {
        "start":   "node-red-builder start",
        "dev":     "node-red-builder dev",
        "build":   "node-red-builder build",
        "prepack": "bun run build"
    },
    "node-red": {
        "version": ">=3.0.0",
        "nodes": {}
    },
    "files": ["./dist/", "./examples/"],
    "devDependencies": {
        "node-red":         "latest",
        "node-red-builder": "latest"
    },
    "engines": { "node": ">=18.0.0" }
}

Runtime API

Import in runtime.js (Node.js side):

import {
    BaseNode,
    BaseConfigNode,
    registerNode,
} from 'node-red-builder';

BaseNode

Base class for functional nodes. The input event is only registered if onInput is overridden in the subclass. Errors thrown inside onInput are caught, passed to done(), and the node status is set to red automatically.

/** @import { NodeMessage } from 'node-red' */
/** @import { NodeProps as BaseProps } from 'node-red-builder' */

/**
 * @typedef {BaseProps & {
 *   action: string,
 *   actionType: string,
 * }} NodeProps
 */

/** @extends {BaseNode<NodeProps>} */
export class MyNode extends BaseNode {
    /** @param {NodeMessage} msg */
    async onInput(msg, send) {
        const action = await this.getProp('action', msg);
        msg.payload = action;
        send(msg);
        this.setStatus('ok', 'green');
        this.clearStatus(1_500);
    }

    async onClose(removed) {}
}

export default (RED) => registerNode(RED, 'my-node', MyNode);

Properties

| Property | Type | Description | |---|---|---| | RED | NodeAPI | Node-RED API | | node | Node | The Node-RED node instance | | props | TProps | Node configuration from the editor | | config | TConfigNode \| undefined | Linked config node | | client | inferred | config.getClient() shortcut |

client throws if the config node or its client is unavailable.

getProp(key, msg)

Resolves props[key] using props[keyType] via RED.util.evaluateNodeProperty. Supports all standard Node-RED typed input types (str, num, bool, json, msg, flow, global, env, jsonata, cred, …).

const action = await this.getProp('action', msg);

getProps(keys, msg)

Resolves multiple properties in parallel. Returns Record<string, any>.

const { action, topic } = await this.getProps(
    ['action', 'topic'], msg,
);

setStatus(text, fill?, shape?)

Sets the node status badge. Text is truncated to 32 chars. Defaults: fill = 'green', shape = 'dot'.

clearStatus(delay?)

Clears the status badge immediately or after delay ms.

Lifecycle hooks

| Method | Called when | |---|---| | onInput(msg, send) | A message arrives | | onClose(removed) | Node closes or Node-RED restarts |


BaseConfigNode

Base class for configuration nodes.

/** @extends {BaseConfigNode<ConfigNodeDef, Credentials, MyClient>} */
export class MyConfigNode extends BaseConfigNode {
    constructor(node, config, RED) {
        super(node, config, RED);
        this.initClient(MyClient, {
            token: node.credentials.token,
        });
        this.registerRoute('get', '/status', (req, res) => {
            res.json({ ok: true });
        });
    }

    async onClose(removed) {}
}

export default (RED) =>
    registerNode(RED, 'my-config', MyConfigNode, {
        token: { type: 'password' },
    });

initClient(ClientClass, options)

Creates new ClientClass(options), stores it, returns it. If the instance emits an 'auth' event, credentials are saved automatically via RED.nodes.addCredentials.

getClient()

Returns the stored client or null.

registerRoute(method, path, handler, permission?)

Registers a route on RED.httpAdmin at:

/<namespace>/<nodeId><path>

permission defaults to 'read'. Auth middleware is applied automatically. Returns the full path string.

On node close, all registered routes are removed from the Express router automatically.

namespace

Derived from config.type — everything before the last -. For example, my-configmy.

Lifecycle hook

Override onClose(removed) for teardown. Client event listeners are removed automatically.


registerNode(RED, typeName, NodeClass, credentials?)

Registers a class-based node with Node-RED. The constructor receives (node, config, RED). The instance is stored on node.instance, allowing config nodes to expose their client to dependent nodes via getClient().

export default (RED) =>
    registerNode(RED, 'my-sensor', MySensorNode);

// with credentials:
export default (RED) =>
    registerNode(RED, 'my-config', MyConfigNode, {
        apiKey: { type: 'password' },
    });

UI Helpers

Import in ui.js (browser side):

import {
    setupTypedInput,
    createTypedInputOptions,
} from 'node-red-builder/ui';

setupTypedInput(elementId, types?)

Initialises a typedInput widget on #<elementId> and wires it to the hidden #<elementId>Type field.

types defaults to ['str', 'msg', 'flow', 'global'].

setupTypedInput('node-input-topic');
setupTypedInput('node-input-value', ['str', 'num', 'msg']);

createTypedInputOptions(items, getLabel?)

Converts an object or array to the options format used by typedInput type definitions.

For objects, getLabel(key, value) receives the object key and value. For arrays, both arguments receive the item value.

const ACTION = { APPLY: 'apply', RESTART: 'restart' };

setupTypedInput('node-input-action', [{
    value: 'action',
    options: createTypedInputOptions(ACTION, (key) =>
        this._(`node.action.${key.toLowerCase()}`),
    ),
}, 'str', 'msg']);

Types

Declaration files are in types/ (built from src/ via tsc), exposed via exports:

| Import | Types | |---|---| | node-red-builder | types/runtime/index.d.ts | | node-red-builder/config | types/cli/config.d.ts | | node-red-builder/ui | types/ui/index.d.ts | | node-red-builder/cli/<cmd> | types/cli/commands/<cmd>.d.ts |