Skip to content

bonsaedev/nrg

Repository files navigation

nrg-icon

npm package build status

nrg

Build Node-RED nodes with Vue 3, TypeScript, and JSON Schema validation.

Install

pnpm add @bonsae/nrg vue
pnpm add -D vite

Package Exports

Export Description
@bonsae/nrg Root entry — defineRuntimeSettings
@bonsae/nrg/server Server node classes, schema utilities, validation (IONode, ConfigNode, defineIONode, defineConfigNode, defineModule, SchemaType, defineSchema, Infer)
@bonsae/nrg/client Client-side registration (registerTypes, defineNode)
@bonsae/nrg/vite Vite plugin for building and developing Node-RED packages
@bonsae/nrg/tsconfig/* Shared TypeScript configurations for consumers

Quick Start

# In your Node-RED package project
pnpm add @bonsae/nrg vue
pnpm add -D vite

vite.config.ts

import { defineConfig } from "vite";
import { nodeRed } from "@bonsae/nrg/vite";

export default defineConfig({
  plugins: [nodeRed()],
});

src/server/schemas/my-node.ts

import { defineSchema, SchemaType } from "@bonsae/nrg/server";

export const ConfigsSchema = defineSchema(
  {
    name: SchemaType.String({ default: "" }),
    prefix: SchemaType.String({ default: "hello" }),
  },
  { $id: "my-node:configs" }
);

src/server/nodes/my-node.ts

NRG supports two ways to define nodes:

Functional APIClass API
import { defineIONode } from "@bonsae/nrg/server";
import { ConfigsSchema } from "../schemas/my-node";

export default defineIONode({
  type: "my-node",
  color: "#ffffff",
  inputs: 1,
  outputs: 1,
  configSchema: ConfigsSchema,

  async input(msg) {
    msg.payload = `${this.config.prefix}: ${msg.payload}`;
    this.send(msg);
  },
});
import { IONode, type Schema, type Infer } from "@bonsae/nrg/server";
import { ConfigsSchema } from "../schemas/my-node";

type Config = Infer<typeof ConfigsSchema>;

export default class MyNode extends IONode<Config> {
  static readonly type = "my-node";
  static readonly category = "function";
  static readonly color: `#${string}` = "#ffffff";
  static readonly inputs = 1;
  static readonly outputs = 1;
  static readonly configSchema: Schema = ConfigsSchema;

  async input(msg: any) {
    msg.payload = `${this.config.prefix}: ${msg.payload}`;
    this.send(msg);
  }
}
Automatic type inference, less boilerplate Custom methods, inheritance, mixins

src/server/index.ts

import { defineModule } from "@bonsae/nrg/server";
import MyNode from "./nodes/my-node";

export default defineModule({
  nodes: [MyNode],
});

See the consumer template for a complete example.

Project Structure

src/
├── core/                        # Runtime framework
│   ├── client/                  # Vue 3 editor components
│   │   ├── app.vue              # Root form wrapper (validation, toggles)
│   │   ├── components/          # Reusable form inputs
│   │   │   ├── node-red-input.vue
│   │   │   ├── node-red-typed-input.vue
│   │   │   ├── node-red-config-input.vue
│   │   │   ├── node-red-select-input.vue
│   │   │   ├── node-red-editor-input.vue
│   │   │   └── node-red-json-schema-form.vue
│   │   └── index.ts             # registerType, defineNode
│   ├── server/                  # Node.js server runtime
│   │   ├── nodes/               # Node, IONode, ConfigNode classes
│   │   ├── schemas/             # TypeBox schema system
│   │   ├── types/               # RED, context store types
│   │   └── index.ts             # registerTypes, exports
│   ├── constants.ts
│   └── validator.ts             # AJV-based validation
├── vite/                        # Build tooling
│   ├── plugin.ts                # Vite plugin factory
│   ├── plugins/                 # Dev server, build orchestration
│   ├── server/                  # Server build (CJS/ESM + bridge)
│   ├── client/                  # Client build (Vue + auto-wiring)
│   └── index.ts                 # nodeRed(), defineRuntimeSettings()
└── tsconfig/                    # Shared configs for consumers
    ├── base.json
    ├── client.json
    └── server.json

Development

pnpm install
pnpm build          # build all (server CJS, client ESM, vite plugin)
pnpm typecheck      # type-check server and client
pnpm lint           # eslint
pnpm format         # prettier

License

MIT