Skip to content

Commit 4b84ab5

Browse files
committed
- Destructured constants, enums, default imports resolve at compile time
- Consecutive instanceof catches without else work correctly - Nested arrays in service params build recursively - Late-bound let from await registers as state output - Steps.merge() deep merge support - JSONata and/or precedence fix - Context property access, deferred reassignment, relational comparisons, string method edge cases, switch-continue — new fixtures + tests - CDK construct and compiler entry point expanded - Local runner fixes (intrinsics, JSONata execution, JSONPath, state handlers) - Playground execution fix - Docs updated (getting-started, cdk-integration, language-reference, constants, services) - Package READMEs added for npm
1 parent 1801e08 commit 4b84ab5

31 files changed

+740
-71
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ npx simplesteps compile workflow.ts -o output/ --query-language jsonpath
170170
| `await Promise.all([...])` | Parallel state |
171171
| `await Steps.parallel(branches, { retry })` | Parallel state with retry |
172172
| Deferred-await (`const p = call(); await p`) | Parallel state (auto-batched) |
173-
| `await Steps.delay({ seconds: 30 })` | Wait state |
173+
| `Steps.delay({ seconds: 30 })` | Wait state |
174174
| `Steps.succeed()` | Succeed state (early termination) |
175175
| `throw new Error(msg)` | Fail state |
176176
| `return value` | Succeed / End state |
@@ -211,7 +211,7 @@ npx simplesteps compile workflow.ts -o output/ --query-language jsonpath
211211
- **Whole-program data flow analysis** with constant propagation lattice across modules
212212
- **Cross-file import resolution** with demand-driven analysis and cycle detection
213213
- **Pure function inlining** for compile-time constant derivation
214-
- **64 typed AWS service bindings**16 with optimized integrations (Lambda, DynamoDB, SQS, SNS, EventBridge, S3, Secrets Manager, SSM, ECS, Bedrock, Glue, CodeBuild, Athena, Batch, StepFunction, HttpEndpoint) + Activity tasks + 48 SDK-generated bindings with full type signatures + `Steps.awsSdk()` escape hatch
214+
- **66 typed AWS service bindings**17 with optimized integrations (Lambda, DynamoDB, SQS, SNS, EventBridge, S3, Secrets Manager, SSM, ECS, Bedrock, Glue, CodeBuild, Athena, Batch, StepFunction, HttpEndpoint, Activity) + 48 SDK-generated bindings with full type signatures + `Steps.awsSdk()` escape hatch
215215
- **CDK token propagation** through CloudFormation intrinsics (`Fn::GetAtt`, `Ref`) with synth-time expression auto-detection
216216
- **Source map comments** — optional `sourceMap` flag annotates ASL states with TypeScript source locations
217217
- **Object destructuring** with rest patterns for extracting service call results

docs/cdk-comparison.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ SimpleSteps doesn't replace CDK — it replaces the step function constructs ins
169169
| `new sfn.Choice().when().otherwise()` | `if/else`, `switch/case` |
170170
| `new sfn.Parallel().branch(...)` | `await Promise.all([...])` |
171171
| `new sfn.Map(scope, id, { ... })` | `for (const item of array)` |
172-
| `new sfn.Wait(scope, id, { time })` | `await Steps.delay(...)` |
172+
| `new sfn.Wait(scope, id, { time })` | `Steps.delay(...)` |
173173
| `new sfn.Fail(scope, id, { error })` | `throw new Error(msg)` |
174174
| `task.addCatch(handler)` | `try/catch` |
175175
| `task.addRetry({ ... })` | `{ retry: { ... } }` option |

docs/cdk-integration.md

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,66 @@ Deploy SimpleSteps workflows with AWS CDK.
88
npm install @simplesteps/core @simplesteps/cdk aws-cdk-lib constructs
99
```
1010

11+
## Transformer Setup
12+
13+
The inline `workflow` pattern requires a TypeScript transformer that extracts `Steps.createFunction()` calls from CDK construct props, compiles them to ASL, and injects the result back at build time. Choose one of the following build setups:
14+
15+
### ts-patch (recommended for CDK projects)
16+
17+
```bash
18+
npm install -D ts-patch typescript
19+
```
20+
21+
Add scripts to `package.json`:
22+
23+
```json
24+
{
25+
"scripts": {
26+
"prepare": "ts-patch install -s",
27+
"build": "tspc"
28+
}
29+
}
30+
```
31+
32+
Add the transformer plugin to `tsconfig.json`:
33+
34+
```json
35+
{
36+
"compilerOptions": {
37+
"plugins": [
38+
{ "transform": "@simplesteps/core/transformer" }
39+
]
40+
}
41+
}
42+
```
43+
44+
Run `npm install` to activate — the `prepare` script runs `ts-patch install` automatically. Then use `tspc` (ts-patch compiler) instead of `tsc` for builds.
45+
46+
### Vite
47+
48+
```typescript
49+
import { simpleStepsVitePlugin } from '@simplesteps/core/transformer/plugins/vite';
50+
51+
export default defineConfig({
52+
plugins: [simpleStepsVitePlugin()],
53+
});
54+
```
55+
56+
### esbuild
57+
58+
```typescript
59+
import { simpleStepsEsbuildPlugin } from '@simplesteps/core/transformer/plugins/esbuild';
60+
61+
await esbuild.build({
62+
plugins: [simpleStepsEsbuildPlugin()],
63+
// ...
64+
});
65+
```
66+
67+
Both the Vite and esbuild plugins read `tsconfig.json` from the project root by default. Pass `{ tsconfig: './path/to/tsconfig.json' }` to override.
68+
69+
> **Note:** The `sourceFile` + `bindings` pattern (see [Alternative: Separate Workflow Files](#alternative-separate-workflow-files)) does not require the transformer — the compiler reads the file directly at synth time.
70+
1171
## `SimpleStepsStateMachine`
1272

1373
An L3 CDK construct that compiles a `Steps.createFunction()` workflow to a Step Functions state machine at synth time. Define your workflow inline — service bindings reference CDK resources directly:
@@ -237,21 +297,6 @@ const cancelMachine = new SimpleStepsStateMachine(this, 'CancelOrder', {
237297
- You want to compile and inspect ASL via the CLI without CDK
238298
- Team preference for separating business logic from infrastructure
239299

240-
## CDK Auto-Detection (SS705)
241-
242-
When using the **inline workflow** pattern, the compiler automatically detects CDK construct property accesses (like `myLambda.functionArn` or `myTable.tableName`) and resolves them as synth-time values. This happens transparently — CDK Tokens flow into ASL as-is.
243-
244-
If the compiler recognizes a variable as a CDK synth-time expression via pattern matching (rather than explicit `declare const` binding), it emits warning **SS705**:
245-
246-
```
247-
[SimpleSteps] SS705: CDK synth-time expression 'validateFn.functionArn'
248-
auto-detected for variable 'validateOrderArn'. It will be resolved at CDK synth time.
249-
```
250-
251-
This warning is informational — the compiled output is correct. The compiler detects common CDK property names (`functionArn`, `tableName`, `queueUrl`, `topicArn`, `bucketName`, etc.) and treats them as synth-time constants rather than raising an unresolvable variable error.
252-
253-
To suppress the warning, use the **file-based** pattern with explicit `bindings` instead.
254-
255300
## Starter Project
256301

257302
See [`examples/starters/cdk/`](../examples/starters/cdk/) for a complete, runnable CDK project using inline workflows.

docs/constants.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ The following `Math` methods are evaluated at compile time when all arguments ar
4848
| `Math.ceil(x)` | `Math.ceil(3.2)` -> `4` |
4949
| `Math.round(x)` | `Math.round(3.5)` -> `4` |
5050
| `Math.abs(x)` | `Math.abs(-5)` -> `5` |
51+
| `Math.sqrt(x)` | `Math.sqrt(16)` -> `4` |
52+
| `Math.trunc(x)` | `Math.trunc(3.7)` -> `3` |
53+
| `Math.sign(x)` | `Math.sign(-5)` -> `-1` |
54+
| `Math.log(x)` | `Math.log(1)` -> `0` |
55+
| `Math.log2(x)` | `Math.log2(8)` -> `3` |
56+
| `Math.log10(x)` | `Math.log10(1000)` -> `3` |
5157
| `Math.min(a, b, ...)` | `Math.min(3, 1, 2)` -> `1` |
5258
| `Math.max(a, b, ...)` | `Math.max(3, 1, 2)` -> `3` |
5359
| `Math.pow(x, y)` | `Math.pow(2, 10)` -> `1024` |
@@ -88,7 +94,7 @@ export const workflow = Steps.createFunction(
8894
- **Runtime values** -- function parameters, service call results, `context.*`
8995
- **String methods** -- `'hello'.toUpperCase()` is not folded
9096
- **User-defined functions** -- `myFunction(42)` is not folded
91-
- **Non-Math pure functions** -- only `Math.floor/ceil/round/abs/min/max/pow` are supported
97+
- **Non-Math pure functions** -- only `Math.floor/ceil/round/abs/sqrt/trunc/sign/log/log2/log10/min/max/pow` are supported
9298
- **Constants declared inside `Steps.createFunction()`** -- only module-level constants are folded
9399

94100
See [`examples/showcase/21-constants.ts`](../examples/showcase/21-constants.ts) for a working example.

docs/getting-started.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,41 @@ npm install @simplesteps/core @simplesteps/cdk aws-cdk-lib constructs
1616
npm install @simplesteps/core
1717
```
1818

19+
## Build Setup
20+
21+
The CDK inline `workflow` pattern uses a TypeScript transformer to compile `Steps.createFunction()` calls at build time. Set up ts-patch:
22+
23+
```bash
24+
npm install -D ts-patch
25+
```
26+
27+
Add scripts to `package.json`:
28+
29+
```json
30+
{
31+
"scripts": {
32+
"prepare": "ts-patch install -s",
33+
"build": "tspc"
34+
}
35+
}
36+
```
37+
38+
Add the transformer plugin to `tsconfig.json`:
39+
40+
```json
41+
{
42+
"compilerOptions": {
43+
"plugins": [
44+
{ "transform": "@simplesteps/core/transformer" }
45+
]
46+
}
47+
}
48+
```
49+
50+
Run `npm install` to activate — the `prepare` script runs `ts-patch install` automatically. Then use `tspc` instead of `tsc` for builds.
51+
52+
> For Vite or esbuild alternatives, see [CDK Integration — Transformer Setup](./cdk-integration.md#transformer-setup).
53+
1954
## Your First Workflow
2055

2156
Create `lib/stack.ts`. Everything lives in one file — infrastructure, service bindings, and workflow logic:
@@ -135,7 +170,7 @@ This produces `output/orderWorkflow.asl.json` — useful for reviewing the gener
135170
- [CDK Integration](./cdk-integration.md) -- `SimpleStepsStateMachine` construct, multiple workflows, file-based mode
136171
- [Library API](./library-api.md) -- use the compiler programmatically for custom pipelines
137172
- [CLI Reference](./cli.md) -- all compiler flags
138-
- [Services](./services.md) -- 64 typed AWS service bindings + Steps.awsSdk()
173+
- [Services](./services.md) -- 66 typed AWS service bindings + Steps.awsSdk()
139174
- [Language Reference](./language-reference.md) -- every TypeScript construct and its ASL mapping
140175

141176
## Starter Projects
@@ -145,3 +180,4 @@ Complete, standalone projects you can clone and run:
145180
- [`examples/starters/cdk/`](../examples/starters/cdk/) -- CDK deployment
146181
- [`examples/starters/library-api/`](../examples/starters/library-api/) -- programmatic compilation
147182
- [`examples/starters/cli/`](../examples/starters/cli/) -- CLI compilation
183+
- [`examples/starters/testing/`](../examples/starters/testing/) -- Jest testing with local execution

docs/language-reference.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Every TypeScript construct supported by SimpleSteps and its ASL mapping.
1818
| `await Steps.parallel(branches, opts?)` | Parallel (with Retry on the Parallel state) |
1919
| Deferred-await (`const p = call(); await p`) | Parallel (auto-batched) |
2020
| `await Steps.distributedMap(items, callback, opts)` | Map (DISTRIBUTED mode, S3 I/O) |
21-
| `await Steps.delay({ seconds: 30 })` | Wait |
21+
| `Steps.delay({ seconds: 30 })` | Wait |
2222
| `throw new Error(msg)` | Fail |
2323
| `Steps.succeed()` | Succeed (explicit early termination) |
2424
| `return value` | Succeed / End |
@@ -342,8 +342,8 @@ The compiler detects non-awaited service calls and batches their subsequent awai
342342
### Wait
343343

344344
```typescript
345-
await Steps.delay({ seconds: 30 });
346-
await Steps.delay({ timestamp: '2024-12-31T23:59:59Z' });
345+
Steps.delay({ seconds: 30 });
346+
Steps.delay({ timestamp: '2024-12-31T23:59:59Z' });
347347
```
348348

349349
### Early Return
@@ -440,21 +440,31 @@ The first parameter (`context: SimpleStepContext`) provides execution metadata f
440440

441441
```typescript
442442
const workflow = Steps.createFunction(async (context, input) => {
443-
const execId = context.execution.id; // $$.Execution.Id
444-
const startTime = context.execution.startTime; // $$.Execution.StartTime
445-
const stateName = context.state.name; // $$.State.Name
446-
const retryCount = context.state.retryCount; // $$.State.RetryCount
447-
const machineId = context.stateMachine.id; // $$.StateMachine.Id
443+
const execId = context.execution.id; // $$.Execution.Id
444+
const startTime = context.execution.startTime; // $$.Execution.StartTime
445+
const stateName = context.state.name; // $$.State.Name
446+
const retryCount = context.state.retryCount; // $$.State.RetryCount
447+
const machineId = context.stateMachine.id; // $$.StateMachine.Id
448448
});
449449
```
450450

451451
| Property | ASL Path | Description |
452452
|---|---|---|
453453
| `context.execution.id` | `$$.Execution.Id` | Unique execution ARN |
454-
| `context.execution.startTime` | `$$.Execution.StartTime` | ISO 8601 timestamp |
454+
| `context.execution.name` | `$$.Execution.Name` | Execution name |
455+
| `context.execution.startTime` | `$$.Execution.StartTime` | ISO 8601 start timestamp |
456+
| `context.execution.roleArn` | `$$.Execution.RoleArn` | IAM role ARN |
457+
| `context.execution.input` | `$$.Execution.Input` | Original execution input |
458+
| `context.execution.redriveCount` | `$$.Execution.RedriveCount` | Number of redrives |
459+
| `context.execution.redriveStatus` | `$$.Execution.RedriveStatus` | Redrive status |
455460
| `context.state.name` | `$$.State.Name` | Current state name |
461+
| `context.state.enteredTime` | `$$.State.EnteredTime` | ISO 8601 state entry timestamp |
456462
| `context.state.retryCount` | `$$.State.RetryCount` | Number of retries for current state |
457463
| `context.stateMachine.id` | `$$.StateMachine.Id` | State machine ARN |
464+
| `context.stateMachine.name` | `$$.StateMachine.Name` | State machine name |
465+
| `context.task.token` | `$$.Task.Token` | Task token (callback pattern) |
466+
| `context.map.item.index` | `$$.Map.Item.Index` | Current Map iteration index |
467+
| `context.map.item.value` | `$$.Map.Item.Value` | Current Map iteration value |
458468

459469
The context object is optional — if your workflow doesn't need execution metadata, you can omit it and use only the input parameter.
460470

docs/library-api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ const obj = AslSerializer.serializeToObject(definition);
122122
|---|---|
123123
| `@simplesteps/core` | `compile()`, `compileFromProgram()`, `SimpleSteps`, `SimpleStepsBuilder`, `SimpleStepsCompileError` |
124124
| `@simplesteps/core/runtime` | `Steps`, `SimpleStepContext`, `StepException`, error classes |
125-
| `@simplesteps/core/runtime/services` | 64 typed bindings — `Lambda`, `DynamoDB`, `S3`, `SQS`, `SNS`, `StepFunction`, `EventBridge`, `SecretsManager`, `SSM`, `ECS`, `Bedrock`, `Glue`, `CodeBuild`, `Athena`, `Batch`, `HttpEndpoint`, `Activity`, + 48 SDK-generated (`APIGateway`, `EC2`, `RDS`, `SageMaker`, etc.) |
125+
| `@simplesteps/core/runtime/services` | 66 typed bindings — 17 optimized (`Lambda`, `DynamoDB`, `S3`, `SQS`, `SNS`, `StepFunction`, `EventBridge`, `SecretsManager`, `SSM`, `ECS`, `Bedrock`, `Glue`, `CodeBuild`, `Athena`, `Batch`, `HttpEndpoint`, `Activity`) + 48 SDK-generated (`APIGateway`, `EC2`, `RDS`, `SageMaker`, etc.) |
126126
| `@simplesteps/core/asl` | `AslSerializer`, `AslParser`, `AslValidator`, `AslParseResult`, `AslValidationError`, ASL type definitions |
127127

128128
Full type signatures are available in the published TypeScript declarations (`@simplesteps/core`).

docs/services.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Services
22

3-
SimpleSteps provides typed bindings for 64 AWS services — 16 with optimized integrations (including HTTPS Endpoints), 48 SDK-generated with full type signatures, plus Activity tasks, a callback pattern (`.waitForTaskToken`), and a generic escape hatch (`Steps.awsSdk()`) for any AWS service.
3+
SimpleSteps provides typed bindings for 66 AWS services — 17 with optimized integrations (including HTTPS Endpoints and Activity tasks), 48 SDK-generated with full type signatures, plus a callback pattern (`.waitForTaskToken`) and a generic escape hatch (`Steps.awsSdk()`) for any AWS service.
44

55
All service bindings are compile-time markers. They provide TypeScript types for the compiler and throw if called at runtime.
66

examples/starters/testing/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ let asl: StateMachineDefinition;
3131

3232
beforeAll(() => {
3333
const result = compile({
34-
sourceFiles: ['./workflows/my-workflow.ts'],
34+
tsconfigPath: './tsconfig.workflows.json',
3535
});
3636
asl = JSON.parse(AslSerializer.serialize(result.stateMachines[0].definition));
3737
});

packages/cdk/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ export function compileDefinitionBody(
220220
throw new Error(`No state machines found in ${sourceFile}`);
221221
}
222222

223+
if (result.stateMachines.length > 1) {
224+
const available = result.stateMachines.map(m => m.name).join(', ');
225+
throw new Error(
226+
`Source file produces ${result.stateMachines.length} state machines (${available}). ` +
227+
`Use the SimpleStepsStateMachine construct with stateMachineName to select one.`,
228+
);
229+
}
230+
223231
const definition = AslSerializer.serialize(result.stateMachines[0].definition);
224232
return sfn.DefinitionBody.fromString(definition);
225233
}

0 commit comments

Comments
 (0)