Add intrinsic functions and newer Choice operators support#3
Merged
Conversation
Update the dependency stack and CI so the library builds and tests cleanly on the currently supported Node.js releases (20, 24, 26): - deps: asl-validator ^4, jsonpath ^1.1.1 - devDeps: jest 30, eslint 9 (flat config), prettier 3, husky 9, lint-staged 15; drop dead babel-eslint, ts-jest, pretty-quick, codecov npm package - mark the `serverless` peer dependency optional so npm 7+ no longer pulls the end-of-life serverless@1 tree (75 -> 19 advisories), and widen its range - add eslint.config.js (flat config) and .husky/pre-commit; remove .eslintrc - engines: node >=20; CI matrix -> 20.x/24.x/26.x on actions/*@v4 + npm - jest 30 compat: replace removed `toThrowError` with `toThrow` - asl-validator 4 compat: update the invalid-definition assertion message - fix Wait state mutating the shared (require-cached) state definition, which the stricter validator now rejects on re-validation; resolve *Path values into locals instead Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
Incorporates #2 by @MarcosRava. When a Task defined both Parameters and Retry, each retry re-evaluated Parameters against the already-transformed input, compounding the payload on every attempt. Pin the pre-retry input via the execution context so Parameters always resolve against the original input. Co-authored-by: Marcos Rava <MarcosRava@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
Add the post-2020 JSONPath-era ASL features that the library was missing, driven by tests, while keeping the existing API and behavior unchanged: - Intrinsic functions in Parameters/ItemSelector/ResultSelector payload templates: States.Format (with brace/quote escaping), Array, ArrayPartition, ArrayContains, ArrayRange, ArrayGetItem, ArrayLength, ArrayUnique, StringToJson, JsonToString, Base64Encode/Decode, Hash, JsonMerge, MathAdd, MathRandom (seedable), StringSplit and UUID, including nested calls - New Choice operators: StringMatches (glob with escaping), IsNull, IsPresent, IsBoolean, IsNumeric, IsString, IsTimestamp, and every *Path variant - ResultSelector on Task/Map/Parallel (runs before ResultPath/OutputPath) - Map ItemProcessor/ItemSelector accepted as aliases of Iterator/Parameters - refactor the payload-template evaluation into a single recursive helper, which also fixes the previous one-level-deep nesting limitation - fix Choice `Or` matching when only one rule matches (was requiring >1) - fix Parallel draining its shared Branches array, which broke re-execution - eslint: allow avoidEscape so single-quote rule agrees with prettier Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
- README: note Node >=20 (tested on 20/24/26), list the newly supported intrinsic functions, Choice operators, ResultSelector and Map ItemProcessor/ItemSelector, and call out JSONata/Variables as not yet supported - add real index.d.ts typings for the public API (the package already points `types` at it); verified against a strict consumer with @types/node Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
Address two issues found in review of the newer-spec work: - InputPath was discarded whenever a state also had Parameters/ItemSelector, because the payload template was evaluated against the pre-InputPath execution input. Fix the root cause of the retry-input recursion instead: task() now resolves its step input into a local so the closed-over input keeps its original value across retries, which lets inputPath evaluate the template against the (InputPath-narrowed) input again. PR #2's retry test still passes. - A negated data-test Choice operator (e.g. `IsString: false`) matched an absent field. Per AWS only `IsPresent` is meaningful for a missing field; every other data-test now requires the field to be present. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
- add the missing States.MathRandom test (seeded determinism + bounded range) - cover the States.Format placeholder/argument mismatch error path - add StringMatches edge cases: both-end anchoring, `*` spanning dots, case-sensitivity, and prefix requirements Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
…o Map
Finish the Distributed-Map-era inline Map semantics:
- run iterations in batches that respect MaxConcurrency (0/omitted = unlimited,
otherwise the per-execution default), preserving input order in the output
- ItemBatcher: group items into `{ Items: [...], BatchInput }` iteration inputs
- ToleratedFailureCount / ToleratedFailurePercentage: drop tolerated failures
from the result and only fail the Map when the threshold is exceeded
(default 0 keeps the previous "any failure fails the Map" behavior)
Also let Prettier own all formatting: drop the overlapping ESLint stylistic
rules (which conflicted with Prettier on ternary-nested calls) and enforce
formatting in CI via `prettier --check` in the lint script.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
Implement the November 2024 ASL features:
- QueryLanguage: "JSONata" at the machine or state level. `{% %}` expressions
are evaluated with the jsonata package, binding the reserved `$states` object
($states.input/result/context) and in-scope variables. Wired into Pass
(Output), Task (Arguments/Output), Choice (Condition), Map (Items/
ItemSelector/Output) and Parallel (Output).
- Variables: `Assign` in both JSONPath and JSONata modes; `$name` references in
later states. Map/Parallel children may read outer variables but their
assignments are scoped and do not leak back to the parent.
- add jsonata ^2 as a dependency
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
Review of the Variables work found that a Map iteration's (or Parallel branch's) Assign mutated a single shared variable object, so concurrent siblings could clobber each other's variables. Give each iteration/branch its own forked scope via AsyncLocalStorage, which keeps scopes isolated across awaits and concurrency while still letting children read outer variables. Also make the reserved `$states` JSONata binding win over any like-named workflow variable. Adds a regression test for concurrent per-iteration Assign. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG
This was referenced Jun 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR significantly expands AWS Step Functions language support by implementing intrinsic functions and newer Choice state operators, bringing the library closer to full ASL specification compliance.
Summary
The library now supports:
Key Changes
Intrinsic function evaluation: Added comprehensive parsing and evaluation of intrinsic expressions with support for:
Payload template refactoring: Extracted Parameters handling into
evaluatePayloadTemplate()andevaluatePayloadValue()methods to support recursive object resolution and reuse across Parameters, ItemSelector, and ResultSelectorChoice state enhancements:
ResultSelector support: Implemented ResultSelector processing in Task, Map, and Parallel states, running before ResultPath/OutputPath per ASL data-flow pipeline
Retry input stability: Fixed bug where Parameters were re-applied to previous retry attempt's output; now maintains original input across retries
Map state improvements: Added support for ItemProcessor (newer spelling of Iterator) and ItemSelector (newer spelling of per-item Parameters)
Error handling: Added new ErrorState.IntrinsicFailure for intrinsic function errors
Dependencies: Updated asl-validator to v4.0.0 and jsonpath to v1.1.1; added crypto module usage for hash operations
Node.js requirement: Updated minimum Node.js version to 20 (tested on 20, 24, 26)
TypeScript definitions: Added index.d.ts with proper type declarations
Tooling: Migrated to modern ESLint config (v9), updated all dev dependencies, added Prettier formatting
Notable Implementation Details
https://claude.ai/code/session_01QznGWnSQuwvNRAcYinsZiG