You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- add ternary expression support (Choice + Pass states)
- support destructured params and default values in substeps
- support nested/transitive substep inlining
- resolve Lambda ARN from pure function calls
- rename helper → substep in user-facing docs/examples
- update limitations.md for accuracy (5 fixes)
Helpers can contain any supported control flow: `if/else`, `try/catch`, loops, `Promise.all`. Parameters can be input references, service call results, or constants.
178
+
Substeps can contain any supported control flow: `if/else`, `try/catch`, loops, `Promise.all`. Substeps can also call other substeps — the compiler inlines them transitively. Parameters can be input references, service call results, or constants.
179
179
180
-
See [Limitations](./limitations.md#helper-functions) for v1 constraints.
180
+
See [Limitations](./limitations.md#substeps) for constraints.
Copy file name to clipboardExpand all lines: docs/limitations.md
+65-29Lines changed: 65 additions & 29 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,9 +23,9 @@ count -= 3; // OK
23
23
24
24
Workaround: Use compile-time constants (`const TIMEOUT = 30 * 1000` is folded at compile time), or delegate the calculation to a Lambda.
25
25
26
-
## Helper Functions
26
+
## Substeps
27
27
28
-
The compiler supports two kinds of inlineable helper functions:
28
+
The compiler supports two kinds of inlineable functions:
29
29
30
30
### Pure functions (expression inlining)
31
31
@@ -36,12 +36,12 @@ Simple pure functions with a single `return` statement are inlined as expression
36
36
const formatKey = (id:string) =>`order-${id}`;
37
37
```
38
38
39
-
### Async helpers (CFG-level inlining)
39
+
### Async substeps (CFG-level inlining)
40
40
41
-
Module-scope `async` functions that make service calls can be inlined at the call site. The compiler splices the helper's body into the main workflow's state machine — no nested executions, no runtime cost.
41
+
Module-scope `async` functions that make service calls can be inlined at the call site. The compiler splices the substep's body into the main workflow's state machine — no nested executions, no runtime cost.
42
42
43
43
```typescript
44
-
// OK — async helper, inlined into the caller's state machine
44
+
// OK — async substep, inlined into the caller's state machine
- Must be declared at **module scope** (top-level `async function` or `const fn = async () => { ... }`)
64
-
- Parameters must be **simple identifiers** (no destructuring, rest, or defaults)
65
-
-**Single-level only**: helpers can call services but not other async helpers
66
-
- Must be **awaited** at the call site (`await helper(...)`)
64
+
- Parameters can be **simple identifiers**, **object destructuring** (`{ id, name }`), or have **default values** (`retries = 3`). Rest parameters are not supported.
65
+
- Must be **awaited** at the call site (`await mySubstep(...)`)
66
+
- Substeps can call other substeps — the compiler inlines transitively
67
+
68
+
```typescript
69
+
// OK — nested substeps (inlined transitively)
70
+
asyncfunction validate(id:string) { awaitvalidateFn.call({ id }); }
71
+
asyncfunction validateAndEnrich(id:string) {
72
+
awaitvalidate(id); // substep calling another substep
73
+
awaitenrichFn.call({ id });
74
+
}
75
+
```
67
76
68
77
**Not supported:**
69
78
@@ -75,40 +84,48 @@ function calculateTotal(items: Item[]) {
75
84
returntotal;
76
85
}
77
86
78
-
// NOT OK — nested helpers (helper calls another helper)
Workaround for unsupported patterns: Move the logic into a Lambda function.
91
+
Workaround for rest parameters: pass an explicit array parameter instead.
87
92
88
93
## Closures and Variable Capture
89
94
90
-
Parallel `for...of` loops compile to ASL Map states, which have isolated state. Each iteration cannot reference variables from the outer scope:
95
+
Parallel `for...of` loops compile to ASL Map states, which have isolated state. Each iteration cannot reference **runtime variables** from the outer scope (service call results, input-derived values):
91
96
92
97
```typescript
93
-
constprefix=input.prefix;
98
+
constresult=awaitlookupFn.call({ id: input.id });
94
99
95
-
// NOT OK — Map state can't access `prefix`
100
+
// NOT OK — Map state can't access `result` (runtime variable)
Compile-time constants and service bindings **are** accessible inside Map state iterations. Only runtime variables are isolated.
107
+
101
108
Workaround: Use `Steps.sequential()` for sequential iteration (which does allow outer variable access), or restructure the data so each item carries what it needs.
102
109
103
110
## Array Methods
104
111
105
-
`Array.map()`, `Array.filter()`, `Array.reduce()`, and `Array.forEach()` are not supported. These are JavaScript runtime methods with no ASL equivalent.
112
+
Synchronous array methods (`Array.filter()`, `Array.reduce()`) and synchronous `.map()` are not supported — these are JavaScript runtime operations with no ASL equivalent.
106
113
107
114
```typescript
108
-
// NOT OK
115
+
// NOT OK — synchronous .map() cannot be compiled
109
116
const names =items.map(i=>i.name);
110
117
111
-
// OK — use for...of instead
118
+
// OK — async .map() compiles to a Map state (parallel)
119
+
const results =awaititems.map(async (item) => {
120
+
returnawaitprocessItem.call(item);
121
+
});
122
+
123
+
// OK — async .forEach() compiles to a Map state (discard results)
124
+
awaititems.forEach(async (item) => {
125
+
awaitprocessItem.call(item);
126
+
});
127
+
128
+
// OK — for...of also compiles to a Map state
112
129
for (const item ofitems) {
113
130
awaitprocessItem.call(item);
114
131
}
@@ -135,6 +152,8 @@ throw new OrderNotFoundError('Not found'); // OK — compiles to Fail s
135
152
136
153
**Switch fall-through** — each `case` must end with `break`, `return`, or `throw`. Fall-through between cases is not allowed.
137
154
155
+
**Ternary expressions** — `const label = count > 5 ? 'large' : 'small'`**is** supported. The compiler desugars it into a Choice state with two Pass branches. Compile-time constant conditions are folded away entirely.
156
+
138
157
## Dynamic Property Access
139
158
140
159
Computed property names and dynamic indexing cannot be expressed in JSONPath:
@@ -167,26 +186,43 @@ Note: `Math.floor()`, `Math.ceil()`, etc. **are** supported as compile-time cons
167
186
168
187
## Variable Shadowing
169
188
170
-
You cannot redeclare a variable name in a nested scope if it already exists in an outer scope:
189
+
Avoid redeclaring a variable name in a nested scope. ASL uses a flat JSONPath namespace (`$.status`), so shadowing can cause the outer value to be silently overwritten:
171
190
172
191
```typescript
173
192
const status =input.status;
174
193
if (status==='pending') {
175
-
const status ='processing'; //NOT OK — shadows outer `status`
194
+
const status ='processing'; //Avoid — overwrites $.status
176
195
}
177
196
```
178
197
179
-
## Spread in Service Call Arguments
198
+
## Spread Operators
199
+
200
+
Object spread in **service call parameters** is not supported — ASL `Parameters` requires explicit key-value mappings:
Workaround: Use `Steps.merge()` or construct the object with explicit properties.
208
+
209
+
Object spread **is** supported in general object literals when all properties are spreads (`{ ...a, ...b }`), which compiles to `States.JsonMerge`:
210
+
211
+
```typescript
212
+
// OK — pure spread compiles to States.JsonMerge
213
+
const merged = { ...defaults, ...overrides };
214
+
```
180
215
181
-
Object spread (`{ ...obj}`) is not supported in service call parameters. Use `Steps.merge()` or construct the object with explicit properties.
216
+
Mixed spread + plain properties (`{ ...obj, key: value }`) is not yet supported.
182
217
183
218
## Module-Level `let`
184
219
185
-
Only `const` declarations at module scope are folded. `let` and `var`declarations may be reassigned, making their values unresolvable at compile time:
220
+
`const` declarations at module scope are always folded. `let` and `var`with a single assignment are also folded (with a warning suggesting `const`). Reassigned `let`/`var` variables are unresolvable:
186
221
187
222
```typescript
188
223
const MAX_RETRIES =3; // OK — folded
189
-
let retryCount =3; // NOT OK — mutable, not folded
224
+
let retryCount =3; // OK — folded (warning SS709: prefer const)
225
+
let x =1; x=2; // NOT OK — reassigned, not foldable
0 commit comments