sfn-diagram 0.6.0 → 0.7.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
@@ -8,11 +8,62 @@ Generate beautiful, interactive diagrams from AWS Step Functions ASL (Amazon Sta
8
8
 
9
9
  **▶ [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
10
 
11
+ ## What it looks like
12
+
13
+ Give it an ASL definition like this order-processing workflow ([`examples/order-processing.asl.json`](examples/order-processing.asl.json)):
14
+
15
+ ```jsonc
16
+ {
17
+ "StartAt": "ValidateOrder",
18
+ "States": {
19
+ "ValidateOrder": { "Type": "Pass", "Next": "CheckStock" },
20
+ "CheckStock": {
21
+ "Type": "Choice",
22
+ "Choices": [{ "Variable": "$.inStock", "BooleanEquals": true, "Next": "ChargePayment" }],
23
+ "Default": "CancelOrder"
24
+ },
25
+ "ChargePayment": { "Type": "Task", "Resource": "arn:aws:lambda:...:charge-payment", "Next": "ShipOrder" },
26
+ "ShipOrder": { "Type": "Task", "Resource": "arn:aws:lambda:...:ship-order", "Next": "OrderComplete" },
27
+ "CancelOrder": { "Type": "Fail", "Error": "OutOfStock" },
28
+ "OrderComplete": { "Type": "Succeed" }
29
+ }
30
+ }
31
+ ```
32
+
33
+ …and `generateMermaid` turns it into a diagram GitHub renders inline (the same code also drives SVG and PNG output):
34
+
35
+ ```mermaid
36
+ stateDiagram-v2
37
+
38
+ [*] --> ValidateOrder
39
+ ValidateOrder --> CheckStock
40
+ CheckStock --> ChargePayment: $.inStock == true
41
+ CheckStock --> CancelOrder: Default
42
+ ChargePayment --> ShipOrder
43
+ ShipOrder --> OrderComplete
44
+
45
+ CancelOrder --> [*]
46
+ OrderComplete --> [*]
47
+
48
+ classDef successState fill:#e8f5e8,stroke:#4caf50,stroke-width:3px
49
+ classDef failState fill:#ffebee,stroke:#f44336,stroke-width:3px
50
+ classDef choiceState fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
51
+ classDef taskState fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
52
+
53
+ class CheckStock choiceState
54
+ class ChargePayment taskState
55
+ class ShipOrder taskState
56
+ class CancelOrder failState
57
+ class OrderComplete successState
58
+ ```
59
+
11
60
  ## Features
12
61
 
13
62
  - **Multiple Output Formats**: SVG (D3.js), Mermaid syntax, and PNG
14
63
  - **Automatic Layout**: Smart graph positioning using Dagre layout engine
15
- - **Full ASL Support**: All state types (Pass, Task, Choice, Wait, Succeed, Fail, Parallel, Map), both JSONPath and JSONata query languages
64
+ - **Full ASL Support**: All state types (Pass, Task, Choice, Wait, Succeed, Fail, Parallel, Map), both JSONPath and JSONata query languages, plus `Catch`/`Retry` rendering
65
+ - **Visual Diffing**: Compare two definitions and highlight added / modified / removed states — drives the PR-preview GitHub Action
66
+ - **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
16
67
  - **Customizable Themes**: AWS light/dark themes plus custom theme support
17
68
  - **Flexible Layouts**: Top-bottom, left-right, right-left, bottom-top
18
69
  - **Type-Safe**: Full TypeScript support with comprehensive type definitions
@@ -222,6 +273,125 @@ const { svg } = generateFromAwsResponse({
222
273
  });
223
274
  ```
224
275
 
276
+ ### generateMermaidDiff(params) / generateDiff(params)
277
+
278
+ 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.
279
+
280
+ ```typescript
281
+ import { generateMermaidDiff } from 'sfn-diagram';
282
+
283
+ const { code, metadata } = generateMermaidDiff({
284
+ before: baseAsl, // old (base) ASL definition, object or JSON string
285
+ after: headAsl, // new (head) ASL definition, object or JSON string
286
+ });
287
+
288
+ metadata; // { added: [...], modified: [...], removed: [...], unchanged: [...], stateCount, edgeCount }
289
+ ```
290
+
291
+ For the order-processing workflow above, adding a `FraudCheck` step, bumping `ChargePayment`'s resource, and dropping `CancelOrder` renders like this:
292
+
293
+ ```mermaid
294
+ stateDiagram-v2
295
+
296
+ [*] --> ValidateOrder
297
+ ValidateOrder --> CheckStock
298
+ CheckStock --> ChargePayment: $.inStock == true
299
+ CheckStock --> OrderComplete: Default
300
+ ChargePayment --> FraudCheck
301
+ FraudCheck --> ShipOrder
302
+ ShipOrder --> OrderComplete
303
+
304
+ OrderComplete --> [*]
305
+ CancelOrder --> [*]
306
+
307
+ classDef diffAdded fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
308
+ classDef diffModified fill:#fff9c4,stroke:#f57f17,stroke-width:2px
309
+ classDef diffRemoved fill:#ffcdd2,stroke:#c62828,stroke-width:2px
310
+
311
+ class FraudCheck diffAdded
312
+ class CheckStock diffModified
313
+ class ChargePayment diffModified
314
+ class CancelOrder diffRemoved
315
+ ```
316
+
317
+ > This is exactly what the [GitHub Action](#github-action) posts on pull requests that touch ASL files.
318
+
319
+ ### generateExecution(params) / generateMermaidExecution(params)
320
+
321
+ Overlay a **real execution** onto the diagram. Where the diff shows how a definition
322
+ _changed_, the execution overlay shows what one _run_ actually did: which path it took,
323
+ which states succeeded (green), failed (red), were caught and recovered (orange), or were
324
+ never reached (grey) — plus per-state duration and retry counts.
325
+
326
+ Pass the ASL definition plus the execution's history. `history` accepts the events array
327
+ from `GetExecutionHistory`, the raw command output, or a JSON string of either — so the
328
+ core stays credential-free and runs in the browser.
329
+
330
+ ```typescript
331
+ import { generateExecution, generateMermaidExecution } from 'sfn-diagram';
332
+
333
+ // history: events from `aws stepfunctions get-execution-history`, or the SDK response
334
+ const { svg, metadata } = generateExecution({ aslDefinition: asl, history });
335
+
336
+ metadata;
337
+ // { succeeded: [...], failed: [...], caught: [...], notReached: [...], running: [...],
338
+ // takenEdgeCount, executionStatus, nodeCount, edgeCount }
339
+
340
+ // Mermaid variant (states coloured + duration/retry annotations in labels):
341
+ const { code } = generateMermaidExecution({ aslDefinition: asl, history });
342
+ ```
343
+
344
+ > **Note:** Mermaid `stateDiagram-v2` cannot style individual transitions, so taken-path
345
+ > emphasis in Mermaid is expressed through node colours and label annotations. The SVG
346
+ > overlay additionally dims the transitions the run did not take.
347
+
348
+ Need just the data (no rendering)? `parseExecutionHistory({ events })` returns the pure
349
+ `ExecutionOverlay` model — per-state status, attempts, duration, and the taken edges — for
350
+ building your own UI.
351
+
352
+ ```typescript
353
+ import { parseExecutionHistory } from 'sfn-diagram';
354
+
355
+ const overlay = parseExecutionHistory({ events });
356
+ overlay.states['ProcessOrder']; // { status: 'succeeded', attempts: 1, durationMs: 1250 }
357
+ overlay.takenEdges; // [{ from: 'ValidateOrder', to: 'ProcessOrder' }, ...]
358
+ ```
359
+
360
+ Fetching history from AWS (Node) — use the `sfn-diagram/aws` helper, which paginates `GetExecutionHistory` for you:
361
+
362
+ ```typescript
363
+ import { SFNClient } from '@aws-sdk/client-sfn';
364
+ import { fetchExecutionHistory } from 'sfn-diagram/aws';
365
+
366
+ const client = new SFNClient({ region: 'us-east-1' });
367
+ const events = await fetchExecutionHistory({ client, executionArn });
368
+
369
+ const { svg } = generateExecution({ aslDefinition: asl, history: events });
370
+ ```
371
+
372
+ `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.
373
+
374
+ <details>
375
+ <summary>Prefer to paginate yourself? The manual loop:</summary>
376
+
377
+ ```typescript
378
+ import { SFNClient, GetExecutionHistoryCommand } from '@aws-sdk/client-sfn';
379
+
380
+ const client = new SFNClient({ region: 'us-east-1' });
381
+ const events = [];
382
+ let nextToken;
383
+ do {
384
+ const page = await client.send(
385
+ new GetExecutionHistoryCommand({ executionArn, nextToken, maxResults: 1000 }),
386
+ );
387
+ events.push(...(page.events ?? []));
388
+ nextToken = page.nextToken;
389
+ } while (nextToken);
390
+
391
+ const { svg } = generateExecution({ aslDefinition: asl, history: events });
392
+ ```
393
+ </details>
394
+
225
395
  ### SfnDiagramGenerator Class
226
396
 
227
397
  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.
@@ -259,23 +429,34 @@ const mermaidResult = generator.generateMermaid({ aslDefinition: asl });
259
429
  - `'dark'` - AWS dark theme
260
430
 
261
431
  **Custom theme:**
432
+
433
+ A `CustomTheme` sets the background, per-state-type fill/stroke colours, edge colours, and typography. Pass it anywhere a theme is accepted.
434
+
262
435
  ```typescript
263
- const customTheme = {
264
- backgroundColor: '#ffffff',
265
- nodeStroke: '#232f3e',
266
- nodeStrokeWidth: 2,
267
- fontSize: 14,
436
+ import type { CustomTheme } from 'sfn-diagram';
437
+
438
+ const customTheme: CustomTheme = {
439
+ background: '#ffffff',
440
+ edgeColors: {
441
+ choice: '#7b1fa2',
442
+ default: '#607d8b',
443
+ error: '#f44336',
444
+ normal: '#232f3e',
445
+ retry: '#f9a825', // optional; falls back to `error` when omitted
446
+ },
268
447
  fontFamily: 'Arial, sans-serif',
269
- stateColors: {
270
- Pass: '#4CAF50',
271
- Task: '#2196F3',
272
- Choice: '#FF9800',
273
- Wait: '#9C27B0',
274
- Succeed: '#4CAF50',
275
- Fail: '#F44336',
276
- Parallel: '#00BCD4',
277
- Map: '#3F51B5'
278
- }
448
+ fontSize: 14,
449
+ nodeColors: {
450
+ Pass: { fill: '#e8f5e9', stroke: '#4caf50' },
451
+ Task: { fill: '#e3f2fd', stroke: '#2196f3' },
452
+ Choice: { fill: '#fff3e0', stroke: '#ff9800' },
453
+ Wait: { fill: '#f3e5f5', stroke: '#9c27b0' },
454
+ Succeed: { fill: '#e8f5e9', stroke: '#4caf50' },
455
+ Fail: { fill: '#ffebee', stroke: '#f44336' },
456
+ Parallel: { fill: '#e0f7fa', stroke: '#00bcd4' },
457
+ Map: { fill: '#e8eaf6', stroke: '#3f51b5' },
458
+ },
459
+ textColor: '#232f3e',
279
460
  };
280
461
 
281
462
  generateSvg({ aslDefinition: asl, theme: customTheme });
@@ -531,7 +712,7 @@ pnpm install
531
712
  pnpm dev
532
713
  ```
533
714
 
534
- Paste any ASL JSON, switch themes, and see the SVG diagram update instantly — no install required beyond the dev server.
715
+ 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.
535
716
 
536
717
  ### VS Code Extension
537
718
 
@@ -547,28 +728,53 @@ code --install-extension vscode-sfn-diagram-*.vsix
547
728
 
548
729
  **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.
549
730
 
731
+ ### React Component
732
+
733
+ 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.
734
+
735
+ ```tsx
736
+ import { SfnDiagram } from 'sfn-diagram-react';
737
+
738
+ <SfnDiagram
739
+ definition={asl} // ASL object or JSON string
740
+ format="svg" // 'svg' (default) | 'mermaid'
741
+ theme="dark" // 'light' | 'dark' | CustomTheme
742
+ layout="LR" // 'TB' | 'LR' | 'RL' | 'BT'
743
+ history={history} // optional: renders an execution overlay
744
+ onError={(err) => console.error(err)}
745
+ />
746
+ ```
747
+
550
748
  ### GitHub Action
551
749
 
552
- Post SVG diff previews and Mermaid diagrams as PR comments whenever ASL files change. Located in [`packages/github-action-sfn-diagram/`](packages/github-action-sfn-diagram/).
750
+ 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.
751
+
752
+ 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.)
553
753
 
554
754
  ```yaml
555
755
  # .github/workflows/sfn-preview.yml
556
756
  name: Step Functions Preview
557
757
  on:
558
758
  pull_request:
559
- paths: ['**/*.asl.json']
759
+ paths: ['**/*.asl.json', '**/*.asl']
760
+
761
+ permissions:
762
+ contents: read
763
+ pull-requests: write # required to post the comment
560
764
 
561
765
  jobs:
562
766
  preview:
563
767
  runs-on: ubuntu-latest
564
768
  steps:
565
- - uses: actions/checkout@v4
566
- with: { fetch-depth: 0 }
567
- - uses: ./packages/github-action-sfn-diagram
568
- with:
569
- github-token: ${{ secrets.GITHUB_TOKEN }}
769
+ - uses: actions/checkout@v5
770
+ with: { fetch-depth: 0 } # needed to diff base vs head
771
+ - uses: yusufaf/sfn-diagram-action@v1
570
772
  ```
571
773
 
774
+ 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`).
775
+
776
+ 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.
777
+
572
778
  ## Contributing
573
779
 
574
780
  Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
package/dist/aws.cjs ADDED
@@ -0,0 +1,50 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _aws_sdk_client_sfn = require("@aws-sdk/client-sfn");
3
+ //#region src/aws.ts
4
+ /**
5
+ * Node-only AWS convenience helpers for `sfn-diagram`.
6
+ *
7
+ * This subpath (`sfn-diagram/aws`) is isolated from the core entry so that
8
+ * importing `sfn-diagram` never pulls in `@aws-sdk/client-sfn`, keeping the core
9
+ * dependency-free and browser-safe. `@aws-sdk/client-sfn` is an optional peer
10
+ * dependency — install it yourself to use these helpers.
11
+ */
12
+ /**
13
+ * Fetches the complete history of a Step Functions execution, paginating through
14
+ * every `GetExecutionHistory` page so callers don't have to hand-roll the
15
+ * `nextToken` loop.
16
+ *
17
+ * The returned `HistoryEvent[]` can be passed straight to `parseExecutionHistory`,
18
+ * `generateExecution`, or `generateMermaidExecution` from `sfn-diagram`.
19
+ *
20
+ * @param params - The SFN client, execution ARN, and optional page size.
21
+ * @returns All history events for the execution, in chronological order.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { SFNClient } from '@aws-sdk/client-sfn';
26
+ * import { fetchExecutionHistory } from 'sfn-diagram/aws';
27
+ * import { generateExecution } from 'sfn-diagram';
28
+ *
29
+ * const client = new SFNClient({ region: 'us-east-1' });
30
+ * const events = await fetchExecutionHistory({ client, executionArn });
31
+ * const { svg } = generateExecution({ aslDefinition: asl, history: events });
32
+ * ```
33
+ */
34
+ async function fetchExecutionHistory(params) {
35
+ const { client, executionArn, maxResults = 1e3 } = params;
36
+ const events = [];
37
+ let nextToken;
38
+ do {
39
+ const page = await client.send(new _aws_sdk_client_sfn.GetExecutionHistoryCommand({
40
+ executionArn,
41
+ maxResults,
42
+ nextToken
43
+ }));
44
+ events.push(...page.events ?? []);
45
+ nextToken = page.nextToken;
46
+ } while (nextToken);
47
+ return events;
48
+ }
49
+ //#endregion
50
+ exports.fetchExecutionHistory = fetchExecutionHistory;
package/dist/aws.d.cts ADDED
@@ -0,0 +1,36 @@
1
+ import { HistoryEvent, SFNClient } from "@aws-sdk/client-sfn";
2
+
3
+ //#region src/aws.d.ts
4
+ interface FetchExecutionHistoryParams {
5
+ /** A caller-constructed SFN client (with the caller's credentials/region). */
6
+ client: SFNClient;
7
+ /** ARN of the execution whose history should be fetched. */
8
+ executionArn: string;
9
+ /** Page size passed to `GetExecutionHistory`. Defaults to 1000 (the AWS max). */
10
+ maxResults?: number;
11
+ }
12
+ /**
13
+ * Fetches the complete history of a Step Functions execution, paginating through
14
+ * every `GetExecutionHistory` page so callers don't have to hand-roll the
15
+ * `nextToken` loop.
16
+ *
17
+ * The returned `HistoryEvent[]` can be passed straight to `parseExecutionHistory`,
18
+ * `generateExecution`, or `generateMermaidExecution` from `sfn-diagram`.
19
+ *
20
+ * @param params - The SFN client, execution ARN, and optional page size.
21
+ * @returns All history events for the execution, in chronological order.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { SFNClient } from '@aws-sdk/client-sfn';
26
+ * import { fetchExecutionHistory } from 'sfn-diagram/aws';
27
+ * import { generateExecution } from 'sfn-diagram';
28
+ *
29
+ * const client = new SFNClient({ region: 'us-east-1' });
30
+ * const events = await fetchExecutionHistory({ client, executionArn });
31
+ * const { svg } = generateExecution({ aslDefinition: asl, history: events });
32
+ * ```
33
+ */
34
+ declare function fetchExecutionHistory(params: FetchExecutionHistoryParams): Promise<HistoryEvent[]>;
35
+ //#endregion
36
+ export { FetchExecutionHistoryParams, fetchExecutionHistory };
package/dist/aws.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { HistoryEvent, SFNClient } from "@aws-sdk/client-sfn";
2
+
3
+ //#region src/aws.d.ts
4
+ interface FetchExecutionHistoryParams {
5
+ /** A caller-constructed SFN client (with the caller's credentials/region). */
6
+ client: SFNClient;
7
+ /** ARN of the execution whose history should be fetched. */
8
+ executionArn: string;
9
+ /** Page size passed to `GetExecutionHistory`. Defaults to 1000 (the AWS max). */
10
+ maxResults?: number;
11
+ }
12
+ /**
13
+ * Fetches the complete history of a Step Functions execution, paginating through
14
+ * every `GetExecutionHistory` page so callers don't have to hand-roll the
15
+ * `nextToken` loop.
16
+ *
17
+ * The returned `HistoryEvent[]` can be passed straight to `parseExecutionHistory`,
18
+ * `generateExecution`, or `generateMermaidExecution` from `sfn-diagram`.
19
+ *
20
+ * @param params - The SFN client, execution ARN, and optional page size.
21
+ * @returns All history events for the execution, in chronological order.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { SFNClient } from '@aws-sdk/client-sfn';
26
+ * import { fetchExecutionHistory } from 'sfn-diagram/aws';
27
+ * import { generateExecution } from 'sfn-diagram';
28
+ *
29
+ * const client = new SFNClient({ region: 'us-east-1' });
30
+ * const events = await fetchExecutionHistory({ client, executionArn });
31
+ * const { svg } = generateExecution({ aslDefinition: asl, history: events });
32
+ * ```
33
+ */
34
+ declare function fetchExecutionHistory(params: FetchExecutionHistoryParams): Promise<HistoryEvent[]>;
35
+ //#endregion
36
+ export { FetchExecutionHistoryParams, fetchExecutionHistory };
package/dist/aws.js ADDED
@@ -0,0 +1,49 @@
1
+ import { GetExecutionHistoryCommand } from "@aws-sdk/client-sfn";
2
+ //#region src/aws.ts
3
+ /**
4
+ * Node-only AWS convenience helpers for `sfn-diagram`.
5
+ *
6
+ * This subpath (`sfn-diagram/aws`) is isolated from the core entry so that
7
+ * importing `sfn-diagram` never pulls in `@aws-sdk/client-sfn`, keeping the core
8
+ * dependency-free and browser-safe. `@aws-sdk/client-sfn` is an optional peer
9
+ * dependency — install it yourself to use these helpers.
10
+ */
11
+ /**
12
+ * Fetches the complete history of a Step Functions execution, paginating through
13
+ * every `GetExecutionHistory` page so callers don't have to hand-roll the
14
+ * `nextToken` loop.
15
+ *
16
+ * The returned `HistoryEvent[]` can be passed straight to `parseExecutionHistory`,
17
+ * `generateExecution`, or `generateMermaidExecution` from `sfn-diagram`.
18
+ *
19
+ * @param params - The SFN client, execution ARN, and optional page size.
20
+ * @returns All history events for the execution, in chronological order.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { SFNClient } from '@aws-sdk/client-sfn';
25
+ * import { fetchExecutionHistory } from 'sfn-diagram/aws';
26
+ * import { generateExecution } from 'sfn-diagram';
27
+ *
28
+ * const client = new SFNClient({ region: 'us-east-1' });
29
+ * const events = await fetchExecutionHistory({ client, executionArn });
30
+ * const { svg } = generateExecution({ aslDefinition: asl, history: events });
31
+ * ```
32
+ */
33
+ async function fetchExecutionHistory(params) {
34
+ const { client, executionArn, maxResults = 1e3 } = params;
35
+ const events = [];
36
+ let nextToken;
37
+ do {
38
+ const page = await client.send(new GetExecutionHistoryCommand({
39
+ executionArn,
40
+ maxResults,
41
+ nextToken
42
+ }));
43
+ events.push(...page.events ?? []);
44
+ nextToken = page.nextToken;
45
+ } while (nextToken);
46
+ return events;
47
+ }
48
+ //#endregion
49
+ export { fetchExecutionHistory };
package/dist/cli.js CHANGED
@@ -1329,6 +1329,8 @@ var SvgRenderer = class {
1329
1329
  });
1330
1330
  nodeGroup.append("text").attr("x", labelX).attr("y", labelY).attr("text-anchor", "middle").attr("dominant-baseline", "middle").attr("fill", this.theme.textColor).attr("font-size", this.theme.fontSize).attr("font-family", this.theme.fontFamily).text(node.label);
1331
1331
  if (this.options.showStateTypes) nodeGroup.append("text").attr("x", labelX).attr("y", labelY + 20).attr("text-anchor", "middle").attr("dominant-baseline", "middle").attr("fill", this.theme.textColor).attr("font-size", this.theme.fontSize - 2).attr("opacity", .7).text(node.type);
1332
+ const annotation = this.options.nodeAnnotations?.[node.id];
1333
+ if (annotation) nodeGroup.append("text").attr("x", labelX).attr("y", labelY + (this.options.showStateTypes ? 36 : 18)).attr("text-anchor", "middle").attr("dominant-baseline", "middle").attr("fill", this.theme.textColor).attr("font-size", this.theme.fontSize - 3).attr("opacity", .75).text(annotation);
1332
1334
  }
1333
1335
  /**
1334
1336
  * Render rectangle node
@@ -1414,7 +1416,11 @@ var SvgRenderer = class {
1414
1416
  const edgeColor = this.resolveEdgeColor(edge.type);
1415
1417
  const markerType = edge.type || "normal";
1416
1418
  const pathData = edge.from === edge.to ? this.buildSelfLoopPath(edge.points) : this.pathGenerator(edge.points) ?? "";
1417
- const pathElement = group.append("path").attr("d", pathData).attr("fill", "none").attr("stroke", edgeColor).attr("stroke-width", edge.type === "error" ? 2 : 1.5).attr("marker-end", `url(#arrowhead-${markerType})`);
1419
+ const override = this.options.edgeOverrides?.[`${edge.from}->${edge.to}`];
1420
+ const strokeColor = override?.stroke ?? edgeColor;
1421
+ const strokeWidth = override?.strokeWidth ?? (edge.type === "error" ? 2 : 1.5);
1422
+ const pathElement = group.append("path").attr("d", pathData).attr("fill", "none").attr("stroke", strokeColor).attr("stroke-width", strokeWidth).attr("marker-end", `url(#arrowhead-${markerType})`);
1423
+ if (override?.strokeOpacity !== void 0) pathElement.attr("stroke-opacity", override.strokeOpacity);
1418
1424
  if (edge.type === "error") pathElement.attr("stroke-dasharray", "5,5");
1419
1425
  else if (edge.type === "default") pathElement.attr("stroke-dasharray", "8,4");
1420
1426
  else if (edge.type === "retry") pathElement.attr("stroke-dasharray", "4,3");
@@ -1482,6 +1488,34 @@ var SvgRenderer = class {
1482
1488
  };
1483
1489
  //#endregion
1484
1490
  //#region src/renderers/MermaidRenderer.ts
1491
+ /** Mermaid classDef declarations for diff highlighting, keyed by diff status. */
1492
+ const DIFF_CLASS_DEFS = {
1493
+ added: "classDef diffAdded fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px",
1494
+ modified: "classDef diffModified fill:#fff9c4,stroke:#f57f17,stroke-width:2px",
1495
+ removed: "classDef diffRemoved fill:#ffcdd2,stroke:#c62828,stroke-width:2px"
1496
+ };
1497
+ /** Mermaid class name applied to a state for a given diff status. */
1498
+ const DIFF_CLASS_NAMES = {
1499
+ added: "diffAdded",
1500
+ modified: "diffModified",
1501
+ removed: "diffRemoved"
1502
+ };
1503
+ /** Mermaid classDef declarations for execution highlighting, keyed by status. */
1504
+ const EXECUTION_CLASS_DEFS = {
1505
+ caught: "classDef execCaught fill:#ffe0b2,stroke:#e65100,stroke-width:2px",
1506
+ failed: "classDef execFailed fill:#ffcdd2,stroke:#c62828,stroke-width:3px",
1507
+ notReached: "classDef execNotReached fill:#f5f5f5,stroke:#bdbdbd,stroke-width:1px",
1508
+ running: "classDef execRunning fill:#bbdefb,stroke:#1565c0,stroke-width:2px",
1509
+ succeeded: "classDef execSucceeded fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px"
1510
+ };
1511
+ /** Mermaid class name applied to a state for a given execution status. */
1512
+ const EXECUTION_CLASS_NAMES = {
1513
+ caught: "execCaught",
1514
+ failed: "execFailed",
1515
+ notReached: "execNotReached",
1516
+ running: "execRunning",
1517
+ succeeded: "execSucceeded"
1518
+ };
1485
1519
  /**
1486
1520
  * MermaidRenderer - Generates Mermaid state diagram syntax from ASL
1487
1521
  */
@@ -1490,7 +1524,7 @@ var MermaidRenderer = class {
1490
1524
  * Render nodes and edges to Mermaid syntax
1491
1525
  */
1492
1526
  render(params) {
1493
- const { asl, edges, nodes } = params;
1527
+ const { asl, edges, executionClasses, nodeAnnotations, nodes, stateClasses } = params;
1494
1528
  const lines = [];
1495
1529
  lines.push("stateDiagram-v2");
1496
1530
  lines.push("");
@@ -1503,8 +1537,11 @@ var MermaidRenderer = class {
1503
1537
  const stateDefinitions = /* @__PURE__ */ new Set();
1504
1538
  nodes.forEach((node) => {
1505
1539
  const id = this.sanitizeId(node.id);
1506
- if (node.label !== node.id && !stateDefinitions.has(id)) {
1507
- lines.push(` ${id}: ${this.escapeLabel(node.label)}`);
1540
+ if (stateDefinitions.has(id)) return;
1541
+ const annotation = nodeAnnotations?.[node.id];
1542
+ const displayLabel = annotation ? `${node.label} (${annotation})` : node.label;
1543
+ if (displayLabel !== node.id) {
1544
+ lines.push(` ${id}: ${this.escapeLabel(displayLabel)}`);
1508
1545
  stateDefinitions.add(id);
1509
1546
  }
1510
1547
  });
@@ -1530,9 +1567,21 @@ var MermaidRenderer = class {
1530
1567
  lines.push(" classDef failState fill:#ffebee,stroke:#f44336,stroke-width:3px");
1531
1568
  lines.push(" classDef choiceState fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px");
1532
1569
  lines.push(" classDef taskState fill:#fff3e0,stroke:#ef6c00,stroke-width:2px");
1570
+ if (stateClasses && Object.keys(stateClasses).length > 0) for (const status of Object.keys(DIFF_CLASS_DEFS)) lines.push(` ${DIFF_CLASS_DEFS[status]}`);
1571
+ if (executionClasses && Object.keys(executionClasses).length > 0) for (const status of Object.keys(EXECUTION_CLASS_DEFS)) lines.push(` ${EXECUTION_CLASS_DEFS[status]}`);
1533
1572
  lines.push("");
1534
1573
  nodes.forEach((node) => {
1535
1574
  const id = this.sanitizeId(node.id);
1575
+ const executionStatus = executionClasses?.[node.id];
1576
+ if (executionStatus) {
1577
+ lines.push(` class ${id} ${EXECUTION_CLASS_NAMES[executionStatus]}`);
1578
+ return;
1579
+ }
1580
+ const diffStatus = stateClasses?.[node.id];
1581
+ if (diffStatus) {
1582
+ lines.push(` class ${id} ${DIFF_CLASS_NAMES[diffStatus]}`);
1583
+ return;
1584
+ }
1536
1585
  switch (node.type) {
1537
1586
  case "Succeed":
1538
1587
  lines.push(` class ${id} successState`);
@@ -1606,7 +1655,9 @@ const DEFAULT_DIAGRAM_OPTIONS = {
1606
1655
  showIcons: false,
1607
1656
  pngQuality: 90,
1608
1657
  backgroundColor: "transparent",
1609
- nodeOverrides: void 0
1658
+ nodeOverrides: void 0,
1659
+ edgeOverrides: void 0,
1660
+ nodeAnnotations: void 0
1610
1661
  };
1611
1662
  /**
1612
1663
  * Merge user-provided options with defaults