sfn-diagram 1.1.1 → 1.3.0

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 CHANGED
@@ -1,6 +1,8 @@
1
1
  # sfn-diagram
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/sfn-diagram.svg)](https://www.npmjs.com/package/sfn-diagram)
4
+ [![npm downloads](https://img.shields.io/npm/dm/sfn-diagram.svg)](https://www.npmjs.com/package/sfn-diagram)
5
+ [![Unit Test](https://github.com/yusufaf/sfn-diagram/actions/workflows/unit-test.yml/badge.svg)](https://github.com/yusufaf/sfn-diagram/actions/workflows/unit-test.yml)
4
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
7
  [![Live Playground](https://img.shields.io/badge/playground-live-brightgreen)](https://yusufaf.github.io/sfn-diagram/)
6
8
 
@@ -8,6 +10,30 @@ Generate beautiful, interactive diagrams from AWS Step Functions ASL (Amazon Sta
8
10
 
9
11
  **▶ [Try it in the live playground](https://yusufaf.github.io/sfn-diagram/)** — paste any ASL definition and preview the diagram instantly, no install required.
10
12
 
13
+ <picture>
14
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/parallel-dark.svg">
15
+ <img src="docs/images/parallel-light.svg" alt="Example diagram of a Parallel state Step Functions workflow generated by sfn-diagram" width="410">
16
+ </picture>
17
+
18
+ ## Table of Contents
19
+
20
+ - [What it looks like](#what-it-looks-like)
21
+ - [Why sfn-diagram](#why-sfn-diagram)
22
+ - [Features](#features)
23
+ - [Installation](#installation)
24
+ - [Runtime Support](#runtime-support)
25
+ - [Command-line Usage](#command-line-usage)
26
+ - [Docker](#docker)
27
+ - [Quick Start](#quick-start)
28
+ - [API Reference](#api-reference)
29
+ - [Configuration Options](#configuration-options)
30
+ - [Supported State Types](#supported-state-types)
31
+ - [Examples](#examples)
32
+ - [TypeScript Support](#typescript-support)
33
+ - [Use with your framework](#use-with-your-framework)
34
+ - [Ecosystem](#ecosystem)
35
+ - [Contributing](#contributing)
36
+
11
37
  ## What it looks like
12
38
 
13
39
  Give it an ASL definition like this order-processing workflow ([`examples/order-processing.asl.json`](examples/order-processing.asl.json)):
@@ -57,11 +83,27 @@ stateDiagram-v2
57
83
  class OrderComplete successState
58
84
  ```
59
85
 
86
+ ## Why sfn-diagram
87
+
88
+ There's no shortage of ways to look at a state machine — this is where sfn-diagram fits versus the other two most common options:
89
+
90
+ | | **sfn-diagram** | AWS Console visualizer | Mermaid Live Editor |
91
+ |---|---|---|---|
92
+ | Input | ASL, CloudFormation/SAM/CDK templates, live AWS state machines | Deployed state machine only | Manual Mermaid you write by hand |
93
+ | Output | SVG, Mermaid, PNG, interactive HTML | Static graph in-browser | Mermaid diagram only |
94
+ | CI / PR integration | [GitHub Action](#github-action) posts a diff overlay on every PR that touches ASL | None | None |
95
+ | Execution overlays | Paints a real run's path/status/duration onto the diagram | Basic per-execution highlighting, console-only | Not applicable |
96
+ | Runs where | CLI, Node, browser, edge runtimes | AWS Console only | Web app only |
97
+ | Automation | Full programmatic API (`generateSvg`, `generateMermaid`, etc.) | None — UI only | None — UI only |
98
+
99
+ In short: reach for the AWS Console visualizer to eyeball a state machine you already deployed, reach for Mermaid Live if you're hand-drawing a diagram from scratch — reach for sfn-diagram when you want diagrams generated automatically from your actual definition, checked into CI, and diffed on every pull request.
100
+
60
101
  ## Features
61
102
 
62
103
  - **Multiple Output Formats**: SVG (D3.js), Mermaid syntax, and PNG
63
104
  - **Automatic Layout**: Smart graph positioning using Dagre layout engine
64
105
  - **Full ASL Support**: All state types (Pass, Task, Choice, Wait, Succeed, Fail, Parallel, Map), both JSONPath and JSONata query languages, plus `Catch`/`Retry` rendering
106
+ - **Modern ASL**: Variables (`Assign`) shown per state, and Distributed Map rendered distinctly from an inline Map — including its `MaxConcurrency`, `ItemReader` source, and `ResultWriter` sink
65
107
  - **Visual Diffing**: Compare two definitions and highlight added / modified / removed states — drives the PR-preview GitHub Action
66
108
  - **Execution Overlays**: Paint a real execution's history onto the diagram — succeeded/failed/caught/not-reached states, the taken path, and per-state duration & retry counts
67
109
  - **Customizable Themes**: AWS light/dark themes plus custom theme support
@@ -117,7 +159,51 @@ npx sfn-diagram state.asl.json --format mermaid > diagram.mmd
117
159
  cat state.asl.json | npx sfn-diagram - --format svg
118
160
  ```
119
161
 
120
- Flags: `--format <svg|mermaid|png|html>`, `-o/--output <path>`, `--theme <light|dark>`, `--layout <TB|LR|RL|BT>`, `--hide-catch`, `--resolve-cfn`, `--resource <logicalId>`, `-h/--help`, `-v/--version`.
162
+ | Flag | Description |
163
+ |---|---|
164
+ | `--format <svg\|mermaid\|png\|html>` | Output format (default: `svg`) |
165
+ | `-o`, `--output <path>` | Output file (required for `png`; stdout otherwise) |
166
+ | `--theme <light\|dark>` | Color theme for SVG/PNG/HTML (default: `light`) |
167
+ | `--layout <TB\|LR\|RL\|BT>` | Graph direction (default: `TB`) |
168
+ | `--hide-catch` | Drop error-handler (`Catch`) branches |
169
+ | `--hide-variables` | Drop the `$var` annotations for ASL `Assign` blocks |
170
+ | `--show-icons` | Draw [AWS service icons](#aws-service-icons) on Task states |
171
+ | `--icon-position <left\|top\|right>` | Icon placement relative to the label (default: `left`) |
172
+ | `--icon-size <pixels>` | Icon size in pixels (default: `24`) |
173
+ | `--diff <baseline>` | Compare the input (head) against a baseline definition |
174
+ | `--execution <history.json>` | Overlay a `GetExecutionHistory` result on the diagram |
175
+ | `--resolve-cfn` | Treat the input as a CloudFormation/SAM/CDK template |
176
+ | `--resource <logicalId>` | State machine to extract when the template has several |
177
+ | `-h`, `--help` / `-v`, `--version` | Show help / version and exit |
178
+
179
+ ### Diff and execution overlays from the CLI
180
+
181
+ ```bash
182
+ # Highlight what changed between two revisions of a definition
183
+ npx sfn-diagram head.asl.json --diff base.asl.json -o diff.svg
184
+ npx sfn-diagram head.asl.json --diff base.asl.json --format mermaid > diff.mmd
185
+
186
+ # Colour a diagram by what actually happened in a run
187
+ aws stepfunctions get-execution-history --execution-arn "$ARN" > history.json
188
+ npx sfn-diagram state.asl.json --execution history.json -o run.svg
189
+ ```
190
+
191
+ Both flags support `--format svg` and `--format mermaid` only, and cannot be combined
192
+ with each other. The change/status summary is written to **stderr**, so the diagram on
193
+ stdout still pipes cleanly:
194
+
195
+ ```
196
+ Diff summary:
197
+ Added: NewStep
198
+ Modified: StepB
199
+ Removed: StepC
200
+ Unchanged: 1
201
+ ```
202
+
203
+ `--execution` accepts either a full `GetExecutionHistory` response (`{"events": [...]}`)
204
+ or a bare events array. See [generateDiff](#generatemermaiddiffparams--generatediffparams)
205
+ and [generateExecution](#generateexecutionparams--generatemermaidexecutionparams) for the
206
+ programmatic equivalents.
121
207
 
122
208
  CloudFormation/SAM/CDK templates work as input too — see [Extracting ASL from a CDK/CloudFormation template](#extracting-asl-from-a-cdkcloudformation-template).
123
209
 
@@ -202,6 +288,7 @@ const result = generateSvg({
202
288
  padding: 20, // Diagram padding
203
289
  edgeStyle: 'curved', // 'curved', 'straight', 'orthogonal'
204
290
  showStateTypes: false, // Display state types on nodes
291
+ showVariables: true, // Annotate nodes with the variables they Assign
205
292
  includeComments: true, // Use state comments as labels
206
293
  customColors: {} // Override colors for specific states
207
294
  });
@@ -535,6 +622,36 @@ Big, branchy state machines are hard to read as a static image. A few options he
535
622
  - **`--layout LR`** (or `layout: 'LR'`) — the default `TB` layout makes catch-heavy
536
623
  or deeply branching machines extremely tall; `LR` reads better for wide graphs.
537
624
 
625
+ ### Variables and Distributed Map
626
+
627
+ Two pieces of modern ASL are rendered explicitly, because both are otherwise
628
+ invisible in a diagram.
629
+
630
+ **Variables (`Assign`).** A state that assigns variables is annotated with their
631
+ names beneath its label — `$orderId, $total`. The list caps at three names, then
632
+ collapses to `+N more`, so a state assigning many variables cannot blow out the
633
+ node width. Disable with `showVariables: false`.
634
+
635
+ ```typescript
636
+ const { svg } = generateSvg({ aslDefinition: asl, showVariables: false });
637
+ ```
638
+
639
+ **Distributed Map.** A Map whose `ItemProcessor` declares
640
+ `ProcessorConfig.Mode: 'DISTRIBUTED'` runs a child execution per batch rather than
641
+ iterating inline, so it is labelled `Distributed` in the container header instead
642
+ of rendering identically to an inline Map. `MaxConcurrency` is shown alongside it
643
+ when set.
644
+
645
+ Its `ItemReader` (dataset source — S3 or Athena) and `ResultWriter` (result sink)
646
+ each become a satellite node beside the container, wired in and out of the Map.
647
+ With `showIcons: true` they pick up the appropriate AWS service icon, resolved
648
+ from the ARN the same way Task states are.
649
+
650
+ ```
651
+ ItemReader (s3) ──▶ ProcessItems ──▶ ResultWriter (s3)
652
+ Distributed · max 100
653
+ ```
654
+
538
655
  ### AWS Service Icons
539
656
 
540
657
  Display AWS service icons on Task state nodes to improve diagram readability and quickly identify which AWS services are being used.
@@ -760,6 +877,120 @@ import type {
760
877
  import type { PngOutput, ExportPngParams } from 'sfn-diagram/png';
761
878
  ```
762
879
 
880
+ ## Use with your framework
881
+
882
+ `generateSvg()` returns an SVG **string** and the core has no DOM dependency, so any
883
+ framework can render a diagram by injecting that string. There is nothing React-specific
884
+ in the core — [`sfn-diagram-react`](packages/sfn-diagram-react/) exists for convenience,
885
+ not necessity.
886
+
887
+ Each snippet below is the whole integration. See [API Reference](#api-reference) for the
888
+ full option set (`theme`, `layout`, `showIcons`, …) and [`generateMermaid`](#generatemermaidparams)
889
+ if you would rather emit Mermaid than SVG.
890
+
891
+ **Svelte 5**
892
+
893
+ ```svelte
894
+ <script lang="ts">
895
+ import { generateSvg } from 'sfn-diagram';
896
+
897
+ let { definition } = $props();
898
+ const svg = $derived(generateSvg({ aslDefinition: definition }).svg);
899
+ </script>
900
+
901
+ {@html svg}
902
+ ```
903
+
904
+ **Vue 3**
905
+
906
+ ```vue
907
+ <script setup lang="ts">
908
+ import { computed } from 'vue';
909
+ import { generateSvg } from 'sfn-diagram';
910
+ import type { AslDefinition } from 'sfn-diagram';
911
+
912
+ const props = defineProps<{ definition: AslDefinition | string }>();
913
+ const svg = computed(() => generateSvg({ aslDefinition: props.definition }).svg);
914
+ </script>
915
+
916
+ <template>
917
+ <div v-html="svg" />
918
+ </template>
919
+ ```
920
+
921
+ **Solid**
922
+
923
+ ```tsx
924
+ import { createMemo } from 'solid-js';
925
+ import { generateSvg } from 'sfn-diagram';
926
+ import type { AslDefinition } from 'sfn-diagram';
927
+
928
+ export function SfnDiagram(props: { definition: AslDefinition | string }) {
929
+ const svg = createMemo(() => generateSvg({ aslDefinition: props.definition }).svg);
930
+ return <div innerHTML={svg()} />;
931
+ }
932
+ ```
933
+
934
+ **Angular**
935
+
936
+ `[innerHTML]` strips SVG unless the value is marked trusted, so the sanitizer bypass is required.
937
+
938
+ ```ts
939
+ import { Component, computed, inject, input } from '@angular/core';
940
+ import { DomSanitizer } from '@angular/platform-browser';
941
+ import { generateSvg } from 'sfn-diagram';
942
+ import type { AslDefinition } from 'sfn-diagram';
943
+
944
+ @Component({
945
+ selector: 'sfn-diagram',
946
+ template: `<div [innerHTML]="svg()"></div>`,
947
+ })
948
+ export class SfnDiagramComponent {
949
+ readonly definition = input.required<AslDefinition | string>();
950
+ private readonly sanitizer = inject(DomSanitizer);
951
+
952
+ readonly svg = computed(() =>
953
+ this.sanitizer.bypassSecurityTrustHtml(
954
+ generateSvg({ aslDefinition: this.definition() }).svg
955
+ )
956
+ );
957
+ }
958
+ ```
959
+
960
+ **Astro**
961
+
962
+ Astro runs the frontmatter at build time, so the diagram ships as static markup with
963
+ **zero client-side JavaScript** — the DOM-free core at its best. (Astro also hosts React
964
+ islands, so `sfn-diagram-react` works there too if you need interactivity.)
965
+
966
+ ```astro
967
+ ---
968
+ import { generateSvg } from 'sfn-diagram';
969
+ // `?raw` gives the file as a string, which `aslDefinition` accepts directly
970
+ import definition from '../workflows/order-processing.asl.json?raw';
971
+
972
+ const { svg } = generateSvg({ aslDefinition: definition, theme: 'light' });
973
+ ---
974
+
975
+ <Fragment set:html={svg} />
976
+ ```
977
+
978
+ **Vanilla JS / htmx / anything else**
979
+
980
+ ```js
981
+ import { generateSvg } from 'sfn-diagram';
982
+
983
+ const response = await fetch('/workflows/order-processing.asl.json');
984
+ const { svg } = generateSvg({ aslDefinition: await response.text() });
985
+ document.querySelector('#diagram').innerHTML = svg;
986
+ ```
987
+
988
+ > **On the "unsafe HTML" APIs.** Every snippet above uses its framework's raw-HTML escape
989
+ > hatch (`{@html}`, `v-html`, `innerHTML`, `dangerouslySetInnerHTML`). The SVG builder
990
+ > HTML-escapes every attribute and text value it emits, following the HTML serialization
991
+ > algorithm, so state names, comments, and choice conditions taken from your ASL are
992
+ > escaped before they reach the string.
993
+
763
994
  ## Ecosystem
764
995
 
765
996
  ### Playground
@@ -790,7 +1021,11 @@ code --install-extension vscode-sfn-diagram-*.vsix
790
1021
 
791
1022
  ### React Component
792
1023
 
793
- Render diagrams in a React app with the [`sfn-diagram-react`](packages/sfn-diagram-react/) package. It wraps `generateSvg`/`generateMermaid` in a component with the same platform-agnostic core, so it works in any React renderer.
1024
+ Render diagrams in a React app with the [`sfn-diagram-react`](https://www.npmjs.com/package/sfn-diagram-react) package ([source](packages/sfn-diagram-react/)). It wraps `generateSvg`/`generateMermaid` in a component with the same platform-agnostic core, so it works in any React renderer. React 18 and 19 are both supported.
1025
+
1026
+ ```bash
1027
+ npm install sfn-diagram-react sfn-diagram react
1028
+ ```
794
1029
 
795
1030
  ```tsx
796
1031
  import { SfnDiagram } from 'sfn-diagram-react';
package/dist/cfn.d.cts CHANGED
@@ -23,8 +23,33 @@ interface ChoiceRule {
23
23
  Variable?: string;
24
24
  [key: string]: any;
25
25
  }
26
+ /**
27
+ * A Map state's `ProcessorConfig`, which selects between the inline and
28
+ * distributed execution modes. `Mode: 'DISTRIBUTED'` marks a Distributed Map —
29
+ * a separate child execution per batch, with its own concurrency and failure
30
+ * tolerance semantics.
31
+ */
32
+ interface ProcessorConfig {
33
+ ExecutionType?: "EXPRESS" | "STANDARD";
34
+ Mode?: "DISTRIBUTED" | "INLINE";
35
+ }
36
+ /**
37
+ * A Distributed Map `ItemReader` (dataset source) or `ResultWriter` (result sink).
38
+ * Both point at an AWS resource — typically S3, or Athena for `ItemReader` — via
39
+ * the same `Resource` ARN shape used by Task states.
40
+ */
41
+ interface ItemIo {
42
+ Parameters?: Record<string, any>;
43
+ ReaderConfig?: {
44
+ InputType?: string;
45
+ MaxItems?: number;
46
+ };
47
+ Resource?: string;
48
+ WriterConfig?: Record<string, any>;
49
+ }
26
50
  /** A single state within an ASL definition, covering fields for every state type. */
27
51
  interface AslState {
52
+ Arguments?: Record<string, any>;
28
53
  Assign?: Record<string, any>;
29
54
  Branches?: AslDefinition[];
30
55
  Catch?: CatchBlock[];
@@ -34,8 +59,13 @@ interface AslState {
34
59
  Default?: string;
35
60
  End?: boolean;
36
61
  Error?: string;
62
+ ItemBatcher?: Record<string, any>;
37
63
  ItemProcessor?: AslDefinition;
64
+ ItemReader?: ItemIo;
65
+ ItemSelector?: Record<string, any>;
38
66
  Iterator?: AslDefinition;
67
+ Label?: string;
68
+ MaxConcurrency?: number | string;
39
69
  Next?: string;
40
70
  Output?: any;
41
71
  Parameters?: Record<string, any>;
@@ -43,16 +73,25 @@ interface AslState {
43
73
  Resource?: string;
44
74
  Result?: any;
45
75
  ResultPath?: string;
76
+ ResultWriter?: ItemIo;
46
77
  Retry?: RetryBlock[];
47
78
  Seconds?: number | string;
48
79
  SecondsPath?: string;
49
80
  Timestamp?: string;
50
81
  TimestampPath?: string;
82
+ ToleratedFailureCount?: number;
83
+ ToleratedFailurePercentage?: number;
51
84
  Type: StateType;
52
85
  }
53
86
  /** A complete Amazon States Language state machine definition. */
54
87
  interface AslDefinition {
55
88
  Comment?: string;
89
+ /**
90
+ * Map-specific, and only valid on an `ItemProcessor` sub-definition rather
91
+ * than on the Map state itself — this is where the ASL spec places it.
92
+ * Selects inline vs distributed execution.
93
+ */
94
+ ProcessorConfig?: ProcessorConfig;
56
95
  QueryLanguage?: "JSONata" | "JSONPath";
57
96
  StartAt: string;
58
97
  States: Record<string, AslState>;
package/dist/cfn.d.ts CHANGED
@@ -23,8 +23,33 @@ interface ChoiceRule {
23
23
  Variable?: string;
24
24
  [key: string]: any;
25
25
  }
26
+ /**
27
+ * A Map state's `ProcessorConfig`, which selects between the inline and
28
+ * distributed execution modes. `Mode: 'DISTRIBUTED'` marks a Distributed Map —
29
+ * a separate child execution per batch, with its own concurrency and failure
30
+ * tolerance semantics.
31
+ */
32
+ interface ProcessorConfig {
33
+ ExecutionType?: "EXPRESS" | "STANDARD";
34
+ Mode?: "DISTRIBUTED" | "INLINE";
35
+ }
36
+ /**
37
+ * A Distributed Map `ItemReader` (dataset source) or `ResultWriter` (result sink).
38
+ * Both point at an AWS resource — typically S3, or Athena for `ItemReader` — via
39
+ * the same `Resource` ARN shape used by Task states.
40
+ */
41
+ interface ItemIo {
42
+ Parameters?: Record<string, any>;
43
+ ReaderConfig?: {
44
+ InputType?: string;
45
+ MaxItems?: number;
46
+ };
47
+ Resource?: string;
48
+ WriterConfig?: Record<string, any>;
49
+ }
26
50
  /** A single state within an ASL definition, covering fields for every state type. */
27
51
  interface AslState {
52
+ Arguments?: Record<string, any>;
28
53
  Assign?: Record<string, any>;
29
54
  Branches?: AslDefinition[];
30
55
  Catch?: CatchBlock[];
@@ -34,8 +59,13 @@ interface AslState {
34
59
  Default?: string;
35
60
  End?: boolean;
36
61
  Error?: string;
62
+ ItemBatcher?: Record<string, any>;
37
63
  ItemProcessor?: AslDefinition;
64
+ ItemReader?: ItemIo;
65
+ ItemSelector?: Record<string, any>;
38
66
  Iterator?: AslDefinition;
67
+ Label?: string;
68
+ MaxConcurrency?: number | string;
39
69
  Next?: string;
40
70
  Output?: any;
41
71
  Parameters?: Record<string, any>;
@@ -43,16 +73,25 @@ interface AslState {
43
73
  Resource?: string;
44
74
  Result?: any;
45
75
  ResultPath?: string;
76
+ ResultWriter?: ItemIo;
46
77
  Retry?: RetryBlock[];
47
78
  Seconds?: number | string;
48
79
  SecondsPath?: string;
49
80
  Timestamp?: string;
50
81
  TimestampPath?: string;
82
+ ToleratedFailureCount?: number;
83
+ ToleratedFailurePercentage?: number;
51
84
  Type: StateType;
52
85
  }
53
86
  /** A complete Amazon States Language state machine definition. */
54
87
  interface AslDefinition {
55
88
  Comment?: string;
89
+ /**
90
+ * Map-specific, and only valid on an `ItemProcessor` sub-definition rather
91
+ * than on the Map state itself — this is where the ASL spec places it.
92
+ * Selects inline vs distributed execution.
93
+ */
94
+ ProcessorConfig?: ProcessorConfig;
56
95
  QueryLanguage?: "JSONata" | "JSONPath";
57
96
  StartAt: string;
58
97
  States: Record<string, AslState>;