npm package: @nextera.one/nestflow-engine-sdk
Description: SDK for building, testing, and publishing custom NestFlow node engines
Author: NestFlow
Latest version: 1.0.1

# @nextera.one/nestflow-engine-sdk

Official SDK for building, testing, and publishing custom **NestFlow** node engines.

## Installation

```bash
npm install @nextera.one/nestflow-engine-sdk
```

**Peer dependencies** (your project must have these):

```bash
npm install @nestjs/common reflect-metadata
```

## Quick Start

### 1. Create a Custom Engine

```typescript
import { Injectable } from "@nestjs/common";
import {
  NodeEngine,
  NodeExecutionStrategy,
  NodeExecutionContext,
  NodeDto,
} from "@nextera.one/nestflow-engine-sdk";

@Injectable()
@NodeEngine("WEATHER_FETCH")
export class WeatherFetchEngine implements NodeExecutionStrategy {
  async execute(node: NodeDto, context: NodeExecutionContext): Promise<any> {
    const { city } = node.params;

    if (!city) {
      throw new Error('Parameter "city" is required');
    }

    // Your custom logic here
    const response = await fetch(
      `https://api.weatherapi.com/v1/current.json?key=${node.params.apiKey}&q=${city}`,
    );
    const data = await response.json();

    // Store result in flow context for downstream nodes
    context._set(context.ctx, `weather.${city}`, data);

    return data;
  }
}
```

### 2. Test Your Engine Locally

```typescript
import {
  EngineTestBed,
  createMockNode,
} from "@nextera.one/nestflow-engine-sdk/testing";
import { WeatherFetchEngine } from "./weather-fetch.engine";

async function main() {
  const engine = new WeatherFetchEngine();
  const testBed = new EngineTestBed(engine);

  // Verify decorator metadata
  console.log("Registered types:", testBed.getRegisteredNodeTypes());
  // → ['WEATHER_FETCH']

  // Run a single execution
  const result = await testBed.execute(
    createMockNode({
      type: "WEATHER_FETCH",
      params: { city: "London", apiKey: "test-key" },
    }),
  );
  console.log("Result:", result);

  // Run a batch of test cases
  const results = await testBed.runTests([
    {
      name: "fetches weather for a valid city",
      node: createMockNode({
        type: "WEATHER_FETCH",
        params: { city: "London", apiKey: "test-key" },
      }),
    },
    {
      name: "throws error when city is missing",
      node: createMockNode({
        type: "WEATHER_FETCH",
        params: { apiKey: "test-key" },
      }),
      shouldThrow: true,
      errorMessage: "city",
    },
  ]);

  testBed.printResults(results);
}

main();
```

### 3. Multi-Type Engine

A single engine can handle multiple node types:

```typescript
@Injectable()
@NodeEngine(["MATH_ADD", "MATH_MULTIPLY", "MATH_DIVIDE"])
export class MathEngine implements NodeExecutionStrategy {
  async execute(node: NodeDto, context: NodeExecutionContext): Promise<any> {
    const { a, b } = node.params;

    switch (node.type) {
      case "MATH_ADD":
        return { result: a + b };
      case "MATH_MULTIPLY":
        return { result: a * b };
      case "MATH_DIVIDE":
        if (b === 0) throw new Error("Division by zero");
        return { result: a / b };
      default:
        throw new Error(`Unknown math operation: ${node.type}`);
    }
  }
}
```

## API Reference

### Main Exports (`@nextera.one/nestflow-engine-sdk`)

| Export                  | Description                                         |
| ----------------------- | --------------------------------------------------- |
| `NodeExecutionStrategy` | Interface — implement `execute(node, context)`      |
| `NodeExecutionContext`  | Interface — the context passed to your engine       |
| `NodeDto`               | Interface — the node data (id, type, params)        |
| `EdgeDto`               | Interface — a directed edge (source → target)       |
| `FlowDefinitionDto`     | Interface — full flow definition (id, nodes, edges) |
| `NodeEngine(type)`      | Decorator — registers your class for node type(s)   |
| `NODE_ENGINE_KEY`       | Metadata key constant                               |

### Testing Exports (`@nextera.one/nestflow-engine-sdk/testing`)

| Export                        | Description                              |
| ----------------------------- | ---------------------------------------- |
| `EngineTestBed`               | Test harness for running engines locally |
| `createMockContext(options?)` | Creates a mock `NodeExecutionContext`    |
| `createMockNode(overrides)`   | Creates a mock `NodeDto`                 |

### `NodeExecutionContext` Properties

| Property                  | Type                       | Description                                  |
| ------------------------- | -------------------------- | -------------------------------------------- |
| `ctx`                     | `any`                      | Mutable flow context shared across all nodes |
| `db`                      | `any`                      | Database client (Knex on the platform)       |
| `nodes`                   | `Record<string, NodeDto>`  | All nodes in the flow keyed by ID            |
| `edges`                   | `Record<string, string[]>` | Adjacency list (source → targets)            |
| `fullDef`                 | `FlowDefinitionDto`        | Complete flow definition                     |
| `logger`                  | `Logger`                   | Scoped logger (log, error, warn, debug)      |
| `_get(obj, path)`         | `Function`                 | Get nested property by dot notation          |
| `_set(obj, path, value)`  | `Function`                 | Set nested property by dot notation          |
| `encryptRow(table, data)` | `Function`                 | Encrypt a data row                           |
| `decryptRow(table, row)`  | `Function`                 | Decrypt a data row                           |
| `executeNode(...)`        | `Function`                 | Trigger sub-node execution                   |
| `subscriberService`       | `any`                      | Event emitter service                        |
| `redisService`            | `any`                      | Redis cache service                          |
| `guardRegistry`           | `Record`                   | Flow guards                                  |
| `middlewareRegistry`      | `Record`                   | Flow middleware                              |
| `interceptorRegistry`     | `Record`                   | Flow interceptors                            |
| `flowRunnerService`       | `any`                      | Flow runner reference                        |

### `EngineTestBed` Methods

| Method                                      | Returns                        | Description                                 |
| ------------------------------------------- | ------------------------------ | ------------------------------------------- |
| `execute(node, contextOptions?)`            | `Promise<any>`                 | Run the engine with a node                  |
| `executeWithContext(node, contextOptions?)` | `Promise<{ result, context }>` | Run and return both result and context      |
| `runTests(testCases)`                       | `Promise<TestCaseResult[]>`    | Run a batch of test cases                   |
| `printResults(results)`                     | `boolean`                      | Print results, returns `true` if all passed |
| `getRegisteredNodeTypes()`                  | `string[] \| undefined`        | Get `@NodeEngine` metadata                  |

## NodeDto Structure

```typescript
interface NodeDto {
  id: string; // Unique node ID
  type: string; // Node type (matches @NodeEngine)
  params: Record<string, any>; // Engine parameters
  position?: { x: number; y: number }; // Canvas position (optional)
  data?: Record<string, any>; // Additional metadata (optional)
}
```

## Publishing to NestFlow

Once your engine passes all local tests, you can upload it to the NestFlow platform:

1. **Build** your engine project
2. **Run tests** to verify all test cases pass
3. **Upload** via the NestFlow dashboard or CLI

## License

MIT