aws-local-stepfunctions 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 +105 -8
- package/{build → bin}/CLI.cjs +56 -6
- package/build/main.browser.esm.js +8508 -7571
- package/build/main.d.ts +193 -24
- package/build/main.node.cjs +679 -183
- package/build/main.node.esm.js +679 -183
- package/package.json +23 -18
package/README.md
CHANGED
|
@@ -2,11 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
A TypeScript implementation of the [Amazon States Language specification](https://states-language.net/spec.html).
|
|
4
4
|
|
|
5
|
-
This package lets you run AWS Step Functions completely locally, both in Node.js and in the browser!
|
|
5
|
+
This package lets you run AWS Step Functions state machines completely locally, both in Node.js and in the browser!
|
|
6
|
+
|
|
7
|
+
<p align="left">
|
|
8
|
+
<a href="/LICENSE">
|
|
9
|
+
<img alt="Project license (MIT)" src="https://img.shields.io/github/license/nibble-4bits/aws-local-stepfunctions">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://github.com/nibble-4bits/aws-local-stepfunctions/actions/workflows/pr-run-tests.yml">
|
|
12
|
+
<img alt="GitHub Workflow Status (with event)" src="https://img.shields.io/github/actions/workflow/status/nibble-4bits/aws-local-stepfunctions/pr-run-tests.yml">
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/aws-local-stepfunctions">
|
|
15
|
+
<img alt="Latest npm version" src="https://img.shields.io/npm/v/aws-local-stepfunctions">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://www.npmjs.com/package/aws-local-stepfunctions">
|
|
18
|
+
<img alt="node-lts" src="https://img.shields.io/node/v-lts/aws-local-stepfunctions">
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://www.npmjs.com/package/aws-local-stepfunctions">
|
|
21
|
+
<img alt="npm type definitions" src="https://img.shields.io/npm/types/aws-local-stepfunctions">
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
6
24
|
|
|
7
25
|
## Table of contents
|
|
8
26
|
|
|
9
27
|
- [Features](#features)
|
|
28
|
+
- [Use cases](#use-cases)
|
|
10
29
|
- [Installation](#installation)
|
|
11
30
|
- [Importing](#importing)
|
|
12
31
|
- [Node.js](#nodejs)
|
|
@@ -22,6 +41,7 @@ This package lets you run AWS Step Functions completely locally, both in Node.js
|
|
|
22
41
|
- [Overriding Task and Wait states](#overriding-task-and-wait-states)
|
|
23
42
|
- [Task state override](#task-state-override)
|
|
24
43
|
- [Wait state override](#wait-state-override)
|
|
44
|
+
- [Passing a custom Context Object](#passing-a-custom-context-object)
|
|
25
45
|
- [Disabling ASL validations](#disabling-asl-validations)
|
|
26
46
|
- [Exit codes](#exit-codes)
|
|
27
47
|
- [Examples](#examples)
|
|
@@ -31,6 +51,16 @@ This package lets you run AWS Step Functions completely locally, both in Node.js
|
|
|
31
51
|
|
|
32
52
|
To see the list of features defined in the specification that have full support, partial support, or no support, refer to [this document](/docs/feature-support.md).
|
|
33
53
|
|
|
54
|
+
## Use cases
|
|
55
|
+
|
|
56
|
+
Why would you want to use this package? Below is a non-exhaustive list of use cases for `aws-local-stepfunctions`:
|
|
57
|
+
|
|
58
|
+
- Testing state machines changes locally before deploying them to AWS.
|
|
59
|
+
- Testing the integration between a state machine and the Lambda functions associated with it in `Task` states.
|
|
60
|
+
- Debugging the code of associated Lambda functions interactively using the [`Task` state resource override feature](/docs/feature-support.md#task-state-resource-override).
|
|
61
|
+
- Debugging a state machine by using the [event logs feature](/docs/feature-support.md#execution-event-logs), to better understand the transitions between states and how data flows between them.
|
|
62
|
+
- Running state machines in the browser (not possible with [AWS Step Functions Local](https://docs.aws.amazon.com/step-functions/latest/dg/sfn-local.html)).
|
|
63
|
+
|
|
34
64
|
## Installation
|
|
35
65
|
|
|
36
66
|
```sh
|
|
@@ -84,6 +114,8 @@ The constructor takes the following parameters:
|
|
|
84
114
|
- `validationOptions?`: An object that specifies how the definition should be validated.
|
|
85
115
|
- `checkPaths`: If set to `false`, won't validate JSONPaths.
|
|
86
116
|
- `checkArn`: If set to `false`, won't validate ARN syntax in `Task` states.
|
|
117
|
+
- `noValidate`: If set to `true`, will skip validation of the definition entirely.
|
|
118
|
+
> NOTE: Use this option at your own risk, there are no guarantees when passing an invalid or non-standard definition to the state machine. Running it might result in undefined/unsupported behavior.
|
|
87
119
|
- `awsConfig?`: An object that specifies the [AWS region and credentials](/docs/feature-support.md#providing-aws-credentials-and-region-to-execute-lambda-functions-specified-in-task-states) to use when invoking a Lambda function in a `Task` state. If not set, the AWS config will be resolved based on the [credentials provider chain](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html) of the AWS SDK for JavaScript V3. You don't need to use this option if you have a [shared config/credentials file](https://docs.aws.amazon.com/sdkref/latest/guide/file-format.html) (for example, if you have the [AWS CLI](https://aws.amazon.com/cli/) installed) or if you use a local override for all of your `Task` states.
|
|
88
120
|
- `region`: The AWS region where the Lambda functions are created.
|
|
89
121
|
- `credentials`: An object that specifies which type of credentials to use.
|
|
@@ -117,10 +149,7 @@ const stateMachine = new StateMachine(machineDefinition, {
|
|
|
117
149
|
|
|
118
150
|
### `StateMachine.run(input[, options])`
|
|
119
151
|
|
|
120
|
-
Runs the state machine with the given `input
|
|
121
|
-
|
|
122
|
-
- `abort`: A function that takes no parameters and doesn't return any value. If called, [aborts the execution](/docs/feature-support.md#abort-a-running-execution) and throws an `ExecutionAbortedError`, unless the `noThrowOnAbort` option is set.
|
|
123
|
-
- `result`: A `Promise` that resolves with the execution result once it finishes.
|
|
152
|
+
Runs the state machine with the given `input`.
|
|
124
153
|
|
|
125
154
|
Each execution is independent of all others, meaning that you can concurrently call this method as many times as needed, without worrying about race conditions.
|
|
126
155
|
|
|
@@ -132,6 +161,15 @@ Each execution is independent of all others, meaning that you can concurrently c
|
|
|
132
161
|
- `taskResourceLocalHandlers?`: An [object that overrides](/docs/feature-support.md#task-state-resource-override) the resource of the specified `Task` states to run a local function.
|
|
133
162
|
- `waitTimeOverrides?`: An [object that overrides](/docs/feature-support.md#wait-state-duration-override) the wait duration of the specified `Wait` states. The specified override duration should be in milliseconds.
|
|
134
163
|
- `noThrowOnAbort?`: If this option is set to `true`, aborting the execution will simply return `null` as result instead of throwing.
|
|
164
|
+
- `context?`: An object that will be used as the [Context Object](https://docs.aws.amazon.com/step-functions/latest/dg/input-output-contextobject.html) for the execution. If not passed, the Context Object will default to an empty object. This option is useful to mock the Context Object in case your definition references it in a JSONPath.
|
|
165
|
+
|
|
166
|
+
#### Return value
|
|
167
|
+
|
|
168
|
+
Returns an object that has the following properties:
|
|
169
|
+
|
|
170
|
+
- `result`: A `Promise` that resolves with the result of the execution, if it ends successfully.
|
|
171
|
+
- `abort`: A function that takes no parameters and doesn't return any value. If called, [aborts the execution](/docs/feature-support.md#abort-a-running-execution) and throws an `ExecutionAbortedError`, unless the `noThrowOnAbort` option is set.
|
|
172
|
+
- `eventLogs`: An `AsyncGenerator` that [produces a log of events](/docs/feature-support.md#execution-event-logs) as the execution runs. To learn more about the events, their type, and their format, see the [following document](/docs/execution-event-logs.md).
|
|
135
173
|
|
|
136
174
|
#### Basic example:
|
|
137
175
|
|
|
@@ -212,7 +250,11 @@ Note that each line of the output corresponds to the result of each input, in th
|
|
|
212
250
|
|
|
213
251
|
### Passing input from stdin
|
|
214
252
|
|
|
215
|
-
`local-sfn` can also read the execution input from the standard input.
|
|
253
|
+
`local-sfn` can also read the execution input from the standard input.
|
|
254
|
+
|
|
255
|
+
If the first line of stdin can be parsed as a single JSON value, then `local-sfn` will consider each line as an input. Otherwise, the entire stdin will be considered as a single JSON input.
|
|
256
|
+
|
|
257
|
+
For example, assume you have the following text file, called `inputs.txt`, and you want to pass the contents of the file as inputs to `local-sfn`:
|
|
216
258
|
|
|
217
259
|
```txt
|
|
218
260
|
{ "num1": 1, "num2": 2 }
|
|
@@ -220,6 +262,8 @@ Note that each line of the output corresponds to the result of each input, in th
|
|
|
220
262
|
{ "num1": 5, "num2": 6 }
|
|
221
263
|
```
|
|
222
264
|
|
|
265
|
+
Because the first line is parsable as JSON, `local-sfn` will process each line as a single input.
|
|
266
|
+
|
|
223
267
|
You can then run the following command to pass the inputs of the text file to `local-sfn`:
|
|
224
268
|
|
|
225
269
|
```sh
|
|
@@ -232,7 +276,16 @@ Alternatively, using input redirection:
|
|
|
232
276
|
local-sfn -f state-machine.json < inputs.txt
|
|
233
277
|
```
|
|
234
278
|
|
|
235
|
-
|
|
279
|
+
On the other hand, assume you have this additional file, called `input.json`:
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"num1": 5,
|
|
284
|
+
"num2": 3
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
If you pass this file as input, `local-sfn` will automatically detect that it is a single, multiline JSON value and process it as a single value.
|
|
236
289
|
|
|
237
290
|
### Overriding Task and Wait states
|
|
238
291
|
|
|
@@ -299,14 +352,58 @@ local-sfn \
|
|
|
299
352
|
|
|
300
353
|
This command would execute the state machine, and override `Wait` states `WaitResponse` and `PauseUntilSignal` to pause the execution for 1500 and 250 milliseconds, respectively. The `Delay` state wouldn't be paused at all, since the override value is set to 0.
|
|
301
354
|
|
|
355
|
+
### Passing a custom Context Object
|
|
356
|
+
|
|
357
|
+
If the JSONPaths in your definition reference the Context Object, you can provide a mock Context Object by passing either the `--context` or the `--context-file` option. For example, given the following definition:
|
|
358
|
+
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"StartAt": "Get execution context data",
|
|
362
|
+
"States": {
|
|
363
|
+
"Get execution context data": {
|
|
364
|
+
"Type": "Pass",
|
|
365
|
+
"Parameters": {
|
|
366
|
+
"execId.$": "$$.Execution.Id",
|
|
367
|
+
"execName.$": "$$.Execution.Name"
|
|
368
|
+
},
|
|
369
|
+
"End": true
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
And given the following `context.json` file:
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"Execution": {
|
|
380
|
+
"Id": "arn:aws:states:us-east-1:123456789012:execution:stateMachineName:executionName",
|
|
381
|
+
"Name": "executionName"
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
You could provide the Context Object to the execution in the following manner:
|
|
387
|
+
|
|
388
|
+
```sh
|
|
389
|
+
local-sfn \
|
|
390
|
+
-f state-machine.json \
|
|
391
|
+
--context-file context.json \
|
|
392
|
+
'{}'
|
|
393
|
+
```
|
|
394
|
+
|
|
302
395
|
### Disabling ASL validations
|
|
303
396
|
|
|
304
397
|
Before attempting to run the state machine with the given inputs, the state machine definition itself is validated to check that:
|
|
305
398
|
|
|
306
399
|
- JSONPath strings are valid.
|
|
307
400
|
- ARNs in the `Resource` field of `Task` states are valid.
|
|
401
|
+
- There are no invalid fields.
|
|
402
|
+
- All states in the definition can be reached.
|
|
403
|
+
|
|
404
|
+
If any of these checks fail, `local-sfn` will print the validation error and exit. To partially suppress this behavior, you can pass the `--no-jsonpath-validation` option, to suppress JSONPath validation; and the `--no-arn-validation` option, to suppress ARN validation.
|
|
308
405
|
|
|
309
|
-
|
|
406
|
+
Alternatively, if you want to completely disable all validations, you can pass the `--no-validation` option. Be aware that passing this option implies no guarantees if the provided definition is invalid or contains non-standard fields: running it might result in undefined/unsupported behavior, so use at your own risk.
|
|
310
407
|
|
|
311
408
|
### Exit codes
|
|
312
409
|
|
package/{build → bin}/CLI.cjs
RENAMED
|
@@ -38,13 +38,14 @@ var import_readline = __toESM(require("readline"), 1);
|
|
|
38
38
|
var import_commander = require("commander");
|
|
39
39
|
|
|
40
40
|
// package.json
|
|
41
|
-
var version = "
|
|
41
|
+
var version = "1.1.0";
|
|
42
42
|
|
|
43
43
|
// src/cli/ArgumentParsers.ts
|
|
44
44
|
var import_fs = require("fs");
|
|
45
45
|
var import_child_process = require("child_process");
|
|
46
46
|
|
|
47
47
|
// src/util/index.ts
|
|
48
|
+
var isBrowserEnvironment = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
48
49
|
function tryJSONParse(jsonStr) {
|
|
49
50
|
try {
|
|
50
51
|
return JSON.parse(jsonStr);
|
|
@@ -110,6 +111,33 @@ function parseOverrideWaitOption(value, previous = {}) {
|
|
|
110
111
|
previous[waitStateName] = Number(duration);
|
|
111
112
|
return previous;
|
|
112
113
|
}
|
|
114
|
+
function parseContextOption(command, context) {
|
|
115
|
+
const jsonOrError = tryJSONParse(context);
|
|
116
|
+
if (jsonOrError instanceof Error) {
|
|
117
|
+
command.error(
|
|
118
|
+
`error: parsing of context object passed in option '--context <json>' failed: ${jsonOrError.message}`,
|
|
119
|
+
{
|
|
120
|
+
exitCode: 1 /* PreExecutionFailure */
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return jsonOrError;
|
|
125
|
+
}
|
|
126
|
+
function parseContextFileOption(command, contextFile) {
|
|
127
|
+
let file;
|
|
128
|
+
try {
|
|
129
|
+
file = (0, import_fs.readFileSync)(contextFile).toString();
|
|
130
|
+
} catch (error) {
|
|
131
|
+
command.error(`error: ${error.message}`, { exitCode: 1 /* PreExecutionFailure */ });
|
|
132
|
+
}
|
|
133
|
+
const jsonOrError = tryJSONParse(file);
|
|
134
|
+
if (jsonOrError instanceof Error) {
|
|
135
|
+
command.error(`error: parsing of context object in file '${contextFile}' failed: ${jsonOrError.message}`, {
|
|
136
|
+
exitCode: 1 /* PreExecutionFailure */
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return jsonOrError;
|
|
140
|
+
}
|
|
113
141
|
function parseInputArguments(command, value, previous = []) {
|
|
114
142
|
const jsonOrError = tryJSONParse(value);
|
|
115
143
|
if (jsonOrError instanceof Error) {
|
|
@@ -121,14 +149,15 @@ function parseInputArguments(command, value, previous = []) {
|
|
|
121
149
|
}
|
|
122
150
|
|
|
123
151
|
// src/cli/CommandHandler.ts
|
|
124
|
-
var import_main = require("
|
|
152
|
+
var import_main = require("../build/main.node.cjs");
|
|
125
153
|
async function commandAction(inputs, options, command) {
|
|
126
154
|
let stateMachine;
|
|
127
155
|
try {
|
|
128
156
|
stateMachine = new import_main.StateMachine(options.definition ?? options.definitionFile, {
|
|
129
157
|
validationOptions: {
|
|
130
158
|
checkPaths: options.jsonpathValidation,
|
|
131
|
-
checkArn: options.arnValidation
|
|
159
|
+
checkArn: options.arnValidation,
|
|
160
|
+
noValidate: !options.validation
|
|
132
161
|
}
|
|
133
162
|
});
|
|
134
163
|
} catch (error) {
|
|
@@ -139,7 +168,8 @@ async function commandAction(inputs, options, command) {
|
|
|
139
168
|
overrides: {
|
|
140
169
|
taskResourceLocalHandlers: options.overrideTask,
|
|
141
170
|
waitTimeOverrides: options.overrideWait
|
|
142
|
-
}
|
|
171
|
+
},
|
|
172
|
+
context: options.context ?? options.contextFile
|
|
143
173
|
});
|
|
144
174
|
return result;
|
|
145
175
|
});
|
|
@@ -204,11 +234,26 @@ Example calls:
|
|
|
204
234
|
"-w, --override-wait <mapping>",
|
|
205
235
|
"Override a Wait state to pause for the specified amount of milliseconds, instead of pausing for the duration specified in the state definition. The mapping value has to be provided in the format [WaitStateToOverride]:[number]."
|
|
206
236
|
).argParser(parseOverrideWaitOption)
|
|
237
|
+
).addOption(
|
|
238
|
+
new import_commander.Option(
|
|
239
|
+
"--context <json>",
|
|
240
|
+
"A JSON object that will be passed to each execution as the context object."
|
|
241
|
+
).argParser((value) => parseContextOption(command, value))
|
|
242
|
+
).addOption(
|
|
243
|
+
new import_commander.Option(
|
|
244
|
+
"--context-file <path>",
|
|
245
|
+
"Path to a file containing a JSON object that will be passed to each execution as the context object."
|
|
246
|
+
).argParser((value) => parseContextFileOption(command, value))
|
|
207
247
|
).addOption(
|
|
208
248
|
new import_commander.Option("--no-jsonpath-validation", "Disable validation of JSONPath strings in the state machine definition.")
|
|
209
|
-
).addOption(new import_commander.Option("--no-arn-validation", "Disable validation of ARNs in the state machine definition.")).
|
|
249
|
+
).addOption(new import_commander.Option("--no-arn-validation", "Disable validation of ARNs in the state machine definition.")).addOption(
|
|
250
|
+
new import_commander.Option(
|
|
251
|
+
"--no-validation",
|
|
252
|
+
"Disable validation of the state machine definition entirely. Use this option at your own risk, there are no guarantees when passing an invalid or non-standard definition to the state machine. Running it might result in undefined/unsupported behavior."
|
|
253
|
+
)
|
|
254
|
+
).argument(
|
|
210
255
|
"[inputs...]",
|
|
211
|
-
"Input data for the state machine, can be any valid JSON value. Each input represents a state machine execution
|
|
256
|
+
"Input data for the state machine, can be any valid JSON value. Each input represents a state machine execution.\n\nWhen reading from the standard input, if the first line can be parsed as a single JSON value, then each line will be considered as an input. Otherwise, the entire standard input will be considered as a single JSON input.",
|
|
212
257
|
(value, previous) => parseInputArguments(command, value, previous)
|
|
213
258
|
).hook("preAction", preActionHook).action(commandAction);
|
|
214
259
|
return command;
|
|
@@ -226,6 +271,11 @@ if (require.main === module) {
|
|
|
226
271
|
for await (const line of rl) {
|
|
227
272
|
stdin.push(line);
|
|
228
273
|
}
|
|
274
|
+
const firstLineJSON = tryJSONParse(stdin[0]);
|
|
275
|
+
if (firstLineJSON instanceof Error) {
|
|
276
|
+
await program.parseAsync([...process.argv, stdin.join("").trim()]);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
229
279
|
await program.parseAsync([...process.argv, ...stdin.filter((line) => line)]);
|
|
230
280
|
}
|
|
231
281
|
})();
|