aws-local-stepfunctions 0.3.2 → 0.5.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.
@@ -0,0 +1,964 @@
1
+ var __accessCheck = (obj, member, msg) => {
2
+ if (!member.has(obj))
3
+ throw TypeError("Cannot " + msg);
4
+ };
5
+ var __privateGet = (obj, member, getter) => {
6
+ __accessCheck(obj, member, "read from private field");
7
+ return getter ? getter.call(obj) : member.get(obj);
8
+ };
9
+ var __privateAdd = (obj, member, value) => {
10
+ if (member.has(obj))
11
+ throw TypeError("Cannot add the same private member more than once");
12
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
13
+ };
14
+ var __privateSet = (obj, member, value, setter) => {
15
+ __accessCheck(obj, member, "write to private field");
16
+ setter ? setter.call(obj, value) : member.set(obj, value);
17
+ return value;
18
+ };
19
+
20
+ // src/error/ExecutionAbortedError.ts
21
+ var ExecutionAbortedError = class extends Error {
22
+ constructor() {
23
+ super("Execution aborted");
24
+ this.name = "ExecutionAbortedError";
25
+ }
26
+ };
27
+
28
+ // src/error/ExecutionError.ts
29
+ var _wrappedError;
30
+ var ExecutionError = class extends Error {
31
+ constructor(caughtError) {
32
+ super(`Execution has failed with the following error: ${caughtError.message}`);
33
+ __privateAdd(this, _wrappedError, void 0);
34
+ this.name = "ExecutionError";
35
+ __privateSet(this, _wrappedError, caughtError);
36
+ }
37
+ get getWrappedError() {
38
+ return __privateGet(this, _wrappedError);
39
+ }
40
+ };
41
+ _wrappedError = new WeakMap();
42
+
43
+ // src/util/index.ts
44
+ function isPlainObj(value) {
45
+ return !!value && Object.getPrototypeOf(value) === Object.prototype;
46
+ }
47
+ function sleep(ms, abortSignal) {
48
+ return new Promise((resolve) => {
49
+ const timeout = setTimeout(resolve, ms);
50
+ abortSignal?.addEventListener("abort", () => {
51
+ clearTimeout(timeout);
52
+ });
53
+ });
54
+ }
55
+
56
+ // src/stateMachine/JsonPath.ts
57
+ import { JSONPath as jp } from "jsonpath-plus";
58
+ function jsonPathQuery(pathExpression, json, context) {
59
+ if (pathExpression.startsWith("$$")) {
60
+ return jp({ path: pathExpression.slice(1), json: context ?? null, wrap: false });
61
+ }
62
+ return jp({ path: pathExpression, json, wrap: false });
63
+ }
64
+
65
+ // src/error/RuntimeError.ts
66
+ var RuntimeError = class extends Error {
67
+ constructor(message) {
68
+ super(message);
69
+ this.name = "RuntimeError";
70
+ this.retryable = true;
71
+ this.catchable = true;
72
+ }
73
+ get isRetryable() {
74
+ return this.retryable;
75
+ }
76
+ get isCatchable() {
77
+ return this.catchable;
78
+ }
79
+ };
80
+
81
+ // src/error/predefined/StatesResultPathMatchFailureError.ts
82
+ var StatesResultPathMatchFailureError = class extends RuntimeError {
83
+ constructor() {
84
+ super("States.ResultPathMatchFailure");
85
+ this.name = "States.ResultPathMatchFailure";
86
+ }
87
+ };
88
+
89
+ // src/stateMachine/InputOutputProcessing.ts
90
+ import cloneDeep from "lodash/cloneDeep.js";
91
+ import set from "lodash/set.js";
92
+ function processInputPath(path, input, context) {
93
+ if (path === void 0) {
94
+ return input;
95
+ }
96
+ if (path === null) {
97
+ return {};
98
+ }
99
+ return jsonPathQuery(path, input, context);
100
+ }
101
+ function processPayloadTemplate(payloadTemplate, json, context) {
102
+ const resolvedProperties = Object.entries(payloadTemplate).map(([key, value]) => {
103
+ let sanitizedKey = key;
104
+ let resolvedValue = value;
105
+ if (isPlainObj(value)) {
106
+ resolvedValue = processPayloadTemplate(value, json);
107
+ }
108
+ if (key.endsWith(".$") && typeof value === "string") {
109
+ sanitizedKey = key.replace(".$", "");
110
+ resolvedValue = jsonPathQuery(value, json, context);
111
+ }
112
+ return [sanitizedKey, resolvedValue];
113
+ });
114
+ return Object.fromEntries(resolvedProperties);
115
+ }
116
+ function processResultPath(path, rawInput, result) {
117
+ if (path === void 0) {
118
+ return result;
119
+ }
120
+ if (path === null) {
121
+ return rawInput;
122
+ }
123
+ const sanitizedPath = path.replace("$.", "");
124
+ if (isPlainObj(rawInput)) {
125
+ const clonedRawInput = cloneDeep(rawInput);
126
+ return set(clonedRawInput, sanitizedPath, result);
127
+ }
128
+ throw new StatesResultPathMatchFailureError();
129
+ }
130
+ function processOutputPath(path, result, context) {
131
+ if (path === void 0) {
132
+ return result;
133
+ }
134
+ if (path === null) {
135
+ return {};
136
+ }
137
+ return jsonPathQuery(path, result, context);
138
+ }
139
+
140
+ // src/stateMachine/stateActions/BaseStateAction.ts
141
+ var BaseStateAction = class {
142
+ constructor(stateDefinition) {
143
+ this.stateDefinition = stateDefinition;
144
+ }
145
+ buildExecutionResult(stateResult) {
146
+ const executionResult = { stateResult, nextState: "", isEndState: false };
147
+ if ("Next" in this.stateDefinition) {
148
+ executionResult.nextState = this.stateDefinition.Next;
149
+ }
150
+ if ("End" in this.stateDefinition) {
151
+ executionResult.isEndState = this.stateDefinition.End;
152
+ }
153
+ return executionResult;
154
+ }
155
+ };
156
+
157
+ // src/error/predefined/StatesNoChoiceMatchedError.ts
158
+ var StatesNoChoiceMatchedError = class extends RuntimeError {
159
+ constructor() {
160
+ super("States.NoChoiceMatched");
161
+ this.name = "States.NoChoiceMatched";
162
+ }
163
+ };
164
+
165
+ // src/stateMachine/stateActions/ChoiceStateAction.ts
166
+ import wcmatch from "wildcard-match";
167
+ var ChoiceStateAction = class extends BaseStateAction {
168
+ constructor(stateDefinition) {
169
+ super(stateDefinition);
170
+ }
171
+ testChoiceRule(choiceRule, input) {
172
+ if ("And" in choiceRule) {
173
+ return choiceRule.And.every((rule) => this.testChoiceRule(rule, input));
174
+ }
175
+ if ("Or" in choiceRule) {
176
+ return choiceRule.Or.some((rule) => this.testChoiceRule(rule, input));
177
+ }
178
+ if ("Not" in choiceRule) {
179
+ return !this.testChoiceRule(choiceRule.Not, input);
180
+ }
181
+ if ("StringEquals" in choiceRule) {
182
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
183
+ return varValue === choiceRule.StringEquals;
184
+ }
185
+ if ("StringEqualsPath" in choiceRule) {
186
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
187
+ const stringValue = jsonPathQuery(choiceRule.StringEqualsPath, input);
188
+ return varValue === stringValue;
189
+ }
190
+ if ("StringLessThan" in choiceRule) {
191
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
192
+ return varValue < choiceRule.StringLessThan;
193
+ }
194
+ if ("StringLessThanPath" in choiceRule) {
195
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
196
+ const stringValue = jsonPathQuery(choiceRule.StringLessThanPath, input);
197
+ return varValue < stringValue;
198
+ }
199
+ if ("StringGreaterThan" in choiceRule) {
200
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
201
+ return varValue > choiceRule.StringGreaterThan;
202
+ }
203
+ if ("StringGreaterThanPath" in choiceRule) {
204
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
205
+ const stringValue = jsonPathQuery(choiceRule.StringGreaterThanPath, input);
206
+ return varValue > stringValue;
207
+ }
208
+ if ("StringLessThanEquals" in choiceRule) {
209
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
210
+ return varValue <= choiceRule.StringLessThanEquals;
211
+ }
212
+ if ("StringLessThanEqualsPath" in choiceRule) {
213
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
214
+ const stringValue = jsonPathQuery(choiceRule.StringLessThanEqualsPath, input);
215
+ return varValue <= stringValue;
216
+ }
217
+ if ("StringGreaterThanEquals" in choiceRule) {
218
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
219
+ return varValue >= choiceRule.StringGreaterThanEquals;
220
+ }
221
+ if ("StringGreaterThanEqualsPath" in choiceRule) {
222
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
223
+ const stringValue = jsonPathQuery(choiceRule.StringGreaterThanEqualsPath, input);
224
+ return varValue >= stringValue;
225
+ }
226
+ if ("StringMatches" in choiceRule) {
227
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
228
+ const isMatch = wcmatch(choiceRule.StringMatches, { separator: false });
229
+ return isMatch(varValue);
230
+ }
231
+ if ("NumericEquals" in choiceRule) {
232
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
233
+ return varValue === choiceRule.NumericEquals;
234
+ }
235
+ if ("NumericEqualsPath" in choiceRule) {
236
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
237
+ const numberValue = jsonPathQuery(choiceRule.NumericEqualsPath, input);
238
+ return varValue === numberValue;
239
+ }
240
+ if ("NumericLessThan" in choiceRule) {
241
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
242
+ return varValue < choiceRule.NumericLessThan;
243
+ }
244
+ if ("NumericLessThanPath" in choiceRule) {
245
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
246
+ const numberValue = jsonPathQuery(choiceRule.NumericLessThanPath, input);
247
+ return varValue < numberValue;
248
+ }
249
+ if ("NumericGreaterThan" in choiceRule) {
250
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
251
+ return varValue > choiceRule.NumericGreaterThan;
252
+ }
253
+ if ("NumericGreaterThanPath" in choiceRule) {
254
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
255
+ const numberValue = jsonPathQuery(choiceRule.NumericGreaterThanPath, input);
256
+ return varValue > numberValue;
257
+ }
258
+ if ("NumericLessThanEquals" in choiceRule) {
259
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
260
+ return varValue <= choiceRule.NumericLessThanEquals;
261
+ }
262
+ if ("NumericLessThanEqualsPath" in choiceRule) {
263
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
264
+ const numberValue = jsonPathQuery(choiceRule.NumericLessThanEqualsPath, input);
265
+ return varValue <= numberValue;
266
+ }
267
+ if ("NumericGreaterThanEquals" in choiceRule) {
268
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
269
+ return varValue >= choiceRule.NumericGreaterThanEquals;
270
+ }
271
+ if ("NumericGreaterThanEqualsPath" in choiceRule) {
272
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
273
+ const numberValue = jsonPathQuery(choiceRule.NumericGreaterThanEqualsPath, input);
274
+ return varValue >= numberValue;
275
+ }
276
+ if ("BooleanEquals" in choiceRule) {
277
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
278
+ return varValue === choiceRule.BooleanEquals;
279
+ }
280
+ if ("BooleanEqualsPath" in choiceRule) {
281
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
282
+ const booleanValue = jsonPathQuery(choiceRule.BooleanEqualsPath, input);
283
+ return varValue === booleanValue;
284
+ }
285
+ if ("TimestampEquals" in choiceRule) {
286
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
287
+ const timestampValue = new Date(choiceRule.TimestampEquals);
288
+ return varValue.getTime() === timestampValue.getTime();
289
+ }
290
+ if ("TimestampEqualsPath" in choiceRule) {
291
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
292
+ const timestampValue = new Date(jsonPathQuery(choiceRule.TimestampEqualsPath, input));
293
+ return varValue.getTime() === timestampValue.getTime();
294
+ }
295
+ if ("TimestampLessThan" in choiceRule) {
296
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
297
+ const timestampValue = new Date(choiceRule.TimestampLessThan);
298
+ return varValue < timestampValue;
299
+ }
300
+ if ("TimestampLessThanPath" in choiceRule) {
301
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
302
+ const timestampValue = new Date(jsonPathQuery(choiceRule.TimestampLessThanPath, input));
303
+ return varValue < timestampValue;
304
+ }
305
+ if ("TimestampGreaterThan" in choiceRule) {
306
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
307
+ const timestampValue = new Date(choiceRule.TimestampGreaterThan);
308
+ return varValue > timestampValue;
309
+ }
310
+ if ("TimestampGreaterThanPath" in choiceRule) {
311
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
312
+ const timestampValue = new Date(jsonPathQuery(choiceRule.TimestampGreaterThanPath, input));
313
+ return varValue > timestampValue;
314
+ }
315
+ if ("TimestampLessThanEquals" in choiceRule) {
316
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
317
+ const timestampValue = new Date(choiceRule.TimestampLessThanEquals);
318
+ return varValue <= timestampValue;
319
+ }
320
+ if ("TimestampLessThanEqualsPath" in choiceRule) {
321
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
322
+ const timestampValue = new Date(jsonPathQuery(choiceRule.TimestampLessThanEqualsPath, input));
323
+ return varValue <= timestampValue;
324
+ }
325
+ if ("TimestampGreaterThanEquals" in choiceRule) {
326
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
327
+ const timestampValue = new Date(choiceRule.TimestampGreaterThanEquals);
328
+ return varValue >= timestampValue;
329
+ }
330
+ if ("TimestampGreaterThanEqualsPath" in choiceRule) {
331
+ const varValue = new Date(jsonPathQuery(choiceRule.Variable, input));
332
+ const timestampValue = new Date(jsonPathQuery(choiceRule.TimestampGreaterThanEqualsPath, input));
333
+ return varValue >= timestampValue;
334
+ }
335
+ if ("IsNull" in choiceRule) {
336
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
337
+ const isNullTrue = choiceRule.IsNull;
338
+ return isNullTrue && varValue === null;
339
+ }
340
+ if ("IsPresent" in choiceRule) {
341
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
342
+ const IsPresentTrue = choiceRule.IsPresent;
343
+ return IsPresentTrue && varValue !== void 0;
344
+ }
345
+ if ("IsNumeric" in choiceRule) {
346
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
347
+ const IsNumericTrue = choiceRule.IsNumeric;
348
+ return IsNumericTrue && typeof varValue === "number";
349
+ }
350
+ if ("IsString" in choiceRule) {
351
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
352
+ const IsStringTrue = choiceRule.IsString;
353
+ return IsStringTrue && typeof varValue === "string";
354
+ }
355
+ if ("IsBoolean" in choiceRule) {
356
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
357
+ const IsBooleanTrue = choiceRule.IsBoolean;
358
+ return IsBooleanTrue && typeof varValue === "boolean";
359
+ }
360
+ if ("IsTimestamp" in choiceRule) {
361
+ const varValue = jsonPathQuery(choiceRule.Variable, input);
362
+ const IsTimestampTrue = choiceRule.IsTimestamp;
363
+ return IsTimestampTrue && /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|(\+|-)\d{2}:\d{2})/.test(varValue);
364
+ }
365
+ return false;
366
+ }
367
+ async execute(input, context, options) {
368
+ const state = this.stateDefinition;
369
+ for (const choice of state.Choices) {
370
+ const choiceIsMatch = this.testChoiceRule(choice, input);
371
+ if (choiceIsMatch) {
372
+ return { stateResult: input, nextState: choice.Next, isEndState: false };
373
+ }
374
+ }
375
+ if (state.Default) {
376
+ return { stateResult: input, nextState: state.Default, isEndState: false };
377
+ }
378
+ throw new StatesNoChoiceMatchedError();
379
+ }
380
+ };
381
+
382
+ // src/error/FailStateError.ts
383
+ var FailStateError = class extends RuntimeError {
384
+ constructor(name = "FailStateError", message = "Execution failed because of a Fail state") {
385
+ super(message);
386
+ this.name = name;
387
+ }
388
+ };
389
+
390
+ // src/stateMachine/stateActions/FailStateAction.ts
391
+ var FailStateAction = class extends BaseStateAction {
392
+ constructor(stateDefinition) {
393
+ super(stateDefinition);
394
+ }
395
+ async execute(input, context, options) {
396
+ throw new FailStateError(this.stateDefinition.Error, this.stateDefinition.Cause);
397
+ }
398
+ };
399
+
400
+ // src/error/predefined/StatesRuntimeError.ts
401
+ var StatesRuntimeError = class extends RuntimeError {
402
+ constructor(message = "States.Runtime") {
403
+ super(message);
404
+ this.name = "States.Runtime";
405
+ this.retryable = false;
406
+ this.catchable = false;
407
+ }
408
+ };
409
+
410
+ // src/stateMachine/stateActions/MapStateAction.ts
411
+ import pLimit from "p-limit";
412
+ var DEFAULT_MAX_CONCURRENCY = 40;
413
+ var MapStateAction = class extends BaseStateAction {
414
+ constructor(stateDefinition) {
415
+ super(stateDefinition);
416
+ this.executionAbortFuncs = [];
417
+ }
418
+ processItem(stateMachine, item, input, context, index, options) {
419
+ const state = this.stateDefinition;
420
+ let paramValue;
421
+ context["Map"] = {
422
+ Item: {
423
+ Index: index,
424
+ Value: item
425
+ }
426
+ };
427
+ if (state.Parameters) {
428
+ paramValue = processPayloadTemplate(state.Parameters, input, context);
429
+ }
430
+ const execution = stateMachine.run(paramValue ?? item, options?.runOptions);
431
+ this.executionAbortFuncs.push(execution.abort);
432
+ return execution.result;
433
+ }
434
+ async execute(input, context, options) {
435
+ const state = this.stateDefinition;
436
+ let items = input;
437
+ if (state.ItemsPath) {
438
+ items = jsonPathQuery(state.ItemsPath, input, context);
439
+ }
440
+ if (!Array.isArray(items)) {
441
+ throw new StatesRuntimeError("Input of Map state must be an array or ItemsPath property must point to an array");
442
+ }
443
+ const iteratorStateMachine = new StateMachine(state.Iterator, options?.stateMachineOptions);
444
+ const limit = pLimit(state.MaxConcurrency || DEFAULT_MAX_CONCURRENCY);
445
+ const processedItemsPromise = items.map(
446
+ (item, i) => limit(() => this.processItem(iteratorStateMachine, item, input, context, i, options))
447
+ );
448
+ try {
449
+ const result = await Promise.all(processedItemsPromise);
450
+ return this.buildExecutionResult(result);
451
+ } catch (error) {
452
+ this.executionAbortFuncs.forEach((abort) => abort());
453
+ if (error instanceof ExecutionError) {
454
+ throw error.getWrappedError;
455
+ }
456
+ throw error;
457
+ } finally {
458
+ delete context["Map"];
459
+ }
460
+ }
461
+ };
462
+
463
+ // src/stateMachine/stateActions/ParallelStateAction.ts
464
+ import pLimit2 from "p-limit";
465
+ var DEFAULT_CONCURRENCY = 40;
466
+ var ParallelStateAction = class extends BaseStateAction {
467
+ constructor(stateDefinition) {
468
+ super(stateDefinition);
469
+ this.executionAbortFuncs = [];
470
+ }
471
+ processBranch(branch, input, context, options) {
472
+ const stateMachine = new StateMachine(branch, options?.stateMachineOptions);
473
+ const execution = stateMachine.run(input, options?.runOptions);
474
+ this.executionAbortFuncs.push(execution.abort);
475
+ return execution.result;
476
+ }
477
+ async execute(input, context, options) {
478
+ const state = this.stateDefinition;
479
+ const limit = pLimit2(DEFAULT_CONCURRENCY);
480
+ const processedBranchesPromise = state.Branches.map(
481
+ (branch) => limit(() => this.processBranch(branch, input, context, options))
482
+ );
483
+ try {
484
+ const result = await Promise.all(processedBranchesPromise);
485
+ return this.buildExecutionResult(result);
486
+ } catch (error) {
487
+ this.executionAbortFuncs.forEach((abort) => abort());
488
+ if (error instanceof ExecutionError) {
489
+ throw error.getWrappedError;
490
+ }
491
+ throw error;
492
+ }
493
+ }
494
+ };
495
+
496
+ // src/stateMachine/stateActions/PassStateAction.ts
497
+ var PassStateAction = class extends BaseStateAction {
498
+ constructor(stateDefinition) {
499
+ super(stateDefinition);
500
+ }
501
+ async execute(input, context, options) {
502
+ if (this.stateDefinition.Result) {
503
+ return this.buildExecutionResult(this.stateDefinition.Result);
504
+ }
505
+ return this.buildExecutionResult(input);
506
+ }
507
+ };
508
+
509
+ // src/stateMachine/stateActions/SucceedStateAction.ts
510
+ var SucceedStateAction = class extends BaseStateAction {
511
+ constructor(stateDefinition) {
512
+ super(stateDefinition);
513
+ }
514
+ async execute(input, context, options) {
515
+ return { stateResult: input, nextState: "", isEndState: true };
516
+ }
517
+ };
518
+
519
+ // src/aws/LambdaClient.ts
520
+ import { LambdaClient as AWSLambdaClient, InvokeCommand } from "@aws-sdk/client-lambda";
521
+ import { fromCognitoIdentityPool } from "@aws-sdk/credential-providers";
522
+ var LambdaClient = class {
523
+ constructor(config) {
524
+ this.client = new AWSLambdaClient({});
525
+ if (config) {
526
+ if (!config.region) {
527
+ throw new Error("`awsConfig` option was specified for state machine, but `region` property is not set");
528
+ }
529
+ if (config.credentials) {
530
+ const credentialsTypes = Object.keys(config.credentials);
531
+ const credentialsNames = credentialsTypes.map((name) => `\`${name}\``).join(", ");
532
+ if (credentialsTypes.length > 1) {
533
+ throw new Error(
534
+ `More than one type of AWS credentials were specified: ${credentialsNames}. Only one type may be specified`
535
+ );
536
+ }
537
+ }
538
+ if (config.credentials?.cognitoIdentityPool) {
539
+ this.client = new AWSLambdaClient({
540
+ region: config.region,
541
+ credentials: fromCognitoIdentityPool({
542
+ ...config.credentials.cognitoIdentityPool,
543
+ clientConfig: { region: config.region }
544
+ })
545
+ });
546
+ } else if (config.credentials?.accessKeys) {
547
+ this.client = new AWSLambdaClient({ region: config.region, credentials: config.credentials.accessKeys });
548
+ }
549
+ }
550
+ }
551
+ async invokeFunction(funcNameOrArn, payload) {
552
+ const payloadBuffer = new TextEncoder().encode(JSON.stringify(payload));
553
+ const invokeCommand = new InvokeCommand({
554
+ FunctionName: funcNameOrArn,
555
+ Payload: payloadBuffer
556
+ });
557
+ const invocationResult = await this.client.send(invokeCommand);
558
+ let resultValue = null;
559
+ if (invocationResult.Payload) {
560
+ resultValue = new TextDecoder().decode(invocationResult.Payload);
561
+ resultValue = JSON.parse(resultValue);
562
+ }
563
+ if (invocationResult.FunctionError) {
564
+ const errorResult = resultValue;
565
+ throw new FailStateError(errorResult.errorType, `Execution of Lambda function "${funcNameOrArn}" failed`);
566
+ }
567
+ return resultValue;
568
+ }
569
+ };
570
+
571
+ // src/stateMachine/stateActions/TaskStateAction.ts
572
+ var TaskStateAction = class extends BaseStateAction {
573
+ constructor(stateDefinition) {
574
+ super(stateDefinition);
575
+ }
576
+ async execute(input, context, options) {
577
+ const state = this.stateDefinition;
578
+ if (options?.overrideFn) {
579
+ const result2 = await options.overrideFn(input);
580
+ return this.buildExecutionResult(result2);
581
+ }
582
+ const lambdaClient = new LambdaClient(options?.awsConfig);
583
+ const result = await lambdaClient.invokeFunction(state.Resource, input);
584
+ return this.buildExecutionResult(result);
585
+ }
586
+ };
587
+
588
+ // src/stateMachine/stateActions/WaitStateAction.ts
589
+ var WaitStateAction = class extends BaseStateAction {
590
+ constructor(stateDefinition) {
591
+ super(stateDefinition);
592
+ }
593
+ async execute(input, context, options) {
594
+ const state = this.stateDefinition;
595
+ if (options?.waitTimeOverrideOption !== void 0) {
596
+ await sleep(options.waitTimeOverrideOption, options.abortSignal);
597
+ return this.buildExecutionResult(input);
598
+ }
599
+ if (state.Seconds) {
600
+ await sleep(state.Seconds * 1e3, options?.abortSignal);
601
+ } else if (state.Timestamp) {
602
+ const dateTimestamp = new Date(state.Timestamp);
603
+ const currentTime = Date.now();
604
+ const timeDiff = dateTimestamp.getTime() - currentTime;
605
+ await sleep(timeDiff, options?.abortSignal);
606
+ } else if (state.SecondsPath) {
607
+ const seconds = jsonPathQuery(state.SecondsPath, input, context);
608
+ await sleep(seconds * 1e3, options?.abortSignal);
609
+ } else if (state.TimestampPath) {
610
+ const timestamp = jsonPathQuery(state.TimestampPath, input, context);
611
+ const dateTimestamp = new Date(timestamp);
612
+ const currentTime = Date.now();
613
+ const timeDiff = dateTimestamp.getTime() - currentTime;
614
+ await sleep(timeDiff, options?.abortSignal);
615
+ }
616
+ return this.buildExecutionResult(input);
617
+ }
618
+ };
619
+
620
+ // src/error/predefined/StatesTimeoutError.ts
621
+ var StatesTimeoutError = class extends RuntimeError {
622
+ constructor() {
623
+ super("States.Timeout");
624
+ this.name = "States.Timeout";
625
+ }
626
+ };
627
+
628
+ // src/stateMachine/StateExecutor.ts
629
+ import cloneDeep2 from "lodash/cloneDeep.js";
630
+ var DEFAULT_MAX_ATTEMPTS = 3;
631
+ var DEFAULT_INTERVAL_SECONDS = 1;
632
+ var DEFAULT_BACKOFF_RATE = 2;
633
+ var WILDCARD_ERROR = "States.ALL";
634
+ var TASK_STATE_WILDCARD_ERROR = "States.TaskFailed";
635
+ var StateExecutor = class {
636
+ constructor(stateName, stateDefinition) {
637
+ this.stateName = stateName;
638
+ this.stateDefinition = stateDefinition;
639
+ this.retrierAttempts = "Retry" in this.stateDefinition ? new Array(this.stateDefinition.Retry.length).fill(0) : [];
640
+ this.stateHandlers = {
641
+ Task: this.executeTaskState,
642
+ Parallel: this.executeParallelState,
643
+ Map: this.executeMapState,
644
+ Pass: this.executePassState,
645
+ Wait: this.executeWaitState,
646
+ Choice: this.executeChoiceState,
647
+ Succeed: this.executeSucceedState,
648
+ Fail: this.executeFailState
649
+ };
650
+ }
651
+ /**
652
+ * Execute the current state.
653
+ */
654
+ async execute(input, context, options) {
655
+ const rawInput = cloneDeep2(input);
656
+ try {
657
+ const processedInput = this.processInput(input, context);
658
+ const {
659
+ stateResult: currResult,
660
+ nextState,
661
+ isEndState
662
+ } = await this.stateHandlers[this.stateDefinition.Type](
663
+ // @ts-expect-error Indexing `this.stateActions` by non-literal value produces a `never` type for the `this.stateDefinition` parameter of the handler being called
664
+ this.stateDefinition,
665
+ processedInput,
666
+ context,
667
+ this.stateName,
668
+ options
669
+ );
670
+ const processedResult = this.processResult(currResult, rawInput, context);
671
+ return { stateResult: processedResult, nextState, isEndState };
672
+ } catch (error) {
673
+ const { shouldRetry, waitTimeBeforeRetry } = this.shouldRetry(error);
674
+ if (shouldRetry && waitTimeBeforeRetry) {
675
+ await sleep(waitTimeBeforeRetry, options.abortSignal);
676
+ return this.execute(input, context, options);
677
+ }
678
+ const { nextState, errorOutput, resultPath } = this.catchError(error);
679
+ if (nextState && errorOutput) {
680
+ return { stateResult: processResultPath(resultPath, rawInput, errorOutput), nextState, isEndState: false };
681
+ }
682
+ throw error;
683
+ }
684
+ }
685
+ /**
686
+ * Process the current input according to the `InputPath` and `Parameters` fields.
687
+ */
688
+ processInput(input, context) {
689
+ let processedInput = input;
690
+ if ("InputPath" in this.stateDefinition) {
691
+ processedInput = processInputPath(this.stateDefinition.InputPath, processedInput, context);
692
+ }
693
+ if ("Parameters" in this.stateDefinition && this.stateDefinition.Type !== "Map") {
694
+ processedInput = processPayloadTemplate(this.stateDefinition.Parameters, processedInput, context);
695
+ }
696
+ return processedInput;
697
+ }
698
+ /**
699
+ * Process the current result according to the `ResultSelector`, `ResultPath` and `OutputPath` fields.
700
+ */
701
+ processResult(result, rawInput, context) {
702
+ let processedResult = result;
703
+ if ("ResultSelector" in this.stateDefinition) {
704
+ processedResult = processPayloadTemplate(this.stateDefinition.ResultSelector, processedResult, context);
705
+ }
706
+ if ("ResultPath" in this.stateDefinition) {
707
+ processedResult = processResultPath(this.stateDefinition.ResultPath, rawInput, processedResult);
708
+ }
709
+ if ("OutputPath" in this.stateDefinition) {
710
+ processedResult = processOutputPath(this.stateDefinition.OutputPath, processedResult, context);
711
+ }
712
+ return processedResult;
713
+ }
714
+ /**
715
+ * Decide whether this state should be retried, according to the `Retry` field.
716
+ */
717
+ shouldRetry(error) {
718
+ if (!("Retry" in this.stateDefinition)) {
719
+ return { shouldRetry: false };
720
+ }
721
+ for (let i = 0; i < this.stateDefinition.Retry.length; i++) {
722
+ const retrier = this.stateDefinition.Retry[i];
723
+ const maxAttempts = retrier.MaxAttempts ?? DEFAULT_MAX_ATTEMPTS;
724
+ const intervalSeconds = retrier.IntervalSeconds ?? DEFAULT_INTERVAL_SECONDS;
725
+ const backoffRate = retrier.BackoffRate ?? DEFAULT_BACKOFF_RATE;
726
+ const waitTimeBeforeRetry = intervalSeconds * Math.pow(backoffRate, this.retrierAttempts[i]) * 1e3;
727
+ const retryable = error.isRetryable ?? true;
728
+ for (const retrierError of retrier.ErrorEquals) {
729
+ const isErrorMatch = retrierError === error.name;
730
+ const isErrorWildcard = retrierError === WILDCARD_ERROR;
731
+ const isErrorTaskWildcard = retrierError === TASK_STATE_WILDCARD_ERROR && this.stateDefinition.Type === "Task" && !(error instanceof StatesTimeoutError);
732
+ const maybeShouldRetry = retryable && (isErrorMatch || isErrorWildcard || isErrorTaskWildcard);
733
+ if (maybeShouldRetry) {
734
+ if (this.retrierAttempts[i] >= maxAttempts)
735
+ return { shouldRetry: false };
736
+ this.retrierAttempts[i]++;
737
+ return { shouldRetry: true, waitTimeBeforeRetry };
738
+ }
739
+ }
740
+ }
741
+ return { shouldRetry: false };
742
+ }
743
+ /**
744
+ * Try to match the current error with a catcher, according to the `Catch` field.
745
+ */
746
+ catchError(error) {
747
+ if (!("Catch" in this.stateDefinition)) {
748
+ return { nextState: "" };
749
+ }
750
+ for (let i = 0; i < this.stateDefinition.Catch.length; i++) {
751
+ const catcher = this.stateDefinition.Catch[i];
752
+ const catchable = error.isCatchable ?? true;
753
+ for (const catcherError of catcher.ErrorEquals) {
754
+ const isErrorMatch = catcherError === error.name;
755
+ const isErrorWildcard = catcherError === WILDCARD_ERROR;
756
+ const isErrorTaskWildcard = catcherError === TASK_STATE_WILDCARD_ERROR && this.stateDefinition.Type === "Task" && !(error instanceof StatesTimeoutError);
757
+ const shouldCatch = catchable && (isErrorMatch || isErrorWildcard || isErrorTaskWildcard);
758
+ if (shouldCatch) {
759
+ const nextState = catcher.Next;
760
+ const errorOutput = {
761
+ Error: error.name,
762
+ Cause: error.message
763
+ };
764
+ const resultPath = catcher.ResultPath;
765
+ return { nextState, errorOutput, resultPath };
766
+ }
767
+ }
768
+ }
769
+ return { nextState: "" };
770
+ }
771
+ /**
772
+ * Handler for task states.
773
+ *
774
+ * Invokes the Lambda function specified in the `Resource` field
775
+ * and sets the current result of the state machine to the value returned by the Lambda.
776
+ */
777
+ async executeTaskState(stateDefinition, input, context, stateName, options) {
778
+ const overrideFn = options.runOptions?.overrides?.taskResourceLocalHandlers?.[stateName];
779
+ const awsConfig = options.stateMachineOptions?.awsConfig;
780
+ const taskStateAction = new TaskStateAction(stateDefinition);
781
+ const executionResult = await taskStateAction.execute(input, context, { overrideFn, awsConfig });
782
+ return executionResult;
783
+ }
784
+ /**
785
+ * Handler for parallel states.
786
+ *
787
+ * Creates a new state machine for each of the branches specified in the `Branches` field,
788
+ * and then executes each branch state machine by passing them the Parallel state input.
789
+ */
790
+ async executeParallelState(stateDefinition, input, context, stateName, options) {
791
+ const parallelStateAction = new ParallelStateAction(stateDefinition);
792
+ const executionResult = await parallelStateAction.execute(input, context, {
793
+ stateMachineOptions: options.stateMachineOptions,
794
+ runOptions: options.runOptions
795
+ });
796
+ return executionResult;
797
+ }
798
+ /**
799
+ * Handler for map states.
800
+ *
801
+ * Iterates over the current input items or the items of an array specified
802
+ * by the `ItemsPath` field, and then processes each item by passing it
803
+ * as the input to the state machine specified in the `Iterator` field.
804
+ */
805
+ async executeMapState(stateDefinition, input, context, stateName, options) {
806
+ const mapStateAction = new MapStateAction(stateDefinition);
807
+ const executionResult = await mapStateAction.execute(input, context, {
808
+ stateMachineOptions: options.stateMachineOptions,
809
+ runOptions: options.runOptions
810
+ });
811
+ return executionResult;
812
+ }
813
+ /**
814
+ * Handler for pass states.
815
+ *
816
+ * If the `Result` field is specified, copies `Result` into the current result.
817
+ * Else, copies the current input into the current result.
818
+ */
819
+ async executePassState(stateDefinition, input, context, stateName, options) {
820
+ const passStateAction = new PassStateAction(stateDefinition);
821
+ const executionResult = await passStateAction.execute(input, context);
822
+ return executionResult;
823
+ }
824
+ /**
825
+ * Handler for wait states.
826
+ *
827
+ * Pauses the state machine execution for a certain amount of time
828
+ * based on one of the `Seconds`, `Timestamp`, `SecondsPath` or `TimestampPath` fields.
829
+ */
830
+ async executeWaitState(stateDefinition, input, context, stateName, options) {
831
+ const waitTimeOverrideOption = options.runOptions?.overrides?.waitTimeOverrides?.[stateName];
832
+ const abortSignal = options.abortSignal;
833
+ const waitStateAction = new WaitStateAction(stateDefinition);
834
+ const executionResult = await waitStateAction.execute(input, context, {
835
+ waitTimeOverrideOption,
836
+ abortSignal
837
+ });
838
+ return executionResult;
839
+ }
840
+ /**
841
+ * Handler for choice states.
842
+ *
843
+ * Evaluates each choice rule specified in the `Choices` field.
844
+ *
845
+ * If one of the rules matches, then the state machine transitions to the
846
+ * state specified in the `Next` field for that choice rule.
847
+ *
848
+ * If no rule matches but the `Default` field is specified,
849
+ * then the next state will be the state specified in said field.
850
+ *
851
+ * If no rule matches and the `Default` field is not specified, throws a
852
+ * States.NoChoiceMatched error.
853
+ */
854
+ async executeChoiceState(stateDefinition, input, context, stateName, options) {
855
+ const choiceStateAction = new ChoiceStateAction(stateDefinition);
856
+ const executionResult = await choiceStateAction.execute(input, context);
857
+ return executionResult;
858
+ }
859
+ /**
860
+ * Handler for succeed states.
861
+ *
862
+ * Ends the state machine execution successfully.
863
+ */
864
+ async executeSucceedState(stateDefinition, input, context, stateName, options) {
865
+ const succeedStateAction = new SucceedStateAction(stateDefinition);
866
+ const executionResult = await succeedStateAction.execute(input, context);
867
+ return executionResult;
868
+ }
869
+ /**
870
+ * Handler for fail states.
871
+ *
872
+ * Ends the state machine execution and marks it as a failure.
873
+ */
874
+ async executeFailState(stateDefinition, input, context, stateName, options) {
875
+ const failStateAction = new FailStateAction(stateDefinition);
876
+ const executionResult = await failStateAction.execute(input, context);
877
+ return executionResult;
878
+ }
879
+ };
880
+
881
+ // src/stateMachine/StateMachine.ts
882
+ import aslValidator from "asl-validator";
883
+ import cloneDeep3 from "lodash/cloneDeep.js";
884
+ var StateMachine = class {
885
+ /**
886
+ * Constructs a new state machine.
887
+ * @param definition The state machine definition defined using the Amazon States Language (https://states-language.net/spec.html).
888
+ * @param stateMachineOptions Options to control certain settings of the state machine.
889
+ * These options also apply to state machines defined in the `Iterator` field of `Map` states and in the `Branches` field of `Parallel` states.
890
+ */
891
+ constructor(definition, stateMachineOptions) {
892
+ const { isValid, errorsText } = aslValidator(definition, {
893
+ checkArn: true,
894
+ checkPaths: true,
895
+ ...stateMachineOptions?.validationOptions
896
+ });
897
+ if (!isValid) {
898
+ throw new Error(`State machine definition is invalid, see error(s) below:
899
+ ${errorsText("\n")}`);
900
+ }
901
+ this.definition = definition;
902
+ this.stateMachineOptions = stateMachineOptions;
903
+ }
904
+ /**
905
+ * Executes the state machine, running through the states specified in the definition.
906
+ * If the execution fails, the result will throw an `ExecutionError` explaining why the
907
+ * execution failed.
908
+ *
909
+ * By default, if the execution is aborted, the result will throw an `ExecutionAbortedError`. This behavior can be changed by setting
910
+ * the `noThrowOnAbort` option to `true`, in which case the result will be `null`.
911
+ *
912
+ * @param input The input to pass to this state machine execution.
913
+ * @param options Miscellaneous options to control certain behaviors of the execution.
914
+ */
915
+ run(input, options) {
916
+ const abortController = new AbortController();
917
+ const settleOnAbort = new Promise((resolve, reject) => {
918
+ if (options?.noThrowOnAbort) {
919
+ abortController.signal.addEventListener("abort", () => resolve(null));
920
+ } else {
921
+ abortController.signal.addEventListener("abort", () => reject(new ExecutionAbortedError()));
922
+ }
923
+ });
924
+ const executionResult = this.execute(input, {
925
+ stateMachineOptions: this.stateMachineOptions,
926
+ runOptions: options,
927
+ abortSignal: abortController.signal
928
+ });
929
+ const result = Promise.race([executionResult, settleOnAbort]);
930
+ return {
931
+ abort: () => abortController.abort(),
932
+ result
933
+ };
934
+ }
935
+ /**
936
+ * Helper method that handles the execution of the machine states and the transitions between them.
937
+ */
938
+ async execute(input, options) {
939
+ let currState = this.definition.States[this.definition.StartAt];
940
+ let currStateName = this.definition.StartAt;
941
+ let currInput = cloneDeep3(input);
942
+ let currResult = null;
943
+ let nextState = "";
944
+ let isEndState = false;
945
+ let context = {};
946
+ try {
947
+ do {
948
+ const stateExecutor = new StateExecutor(currStateName, currState);
949
+ ({ stateResult: currResult, nextState, isEndState } = await stateExecutor.execute(currInput, context, options));
950
+ currInput = currResult;
951
+ currState = this.definition.States[nextState];
952
+ currStateName = nextState;
953
+ } while (!isEndState && !options.abortSignal.aborted);
954
+ } catch (error) {
955
+ throw new ExecutionError(error);
956
+ }
957
+ return currResult;
958
+ }
959
+ };
960
+ export {
961
+ ExecutionAbortedError,
962
+ ExecutionError,
963
+ StateMachine
964
+ };