@lafken/state-machine 0.10.1
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/LICENCE +21 -0
- package/README.md +383 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +18 -0
- package/lib/main/index.d.ts +2 -0
- package/lib/main/index.js +18 -0
- package/lib/main/param/index.d.ts +2 -0
- package/lib/main/param/index.js +18 -0
- package/lib/main/param/param.d.ts +105 -0
- package/lib/main/param/param.js +115 -0
- package/lib/main/param/param.types.d.ts +77 -0
- package/lib/main/param/param.types.js +2 -0
- package/lib/main/state-machine/index.d.ts +2 -0
- package/lib/main/state-machine/index.js +18 -0
- package/lib/main/state-machine/state-machine.d.ts +113 -0
- package/lib/main/state-machine/state-machine.js +133 -0
- package/lib/main/state-machine/state-machine.types.d.ts +977 -0
- package/lib/main/state-machine/state-machine.types.js +7 -0
- package/lib/resolver/index.d.ts +1 -0
- package/lib/resolver/index.js +17 -0
- package/lib/resolver/resolver.d.ts +6 -0
- package/lib/resolver/resolver.js +28 -0
- package/lib/resolver/state-machine/schema/schema.d.ts +34 -0
- package/lib/resolver/state-machine/schema/schema.js +431 -0
- package/lib/resolver/state-machine/schema/schema.types.d.ts +149 -0
- package/lib/resolver/state-machine/schema/schema.types.js +2 -0
- package/lib/resolver/state-machine/schema/schema.utils.d.ts +15 -0
- package/lib/resolver/state-machine/schema/schema.utils.js +53 -0
- package/lib/resolver/state-machine/state-machine.d.ts +19 -0
- package/lib/resolver/state-machine/state-machine.js +96 -0
- package/lib/resolver/state-machine/state-machine.types.d.ts +7 -0
- package/lib/resolver/state-machine/state-machine.types.js +2 -0
- package/package.json +87 -0
package/LICENCE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Aníbal Emilio Jorquera Cornejo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
# @lafken/state-machine
|
|
2
|
+
|
|
3
|
+
Define AWS Step Functions state machines using TypeScript decorators. `@lafken/state-machine` lets you declare states, transitions, service integrations, and nested workflows directly within your classes — no raw JSON or ASL required.
|
|
4
|
+
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> This library exclusively supports **JSONata** for data transformation and querying. **JSONPath is not supported**.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @lafken/state-machine
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Getting Started
|
|
15
|
+
|
|
16
|
+
Register the `StateMachineResolver` in your application and define your first workflow:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { createApp, createModule } from '@lafken/main';
|
|
20
|
+
import { StateMachineResolver } from '@lafken/state-machine/resolver';
|
|
21
|
+
import { StateMachine, State, Event } from '@lafken/state-machine/main';
|
|
22
|
+
|
|
23
|
+
// 1. Define the state machine
|
|
24
|
+
@StateMachine({
|
|
25
|
+
startAt: 'processOrder',
|
|
26
|
+
})
|
|
27
|
+
class OrderWorkflow {
|
|
28
|
+
@State({ next: 'notifyCustomer' })
|
|
29
|
+
processOrder(@Event('{% $states.input %}') order: any) {
|
|
30
|
+
return { orderId: order.id, status: 'processed' };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@State({ end: true })
|
|
34
|
+
notifyCustomer(@Event('{% $states.input %}') data: any) {
|
|
35
|
+
console.log(`Order ${data.orderId} completed`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 2. Register it in a module
|
|
40
|
+
const orderModule = createModule({
|
|
41
|
+
name: 'orders',
|
|
42
|
+
resources: [OrderWorkflow],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// 3. Add the resolver to your app
|
|
46
|
+
createApp({
|
|
47
|
+
name: 'my-app',
|
|
48
|
+
resolvers: [new StateMachineResolver()],
|
|
49
|
+
modules: [orderModule],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
### Defining a State Machine
|
|
56
|
+
|
|
57
|
+
Use the `@StateMachine` decorator to mark a class as a state machine resource. The `startAt` property defines the entry point of the workflow and must reference either a method name or an inline state definition.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { StateMachine, State } from '@lafken/state-machine/main';
|
|
61
|
+
|
|
62
|
+
@StateMachine({
|
|
63
|
+
startAt: 'validate',
|
|
64
|
+
})
|
|
65
|
+
export class PaymentWorkflow {
|
|
66
|
+
@State({ next: 'charge' })
|
|
67
|
+
validate(@Event('{% $states.input %}') input: any) {
|
|
68
|
+
return { amount: input.amount, valid: true };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@State({ end: true })
|
|
72
|
+
charge(@Event('{% $states.input %}') input: any) {
|
|
73
|
+
return { charged: input.amount };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You can also start with a declarative state instead of a Lambda task:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
@StateMachine({
|
|
82
|
+
startAt: {
|
|
83
|
+
type: 'wait',
|
|
84
|
+
seconds: 5,
|
|
85
|
+
next: { type: 'succeed' },
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
export class DelayedWorkflow {}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Lambda Tasks
|
|
92
|
+
|
|
93
|
+
The `@State` decorator turns a method into a Lambda-backed task within the state machine. Step Functions will invoke the Lambda automatically during execution.
|
|
94
|
+
|
|
95
|
+
Use `next` to chain to the following state, or `end: true` to mark it as a terminal state:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
@StateMachine({ startAt: 'enrich' })
|
|
99
|
+
export class DataPipeline {
|
|
100
|
+
@State({ next: 'store' })
|
|
101
|
+
enrich(@Event('{% $states.input %}') record: any) {
|
|
102
|
+
return { ...record, enrichedAt: new Date().toISOString() };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@State({ end: true })
|
|
106
|
+
store(@Event('{% $states.input %}') record: any) {
|
|
107
|
+
console.log('Storing record:', record);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Output Transformation
|
|
113
|
+
|
|
114
|
+
Use the `output` option to transform a state's result before passing it to the next state. It accepts a JSONata expression or a plain object:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
@State({
|
|
118
|
+
next: 'process',
|
|
119
|
+
output: '{% $states.result.data %}',
|
|
120
|
+
})
|
|
121
|
+
fetchData() {
|
|
122
|
+
return { data: { items: [1, 2, 3] }, metadata: {} };
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### State Assignment
|
|
127
|
+
|
|
128
|
+
Use `assign` to add or update values in the state machine context. Assigned values persist across subsequent states:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
@State({
|
|
132
|
+
next: 'sendEmail',
|
|
133
|
+
assign: { attemptCount: '{% $states.input.attemptCount + 1 %}' },
|
|
134
|
+
})
|
|
135
|
+
retry(@Event('{% $states.input %}') input: any) {
|
|
136
|
+
return input;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### AWS Service Integrations
|
|
141
|
+
|
|
142
|
+
Instead of invoking a Lambda function, a state can directly call an AWS service API. Use the `integrationResource` property to specify the service ARN, and the `@IntegrationOptions` decorator to access resource references.
|
|
143
|
+
|
|
144
|
+
This pattern eliminates the need for intermediate Lambda functions when you just need to call an AWS service (DynamoDB, SQS, SNS, etc.).
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import {
|
|
148
|
+
StateMachine,
|
|
149
|
+
State,
|
|
150
|
+
Event,
|
|
151
|
+
type IntegrationOptionsParams,
|
|
152
|
+
} from '@lafken/state-machine/main';
|
|
153
|
+
import { IntegrationOptions } from '@lafken/api/main';
|
|
154
|
+
|
|
155
|
+
@StateMachine({
|
|
156
|
+
startAt: 'saveItem',
|
|
157
|
+
services: ['dynamodb'],
|
|
158
|
+
})
|
|
159
|
+
export class InventoryWorkflow {
|
|
160
|
+
@State({
|
|
161
|
+
integrationResource: 'arn:aws:states:::dynamodb:putItem',
|
|
162
|
+
next: 'confirm',
|
|
163
|
+
})
|
|
164
|
+
saveItem(@IntegrationOptions() { getResourceValue }: IntegrationOptionsParams) {
|
|
165
|
+
return {
|
|
166
|
+
TableName: getResourceValue('dynamo::inventory', 'id'),
|
|
167
|
+
Item: {
|
|
168
|
+
sku: { S: '{% $states.input.sku %}' },
|
|
169
|
+
quantity: { N: '{% $string($states.input.quantity) %}' },
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@State({ end: true })
|
|
175
|
+
confirm(@Event('{% $states.input %}') e: any) {
|
|
176
|
+
console.log('Item saved successfully');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
A read example using `getItem`:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
@State({
|
|
185
|
+
integrationResource: 'arn:aws:states:::dynamodb:getItem',
|
|
186
|
+
next: 'processResult',
|
|
187
|
+
output: '{% $states.result.Item %}',
|
|
188
|
+
})
|
|
189
|
+
lookupUser(@IntegrationOptions() { getResourceValue }: IntegrationOptionsParams) {
|
|
190
|
+
return {
|
|
191
|
+
TableName: getResourceValue('dynamo::users', 'id'),
|
|
192
|
+
Key: {
|
|
193
|
+
email: { S: '{% $states.input.email %}' },
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Nested State Machines
|
|
200
|
+
|
|
201
|
+
Complex workflows often require sub-workflows for parallel execution or iteration over collections. Use `@NestedStateMachine` to define these embedded workflows.
|
|
202
|
+
|
|
203
|
+
#### Map State
|
|
204
|
+
|
|
205
|
+
A Map state iterates over a collection and executes a set of states for each item:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import { StateMachine, NestedStateMachine, State, Event } from '@lafken/state-machine/main';
|
|
209
|
+
|
|
210
|
+
@NestedStateMachine({
|
|
211
|
+
startAt: 'processItem',
|
|
212
|
+
})
|
|
213
|
+
class ItemProcessor {
|
|
214
|
+
@State({ end: true })
|
|
215
|
+
processItem(@Event('{% $states.input %}') item: any) {
|
|
216
|
+
return { id: item.id, processed: true };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@StateMachine({
|
|
221
|
+
startAt: {
|
|
222
|
+
type: 'map',
|
|
223
|
+
mode: 'inline',
|
|
224
|
+
items: '{% $states.input.items %}',
|
|
225
|
+
states: ItemProcessor,
|
|
226
|
+
end: true,
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
export class BatchWorkflow {}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### Parallel State
|
|
233
|
+
|
|
234
|
+
A Parallel state runs multiple branches concurrently. Each branch is a class decorated with `@NestedStateMachine`:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
@NestedStateMachine({ startAt: 'sendEmail' })
|
|
238
|
+
class EmailBranch {
|
|
239
|
+
@State({ end: true })
|
|
240
|
+
sendEmail(@Event('{% $states.input %}') e: any) {
|
|
241
|
+
console.log('Sending email...');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@NestedStateMachine({ startAt: 'sendSms' })
|
|
246
|
+
class SmsBranch {
|
|
247
|
+
@State({ end: true })
|
|
248
|
+
sendSms(@Event('{% $states.input %}') e: any) {
|
|
249
|
+
console.log('Sending SMS...');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@StateMachine({
|
|
254
|
+
startAt: {
|
|
255
|
+
type: 'parallel',
|
|
256
|
+
branches: [EmailBranch, SmsBranch],
|
|
257
|
+
end: true,
|
|
258
|
+
},
|
|
259
|
+
})
|
|
260
|
+
export class NotificationWorkflow {}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Choice State
|
|
264
|
+
|
|
265
|
+
Use the `choice` type in `startAt` or `next` to define conditional branching based on JSONata expressions:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
@StateMachine({
|
|
269
|
+
startAt: {
|
|
270
|
+
type: 'choice',
|
|
271
|
+
choices: [
|
|
272
|
+
{
|
|
273
|
+
condition: '{% $states.input.priority = "high" %}',
|
|
274
|
+
next: 'expedite',
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
condition: '{% $states.input.priority = "low" %}',
|
|
278
|
+
next: 'enqueue',
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
default: { type: 'fail', error: 'UnknownPriority', cause: 'Priority not recognized' },
|
|
282
|
+
},
|
|
283
|
+
})
|
|
284
|
+
export class RoutingWorkflow {
|
|
285
|
+
@State({ end: true })
|
|
286
|
+
expedite(@Event('{% $states.input %}') e: any) {
|
|
287
|
+
return { routed: 'express' };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@State({ end: true })
|
|
291
|
+
enqueue(@Event('{% $states.input %}') e: any) {
|
|
292
|
+
return { routed: 'standard' };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Error Handling
|
|
298
|
+
|
|
299
|
+
States support `retry` and `catch` configurations for resilient workflows:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
@State({
|
|
303
|
+
next: 'done',
|
|
304
|
+
retry: [
|
|
305
|
+
{
|
|
306
|
+
errorEquals: ['States.TaskFailed'],
|
|
307
|
+
intervalSeconds: 2,
|
|
308
|
+
maxAttempt: 3,
|
|
309
|
+
backoffRate: 2,
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
catch: [
|
|
313
|
+
{
|
|
314
|
+
errorEquals: ['States.ALL'],
|
|
315
|
+
next: { type: 'fail', error: 'ProcessingError', cause: 'Task failed after retries' },
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
})
|
|
319
|
+
riskyOperation(@Event('{% $states.input %}') input: any) {
|
|
320
|
+
// This operation will be retried up to 3 times on failure
|
|
321
|
+
return { result: 'done' };
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### State Events & Payloads
|
|
326
|
+
|
|
327
|
+
Each `@State` method can receive input data through the `@Event` decorator. You can pass either a JSONata expression or a typed `@Payload` class.
|
|
328
|
+
|
|
329
|
+
#### JSONata Expression
|
|
330
|
+
|
|
331
|
+
Extract or transform the incoming event inline:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
@State({ end: true })
|
|
335
|
+
handle(@Event('{% $states.input.user %}') user: { name: string; role: string }) {
|
|
336
|
+
console.log(`User: ${user.name}, Role: ${user.role}`);
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
#### Typed Payload
|
|
341
|
+
|
|
342
|
+
Define a structured payload class with `@Payload` and `@Param` decorators for clear, declarative input mapping:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { Payload, Param } from '@lafken/state-machine/main';
|
|
346
|
+
|
|
347
|
+
@Payload()
|
|
348
|
+
export class InvoiceInput {
|
|
349
|
+
@Param({ context: 'input', source: 'invoiceId' })
|
|
350
|
+
invoiceId: string;
|
|
351
|
+
|
|
352
|
+
@Param({ context: 'input', source: 'lineItems', type: [Number] })
|
|
353
|
+
lineItems: number[];
|
|
354
|
+
|
|
355
|
+
@Param({ context: 'execution', source: 'id' })
|
|
356
|
+
executionId: string;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@StateMachine({ startAt: 'generateInvoice' })
|
|
360
|
+
export class InvoiceWorkflow {
|
|
361
|
+
@State({ next: 'send' })
|
|
362
|
+
generateInvoice(@Event(InvoiceInput) input: InvoiceInput) {
|
|
363
|
+
return { id: input.invoiceId, total: input.lineItems.reduce((a, b) => a + b, 0) };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@State({ end: true })
|
|
367
|
+
send(@Event('{% $states.input %}') invoice: any) {
|
|
368
|
+
console.log('Sending invoice:', invoice.id);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Available `@Param` contexts:
|
|
374
|
+
|
|
375
|
+
| Context | Description | Example sources |
|
|
376
|
+
| ---------------- | ------------------------------------------- | -------------------------------------- |
|
|
377
|
+
| `input` | Values from the state input | Any field name (e.g. `'orderId'`) |
|
|
378
|
+
| `execution` | Execution metadata | `'id'`, `'name'`, `'start_time'` |
|
|
379
|
+
| `state` | Current state metadata | `'entered_time'`, `'name'` |
|
|
380
|
+
| `state_machine` | State machine metadata | `'id'`, `'name'` |
|
|
381
|
+
| `task` | Task metadata | `'token'` |
|
|
382
|
+
| `custom` | A hardcoded value | Use `value` instead of `source` |
|
|
383
|
+
| `jsonata` | A dynamic JSONata expression | Use `value` with a JSONata string |
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./main"), exports);
|
|
18
|
+
__exportStar(require("./resolver"), exports);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./param"), exports);
|
|
18
|
+
__exportStar(require("./state-machine"), exports);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./param"), exports);
|
|
18
|
+
__exportStar(require("./param.types"), exports);
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type ClassResource } from '@lafken/common';
|
|
2
|
+
import type { JsonAtaString, ParamProps } from './param.types';
|
|
3
|
+
export declare const stateMachineFieldKey: string;
|
|
4
|
+
export declare const stateMachinePayloadKey: string;
|
|
5
|
+
/**
|
|
6
|
+
* Parameter decorator that binds the incoming Step Functions state
|
|
7
|
+
* input to a handler method argument.
|
|
8
|
+
*
|
|
9
|
+
* The event data can come from either:
|
|
10
|
+
* - A class decorated with `@Payload`, which maps the incoming data
|
|
11
|
+
* to a typed class structure.
|
|
12
|
+
* - A JSONata expression string (`{%...%}`), allowing dynamic
|
|
13
|
+
* extraction or transformation of the event data.
|
|
14
|
+
*
|
|
15
|
+
* @typeParam E - A `@Payload` class or a JSONata string.
|
|
16
|
+
* @param FieldClass - The payload class or JSONata expression that
|
|
17
|
+
* describes how to extract the event data.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* @StateMachine()
|
|
22
|
+
* export class OrderFlow {
|
|
23
|
+
* @Task()
|
|
24
|
+
* validate(@Event(OrderInput) input: OrderInput) { }
|
|
25
|
+
*
|
|
26
|
+
* @Task()
|
|
27
|
+
* transform(@Event('{%$states.input.orderId%}') id: string) { }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const Event: <E extends ClassResource | JsonAtaString>(FieldClass: E) => (target: any, methodName: string, _number: number) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Class decorator that declares a class as a Step Functions state
|
|
34
|
+
* input/output payload.
|
|
35
|
+
*
|
|
36
|
+
* The decorated class defines the shape of the data passed between
|
|
37
|
+
* states. Use `@Param` on its properties to describe where each value
|
|
38
|
+
* is extracted from (execution context, state input, custom value, etc.).
|
|
39
|
+
*
|
|
40
|
+
* @param props - Optional payload configuration (e.g. a custom `name`).
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* @Payload()
|
|
45
|
+
* export class OrderInput {
|
|
46
|
+
* @Param({ context: 'input', source: 'orderId' })
|
|
47
|
+
* orderId: string;
|
|
48
|
+
*
|
|
49
|
+
* @Param({ context: 'execution', source: 'id' })
|
|
50
|
+
* executionId: string;
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare const Payload: (props?: import("@lafken/common").PayloadProps | undefined) => (target: Function) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Parameter decorator that injects the integration options
|
|
57
|
+
*
|
|
58
|
+
* Use it to receive a `GetResourceProps` object (or custom integration
|
|
59
|
+
* parameters) that provides access to `getResourceValue`, allowing you
|
|
60
|
+
* to reference other infrastructure resources (queues, buckets, tables,
|
|
61
|
+
* etc.) when building the integration payload returned by the handler.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* @StateMachine({ startAt: 'send' })
|
|
66
|
+
* export class OrderFlow {
|
|
67
|
+
* @State({ integrationService: 'sqs', action: 'sendMessage', mode: 'token' })
|
|
68
|
+
* send(@IntegrationOptions() { getResourceValue }: GetResourceProps) {
|
|
69
|
+
* return {
|
|
70
|
+
* QueueUrl: getResourceValue('queue::orders', 'id'),
|
|
71
|
+
* MessageBody: { message: 'new order' },
|
|
72
|
+
* };
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare const IntegrationOptions: () => (target: any, methodName: string, _number: number) => void;
|
|
78
|
+
/**
|
|
79
|
+
* Property decorator that maps a class field to a value extracted from
|
|
80
|
+
* the Step Functions execution context.
|
|
81
|
+
*
|
|
82
|
+
* Use it inside a `@Payload` class to specify which context or source
|
|
83
|
+
* provides the value for the field. Supported contexts include:
|
|
84
|
+
* `input`, `execution`, `state`, `state_machine`, `task`, `custom`,
|
|
85
|
+
* and `jsonata`.
|
|
86
|
+
*
|
|
87
|
+
* @param props - Extraction configuration (context, source/value, optional
|
|
88
|
+
* name and type overrides).
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* @Payload()
|
|
93
|
+
* export class TaskInput {
|
|
94
|
+
* @Param({ context: 'input', source: 'userId' })
|
|
95
|
+
* userId: string;
|
|
96
|
+
*
|
|
97
|
+
* @Param({ context: 'execution', source: 'start_time' })
|
|
98
|
+
* startedAt: string;
|
|
99
|
+
*
|
|
100
|
+
* @Param({ context: 'custom', value: 'pending' })
|
|
101
|
+
* status: string;
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare const Param: (props?: ParamProps | undefined) => (target: any, destinationName: string) => void;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Param = exports.IntegrationOptions = exports.Payload = exports.Event = exports.stateMachinePayloadKey = exports.stateMachineFieldKey = void 0;
|
|
4
|
+
const common_1 = require("@lafken/common");
|
|
5
|
+
const state_machine_1 = require("../state-machine");
|
|
6
|
+
exports.stateMachineFieldKey = (0, common_1.createFieldName)(state_machine_1.RESOURCE_TYPE, common_1.FieldProperties.field);
|
|
7
|
+
exports.stateMachinePayloadKey = (0, common_1.createFieldName)(state_machine_1.RESOURCE_TYPE, common_1.FieldProperties.payload);
|
|
8
|
+
/**
|
|
9
|
+
* Parameter decorator that binds the incoming Step Functions state
|
|
10
|
+
* input to a handler method argument.
|
|
11
|
+
*
|
|
12
|
+
* The event data can come from either:
|
|
13
|
+
* - A class decorated with `@Payload`, which maps the incoming data
|
|
14
|
+
* to a typed class structure.
|
|
15
|
+
* - A JSONata expression string (`{%...%}`), allowing dynamic
|
|
16
|
+
* extraction or transformation of the event data.
|
|
17
|
+
*
|
|
18
|
+
* @typeParam E - A `@Payload` class or a JSONata string.
|
|
19
|
+
* @param FieldClass - The payload class or JSONata expression that
|
|
20
|
+
* describes how to extract the event data.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* @StateMachine()
|
|
25
|
+
* export class OrderFlow {
|
|
26
|
+
* @Task()
|
|
27
|
+
* validate(@Event(OrderInput) input: OrderInput) { }
|
|
28
|
+
*
|
|
29
|
+
* @Task()
|
|
30
|
+
* transform(@Event('{%$states.input.orderId%}') id: string) { }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
const Event = (FieldClass) => (0, common_1.createEventDecorator)({ prefix: state_machine_1.RESOURCE_TYPE })(FieldClass);
|
|
35
|
+
exports.Event = Event;
|
|
36
|
+
/**
|
|
37
|
+
* Class decorator that declares a class as a Step Functions state
|
|
38
|
+
* input/output payload.
|
|
39
|
+
*
|
|
40
|
+
* The decorated class defines the shape of the data passed between
|
|
41
|
+
* states. Use `@Param` on its properties to describe where each value
|
|
42
|
+
* is extracted from (execution context, state input, custom value, etc.).
|
|
43
|
+
*
|
|
44
|
+
* @param props - Optional payload configuration (e.g. a custom `name`).
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* @Payload()
|
|
49
|
+
* export class OrderInput {
|
|
50
|
+
* @Param({ context: 'input', source: 'orderId' })
|
|
51
|
+
* orderId: string;
|
|
52
|
+
*
|
|
53
|
+
* @Param({ context: 'execution', source: 'id' })
|
|
54
|
+
* executionId: string;
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
exports.Payload = (0, common_1.createPayloadDecorator)({
|
|
59
|
+
prefix: state_machine_1.RESOURCE_TYPE,
|
|
60
|
+
createUniqueId: false,
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Parameter decorator that injects the integration options
|
|
64
|
+
*
|
|
65
|
+
* Use it to receive a `GetResourceProps` object (or custom integration
|
|
66
|
+
* parameters) that provides access to `getResourceValue`, allowing you
|
|
67
|
+
* to reference other infrastructure resources (queues, buckets, tables,
|
|
68
|
+
* etc.) when building the integration payload returned by the handler.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* @StateMachine({ startAt: 'send' })
|
|
73
|
+
* export class OrderFlow {
|
|
74
|
+
* @State({ integrationService: 'sqs', action: 'sendMessage', mode: 'token' })
|
|
75
|
+
* send(@IntegrationOptions() { getResourceValue }: GetResourceProps) {
|
|
76
|
+
* return {
|
|
77
|
+
* QueueUrl: getResourceValue('queue::orders', 'id'),
|
|
78
|
+
* MessageBody: { message: 'new order' },
|
|
79
|
+
* };
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
exports.IntegrationOptions = common_1.Context;
|
|
85
|
+
/**
|
|
86
|
+
* Property decorator that maps a class field to a value extracted from
|
|
87
|
+
* the Step Functions execution context.
|
|
88
|
+
*
|
|
89
|
+
* Use it inside a `@Payload` class to specify which context or source
|
|
90
|
+
* provides the value for the field. Supported contexts include:
|
|
91
|
+
* `input`, `execution`, `state`, `state_machine`, `task`, `custom`,
|
|
92
|
+
* and `jsonata`.
|
|
93
|
+
*
|
|
94
|
+
* @param props - Extraction configuration (context, source/value, optional
|
|
95
|
+
* name and type overrides).
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* @Payload()
|
|
100
|
+
* export class TaskInput {
|
|
101
|
+
* @Param({ context: 'input', source: 'userId' })
|
|
102
|
+
* userId: string;
|
|
103
|
+
*
|
|
104
|
+
* @Param({ context: 'execution', source: 'start_time' })
|
|
105
|
+
* startedAt: string;
|
|
106
|
+
*
|
|
107
|
+
* @Param({ context: 'custom', value: 'pending' })
|
|
108
|
+
* status: string;
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
exports.Param = (0, common_1.createFieldDecorator)({
|
|
113
|
+
prefix: state_machine_1.RESOURCE_TYPE,
|
|
114
|
+
getMetadata: (props) => props,
|
|
115
|
+
});
|