sfn-diagram-react 0.7.0 → 1.0.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
@@ -15,14 +15,15 @@ npm install sfn-diagram-react sfn-diagram
15
15
  pnpm add sfn-diagram-react sfn-diagram
16
16
  ```
17
17
 
18
- `react` and `sfn-diagram` are peer dependencies. `sfn-diagram >= 1.7.0` is required — earlier versions don't have the options this package forwards (`collapse`, `catchHandling`, `showVariables`, `generateHtml`, `edgeOverrides`' qualified-id form, and `layout`/`theme` on the Mermaid path).
18
+ `react` and `sfn-diagram` are peer dependencies. `sfn-diagram-react` 1.x needs `sfn-diagram` 2, because `format="html"` renders through the `sfn-diagram/html` subpath that 2.0 introduced. Like core, it ships ES modules only and needs Node >= 22.12, where `require()` still loads it. Upgrading from 1.x? See [Migrating to 2.0](https://sfn.yusufaf.dev/upgrading-to-v2/), which also covers Jest's `transformIgnorePatterns`.
19
19
 
20
20
  ## Usage
21
21
 
22
22
  ```tsx
23
+ import type { AslDefinition } from 'sfn-diagram'
23
24
  import { SfnDiagram } from 'sfn-diagram-react'
24
25
 
25
- const definition = {
26
+ const definition: AslDefinition = {
26
27
  StartAt: 'Hello',
27
28
  States: {
28
29
  Hello: { Type: 'Pass', Next: 'World' },
@@ -42,13 +43,21 @@ export function Diagram() {
42
43
  }
43
44
  ```
44
45
 
45
- `definition` accepts either an ASL object or a JSON string.
46
+ `definition` accepts an `AslDefinition` object or a JSON string. An unannotated object
47
+ in a variable widens `Type` to `string`, so annotate it
48
+ (`const definition: AslDefinition = {...}`). A definition imported from a `.json` file
49
+ is widened the same way: pass it as a string (`JSON.stringify(json)` or the raw file
50
+ text), or cast it with `as unknown as AslDefinition`. A direct `as AslDefinition` fails
51
+ for a Parallel whose branches name different states.
46
52
 
47
53
  ### Execution overlay
48
54
 
49
55
  Pass an execution's `history` to overlay a real run onto the diagram: states are
50
- coloured by outcome (succeeded / failed / caught / not-reached), the taken path is
51
- emphasized, and per-state duration and retry counts are annotated.
56
+ coloured by outcome (succeeded / failed / caught / aborted / not-reached), the taken
57
+ path is emphasized, and per-state duration and retry counts are annotated. An aborted
58
+ state was entered but abandoned before it finished: another branch's or iteration's
59
+ failure stopped it, AWS aborted its container, or it was still open when the execution
60
+ was stopped.
52
61
 
53
62
  ```tsx
54
63
  // historyJson: output of `aws stepfunctions get-execution-history`, as a JSON string
@@ -58,16 +67,14 @@ emphasized, and per-state duration and retry counts are annotated.
58
67
  `history` accepts a `GetExecutionHistory` events array, the raw command output, or a
59
68
  JSON string of either. In the browser, pass the JSON string.
60
69
 
61
- `history` and `before` (diff mode, below) cannot be combined — core has no API for
62
- overlaying an execution onto a diff. Combining them calls `onError` and renders `null`,
63
- the same way any other invalid input is handled.
70
+ `history` and `before` (diff mode, below) cannot be combined — core's SVG and Mermaid
71
+ functions can't overlay an execution onto a diff. Its interactive viewer can
72
+ (`generateHtml` takes both), but this package doesn't wire that up yet. Combining them
73
+ calls `onError` and renders `null`, the same way any other invalid input is handled.
64
74
 
65
75
  **`format="mermaid"` combined with `history` honours the same options as plain
66
76
  `format="mermaid"`** — `catchHandling`, `collapse`, `showVariables`, `layout` and
67
- `theme` — with sfn-diagram 1.10 or later. Earlier versions of core's Mermaid
68
- execution-overlay function (`generateMermaidExecution`) accept only the ASL definition,
69
- `history`, `layout`, and `theme`, and ignore the rest. The SVG execution overlay
70
- honours every option.
77
+ `theme`. The SVG execution overlay honours every option.
71
78
 
72
79
  ### Interactive viewer (`format="html"`)
73
80
 
@@ -84,8 +91,8 @@ Consequences worth knowing:
84
91
  - `onStateClick` does not fire from inside it — clicks inside a sandboxed iframe never
85
92
  reach the parent document's event handlers.
86
93
  - `history` cannot be combined with `format="html"` yet — core's `generateHtml` accepts
87
- `history` and `diff` overlays since sfn-diagram 1.8, but this hook does not wire them
88
- up. Combining them calls `onError` and renders `null`.
94
+ `history` and `diff` overlays, but this package does not wire them up yet. Combining
95
+ them calls `onError` and renders `null`.
89
96
 
90
97
  ```tsx
91
98
  <SfnDiagram definition={definition} format="html" style={{ height: 600, width: '100%' }} />
@@ -105,13 +112,15 @@ coloured (green / yellow / red) instead of the machine's normal styling.
105
112
  ```
106
113
 
107
114
  - `before` accepts an ASL object or a JSON string, same as `definition`.
108
- - `before` cannot be combined with `history` or `format="html"` — core has no diff API
109
- for either combination. Combining them calls `onError` and renders `null`.
115
+ - `before` cannot be combined with `history` (see
116
+ [Execution overlay](#execution-overlay)) or with `format="html"`. Core's
117
+ `generateHtml` takes a diff, but this package doesn't pass one to it yet. Combining
118
+ them calls `onError` and renders `null`.
110
119
  - The **SVG diff** (`generateDiff`) honours every diagram option this package forwards
111
120
  (`collapse`, `catchHandling`, `showIcons`, and the rest).
112
- - The **Mermaid diff** ignores every prop except the two definitions (`layout`, `theme`,
113
- `collapse`, …) in `format="mermaid"` diff mode: this package doesn't forward them to
114
- `generateMermaidDiff` yet (#393), although core accepts them since sfn-diagram 1.10.
121
+ - The **Mermaid diff** ignores every diagram prop (`layout`, `theme`, `collapse`, …):
122
+ core's `generateMermaidDiff` accepts them, but this package doesn't forward them yet
123
+ (#393).
115
124
 
116
125
  ### `onStateClick`
117
126
 
@@ -148,15 +157,24 @@ There is no programmatic zoom/pan control — core doesn't expose the interactiv
148
157
  viewer's controller from a public subpath yet, so a meaningful pan/zoom handle needs a
149
158
  core change first.
150
159
 
160
+ ### Several diagrams on one page
161
+
162
+ Each `<SfnDiagram>` (and each `useSfnDiagram` call) prefixes its SVG marker ids with a
163
+ value from React's `useId()`. Within one React root, two diagrams, even of the same
164
+ definition, never pick up each other's arrowheads, and server-rendered markup keeps the
165
+ same ids after hydration. When a page hydrates several roots, give each root its own
166
+ `identifierPrefix`, as React requires for `useId`.
167
+
151
168
  ### `useSfnDiagram`
152
169
 
153
170
  For the raw output — the SVG/HTML string, Mermaid code, dimensions, and metadata —
154
171
  without mounting anything, use the hook `<SfnDiagram>` is built on:
155
172
 
156
173
  ```tsx
174
+ import type { AslDefinition } from 'sfn-diagram'
157
175
  import { useSfnDiagram } from 'sfn-diagram-react'
158
176
 
159
- function DownloadSvgButton({ definition }: { definition: object }) {
177
+ function DownloadSvgButton({ definition }: { definition: AslDefinition }) {
160
178
  const result = useSfnDiagram({ definition })
161
179
  if (result.type !== 'svg') return null
162
180
 
@@ -178,31 +196,29 @@ ones (`className`, `onError`, `onStateClick`, `style`, `title`). Errors are retu
178
196
 
179
197
  | Prop | Type | Default | Applies to | Description |
180
198
  | --- | --- | --- | --- | --- |
181
- | `before` | `object \| string` | — | svg, mermaid | The *original* side of a diff; `definition` becomes the *after* side. See [Diff mode](#diff-mode). |
199
+ | `before` | `AslDefinition \| string` | — | svg, mermaid | The *original* side of a diff; `definition` becomes the *after* side. See [Diff mode](#diff-mode). |
182
200
  | `catchHandling` | `'hide' \| 'show'` | `'show'` | svg, mermaid¹ | `'hide'` drops Catch error edges and handler-only nodes. |
183
201
  | `className` | `string` | — | all | Applied to the wrapper element (`div`, `pre`, or `iframe`). |
184
202
  | `collapse` | `string[] \| boolean` | — | svg, mermaid¹ | Collapse Parallel/Map containers into a placeholder node. `true` collapses every container; an array collapses only the named ones. |
185
- | `definition` | `object \| string` | — (required) | all | ASL definition as an object or JSON string. |
203
+ | `definition` | `AslDefinition \| string` | — (required) | all | ASL definition as an object or JSON string. |
186
204
  | `edgeOverrides` | `Record<string, EdgeStyleOverride>` | — | svg only | Per-edge style overrides keyed by `GraphEdge.id` (preferred, e.g. `Route->Work#choice#1`) or the legacy bare `${from}->${to}` (broad-matches every edge between that pair). |
187
- | `edgeStyle` | `'straight' \| 'curved' \| 'orthogonal'` | `'curved'` | svg, mermaid¹ | Edge path rendering style. |
205
+ | `edgeStyle` | `'straight' \| 'curved' \| 'orthogonal'` | `'curved'` | svg only | Edge path rendering style. |
188
206
  | `format` | `'svg' \| 'mermaid' \| 'html'` | `'svg'` | — | Output format. See [Interactive viewer](#interactive-viewer-formathtml) for `'html'`. |
189
207
  | `history` | `HistoryEvent[] \| GetExecutionHistoryCommandOutput \| string` | — | svg, mermaid | Execution history; when set, renders an execution overlay. See [Execution overlay](#execution-overlay). |
190
208
  | `iconPosition` | `'left' \| 'top' \| 'right'` | `'left'` | svg only | Position of AWS service icons relative to the node label. |
191
209
  | `iconSize` | `number` | `24` | svg only | Size of AWS service icons in pixels. |
192
- | `layout` | `'TB' \| 'LR' \| 'RL' \| 'BT'` | `'TB'` | svg, mermaid | Graph layout direction. |
210
+ | `layout` | `'TB' \| 'LR' \| 'RL' \| 'BT'` | `'TB'` | svg, mermaid¹ | Graph layout direction. |
193
211
  | `nodeOverrides` | `Record<string, Partial<NodeStyle>>` | — | svg only | Per-node style overrides keyed by graph node id. |
194
212
  | `onError` | `(error: Error) => void` | — | all | Called when the definition fails to parse/render. |
195
213
  | `onStateClick` | `(params: { event, stateId }) => void` | — | svg only | See [`onStateClick`](#onstateclick). |
196
214
  | `showIcons` | `boolean` | `false` | svg only | Whether to display AWS service icons on Task state nodes. |
197
215
  | `showVariables` | `boolean` | `true` | svg, mermaid¹ | Whether to annotate nodes with the variables they assign via ASL `Assign`. |
198
216
  | `style` | `React.CSSProperties` | — | all | Applied to the wrapper element. |
199
- | `theme` | `'light' \| 'dark' \| CustomTheme` | `'light'` | svg, mermaid | Diagram theme. |
217
+ | `theme` | `'light' \| 'dark' \| CustomTheme` | `'light'` | svg, mermaid¹ | Diagram theme. |
200
218
  | `title` | `string` | `'Step Functions diagram'` | html only | Accessible title for the rendered iframe. |
201
219
 
202
- ¹ Ignored when `format="mermaid"` is combined with `history` on sfn-diagram before 1.10 —
203
- see [Execution overlay](#execution-overlay). Applies normally to plain `format="mermaid"`
204
- (no `history`). `format="mermaid"` diff mode (the `before` prop) is a separate
205
- carve-out with its own rule — see [Diff mode](#diff-mode).
220
+ ¹ Not applied in `format="mermaid"` diff mode (the `before` prop) — see
221
+ [Diff mode](#diff-mode).
206
222
 
207
223
  When the definition is invalid the component renders `null` and (if provided) calls
208
224
  `onError`.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { DiagramOptions, DiffOutput, ExecutionHistoryInput, ExecutionOutput, HtmlOutput, MermaidDiffOutput, MermaidExecutionOutput, MermaidOutput, SvgOutput } from "sfn-diagram";
2
+ import { AslDefinition, DiagramOptions, DiffOutput, ExecutionHistoryInput, ExecutionOutput, HtmlOutput, MermaidDiffOutput, MermaidExecutionOutput, MermaidOutput, SvgOutput } from "sfn-diagram";
3
3
  interface UseSfnDiagramParams extends Pick<DiagramOptions, 'catchHandling' | 'collapse' | 'edgeOverrides' | 'edgeStyle' | 'iconPosition' | 'iconSize' | 'layout' | 'nodeOverrides' | 'showIcons' | 'showVariables' | 'theme'> {
4
4
  /**
5
5
  * When set, `definition` is treated as the *after* side of a diff and `before`
@@ -7,15 +7,15 @@ interface UseSfnDiagramParams extends Pick<DiagramOptions, 'catchHandling' | 'co
7
7
  * removed states rather than the plain machine. Accepts an ASL object or a
8
8
  * JSON string. Cannot be combined with `history` (core's SVG and Mermaid diff
9
9
  * functions take no history) or with `format="html"` (core's `generateHtml`
10
- * accepts a diff since sfn-diagram 1.8, but this hook does not wire it up yet).
10
+ * accepts a diff, but this hook does not wire it up yet).
11
11
  *
12
12
  * The Mermaid diff ignores every option in this type's `Pick<DiagramOptions, ...>`
13
13
  * list - `layout`, `theme`, and the rest: this hook doesn't forward them to
14
- * `generateMermaidDiff` yet (#393), although core accepts them since sfn-diagram
15
- * 1.10. The SVG diff honours them.
14
+ * `generateMermaidDiff` yet (#393), although core's `generateMermaidDiff` accepts
15
+ * them. The SVG diff honours them.
16
16
  */
17
- before?: object | string;
18
- definition: object | string;
17
+ before?: AslDefinition | string;
18
+ definition: AslDefinition | string;
19
19
  /**
20
20
  * Output format. `'svg'` and `'mermaid'` render static markup; `'html'` renders
21
21
  * the interactive pan/zoom/search viewer as a self-contained HTML document.
@@ -62,6 +62,9 @@ type SfnDiagramResult = {
62
62
  * than mounted markup - for example, building a data URI for `<img src>`, a
63
63
  * download button, or a custom render target.
64
64
  *
65
+ * SVG marker ids are prefixed per hook instance from `useId()`, so diagrams on one
66
+ * page don't share them and server-rendered ids match the client's.
67
+ *
65
68
  * @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
66
69
  * the presentational ones (`className`, `onError`, `onStateClick`, `style`,
67
70
  * `title`).
@@ -70,7 +73,7 @@ type SfnDiagramResult = {
70
73
  *
71
74
  * @example
72
75
  * ```tsx
73
- * function DownloadSvgButton({ definition }: { definition: object }) {
76
+ * function DownloadSvgButton({ definition }: { definition: AslDefinition }) {
74
77
  * const result = useSfnDiagram({ definition })
75
78
  * if (result.type !== 'svg') return null
76
79
  *
package/dist/index.js CHANGED
@@ -1,7 +1,29 @@
1
1
  'use client'
2
- import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef } from "react";
3
- import { generateDiff, generateExecution, generateHtml, generateMermaid, generateMermaidDiff, generateMermaidExecution, generateSvg } from "sfn-diagram";
2
+ import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef } from "react";
3
+ import { generateDiff, generateExecution, generateMermaid, generateMermaidDiff, generateMermaidExecution, generateSvg } from "sfn-diagram";
4
+ import { generateHtml } from "sfn-diagram/html";
4
5
  import { jsx } from "react/jsx-runtime";
6
+ // src/idPrefix.ts
7
+ /**
8
+ * Turns a `useId()` value into a core `DiagramOptions.idPrefix`, so each diagram's
9
+ * SVG marker ids are its own and match between the server render and hydration.
10
+ *
11
+ * React 18 ids look like `:r0:`, React 19 ids like `_r_0_`, and both embed the
12
+ * root's `identifierPrefix`, which the caller picks and may contain characters core
13
+ * rejects. Every character outside `[A-Za-z0-9_.-]` becomes `_`, `:` included, so
14
+ * the ids stay valid in a standalone SVG file too. The `sfn-` start keeps the
15
+ * prefix a valid XML name, and the trailing `-` separates it from core's id.
16
+ *
17
+ * @param params - The React id to convert.
18
+ * @returns The prefix core prepends to each marker id.
19
+ *
20
+ * @example
21
+ * toIdPrefix({ reactId: ':r0:' }) // 'sfn-_r0_-'
22
+ */
23
+ function toIdPrefix(params) {
24
+ const { reactId } = params;
25
+ return `sfn-${reactId.replace(/[^A-Za-z0-9_.-]/g, "_")}-`;
26
+ }
5
27
  // src/useSfnDiagram.ts
6
28
  function toAslString(params) {
7
29
  const { definition } = params;
@@ -21,6 +43,9 @@ function omitUndefinedValues(options) {
21
43
  * than mounted markup - for example, building a data URI for `<img src>`, a
22
44
  * download button, or a custom render target.
23
45
  *
46
+ * SVG marker ids are prefixed per hook instance from `useId()`, so diagrams on one
47
+ * page don't share them and server-rendered ids match the client's.
48
+ *
24
49
  * @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
25
50
  * the presentational ones (`className`, `onError`, `onStateClick`, `style`,
26
51
  * `title`).
@@ -29,7 +54,7 @@ function omitUndefinedValues(options) {
29
54
  *
30
55
  * @example
31
56
  * ```tsx
32
- * function DownloadSvgButton({ definition }: { definition: object }) {
57
+ * function DownloadSvgButton({ definition }: { definition: AslDefinition }) {
33
58
  * const result = useSfnDiagram({ definition })
34
59
  * if (result.type !== 'svg') return null
35
60
  *
@@ -40,6 +65,7 @@ function omitUndefinedValues(options) {
40
65
  */
41
66
  function useSfnDiagram(params) {
42
67
  const { before, catchHandling, collapse, definition, edgeOverrides, edgeStyle, format = "svg", history, iconPosition, iconSize, layout = "TB", nodeOverrides, showIcons, showVariables, theme = "light" } = params;
68
+ const idPrefix = toIdPrefix({ reactId: useId() });
43
69
  const asl = useMemo(() => toAslString({ definition }), [definition]);
44
70
  const beforeAsl = useMemo(() => before === void 0 ? void 0 : toAslString({ definition: before }), [before]);
45
71
  return useMemo(() => {
@@ -57,6 +83,10 @@ function useSfnDiagram(params) {
57
83
  showVariables,
58
84
  theme
59
85
  });
86
+ const svgOptions = {
87
+ ...diagramOptions,
88
+ idPrefix
89
+ };
60
90
  if (beforeAsl !== void 0) {
61
91
  if (history) throw new Error("history and before cannot be combined");
62
92
  if (format === "html") throw new Error("before and format=\"html\" cannot be combined");
@@ -74,7 +104,7 @@ function useSfnDiagram(params) {
74
104
  const output = generateDiff({
75
105
  after: asl,
76
106
  before: beforeAsl,
77
- ...diagramOptions
107
+ ...svgOptions
78
108
  });
79
109
  return {
80
110
  height: output.height,
@@ -116,10 +146,10 @@ function useSfnDiagram(params) {
116
146
  const output = history ? generateExecution({
117
147
  aslDefinition: asl,
118
148
  history,
119
- ...diagramOptions
149
+ ...svgOptions
120
150
  }) : generateSvg({
121
151
  aslDefinition: asl,
122
- ...diagramOptions
152
+ ...svgOptions
123
153
  });
124
154
  return {
125
155
  height: output.height,
@@ -145,6 +175,7 @@ function useSfnDiagram(params) {
145
175
  history,
146
176
  iconPosition,
147
177
  iconSize,
178
+ idPrefix,
148
179
  layout,
149
180
  nodeOverrides,
150
181
  showIcons,
package/package.json CHANGED
@@ -1,29 +1,22 @@
1
1
  {
2
2
  "name": "sfn-diagram-react",
3
- "version": "0.7.0",
3
+ "version": "1.0.0",
4
4
  "description": "React component for AWS Step Functions ASL diagrams",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
- "main": "./dist/index.cjs",
8
7
  "module": "./dist/index.js",
9
8
  "types": "./dist/index.d.ts",
10
9
  "exports": {
11
10
  ".": {
12
- "import": {
13
- "types": "./dist/index.d.ts",
14
- "default": "./dist/index.js"
15
- },
16
- "require": {
17
- "types": "./dist/index.d.cts",
18
- "default": "./dist/index.cjs"
19
- }
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
20
13
  }
21
14
  },
22
15
  "files": [
23
16
  "dist"
24
17
  ],
25
18
  "engines": {
26
- "node": ">=20"
19
+ "node": ">=22.12.0"
27
20
  },
28
21
  "scripts": {
29
22
  "build": "tsdown",
@@ -34,7 +27,7 @@
34
27
  "prepublishOnly": "pnpm run build && pnpm run validate",
35
28
  "validate": "pnpm run validate:publint && pnpm run validate:types",
36
29
  "validate:publint": "publint",
37
- "validate:types": "attw --pack ."
30
+ "validate:types": "attw --pack . --profile node16 --ignore-rules cjs-resolves-to-esm"
38
31
  },
39
32
  "keywords": [
40
33
  "aws",
@@ -64,7 +57,7 @@
64
57
  "homepage": "https://sfn.yusufaf.dev/ecosystem/react/",
65
58
  "peerDependencies": {
66
59
  "react": "^18.0.0 || ^19.0.0",
67
- "sfn-diagram": "^1.7.0"
60
+ "sfn-diagram": "^2.0.0"
68
61
  },
69
62
  "devDependencies": {
70
63
  "@arethetypeswrong/cli": "^0.18.5",
package/dist/index.cjs DELETED
@@ -1,198 +0,0 @@
1
- 'use client'
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- let react = require("react");
4
- let sfn_diagram = require("sfn-diagram");
5
- let react_jsx_runtime = require("react/jsx-runtime");
6
- // src/useSfnDiagram.ts
7
- function toAslString(params) {
8
- const { definition } = params;
9
- return typeof definition === "string" ? definition : JSON.stringify(definition);
10
- }
11
- function omitUndefinedValues(options) {
12
- const result = {};
13
- for (const key of Object.keys(options)) if (options[key] !== void 0) result[key] = options[key];
14
- return result;
15
- }
16
- /**
17
- * Generate a Step Functions diagram and return the raw output - SVG or HTML
18
- * markup, Mermaid code, dimensions, and metadata - without rendering it.
19
- *
20
- * This is the same generation logic `<SfnDiagram>` runs internally, exposed
21
- * directly for a consumer who needs the underlying string or metadata rather
22
- * than mounted markup - for example, building a data URI for `<img src>`, a
23
- * download button, or a custom render target.
24
- *
25
- * @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
26
- * the presentational ones (`className`, `onError`, `onStateClick`, `style`,
27
- * `title`).
28
- * @returns A {@link SfnDiagramResult} discriminated union. Errors are returned
29
- * as `{ type: 'error', error }` rather than thrown.
30
- *
31
- * @example
32
- * ```tsx
33
- * function DownloadSvgButton({ definition }: { definition: object }) {
34
- * const result = useSfnDiagram({ definition })
35
- * if (result.type !== 'svg') return null
36
- *
37
- * const href = `data:image/svg+xml;base64,${btoa(result.svg)}`
38
- * return <a download="diagram.svg" href={href}>Download SVG</a>
39
- * }
40
- * ```
41
- */
42
- function useSfnDiagram(params) {
43
- const { before, catchHandling, collapse, definition, edgeOverrides, edgeStyle, format = "svg", history, iconPosition, iconSize, layout = "TB", nodeOverrides, showIcons, showVariables, theme = "light" } = params;
44
- const asl = (0, react.useMemo)(() => toAslString({ definition }), [definition]);
45
- const beforeAsl = (0, react.useMemo)(() => before === void 0 ? void 0 : toAslString({ definition: before }), [before]);
46
- return (0, react.useMemo)(() => {
47
- try {
48
- const diagramOptions = omitUndefinedValues({
49
- catchHandling,
50
- collapse,
51
- edgeOverrides,
52
- edgeStyle,
53
- iconPosition,
54
- iconSize,
55
- layout,
56
- nodeOverrides,
57
- showIcons,
58
- showVariables,
59
- theme
60
- });
61
- if (beforeAsl !== void 0) {
62
- if (history) throw new Error("history and before cannot be combined");
63
- if (format === "html") throw new Error("before and format=\"html\" cannot be combined");
64
- if (format === "mermaid") {
65
- const output = (0, sfn_diagram.generateMermaidDiff)({
66
- after: asl,
67
- before: beforeAsl
68
- });
69
- return {
70
- code: output.code,
71
- metadata: output.metadata,
72
- type: "mermaid"
73
- };
74
- }
75
- const output = (0, sfn_diagram.generateDiff)({
76
- after: asl,
77
- before: beforeAsl,
78
- ...diagramOptions
79
- });
80
- return {
81
- height: output.height,
82
- metadata: output.metadata,
83
- svg: output.svg,
84
- type: "svg",
85
- width: output.width
86
- };
87
- }
88
- if (format === "html") {
89
- if (history) throw new Error("history and format=\"html\" cannot be combined");
90
- const output = (0, sfn_diagram.generateHtml)({
91
- aslDefinition: asl,
92
- ...diagramOptions
93
- });
94
- return {
95
- height: output.height,
96
- html: output.html,
97
- metadata: output.metadata,
98
- type: "html",
99
- width: output.width
100
- };
101
- }
102
- if (format === "mermaid") {
103
- const output = history ? (0, sfn_diagram.generateMermaidExecution)({
104
- aslDefinition: asl,
105
- history,
106
- ...diagramOptions
107
- }) : (0, sfn_diagram.generateMermaid)({
108
- aslDefinition: asl,
109
- ...diagramOptions
110
- });
111
- return {
112
- code: output.code,
113
- metadata: output.metadata,
114
- type: "mermaid"
115
- };
116
- }
117
- const output = history ? (0, sfn_diagram.generateExecution)({
118
- aslDefinition: asl,
119
- history,
120
- ...diagramOptions
121
- }) : (0, sfn_diagram.generateSvg)({
122
- aslDefinition: asl,
123
- ...diagramOptions
124
- });
125
- return {
126
- height: output.height,
127
- metadata: output.metadata,
128
- svg: output.svg,
129
- type: "svg",
130
- width: output.width
131
- };
132
- } catch (err) {
133
- return {
134
- error: err instanceof Error ? err : new Error(String(err)),
135
- type: "error"
136
- };
137
- }
138
- }, [
139
- asl,
140
- beforeAsl,
141
- catchHandling,
142
- collapse,
143
- edgeOverrides,
144
- edgeStyle,
145
- format,
146
- history,
147
- iconPosition,
148
- iconSize,
149
- layout,
150
- nodeOverrides,
151
- showIcons,
152
- showVariables,
153
- theme
154
- ]);
155
- }
156
- // src/SfnDiagram.tsx
157
- const SfnDiagram = (0, react.forwardRef)(function SfnDiagram({ className, onError, onStateClick, style, title = "Step Functions diagram", ...diagramParams }, ref) {
158
- const result = useSfnDiagram(diagramParams);
159
- (0, react.useImperativeHandle)(ref, () => ({ getSvg: () => result.type === "svg" ? result.svg : null }), [result]);
160
- const handleStateClick = (0, react.useCallback)((event) => {
161
- const stateId = (event.target instanceof Element ? event.target : null)?.closest("[data-state-id]")?.getAttribute("data-state-id");
162
- if (stateId) onStateClick?.({
163
- event,
164
- stateId
165
- });
166
- }, [onStateClick]);
167
- const reportedResult = (0, react.useRef)(null);
168
- (0, react.useEffect)(() => {
169
- if (result.type !== "error") {
170
- reportedResult.current = null;
171
- return;
172
- }
173
- if (reportedResult.current === result) return;
174
- reportedResult.current = result;
175
- onError?.(result.error);
176
- }, [onError, result]);
177
- if (result.type === "error") return null;
178
- if (result.type === "mermaid") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
179
- className,
180
- style,
181
- children: result.code
182
- });
183
- if (result.type === "html") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
184
- className,
185
- sandbox: "allow-downloads allow-scripts",
186
- srcDoc: result.html,
187
- style,
188
- title
189
- });
190
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
191
- className,
192
- dangerouslySetInnerHTML: { __html: result.svg },
193
- onClick: onStateClick ? handleStateClick : void 0,
194
- style
195
- });
196
- });
197
- exports.SfnDiagram = SfnDiagram;
198
- exports.useSfnDiagram = useSfnDiagram;
package/dist/index.d.cts DELETED
@@ -1,121 +0,0 @@
1
-
2
- import { DiagramOptions, DiffOutput, ExecutionHistoryInput, ExecutionOutput, HtmlOutput, MermaidDiffOutput, MermaidExecutionOutput, MermaidOutput, SvgOutput } from "sfn-diagram";
3
- interface UseSfnDiagramParams extends Pick<DiagramOptions, 'catchHandling' | 'collapse' | 'edgeOverrides' | 'edgeStyle' | 'iconPosition' | 'iconSize' | 'layout' | 'nodeOverrides' | 'showIcons' | 'showVariables' | 'theme'> {
4
- /**
5
- * When set, `definition` is treated as the *after* side of a diff and `before`
6
- * as the *original* side: the rendered diagram highlights added, modified, and
7
- * removed states rather than the plain machine. Accepts an ASL object or a
8
- * JSON string. Cannot be combined with `history` (core's SVG and Mermaid diff
9
- * functions take no history) or with `format="html"` (core's `generateHtml`
10
- * accepts a diff since sfn-diagram 1.8, but this hook does not wire it up yet).
11
- *
12
- * The Mermaid diff ignores every option in this type's `Pick<DiagramOptions, ...>`
13
- * list - `layout`, `theme`, and the rest: this hook doesn't forward them to
14
- * `generateMermaidDiff` yet (#393), although core accepts them since sfn-diagram
15
- * 1.10. The SVG diff honours them.
16
- */
17
- before?: object | string;
18
- definition: object | string;
19
- /**
20
- * Output format. `'svg'` and `'mermaid'` render static markup; `'html'` renders
21
- * the interactive pan/zoom/search viewer as a self-contained HTML document.
22
- * Cannot be combined with `history`.
23
- * @default 'svg'
24
- */
25
- format?: 'html' | 'mermaid' | 'svg';
26
- /**
27
- * Optional execution history. When provided, the diagram is rendered as an
28
- * execution overlay: states are coloured by outcome, the taken path is
29
- * emphasized, and per-state duration / retry counts are annotated.
30
- * Accepts a GetExecutionHistory events array, the raw command output, or a
31
- * JSON string of either. Cannot be combined with `format="html"`.
32
- */
33
- history?: ExecutionHistoryInput;
34
- }
35
- /** Result of {@link useSfnDiagram} - the raw output of whichever core function the params select. */
36
- type SfnDiagramResult = {
37
- error: Error;
38
- type: 'error';
39
- } | {
40
- height: number;
41
- html: string;
42
- metadata: HtmlOutput['metadata'];
43
- type: 'html';
44
- width: number;
45
- } | {
46
- code: string;
47
- metadata: MermaidDiffOutput['metadata'] | MermaidExecutionOutput['metadata'] | MermaidOutput['metadata'];
48
- type: 'mermaid';
49
- } | {
50
- height: number;
51
- metadata: DiffOutput['metadata'] | ExecutionOutput['metadata'] | SvgOutput['metadata'];
52
- svg: string;
53
- type: 'svg';
54
- width: number;
55
- };
56
- /**
57
- * Generate a Step Functions diagram and return the raw output - SVG or HTML
58
- * markup, Mermaid code, dimensions, and metadata - without rendering it.
59
- *
60
- * This is the same generation logic `<SfnDiagram>` runs internally, exposed
61
- * directly for a consumer who needs the underlying string or metadata rather
62
- * than mounted markup - for example, building a data URI for `<img src>`, a
63
- * download button, or a custom render target.
64
- *
65
- * @param params - The same option-bearing props `<SfnDiagram>` accepts, minus
66
- * the presentational ones (`className`, `onError`, `onStateClick`, `style`,
67
- * `title`).
68
- * @returns A {@link SfnDiagramResult} discriminated union. Errors are returned
69
- * as `{ type: 'error', error }` rather than thrown.
70
- *
71
- * @example
72
- * ```tsx
73
- * function DownloadSvgButton({ definition }: { definition: object }) {
74
- * const result = useSfnDiagram({ definition })
75
- * if (result.type !== 'svg') return null
76
- *
77
- * const href = `data:image/svg+xml;base64,${btoa(result.svg)}`
78
- * return <a download="diagram.svg" href={href}>Download SVG</a>
79
- * }
80
- * ```
81
- */
82
- export declare function useSfnDiagram(params: UseSfnDiagramParams): SfnDiagramResult;
83
- interface SfnDiagramProps extends UseSfnDiagramParams {
84
- className?: string;
85
- onError?: (error: Error) => void;
86
- /**
87
- * Called when a state node is clicked. SVG format only - a click inside the
88
- * sandboxed `format="html"` iframe does not reach this handler, and Mermaid
89
- * output has no clickable elements. `stateId` is the graph node id, which
90
- * equals the state name for every state whose name is unique across the
91
- * machine; for a nested state whose name repeats elsewhere in the machine the
92
- * id is qualified by its scope, so read it off the rendered `data-state-id`
93
- * rather than assuming the bare name.
94
- */
95
- onStateClick?: (params: OnStateClickParams) => void;
96
- style?: React.CSSProperties;
97
- /**
98
- * Accessible title for the rendered iframe when `format` is `'html'`. Ignored
99
- * for every other format. Set this when a page renders more than one diagram
100
- * so screen readers can distinguish between them.
101
- * @default 'Step Functions diagram'
102
- */
103
- title?: string;
104
- }
105
- /** Parameters passed to {@link SfnDiagramProps.onStateClick}. */
106
- interface OnStateClickParams {
107
- event: React.MouseEvent<HTMLDivElement>;
108
- stateId: string;
109
- }
110
- /**
111
- * Imperative handle exposed via `ref` on {@link SfnDiagram}. There is no
112
- * programmatic zoom/pan control - core has no public API for it yet (the
113
- * interactive viewer's controller is not exported from a public subpath) -
114
- * this handle only surfaces the rendered markup.
115
- */
116
- interface SfnDiagramHandle {
117
- /** The rendered SVG markup for `format="svg"`, or `null` for every other format (including an errored render). */
118
- getSvg(): string | null;
119
- }
120
- export declare const SfnDiagram: import("react").ForwardRefExoticComponent<SfnDiagramProps & import("react").RefAttributes<SfnDiagramHandle>>;
121
- export type { OnStateClickParams, SfnDiagramHandle, SfnDiagramProps, SfnDiagramResult, UseSfnDiagramParams };