@tauri-liesel/reg
v0.1.5
Published
[](https://www.npmjs.com/package/@tauri-liesel/reg) [](https://crates.io/crates/tauri-plugin-reg) [, making it tolerant of loosely-typed node files.
- 🧩 In-memory
NodeRegistrysingleton — A ready-to-use TypeScript class that acts as an in-memory store: register nodes, look them up byIdentifier, and iterate over all loaded definitions. - 📐 Fully typed API — Complete TypeScript types for
NodeData,NodePort,WireConnection,DataType,PortType, andIdentifier. - 🛡 Tauri capability-based permissions — Fine-grained
allow/denypermission identifiers for every exposed command (reg:allow-load-lsnodes,reg:allow-ping). - 🖥 Desktop & Mobile — Conditional compilation targets for both desktop (direct file I/O) and mobile (delegated to native Android/iOS plugin handles).
- 📦 Dual ESM + CJS build — Ships both ES Module (
.js) and CommonJS (.cjs) bundles with bundled TypeScript declarations (.d.ts). - ⚡ Zero UI dependencies — Pure data layer; no UI framework assumptions. Works with React, Svelte, Vue, or vanilla JS.
📦 Installation
1. Install the JavaScript package
npm install @tauri-liesel/reg
# or
pnpm add @tauri-liesel/reg
# or
yarn add @tauri-liesel/reg
# or
bun add @tauri-liesel/reg2. Add the Rust plugin to your Tauri application
In your Tauri app's src-tauri/Cargo.toml, add:
[dependencies]
tauri-plugin-reg = "0.1.5"Local workspace path (if using from source):
tauri-plugin-reg = { path = "../path/to/tauri-liesel/reg" }
3. Register the plugin in Rust
In src-tauri/src/lib.rs:
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_reg::init())
// ... other plugins
.run(tauri::generate_context!())
.expect("error while running tauri application");
}4. Grant permissions in your Tauri capability
In src-tauri/capabilities/default.json:
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Default capability for the application",
"windows": ["main"],
"permissions": [
"core:default",
"reg:default",
"reg:allow-load-lsnodes"
]
}🚀 Quick Start
Setting up the node directory
Create a nodes/ directory alongside your Tauri application binary. Each .json file in this directory describes one node definition:
my-tauri-app/
├── nodes/
│ ├── prompt_builder.json
│ ├── image_input.json
│ └── llm_output.json
└── src-tauri/
└── ...Example node definition (nodes/prompt_builder.json):
{
"id": "core:prompt-builder",
"title": "Prompt Builder",
"subtitle": "Constructs an LLM prompt",
"x": 0,
"y": 0,
"selected": false,
"headerPreset": "llm",
"nodeType": "promptBuilder",
"icon": "📝",
"inputs": [
{
"id": "in_text",
"name": "Text",
"type": "input",
"dataType": "string",
"enabled": true
}
],
"outputs": [
{
"id": "out_prompt",
"name": "Prompt",
"type": "output",
"dataType": "string",
"enabled": true
}
]
}Using the NodeRegistry in your frontend
import { NodeRegistry } from '@tauri-liesel/reg';
// Load all node definitions from the file system (calls Rust via IPC)
await NodeRegistry.loadFromDisk();
// Get all registered node definitions
const allNodes = NodeRegistry.values();
console.log(allNodes); // NodeData[]
// Look up a specific node by its Identifier ('namespace:name')
const node = NodeRegistry.get('core:prompt-builder');
console.log(node.title); // "Prompt Builder"Registering nodes manually
You can also register node definitions programmatically at runtime:
import { NodeRegistry } from '@tauri-liesel/reg';
import type { NodeData } from '@tauri-liesel/reg';
const myNode: NodeData = {
id: 'custom:my-node',
title: 'My Custom Node',
x: 100,
y: 200,
selected: false,
nodeType: 'custom',
icon: '⭐',
inputs: [],
outputs: [],
};
NodeRegistry.register(myNode);📖 API Reference
JavaScript / TypeScript
NodeRegistry (singleton)
The exported NodeRegistry is a pre-instantiated singleton of the internal NodeReg class.
| Method | Signature | Description |
|--------|-----------|-------------|
| register | (node: NodeData) => void | Adds or overwrites a node in the in-memory registry by its id. |
| get | (id: Identifier) => NodeData | Retrieves a node by its Identifier. Returns a sentinel "Missing Node" if not found. |
| values | () => NodeData[] | Returns an array of all currently registered NodeData objects. |
| loadFromDisk | () => Promise<void> | Invokes the plugin:reg|load_lsnodes Tauri command to load .json node files from the nodes/ directory and registers them all. Errors are silently swallowed. |
TypeScript Types
Identifier
A branded template literal type for namespaced node IDs.
type Identifier = `${string}:${string}`;
// e.g. 'core:prompt-builder', 'custom:my-node'PortType
type PortType = 'input' | 'output';DataType
type DataType =
| 'string'
| 'float'
| 'vector3'
| 'color'
| 'texture'
| 'exec'
| 'bool'
| 'mask';NodePort
Describes a single input or output socket on a node.
interface NodePort {
id: string;
name: string;
type: PortType;
dataType?: DataType;
enabled: boolean;
color?: string; // Custom socket ring color (CSS color string)
value?: string; // Optional default value
}NodeData
The primary node definition shape.
interface NodeData {
id: Identifier;
title: string;
subtitle?: string;
x: number;
y: number;
selected: boolean;
headerPreset?: 'llm' | 'subject' | 'style' | 'setting' | 'lighting' | 'camera' | 'parameters' | 'special';
customHeaderGradient?: string;
inputs: NodePort[];
outputs: NodePort[];
width?: number;
nodeType: string;
icon: string;
}WireConnection
Represents a directed connection between two node ports.
interface WireConnection {
id: string;
fromNodeId: string;
fromPortId: string;
toNodeId: string;
toPortId: string;
color?: string;
}Tauri IPC Commands
These commands are invoked internally by the JS API but can also be called directly via @tauri-apps/api's invoke.
| Command | IPC Call | Description | Returns |
|---------|----------|-------------|---------|
| load_lsnodes | plugin:reg\|load_lsnodes | Reads all .json files from the nodes/ directory relative to the app's working directory and deserializes them into NodeDataDef structs. | NodeDataDef[] |
| ping | plugin:reg\|ping | Diagnostic echo command. Returns the same value that was sent. | PingResponse |
Rust API
The plugin exposes a Ext trait implemented for tauri::App, tauri::AppHandle, and tauri::Window, providing access to the underlying Reg instance:
use tauri_plugin_reg::Ext;
// Inside a Tauri command or event handler:
fn my_command(app: tauri::AppHandle) {
let reg = app.reg(); // -> &Reg<R>
let response = reg.ping(PingRequest { value: Some("hello".into()) });
}🔐 Permissions Reference
All permissions follow Tauri v2's capability-based security model with the reg: prefix.
| Permission Identifier | Description |
|-----------------------|-------------|
| reg:default | Grants the default permission set (currently includes allow-ping). |
| reg:allow-ping | Allows the ping command. |
| reg:deny-ping | Denies the ping command. |
| reg:allow-load-lsnodes | Allows the load_lsnodes command (required for NodeRegistry.loadFromDisk()). |
| reg:deny-load-lsnodes | Denies the load_lsnodes command. |
Note:
reg:allow-load-lsnodesis not included inreg:default. You must explicitly grant it in your capability configuration.
🏗 Build System
| Asset | Tool | Output |
|-------|------|--------|
| JavaScript (ESM) | Rollup + @rollup/plugin-typescript | dist-js/index.js |
| JavaScript (CJS) | Rollup | dist-js/index.cjs |
| TypeScript declarations | tsc (via Rollup plugin) | dist-js/index.d.ts |
| Rust library | cargo build | target/ |
| Plugin permissions | tauri-plugin build script | permissions/autogenerated/ |
Build the JavaScript bundle:
npm run build
# or
pnpm build🧱 Project Structure
tauri-liesel/reg/
├── guest-js/ # TypeScript source (compiled to dist-js/)
│ ├── index.ts # NodeRegistry class + all type exports
│ └── types.ts # Standalone type definitions
├── src/ # Rust plugin source
│ ├── lib.rs # Plugin init, Ext trait, command handler registration
│ ├── commands.rs # Tauri command implementations (ping, load_lsnodes)
│ ├── models.rs # Shared Rust models (PingRequest, PingResponse)
│ ├── error.rs # Plugin error enum + Serialize impl
│ ├── desktop.rs # Desktop-specific Reg struct (direct file I/O)
│ └── mobile.rs # Mobile-specific Reg struct (Android/iOS plugin handle)
├── permissions/
│ ├── default.toml # Default permission set definition
│ └── autogenerated/ # Auto-generated per-command allow/deny TOMLs
├── examples/
│ └── tauri-app/ # Svelte + Tauri v2 reference application
├── dist-js/ # Compiled JS/TS output (not checked in)
├── build.rs # Rust build script (registers commands for tauri-plugin)
├── Cargo.toml # Rust crate metadata
├── package.json # npm package metadata
├── rollup.config.js # Rollup build configuration
└── tsconfig.json # TypeScript compiler options🔧 Serde Deserialization Details
The Rust side employs custom deserializers in commands.rs to tolerate loosely-typed legacy JSON node files:
| Deserializer | Handles |
|---|---|
| deserialize_bool_from_string | true/false as native booleans or "true"/"false" strings |
| deserialize_number_from_string | Numbers as native JSON numbers or numeric strings |
| deserialize_opt_number_from_string | Optional numbers; also strips trailing "px" suffixes |
| deserialize_vec_or_single | Port arrays that may be a single object {} instead of [{}] |
This means your nodes/*.json files are resilient to minor type inconsistencies without causing a load failure.
🛠 Development & Contributing
# Clone the repository
git clone https://github.com/valeArt/tauri-liesel
# Navigate to the plugin directory
cd tauri-liesel/reg
# Install JS dependencies
pnpm install
# Build the JS bundle
pnpm build
# Run the example application
cd examples/tauri-app
pnpm install
pnpm tauri dev📋 Requirements
| Requirement | Minimum Version |
|-------------|----------------|
| Tauri | ^2.11.3 |
| @tauri-apps/api (JS peer) | ^2.0.0 |
| Rust | 1.77.2 |
| Node.js | >=18 (LTS recommended) |
📄 License
MIT © ValeArt
