npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

awaitly-analyze

v0.32.1

Published

Static workflow analysis for awaitly. Analyze workflow source code to extract structure, types, and generate visualizations.

Readme

awaitly-analyze

Static workflow analysis for awaitly. Analyze workflow source code to extract structure, calculate complexity metrics, and generate visualizations.

Deterministic diagrams from real code

XState draws a perfect diagram because you hand-write the machine as data; the diagram is a serialization of that data, a second artifact you keep in sync with your implementation. awaitly diagrams the real executable code, so the diagram can't drift from behavior.

The cost: imperative control flow (if, for, computed ids) isn't always deterministically drawable. awaitly closes that gap the way XState does, by making the load-bearing control flow declarative so there's nothing left to infer:

  • Write step.if, when, or unless instead of a raw if, and the branch gets a stable, labelled id.
  • Write step.forEach instead of a raw loop, and each iteration gets a structured id.
  • Use literal step ids (put the dynamic part in key), and every node has a stable identity.

A workflow built from those constructs is fully diagrammable: its diagram is deterministic, and a runtime trace overlays onto it exactly. Native if/for...of/while qualify too — the analyzer derives a stable branch id from the condition and a stable loop id from the iterable. --assert-diagrammable (below) enforces this in CI, flagging only expressions it cannot read statically.

CLI Usage

# Writes checkout.workflow.md next to the source and prints the diagram
awaitly-analyze ./src/workflows/checkout.ts

# Print only (no adjacent file)
awaitly-analyze ./src/workflows/checkout.ts --no-output-adjacent

# JSON format (also writes checkout.workflow.json unless you pass --no-output-adjacent)
awaitly-analyze ./src/workflows/checkout.ts --format=json

# Show step keys and change diagram direction
awaitly-analyze ./src/workflows/checkout.ts --keys --direction=LR

# Custom suffix (creates checkout.diagram.md)
awaitly-analyze ./src/workflows/checkout.ts --suffix=diagram

# JSON with a custom adjacent name (creates checkout.analysis.json)
awaitly-analyze ./src/workflows/checkout.ts --suffix=analysis --format=json

# Write to file only, suppress stdout
awaitly-analyze ./src/workflows/checkout.ts --no-stdout

# Generate .types.ts next to the source
awaitly-analyze ./src/workflows/checkout.ts --types

# Generate interactive HTML with click-to-inspect
awaitly-analyze ./src/workflows/checkout.ts --html

# Custom HTML output path
awaitly-analyze ./src/workflows/checkout.ts --html --html-output=./docs/checkout.html

# Doctor mode: strict diagnostics with concrete fix guidance
awaitly-analyze ./src/workflows/checkout.ts --doctor

# Doctor mode JSON for AI/tooling pipelines
awaitly-analyze ./src/workflows/checkout.ts --doctor --format=json

# CI gate: fail if any workflow's diagram is not fully deterministic
awaitly-analyze ./src/workflows/checkout.ts --assert-diagrammable

# Overlay a recorded run's executed path onto the static diagram
awaitly-analyze ./src/workflows/checkout.ts --trace=./run-events.json

# Review every workflow your branch changed: diff, regressions, new doctor findings, railway diagrams
awaitly-analyze review --base origin/main src/

The same review runs on pull requests as a GitHub Action (uses: jagreehal/awaitly@analyze-v0) and posts one sticky comment. See GitHub Action.

CLI Options

| Option | Default | Description | |--------|---------|-------------| | --format=<format> | mermaid | Output format: mermaid or json | | --html | - | Generate interactive HTML file (Mermaid CDN + click-to-inspect) | | --html-output=<path> | <basename>.html | Output path for the HTML file | | --keys | - | Show step cache keys in diagram | | --direction=<dir> | TB | Diagram direction: TB, LR, BT, RL | | --output-adjacent, -o | on | Write <basename>.workflow.md (or .json) next to the source | | --no-output-adjacent | - | Print only; skip the adjacent diagram file | | --suffix=<value> | workflow | Configurable suffix for output file | | --no-stdout | - | Suppress stdout when a file is being written | | --types / --no-types | off | Generate <workflowName>.types.ts | | --dsl-output=<value> | off | Write DSL: off, .awaitly, or custom path (for visualization) | | --write-dsl | - | Shorthand for --dsl-output=.awaitly | | --doctor | - | Print strict diagnostics with fix suggestions (includes the diagrammability verdict) | | --assert-diagrammable | - | Exit non-zero if any workflow's diagram is not fully deterministic (CI gate) | | --trace=<events.json> | - | Overlay a recorded run's executed path onto the static diagram (JSON array of workflow events) | | --help, -h | - | Show help message |

Output File Naming

Adjacent output (default):

  • Mermaid format: {basename}.{suffix}.md
  • JSON format: {basename}.{suffix}.json

Example: checkout.ts with --suffix=workflow produces checkout.workflow.md

Features

  • Static Analysis - Extract workflow structure from TypeScript source without execution
  • Type Extraction - Extract Result-like types (AsyncResult, Result) from dependencies and steps
  • Path Generation - Enumerate all possible execution paths through a workflow
  • Complexity Metrics - Calculate cyclomatic complexity, cognitive complexity, and more
  • Mermaid Diagrams - Generate flowchart visualizations
  • Test Matrix - Generate test coverage matrices for workflow paths
  • Cross-Workflow Composition - Analyze dependencies between workflows
  • Data Flow Analysis - Track data dependencies with typed propagation

Installation

npm install awaitly-analyze ts-morph
# or
pnpm add awaitly-analyze ts-morph

ts-morph is a required peer dependency.

Quick Start

import {
  analyze,
  generatePaths,
  calculateComplexity,
  renderStaticMermaid,
} from 'awaitly-analyze';

// Analyze a workflow file
const ir = analyze('./src/workflows/checkout.ts').single();

// Generate all possible paths
const paths = generatePaths(ir);
console.log(`Found ${paths.length} unique execution paths`);

// Calculate complexity metrics
const metrics = calculateComplexity(ir);
console.log(`Cyclomatic complexity: ${metrics.cyclomaticComplexity}`);

// Generate Mermaid diagram
const mermaid = renderStaticMermaid(ir);
console.log(mermaid);

API Reference

Analyzing Workflows

analyze(filePath, options?)

Returns a fluent interface for analyzing workflows in a file.

import { analyze } from 'awaitly-analyze';

// Single workflow file - get the workflow directly
const ir = analyze('./checkout.ts').single();

// Multiple workflows - get all as array
const workflows = analyze('./workflows.ts').all();

// Get specific workflow by name
const checkout = analyze('./workflows.ts').named('checkoutWorkflow');

// Safe access (returns null instead of throwing)
const ir = analyze('./checkout.ts').singleOrNull();
const first = analyze('./workflows.ts').firstOrNull();

analyze.source(code, options?)

Analyze workflow source code from a string.

const ir = analyze.source(`
  const checkout = createWorkflow({ fetchUser });
  await checkout.run(async ({ steps }) => steps.fetchUser('1'));
`).single();
// ir.root.workflowName === 'checkout'

Path Generation

generatePaths(ir, options?)

Generate all unique execution paths through a workflow.

import { generatePaths, calculatePathStatistics } from 'awaitly-analyze';

const paths = generatePaths(ir, { maxPaths: 100 });

// Get statistics
const stats = calculatePathStatistics(paths);
console.log(`Total paths: ${stats.totalPaths}`);
console.log(`Shortest path: ${stats.shortestPathLength} steps`);
console.log(`Longest path: ${stats.longestPathLength} steps`);

generatePathsWithMetadata(ir, options?)

Generate paths with additional metadata about limit hits.

import { generatePathsWithMetadata } from 'awaitly-analyze';

const { paths, limitHit } = generatePathsWithMetadata(ir, { maxPaths: 50 });

if (limitHit) {
  console.log('Warning: Path limit reached, not all paths generated');
}

Complexity Metrics

calculateComplexity(ir)

Calculate complexity metrics for a workflow.

import { calculateComplexity } from 'awaitly-analyze';

const metrics = calculateComplexity(ir);

console.log(`Cyclomatic complexity: ${metrics.cyclomaticComplexity}`);
console.log(`Cognitive complexity: ${metrics.cognitiveComplexity}`);
console.log(`Max nesting depth: ${metrics.maxDepth}`);
console.log(`Max parallel breadth: ${metrics.maxParallelBreadth}`);
console.log(`Decision points: ${metrics.decisionPoints}`);
console.log(`Path count: ${metrics.pathCount}`);

assessComplexity(metrics, thresholds?)

Get a complexity assessment with warnings.

import {
  analyze,
  calculateComplexity,
  assessComplexity,
  formatComplexitySummary
} from 'awaitly-analyze';

const ir = analyze('./checkout.ts').single();
const metrics = calculateComplexity(ir);
const assessment = assessComplexity(metrics);
console.log(formatComplexitySummary(metrics, assessment));

Mermaid Diagrams

renderStaticMermaid(ir, options?)

Generate a Mermaid flowchart diagram.

import { renderStaticMermaid } from 'awaitly-analyze';

const mermaid = renderStaticMermaid(ir, {
  direction: 'TB',  // 'TB' | 'LR' | 'BT' | 'RL'
  includeKeys: true,
  includeDescriptions: true,
});

// Use in markdown:
// ```mermaid
// ${mermaid}
// ```

renderPathsMermaid(ir, paths, options?)

Generate a diagram highlighting specific paths.

import { renderPathsMermaid, generatePaths } from 'awaitly-analyze';

const paths = generatePaths(ir);
const diagram = renderPathsMermaid(ir, paths.slice(0, 3));

Diagrammability

computeDiagrammability(ir)

Return a single verdict on whether a workflow's diagram is fully deterministic, with each gap naming the first-class construct that closes it.

import { analyze, computeDiagrammability } from 'awaitly-analyze';

const ir = analyze('./src/workflows/checkout.ts').single();
const report = computeDiagrammability(ir);

report.deterministic; // boolean, true when there are no gaps
report.score;         // 0-100, share of nodes with a stable identity
report.issues;        // [{ kind, message, suggestion, location, nodeId }]
// kinds: 'dynamic-step-id' | 'dynamic-decision-id' | 'raw-conditional'
//        | 'raw-loop' | 'unbounded-loop' | 'unknown-node'

Use --assert-diagrammable on the CLI to turn this into a CI gate.

Runtime Trace Overlay

Render the static skeleton and highlight the path a real run took, the view XState's inspector gives, on the real executable code (so the diagram can't drift from behavior).

traceFromEvents(events)

Reduce an awaitly workflow event stream (captured via the onEvent option) into a per-step trace.

import { traceFromEvents, renderStaticMermaidWithTrace, analyze } from 'awaitly-analyze';

const trace = traceFromEvents(recordedEvents); // WorkflowEvent[] → per-step status

renderStaticMermaidWithTrace(ir, trace, options?)

Overlay the trace onto the static diagram: each executed step is restyled by its status (success / error / aborted / skipped / cache-hit / running); untouched steps stay in the base style.

const ir = analyze('./src/workflows/checkout.ts').single();
const { mermaid, matched, unmatched } = renderStaticMermaidWithTrace(ir, trace);
// `unmatched` lists trace steps with no static node; empty when the workflow
// is diagrammable (literal step ids), which is why the two features reinforce
// each other.

Test Matrix

generateTestMatrix(paths)

Generate a test coverage matrix from paths.

import { generateTestMatrix, formatTestMatrixMarkdown } from 'awaitly-analyze';

const paths = generatePaths(ir);
const matrix = generateTestMatrix(paths);

// Format as markdown table
console.log(formatTestMatrixMarkdown(matrix));

// Or as code
import { formatTestMatrixAsCode } from 'awaitly-analyze';
console.log(formatTestMatrixAsCode(matrix));

Cross-Workflow Composition

analyzeWorkflowGraph(files, options?)

Analyze dependencies between workflows across multiple files.

import {
  analyzeWorkflowGraph,
  getTopologicalOrder,
  renderGraphMermaid,
} from 'awaitly-analyze';

const graph = analyzeWorkflowGraph([
  './src/workflows/checkout.ts',
  './src/workflows/payment.ts',
  './src/workflows/shipping.ts',
]);

// Get execution order (workflows with no dependencies first)
const order = getTopologicalOrder(graph);
console.log('Execution order:', order.map(n => n.name).join(' -> '));

// Visualize the dependency graph
const diagram = renderGraphMermaid(graph);

Interactive HTML

Generate a self-contained HTML file with an interactive Mermaid diagram and a click-to-inspect panel. The output includes 6 color themes, system preference auto-detection, and localStorage persistence.

extractNodeMetadata(ir)

Walk the IR tree and produce a WorkflowMetadata object with per-node details (step IDs, callees, retry/timeout config, types, source locations) keyed by Mermaid node ID.

import { analyze, extractNodeMetadata } from 'awaitly-analyze';
import type { WorkflowMetadata } from 'awaitly-analyze';

const ir = analyze('./checkout.ts').single();
const metadata: WorkflowMetadata = extractNodeMetadata(ir);

generateInteractiveHTML(mermaidText, metadata, options?)

Combine Mermaid text from renderStaticMermaid() with metadata from extractNodeMetadata() into a complete HTML string.

import {
  analyze,
  renderStaticMermaid,
  extractNodeMetadata,
  generateInteractiveHTML,
} from 'awaitly-analyze';
import type { InteractiveHTMLOptions } from 'awaitly-analyze';

const ir = analyze('./checkout.ts').single();
const mermaid = renderStaticMermaid(ir);
const metadata = extractNodeMetadata(ir);
const html = generateInteractiveHTML(mermaid, metadata, {
  theme: 'midnight',          // 'midnight' | 'ocean' | 'ember' | 'forest' | 'daylight' | 'paper'
  title: 'Checkout Workflow', // defaults to workflow name
  direction: 'TB',            // diagram direction
});

import { writeFileSync } from 'node:fs';
writeFileSync('checkout.html', html);

InteractiveHTMLOptions:

| Option | Type | Default | Description | |--------|------|---------|-------------| | title | string | workflow name | Page title | | theme | string | auto-detect | Initial color theme | | mermaidCdnUrl | string | latest v11 | Mermaid CDN URL | | direction | "TB" \| "LR" \| "BT" \| "RL" | "TB" | Diagram direction |

Workflow Diagram DSL

renderWorkflowDSL(ir)

Produce a state-machine-like DSL for xstate-style visualization (states and transitions with event labels). Types are defined in awaitly; the analyzer emits DSL that conforms to them.

import { analyze, renderWorkflowDSL, writeDSLToAwaitlyDir } from 'awaitly-analyze';
import type { WorkflowDiagramDSL } from 'awaitly';

const ir = analyze('./checkout.ts').single();
const dsl: WorkflowDiagramDSL = renderWorkflowDSL(ir);

// Optional: write to .awaitly/dsl/ (default) or a custom folder
await writeDSLToAwaitlyDir(dsl, { rootDir: process.cwd() });
// Custom folder: await writeDSLToAwaitlyDir(dsl, { rootDir: process.cwd(), outputDir: 'dist/dsl' });

Identity contract: DSL state ids normally use the semantic ids authored in the code (step()'s literal first argument, step.if()'s decision id). If a collision requires a suffixed diagram id, state.semanticId preserves the authored identity used by runtime graph validation, so the DSL remains directly usable as the graph option. Literal cache keys are carried on state.key. For current-node highlighting, WorkflowSnapshot.execution.currentStepId holds the step key — match it against state.key ?? state.semanticId ?? state.id. See awaitly diagram-dsl types for details.

JSON Output

renderStaticJSON(ir, options?)

Serialize workflow IR to JSON.

import { renderStaticJSON } from 'awaitly-analyze';

const json = renderStaticJSON(ir, { pretty: true });
fs.writeFileSync('workflow.json', json);

Detected Patterns

The analyzer detects the following awaitly patterns:

  • createWorkflow() - Standard workflow creation (named or deps-first: createWorkflow({ deps }) recovers the workflow name from the binding variable)
  • run() - Inline workflow execution (deps-first with auto-bound steps.*())
  • createSagaWorkflow() - Saga workflow creation
  • runSaga() - Inline saga execution

Within workflows, it detects:

  • steps.fetchUser(id) / step('fetchUser', () => deps.fetchUser(id)) - Deps-first bound steps and classic step calls
  • steps.validateUser(await steps.fetchUser(id)) - Steps awaited inline as call arguments, in evaluation order
  • step.parallel() / allAsync() / allSettledAsync() - Parallel execution
  • step.race() / anyAsync() - Race execution
  • step.sleep(id, duration, opts?) - Sleep steps (ID required as first argument)
  • step.retry(id, operation, opts) - Retry wrappers (ID required as first argument)
  • step.withTimeout(id, operation, opts) - Timeout wrappers (ID required as first argument)
  • step.try(id, operation, opts) - Try/catch steps (ID required as first argument)
  • step.fromResult(id, operation, opts) - FromResult steps (ID required as first argument)
  • step.getWritable() / step.getReadable() / step.streamForEach() - Streaming
  • when() / unless() / whenOr() / unlessOr() - Conditional helpers
  • if/else, switch - Control flow
  • for, while, forEach, map - Loops
  • Saga steps with compensation (saga.step(), saga.tryStep())

Import Styles

The analyzer supports various import styles:

// Named imports
import { createWorkflow, run } from 'awaitly';

// Aliased imports
import { createWorkflow as cw } from 'awaitly';

Namespace imports (import * as X from 'awaitly') and default imports from older awaitly versions are also recognized, so the analyzer keeps working on codebases that have not migrated to named imports yet.

Types

Key types exported from the package:

import type {
  // IR nodes
  StaticWorkflowIR,
  StaticFlowNode,
  StaticStepNode,
  StaticParallelNode,
  StaticConditionalNode,

  // Paths
  WorkflowPath,
  PathStatistics,

  // Complexity
  ComplexityMetrics,
  ComplexityAssessment,

  // Test matrix
  TestMatrix,
  TestPath,

  // Graph
  WorkflowGraph,
  WorkflowGraphNode,

  // Interactive HTML
  NodeMetadata,
  WorkflowMetadata,
  InteractiveHTMLOptions,
} from 'awaitly-analyze';

API types reference

Main static-analysis node types and when fields are populated:

  • StaticWorkflowNode (root): workflowName, source, dependencies, children, description, markdown, jsdocDescription?, errorTypes.
    description and markdown are set only for createWorkflow / createSagaWorkflow (from options or deps). They are undefined for run() / runSaga() (no options object). jsdocDescription is extracted from JSDoc above the workflow variable when present.

  • StaticStepNode: stepId, callee, name, key, description, markdown, jsdocDescription?, retry, timeout.
    stepId is the required first argument for all step types: step('id', fn, opts), step.sleep('id', duration, opts?), step.retry('id', operation, opts), step.withTimeout('id', operation, opts), step.try('id', operation, opts), step.fromResult('id', operation, opts). Legacy step(fn, opts) is still parsed but yields stepId: "<missing>" and a warning. description and markdown come from step options; jsdocDescription is extracted from JSDoc above the step statement when present.

  • StaticSagaStepNode: callee, name, description, markdown, jsdocDescription?, hasCompensation, compensationCallee, isTryStep.
    description and markdown come from saga step options (e.g. saga.step(fn, { description, markdown })). jsdocDescription is extracted from JSDoc above the saga step statement when present.

  • DependencyInfo: name, typeSignature?, errorTypes, signature?.
    typeSignature is the TypeScript type of the dependency (e.g. the function type), when the type checker is available; it may be undefined. errorTypes is not yet inferred from types and is typically empty. signature provides typed parameter and return type information when available.

JSDoc

The analyzer extracts JSDoc comments from workflow declarations (createWorkflow / createSagaWorkflow variable statements) and from step call sites (the statement containing await step(...), step.sleep(...), saga.step(...), etc.). Extracted text is exposed as jsdocDescription on the root (StaticWorkflowNode) and on step nodes (StaticStepNode, StaticSagaStepNode). Only the main description (text before the first @tag) is extracted; @param / @returns are not parsed into separate fields. Option-based description and markdown remain the canonical documentation fields and take precedence for display; JSDoc is additive so consumers can use description ?? jsdocDescription for fallback.

Type Extraction

The analyzer extracts type information from workflows when the TypeScript type checker is available. This includes:

  • Step output types - Extracted from dependency return types
  • Error types - Inferred from Result-like types
  • Parameter types - For dependency functions
  • Typed signatures - Full function signatures with Result-like type details

TypeInfo

Type information is represented by the TypeInfo interface:

interface TypeInfo {
  /** Human-readable type string */
  display: string;
  /** Normalized, fully qualified type string */
  canonical: string;
  /** Kind of Result-like type detected */
  kind: "asyncResult" | "result" | "promiseResult" | "plain" | "unknown";
  /** Confidence level of the extraction */
  confidence: "exact" | "inferred" | "fallback";
  /** Where the type information came from */
  source: "checker" | "annotation" | "fallback";
}

Confidence Levels

  • exact: Type extracted directly from explicit type annotations (e.g., : AsyncResult<User, Error>)
  • inferred: Type inferred from usage patterns or string parsing of type signatures
  • fallback: Default when type checker unavailable or type cannot be resolved

Result-like Types

The analyzer recognizes and extracts generics from:

  • AsyncResult<T, E, C> - Async Result with ok, error, and cause types
  • Result<T, E, C> - Synchronous Result
  • Promise<Result<T, E>> - Wrapped Promise Result

Example: Extracting Typed Dependencies

import { analyze } from 'awaitly-analyze';

const ir = analyze('./user-workflow.ts').single();

// Access typed dependency signatures
for (const dep of ir.root.dependencies) {
  console.log(`Dependency: ${dep.name}`);
  
  if (dep.signature) {
    console.log(`  Parameters:`);
    for (const param of dep.signature.params) {
      console.log(`    - ${param.name}: ${param.type.display}`);
    }
    console.log(`  Returns: ${dep.signature.returnType.display}`);
    
    if (dep.signature.resultLike) {
      console.log(`  Ok Type: ${dep.signature.resultLike.okType.display}`);
      console.log(`  Error Type: ${dep.signature.resultLike.errorType.display}`);
    }
  }
}

Example: Step Type Information

import { analyze } from 'awaitly-analyze';

const ir = analyze('./user-workflow.ts').single();

// Walk steps to get output type info
function walkSteps(nodes) {
  for (const node of nodes) {
    if (node.type === 'step') {
      console.log(`Step: ${node.stepId}`);
      console.log(`  Output Type: ${node.outputType}`);
      console.log(`  Output Type Info:`, node.outputTypeInfo);
      console.log(`  Error Type Info:`, node.errorTypeInfo);
      console.log(`  Cause Type Info:`, node.causeTypeInfo);
    }
    if (node.children) walkSteps(node.children);
  }
}

walkSteps(ir.root.children);

JSON output shape

The output of renderStaticJSON(ir) has this structure. Agents and doc generators can use it to parse or validate the JSON.

  • Top level: { root, metadata?, references? }

    • root: StaticWorkflowNode (see below)
    • metadata: { analyzedAt, filePath, tsVersion?, warnings?, stats? }
    • references: object mapping workflow name to { root, metadata? } (when inlined)
  • StaticWorkflowNode (root): type: "workflow", id, workflowName, source?, dependencies[], errorTypes[], children[], description?, markdown?, jsdocDescription?, name?, key?, location?, typeSummary?

  • DependencyInfo (each entry in dependencies): name, typeSignature?, errorTypes[], signature?

    • signature (optional): { params: [{ name, type: TypeInfo }], returnType: TypeInfo, resultLike?: { okType: TypeInfo, errorType: TypeInfo, causeType?: TypeInfo } }
  • Flow nodes (children and nested): discriminated by type:

    • "step": id, stepId, name?, key?, callee?, description?, markdown?, jsdocDescription?, retry?, timeout?, location?, outputType?, inputType?, outputTypeInfo?, errorTypeInfo?, causeTypeInfo?
    • "saga-step": id, name?, callee?, description?, markdown?, jsdocDescription?, hasCompensation, compensationCallee?, isTryStep?, location?, outputTypeInfo?, errorTypeInfo?, compensationParamTypeInfo?
    • "sequence": id, children[]
    • "parallel": id, children[], mode ("all" | "allSettled"), callee?
    • "race": id, children[], callee?
    • "conditional": id, condition, consequent[], alternate?, helper?, defaultValue?
    • "switch": id, expression, cases[] (each: value?, isDefault, body[])
    • "loop": id, loopType, body[], iterSource?, boundKnown, boundCount?
    • "stream": id, streamType, namespace?, options?, callee?
    • "workflow-ref": id, workflowName, resolved, resolvedPath?, inlinedIR?
    • "unknown": id, reason, sourceCode?
  • TypeInfo (embedded in various nodes):

    • display: Human-readable type string
    • canonical: Normalized type string
    • kind: "asyncResult" | "result" | "promiseResult" | "plain" | "unknown"
    • confidence: "exact" | "inferred" | "fallback"
    • source: "checker" | "annotation" | "fallback"

A JSON Schema for this structure is available at schema/static-workflow-ir.schema.json in this package.

Requirements

  • Node.js >= 22
  • TypeScript project with ts-morph >= 28.0.0

License

MIT