sfn-diagram 1.2.0 → 1.4.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
@@ -4,83 +4,53 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/sfn-diagram.svg)](https://www.npmjs.com/package/sfn-diagram)
5
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)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![Live Playground](https://img.shields.io/badge/playground-live-brightgreen)](https://yusufaf.github.io/sfn-diagram/)
7
+ [![Docs](https://img.shields.io/badge/docs-sfn.yusufaf.dev-brightgreen)](https://sfn.yusufaf.dev)
8
8
 
9
- Generate beautiful, interactive diagrams from AWS Step Functions ASL (Amazon States Language) definitions. Supports dual output formats: D3.js-based SVG and Mermaid.js diagram code, plus PNG export.
9
+ Generate diagrams from AWS Step Functions ASL (Amazon States Language) definitions, CloudFormation/SAM/CDK templates, or live AWS state machines. Outputs SVG, Mermaid, standalone HTML, and PNG.
10
10
 
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.
11
+ **📖 [Documentation](https://sfn.yusufaf.dev)** · **▶ [Live playground](https://sfn.yusufaf.dev/playground/)** — paste any ASL definition and preview the diagram instantly, no install required.
12
12
 
13
13
  <picture>
14
- <source media="(prefers-color-scheme: dark)" srcset="examples/outputs/parallel-dark.svg">
15
- <img src="examples/outputs/parallel-light.svg" alt="Example diagram of a Parallel state Step Functions workflow generated by sfn-diagram" width="410">
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
16
  </picture>
17
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
-
37
- ## What it looks like
38
-
39
- Give it an ASL definition like this order-processing workflow ([`examples/order-processing.asl.json`](examples/order-processing.asl.json)):
40
-
41
- ```jsonc
42
- {
43
- "StartAt": "ValidateOrder",
44
- "States": {
45
- "ValidateOrder": { "Type": "Pass", "Next": "CheckStock" },
46
- "CheckStock": {
47
- "Type": "Choice",
48
- "Choices": [{ "Variable": "$.inStock", "BooleanEquals": true, "Next": "ChargePayment" }],
49
- "Default": "CancelOrder"
50
- },
51
- "ChargePayment": { "Type": "Task", "Resource": "arn:aws:lambda:...:charge-payment", "Next": "ShipOrder" },
52
- "ShipOrder": { "Type": "Task", "Resource": "arn:aws:lambda:...:ship-order", "Next": "OrderComplete" },
53
- "CancelOrder": { "Type": "Fail", "Error": "OutOfStock" },
54
- "OrderComplete": { "Type": "Succeed" }
55
- }
56
- }
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install sfn-diagram
22
+ ```
23
+
24
+ The core package pulls in **no browser engine** — SVG and Mermaid generation stay lightweight. PNG export relies on a headless browser, provided by the optional peer dependency `node-html-to-image`. Install it only if you use `sfn-diagram/png`:
25
+
26
+ ```bash
27
+ npm install sfn-diagram node-html-to-image
57
28
  ```
58
29
 
59
- …and `generateMermaid` turns it into a diagram GitHub renders inline (the same code also drives SVG and PNG output):
30
+ ## Quick start
60
31
 
61
- ```mermaid
62
- stateDiagram-v2
32
+ ```ts
33
+ import { generateSvg } from 'sfn-diagram';
63
34
 
64
- [*] --> ValidateOrder
65
- ValidateOrder --> CheckStock
66
- CheckStock --> ChargePayment: $.inStock == true
67
- CheckStock --> CancelOrder: Default
68
- ChargePayment --> ShipOrder
69
- ShipOrder --> OrderComplete
35
+ const { svg } = generateSvg({
36
+ aslDefinition: {
37
+ StartAt: 'Hello',
38
+ States: {
39
+ Hello: { Type: 'Pass', Next: 'World' },
40
+ World: { Type: 'Succeed' },
41
+ },
42
+ },
43
+ layout: 'TB',
44
+ theme: 'light',
45
+ });
46
+ ```
70
47
 
71
- CancelOrder --> [*]
72
- OrderComplete --> [*]
48
+ Mermaid instead of SVG? Swap in `generateMermaid`. Rendering a live state machine, a CDK template, or a PNG? See the [quick start guide](https://sfn.yusufaf.dev/quick-start/).
73
49
 
74
- classDef successState fill:#e8f5e8,stroke:#4caf50,stroke-width:3px
75
- classDef failState fill:#ffebee,stroke:#f44336,stroke-width:3px
76
- classDef choiceState fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
77
- classDef taskState fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
50
+ Prefer the command line:
78
51
 
79
- class CheckStock choiceState
80
- class ChargePayment taskState
81
- class ShipOrder taskState
82
- class CancelOrder failState
83
- class OrderComplete successState
52
+ ```bash
53
+ npx sfn-diagram diagram my-workflow.asl.json --format svg --out diagram.svg
84
54
  ```
85
55
 
86
56
  ## Why sfn-diagram
@@ -91,7 +61,7 @@ There's no shortage of ways to look at a state machine — this is where sfn-dia
91
61
  |---|---|---|---|
92
62
  | Input | ASL, CloudFormation/SAM/CDK templates, live AWS state machines | Deployed state machine only | Manual Mermaid you write by hand |
93
63
  | 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 |
64
+ | CI / PR integration | [GitHub Action](https://sfn.yusufaf.dev/ecosystem/github-action/) posts a diff overlay on every PR that touches ASL | None | None |
95
65
  | Execution overlays | Paints a real run's path/status/duration onto the diagram | Basic per-execution highlighting, console-only | Not applicable |
96
66
  | Runs where | CLI, Node, browser, edge runtimes | AWS Console only | Web app only |
97
67
  | Automation | Full programmatic API (`generateSvg`, `generateMermaid`, etc.) | None — UI only | None — UI only |
@@ -103,6 +73,7 @@ In short: reach for the AWS Console visualizer to eyeball a state machine you al
103
73
  - **Multiple Output Formats**: SVG (D3.js), Mermaid syntax, and PNG
104
74
  - **Automatic Layout**: Smart graph positioning using Dagre layout engine
105
75
  - **Full ASL Support**: All state types (Pass, Task, Choice, Wait, Succeed, Fail, Parallel, Map), both JSONPath and JSONata query languages, plus `Catch`/`Retry` rendering
76
+ - **Modern ASL**: Variables (`Assign`) shown per state, and Distributed Map rendered distinctly from an inline Map — including its `MaxConcurrency`, `ItemReader` source, and `ResultWriter` sink
106
77
  - **Visual Diffing**: Compare two definitions and highlight added / modified / removed states — drives the PR-preview GitHub Action
107
78
  - **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
108
79
  - **Customizable Themes**: AWS light/dark themes plus custom theme support
@@ -111,536 +82,9 @@ In short: reach for the AWS Console visualizer to eyeball a state machine you al
111
82
  - **AWS SDK Integration**: Direct integration with AWS Step Functions API
112
83
  - **Dual APIs**: Function-based and class-based interfaces
113
84
  - **Runs Anywhere**: SVG and Mermaid generation has zero platform dependencies — works in Node, the browser, and edge runtimes
85
+ - **Collapse Large Diagrams**: Replace Parallel/Map containers with placeholder nodes before layout so huge diagrams stay readable, with a live toggle in the interactive HTML viewer. See the [configuration guide](https://sfn.yusufaf.dev/guides/configuration/) for `collapse` and every other option.
114
86
 
115
- ## Installation
116
-
117
- ```bash
118
- npm install sfn-diagram
119
- ```
120
-
121
- The core package pulls in **no browser engine** — SVG and Mermaid generation stay lightweight. PNG export relies on a headless browser, provided by the optional peer dependency `node-html-to-image`. Install it only if you use `sfn-diagram/png`:
122
-
123
- ```bash
124
- npm install sfn-diagram node-html-to-image
125
- ```
126
-
127
- ## Runtime Support
128
-
129
- The core entry (`sfn-diagram`) builds SVG with a DOM-free string renderer, so
130
- `generateSvg`, `generateMermaid`, `generateDiagram`, and `generateFromAwsResponse`
131
- run in **Node, browsers, and edge runtimes** (Cloudflare Workers, Vercel Edge, Deno, Bun)
132
- with no DOM polyfill.
133
-
134
- PNG export (`sfn-diagram/png`) and the CLI are **Node-only** — they rely on a headless
135
- browser (`node-html-to-image`) and Node's filesystem respectively. Because `node-html-to-image`
136
- is an **optional peer dependency**, install it alongside `sfn-diagram` when you need PNG output;
137
- `exportPng` throws an actionable error if it is missing.
138
-
139
- **Node versions:** the package requires **Node >= 20**. PNG export additionally requires
140
- **Node >= 22.12.0**, the floor set by `node-html-to-image` v6 — SVG, Mermaid, and HTML
141
- output are unaffected and keep working on Node 20.
142
-
143
- ```ts
144
- // Works in Node, browser, and edge:
145
- import { generateSvg } from 'sfn-diagram';
146
-
147
- // Node-only:
148
- import { exportPng } from 'sfn-diagram/png';
149
- ```
150
-
151
- ## Command-line Usage
152
-
153
- The package ships a CLI for use without writing any JavaScript:
154
-
155
- ```bash
156
- npx sfn-diagram state.asl.json --format svg -o diagram.svg
157
- npx sfn-diagram state.asl.json --format mermaid > diagram.mmd
158
- cat state.asl.json | npx sfn-diagram - --format svg
159
- ```
160
-
161
- 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
-
163
- CloudFormation/SAM/CDK templates work as input too — see [Extracting ASL from a CDK/CloudFormation template](#extracting-asl-from-a-cdkcloudformation-template).
164
-
165
- > **`--format png` needs the optional `node-html-to-image` peer.** It is not installed by default. With `npx`, run `npx --package sfn-diagram --package node-html-to-image sfn-diagram …`; in a project, `npm install node-html-to-image`. Without it the CLI exits with an actionable error. Or skip the install entirely with the [Docker image](#docker), which bundles Chromium.
166
-
167
- ## Docker
168
-
169
- A prebuilt image is published to GitHub Container Registry with Chromium baked in, so PNG export works out of the box:
170
-
171
- ```bash
172
- docker run --rm -v "$PWD":/work ghcr.io/yusufaf/sfn-diagram:latest \
173
- /work/state.asl.json --format svg -o /work/diagram.svg
174
-
175
- docker run --rm -v "$PWD":/work ghcr.io/yusufaf/sfn-diagram:latest \
176
- /work/state.asl.json --format png -o /work/diagram.png
177
- ```
178
-
179
- Tags: `latest`, `<major>`, `<major>.<minor>`, `<major>.<minor>.<patch>`.
180
-
181
- ## Quick Start
182
-
183
- ### Function-based API
184
-
185
- ```typescript
186
- import { generateSvg } from 'sfn-diagram';
187
- import { writeFileSync } from 'fs';
188
-
189
- const asl = {
190
- StartAt: 'HelloWorld',
191
- States: {
192
- HelloWorld: {
193
- Type: 'Pass',
194
- Result: 'Hello, World!',
195
- End: true
196
- }
197
- }
198
- };
199
-
200
- const { svg, width, height } = generateSvg({
201
- aslDefinition: asl,
202
- theme: 'dark',
203
- layout: 'LR'
204
- });
205
-
206
- writeFileSync('diagram.svg', svg);
207
- console.log(`Generated ${width}x${height} diagram`);
208
- ```
209
-
210
- ### Class-based API
211
-
212
- ```typescript
213
- import { SfnDiagramGenerator } from 'sfn-diagram';
214
-
215
- const generator = new SfnDiagramGenerator({
216
- theme: 'dark',
217
- layout: 'TB',
218
- nodeWidth: 150,
219
- nodeHeight: 80,
220
- });
221
-
222
- const { svg } = generator.generateSvg({ aslDefinition: asl });
223
- const { code } = generator.generateMermaid({ aslDefinition: asl });
224
- ```
225
-
226
- ## API Reference
227
-
228
- ### generateSvg(params)
229
-
230
- Generate an SVG diagram using D3.js and Dagre layout.
231
-
232
- ```typescript
233
- import { generateSvg } from 'sfn-diagram';
234
-
235
- const result = generateSvg({
236
- aslDefinition: asl, // ASL definition (object or JSON string)
237
- theme: 'light', // 'light', 'dark', or CustomTheme object
238
- layout: 'TB', // 'TB', 'LR', 'RL', 'BT'
239
- nodeWidth: 120, // Node width in pixels
240
- nodeHeight: 60, // Node height in pixels
241
- rankSeparation: 50, // Vertical spacing between ranks
242
- nodeSeparation: 50, // Horizontal spacing between nodes
243
- padding: 20, // Diagram padding
244
- edgeStyle: 'curved', // 'curved', 'straight', 'orthogonal'
245
- showStateTypes: false, // Display state types on nodes
246
- includeComments: true, // Use state comments as labels
247
- customColors: {} // Override colors for specific states
248
- });
249
-
250
- // Returns SvgOutput: { svg: string, width: number, height: number, metadata: { edgeCount: number, nodeCount: number } }
251
- ```
252
-
253
- ### generateMermaid(params)
254
-
255
- Generate Mermaid.js diagram syntax.
256
-
257
- ```typescript
258
- import { generateMermaid } from 'sfn-diagram';
259
-
260
- const result = generateMermaid({
261
- aslDefinition: asl,
262
- includeComments: true
263
- });
264
-
265
- // Returns MermaidOutput: { code: string, metadata: { edgeCount: number, stateCount: number } }
266
- ```
267
-
268
- ### generateDiagram(params)
269
-
270
- Generate a diagram, choosing the output format via the `format` option (defaults to `'svg'`).
271
-
272
- ```typescript
273
- import { generateDiagram } from 'sfn-diagram';
274
-
275
- const svgResult = generateDiagram({
276
- aslDefinition: asl,
277
- theme: 'dark'
278
- }); // Returns SvgOutput
279
-
280
- const mermaidResult = generateDiagram({
281
- aslDefinition: asl,
282
- format: 'mermaid'
283
- }); // Returns MermaidOutput
284
- ```
285
-
286
- ### exportPng(params)
287
-
288
- Export diagram as PNG image. Node-only — imported from the `sfn-diagram/png` subpath.
289
-
290
- ```typescript
291
- import { exportPng } from 'sfn-diagram/png';
292
-
293
- const result = await exportPng({
294
- aslDefinition: asl,
295
- theme: 'light',
296
- pngQuality: 90, // 1–100 (default 90)
297
- backgroundColor: 'transparent'
298
- });
299
-
300
- // Returns PngOutput: { buffer: Buffer, width: number, height: number, metadata: { format: 'png' } }
301
- writeFileSync('diagram.png', result.buffer);
302
- ```
303
-
304
- ### generateFromAwsResponse(params)
305
-
306
- Generate diagram directly from AWS SDK response.
307
-
308
- ```typescript
309
- import { SFNClient, DescribeStateMachineCommand } from '@aws-sdk/client-sfn';
310
- import { generateFromAwsResponse } from 'sfn-diagram';
311
-
312
- const client = new SFNClient({ region: 'us-east-1' });
313
- const response = await client.send(
314
- new DescribeStateMachineCommand({
315
- stateMachineArn: 'arn:aws:states:us-east-1:123456789012:stateMachine:MyStateMachine'
316
- })
317
- );
318
-
319
- const { svg } = generateFromAwsResponse({
320
- response,
321
- theme: 'dark'
322
- });
323
- ```
324
-
325
- ### Extracting ASL from a CDK/CloudFormation template
326
-
327
- `cdk synth` emits a state machine whose `DefinitionString` is an `Fn::Join` full
328
- of intrinsics, not plain ASL. The `sfn-diagram/cfn` subpath flattens it:
329
-
330
- ```typescript
331
- import { extractAslFromTemplate } from 'sfn-diagram/cfn';
332
- import { generateMermaid } from 'sfn-diagram';
333
-
334
- const { aslDefinition, warnings } = extractAslFromTemplate({ template: cdkSynthJson });
335
- const { code } = generateMermaid({ aslDefinition });
336
- ```
337
-
338
- Both JSON and YAML templates are supported (CloudFormation short-form tags like
339
- `!Sub` / `!GetAtt` included), and `DefinitionSubstitutions` are applied. When a
340
- template contains more than one state machine, pass `resourceId` to pick one.
341
-
342
- CLI (JSON templates auto-detect; use `--resolve-cfn` for YAML):
343
-
344
- ```bash
345
- cdk synth > template.json
346
- npx sfn-diagram template.json --format mermaid
347
- npx sfn-diagram template.yaml --resolve-cfn --resource MyMachine --format svg -o out.svg
348
- ```
349
-
350
- Intrinsics that don't affect the flow become readable placeholders
351
- (`${AWS::Partition}`, `<Ref:LogicalId>`). External `DefinitionUri` definitions
352
- are not supported — pass a template with an inline definition.
353
-
354
- ### generateMermaidDiff(params) / generateDiff(params)
355
-
356
- Compare two ASL definitions and highlight what changed. `generateMermaidDiff` returns Mermaid code (renders inline on GitHub); `generateDiff` returns the same comparison as an SVG. Added states are green, modified yellow, removed red — and both return a `metadata` change summary (`added`, `modified`, `removed`, `unchanged`). The comparison is order-insensitive, so reordering a state's properties is **not** flagged as a change.
357
-
358
- ```typescript
359
- import { generateMermaidDiff } from 'sfn-diagram';
360
-
361
- const { code, metadata } = generateMermaidDiff({
362
- before: baseAsl, // old (base) ASL definition, object or JSON string
363
- after: headAsl, // new (head) ASL definition, object or JSON string
364
- });
365
-
366
- metadata; // { added: [...], modified: [...], removed: [...], unchanged: [...], stateCount, edgeCount }
367
- ```
368
-
369
- For the order-processing workflow above, adding a `FraudCheck` step, bumping `ChargePayment`'s resource, and dropping `CancelOrder` renders like this:
370
-
371
- ```mermaid
372
- stateDiagram-v2
373
-
374
- [*] --> ValidateOrder
375
- ValidateOrder --> CheckStock
376
- CheckStock --> ChargePayment: $.inStock == true
377
- CheckStock --> OrderComplete: Default
378
- ChargePayment --> FraudCheck
379
- FraudCheck --> ShipOrder
380
- ShipOrder --> OrderComplete
381
-
382
- OrderComplete --> [*]
383
- CancelOrder --> [*]
384
-
385
- classDef diffAdded fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
386
- classDef diffModified fill:#fff9c4,stroke:#f57f17,stroke-width:2px
387
- classDef diffRemoved fill:#ffcdd2,stroke:#c62828,stroke-width:2px
388
-
389
- class FraudCheck diffAdded
390
- class CheckStock diffModified
391
- class ChargePayment diffModified
392
- class CancelOrder diffRemoved
393
- ```
394
-
395
- > This is exactly what the [GitHub Action](#github-action) posts on pull requests that touch ASL files.
396
-
397
- ### generateExecution(params) / generateMermaidExecution(params)
398
-
399
- Overlay a **real execution** onto the diagram. Where the diff shows how a definition
400
- _changed_, the execution overlay shows what one _run_ actually did: which path it took,
401
- which states succeeded (green), failed (red), were caught and recovered (orange), or were
402
- never reached (grey) — plus per-state duration and retry counts.
403
-
404
- Pass the ASL definition plus the execution's history. `history` accepts the events array
405
- from `GetExecutionHistory`, the raw command output, or a JSON string of either — so the
406
- core stays credential-free and runs in the browser.
407
-
408
- ```typescript
409
- import { generateExecution, generateMermaidExecution } from 'sfn-diagram';
410
-
411
- // history: events from `aws stepfunctions get-execution-history`, or the SDK response
412
- const { svg, metadata } = generateExecution({ aslDefinition: asl, history });
413
-
414
- metadata;
415
- // { succeeded: [...], failed: [...], caught: [...], notReached: [...], running: [...],
416
- // takenEdgeCount, executionStatus, nodeCount, edgeCount }
417
-
418
- // Mermaid variant (states coloured + duration/retry annotations in labels):
419
- const { code } = generateMermaidExecution({ aslDefinition: asl, history });
420
- ```
421
-
422
- > **Note:** Mermaid `stateDiagram-v2` cannot style individual transitions, so taken-path
423
- > emphasis in Mermaid is expressed through node colours and label annotations. The SVG
424
- > overlay additionally dims the transitions the run did not take.
425
-
426
- Need just the data (no rendering)? `parseExecutionHistory({ events })` returns the pure
427
- `ExecutionOverlay` model — per-state status, attempts, duration, and the taken edges — for
428
- building your own UI.
429
-
430
- ```typescript
431
- import { parseExecutionHistory } from 'sfn-diagram';
432
-
433
- const overlay = parseExecutionHistory({ events });
434
- overlay.states['ProcessOrder']; // { status: 'succeeded', attempts: 1, durationMs: 1250 }
435
- overlay.takenEdges; // [{ from: 'ValidateOrder', to: 'ProcessOrder' }, ...]
436
- ```
437
-
438
- Fetching history from AWS (Node) — use the `sfn-diagram/aws` helper, which paginates `GetExecutionHistory` for you:
439
-
440
- ```typescript
441
- import { SFNClient } from '@aws-sdk/client-sfn';
442
- import { fetchExecutionHistory } from 'sfn-diagram/aws';
443
-
444
- const client = new SFNClient({ region: 'us-east-1' });
445
- const events = await fetchExecutionHistory({ client, executionArn });
446
-
447
- const { svg } = generateExecution({ aslDefinition: asl, history: events });
448
- ```
449
-
450
- `sfn-diagram/aws` is a Node-only subpath. `@aws-sdk/client-sfn` is an **optional peer dependency** — install it yourself (`npm i @aws-sdk/client-sfn`); the core `sfn-diagram` package stays dependency-free and browser-safe.
451
-
452
- <details>
453
- <summary>Prefer to paginate yourself? The manual loop:</summary>
454
-
455
- ```typescript
456
- import { SFNClient, GetExecutionHistoryCommand } from '@aws-sdk/client-sfn';
457
-
458
- const client = new SFNClient({ region: 'us-east-1' });
459
- const events = [];
460
- let nextToken;
461
- do {
462
- const page = await client.send(
463
- new GetExecutionHistoryCommand({ executionArn, nextToken, maxResults: 1000 }),
464
- );
465
- events.push(...(page.events ?? []));
466
- nextToken = page.nextToken;
467
- } while (nextToken);
468
-
469
- const { svg } = generateExecution({ aslDefinition: asl, history: events });
470
- ```
471
- </details>
472
-
473
- ### SfnDiagramGenerator Class
474
-
475
- Reusable generator that holds diagram options once and applies them to every call. Configure options via the constructor or the fluent `setOptions()` method; pass the `aslDefinition` per generation.
476
-
477
- ```typescript
478
- import { SfnDiagramGenerator } from 'sfn-diagram';
479
-
480
- const generator = new SfnDiagramGenerator({
481
- theme: 'dark',
482
- layout: 'LR',
483
- nodeWidth: 150,
484
- nodeHeight: 80,
485
- rankSeparation: 60,
486
- nodeSeparation: 60,
487
- edgeStyle: 'curved',
488
- padding: 30,
489
- });
490
-
491
- // Update options later (chainable)
492
- generator.setOptions({ theme: 'light' });
493
-
494
- // Generate outputs (aslDefinition passed per call)
495
- const svgResult = generator.generateSvg({ aslDefinition: asl });
496
- const mermaidResult = generator.generateMermaid({ aslDefinition: asl });
497
- ```
498
-
499
- > PNG export is a standalone function from `sfn-diagram/png` (`exportPng`), not a method on this class.
500
-
501
- ## Configuration Options
502
-
503
- ### Themes
504
-
505
- **Built-in themes:**
506
- - `'light'` - AWS light theme (default)
507
- - `'dark'` - AWS dark theme
508
-
509
- **Custom theme:**
510
-
511
- A `CustomTheme` sets the background, per-state-type fill/stroke colours, edge colours, and typography. Pass it anywhere a theme is accepted.
512
-
513
- ```typescript
514
- import type { CustomTheme } from 'sfn-diagram';
515
-
516
- const customTheme: CustomTheme = {
517
- background: '#ffffff',
518
- edgeColors: {
519
- choice: '#7b1fa2',
520
- default: '#607d8b',
521
- error: '#f44336',
522
- normal: '#232f3e',
523
- retry: '#f9a825', // optional; falls back to `error` when omitted
524
- },
525
- fontFamily: 'Arial, sans-serif',
526
- fontSize: 14,
527
- nodeColors: {
528
- Pass: { fill: '#e8f5e9', stroke: '#4caf50' },
529
- Task: { fill: '#e3f2fd', stroke: '#2196f3' },
530
- Choice: { fill: '#fff3e0', stroke: '#ff9800' },
531
- Wait: { fill: '#f3e5f5', stroke: '#9c27b0' },
532
- Succeed: { fill: '#e8f5e9', stroke: '#4caf50' },
533
- Fail: { fill: '#ffebee', stroke: '#f44336' },
534
- Parallel: { fill: '#e0f7fa', stroke: '#00bcd4' },
535
- Map: { fill: '#e8eaf6', stroke: '#3f51b5' },
536
- },
537
- textColor: '#232f3e',
538
- };
539
-
540
- generateSvg({ aslDefinition: asl, theme: customTheme });
541
- ```
542
-
543
- ### Layouts
544
-
545
- - `'TB'` - Top to Bottom (default)
546
- - `'LR'` - Left to Right
547
- - `'RL'` - Right to Left
548
- - `'BT'` - Bottom to Top
549
-
550
- ### Edge Styles
551
-
552
- - `'curved'` - Smooth curved paths (default)
553
- - `'straight'` - Direct straight lines
554
- - `'orthogonal'` - Right-angled paths
555
-
556
- ### Large diagrams
557
-
558
- Big, branchy state machines are hard to read as a static image. A few options help:
559
-
560
- - **`--format html`** (or `generateHtml()`) — a self-contained interactive viewer
561
- (drag to pan, wheel to zoom, fit/reset toolbar). No external dependencies, opens
562
- offline straight from `file://`.
563
- ```bash
564
- npx sfn-diagram state.asl.json --format html -o diagram.html
565
- ```
566
- > **Known limitation:** if you also pass `showIcons: true`, the embedded SVG
567
- > references AWS service icons hosted on a jsDelivr CDN (see
568
- > [AWS Service Icons](#aws-service-icons) below), so the HTML is no longer fully
569
- > offline — icons won't load without network access.
570
- - **`--hide-catch`** (or `catchHandling: 'hide'`) — drop per-state error-handler
571
- (`Catch`) branches so the happy path stands out. A handler that's also reachable
572
- via the happy path is kept.
573
- ```bash
574
- npx sfn-diagram state.asl.json --hide-catch --format svg -o diagram.svg
575
- ```
576
- - **`--layout LR`** (or `layout: 'LR'`) — the default `TB` layout makes catch-heavy
577
- or deeply branching machines extremely tall; `LR` reads better for wide graphs.
578
-
579
- ### AWS Service Icons
580
-
581
- Display AWS service icons on Task state nodes to improve diagram readability and quickly identify which AWS services are being used.
582
-
583
- **Basic Usage:**
584
-
585
- ```typescript
586
- import { generateSvg } from 'sfn-diagram';
587
-
588
- const { svg } = generateSvg({
589
- aslDefinition: asl,
590
- showIcons: true, // Enable icons
591
- iconPosition: 'left', // Icon placement (default)
592
- iconSize: 24 // Icon dimensions in pixels (default)
593
- });
594
- ```
595
-
596
- **Supported Services (30+):**
597
-
598
- Lambda, ECS, Fargate, EC2, Batch, DynamoDB, RDS, Aurora, Neptune, S3, EFS, FSx, SQS, SNS, EventBridge, Kinesis, Glue, Athena, EMR, Redshift, SageMaker, Bedrock, Comprehend, Rekognition, Step Functions, API Gateway, AppSync, CloudWatch, CloudFormation, Systems Manager, Secrets Manager, KMS, and more.
599
-
600
- **Icon Positioning:**
601
-
602
- - `'left'` - Icon to the left of label (default, matches AWS Console style)
603
- - `'top'` - Icon above label
604
- - `'right'` - Icon to the right of label
605
-
606
- **Custom Icon Resolver:**
607
-
608
- Provide your own icon URLs for services:
609
-
610
- ```typescript
611
- const { svg } = generateSvg({
612
- aslDefinition: asl,
613
- showIcons: true,
614
- iconResolver: (service) => {
615
- if (service === 'lambda') {
616
- return 'https://my-cdn.com/lambda-icon.svg';
617
- }
618
- return null; // Fall back to default
619
- }
620
- });
621
- ```
622
-
623
- **Recommended Node Dimensions:**
624
-
625
- For optimal icon visibility, use wider nodes:
626
-
627
- ```typescript
628
- const { svg } = generateSvg({
629
- aslDefinition: asl,
630
- showIcons: true,
631
- nodeWidth: 150, // Default: 120
632
- nodeHeight: 70 // Default: 60
633
- });
634
- ```
635
-
636
- **Important Notes:**
637
- - Icons are only displayed on Task states (states with AWS service integrations)
638
- - Icons are sourced from [aws-icons](https://www.npmjs.com/package/aws-icons) via jsDelivr CDN
639
- - **PNG export limitation**: External CDN images may not render in PNG output due to headless browser limitations. **Use SVG output for diagrams with icons.**
640
- - Unsupported services gracefully fall back to text-only labels
641
- - Icons are opt-in via `showIcons: true` (disabled by default)
642
-
643
- ## Supported State Types
87
+ ## Supported state types
644
88
 
645
89
  All AWS Step Functions state types are fully supported:
646
90
 
@@ -655,360 +99,44 @@ All AWS Step Functions state types are fully supported:
655
99
  | **Parallel** | Rectangle | Executes branches in parallel |
656
100
  | **Map** | Rectangle | Iterates over array items (legacy `Iterator` and modern `ItemProcessor`/Distributed Map) |
657
101
 
658
- ### Error handling & retries
102
+ `Catch` blocks render as dashed error edges, `Retry` policies as labelled self-loops, and both JSONPath and JSONata definitions are supported. Details in the [state types guide](https://sfn.yusufaf.dev/guides/state-types/).
659
103
 
660
- - **`Catch`** blocks render as dashed error edges to their handler states.
661
- - **`Retry`** policies render as a labelled self-loop on the state (e.g. `↻ States.Timeout (4x); States.ALL (2x)`) — a self-transition in Mermaid output.
104
+ ## Documentation
662
105
 
663
- ### Query languages
106
+ Full documentation lives at **[sfn.yusufaf.dev](https://sfn.yusufaf.dev)**.
664
107
 
665
- Both JSONPath and JSONata (`QueryLanguage: "JSONata"`) definitions are supported. Choice branch labels are derived from JSONPath comparison operators (`$.score >= 90`, `And`/`Or`/`Not`, `Is*` checks) or from JSONata `Condition` expressions, whichever the state uses.
108
+ - [Introduction](https://sfn.yusufaf.dev/introduction/) — what it does and how it compares
109
+ - [Quick start](https://sfn.yusufaf.dev/quick-start/) — your first diagram
110
+ - [Command line](https://sfn.yusufaf.dev/guides/cli/) — the `sfn-diagram` CLI, diffs, and execution overlays
111
+ - [Configuration](https://sfn.yusufaf.dev/guides/configuration/) — themes, layouts, edge styles, service icons
112
+ - [Examples](https://sfn.yusufaf.dev/guides/examples/) — complete worked examples
113
+ - [Use with your framework](https://sfn.yusufaf.dev/guides/frameworks/) — Next.js, Remix, SvelteKit, Astro
114
+ - [API reference](https://sfn.yusufaf.dev/reference/) — generated from source, always current
666
115
 
667
- ## Examples
668
-
669
- ### Complex State Machine
670
-
671
- ```typescript
672
- import { generateSvg } from 'sfn-diagram';
673
-
674
- const complexAsl = {
675
- Comment: 'Order processing workflow',
676
- StartAt: 'ValidateOrder',
677
- States: {
678
- ValidateOrder: {
679
- Type: 'Task',
680
- Resource: 'arn:aws:lambda:us-east-1:123456789012:function:ValidateOrder',
681
- Next: 'CheckInventory',
682
- Catch: [{
683
- ErrorEquals: ['ValidationError'],
684
- Next: 'OrderFailed'
685
- }]
686
- },
687
- CheckInventory: {
688
- Type: 'Task',
689
- Resource: 'arn:aws:lambda:us-east-1:123456789012:function:CheckInventory',
690
- Next: 'IsInStock'
691
- },
692
- IsInStock: {
693
- Type: 'Choice',
694
- Choices: [{
695
- Variable: '$.inStock',
696
- BooleanEquals: true,
697
- Next: 'ProcessPayment'
698
- }],
699
- Default: 'OutOfStock'
700
- },
701
- ProcessPayment: {
702
- Type: 'Task',
703
- Resource: 'arn:aws:states:::dynamodb:putItem',
704
- Next: 'OrderSucceeded'
705
- },
706
- OutOfStock: {
707
- Type: 'Fail',
708
- Error: 'OutOfStockError',
709
- Cause: 'Item not available'
710
- },
711
- OrderFailed: {
712
- Type: 'Fail',
713
- Error: 'OrderValidationError'
714
- },
715
- OrderSucceeded: {
716
- Type: 'Succeed'
717
- }
718
- }
719
- };
720
-
721
- const { svg } = generateSvg({
722
- aslDefinition: complexAsl,
723
- theme: 'dark',
724
- layout: 'TB',
725
- edgeStyle: 'curved',
726
- nodeWidth: 150,
727
- nodeHeight: 70
728
- });
729
- ```
730
-
731
- ### Parallel State Machine
732
-
733
- ```typescript
734
- const parallelAsl = {
735
- StartAt: 'ProcessInParallel',
736
- States: {
737
- ProcessInParallel: {
738
- Type: 'Parallel',
739
- Branches: [
740
- {
741
- StartAt: 'Branch1',
742
- States: {
743
- Branch1: { Type: 'Pass', Result: 'Branch 1', End: true }
744
- }
745
- },
746
- {
747
- StartAt: 'Branch2',
748
- States: {
749
- Branch2: { Type: 'Pass', Result: 'Branch 2', End: true }
750
- }
751
- }
752
- ],
753
- Next: 'FinalState'
754
- },
755
- FinalState: {
756
- Type: 'Succeed'
757
- }
758
- }
759
- };
760
-
761
- const { svg } = generateSvg({ aslDefinition: parallelAsl });
762
- ```
763
-
764
- ### Export Multiple Formats
765
-
766
- ```typescript
767
- import { generateSvg, generateMermaid } from 'sfn-diagram';
768
- import { exportPng } from 'sfn-diagram/png';
769
- import { writeFileSync } from 'fs';
770
-
771
- // Generate SVG and Mermaid
772
- const { svg } = generateSvg({ aslDefinition: asl });
773
- const { code } = generateMermaid({ aslDefinition: asl });
774
- writeFileSync('diagram.svg', svg);
775
- writeFileSync('diagram.mmd', code);
776
-
777
- // Generate PNG (Node-only)
778
- const { buffer } = await exportPng({
779
- aslDefinition: asl,
780
- pngQuality: 90,
781
- backgroundColor: 'transparent'
782
- });
783
- writeFileSync('diagram.png', buffer);
784
- ```
785
-
786
- ## TypeScript Support
787
-
788
- Full TypeScript definitions included:
789
-
790
- ```typescript
791
- import type {
792
- AslDefinition,
793
- DiagramOptions,
794
- SvgOutput,
795
- MermaidOutput,
796
- CustomTheme,
797
- StateType
798
- } from 'sfn-diagram';
799
-
800
- // PNG types live on the Node-only subpath
801
- import type { PngOutput, ExportPngParams } from 'sfn-diagram/png';
802
- ```
803
-
804
- ## Use with your framework
805
-
806
- `generateSvg()` returns an SVG **string** and the core has no DOM dependency, so any
807
- framework can render a diagram by injecting that string. There is nothing React-specific
808
- in the core — [`sfn-diagram-react`](packages/sfn-diagram-react/) exists for convenience,
809
- not necessity.
810
-
811
- Each snippet below is the whole integration. See [API Reference](#api-reference) for the
812
- full option set (`theme`, `layout`, `showIcons`, …) and [`generateMermaid`](#generatemermaidparams)
813
- if you would rather emit Mermaid than SVG.
814
-
815
- **Svelte 5**
816
-
817
- ```svelte
818
- <script lang="ts">
819
- import { generateSvg } from 'sfn-diagram';
820
-
821
- let { definition } = $props();
822
- const svg = $derived(generateSvg({ aslDefinition: definition }).svg);
823
- </script>
824
-
825
- {@html svg}
826
- ```
827
-
828
- **Vue 3**
829
-
830
- ```vue
831
- <script setup lang="ts">
832
- import { computed } from 'vue';
833
- import { generateSvg } from 'sfn-diagram';
834
- import type { AslDefinition } from 'sfn-diagram';
835
-
836
- const props = defineProps<{ definition: AslDefinition | string }>();
837
- const svg = computed(() => generateSvg({ aslDefinition: props.definition }).svg);
838
- </script>
839
-
840
- <template>
841
- <div v-html="svg" />
842
- </template>
843
- ```
844
-
845
- **Solid**
846
-
847
- ```tsx
848
- import { createMemo } from 'solid-js';
849
- import { generateSvg } from 'sfn-diagram';
850
- import type { AslDefinition } from 'sfn-diagram';
851
-
852
- export function SfnDiagram(props: { definition: AslDefinition | string }) {
853
- const svg = createMemo(() => generateSvg({ aslDefinition: props.definition }).svg);
854
- return <div innerHTML={svg()} />;
855
- }
856
- ```
857
-
858
- **Angular**
859
-
860
- `[innerHTML]` strips SVG unless the value is marked trusted, so the sanitizer bypass is required.
861
-
862
- ```ts
863
- import { Component, computed, inject, input } from '@angular/core';
864
- import { DomSanitizer } from '@angular/platform-browser';
865
- import { generateSvg } from 'sfn-diagram';
866
- import type { AslDefinition } from 'sfn-diagram';
867
-
868
- @Component({
869
- selector: 'sfn-diagram',
870
- template: `<div [innerHTML]="svg()"></div>`,
871
- })
872
- export class SfnDiagramComponent {
873
- readonly definition = input.required<AslDefinition | string>();
874
- private readonly sanitizer = inject(DomSanitizer);
875
-
876
- readonly svg = computed(() =>
877
- this.sanitizer.bypassSecurityTrustHtml(
878
- generateSvg({ aslDefinition: this.definition() }).svg
879
- )
880
- );
881
- }
882
- ```
883
-
884
- **Astro**
885
-
886
- Astro runs the frontmatter at build time, so the diagram ships as static markup with
887
- **zero client-side JavaScript** — the DOM-free core at its best. (Astro also hosts React
888
- islands, so `sfn-diagram-react` works there too if you need interactivity.)
889
-
890
- ```astro
891
- ---
892
- import { generateSvg } from 'sfn-diagram';
893
- // `?raw` gives the file as a string, which `aslDefinition` accepts directly
894
- import definition from '../workflows/order-processing.asl.json?raw';
895
-
896
- const { svg } = generateSvg({ aslDefinition: definition, theme: 'light' });
897
- ---
898
-
899
- <Fragment set:html={svg} />
900
- ```
901
-
902
- **Vanilla JS / htmx / anything else**
903
-
904
- ```js
905
- import { generateSvg } from 'sfn-diagram';
906
-
907
- const response = await fetch('/workflows/order-processing.asl.json');
908
- const { svg } = generateSvg({ aslDefinition: await response.text() });
909
- document.querySelector('#diagram').innerHTML = svg;
910
- ```
911
-
912
- > **On the "unsafe HTML" APIs.** Every snippet above uses its framework's raw-HTML escape
913
- > hatch (`{@html}`, `v-html`, `innerHTML`, `dangerouslySetInnerHTML`). The SVG builder
914
- > HTML-escapes every attribute and text value it emits, following the HTML serialization
915
- > algorithm, so state names, comments, and choice conditions taken from your ASL are
916
- > escaped before they reach the string.
116
+ Working with an AI coding agent? The docs are published as [llms.txt](https://sfn.yusufaf.dev/llms.txt), and every page is available as raw Markdown by appending `.md` to its URL.
917
117
 
918
118
  ## Ecosystem
919
119
 
920
- ### Playground
921
-
922
- An interactive browser-based editor for exploring ASL definitions and previewing diagrams in real time. **[Open the live playground →](https://yusufaf.github.io/sfn-diagram/)** or run it locally from the [`playground/`](playground/) directory.
923
-
924
- ```bash
925
- cd playground
926
- pnpm install
927
- pnpm dev
928
- ```
929
-
930
- Paste any ASL JSON, switch themes, and see the SVG diagram update instantly — no install required beyond the dev server. Switch **Mode** to _Execution overlay_ to paste an execution history alongside the definition and watch the run's path light up.
931
-
932
- ### VS Code Extension
933
-
934
- Preview Step Functions diagrams directly inside VS Code. Located in [`packages/vscode-sfn-diagram/`](packages/vscode-sfn-diagram/).
935
-
936
- **Install from source:**
937
- ```bash
938
- cd packages/vscode-sfn-diagram
939
- pnpm install
940
- pnpm package # produces vscode-sfn-diagram-*.vsix
941
- code --install-extension vscode-sfn-diagram-*.vsix
942
- ```
943
-
944
- **Usage:** Open any `.json` or `.asl` file and run **Step Functions: Preview Step Functions Diagram** from the command palette, or click the diagram icon in the editor title bar.
945
-
946
- ### React Component
947
-
948
- 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.
949
-
950
- ```bash
951
- npm install sfn-diagram-react sfn-diagram react
952
- ```
953
-
954
- ```tsx
955
- import { SfnDiagram } from 'sfn-diagram-react';
956
-
957
- <SfnDiagram
958
- definition={asl} // ASL object or JSON string
959
- format="svg" // 'svg' (default) | 'mermaid'
960
- theme="dark" // 'light' | 'dark' | CustomTheme
961
- layout="LR" // 'TB' | 'LR' | 'RL' | 'BT'
962
- history={history} // optional: renders an execution overlay
963
- onError={(err) => console.error(err)}
964
- />
965
- ```
120
+ | | |
121
+ |---|---|
122
+ | **[Playground](https://sfn.yusufaf.dev/playground/)** | Interactive browser editor for ASL definitions and execution overlays |
123
+ | **[VS Code extension](https://sfn.yusufaf.dev/ecosystem/vscode/)** | Preview diagrams in VS Code, Cursor, Windsurf, VSCodium, and Theia |
124
+ | **[`<sfn-diagram>` web component](https://sfn.yusufaf.dev/ecosystem/web-component/)** | Framework-agnostic custom element — Vue, Svelte, Solid, Angular, Astro, Hono, vanilla HTML |
125
+ | **[React component](https://sfn.yusufaf.dev/ecosystem/react/)** | `sfn-diagram-react` — render diagrams in any React renderer |
126
+ | **[GitHub Action](https://sfn.yusufaf.dev/ecosystem/github-action/)** | Comment a diagram and diff on every PR that touches an ASL file |
966
127
 
967
- ### GitHub Action
128
+ ## Runtime support
968
129
 
969
- Comment a Step Functions diagram on every pull request that touches an ASL file. For **changed** files the comment highlights the diff — added states green, modified yellow, removed red — plus a summary table of what changed; **new** and **deleted** files get a plain diagram. Everything is Mermaid, so it renders inline in the PR with no image hosting. The comment is upserted (updated in place) on each push.
970
-
971
- Available on the [GitHub Marketplace](https://github.com/marketplace/actions/step-functions-diagram-preview) as [`yusufaf/sfn-diagram-action`](https://github.com/yusufaf/sfn-diagram-action) — pin the moving major tag `@v1`. (Source lives here in [`packages/github-action-sfn-diagram/`](packages/github-action-sfn-diagram/); the Marketplace repo is generated from it.)
972
-
973
- ```yaml
974
- # .github/workflows/sfn-preview.yml
975
- name: Step Functions Preview
976
- on:
977
- pull_request:
978
- paths: ['**/*.asl.json', '**/*.asl']
979
-
980
- permissions:
981
- contents: read
982
- pull-requests: write # required to post the comment
983
-
984
- jobs:
985
- preview:
986
- runs-on: ubuntu-latest
987
- steps:
988
- - uses: actions/checkout@v5
989
- with: { fetch-depth: 0 } # needed to diff base vs head
990
- - uses: yusufaf/sfn-diagram-action@v1
991
- ```
992
-
993
- Inputs: `github-token` (defaults to `${{ github.token }}`), `asl-glob` (comma-separated globs, default `**/*.asl.json,**/*.asl`), `comment-tag` (marker used to find/update the comment, default `sfn-diagram-preview`).
994
-
995
- Optionally overlay a real run: set `execution-mode` (`latest` or `latest-failed`) and `state-machine-arn` to append the most recent (or most recent failed) execution as a Mermaid overlay beneath the diff. This is opt-in (`off` by default), needs AWS credentials (`states:ListExecutions` + `states:GetExecutionHistory`), and applies when exactly one ASL file changed — see the [action README](packages/github-action-sfn-diagram/README.marketplace.md) for a full workflow.
130
+ Core (`sfn-diagram`) is platform-agnostic — it runs in Node, the browser, and edge runtimes. Only `sfn-diagram/png` and the CLI are Node-only. See [runtime support](https://sfn.yusufaf.dev/guides/runtimes/).
996
131
 
997
132
  ## Contributing
998
133
 
999
- Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
134
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
1000
135
 
1001
136
  ## License
1002
137
 
1003
- MIT License - see [LICENSE](LICENSE) for details.
138
+ MIT — see [LICENSE](LICENSE).
1004
139
 
1005
140
  ## Author
1006
141
 
1007
142
  Yusuf Afzal
1008
-
1009
- ## Links
1010
-
1011
- - [GitHub Repository](https://github.com/yusufaf/sfn-diagram)
1012
- - [npm Package](https://www.npmjs.com/package/sfn-diagram)
1013
- - [Issue Tracker](https://github.com/yusufaf/sfn-diagram/issues)
1014
- - [AWS Step Functions Documentation](https://docs.aws.amazon.com/step-functions/)