sfn-diagram 0.7.0 → 1.1.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 +61 -1
- package/dist/aws.cjs +3 -1
- package/dist/aws.d.cts +21 -21
- package/dist/aws.d.ts +21 -21
- package/dist/aws.js +3 -1
- package/dist/cfn.cjs +207 -0
- package/dist/cfn.d.cts +102 -0
- package/dist/cfn.d.ts +102 -0
- package/dist/cfn.js +206 -0
- package/dist/cli.js +600 -32
- package/dist/index.cjs +303 -27
- package/dist/index.d.cts +509 -444
- package/dist/index.d.ts +509 -444
- package/dist/index.js +303 -28
- package/dist/png.cjs +177 -9
- package/dist/png.d.cts +119 -82
- package/dist/png.d.ts +119 -82
- package/dist/png.js +177 -9
- package/package.json +36 -11
package/README.md
CHANGED
|
@@ -95,6 +95,10 @@ browser (`node-html-to-image`) and Node's filesystem respectively. Because `node
|
|
|
95
95
|
is an **optional peer dependency**, install it alongside `sfn-diagram` when you need PNG output;
|
|
96
96
|
`exportPng` throws an actionable error if it is missing.
|
|
97
97
|
|
|
98
|
+
**Node versions:** the package requires **Node >= 20**. PNG export additionally requires
|
|
99
|
+
**Node >= 22.12.0**, the floor set by `node-html-to-image` v6 — SVG, Mermaid, and HTML
|
|
100
|
+
output are unaffected and keep working on Node 20.
|
|
101
|
+
|
|
98
102
|
```ts
|
|
99
103
|
// Works in Node, browser, and edge:
|
|
100
104
|
import { generateSvg } from 'sfn-diagram';
|
|
@@ -113,7 +117,11 @@ npx sfn-diagram state.asl.json --format mermaid > diagram.mmd
|
|
|
113
117
|
cat state.asl.json | npx sfn-diagram - --format svg
|
|
114
118
|
```
|
|
115
119
|
|
|
116
|
-
Flags: `--format <svg|mermaid|png>`, `-o/--output <path>`, `--theme <light|dark>`, `--layout <TB|LR|RL|BT>`, `-h/--help`, `-v/--version`.
|
|
120
|
+
Flags: `--format <svg|mermaid|png|html>`, `-o/--output <path>`, `--theme <light|dark>`, `--layout <TB|LR|RL|BT>`, `--hide-catch`, `--resolve-cfn`, `--resource <logicalId>`, `-h/--help`, `-v/--version`.
|
|
121
|
+
|
|
122
|
+
CloudFormation/SAM/CDK templates work as input too — see [Extracting ASL from a CDK/CloudFormation template](#extracting-asl-from-a-cdkcloudformation-template).
|
|
123
|
+
|
|
124
|
+
> **`--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.
|
|
117
125
|
|
|
118
126
|
## Docker
|
|
119
127
|
|
|
@@ -273,6 +281,35 @@ const { svg } = generateFromAwsResponse({
|
|
|
273
281
|
});
|
|
274
282
|
```
|
|
275
283
|
|
|
284
|
+
### Extracting ASL from a CDK/CloudFormation template
|
|
285
|
+
|
|
286
|
+
`cdk synth` emits a state machine whose `DefinitionString` is an `Fn::Join` full
|
|
287
|
+
of intrinsics, not plain ASL. The `sfn-diagram/cfn` subpath flattens it:
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
import { extractAslFromTemplate } from 'sfn-diagram/cfn';
|
|
291
|
+
import { generateMermaid } from 'sfn-diagram';
|
|
292
|
+
|
|
293
|
+
const { aslDefinition, warnings } = extractAslFromTemplate({ template: cdkSynthJson });
|
|
294
|
+
const { code } = generateMermaid({ aslDefinition });
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Both JSON and YAML templates are supported (CloudFormation short-form tags like
|
|
298
|
+
`!Sub` / `!GetAtt` included), and `DefinitionSubstitutions` are applied. When a
|
|
299
|
+
template contains more than one state machine, pass `resourceId` to pick one.
|
|
300
|
+
|
|
301
|
+
CLI (JSON templates auto-detect; use `--resolve-cfn` for YAML):
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
cdk synth > template.json
|
|
305
|
+
npx sfn-diagram template.json --format mermaid
|
|
306
|
+
npx sfn-diagram template.yaml --resolve-cfn --resource MyMachine --format svg -o out.svg
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Intrinsics that don't affect the flow become readable placeholders
|
|
310
|
+
(`${AWS::Partition}`, `<Ref:LogicalId>`). External `DefinitionUri` definitions
|
|
311
|
+
are not supported — pass a template with an inline definition.
|
|
312
|
+
|
|
276
313
|
### generateMermaidDiff(params) / generateDiff(params)
|
|
277
314
|
|
|
278
315
|
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.
|
|
@@ -475,6 +512,29 @@ generateSvg({ aslDefinition: asl, theme: customTheme });
|
|
|
475
512
|
- `'straight'` - Direct straight lines
|
|
476
513
|
- `'orthogonal'` - Right-angled paths
|
|
477
514
|
|
|
515
|
+
### Large diagrams
|
|
516
|
+
|
|
517
|
+
Big, branchy state machines are hard to read as a static image. A few options help:
|
|
518
|
+
|
|
519
|
+
- **`--format html`** (or `generateHtml()`) — a self-contained interactive viewer
|
|
520
|
+
(drag to pan, wheel to zoom, fit/reset toolbar). No external dependencies, opens
|
|
521
|
+
offline straight from `file://`.
|
|
522
|
+
```bash
|
|
523
|
+
npx sfn-diagram state.asl.json --format html -o diagram.html
|
|
524
|
+
```
|
|
525
|
+
> **Known limitation:** if you also pass `showIcons: true`, the embedded SVG
|
|
526
|
+
> references AWS service icons hosted on a jsDelivr CDN (see
|
|
527
|
+
> [AWS Service Icons](#aws-service-icons) below), so the HTML is no longer fully
|
|
528
|
+
> offline — icons won't load without network access.
|
|
529
|
+
- **`--hide-catch`** (or `catchHandling: 'hide'`) — drop per-state error-handler
|
|
530
|
+
(`Catch`) branches so the happy path stands out. A handler that's also reachable
|
|
531
|
+
via the happy path is kept.
|
|
532
|
+
```bash
|
|
533
|
+
npx sfn-diagram state.asl.json --hide-catch --format svg -o diagram.svg
|
|
534
|
+
```
|
|
535
|
+
- **`--layout LR`** (or `layout: 'LR'`) — the default `TB` layout makes catch-heavy
|
|
536
|
+
or deeply branching machines extremely tall; `LR` reads better for wide graphs.
|
|
537
|
+
|
|
478
538
|
### AWS Service Icons
|
|
479
539
|
|
|
480
540
|
Display AWS service icons on Task state nodes to improve diagram readability and quickly identify which AWS services are being used.
|
package/dist/aws.cjs
CHANGED
|
@@ -2,7 +2,9 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
let _aws_sdk_client_sfn = require("@aws-sdk/client-sfn");
|
|
3
3
|
//#region src/aws.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* @module
|
|
6
|
+
*
|
|
7
|
+
* Node-only AWS convenience helpers for `sfn-diagram` (the `sfn-diagram/aws` subpath).
|
|
6
8
|
*
|
|
7
9
|
* This subpath (`sfn-diagram/aws`) is isolated from the core entry so that
|
|
8
10
|
* importing `sfn-diagram` never pulls in `@aws-sdk/client-sfn`, keeping the core
|
package/dist/aws.d.cts
CHANGED
|
@@ -10,27 +10,27 @@ interface FetchExecutionHistoryParams {
|
|
|
10
10
|
maxResults?: number;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
34
|
declare function fetchExecutionHistory(params: FetchExecutionHistoryParams): Promise<HistoryEvent[]>;
|
|
35
35
|
//#endregion
|
|
36
36
|
export { FetchExecutionHistoryParams, fetchExecutionHistory };
|
package/dist/aws.d.ts
CHANGED
|
@@ -10,27 +10,27 @@ interface FetchExecutionHistoryParams {
|
|
|
10
10
|
maxResults?: number;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
34
|
declare function fetchExecutionHistory(params: FetchExecutionHistoryParams): Promise<HistoryEvent[]>;
|
|
35
35
|
//#endregion
|
|
36
36
|
export { FetchExecutionHistoryParams, fetchExecutionHistory };
|
package/dist/aws.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { GetExecutionHistoryCommand } from "@aws-sdk/client-sfn";
|
|
2
2
|
//#region src/aws.ts
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* @module
|
|
5
|
+
*
|
|
6
|
+
* Node-only AWS convenience helpers for `sfn-diagram` (the `sfn-diagram/aws` subpath).
|
|
5
7
|
*
|
|
6
8
|
* This subpath (`sfn-diagram/aws`) is isolated from the core entry so that
|
|
7
9
|
* importing `sfn-diagram` never pulls in `@aws-sdk/client-sfn`, keeping the core
|
package/dist/cfn.cjs
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let yaml = require("yaml");
|
|
3
|
+
//#region src/cfn/intrinsics.ts
|
|
4
|
+
function isPseudoParam(name) {
|
|
5
|
+
return name.startsWith("AWS::");
|
|
6
|
+
}
|
|
7
|
+
function substitute(template, substitutions) {
|
|
8
|
+
return template.replace(/\$\{([^}]+)\}/g, (match, name) => {
|
|
9
|
+
if (isPseudoParam(name)) return match;
|
|
10
|
+
if (name in substitutions) return substitutions[name];
|
|
11
|
+
return match;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Replaces every CloudFormation intrinsic in a parsed template value with a
|
|
16
|
+
* readable placeholder string, so the result can be parsed as ASL.
|
|
17
|
+
*
|
|
18
|
+
* `Fn::Join` is concatenated, `Fn::Sub` variables are filled from the
|
|
19
|
+
* substitutions map, `Ref`/`Fn::GetAtt` become `<Ref:Id>` / `<Res.Attr>`, and
|
|
20
|
+
* pseudo-parameters stay as `${AWS::Partition}`-style tokens. Anything else is
|
|
21
|
+
* stubbed as `<Fn::Name>` and reported in `warnings`.
|
|
22
|
+
*
|
|
23
|
+
* @param params - The value to resolve plus optional DefinitionSubstitutions.
|
|
24
|
+
* @returns The resolved value and any non-fatal warnings.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const { value } = resolveIntrinsics({ value: { Ref: 'AWS::Partition' } });
|
|
29
|
+
* // value === '${AWS::Partition}'
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function resolveIntrinsics(params) {
|
|
33
|
+
const { substitutions = {}, value } = params;
|
|
34
|
+
const warnings = [];
|
|
35
|
+
function walk(current) {
|
|
36
|
+
if (Array.isArray(current)) return current.map(walk);
|
|
37
|
+
if (current === null || typeof current !== "object") return current;
|
|
38
|
+
const objectKeys = Object.keys(current);
|
|
39
|
+
if (objectKeys.length === 1) {
|
|
40
|
+
const key = objectKeys[0];
|
|
41
|
+
const inner = current[key];
|
|
42
|
+
if (key === "Ref" && typeof inner === "string") return isPseudoParam(inner) ? `\${${inner}}` : `<Ref:${inner}>`;
|
|
43
|
+
if (key === "Fn::GetAtt") return `<${(Array.isArray(inner) ? inner : String(inner).split(".")).join(".")}>`;
|
|
44
|
+
if (key === "Fn::Join") {
|
|
45
|
+
const [delimiter, parts] = inner;
|
|
46
|
+
return parts.map((part) => String(walk(part))).join(delimiter);
|
|
47
|
+
}
|
|
48
|
+
if (key === "Fn::Sub") {
|
|
49
|
+
if (typeof inner === "string") return substitute(inner, substitutions);
|
|
50
|
+
if (Array.isArray(inner)) {
|
|
51
|
+
const [subTemplate, localMap] = inner;
|
|
52
|
+
const localResolved = { ...substitutions };
|
|
53
|
+
for (const localKey of Object.keys(localMap)) localResolved[localKey] = String(walk(localMap[localKey]));
|
|
54
|
+
return substitute(subTemplate, localResolved);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (key.startsWith("Fn::")) {
|
|
58
|
+
warnings.push(`Unresolved intrinsic ${key} replaced with placeholder`);
|
|
59
|
+
return `<${key}>`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const output = {};
|
|
63
|
+
for (const key of objectKeys) output[key] = walk(current[key]);
|
|
64
|
+
return output;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
value: walk(value),
|
|
68
|
+
warnings
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/cfn/templateParser.ts
|
|
73
|
+
/**
|
|
74
|
+
* CloudFormation short-form YAML tags, mapped to the JSON long form the rest of
|
|
75
|
+
* the pipeline understands. `!Ref x` becomes `{ Ref: 'x' }`; every `Fn::*` tag
|
|
76
|
+
* becomes `{ 'Fn::Name': value }`.
|
|
77
|
+
*/
|
|
78
|
+
const CFN_TAG_NAMES = [
|
|
79
|
+
"Ref",
|
|
80
|
+
"Base64",
|
|
81
|
+
"Cidr",
|
|
82
|
+
"FindInMap",
|
|
83
|
+
"GetAtt",
|
|
84
|
+
"GetAZs",
|
|
85
|
+
"ImportValue",
|
|
86
|
+
"Join",
|
|
87
|
+
"Select",
|
|
88
|
+
"Split",
|
|
89
|
+
"Sub",
|
|
90
|
+
"Transform",
|
|
91
|
+
"And",
|
|
92
|
+
"Equals",
|
|
93
|
+
"If",
|
|
94
|
+
"Not",
|
|
95
|
+
"Or",
|
|
96
|
+
"Condition"
|
|
97
|
+
];
|
|
98
|
+
function longFormKey(tagName) {
|
|
99
|
+
return tagName === "Ref" || tagName === "Condition" ? tagName : `Fn::${tagName}`;
|
|
100
|
+
}
|
|
101
|
+
function buildCfnTags() {
|
|
102
|
+
const tags = [];
|
|
103
|
+
for (const tagName of CFN_TAG_NAMES) {
|
|
104
|
+
const toLongForm = (value) => {
|
|
105
|
+
if (tagName === "GetAtt" && typeof value === "string") return { "Fn::GetAtt": value.split(".") };
|
|
106
|
+
return { [longFormKey(tagName)]: value };
|
|
107
|
+
};
|
|
108
|
+
tags.push({
|
|
109
|
+
identify: () => false,
|
|
110
|
+
resolve: (value) => toLongForm(value),
|
|
111
|
+
tag: `!${tagName}`
|
|
112
|
+
});
|
|
113
|
+
tags.push({
|
|
114
|
+
collection: "seq",
|
|
115
|
+
identify: () => false,
|
|
116
|
+
resolve: (value) => toLongForm(value.toJSON()),
|
|
117
|
+
tag: `!${tagName}`
|
|
118
|
+
});
|
|
119
|
+
tags.push({
|
|
120
|
+
collection: "map",
|
|
121
|
+
identify: () => false,
|
|
122
|
+
resolve: (value) => toLongForm(value.toJSON()),
|
|
123
|
+
tag: `!${tagName}`
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return tags;
|
|
127
|
+
}
|
|
128
|
+
const CFN_TAGS = buildCfnTags();
|
|
129
|
+
function looksLikeJson(text) {
|
|
130
|
+
return text.trimStart().startsWith("{");
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Parses a CloudFormation/SAM/CDK template into a plain object.
|
|
134
|
+
*
|
|
135
|
+
* JSON is parsed with `JSON.parse`; YAML is parsed with CloudFormation
|
|
136
|
+
* short-form intrinsic tags normalized to their JSON long form, so downstream
|
|
137
|
+
* code only has to understand `{ Ref: ... }` / `{ 'Fn::Sub': ... }` shapes.
|
|
138
|
+
*
|
|
139
|
+
* @param params - Template source and optional explicit format.
|
|
140
|
+
* @returns The parsed template object.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const template = parseTemplate({ template: yamlSource, format: 'yaml' });
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
function parseTemplate(params) {
|
|
148
|
+
const { format = "auto", template } = params;
|
|
149
|
+
if (typeof template !== "string") return template;
|
|
150
|
+
if (format === "json" || format === "auto" && looksLikeJson(template)) return JSON.parse(template);
|
|
151
|
+
return (0, yaml.parse)(template, { customTags: CFN_TAGS });
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/cfn/extract.ts
|
|
155
|
+
const STATE_MACHINE_TYPE = "AWS::StepFunctions::StateMachine";
|
|
156
|
+
function findStateMachineIds(resources) {
|
|
157
|
+
return Object.keys(resources).filter((logicalId) => resources[logicalId]?.Type === STATE_MACHINE_TYPE);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Recovers a renderable ASL definition from a CloudFormation/SAM/CDK template.
|
|
161
|
+
*
|
|
162
|
+
* Locates the `AWS::StepFunctions::StateMachine` resource (disambiguated with
|
|
163
|
+
* `resourceId` when the template has more than one), flattens the intrinsics in
|
|
164
|
+
* its `DefinitionString`/`Definition`, applies `DefinitionSubstitutions`, and
|
|
165
|
+
* parses the result as ASL.
|
|
166
|
+
*
|
|
167
|
+
* @param params - Template source, optional format, optional resource id.
|
|
168
|
+
* @returns The extracted ASL definition, the logical id used, and any warnings.
|
|
169
|
+
* @throws If no state machine is found, the choice is ambiguous, or the
|
|
170
|
+
* definition is external (`DefinitionUri`).
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* const { aslDefinition } = extractAslFromTemplate({ template: cdkSynthJson });
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
function extractAslFromTemplate(params) {
|
|
178
|
+
const { format = "auto", resourceId, template } = params;
|
|
179
|
+
const resources = parseTemplate({
|
|
180
|
+
format,
|
|
181
|
+
template
|
|
182
|
+
}).Resources ?? {};
|
|
183
|
+
const machineIds = findStateMachineIds(resources);
|
|
184
|
+
if (machineIds.length === 0) throw new Error(`Template contains no ${STATE_MACHINE_TYPE} resource.`);
|
|
185
|
+
let chosenId;
|
|
186
|
+
if (resourceId) {
|
|
187
|
+
if (!machineIds.includes(resourceId)) throw new Error(`Resource '${resourceId}' is not an ${STATE_MACHINE_TYPE}. Found: ${machineIds.join(", ")}.`);
|
|
188
|
+
chosenId = resourceId;
|
|
189
|
+
} else if (machineIds.length === 1) chosenId = machineIds[0];
|
|
190
|
+
else throw new Error(`Template has multiple state machines (${machineIds.join(", ")}). Pass resourceId (or --resource) to choose one.`);
|
|
191
|
+
const properties = resources[chosenId].Properties ?? {};
|
|
192
|
+
if ("DefinitionUri" in properties && !("DefinitionString" in properties) && !("Definition" in properties)) throw new Error(`State machine '${chosenId}' uses an external DefinitionUri, which is not supported. Pass a template with an inline DefinitionString or Definition.`);
|
|
193
|
+
const substitutions = properties.DefinitionSubstitutions ?? {};
|
|
194
|
+
const rawDefinition = properties.DefinitionString ?? properties.Definition;
|
|
195
|
+
if (rawDefinition === void 0) throw new Error(`State machine '${chosenId}' has no DefinitionString or Definition.`);
|
|
196
|
+
const { value: resolved, warnings } = resolveIntrinsics({
|
|
197
|
+
substitutions,
|
|
198
|
+
value: rawDefinition
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
aslDefinition: typeof resolved === "string" ? JSON.parse(resolved) : resolved,
|
|
202
|
+
resourceId: chosenId,
|
|
203
|
+
warnings
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
exports.extractAslFromTemplate = extractAslFromTemplate;
|
package/dist/cfn.d.cts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//#region src/types/index.d.ts
|
|
2
|
+
/** The set of Amazon States Language state types a state machine can contain. */
|
|
3
|
+
type StateType = "Pass" | "Task" | "Choice" | "Wait" | "Succeed" | "Fail" | "Parallel" | "Map";
|
|
4
|
+
/** An ASL `Catch` handler: routes matching errors to a fallback state. */
|
|
5
|
+
interface CatchBlock {
|
|
6
|
+
ErrorEquals: string[];
|
|
7
|
+
Next?: string;
|
|
8
|
+
ResultPath?: string;
|
|
9
|
+
}
|
|
10
|
+
/** An ASL `Retry` policy: re-attempts a state on matching errors with backoff. */
|
|
11
|
+
interface RetryBlock {
|
|
12
|
+
BackoffRate?: number;
|
|
13
|
+
ErrorEquals: string[];
|
|
14
|
+
IntervalSeconds?: number;
|
|
15
|
+
MaxAttempts?: number;
|
|
16
|
+
}
|
|
17
|
+
/** A single rule in a Choice state: a condition plus the `Next` state to take when it matches. */
|
|
18
|
+
interface ChoiceRule {
|
|
19
|
+
BooleanEquals?: boolean;
|
|
20
|
+
Next: string;
|
|
21
|
+
NumericEquals?: number;
|
|
22
|
+
StringEquals?: string;
|
|
23
|
+
Variable?: string;
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}
|
|
26
|
+
/** A single state within an ASL definition, covering fields for every state type. */
|
|
27
|
+
interface AslState {
|
|
28
|
+
Assign?: Record<string, any>;
|
|
29
|
+
Branches?: AslDefinition[];
|
|
30
|
+
Catch?: CatchBlock[];
|
|
31
|
+
Cause?: string;
|
|
32
|
+
Choices?: ChoiceRule[];
|
|
33
|
+
Comment?: string;
|
|
34
|
+
Default?: string;
|
|
35
|
+
End?: boolean;
|
|
36
|
+
Error?: string;
|
|
37
|
+
ItemProcessor?: AslDefinition;
|
|
38
|
+
Iterator?: AslDefinition;
|
|
39
|
+
Next?: string;
|
|
40
|
+
Output?: any;
|
|
41
|
+
Parameters?: Record<string, any>;
|
|
42
|
+
QueryLanguage?: "JSONata" | "JSONPath";
|
|
43
|
+
Resource?: string;
|
|
44
|
+
Result?: any;
|
|
45
|
+
ResultPath?: string;
|
|
46
|
+
Retry?: RetryBlock[];
|
|
47
|
+
Seconds?: number | string;
|
|
48
|
+
SecondsPath?: string;
|
|
49
|
+
Timestamp?: string;
|
|
50
|
+
TimestampPath?: string;
|
|
51
|
+
Type: StateType;
|
|
52
|
+
}
|
|
53
|
+
/** A complete Amazon States Language state machine definition. */
|
|
54
|
+
interface AslDefinition {
|
|
55
|
+
Comment?: string;
|
|
56
|
+
QueryLanguage?: "JSONata" | "JSONPath";
|
|
57
|
+
StartAt: string;
|
|
58
|
+
States: Record<string, AslState>;
|
|
59
|
+
TimeoutSeconds?: number;
|
|
60
|
+
Version?: string;
|
|
61
|
+
}
|
|
62
|
+
/** Parameters for `extractAslFromTemplate` (from the `sfn-diagram/cfn` subpath). */
|
|
63
|
+
interface ExtractAslFromTemplateParams {
|
|
64
|
+
/** Input format. 'auto' (default) sniffs JSON vs YAML. */
|
|
65
|
+
format?: "auto" | "json" | "yaml";
|
|
66
|
+
/** Logical id of the state machine to extract when the template has more than one. */
|
|
67
|
+
resourceId?: string;
|
|
68
|
+
/** Raw template string (JSON or YAML) or an already-parsed template object. */
|
|
69
|
+
template: string | Record<string, unknown>;
|
|
70
|
+
}
|
|
71
|
+
/** Result of extracting an ASL definition from a CloudFormation/SAM template. */
|
|
72
|
+
interface ExtractAslResult {
|
|
73
|
+
/** The recovered ASL definition, ready for generateSvg/generateMermaid/etc. */
|
|
74
|
+
aslDefinition: AslDefinition;
|
|
75
|
+
/** Logical id of the state machine that was extracted. */
|
|
76
|
+
resourceId: string;
|
|
77
|
+
/** Non-fatal notes (e.g. unresolved intrinsics replaced with placeholders). */
|
|
78
|
+
warnings: string[];
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/cfn/extract.d.ts
|
|
82
|
+
/**
|
|
83
|
+
* Recovers a renderable ASL definition from a CloudFormation/SAM/CDK template.
|
|
84
|
+
*
|
|
85
|
+
* Locates the `AWS::StepFunctions::StateMachine` resource (disambiguated with
|
|
86
|
+
* `resourceId` when the template has more than one), flattens the intrinsics in
|
|
87
|
+
* its `DefinitionString`/`Definition`, applies `DefinitionSubstitutions`, and
|
|
88
|
+
* parses the result as ASL.
|
|
89
|
+
*
|
|
90
|
+
* @param params - Template source, optional format, optional resource id.
|
|
91
|
+
* @returns The extracted ASL definition, the logical id used, and any warnings.
|
|
92
|
+
* @throws If no state machine is found, the choice is ambiguous, or the
|
|
93
|
+
* definition is external (`DefinitionUri`).
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const { aslDefinition } = extractAslFromTemplate({ template: cdkSynthJson });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function extractAslFromTemplate(params: ExtractAslFromTemplateParams): ExtractAslResult;
|
|
101
|
+
//#endregion
|
|
102
|
+
export { type ExtractAslFromTemplateParams, type ExtractAslResult, extractAslFromTemplate };
|
package/dist/cfn.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//#region src/types/index.d.ts
|
|
2
|
+
/** The set of Amazon States Language state types a state machine can contain. */
|
|
3
|
+
type StateType = "Pass" | "Task" | "Choice" | "Wait" | "Succeed" | "Fail" | "Parallel" | "Map";
|
|
4
|
+
/** An ASL `Catch` handler: routes matching errors to a fallback state. */
|
|
5
|
+
interface CatchBlock {
|
|
6
|
+
ErrorEquals: string[];
|
|
7
|
+
Next?: string;
|
|
8
|
+
ResultPath?: string;
|
|
9
|
+
}
|
|
10
|
+
/** An ASL `Retry` policy: re-attempts a state on matching errors with backoff. */
|
|
11
|
+
interface RetryBlock {
|
|
12
|
+
BackoffRate?: number;
|
|
13
|
+
ErrorEquals: string[];
|
|
14
|
+
IntervalSeconds?: number;
|
|
15
|
+
MaxAttempts?: number;
|
|
16
|
+
}
|
|
17
|
+
/** A single rule in a Choice state: a condition plus the `Next` state to take when it matches. */
|
|
18
|
+
interface ChoiceRule {
|
|
19
|
+
BooleanEquals?: boolean;
|
|
20
|
+
Next: string;
|
|
21
|
+
NumericEquals?: number;
|
|
22
|
+
StringEquals?: string;
|
|
23
|
+
Variable?: string;
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
}
|
|
26
|
+
/** A single state within an ASL definition, covering fields for every state type. */
|
|
27
|
+
interface AslState {
|
|
28
|
+
Assign?: Record<string, any>;
|
|
29
|
+
Branches?: AslDefinition[];
|
|
30
|
+
Catch?: CatchBlock[];
|
|
31
|
+
Cause?: string;
|
|
32
|
+
Choices?: ChoiceRule[];
|
|
33
|
+
Comment?: string;
|
|
34
|
+
Default?: string;
|
|
35
|
+
End?: boolean;
|
|
36
|
+
Error?: string;
|
|
37
|
+
ItemProcessor?: AslDefinition;
|
|
38
|
+
Iterator?: AslDefinition;
|
|
39
|
+
Next?: string;
|
|
40
|
+
Output?: any;
|
|
41
|
+
Parameters?: Record<string, any>;
|
|
42
|
+
QueryLanguage?: "JSONata" | "JSONPath";
|
|
43
|
+
Resource?: string;
|
|
44
|
+
Result?: any;
|
|
45
|
+
ResultPath?: string;
|
|
46
|
+
Retry?: RetryBlock[];
|
|
47
|
+
Seconds?: number | string;
|
|
48
|
+
SecondsPath?: string;
|
|
49
|
+
Timestamp?: string;
|
|
50
|
+
TimestampPath?: string;
|
|
51
|
+
Type: StateType;
|
|
52
|
+
}
|
|
53
|
+
/** A complete Amazon States Language state machine definition. */
|
|
54
|
+
interface AslDefinition {
|
|
55
|
+
Comment?: string;
|
|
56
|
+
QueryLanguage?: "JSONata" | "JSONPath";
|
|
57
|
+
StartAt: string;
|
|
58
|
+
States: Record<string, AslState>;
|
|
59
|
+
TimeoutSeconds?: number;
|
|
60
|
+
Version?: string;
|
|
61
|
+
}
|
|
62
|
+
/** Parameters for `extractAslFromTemplate` (from the `sfn-diagram/cfn` subpath). */
|
|
63
|
+
interface ExtractAslFromTemplateParams {
|
|
64
|
+
/** Input format. 'auto' (default) sniffs JSON vs YAML. */
|
|
65
|
+
format?: "auto" | "json" | "yaml";
|
|
66
|
+
/** Logical id of the state machine to extract when the template has more than one. */
|
|
67
|
+
resourceId?: string;
|
|
68
|
+
/** Raw template string (JSON or YAML) or an already-parsed template object. */
|
|
69
|
+
template: string | Record<string, unknown>;
|
|
70
|
+
}
|
|
71
|
+
/** Result of extracting an ASL definition from a CloudFormation/SAM template. */
|
|
72
|
+
interface ExtractAslResult {
|
|
73
|
+
/** The recovered ASL definition, ready for generateSvg/generateMermaid/etc. */
|
|
74
|
+
aslDefinition: AslDefinition;
|
|
75
|
+
/** Logical id of the state machine that was extracted. */
|
|
76
|
+
resourceId: string;
|
|
77
|
+
/** Non-fatal notes (e.g. unresolved intrinsics replaced with placeholders). */
|
|
78
|
+
warnings: string[];
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/cfn/extract.d.ts
|
|
82
|
+
/**
|
|
83
|
+
* Recovers a renderable ASL definition from a CloudFormation/SAM/CDK template.
|
|
84
|
+
*
|
|
85
|
+
* Locates the `AWS::StepFunctions::StateMachine` resource (disambiguated with
|
|
86
|
+
* `resourceId` when the template has more than one), flattens the intrinsics in
|
|
87
|
+
* its `DefinitionString`/`Definition`, applies `DefinitionSubstitutions`, and
|
|
88
|
+
* parses the result as ASL.
|
|
89
|
+
*
|
|
90
|
+
* @param params - Template source, optional format, optional resource id.
|
|
91
|
+
* @returns The extracted ASL definition, the logical id used, and any warnings.
|
|
92
|
+
* @throws If no state machine is found, the choice is ambiguous, or the
|
|
93
|
+
* definition is external (`DefinitionUri`).
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const { aslDefinition } = extractAslFromTemplate({ template: cdkSynthJson });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
declare function extractAslFromTemplate(params: ExtractAslFromTemplateParams): ExtractAslResult;
|
|
101
|
+
//#endregion
|
|
102
|
+
export { type ExtractAslFromTemplateParams, type ExtractAslResult, extractAslFromTemplate };
|