@gravito/flux 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -9
- package/dist/bun.cjs +2 -2
- package/dist/bun.cjs.map +1 -1
- package/dist/bun.d.cts +1 -1
- package/dist/bun.d.ts +1 -1
- package/dist/bun.js +1 -1
- package/dist/chunk-6AZNHVEO.cjs.map +1 -1
- package/dist/{chunk-WAPZDXSX.cjs → chunk-DN7SIQ34.cjs} +147 -47
- package/dist/chunk-DN7SIQ34.cjs.map +1 -0
- package/dist/{chunk-NAIVO7RR.js → chunk-EZGSU6AW.js} +10 -2
- package/dist/chunk-EZGSU6AW.js.map +1 -0
- package/dist/{chunk-4DXCQ6CL.js → chunk-M2ZRQRF4.js} +112 -12
- package/dist/chunk-M2ZRQRF4.js.map +1 -0
- package/dist/{chunk-YXBEYVGY.cjs → chunk-ZE2RDS47.cjs} +10 -2
- package/dist/chunk-ZE2RDS47.cjs.map +1 -0
- package/dist/cli/flux-visualize.cjs.map +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.node.cjs +3 -3
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.d.cts +37 -6
- package/dist/index.node.d.ts +37 -6
- package/dist/index.node.js +2 -2
- package/dist/{types-CRz5XdLd.d.cts → types-CGIEQPFv.d.cts} +10 -0
- package/dist/{types-CRz5XdLd.d.ts → types-CGIEQPFv.d.ts} +10 -0
- package/package.json +5 -3
- package/dist/chunk-4DXCQ6CL.js.map +0 -1
- package/dist/chunk-NAIVO7RR.js.map +0 -1
- package/dist/chunk-WAPZDXSX.cjs.map +0 -1
- package/dist/chunk-YXBEYVGY.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -2,16 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
> ⚡ Platform-agnostic, high-performance workflow engine for Gravito
|
|
4
4
|
|
|
5
|
-
## Features
|
|
5
|
+
## ✨ Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **Distributed Locking** -
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
- 🪐 **Galaxy-Ready Workflow Engine** - Native integration with PlanetCore for universal business process orchestration.
|
|
8
|
+
- 🔄 **Distributed Saga Coordination** - Reliable transaction management across multiple isolated Satellites with automatic rollback.
|
|
9
|
+
- ⚡ **Pure State Machine** - High-performance engine with zero runtime dependencies beyond Web Standard APIs.
|
|
10
|
+
- 🔐 **Distributed Locking** - Plasma-backed Redis locking to ensure process safety in multi-node clusters.
|
|
11
|
+
- 📡 **Signal & Suspend** - Pause workflows to wait for external stimulus (Webhooks, manual approvals) and resume instantly.
|
|
12
|
+
- 🛠️ **Fluent Builder API** - Fully type-safe, chainable definitions for complex logic flows.
|
|
13
|
+
|
|
14
|
+
## 🌌 Role in Galaxy Architecture
|
|
15
|
+
|
|
16
|
+
In the **Gravito Galaxy Architecture**, Flux acts as the **Logic Orchestrator (Cerebral Cortex)**.
|
|
17
|
+
|
|
18
|
+
- **Process Master**: Manages complex, long-running business processes that span across multiple Satellites (e.g., a "Checkout" process involving `Catalog`, `Payment`, and `Notification`).
|
|
19
|
+
- **Reliability Engine**: Ensures that even if a Satellite fails mid-process, the state is preserved and the appropriate compensation logic (Saga) is executed.
|
|
20
|
+
- **State Persistence**: Works with `Atlas` or `Plasma` to store the execution state, allowing workflows to survive system restarts or move between nodes.
|
|
21
|
+
|
|
22
|
+
```mermaid
|
|
23
|
+
graph TD
|
|
24
|
+
S1[Satellite: Order] -- "Trigger Workflow" --> Flux{Flux Engine}
|
|
25
|
+
Flux -- "Step 1" --> S2[Satellite: Inventory]
|
|
26
|
+
Flux -- "Step 2" --> S3[Satellite: Payment]
|
|
27
|
+
Flux -- "Fail / Rollback" --> S2
|
|
28
|
+
Flux -.-> State[(State: Plasma/Redis)]
|
|
29
|
+
```
|
|
15
30
|
|
|
16
31
|
## Installation
|
|
17
32
|
|
|
@@ -151,6 +166,14 @@ const reportWorkflow = createWorkflow('generate-report')
|
|
|
151
166
|
})
|
|
152
167
|
```
|
|
153
168
|
|
|
169
|
+
## 📚 Documentation
|
|
170
|
+
|
|
171
|
+
Detailed guides and references for the Galaxy Architecture:
|
|
172
|
+
|
|
173
|
+
- [🏗️ **Architecture Overview**](./README.md) — Under the hood of the state machine.
|
|
174
|
+
- [🔄 **Workflow Patterns**](./doc/WORKFLOW_PATTERNS.md) — **NEW**: Sagas, Suspension, and Distributed Locking.
|
|
175
|
+
- [🧪 **Testing Workflows**](./tests/README.md) — Mocking and execution testing.
|
|
176
|
+
|
|
154
177
|
## API
|
|
155
178
|
|
|
156
179
|
### `createWorkflow(name)`
|
package/dist/bun.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkZE2RDS47cjs = require('./chunk-ZE2RDS47.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
exports.BunSQLiteStorage =
|
|
6
|
+
exports.BunSQLiteStorage = _chunkZE2RDS47cjs.BunSQLiteStorage;
|
|
7
7
|
//# sourceMappingURL=bun.cjs.map
|
package/dist/bun.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/carl/Dev/Carl/gravito-core
|
|
1
|
+
{"version":3,"sources":["/Users/carl/Dev/Carl/gravito-core/packages/flux/dist/bun.cjs"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,8DAAC","file":"/Users/carl/Dev/Carl/gravito-core/packages/flux/dist/bun.cjs"}
|
package/dist/bun.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database } from 'bun:sqlite';
|
|
2
|
-
import { p as WorkflowStorage, a as WorkflowState, n as WorkflowFilter } from './types-
|
|
2
|
+
import { p as WorkflowStorage, a as WorkflowState, n as WorkflowFilter } from './types-CGIEQPFv.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Configuration options for the Bun SQLite storage adapter.
|
package/dist/bun.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database } from 'bun:sqlite';
|
|
2
|
-
import { p as WorkflowStorage, a as WorkflowState, n as WorkflowFilter } from './types-
|
|
2
|
+
import { p as WorkflowStorage, a as WorkflowState, n as WorkflowFilter } from './types-CGIEQPFv.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Configuration options for the Bun SQLite storage adapter.
|
package/dist/bun.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/carl/Dev/Carl/gravito-core-ci-fix/packages/flux/dist/chunk-6AZNHVEO.cjs","../src/visualization/MermaidGenerator.ts"],"names":[],"mappings":"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","file":"/Users/carl/Dev/Carl/gravito-core-ci-fix/packages/flux/dist/chunk-6AZNHVEO.cjs","sourcesContent":[null,"import type { StepExecution, WorkflowDefinition, WorkflowState } from '../types'\n\n/**\n * Options for customizing Mermaid diagram generation.\n */\nexport interface MermaidOptions {\n /** Include detailed step information (retries, timeout, conditions). */\n showDetails?: boolean\n /** Include execution history with status colors. */\n showStatus?: boolean\n /** Render parallel groups visually. */\n showParallelGroups?: boolean\n /** Theme: 'default' | 'dark' | 'forest' | 'neutral'. */\n theme?: 'default' | 'dark' | 'forest' | 'neutral'\n}\n\n/**\n * Generates Mermaid flowchart diagrams from workflow definitions and execution states.\n *\n * **Features**:\n * - Workflow structure visualization (steps, parallel groups, conditions)\n * - Execution status overlay (completed, failed, compensated)\n * - Detailed metadata (retries, timeout, commit markers)\n *\n * **Use Cases**:\n * - Documentation generation\n * - Debugging workflow execution\n * - Visual workflow design validation\n */\nexport class MermaidGenerator {\n /**\n * Generates a Mermaid flowchart from a workflow definition.\n *\n * @param definition - The workflow definition to visualize\n * @param options - Customization options\n * @returns Mermaid diagram syntax as a string\n *\n * @example\n * ```typescript\n * const generator = new MermaidGenerator()\n * const diagram = generator.generateFromDefinition(workflow, {\n * showDetails: true,\n * showParallelGroups: true\n * })\n * console.log(diagram)\n * ```\n */\n generateFromDefinition<TInput, TData>(\n definition: WorkflowDefinition<TInput, TData>,\n options: MermaidOptions = {}\n ): string {\n const { showDetails = false, showParallelGroups = true, theme = 'default' } = options\n\n const lines: string[] = []\n lines.push(`%%{init: {'theme':'${theme}'}}%%`)\n lines.push('flowchart TD')\n lines.push(` Start([Start: ${definition.name}])`)\n\n let prevNode = 'Start'\n const parallelGroups = new Map<string, string[]>()\n\n // First pass: identify parallel groups\n if (showParallelGroups) {\n for (const step of definition.steps) {\n if (step.parallelGroup) {\n const group = parallelGroups.get(step.parallelGroup) || []\n group.push(step.name)\n parallelGroups.set(step.parallelGroup, group)\n }\n }\n }\n\n const processedGroups = new Set<string>()\n\n for (let i = 0; i < definition.steps.length; i++) {\n const step = definition.steps[i]!\n\n // Handle parallel groups\n if (step.parallelGroup && showParallelGroups) {\n if (processedGroups.has(step.parallelGroup)) {\n continue // Already processed this group\n }\n processedGroups.add(step.parallelGroup)\n\n const groupSteps = parallelGroups.get(step.parallelGroup)!\n const groupName = `ParallelGroup_${step.parallelGroup}`\n\n // Create subgraph for parallel execution\n lines.push(` subgraph ${groupName}[\" \"]`)\n lines.push(` direction LR`)\n\n for (const stepName of groupSteps) {\n const s = definition.steps.find((st) => st.name === stepName)!\n const nodeId = this.sanitizeNodeId(stepName)\n const label = this.buildStepLabel(s, showDetails)\n const shape = s.commit ? `[${label}]` : `(${label})`\n lines.push(` ${nodeId}${shape}`)\n\n if (s.compensate) {\n lines.push(` ${nodeId}_comp[🔄 Compensate]`)\n lines.push(` ${nodeId} -.->|on failure| ${nodeId}_comp`)\n }\n }\n\n lines.push(` end`)\n lines.push(` ${prevNode} --> ${groupName}`)\n prevNode = groupName\n } else if (!step.parallelGroup) {\n // Regular sequential step\n const nodeId = this.sanitizeNodeId(step.name)\n const label = this.buildStepLabel(step, showDetails)\n const shape = step.commit ? `[${label}]` : `(${label})`\n\n lines.push(` ${nodeId}${shape}`)\n\n // Handle conditional steps\n if (step.when) {\n const condId = `${nodeId}_cond`\n lines.push(` ${condId}{Condition?}`)\n lines.push(` ${prevNode} --> ${condId}`)\n lines.push(` ${condId} -->|yes| ${nodeId}`)\n lines.push(` ${condId} -->|no| ${this.getNextNodeId(definition.steps, i)}`)\n prevNode = nodeId\n } else {\n lines.push(` ${prevNode} --> ${nodeId}`)\n prevNode = nodeId\n }\n\n // Show compensation\n if (step.compensate) {\n lines.push(` ${nodeId}_comp[🔄 Compensate]`)\n lines.push(` ${nodeId} -.->|on failure| ${nodeId}_comp`)\n }\n }\n }\n\n lines.push(` ${prevNode} --> End([End])`)\n\n // Add styling\n lines.push('')\n lines.push(' classDef commitStep fill:#e1f5e1,stroke:#4caf50,stroke-width:2px')\n lines.push(' classDef normalStep fill:#e3f2fd,stroke:#2196f3,stroke-width:2px')\n lines.push(\n ' classDef compensateStep fill:#fff3e0,stroke:#ff9800,stroke-width:1px,stroke-dasharray: 5 5'\n )\n\n return lines.join('\\n')\n }\n\n /**\n * Generates a Mermaid flowchart from a workflow execution state.\n * Overlays execution status (completed, failed, compensated) on top of the structure.\n *\n * @param definition - The workflow definition\n * @param state - The current execution state\n * @param options - Customization options\n * @returns Mermaid diagram syntax with status overlay\n *\n * @example\n * ```typescript\n * const diagram = generator.generateFromState(workflow, executionState, {\n * showStatus: true,\n * showDetails: true\n * })\n * ```\n */\n generateFromState<TInput, TData>(\n definition: WorkflowDefinition<TInput, TData>,\n state: WorkflowState<TInput, TData>,\n options: MermaidOptions = {}\n ): string {\n const {\n showDetails = true,\n showStatus = true,\n showParallelGroups = true,\n theme = 'default',\n } = options\n\n const lines: string[] = []\n lines.push(`%%{init: {'theme':'${theme}'}}%%`)\n lines.push('flowchart TD')\n lines.push(` Start([Start: ${definition.name}])`)\n lines.push(` Start:::started`)\n\n let prevNode = 'Start'\n const parallelGroups = new Map<string, string[]>()\n const executionMap = new Map<string, StepExecution>()\n\n // Build execution map\n for (const exec of state.history) {\n executionMap.set(exec.name, exec)\n }\n\n // Identify parallel groups\n if (showParallelGroups) {\n for (const step of definition.steps) {\n if (step.parallelGroup) {\n const group = parallelGroups.get(step.parallelGroup) || []\n group.push(step.name)\n parallelGroups.set(step.parallelGroup, group)\n }\n }\n }\n\n const processedGroups = new Set<string>()\n\n for (let i = 0; i < definition.steps.length; i++) {\n const step = definition.steps[i]!\n\n // Handle parallel groups\n if (step.parallelGroup && showParallelGroups) {\n if (processedGroups.has(step.parallelGroup)) {\n continue\n }\n processedGroups.add(step.parallelGroup)\n\n const groupSteps = parallelGroups.get(step.parallelGroup)!\n const groupName = `ParallelGroup_${step.parallelGroup}`\n\n lines.push(` subgraph ${groupName}[\" \"]`)\n lines.push(` direction LR`)\n\n for (const stepName of groupSteps) {\n const s = definition.steps.find((st) => st.name === stepName)!\n const nodeId = this.sanitizeNodeId(stepName)\n const exec = executionMap.get(stepName)\n const label = this.buildStepLabelWithStatus(s, exec, showDetails, showStatus)\n const shape = s.commit ? `[${label}]` : `(${label})`\n\n lines.push(` ${nodeId}${shape}`)\n\n if (exec && showStatus) {\n const statusClass = this.getStatusClass(exec.status)\n lines.push(` ${nodeId}:::${statusClass}`)\n }\n\n if (s.compensate && exec?.status === 'compensated') {\n lines.push(` ${nodeId}_comp[🔄 Compensated]`)\n lines.push(` ${nodeId}_comp:::compensated`)\n lines.push(` ${nodeId} -.->|rolled back| ${nodeId}_comp`)\n }\n }\n\n lines.push(` end`)\n lines.push(` ${prevNode} --> ${groupName}`)\n prevNode = groupName\n } else if (!step.parallelGroup) {\n const nodeId = this.sanitizeNodeId(step.name)\n const exec = executionMap.get(step.name)\n const label = this.buildStepLabelWithStatus(step, exec, showDetails, showStatus)\n const shape = step.commit ? `[${label}]` : `(${label})`\n\n lines.push(` ${nodeId}${shape}`)\n\n if (exec && showStatus) {\n const statusClass = this.getStatusClass(exec.status)\n lines.push(` ${nodeId}:::${statusClass}`)\n }\n\n if (step.when) {\n const condId = `${nodeId}_cond`\n const skipped = exec?.status === 'skipped'\n lines.push(` ${condId}{Condition?}`)\n lines.push(` ${prevNode} --> ${condId}`)\n\n if (skipped) {\n lines.push(` ${condId}:::skipped`)\n lines.push(` ${condId} -->|no| ${nodeId}`)\n } else {\n lines.push(` ${condId} -->|yes| ${nodeId}`)\n }\n } else {\n lines.push(` ${prevNode} --> ${nodeId}`)\n }\n\n if (step.compensate && exec?.status === 'compensated') {\n lines.push(` ${nodeId}_comp[🔄 Compensated]`)\n lines.push(` ${nodeId}_comp:::compensated`)\n lines.push(` ${nodeId} -.->|rolled back| ${nodeId}_comp`)\n }\n\n prevNode = nodeId\n }\n }\n\n const finalStatus = this.getWorkflowFinalStatus(state.status)\n lines.push(` ${prevNode} --> End([End: ${finalStatus}])`)\n lines.push(` End:::${this.getStatusClass(state.status)}`)\n\n // Add status-based styling\n lines.push('')\n lines.push(' classDef started fill:#e3f2fd,stroke:#2196f3,stroke-width:2px')\n lines.push(' classDef completed fill:#e8f5e9,stroke:#4caf50,stroke-width:2px')\n lines.push(' classDef failed fill:#ffebee,stroke:#f44336,stroke-width:2px')\n lines.push(' classDef compensated fill:#fff3e0,stroke:#ff9800,stroke-width:2px')\n lines.push(\n ' classDef skipped fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px,stroke-dasharray: 5 5'\n )\n lines.push(' classDef pending fill:#fafafa,stroke:#bdbdbd,stroke-width:1px')\n lines.push(\n ' classDef suspended fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px,stroke-dasharray: 3 3'\n )\n\n return lines.join('\\n')\n }\n\n private sanitizeNodeId(name: string): string {\n return name.replace(/[^a-zA-Z0-9_]/g, '_')\n }\n\n private buildStepLabel(step: any, showDetails: boolean): string {\n let label = step.name\n\n if (showDetails) {\n const meta: string[] = []\n if (step.commit) {\n meta.push('📝')\n }\n if (step.retries && step.retries > 0) {\n meta.push(`↻${step.retries}`)\n }\n if (step.timeout) {\n meta.push(`⏱${step.timeout}ms`)\n }\n if (step.when) {\n meta.push('❓')\n }\n\n if (meta.length > 0) {\n label += `<br/><small>${meta.join(' ')}</small>`\n }\n }\n\n return label\n }\n\n private buildStepLabelWithStatus(\n step: any,\n exec: StepExecution | undefined,\n showDetails: boolean,\n showStatus: boolean\n ): string {\n let label = step.name\n\n if (showStatus && exec) {\n label += `<br/><small><b>${exec.status.toUpperCase()}</b></small>`\n\n if (exec.duration) {\n label += `<br/><small>${exec.duration}ms</small>`\n }\n\n if (exec.retries > 0) {\n label += `<br/><small>Retries: ${exec.retries}</small>`\n }\n } else if (showDetails) {\n const meta: string[] = []\n if (step.commit) {\n meta.push('📝')\n }\n if (step.retries && step.retries > 0) {\n meta.push(`↻${step.retries}`)\n }\n if (step.timeout) {\n meta.push(`⏱${step.timeout}ms`)\n }\n if (step.when) {\n meta.push('❓')\n }\n\n if (meta.length > 0) {\n label += `<br/><small>${meta.join(' ')}</small>`\n }\n }\n\n return label\n }\n\n private getStatusClass(status: string): string {\n switch (status) {\n case 'completed':\n return 'completed'\n case 'failed':\n return 'failed'\n case 'compensated':\n case 'compensating':\n return 'compensated'\n case 'skipped':\n return 'skipped'\n case 'suspended':\n return 'suspended'\n case 'running':\n return 'started'\n default:\n return 'pending'\n }\n }\n\n private getWorkflowFinalStatus(status: string): string {\n switch (status) {\n case 'completed':\n return '✅ Completed'\n case 'failed':\n return '❌ Failed'\n case 'rolled_back':\n return '🔄 Rolled Back'\n case 'suspended':\n return '⏸ Suspended'\n default:\n return status\n }\n }\n\n private getNextNodeId(steps: any[], currentIndex: number): string {\n const nextStep = steps[currentIndex + 1]\n return nextStep ? this.sanitizeNodeId(nextStep.name) : 'End'\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/carl/Dev/Carl/gravito-core/packages/flux/dist/chunk-6AZNHVEO.cjs","../src/visualization/MermaidGenerator.ts"],"names":[],"mappings":"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","file":"/Users/carl/Dev/Carl/gravito-core/packages/flux/dist/chunk-6AZNHVEO.cjs","sourcesContent":[null,"import type { StepExecution, WorkflowDefinition, WorkflowState } from '../types'\n\n/**\n * Options for customizing Mermaid diagram generation.\n */\nexport interface MermaidOptions {\n /** Include detailed step information (retries, timeout, conditions). */\n showDetails?: boolean\n /** Include execution history with status colors. */\n showStatus?: boolean\n /** Render parallel groups visually. */\n showParallelGroups?: boolean\n /** Theme: 'default' | 'dark' | 'forest' | 'neutral'. */\n theme?: 'default' | 'dark' | 'forest' | 'neutral'\n}\n\n/**\n * Generates Mermaid flowchart diagrams from workflow definitions and execution states.\n *\n * **Features**:\n * - Workflow structure visualization (steps, parallel groups, conditions)\n * - Execution status overlay (completed, failed, compensated)\n * - Detailed metadata (retries, timeout, commit markers)\n *\n * **Use Cases**:\n * - Documentation generation\n * - Debugging workflow execution\n * - Visual workflow design validation\n */\nexport class MermaidGenerator {\n /**\n * Generates a Mermaid flowchart from a workflow definition.\n *\n * @param definition - The workflow definition to visualize\n * @param options - Customization options\n * @returns Mermaid diagram syntax as a string\n *\n * @example\n * ```typescript\n * const generator = new MermaidGenerator()\n * const diagram = generator.generateFromDefinition(workflow, {\n * showDetails: true,\n * showParallelGroups: true\n * })\n * console.log(diagram)\n * ```\n */\n generateFromDefinition<TInput, TData>(\n definition: WorkflowDefinition<TInput, TData>,\n options: MermaidOptions = {}\n ): string {\n const { showDetails = false, showParallelGroups = true, theme = 'default' } = options\n\n const lines: string[] = []\n lines.push(`%%{init: {'theme':'${theme}'}}%%`)\n lines.push('flowchart TD')\n lines.push(` Start([Start: ${definition.name}])`)\n\n let prevNode = 'Start'\n const parallelGroups = new Map<string, string[]>()\n\n // First pass: identify parallel groups\n if (showParallelGroups) {\n for (const step of definition.steps) {\n if (step.parallelGroup) {\n const group = parallelGroups.get(step.parallelGroup) || []\n group.push(step.name)\n parallelGroups.set(step.parallelGroup, group)\n }\n }\n }\n\n const processedGroups = new Set<string>()\n\n for (let i = 0; i < definition.steps.length; i++) {\n const step = definition.steps[i]!\n\n // Handle parallel groups\n if (step.parallelGroup && showParallelGroups) {\n if (processedGroups.has(step.parallelGroup)) {\n continue // Already processed this group\n }\n processedGroups.add(step.parallelGroup)\n\n const groupSteps = parallelGroups.get(step.parallelGroup)!\n const groupName = `ParallelGroup_${step.parallelGroup}`\n\n // Create subgraph for parallel execution\n lines.push(` subgraph ${groupName}[\" \"]`)\n lines.push(` direction LR`)\n\n for (const stepName of groupSteps) {\n const s = definition.steps.find((st) => st.name === stepName)!\n const nodeId = this.sanitizeNodeId(stepName)\n const label = this.buildStepLabel(s, showDetails)\n const shape = s.commit ? `[${label}]` : `(${label})`\n lines.push(` ${nodeId}${shape}`)\n\n if (s.compensate) {\n lines.push(` ${nodeId}_comp[🔄 Compensate]`)\n lines.push(` ${nodeId} -.->|on failure| ${nodeId}_comp`)\n }\n }\n\n lines.push(` end`)\n lines.push(` ${prevNode} --> ${groupName}`)\n prevNode = groupName\n } else if (!step.parallelGroup) {\n // Regular sequential step\n const nodeId = this.sanitizeNodeId(step.name)\n const label = this.buildStepLabel(step, showDetails)\n const shape = step.commit ? `[${label}]` : `(${label})`\n\n lines.push(` ${nodeId}${shape}`)\n\n // Handle conditional steps\n if (step.when) {\n const condId = `${nodeId}_cond`\n lines.push(` ${condId}{Condition?}`)\n lines.push(` ${prevNode} --> ${condId}`)\n lines.push(` ${condId} -->|yes| ${nodeId}`)\n lines.push(` ${condId} -->|no| ${this.getNextNodeId(definition.steps, i)}`)\n prevNode = nodeId\n } else {\n lines.push(` ${prevNode} --> ${nodeId}`)\n prevNode = nodeId\n }\n\n // Show compensation\n if (step.compensate) {\n lines.push(` ${nodeId}_comp[🔄 Compensate]`)\n lines.push(` ${nodeId} -.->|on failure| ${nodeId}_comp`)\n }\n }\n }\n\n lines.push(` ${prevNode} --> End([End])`)\n\n // Add styling\n lines.push('')\n lines.push(' classDef commitStep fill:#e1f5e1,stroke:#4caf50,stroke-width:2px')\n lines.push(' classDef normalStep fill:#e3f2fd,stroke:#2196f3,stroke-width:2px')\n lines.push(\n ' classDef compensateStep fill:#fff3e0,stroke:#ff9800,stroke-width:1px,stroke-dasharray: 5 5'\n )\n\n return lines.join('\\n')\n }\n\n /**\n * Generates a Mermaid flowchart from a workflow execution state.\n * Overlays execution status (completed, failed, compensated) on top of the structure.\n *\n * @param definition - The workflow definition\n * @param state - The current execution state\n * @param options - Customization options\n * @returns Mermaid diagram syntax with status overlay\n *\n * @example\n * ```typescript\n * const diagram = generator.generateFromState(workflow, executionState, {\n * showStatus: true,\n * showDetails: true\n * })\n * ```\n */\n generateFromState<TInput, TData>(\n definition: WorkflowDefinition<TInput, TData>,\n state: WorkflowState<TInput, TData>,\n options: MermaidOptions = {}\n ): string {\n const {\n showDetails = true,\n showStatus = true,\n showParallelGroups = true,\n theme = 'default',\n } = options\n\n const lines: string[] = []\n lines.push(`%%{init: {'theme':'${theme}'}}%%`)\n lines.push('flowchart TD')\n lines.push(` Start([Start: ${definition.name}])`)\n lines.push(` Start:::started`)\n\n let prevNode = 'Start'\n const parallelGroups = new Map<string, string[]>()\n const executionMap = new Map<string, StepExecution>()\n\n // Build execution map\n for (const exec of state.history) {\n executionMap.set(exec.name, exec)\n }\n\n // Identify parallel groups\n if (showParallelGroups) {\n for (const step of definition.steps) {\n if (step.parallelGroup) {\n const group = parallelGroups.get(step.parallelGroup) || []\n group.push(step.name)\n parallelGroups.set(step.parallelGroup, group)\n }\n }\n }\n\n const processedGroups = new Set<string>()\n\n for (let i = 0; i < definition.steps.length; i++) {\n const step = definition.steps[i]!\n\n // Handle parallel groups\n if (step.parallelGroup && showParallelGroups) {\n if (processedGroups.has(step.parallelGroup)) {\n continue\n }\n processedGroups.add(step.parallelGroup)\n\n const groupSteps = parallelGroups.get(step.parallelGroup)!\n const groupName = `ParallelGroup_${step.parallelGroup}`\n\n lines.push(` subgraph ${groupName}[\" \"]`)\n lines.push(` direction LR`)\n\n for (const stepName of groupSteps) {\n const s = definition.steps.find((st) => st.name === stepName)!\n const nodeId = this.sanitizeNodeId(stepName)\n const exec = executionMap.get(stepName)\n const label = this.buildStepLabelWithStatus(s, exec, showDetails, showStatus)\n const shape = s.commit ? `[${label}]` : `(${label})`\n\n lines.push(` ${nodeId}${shape}`)\n\n if (exec && showStatus) {\n const statusClass = this.getStatusClass(exec.status)\n lines.push(` ${nodeId}:::${statusClass}`)\n }\n\n if (s.compensate && exec?.status === 'compensated') {\n lines.push(` ${nodeId}_comp[🔄 Compensated]`)\n lines.push(` ${nodeId}_comp:::compensated`)\n lines.push(` ${nodeId} -.->|rolled back| ${nodeId}_comp`)\n }\n }\n\n lines.push(` end`)\n lines.push(` ${prevNode} --> ${groupName}`)\n prevNode = groupName\n } else if (!step.parallelGroup) {\n const nodeId = this.sanitizeNodeId(step.name)\n const exec = executionMap.get(step.name)\n const label = this.buildStepLabelWithStatus(step, exec, showDetails, showStatus)\n const shape = step.commit ? `[${label}]` : `(${label})`\n\n lines.push(` ${nodeId}${shape}`)\n\n if (exec && showStatus) {\n const statusClass = this.getStatusClass(exec.status)\n lines.push(` ${nodeId}:::${statusClass}`)\n }\n\n if (step.when) {\n const condId = `${nodeId}_cond`\n const skipped = exec?.status === 'skipped'\n lines.push(` ${condId}{Condition?}`)\n lines.push(` ${prevNode} --> ${condId}`)\n\n if (skipped) {\n lines.push(` ${condId}:::skipped`)\n lines.push(` ${condId} -->|no| ${nodeId}`)\n } else {\n lines.push(` ${condId} -->|yes| ${nodeId}`)\n }\n } else {\n lines.push(` ${prevNode} --> ${nodeId}`)\n }\n\n if (step.compensate && exec?.status === 'compensated') {\n lines.push(` ${nodeId}_comp[🔄 Compensated]`)\n lines.push(` ${nodeId}_comp:::compensated`)\n lines.push(` ${nodeId} -.->|rolled back| ${nodeId}_comp`)\n }\n\n prevNode = nodeId\n }\n }\n\n const finalStatus = this.getWorkflowFinalStatus(state.status)\n lines.push(` ${prevNode} --> End([End: ${finalStatus}])`)\n lines.push(` End:::${this.getStatusClass(state.status)}`)\n\n // Add status-based styling\n lines.push('')\n lines.push(' classDef started fill:#e3f2fd,stroke:#2196f3,stroke-width:2px')\n lines.push(' classDef completed fill:#e8f5e9,stroke:#4caf50,stroke-width:2px')\n lines.push(' classDef failed fill:#ffebee,stroke:#f44336,stroke-width:2px')\n lines.push(' classDef compensated fill:#fff3e0,stroke:#ff9800,stroke-width:2px')\n lines.push(\n ' classDef skipped fill:#f5f5f5,stroke:#9e9e9e,stroke-width:1px,stroke-dasharray: 5 5'\n )\n lines.push(' classDef pending fill:#fafafa,stroke:#bdbdbd,stroke-width:1px')\n lines.push(\n ' classDef suspended fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px,stroke-dasharray: 3 3'\n )\n\n return lines.join('\\n')\n }\n\n private sanitizeNodeId(name: string): string {\n return name.replace(/[^a-zA-Z0-9_]/g, '_')\n }\n\n private buildStepLabel(step: any, showDetails: boolean): string {\n let label = step.name\n\n if (showDetails) {\n const meta: string[] = []\n if (step.commit) {\n meta.push('📝')\n }\n if (step.retries && step.retries > 0) {\n meta.push(`↻${step.retries}`)\n }\n if (step.timeout) {\n meta.push(`⏱${step.timeout}ms`)\n }\n if (step.when) {\n meta.push('❓')\n }\n\n if (meta.length > 0) {\n label += `<br/><small>${meta.join(' ')}</small>`\n }\n }\n\n return label\n }\n\n private buildStepLabelWithStatus(\n step: any,\n exec: StepExecution | undefined,\n showDetails: boolean,\n showStatus: boolean\n ): string {\n let label = step.name\n\n if (showStatus && exec) {\n label += `<br/><small><b>${exec.status.toUpperCase()}</b></small>`\n\n if (exec.duration) {\n label += `<br/><small>${exec.duration}ms</small>`\n }\n\n if (exec.retries > 0) {\n label += `<br/><small>Retries: ${exec.retries}</small>`\n }\n } else if (showDetails) {\n const meta: string[] = []\n if (step.commit) {\n meta.push('📝')\n }\n if (step.retries && step.retries > 0) {\n meta.push(`↻${step.retries}`)\n }\n if (step.timeout) {\n meta.push(`⏱${step.timeout}ms`)\n }\n if (step.when) {\n meta.push('❓')\n }\n\n if (meta.length > 0) {\n label += `<br/><small>${meta.join(' ')}</small>`\n }\n }\n\n return label\n }\n\n private getStatusClass(status: string): string {\n switch (status) {\n case 'completed':\n return 'completed'\n case 'failed':\n return 'failed'\n case 'compensated':\n case 'compensating':\n return 'compensated'\n case 'skipped':\n return 'skipped'\n case 'suspended':\n return 'suspended'\n case 'running':\n return 'started'\n default:\n return 'pending'\n }\n }\n\n private getWorkflowFinalStatus(status: string): string {\n switch (status) {\n case 'completed':\n return '✅ Completed'\n case 'failed':\n return '❌ Failed'\n case 'rolled_back':\n return '🔄 Rolled Back'\n case 'suspended':\n return '⏸ Suspended'\n default:\n return status\n }\n }\n\n private getNextNodeId(steps: any[], currentIndex: number): string {\n const nextStep = steps[currentIndex + 1]\n return nextStep ? this.sanitizeNodeId(nextStep.name) : 'End'\n }\n}\n"]}
|