aws-local-stepfunctions 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
@@ -16,6 +16,14 @@ This package lets you run AWS Step Functions completely locally, both in Node.js
16
16
  - [API](#api)
17
17
  - [Constructor](#constructor-new-statemachinedefinition-statemachineoptions)
18
18
  - [StateMachine.run](#statemachineruninput-options)
19
+ - [CLI](#cli)
20
+ - [Basic usage](#basic-usage)
21
+ - [Passing input from stdin](#passing-input-from-stdin)
22
+ - [Overriding Task and Wait states](#overriding-task-and-wait-states)
23
+ - [Task state override](#task-state-override)
24
+ - [Wait state override](#wait-state-override)
25
+ - [Disabling ASL validations](#disabling-asl-validations)
26
+ - [Exit codes](#exit-codes)
19
27
  - [Examples](#examples)
20
28
  - [License](#license)
21
29
 
@@ -49,7 +57,7 @@ import { StateMachine } from 'aws-local-stepfunctions';
49
57
 
50
58
  You can import the bundled package directly into a browser script as an ES module, from one of the following CDNs:
51
59
 
52
- > NOTE: The following examples will import the latest package version. Refer to the CDNs websites to know about other ways in which you can specify the package URL (for example, to import a specific version).
60
+ > NOTE: The following examples will import the latest package version. Refer to the CDNs websites to know about other ways in which you can specify the package URL (for example, to import a specific version or a minified version).
53
61
 
54
62
  #### [unpkg](https://unpkg.com/)
55
63
 
@@ -122,7 +130,7 @@ Each execution is independent of all others, meaning that you can concurrently c
122
130
  - `options?`:
123
131
  - `overrides?`: An object to override the behavior of certain states:
124
132
  - `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.
125
- - `waitTimeOverrides?`: An [object that overrides](/docs/feature-support.md#wait-state-duration-override) the wait duration of the specified `Wait` states. The specifed override duration should be in milliseconds.
133
+ - `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.
126
134
  - `noThrowOnAbort?`: If this option is set to `true`, aborting the execution will simply return `null` as result instead of throwing.
127
135
 
128
136
  #### Basic example:
@@ -149,6 +157,167 @@ const result = await execution.result; // wait until the execution finishes to g
149
157
  console.log(result); // log the result of the execution
150
158
  ```
151
159
 
160
+ ## CLI
161
+
162
+ In addition to the JavaScript API, `aws-local-stepfunctions` also provides a command-line interface. The CLI allows you to run one or several executions without having to create a Node.js script.
163
+
164
+ To use the CLI as a global shell command, you need to install the package globally:
165
+
166
+ ```sh
167
+ npm install -g aws-local-stepfunctions
168
+ ```
169
+
170
+ After installing the package, the command `local-sfn` will be available in your shell.
171
+
172
+ ### Basic usage
173
+
174
+ The simplest way to use the CLI is by passing either the `-d, --definition` or the `-f, --definition-file` option, along with the input(s) for the state machine. For example:
175
+
176
+ ```sh
177
+ local-sfn \
178
+ -f state-machine.json \
179
+ '{ "num1": 1, "num2": 2 }' \
180
+ '{ "num1": 3, "num2": 4 }' \
181
+ '{ "num1": 5, "num2": 6 }'
182
+ ```
183
+
184
+ This command would execute the state machine defined in file `state-machine.json` with `'{ "num1": 1, "num2": 2 }'`, `'{ "num1": 3, "num2": 4 }'`, and `'{ "num1": 5, "num2": 6 }'` as inputs. Each input corresponds to a state machine execution, and each execution is run independently, so the failure of one execution doesn't mean the failure of all of the other executions.
185
+
186
+ Now, suppose the state machine in file `state-machine.json` is defined as a single `Task` state that calls a Lambda function that adds `num1` and `num2`:
187
+
188
+ <a id="cli-state-machine"></a>
189
+
190
+ ```json
191
+ {
192
+ "StartAt": "AddNumbers",
193
+ "States": {
194
+ "AddNumbers": {
195
+ "Type": "Task",
196
+ "Resource": "arn:aws:lambda:us-east-1:123456789012:function:AddNumbers",
197
+ "End": true
198
+ }
199
+ }
200
+ }
201
+ ```
202
+
203
+ Then, the output of the `local-sfn` command above may look something like this:
204
+
205
+ ```sh
206
+ 3
207
+ 7
208
+ 11
209
+ ```
210
+
211
+ Note that each line of the output corresponds to the result of each input, in the same order that the inputs were given to the command.
212
+
213
+ ### Passing input from stdin
214
+
215
+ `local-sfn` can also read the execution input from the standard input. 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
+
217
+ ```txt
218
+ { "num1": 1, "num2": 2 }
219
+ { "num1": 3, "num2": 4 }
220
+ { "num1": 5, "num2": 6 }
221
+ ```
222
+
223
+ You can then run the following command to pass the inputs of the text file to `local-sfn`:
224
+
225
+ ```sh
226
+ cat inputs.txt | local-sfn -f state-machine.json
227
+ ```
228
+
229
+ Alternatively, using input redirection:
230
+
231
+ ```sh
232
+ local-sfn -f state-machine.json < inputs.txt
233
+ ```
234
+
235
+ When reading from stdin, `local-sfn` will take each line and use it as an input. Hence, to avoid any parsing errors, make sure the output of the command you're piping into `local-sfn` prints each input in a new line.
236
+
237
+ ### Overriding Task and Wait states
238
+
239
+ As explained in the Feature support document, it's possible to override the default actions of [`Task` states](/docs/feature-support.md#task-state-resource-override) and [`Wait` states](/docs/feature-support.md#wait-state-duration-override).
240
+
241
+ #### Task state override
242
+
243
+ To override a `Task` state, pass the `-t, --override-task` option. This option takes as value the name of the `Task` state you want to override, and a path to a script or program that will be executed instead of the resource specified in the state definition. The state name and the path must be separated by a colon `:`.
244
+
245
+ Using the same [state machine definition](#cli-state-machine) as before, if you wanted to override the `AddNumbers` state to run a custom script, you can do it like this:
246
+
247
+ ```sh
248
+ local-sfn -f state-machine.json -t AddNumbers:./override.sh '{ "num1": 1, "num2": 2 }'
249
+ ```
250
+
251
+ This command would run the state machine, but instead of invoking the Lambda function specified in the `Resource` field of the `AddNumbers` state, the `override.sh` script would be executed.
252
+
253
+ Now, suppose the `override.sh` script is defined like this:
254
+
255
+ ```sh
256
+ #!/bin/sh
257
+
258
+ TASK_INPUT=$1 # First argument is the input to the overridden Task state
259
+ echo "$TASK_INPUT" | jq '.num1 + .num2' # Use jq to add "num1" and "num2", and print result to stdout
260
+ ```
261
+
262
+ When overriding a `Task` state, the overriding executable will be passed the input to the `Task` state as first argument, which can then be used to compute the task result. Similarly, the executable must print the task result as a JSON value to the standard output, so `local-sfn` can then read stdout and use the value as the result of the `Task` state. If the executable terminates with an exit code different from `0`, its standard error will be printed and the execution will be marked as a failure.
263
+
264
+ Additionally, you can pass the `-t, --override-task` option multiple times, to override more than one `Task` state. For example:
265
+
266
+ ```sh
267
+ local-sfn
268
+ -f state-machine.json \
269
+ -t AddNumbers:./override.sh \
270
+ -t SendRequest:./request.py \
271
+ -t ProcessImage:./proc_image \
272
+ '{ "num1": 1, "num2": 2 }'
273
+ ```
274
+
275
+ This command would execute the state machine, and override `Task` states `AddNumbers`, `SendRequest`, and `ProcessImage` to run the `override.sh` shell script, the `request.py` Python script, and the `proc_image` program, respectively.
276
+
277
+ #### Wait state override
278
+
279
+ To override the duration of a `Wait` state, pass the `-w, --override-wait` option. This option takes as value the name of the `Wait` state you want to override, and a number that represents the amount in milliseconds that you want to pause the execution for. The state name and the milliseconds amount must be separated by a colon `:`.
280
+
281
+ For example:
282
+
283
+ ```sh
284
+ local-sfn -f state-machine.json -w WaitResponse:1500 '{ "num1": 1, "num2": 2 }'
285
+ ```
286
+
287
+ This command would execute the state machine, and when entering the `WaitResponse` `Wait` state, the execution would be paused for 1500 milliseconds (1.5 seconds), disregarding the `Seconds`, `Timestamp`, `SecondsPath`, or `TimestampPath` fields that could've been specified in the definition of `WaitResponse`.
288
+
289
+ In the same way as the `-t, --override-task` option, you can pass the `-w, --override-wait` option multiple times, to override more than one `Wait` state. For example:
290
+
291
+ ```sh
292
+ local-sfn \
293
+ -f state-machine.json \
294
+ -w WaitResponse:1500 \
295
+ -w PauseUntilSignal:250 \
296
+ -w Delay:0 \
297
+ '{ "num1": 1, "num2": 2 }'
298
+ ```
299
+
300
+ 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
+
302
+ ### Disabling ASL validations
303
+
304
+ Before attempting to run the state machine with the given inputs, the state machine definition itself is validated to check that:
305
+
306
+ - JSONPath strings are valid.
307
+ - ARNs in the `Resource` field of `Task` states are valid.
308
+
309
+ If any of these two checks fail, `local-sfn` will print the validation error and exit. To 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.
310
+
311
+ ### Exit codes
312
+
313
+ `local-sfn` can terminate with the following exit codes:
314
+
315
+ | Exit code | Explanation |
316
+ | :-------: | ------------------------------------------------------------------------------------ |
317
+ | 0 | The state machine was executed, and all executions ran successfully. |
318
+ | 1 | An error occurred before the state machine could be executed (e.g. a parsing error). |
319
+ | 2 | The state machine was executed, but at least one execution had an error. |
320
+
152
321
  ## Examples
153
322
 
154
323
  You can check more examples and options usage in the [examples](/examples) directory.
package/build/CLI.cjs ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/cli/CLI.ts
32
+ var CLI_exports = {};
33
+ __export(CLI_exports, {
34
+ makeProgram: () => makeProgram
35
+ });
36
+ module.exports = __toCommonJS(CLI_exports);
37
+ var import_readline = __toESM(require("readline"), 1);
38
+ var import_commander = require("commander");
39
+
40
+ // package.json
41
+ var version = "0.7.0";
42
+
43
+ // src/cli/ArgumentParsers.ts
44
+ var import_fs = require("fs");
45
+ var import_child_process = require("child_process");
46
+
47
+ // src/util/index.ts
48
+ function tryJSONParse(jsonStr) {
49
+ try {
50
+ return JSON.parse(jsonStr);
51
+ } catch (error) {
52
+ return error;
53
+ }
54
+ }
55
+
56
+ // src/cli/ArgumentParsers.ts
57
+ function parseDefinitionOption(command, definition) {
58
+ const jsonOrError = tryJSONParse(definition);
59
+ if (jsonOrError instanceof Error) {
60
+ command.error(
61
+ `error: parsing of state machine definition passed in option '-d, --definition <definition>' failed: ${jsonOrError.message}`,
62
+ {
63
+ exitCode: 1 /* PreExecutionFailure */
64
+ }
65
+ );
66
+ }
67
+ return jsonOrError;
68
+ }
69
+ function parseDefinitionFileOption(command, definitionFile) {
70
+ let file;
71
+ try {
72
+ file = (0, import_fs.readFileSync)(definitionFile).toString();
73
+ } catch (error) {
74
+ command.error(`error: ${error.message}`, { exitCode: 1 /* PreExecutionFailure */ });
75
+ }
76
+ const jsonOrError = tryJSONParse(file);
77
+ if (jsonOrError instanceof Error) {
78
+ command.error(
79
+ `error: parsing of state machine definition in file '${definitionFile}' failed: ${jsonOrError.message}`,
80
+ { exitCode: 1 /* PreExecutionFailure */ }
81
+ );
82
+ }
83
+ return jsonOrError;
84
+ }
85
+ function parseOverrideTaskOption(value, previous = {}) {
86
+ const [taskStateName, scriptPath] = value.split(":");
87
+ previous[taskStateName] = (input) => {
88
+ const spawnResult = (0, import_child_process.spawnSync)(scriptPath, [JSON.stringify(input)]);
89
+ if (spawnResult.error) {
90
+ throw new Error(
91
+ `Attempt to run task override '${scriptPath}' for state '${taskStateName}' failed: ${spawnResult.error.message}`
92
+ );
93
+ }
94
+ if (spawnResult.status !== 0) {
95
+ throw new Error(`${scriptPath} ('${taskStateName}'): ${spawnResult.stderr.toString()}`);
96
+ }
97
+ const overrideResult = spawnResult.stdout.toString();
98
+ const jsonOrError = tryJSONParse(overrideResult);
99
+ if (jsonOrError instanceof Error) {
100
+ throw new Error(
101
+ `Parsing of output '${overrideResult}' from task override '${scriptPath}' for state '${taskStateName}' failed: ${jsonOrError.message}`
102
+ );
103
+ }
104
+ return Promise.resolve(jsonOrError);
105
+ };
106
+ return previous;
107
+ }
108
+ function parseOverrideWaitOption(value, previous = {}) {
109
+ const [waitStateName, duration] = value.split(":");
110
+ previous[waitStateName] = Number(duration);
111
+ return previous;
112
+ }
113
+ function parseInputArguments(command, value, previous = []) {
114
+ const jsonOrError = tryJSONParse(value);
115
+ if (jsonOrError instanceof Error) {
116
+ command.error(`error: parsing of input value '${value}' failed: ${jsonOrError.message}`, {
117
+ exitCode: 1 /* PreExecutionFailure */
118
+ });
119
+ }
120
+ return previous.concat(jsonOrError);
121
+ }
122
+
123
+ // src/cli/CommandHandler.ts
124
+ var import_main = require("./main.node.cjs");
125
+ async function commandAction(inputs, options, command) {
126
+ let stateMachine;
127
+ try {
128
+ stateMachine = new import_main.StateMachine(options.definition ?? options.definitionFile, {
129
+ validationOptions: {
130
+ checkPaths: options.jsonpathValidation,
131
+ checkArn: options.arnValidation
132
+ }
133
+ });
134
+ } catch (error) {
135
+ command.error(`error: ${error.message}`);
136
+ }
137
+ const resultsPromises = inputs.map((input) => {
138
+ const { result } = stateMachine.run(input, {
139
+ overrides: {
140
+ taskResourceLocalHandlers: options.overrideTask,
141
+ waitTimeOverrides: options.overrideWait
142
+ }
143
+ });
144
+ return result;
145
+ });
146
+ const results = await Promise.allSettled(resultsPromises);
147
+ let exitCode = 0 /* Success */;
148
+ for (const result of results) {
149
+ if (result.status === "fulfilled") {
150
+ console.log(result.value);
151
+ } else {
152
+ exitCode = 2 /* StateMachineExecutionFailure */;
153
+ let msg = result.reason.message;
154
+ if (result.reason instanceof import_main.ExecutionTimeoutError) {
155
+ msg = "Execution timed out";
156
+ }
157
+ console.log(msg.trim());
158
+ }
159
+ }
160
+ process.exitCode = exitCode;
161
+ }
162
+ function preActionHook(thisCommand) {
163
+ const options = thisCommand.opts();
164
+ if (thisCommand.args.length === 0) {
165
+ thisCommand.help();
166
+ }
167
+ if (!options["definition"] && !options["definitionFile"]) {
168
+ thisCommand.error(
169
+ "error: missing either option '-d, --definition <definition>' or option '-f, --definition-file <path>'",
170
+ { exitCode: 1 /* PreExecutionFailure */ }
171
+ );
172
+ }
173
+ }
174
+
175
+ // src/cli/CLI.ts
176
+ function makeProgram() {
177
+ const command = new import_commander.Command();
178
+ command.name("local-sfn").description(
179
+ `Execute an Amazon States Language state machine with the given inputs.
180
+ The result of each execution will be output in a new line and in the same order as its corresponding input.`
181
+ ).helpOption("-h, --help", "Print help for command and exit.").configureHelp({ helpWidth: 80 }).addHelpText(
182
+ "after",
183
+ `
184
+ Exit codes:
185
+ 0 All executions ran successfully.
186
+ 1 An error occurred before the state machine could be executed.
187
+ 2 At least one execution had an error.
188
+
189
+ Example calls:
190
+ $ local-sfn -f state-machine.json '{ "num1": 2, "num2": 2 }'
191
+ $ local-sfn -f state-machine.json -t SendRequest:./override.sh -w WaitResponse:2000 '{ "num1": 2, "num2": 2 }'
192
+ $ cat inputs.txt | local-sfn -f state-machine.json`
193
+ ).version(version, "-V, --version", "Print the version number and exit.").addOption(
194
+ new import_commander.Option("-d, --definition <definition>", "A JSON definition of a state machine.").conflicts("definition-file").argParser((value) => parseDefinitionOption(command, value))
195
+ ).addOption(
196
+ new import_commander.Option("-f, --definition-file <path>", "Path to a file containing a JSON state machine definition.").conflicts("definition").argParser((value) => parseDefinitionFileOption(command, value))
197
+ ).addOption(
198
+ new import_commander.Option(
199
+ "-t, --override-task <mapping>",
200
+ "Override a Task state to run an executable file or script, instead of calling the service specified in the 'Resource' field of the state definition. The mapping value has to be provided in the format [TaskStateToOverride]:[path/to/override/script]. The override script will be passed the input of the Task state as first argument, which can then be used to compute the task result. The script must print the task result as a JSON value to the standard output."
201
+ ).argParser(parseOverrideTaskOption)
202
+ ).addOption(
203
+ new import_commander.Option(
204
+ "-w, --override-wait <mapping>",
205
+ "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
+ ).argParser(parseOverrideWaitOption)
207
+ ).addOption(
208
+ 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.")).argument(
210
+ "[inputs...]",
211
+ "Input data for the state machine, can be any valid JSON value. Each input represents a state machine execution. If reading from the standard input, each line will be considered as an input.",
212
+ (value, previous) => parseInputArguments(command, value, previous)
213
+ ).hook("preAction", preActionHook).action(commandAction);
214
+ return command;
215
+ }
216
+ if (require.main === module) {
217
+ (async function() {
218
+ const program = makeProgram();
219
+ if (process.stdin.isTTY) {
220
+ await program.parseAsync();
221
+ } else {
222
+ const rl = import_readline.default.createInterface({
223
+ input: process.stdin
224
+ });
225
+ const stdin = [];
226
+ for await (const line of rl) {
227
+ stdin.push(line);
228
+ }
229
+ await program.parseAsync([...process.argv, ...stdin.filter((line) => line)]);
230
+ }
231
+ })();
232
+ }
233
+ // Annotate the CommonJS export names for ESM import in node:
234
+ 0 && (module.exports = {
235
+ makeProgram
236
+ });
@@ -19950,7 +19950,7 @@ function validateArgumentType(allowedTypes, argPosition, funcArg, funcName) {
19950
19950
  if (matchesAllowedType)
19951
19951
  break;
19952
19952
  }
19953
- const expectedType = allowedTypes.map((type) => `"${type}"`).join(" | ");
19953
+ const expectedType = allowedTypes.map((type) => `'${type}'`).join(" | ");
19954
19954
  if (!matchesAllowedType) {
19955
19955
  throw new StatesRuntimeError(
19956
19956
  `Intrinsic function ${funcName} expected argument ${argPosition} to be of type ${expectedType}, but received ${typeof funcArg}`
@@ -19978,7 +19978,7 @@ function validateArgumentConstraints(argConstraints, argPosition, funcArg, funcN
19978
19978
  if (matchesAllConstraints)
19979
19979
  break;
19980
19980
  }
19981
- const expectedConstraints = argConstraints.map((constraint) => `"${constraint}"`).join(" | ");
19981
+ const expectedConstraints = argConstraints.map((constraint) => `'${constraint}'`).join(" | ");
19982
19982
  if (!matchesAllConstraints) {
19983
19983
  throw new StatesRuntimeError(
19984
19984
  `Intrinsic function ${funcName} expected argument ${argPosition} to satisfy the following constraints: ${expectedConstraints}`
@@ -20600,7 +20600,7 @@ function processPayloadTemplate(payloadTemplate, json, context) {
20600
20600
  let sanitizedKey = key;
20601
20601
  let resolvedValue = value;
20602
20602
  if (isPlainObj(value)) {
20603
- resolvedValue = processPayloadTemplate(value, json);
20603
+ resolvedValue = processPayloadTemplate(value, json, context);
20604
20604
  }
20605
20605
  if (key.endsWith(".$") && typeof value === "string") {
20606
20606
  sanitizedKey = key.replace(".$", "");
@@ -21048,7 +21048,10 @@ var MapStateAction = class extends BaseStateAction {
21048
21048
  if (!Array.isArray(items)) {
21049
21049
  throw new StatesRuntimeError("Input of Map state must be an array or ItemsPath property must point to an array");
21050
21050
  }
21051
- const iteratorStateMachine = new StateMachine(state.Iterator, options?.stateMachineOptions);
21051
+ const iteratorStateMachine = new StateMachine(state.Iterator, {
21052
+ ...options?.stateMachineOptions,
21053
+ validationOptions: { _noValidate: true }
21054
+ });
21052
21055
  const limit = (0, import_p_limit.default)(state.MaxConcurrency || DEFAULT_MAX_CONCURRENCY);
21053
21056
  const processedItemsPromise = items.map(
21054
21057
  (item, i3) => limit(() => this.processItem(iteratorStateMachine, item, input, context, i3, options))
@@ -21077,7 +21080,10 @@ var ParallelStateAction = class extends BaseStateAction {
21077
21080
  this.executionAbortFuncs = [];
21078
21081
  }
21079
21082
  processBranch(branch, input, context, options) {
21080
- const stateMachine = new StateMachine(branch, options?.stateMachineOptions);
21083
+ const stateMachine = new StateMachine(branch, {
21084
+ ...options?.stateMachineOptions,
21085
+ validationOptions: { _noValidate: true }
21086
+ });
21081
21087
  const execution = stateMachine.run(input, options?.runOptions);
21082
21088
  this.executionAbortFuncs.push(execution.abort);
21083
21089
  return execution.result;
@@ -27039,12 +27045,12 @@ var LambdaClient2 = class {
27039
27045
  if (config) {
27040
27046
  if (!config.region) {
27041
27047
  throw new StatesRuntimeError(
27042
- "`awsConfig` option was specified for state machine, but `region` property is not set"
27048
+ "'awsConfig' option was specified for state machine, but 'region' property is not set"
27043
27049
  );
27044
27050
  }
27045
27051
  if (config.credentials) {
27046
27052
  const credentialsTypes = Object.keys(config.credentials);
27047
- const credentialsNames = credentialsTypes.map((name) => `\`${name}\``).join(", ");
27053
+ const credentialsNames = credentialsTypes.map((name) => `'${name}'`).join(", ");
27048
27054
  if (credentialsTypes.length > 1) {
27049
27055
  throw new StatesRuntimeError(
27050
27056
  `More than one type of AWS credentials were specified: ${credentialsNames}. Only one type may be specified`
@@ -27078,7 +27084,7 @@ var LambdaClient2 = class {
27078
27084
  }
27079
27085
  if (invocationResult.FunctionError) {
27080
27086
  const errorResult = resultValue;
27081
- throw new FailStateError(errorResult.errorType, `Execution of Lambda function "${funcNameOrArn}" failed`);
27087
+ throw new FailStateError(errorResult.errorType, `Execution of Lambda function '${funcNameOrArn}' failed`);
27082
27088
  }
27083
27089
  return resultValue;
27084
27090
  }
@@ -27389,6 +27395,7 @@ var StateExecutor = class {
27389
27395
  // src/stateMachine/StateMachine.ts
27390
27396
  var import_asl_validator = __toESM(require_validator(), 1);
27391
27397
  var import_cloneDeep3 = __toESM(require_cloneDeep(), 1);
27398
+ var DEFAULT_MAX_EXECUTION_TIMEOUT = 2147483647e-3;
27392
27399
  var StateMachine = class {
27393
27400
  /**
27394
27401
  * Constructs a new state machine.
@@ -27397,14 +27404,16 @@ var StateMachine = class {
27397
27404
  * These options also apply to state machines defined in the `Iterator` field of `Map` states and in the `Branches` field of `Parallel` states.
27398
27405
  */
27399
27406
  constructor(definition, stateMachineOptions) {
27400
- const { isValid, errorsText } = (0, import_asl_validator.default)(definition, {
27401
- checkArn: true,
27402
- checkPaths: true,
27403
- ...stateMachineOptions?.validationOptions
27404
- });
27405
- if (!isValid) {
27406
- throw new Error(`State machine definition is invalid, see error(s) below:
27407
+ if (!stateMachineOptions?.validationOptions?._noValidate) {
27408
+ const { isValid, errorsText } = (0, import_asl_validator.default)(definition, {
27409
+ checkArn: true,
27410
+ checkPaths: true,
27411
+ ...stateMachineOptions?.validationOptions
27412
+ });
27413
+ if (!isValid) {
27414
+ throw new Error(`State machine definition is invalid, see error(s) below:
27407
27415
  ${errorsText("\n")}`);
27416
+ }
27408
27417
  }
27409
27418
  this.definition = definition;
27410
27419
  this.stateMachineOptions = stateMachineOptions;
@@ -27422,6 +27431,7 @@ var StateMachine = class {
27422
27431
  */
27423
27432
  run(input, options) {
27424
27433
  const abortController = new AbortController();
27434
+ const timeoutSeconds = this.definition.TimeoutSeconds ?? DEFAULT_MAX_EXECUTION_TIMEOUT;
27425
27435
  let onAbortHandler;
27426
27436
  const settleOnAbort = new Promise((resolve, reject) => {
27427
27437
  if (options?.noThrowOnAbort) {
@@ -27431,25 +27441,27 @@ var StateMachine = class {
27431
27441
  }
27432
27442
  abortController.signal.addEventListener("abort", onAbortHandler);
27433
27443
  });
27434
- let rejectOnTimeout = null;
27435
- if (this.definition.TimeoutSeconds) {
27436
- rejectOnTimeout = new Promise((_, reject) => {
27437
- setTimeout(() => {
27438
- abortController.signal.removeEventListener("abort", onAbortHandler);
27439
- abortController.abort();
27440
- reject(new StatesTimeoutError());
27441
- }, this.definition.TimeoutSeconds * 1e3);
27442
- });
27443
- }
27444
- const executionResult = this.execute(input, {
27445
- stateMachineOptions: this.stateMachineOptions,
27446
- runOptions: options,
27447
- abortSignal: abortController.signal
27444
+ let timeoutId;
27445
+ const rejectOnTimeout = new Promise((_, reject) => {
27446
+ timeoutId = setTimeout(() => {
27447
+ abortController.signal.removeEventListener("abort", onAbortHandler);
27448
+ abortController.abort();
27449
+ reject(new StatesTimeoutError());
27450
+ }, timeoutSeconds * 1e3);
27448
27451
  });
27449
- const racingPromises = [executionResult, settleOnAbort];
27450
- if (rejectOnTimeout) {
27451
- racingPromises.push(rejectOnTimeout);
27452
- }
27452
+ const executionResult = this.execute(
27453
+ input,
27454
+ {
27455
+ stateMachineOptions: this.stateMachineOptions,
27456
+ runOptions: options,
27457
+ abortSignal: abortController.signal
27458
+ },
27459
+ () => {
27460
+ abortController.signal.removeEventListener("abort", onAbortHandler);
27461
+ clearTimeout(timeoutId);
27462
+ }
27463
+ );
27464
+ const racingPromises = [executionResult, settleOnAbort, rejectOnTimeout];
27453
27465
  const result = Promise.race(racingPromises);
27454
27466
  return {
27455
27467
  abort: () => abortController.abort(),
@@ -27459,7 +27471,7 @@ var StateMachine = class {
27459
27471
  /**
27460
27472
  * Helper method that handles the execution of the machine states and the transitions between them.
27461
27473
  */
27462
- async execute(input, options) {
27474
+ async execute(input, options, cleanupFn) {
27463
27475
  let currState = this.definition.States[this.definition.StartAt];
27464
27476
  let currStateName = this.definition.StartAt;
27465
27477
  let currInput = (0, import_cloneDeep3.default)(input);
@@ -27477,6 +27489,8 @@ var StateMachine = class {
27477
27489
  } while (!isEndState && !options.abortSignal.aborted);
27478
27490
  } catch (error) {
27479
27491
  throw new ExecutionError(error);
27492
+ } finally {
27493
+ cleanupFn();
27480
27494
  }
27481
27495
  return currResult;
27482
27496
  }
package/build/main.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FromCognitoIdentityPoolParameters } from '@aws-sdk/credential-provider-cognito-identity/dist-types/fromCognitoIdentityPool';
2
- import { Credentials } from '@aws-sdk/types/dist-types/credentials';
2
+ import { AwsCredentialIdentity } from '@aws-sdk/types/dist-types/identity/AwsCredentialIdentity';
3
3
 
4
4
  type StateType = 'Task' | 'Parallel' | 'Map' | 'Pass' | 'Wait' | 'Choice' | 'Succeed' | 'Fail';
5
5
 
@@ -197,7 +197,7 @@ interface AWSConfig {
197
197
  region: string;
198
198
  credentials?: {
199
199
  cognitoIdentityPool?: FromCognitoIdentityPoolParameters;
200
- accessKeys?: Omit<Credentials, 'expiration'>;
200
+ accessKeys?: Omit<AwsCredentialIdentity, 'expiration'>;
201
201
  };
202
202
  }
203
203
  interface StateMachineOptions {
@@ -207,7 +207,7 @@ function validateArgumentType(allowedTypes, argPosition, funcArg, funcName) {
207
207
  if (matchesAllowedType)
208
208
  break;
209
209
  }
210
- const expectedType = allowedTypes.map((type) => `"${type}"`).join(" | ");
210
+ const expectedType = allowedTypes.map((type) => `'${type}'`).join(" | ");
211
211
  if (!matchesAllowedType) {
212
212
  throw new StatesRuntimeError(
213
213
  `Intrinsic function ${funcName} expected argument ${argPosition} to be of type ${expectedType}, but received ${typeof funcArg}`
@@ -235,7 +235,7 @@ function validateArgumentConstraints(argConstraints, argPosition, funcArg, funcN
235
235
  if (matchesAllConstraints)
236
236
  break;
237
237
  }
238
- const expectedConstraints = argConstraints.map((constraint) => `"${constraint}"`).join(" | ");
238
+ const expectedConstraints = argConstraints.map((constraint) => `'${constraint}'`).join(" | ");
239
239
  if (!matchesAllConstraints) {
240
240
  throw new StatesRuntimeError(
241
241
  `Intrinsic function ${funcName} expected argument ${argPosition} to satisfy the following constraints: ${expectedConstraints}`
@@ -857,7 +857,7 @@ function processPayloadTemplate(payloadTemplate, json, context) {
857
857
  let sanitizedKey = key;
858
858
  let resolvedValue = value;
859
859
  if (isPlainObj(value)) {
860
- resolvedValue = processPayloadTemplate(value, json);
860
+ resolvedValue = processPayloadTemplate(value, json, context);
861
861
  }
862
862
  if (key.endsWith(".$") && typeof value === "string") {
863
863
  sanitizedKey = key.replace(".$", "");
@@ -1188,7 +1188,10 @@ var MapStateAction = class extends BaseStateAction {
1188
1188
  if (!Array.isArray(items)) {
1189
1189
  throw new StatesRuntimeError("Input of Map state must be an array or ItemsPath property must point to an array");
1190
1190
  }
1191
- const iteratorStateMachine = new StateMachine(state.Iterator, options?.stateMachineOptions);
1191
+ const iteratorStateMachine = new StateMachine(state.Iterator, {
1192
+ ...options?.stateMachineOptions,
1193
+ validationOptions: { _noValidate: true }
1194
+ });
1192
1195
  const limit = (0, import_p_limit.default)(state.MaxConcurrency || DEFAULT_MAX_CONCURRENCY);
1193
1196
  const processedItemsPromise = items.map(
1194
1197
  (item, i) => limit(() => this.processItem(iteratorStateMachine, item, input, context, i, options))
@@ -1217,7 +1220,10 @@ var ParallelStateAction = class extends BaseStateAction {
1217
1220
  this.executionAbortFuncs = [];
1218
1221
  }
1219
1222
  processBranch(branch, input, context, options) {
1220
- const stateMachine = new StateMachine(branch, options?.stateMachineOptions);
1223
+ const stateMachine = new StateMachine(branch, {
1224
+ ...options?.stateMachineOptions,
1225
+ validationOptions: { _noValidate: true }
1226
+ });
1221
1227
  const execution = stateMachine.run(input, options?.runOptions);
1222
1228
  this.executionAbortFuncs.push(execution.abort);
1223
1229
  return execution.result;
@@ -1273,12 +1279,12 @@ var LambdaClient = class {
1273
1279
  if (config) {
1274
1280
  if (!config.region) {
1275
1281
  throw new StatesRuntimeError(
1276
- "`awsConfig` option was specified for state machine, but `region` property is not set"
1282
+ "'awsConfig' option was specified for state machine, but 'region' property is not set"
1277
1283
  );
1278
1284
  }
1279
1285
  if (config.credentials) {
1280
1286
  const credentialsTypes = Object.keys(config.credentials);
1281
- const credentialsNames = credentialsTypes.map((name) => `\`${name}\``).join(", ");
1287
+ const credentialsNames = credentialsTypes.map((name) => `'${name}'`).join(", ");
1282
1288
  if (credentialsTypes.length > 1) {
1283
1289
  throw new StatesRuntimeError(
1284
1290
  `More than one type of AWS credentials were specified: ${credentialsNames}. Only one type may be specified`
@@ -1312,7 +1318,7 @@ var LambdaClient = class {
1312
1318
  }
1313
1319
  if (invocationResult.FunctionError) {
1314
1320
  const errorResult = resultValue;
1315
- throw new FailStateError(errorResult.errorType, `Execution of Lambda function "${funcNameOrArn}" failed`);
1321
+ throw new FailStateError(errorResult.errorType, `Execution of Lambda function '${funcNameOrArn}' failed`);
1316
1322
  }
1317
1323
  return resultValue;
1318
1324
  }
@@ -1623,6 +1629,7 @@ var StateExecutor = class {
1623
1629
  // src/stateMachine/StateMachine.ts
1624
1630
  var import_asl_validator = __toESM(require("asl-validator"), 1);
1625
1631
  var import_cloneDeep3 = __toESM(require("lodash/cloneDeep.js"), 1);
1632
+ var DEFAULT_MAX_EXECUTION_TIMEOUT = 2147483647e-3;
1626
1633
  var StateMachine = class {
1627
1634
  /**
1628
1635
  * Constructs a new state machine.
@@ -1631,14 +1638,16 @@ var StateMachine = class {
1631
1638
  * These options also apply to state machines defined in the `Iterator` field of `Map` states and in the `Branches` field of `Parallel` states.
1632
1639
  */
1633
1640
  constructor(definition, stateMachineOptions) {
1634
- const { isValid, errorsText } = (0, import_asl_validator.default)(definition, {
1635
- checkArn: true,
1636
- checkPaths: true,
1637
- ...stateMachineOptions?.validationOptions
1638
- });
1639
- if (!isValid) {
1640
- throw new Error(`State machine definition is invalid, see error(s) below:
1641
+ if (!stateMachineOptions?.validationOptions?._noValidate) {
1642
+ const { isValid, errorsText } = (0, import_asl_validator.default)(definition, {
1643
+ checkArn: true,
1644
+ checkPaths: true,
1645
+ ...stateMachineOptions?.validationOptions
1646
+ });
1647
+ if (!isValid) {
1648
+ throw new Error(`State machine definition is invalid, see error(s) below:
1641
1649
  ${errorsText("\n")}`);
1650
+ }
1642
1651
  }
1643
1652
  this.definition = definition;
1644
1653
  this.stateMachineOptions = stateMachineOptions;
@@ -1656,6 +1665,7 @@ var StateMachine = class {
1656
1665
  */
1657
1666
  run(input, options) {
1658
1667
  const abortController = new AbortController();
1668
+ const timeoutSeconds = this.definition.TimeoutSeconds ?? DEFAULT_MAX_EXECUTION_TIMEOUT;
1659
1669
  let onAbortHandler;
1660
1670
  const settleOnAbort = new Promise((resolve, reject) => {
1661
1671
  if (options?.noThrowOnAbort) {
@@ -1665,25 +1675,27 @@ var StateMachine = class {
1665
1675
  }
1666
1676
  abortController.signal.addEventListener("abort", onAbortHandler);
1667
1677
  });
1668
- let rejectOnTimeout = null;
1669
- if (this.definition.TimeoutSeconds) {
1670
- rejectOnTimeout = new Promise((_, reject) => {
1671
- setTimeout(() => {
1672
- abortController.signal.removeEventListener("abort", onAbortHandler);
1673
- abortController.abort();
1674
- reject(new StatesTimeoutError());
1675
- }, this.definition.TimeoutSeconds * 1e3);
1676
- });
1677
- }
1678
- const executionResult = this.execute(input, {
1679
- stateMachineOptions: this.stateMachineOptions,
1680
- runOptions: options,
1681
- abortSignal: abortController.signal
1678
+ let timeoutId;
1679
+ const rejectOnTimeout = new Promise((_, reject) => {
1680
+ timeoutId = setTimeout(() => {
1681
+ abortController.signal.removeEventListener("abort", onAbortHandler);
1682
+ abortController.abort();
1683
+ reject(new StatesTimeoutError());
1684
+ }, timeoutSeconds * 1e3);
1682
1685
  });
1683
- const racingPromises = [executionResult, settleOnAbort];
1684
- if (rejectOnTimeout) {
1685
- racingPromises.push(rejectOnTimeout);
1686
- }
1686
+ const executionResult = this.execute(
1687
+ input,
1688
+ {
1689
+ stateMachineOptions: this.stateMachineOptions,
1690
+ runOptions: options,
1691
+ abortSignal: abortController.signal
1692
+ },
1693
+ () => {
1694
+ abortController.signal.removeEventListener("abort", onAbortHandler);
1695
+ clearTimeout(timeoutId);
1696
+ }
1697
+ );
1698
+ const racingPromises = [executionResult, settleOnAbort, rejectOnTimeout];
1687
1699
  const result = Promise.race(racingPromises);
1688
1700
  return {
1689
1701
  abort: () => abortController.abort(),
@@ -1693,7 +1705,7 @@ var StateMachine = class {
1693
1705
  /**
1694
1706
  * Helper method that handles the execution of the machine states and the transitions between them.
1695
1707
  */
1696
- async execute(input, options) {
1708
+ async execute(input, options, cleanupFn) {
1697
1709
  let currState = this.definition.States[this.definition.StartAt];
1698
1710
  let currStateName = this.definition.StartAt;
1699
1711
  let currInput = (0, import_cloneDeep3.default)(input);
@@ -1711,6 +1723,8 @@ var StateMachine = class {
1711
1723
  } while (!isEndState && !options.abortSignal.aborted);
1712
1724
  } catch (error) {
1713
1725
  throw new ExecutionError(error);
1726
+ } finally {
1727
+ cleanupFn();
1714
1728
  }
1715
1729
  return currResult;
1716
1730
  }
@@ -169,7 +169,7 @@ function validateArgumentType(allowedTypes, argPosition, funcArg, funcName) {
169
169
  if (matchesAllowedType)
170
170
  break;
171
171
  }
172
- const expectedType = allowedTypes.map((type) => `"${type}"`).join(" | ");
172
+ const expectedType = allowedTypes.map((type) => `'${type}'`).join(" | ");
173
173
  if (!matchesAllowedType) {
174
174
  throw new StatesRuntimeError(
175
175
  `Intrinsic function ${funcName} expected argument ${argPosition} to be of type ${expectedType}, but received ${typeof funcArg}`
@@ -197,7 +197,7 @@ function validateArgumentConstraints(argConstraints, argPosition, funcArg, funcN
197
197
  if (matchesAllConstraints)
198
198
  break;
199
199
  }
200
- const expectedConstraints = argConstraints.map((constraint) => `"${constraint}"`).join(" | ");
200
+ const expectedConstraints = argConstraints.map((constraint) => `'${constraint}'`).join(" | ");
201
201
  if (!matchesAllConstraints) {
202
202
  throw new StatesRuntimeError(
203
203
  `Intrinsic function ${funcName} expected argument ${argPosition} to satisfy the following constraints: ${expectedConstraints}`
@@ -819,7 +819,7 @@ function processPayloadTemplate(payloadTemplate, json, context) {
819
819
  let sanitizedKey = key;
820
820
  let resolvedValue = value;
821
821
  if (isPlainObj(value)) {
822
- resolvedValue = processPayloadTemplate(value, json);
822
+ resolvedValue = processPayloadTemplate(value, json, context);
823
823
  }
824
824
  if (key.endsWith(".$") && typeof value === "string") {
825
825
  sanitizedKey = key.replace(".$", "");
@@ -1150,7 +1150,10 @@ var MapStateAction = class extends BaseStateAction {
1150
1150
  if (!Array.isArray(items)) {
1151
1151
  throw new StatesRuntimeError("Input of Map state must be an array or ItemsPath property must point to an array");
1152
1152
  }
1153
- const iteratorStateMachine = new StateMachine(state.Iterator, options?.stateMachineOptions);
1153
+ const iteratorStateMachine = new StateMachine(state.Iterator, {
1154
+ ...options?.stateMachineOptions,
1155
+ validationOptions: { _noValidate: true }
1156
+ });
1154
1157
  const limit = pLimit(state.MaxConcurrency || DEFAULT_MAX_CONCURRENCY);
1155
1158
  const processedItemsPromise = items.map(
1156
1159
  (item, i) => limit(() => this.processItem(iteratorStateMachine, item, input, context, i, options))
@@ -1179,7 +1182,10 @@ var ParallelStateAction = class extends BaseStateAction {
1179
1182
  this.executionAbortFuncs = [];
1180
1183
  }
1181
1184
  processBranch(branch, input, context, options) {
1182
- const stateMachine = new StateMachine(branch, options?.stateMachineOptions);
1185
+ const stateMachine = new StateMachine(branch, {
1186
+ ...options?.stateMachineOptions,
1187
+ validationOptions: { _noValidate: true }
1188
+ });
1183
1189
  const execution = stateMachine.run(input, options?.runOptions);
1184
1190
  this.executionAbortFuncs.push(execution.abort);
1185
1191
  return execution.result;
@@ -1235,12 +1241,12 @@ var LambdaClient = class {
1235
1241
  if (config) {
1236
1242
  if (!config.region) {
1237
1243
  throw new StatesRuntimeError(
1238
- "`awsConfig` option was specified for state machine, but `region` property is not set"
1244
+ "'awsConfig' option was specified for state machine, but 'region' property is not set"
1239
1245
  );
1240
1246
  }
1241
1247
  if (config.credentials) {
1242
1248
  const credentialsTypes = Object.keys(config.credentials);
1243
- const credentialsNames = credentialsTypes.map((name) => `\`${name}\``).join(", ");
1249
+ const credentialsNames = credentialsTypes.map((name) => `'${name}'`).join(", ");
1244
1250
  if (credentialsTypes.length > 1) {
1245
1251
  throw new StatesRuntimeError(
1246
1252
  `More than one type of AWS credentials were specified: ${credentialsNames}. Only one type may be specified`
@@ -1274,7 +1280,7 @@ var LambdaClient = class {
1274
1280
  }
1275
1281
  if (invocationResult.FunctionError) {
1276
1282
  const errorResult = resultValue;
1277
- throw new FailStateError(errorResult.errorType, `Execution of Lambda function "${funcNameOrArn}" failed`);
1283
+ throw new FailStateError(errorResult.errorType, `Execution of Lambda function '${funcNameOrArn}' failed`);
1278
1284
  }
1279
1285
  return resultValue;
1280
1286
  }
@@ -1585,6 +1591,7 @@ var StateExecutor = class {
1585
1591
  // src/stateMachine/StateMachine.ts
1586
1592
  import aslValidator from "asl-validator";
1587
1593
  import cloneDeep3 from "lodash/cloneDeep.js";
1594
+ var DEFAULT_MAX_EXECUTION_TIMEOUT = 2147483647e-3;
1588
1595
  var StateMachine = class {
1589
1596
  /**
1590
1597
  * Constructs a new state machine.
@@ -1593,14 +1600,16 @@ var StateMachine = class {
1593
1600
  * These options also apply to state machines defined in the `Iterator` field of `Map` states and in the `Branches` field of `Parallel` states.
1594
1601
  */
1595
1602
  constructor(definition, stateMachineOptions) {
1596
- const { isValid, errorsText } = aslValidator(definition, {
1597
- checkArn: true,
1598
- checkPaths: true,
1599
- ...stateMachineOptions?.validationOptions
1600
- });
1601
- if (!isValid) {
1602
- throw new Error(`State machine definition is invalid, see error(s) below:
1603
+ if (!stateMachineOptions?.validationOptions?._noValidate) {
1604
+ const { isValid, errorsText } = aslValidator(definition, {
1605
+ checkArn: true,
1606
+ checkPaths: true,
1607
+ ...stateMachineOptions?.validationOptions
1608
+ });
1609
+ if (!isValid) {
1610
+ throw new Error(`State machine definition is invalid, see error(s) below:
1603
1611
  ${errorsText("\n")}`);
1612
+ }
1604
1613
  }
1605
1614
  this.definition = definition;
1606
1615
  this.stateMachineOptions = stateMachineOptions;
@@ -1618,6 +1627,7 @@ var StateMachine = class {
1618
1627
  */
1619
1628
  run(input, options) {
1620
1629
  const abortController = new AbortController();
1630
+ const timeoutSeconds = this.definition.TimeoutSeconds ?? DEFAULT_MAX_EXECUTION_TIMEOUT;
1621
1631
  let onAbortHandler;
1622
1632
  const settleOnAbort = new Promise((resolve, reject) => {
1623
1633
  if (options?.noThrowOnAbort) {
@@ -1627,25 +1637,27 @@ var StateMachine = class {
1627
1637
  }
1628
1638
  abortController.signal.addEventListener("abort", onAbortHandler);
1629
1639
  });
1630
- let rejectOnTimeout = null;
1631
- if (this.definition.TimeoutSeconds) {
1632
- rejectOnTimeout = new Promise((_, reject) => {
1633
- setTimeout(() => {
1634
- abortController.signal.removeEventListener("abort", onAbortHandler);
1635
- abortController.abort();
1636
- reject(new StatesTimeoutError());
1637
- }, this.definition.TimeoutSeconds * 1e3);
1638
- });
1639
- }
1640
- const executionResult = this.execute(input, {
1641
- stateMachineOptions: this.stateMachineOptions,
1642
- runOptions: options,
1643
- abortSignal: abortController.signal
1640
+ let timeoutId;
1641
+ const rejectOnTimeout = new Promise((_, reject) => {
1642
+ timeoutId = setTimeout(() => {
1643
+ abortController.signal.removeEventListener("abort", onAbortHandler);
1644
+ abortController.abort();
1645
+ reject(new StatesTimeoutError());
1646
+ }, timeoutSeconds * 1e3);
1644
1647
  });
1645
- const racingPromises = [executionResult, settleOnAbort];
1646
- if (rejectOnTimeout) {
1647
- racingPromises.push(rejectOnTimeout);
1648
- }
1648
+ const executionResult = this.execute(
1649
+ input,
1650
+ {
1651
+ stateMachineOptions: this.stateMachineOptions,
1652
+ runOptions: options,
1653
+ abortSignal: abortController.signal
1654
+ },
1655
+ () => {
1656
+ abortController.signal.removeEventListener("abort", onAbortHandler);
1657
+ clearTimeout(timeoutId);
1658
+ }
1659
+ );
1660
+ const racingPromises = [executionResult, settleOnAbort, rejectOnTimeout];
1649
1661
  const result = Promise.race(racingPromises);
1650
1662
  return {
1651
1663
  abort: () => abortController.abort(),
@@ -1655,7 +1667,7 @@ var StateMachine = class {
1655
1667
  /**
1656
1668
  * Helper method that handles the execution of the machine states and the transitions between them.
1657
1669
  */
1658
- async execute(input, options) {
1670
+ async execute(input, options, cleanupFn) {
1659
1671
  let currState = this.definition.States[this.definition.StartAt];
1660
1672
  let currStateName = this.definition.StartAt;
1661
1673
  let currInput = cloneDeep3(input);
@@ -1673,6 +1685,8 @@ var StateMachine = class {
1673
1685
  } while (!isEndState && !options.abortSignal.aborted);
1674
1686
  } catch (error) {
1675
1687
  throw new ExecutionError(error);
1688
+ } finally {
1689
+ cleanupFn();
1676
1690
  }
1677
1691
  return currResult;
1678
1692
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-local-stepfunctions",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Execute an AWS Step Function locally",
5
5
  "keywords": [
6
6
  "aws",
@@ -35,6 +35,9 @@
35
35
  "browser": "./build/main.browser.esm.js",
36
36
  "default": "./build/main.browser.esm.js"
37
37
  },
38
+ "bin": {
39
+ "local-sfn": "./build/CLI.cjs"
40
+ },
38
41
  "scripts": {
39
42
  "test": "jest",
40
43
  "build": "tsup",
@@ -64,6 +67,7 @@
64
67
  "@aws-sdk/client-lambda": "^3.314.0",
65
68
  "@aws-sdk/credential-providers": "^3.312.0",
66
69
  "asl-validator": "^3.5.1",
70
+ "commander": "^10.0.1",
67
71
  "crypto-js": "^4.1.1",
68
72
  "jsonpath-plus": "^7.2.0",
69
73
  "lodash": "^4.17.21",