Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 27 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ npm install @jam-nodes/core @jam-nodes/nodes zod
## Quick Start

```typescript
import { NodeRegistry, defineNode, ExecutionContext } from '@jam-nodes/core';
import { conditionalNode, endNode, builtInNodes } from '@jam-nodes/nodes';
import { z } from 'zod';
import { 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',
type: "greet",
name: "Greet",
description: "Generate a greeting message",
category: "action",
inputSchema: z.object({
name: z.string(),
}),
Expand All @@ -49,11 +49,11 @@ const greetNode = defineNode({
registry.register(greetNode);

// Execute a node
const context = new ExecutionContext({ userName: 'World' });
const executor = registry.getExecutor('greet');
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')
{ name: context.interpolate("{{userName}}") },
context.toNodeContext("user-123", "workflow-456"),
);

console.log(result.output?.message); // "Hello, World!"
Expand All @@ -62,14 +62,14 @@ console.log(result.output?.message); // "Hello, World!"
## Creating Custom Nodes

```typescript
import { defineNode } from '@jam-nodes/core';
import { z } from 'zod';
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'
type: "my_custom_node",
name: "My Custom Node",
description: "Does something awesome",
category: "action", // 'action' | 'logic' | 'integration' | 'transform'

inputSchema: z.object({
input1: z.string(),
Expand All @@ -87,7 +87,7 @@ export const myNode = defineNode({

executor: async (input, context) => {
// Access workflow variables
const previousData = context.resolveNestedPath('someNode.output');
const previousData = context.resolveNestedPath("someNode.output");

// Your logic here
const result = `Processed: ${input.input1}`;
Expand All @@ -97,8 +97,8 @@ export const myNode = defineNode({
output: { result },
// Optional: send notification
notification: {
title: 'Node Complete',
message: 'Processing finished',
title: "Node Complete",
message: "Processing finished",
},
};
},
Expand All @@ -108,15 +108,18 @@ export const myNode = defineNode({
## Built-in Nodes

### Logic

- **conditional** - Branch workflow based on conditions
- **end** - Mark end of workflow branch
- **delay** - Wait for specified duration

### Transform

- **map** - Extract property from array items
- **filter** - Filter array based on conditions

### Examples

- **http_request** - Make HTTP requests to external APIs

## Variable Interpolation
Expand All @@ -125,22 +128,20 @@ The `ExecutionContext` supports powerful variable interpolation:

```typescript
const ctx = new ExecutionContext({
user: { name: 'Alice', email: 'alice@example.com' },
user: { name: "Alice", email: "alice@example.com" },
items: [1, 2, 3],
});

// Simple interpolation
ctx.interpolate('Hello {{user.name}}'); // "Hello Alice"
ctx.interpolate("Hello {{user.name}}"); // "Hello Alice"

// Direct value (returns actual type)
ctx.interpolate('{{items}}'); // [1, 2, 3]
ctx.interpolate("{{items}}"); // [1, 2, 3]

// JSONPath
ctx.evaluateJsonPath('$.user.email'); // "alice@example.com"
ctx.evaluateJsonPath("$.user.email"); // "alice@example.com"
```

## License

also we have a github with 668 contributions which we will open source more of in the coming days - jia and mal

MIT
147 changes: 147 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# jam-nodes

Extensible workflow node framework for building automation pipelines. Define, register, and execute typed nodes with Zod validation.

📚 **[Documentation](https://docs.spreadjam.com)** · 🎮 **[Playground](https://docs.spreadjam.com/playground/overview)**

## Packages

- **[@jam-nodes/core](./packages/core)** - Core framework with types, registry, and execution context
- **[@jam-nodes/nodes](./packages/nodes)** - Built-in nodes (conditional, delay, filter, map, http-request)
- **[@jam-nodes/playground](./packages/playground)** - CLI tool for testing nodes interactively

## Installation

```bash
npm install @jam-nodes/core @jam-nodes/nodes zod
```

## Quick Start

```typescript
import { 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!"
```

## Creating Custom Nodes

```typescript
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",
},
};
},
});
```

## Built-in Nodes

### Logic

- **conditional** - Branch workflow based on conditions
- **end** - Mark end of workflow branch
- **delay** - Wait for specified duration

### Transform

- **map** - Extract property from array items
- **filter** - Filter array based on conditions

### Examples

- **http_request** - Make HTTP requests to external APIs

## Variable Interpolation

The `ExecutionContext` supports powerful variable interpolation:

```typescript
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"
```

## License

MIT
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.2.8",
"description": "Core framework for building workflow nodes with type-safe executors and Zod validation",
"type": "module",
"sideEffects": false,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
Expand Down
14 changes: 5 additions & 9 deletions packages/core/src/types/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,13 @@ export interface NodeCredentials {
/** Firecrawl API credentials */
firecrawl?: {
bearerToken?: string;
}
apiKey?: string;
};
/** Twitter/X API credentials */
twitter?: {
/** Official Twitter API v2 Bearer Token */
bearerToken?: string;
/** TwitterAPI.io API key (third-party, simpler) */
twitterApiIoKey?: string;
};
/** ForumScout API credentials (for LinkedIn monitoring) */
forumScout?: {
apiKey: string;
};
Expand All @@ -30,14 +28,15 @@ export interface NodeCredentials {
/** Base64 encoded login:password */
apiToken: string;
};
/** OpenAI API credentials */
openai?: {
apiKey: string;
};
/** Anthropic API credentials */
anthropic?: {
apiKey: string;
};
devto?: {
apiKey: string;
};
/** Discord Bot credentials */
discordBot?: {
botToken: string;
Expand Down Expand Up @@ -120,9 +119,6 @@ export type NodeExecutor<TInput = unknown, TOutput = unknown> = (
context: NodeExecutionContext
) => Promise<NodeExecutionResult<TOutput>>;

/**
* Node capabilities for UI and runtime behavior.
*/
export interface NodeCapabilities {
/** Node supports data enrichment */
supportsEnrichment?: boolean;
Expand Down
Loading