Skip to content

bonsaedev/nrg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

618 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nrg-icon

npm package build status codecov Socket Badge

Warning

While NRG is at v0, breaking changes can land in any release and will not bump the major version. Pin an exact version and review the release notes before upgrading.

NRG

Build Node-RED nodes with Vue 3, TypeScript, JSON Schemas, Vite and Vitest.

Quick Start

Scaffold a new project with everything wired up:

pnpm create @bonsae/nrg

A node is a config schema (drives the editor form + validation) and a class (the logic). Here's a small greeting node — a few typed fields and one output:

src/shared/schemas/greeting.ts

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

export const ConfigsSchema = defineSchema(
  {
    greeting: SchemaType.String({ default: "Hello", description: "The greeting word placed before the name.", "x-nrg-form": { icon: "comment" } }),
    style: SchemaType.Union(
      [SchemaType.Literal("plain"), SchemaType.Literal("excited"), SchemaType.Literal("friendly")],
      { default: "plain", description: "Tone of the greeting.", "x-nrg-form": { icon: "paint-brush" } },
    ),
    repeat: SchemaType.Number({ default: 1, description: "How many times to repeat the greeting.", "x-nrg-form": { icon: "repeat" } }),
    note: SchemaType.Optional(
      SchemaType.String({ default: "", description: "Optional note shown under the node.", "x-nrg-form": { icon: "pencil" } }),
    ),
  },
  { $id: "GreetingConfigsSchema" },
);

src/server/nodes/greeting.ts

import { IONode, type Infer, type Input, type Outputs, type Port } from "@bonsae/nrg/server";
import { ConfigsSchema } from "@/schemas/greeting";

type Config = Infer<typeof ConfigsSchema>;
type GreetingInput = Input<Port<{ name: string }>>;
type GreetingOutputs = Outputs<{ greeting: Port<{ text: string }> }>;

export default class Greeting extends IONode<Config, never, GreetingInput, GreetingOutputs> {
  static override readonly type = "greeting";
  static override readonly configSchema = ConfigsSchema;

  override async input(msg: GreetingInput) {
    const { greeting, style, repeat } = this.config;
    const suffix = style === "excited" ? "!" : style === "friendly" ? " :)" : "";
    const text = Array.from({ length: repeat }, () => `${greeting}, ${msg.name}${suffix}`).join(" ");
    this.send("greeting", { text });
  }
}

You wrote no editor HTML, no jQuery, no oneditprepare — nrg generates the entire edit dialog from the schema and types above: the style union becomes a dropdown, strings and numbers become validated inputs, every non-Optional field is marked required with a * (so greeting, style, and repeat get one; note, wrapped in Optional, doesn't), and the input/output ports and lifecycle wiring come for free.

The greeting node's editor form, generated by nrg from the schema

A classic Node-RED node hand-writes hundreds of lines of HTML for its edit form and keeps it in sync with the runtime by hand. Here it's derived from your schema and types — so it can't drift, and every field is validated for free.

See the documentation for the full walkthrough — schemas, the generated editor form, testing, and building — or the node-red-salesforce repo for a real-world reference.

License

MIT

About

Create type-safe Node-RED nodes

Topics

Resources

License

Stars

6 stars

Watchers

0 watching

Forks

Contributors