---
name: Export Interface (Ports)
description: How to define workflow input/output ports, scoped iteration, and forEach patterns
keywords: [ports, interface, inputs, outputs, scoped, forEach, iteration, param, returns, workflow]
---

# Code-First Architecture

Flow Weaver generates JS/TS files from TypeScript sources with `@flowWeaver` annotations. Each export becomes a callable function.

Any `.ts`, `.tsx`, `.js`, or `.jsx` file with `@flowWeaver` annotations works.

# Port Definition

## Workflow Inputs (@param)

Use JSDoc `@param` to define Start node ports:

```typescript
/**
 * @flowWeaver workflow
 * @param data - Input data to process
 * @param [optional] - Optional input (brackets = optional in TS type)
 * @param [withDefault=42] - Optional with default value
 */
export function myWorkflow(
  execute: boolean,
  params: { data: any; optional?: any; withDefault?: number }
) { ... }
```

**IMPORTANT:**

- Second parameter MUST be named `params` - generator uses this name
- Optional `[param]` marks the TypeScript type as optional, but **if you connect to that port, you must provide a value**. Use optional params for ports that may not be connected.

## Workflow Outputs (@returns)

Use JSDoc `@returns` to define Exit node ports:

```typescript
/**
 * @flowWeaver workflow
 * @returns result - The output result
 * @returns status - Status message
 */
export function myWorkflow(...): {
  onSuccess: boolean;
  onFailure: boolean;
  result: any;
  status: string
} { ... }
```

## Node Inputs/Outputs

Use `@input` and `@output` for node types. **Inputs become direct parameters:**

```typescript
/**
 * @flowWeaver nodeType
 * @input value - Value to process
 * @input count - Number of times
 * @output result - Processed result
 */
function processNode(execute: boolean, value: any, count: number) { ... }
//                                     ^           ^
//                               Direct parameters (NOT wrapped in object)
```

# Mandatory Ports

Always present on every node/workflow:

**Input:**

- `execute` (STEP) - First function parameter

**Output:**

- `onSuccess` (STEP) - In return type
- `onFailure` (STEP) - In return type

# Async Workflows

Use `async` keyword on function - no annotation needed:

```typescript
/**
 * @flowWeaver workflow
 * @param data - Input
 * @returns result - Output
 */
export async function asyncWorkflow(
  execute: boolean,
  params: { data: any }
): Promise<{ onSuccess: boolean; onFailure: boolean; result: any }> {
  // async implementation
}
```

# Scoped Ports (Iteration/Looping)

For iteration (forEach), use **per-port scopes** with `scope:scopeName` suffix on ports.

## 1. Define ForEach Node Type

```typescript
/**
 * @flowWeaver nodeType
 * @label For Each
 * @input items - Array to iterate
 * @output start scope:processItem - Mandatory: triggers child execute
 * @output item scope:processItem - Current item to process
 * @input success scope:processItem - Mandatory: from child onSuccess
 * @input failure scope:processItem - Mandatory: from child onFailure
 * @input processed scope:processItem - Result from child
 * @output results - Collected results
 */
function forEach(
  execute: boolean,
  items: any[],
  processItem: (start: boolean, item: any) => { success: boolean; failure: boolean; processed: any }
) {
  if (!execute) return { onSuccess: false, onFailure: false, results: [] };
  const results = items.map((item) => processItem(true, item).processed);
  return { onSuccess: true, onFailure: false, results };
}
```

Key points:

- Scope name (`processItem`) MUST match callback parameter name
- Callback is auto-generated by compiler, receives scoped port values as args
- The node implementation iterates by calling callback for each item
- Mandatory scoped STEP ports: `start` (output), `success`/`failure` (inputs)

## 2. Use ForEach in Workflow

```typescript
/**
 * @flowWeaver workflow
 * @node loop forEach
 * @node proc processor loop.processItem
 * @connect Start.execute -> loop.execute
 * @connect Start.items -> loop.items
 * @connect loop.start:processItem -> proc.execute
 * @connect loop.item:processItem -> proc.item
 * @connect proc.result -> loop.processed:processItem
 * @connect proc.onSuccess -> loop.success:processItem
 * @connect proc.onFailure -> loop.failure:processItem
 * @connect loop.results -> Exit.results
 * @connect loop.onSuccess -> Exit.onSuccess
 * @connect loop.onFailure -> Exit.onFailure
 */
```

Key syntax:

- `@node proc processor loop.processItem` - child node inside `loop`'s `processItem` scope
- `loop.item:processItem` - scoped OUTPUT port (`:scopeName` suffix)
- `loop.processed:processItem` - scoped INPUT port (`:scopeName` suffix)
- Connect child's `execute`/`onSuccess`/`onFailure` to scope's mandatory ports

**IMPORTANT:** Don't forget to wire `Start.execute` and `Exit.onSuccess/onFailure`!

# Common Mistakes

**Wrong node signature (wrapping inputs)**

```typescript
// WRONG - inputs wrapped in object
function node(execute: boolean, params: { value: any }) { ... }

// CORRECT - direct parameters for nodes
function node(execute: boolean, value: any) { ... }
```

**Missing execute parameter**

```typescript
// WRONG - no execute
function node(value: any) { ... }

// CORRECT
function node(execute: boolean, value: any) { ... }
```

**Missing return properties**

```typescript
// WRONG - missing onFailure
return { onSuccess: true, result: 42 };

// CORRECT
return { onSuccess: true, onFailure: false, result: 42 };
```

**Multiple connections to same Exit port**

```typescript
// PROBLEMATIC - only one value will be used
@connect nodeA.result -> Exit.output
@connect nodeB.result -> Exit.output

// BETTER - use separate Exit ports
@connect nodeA.result -> Exit.successOutput
@connect nodeB.result -> Exit.errorOutput
```

**Using reserved names for node types**

```typescript
// WRONG - 'process' is a Node.js global
function process(execute: boolean, item: any) { ... }

// CORRECT - use non-reserved names
function processItem(execute: boolean, item: any) { ... }
```

Avoid: `process`, `module`, `require`, `exports`, `console`, `global`

# Validation

Always validate after changes:

```bash
fw validate <file>
```
