Extensible workflow node framework for building automation pipelines. Define, register, and execute typed nodes with Zod validation.
📚 Documentation · 🎮 Playground
- @jam-nodes/core - Core framework with types, registry, and execution context
- @jam-nodes/nodes - Built-in nodes (conditional, delay, filter, map, http-request)
- @jam-nodes/playground - CLI tool for testing nodes interactively
npm install @jam-nodes/core @jam-nodes/nodes zodimport { NodeRegistry, defineNode, ExecutionContext } from '@jam-nodes/core';
import { conditionalNode, endNode, builtInNodes } from '@jam-nodes/nodes';
import { z } from 'zod';
// Create a registry and register built-in nodes
const registry = new NodeRegistry();
registry.registerAll(builtInNodes);
// Define a custom node
const greetNode = defineNode({
type: 'greet',
name: 'Greet',
description: 'Generate a greeting message',
category: 'action',
inputSchema: z.object({
name: z.string(),
}),
outputSchema: z.object({
message: z.string(),
}),
executor: async (input) => ({
success: true,
output: { message: `Hello, ${input.name}!` },
}),
});
// Register custom node
registry.register(greetNode);
// Execute a node
const context = new ExecutionContext({ userName: 'World' });
const executor = registry.getExecutor('greet');
const result = await executor(
{ name: context.interpolate('{{userName}}') },
context.toNodeContext('user-123', 'workflow-456')
);
console.log(result.output?.message); // "Hello, World!"import { defineNode } from '@jam-nodes/core';
import { z } from 'zod';
export const myNode = defineNode({
type: 'my_custom_node',
name: 'My Custom Node',
description: 'Does something awesome',
category: 'action', // 'action' | 'logic' | 'integration' | 'transform'
inputSchema: z.object({
input1: z.string(),
input2: z.number().optional(),
}),
outputSchema: z.object({
result: z.string(),
}),
capabilities: {
supportsRerun: true,
supportsCancel: true,
},
executor: async (input, context) => {
// Access workflow variables
const previousData = context.resolveNestedPath('someNode.output');
// Your logic here
const result = `Processed: ${input.input1}`;
return {
success: true,
output: { result },
// Optional: send notification
notification: {
title: 'Node Complete',
message: 'Processing finished',
},
};
},
});- conditional - Branch workflow based on conditions
- end - Mark end of workflow branch
- delay - Wait for specified duration
- map - Extract property from array items
- filter - Filter array based on conditions
- http_request - Make HTTP requests to external APIs
The ExecutionContext supports powerful variable interpolation:
const ctx = new ExecutionContext({
user: { name: 'Alice', email: 'alice@example.com' },
items: [1, 2, 3],
});
// Simple interpolation
ctx.interpolate('Hello {{user.name}}'); // "Hello Alice"
// Direct value (returns actual type)
ctx.interpolate('{{items}}'); // [1, 2, 3]
// JSONPath
ctx.evaluateJsonPath('$.user.email'); // "alice@example.com"also we have a github with 668 contributions which we will open source more of in the coming days - jia and mal
MIT