nestjs-serverless-workflow 0.1.0 → 0.1.3
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/README.md +43 -5
- package/dist/adapter/base-workflow.adapter.d.ts +38 -0
- package/dist/adapter/base-workflow.adapter.d.ts.map +1 -0
- package/dist/adapter/base-workflow.adapter.js +42 -0
- package/dist/adapter/base-workflow.adapter.js.map +1 -0
- package/dist/adapter/durable-lambda.adapter.d.ts +1 -5
- package/dist/adapter/durable-lambda.adapter.d.ts.map +1 -1
- package/dist/adapter/durable-lambda.adapter.js +81 -77
- package/dist/adapter/durable-lambda.adapter.js.map +1 -1
- package/dist/adapter/index.d.ts +1 -0
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +1 -0
- package/dist/adapter/index.js.map +1 -1
- package/dist/core/decorators/default.decorator.d.ts +17 -0
- package/dist/core/decorators/default.decorator.d.ts.map +1 -1
- package/dist/core/decorators/default.decorator.js +17 -0
- package/dist/core/decorators/default.decorator.js.map +1 -1
- package/dist/core/decorators/event.decorator.d.ts +18 -0
- package/dist/core/decorators/event.decorator.d.ts.map +1 -1
- package/dist/core/decorators/event.decorator.js +18 -0
- package/dist/core/decorators/event.decorator.js.map +1 -1
- package/dist/core/decorators/params.decorator.d.ts +27 -0
- package/dist/core/decorators/params.decorator.d.ts.map +1 -1
- package/dist/core/decorators/params.decorator.js +27 -0
- package/dist/core/decorators/params.decorator.js.map +1 -1
- package/dist/core/decorators/with-retry.decorator.d.ts +27 -0
- package/dist/core/decorators/with-retry.decorator.d.ts.map +1 -1
- package/dist/core/decorators/with-retry.decorator.js +28 -0
- package/dist/core/decorators/with-retry.decorator.js.map +1 -1
- package/dist/core/decorators/workflow.decorator.d.ts +29 -0
- package/dist/core/decorators/workflow.decorator.d.ts.map +1 -1
- package/dist/core/decorators/workflow.decorator.js +29 -0
- package/dist/core/decorators/workflow.decorator.js.map +1 -1
- package/dist/core/index.d.ts +14 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/providers/orchestrator.service.d.ts +23 -0
- package/dist/core/providers/orchestrator.service.d.ts.map +1 -1
- package/dist/core/providers/orchestrator.service.js +23 -0
- package/dist/core/providers/orchestrator.service.js.map +1 -1
- package/dist/core/providers/router.factory.d.ts +8 -0
- package/dist/core/providers/router.factory.d.ts.map +1 -1
- package/dist/core/providers/router.factory.js +8 -0
- package/dist/core/providers/router.factory.js.map +1 -1
- package/dist/core/providers/router.service.d.ts +28 -0
- package/dist/core/providers/router.service.d.ts.map +1 -1
- package/dist/core/providers/router.service.js +28 -0
- package/dist/core/providers/router.service.js.map +1 -1
- package/dist/core/types/entity.interface.d.ts +22 -0
- package/dist/core/types/entity.interface.d.ts.map +1 -1
- package/dist/core/types/retry.interface.d.ts +32 -2
- package/dist/core/types/retry.interface.d.ts.map +1 -1
- package/dist/core/types/retry.interface.js +5 -1
- package/dist/core/types/retry.interface.js.map +1 -1
- package/dist/core/types/shared.type.d.ts +15 -1
- package/dist/core/types/shared.type.d.ts.map +1 -1
- package/dist/core/types/transit-result.type.d.ts +15 -0
- package/dist/core/types/transit-result.type.d.ts.map +1 -1
- package/dist/core/types/workflow-definition.interface.d.ts +51 -2
- package/dist/core/types/workflow-definition.interface.d.ts.map +1 -1
- package/dist/core/types/workflow-event.interface.d.ts +20 -0
- package/dist/core/types/workflow-event.interface.d.ts.map +1 -1
- package/dist/core/workflow.module.d.ts +28 -0
- package/dist/core/workflow.module.d.ts.map +1 -1
- package/dist/core/workflow.module.js +28 -0
- package/dist/core/workflow.module.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -154,7 +154,7 @@ The library is organized into tree-shakable subpath exports:
|
|
|
154
154
|
```
|
|
155
155
|
nestjs-serverless-workflow/
|
|
156
156
|
├── core # Core workflow engine (decorators, services, types, IWorkflowEvent)
|
|
157
|
-
├── adapter # Durable Lambda adapter for checkpoint/replay execution
|
|
157
|
+
├── adapter # BaseWorkflowAdapter + Durable Lambda adapter for checkpoint/replay execution
|
|
158
158
|
└── exception # Custom exception types
|
|
159
159
|
```
|
|
160
160
|
|
|
@@ -164,8 +164,8 @@ nestjs-serverless-workflow/
|
|
|
164
164
|
// Core workflow engine
|
|
165
165
|
import { WorkflowModule, IWorkflowEvent } from 'nestjs-serverless-workflow/core';
|
|
166
166
|
|
|
167
|
-
//
|
|
168
|
-
import { DurableLambdaEventHandler } from 'nestjs-serverless-workflow/adapter';
|
|
167
|
+
// Adapter — base class + built-in durable Lambda adapter
|
|
168
|
+
import { BaseWorkflowAdapter, DurableLambdaEventHandler } from 'nestjs-serverless-workflow/adapter';
|
|
169
169
|
|
|
170
170
|
// Exceptions
|
|
171
171
|
import { UnretriableException } from 'nestjs-serverless-workflow/exception';
|
|
@@ -180,9 +180,9 @@ The `transit()` method on the orchestrator returns a `TransitResult`, which adap
|
|
|
180
180
|
```typescript
|
|
181
181
|
type TransitResult =
|
|
182
182
|
| { status: 'final'; state: string | number }
|
|
183
|
-
| { status: 'idle'; state: string | number }
|
|
183
|
+
| { status: 'idle'; state: string | number; timeout?: Duration }
|
|
184
184
|
| { status: 'continued'; nextEvent: IWorkflowEvent }
|
|
185
|
-
| { status: 'no_transition'; state: string | number };
|
|
185
|
+
| { status: 'no_transition'; state: string | number; timeout?: Duration };
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
- **`final`** -- the workflow has reached a terminal state.
|
|
@@ -190,6 +190,44 @@ type TransitResult =
|
|
|
190
190
|
- **`continued`** -- the workflow auto-transitioned and provides the next event to process.
|
|
191
191
|
- **`no_transition`** -- no matching transition was found from the current state.
|
|
192
192
|
|
|
193
|
+
## Custom Adapters
|
|
194
|
+
|
|
195
|
+
Extend `BaseWorkflowAdapter` to plug the orchestrator into any runtime. The base class owns the orchestration loop and dispatches each `TransitResult` variant to a handler method you override:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import { BaseWorkflowAdapter } from 'nestjs-serverless-workflow/adapter';
|
|
199
|
+
import { OrchestratorService } from 'nestjs-serverless-workflow/core';
|
|
200
|
+
import type { IWorkflowEvent, TransitResult } from 'nestjs-serverless-workflow/core';
|
|
201
|
+
|
|
202
|
+
class MyAdapter extends BaseWorkflowAdapter<MyContext, MyResult> {
|
|
203
|
+
constructor(orchestrator: OrchestratorService) {
|
|
204
|
+
super(orchestrator);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
protected async executeTransit(event: IWorkflowEvent): Promise<TransitResult> {
|
|
208
|
+
return this.orchestrator.transit(event);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
protected onFinal(result: Extract<TransitResult, { status: 'final' }>): MyResult {
|
|
212
|
+
return { status: 'completed', state: result.state };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
protected async onIdle(result: Extract<TransitResult, { status: 'idle' }>): Promise<IWorkflowEvent> {
|
|
216
|
+
// Wait for callback, poll a queue, etc.
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
protected async onContinued(result: Extract<TransitResult, { status: 'continued' }>): Promise<IWorkflowEvent> {
|
|
220
|
+
return result.nextEvent;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
protected async onNoTransition(result: Extract<TransitResult, { status: 'no_transition' }>): Promise<IWorkflowEvent> {
|
|
224
|
+
// Wait for an explicit external event
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The built-in `DurableLambdaEventHandler` extends this base with AWS checkpointing and `waitForCallback()` support. See the [Custom Adapter recipe](https://tung-dnt.github.io/nestjs-serverless-workflow/docs/recipes/custom-adapter) for full examples.
|
|
230
|
+
|
|
193
231
|
## Examples
|
|
194
232
|
|
|
195
233
|
Check out the [examples directory](https://github.com/tung-dnt/nestjs-serverless-workflow/tree/main/examples) for complete working examples:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { OrchestratorService, type IWorkflowEvent, type TransitResult } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base for workflow adapters.
|
|
4
|
+
*
|
|
5
|
+
* Owns the orchestration loop — call `transit()`, dispatch by status, repeat.
|
|
6
|
+
* Concrete adapters override the handler methods to plug in infrastructure
|
|
7
|
+
* (checkpointing, callbacks, queues, HTTP responses, etc.).
|
|
8
|
+
*
|
|
9
|
+
* @typeParam TContext - Adapter-specific execution context (e.g. IDurableContext)
|
|
10
|
+
* @typeParam TResult - The value returned when the workflow completes
|
|
11
|
+
*/
|
|
12
|
+
export declare abstract class BaseWorkflowAdapter<TContext, TResult> {
|
|
13
|
+
protected readonly orchestrator: OrchestratorService;
|
|
14
|
+
constructor(orchestrator: OrchestratorService);
|
|
15
|
+
/**
|
|
16
|
+
* Run the workflow loop until a final state is reached.
|
|
17
|
+
*/
|
|
18
|
+
protected runWorkflowLoop(initialEvent: IWorkflowEvent, ctx: TContext): Promise<TResult>;
|
|
19
|
+
/** Execute a single transit step (may include retry, checkpointing, etc.) */
|
|
20
|
+
protected abstract executeTransit(event: IWorkflowEvent, iteration: number, ctx: TContext): Promise<TransitResult>;
|
|
21
|
+
/** Workflow reached a terminal state — return the final result. */
|
|
22
|
+
protected abstract onFinal(result: Extract<TransitResult, {
|
|
23
|
+
status: 'final';
|
|
24
|
+
}>, event: IWorkflowEvent, ctx: TContext): TResult;
|
|
25
|
+
/** Entity is idle — wait for an external callback and return the next event. */
|
|
26
|
+
protected abstract onIdle(result: Extract<TransitResult, {
|
|
27
|
+
status: 'idle';
|
|
28
|
+
}>, event: IWorkflowEvent, iteration: number, ctx: TContext): Promise<IWorkflowEvent>;
|
|
29
|
+
/** Auto-transition found — checkpoint if needed and return the next event. */
|
|
30
|
+
protected abstract onContinued(result: Extract<TransitResult, {
|
|
31
|
+
status: 'continued';
|
|
32
|
+
}>, iteration: number, ctx: TContext): Promise<IWorkflowEvent>;
|
|
33
|
+
/** No unambiguous transition — wait for an explicit external event. */
|
|
34
|
+
protected abstract onNoTransition(result: Extract<TransitResult, {
|
|
35
|
+
status: 'no_transition';
|
|
36
|
+
}>, event: IWorkflowEvent, iteration: number, ctx: TContext): Promise<IWorkflowEvent>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=base-workflow.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-workflow.adapter.d.ts","sourceRoot":"","sources":["../../packages/adapter/base-workflow.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEtF;;;;;;;;;GASG;AACH,8BAAsB,mBAAmB,CAAC,QAAQ,EAAE,OAAO;IAC7C,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,mBAAmB;IAAhE,YAA+B,YAAY,EAAE,mBAAmB,EAAI;IAEpE;;OAEG;IACH,UAAgB,eAAe,CAAC,YAAY,EAAE,cAAc,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CA0B7F;IAID,6EAA6E;IAC7E,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAEnH,qEAAmE;IACnE,SAAS,CAAC,QAAQ,CAAC,OAAO,CACxB,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,EACnD,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC;IAEX,kFAAgF;IAChF,SAAS,CAAC,QAAQ,CAAC,MAAM,CACvB,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,cAAc,CAAC,CAAC;IAE3B,gFAA8E;IAC9E,SAAS,CAAC,QAAQ,CAAC,WAAW,CAC5B,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE;QAAE,MAAM,EAAE,WAAW,CAAA;KAAE,CAAC,EACvD,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,cAAc,CAAC,CAAC;IAE3B,yEAAuE;IACvE,SAAS,CAAC,QAAQ,CAAC,cAAc,CAC/B,MAAM,EAAE,OAAO,CAAC,aAAa,EAAE;QAAE,MAAM,EAAE,eAAe,CAAA;KAAE,CAAC,EAC3D,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { OrchestratorService } from '../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base for workflow adapters.
|
|
4
|
+
*
|
|
5
|
+
* Owns the orchestration loop — call `transit()`, dispatch by status, repeat.
|
|
6
|
+
* Concrete adapters override the handler methods to plug in infrastructure
|
|
7
|
+
* (checkpointing, callbacks, queues, HTTP responses, etc.).
|
|
8
|
+
*
|
|
9
|
+
* @typeParam TContext - Adapter-specific execution context (e.g. IDurableContext)
|
|
10
|
+
* @typeParam TResult - The value returned when the workflow completes
|
|
11
|
+
*/
|
|
12
|
+
export class BaseWorkflowAdapter {
|
|
13
|
+
orchestrator;
|
|
14
|
+
constructor(orchestrator) {
|
|
15
|
+
this.orchestrator = orchestrator;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Run the workflow loop until a final state is reached.
|
|
19
|
+
*/
|
|
20
|
+
async runWorkflowLoop(initialEvent, ctx) {
|
|
21
|
+
let currentEvent = initialEvent;
|
|
22
|
+
let iteration = 0;
|
|
23
|
+
while (true) {
|
|
24
|
+
const result = await this.executeTransit(currentEvent, iteration, ctx);
|
|
25
|
+
switch (result.status) {
|
|
26
|
+
case 'final':
|
|
27
|
+
return this.onFinal(result, currentEvent, ctx);
|
|
28
|
+
case 'idle':
|
|
29
|
+
currentEvent = await this.onIdle(result, currentEvent, iteration, ctx);
|
|
30
|
+
break;
|
|
31
|
+
case 'continued':
|
|
32
|
+
currentEvent = await this.onContinued(result, iteration, ctx);
|
|
33
|
+
break;
|
|
34
|
+
case 'no_transition':
|
|
35
|
+
currentEvent = await this.onNoTransition(result, currentEvent, iteration, ctx);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
iteration++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=base-workflow.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-workflow.adapter.js","sourceRoot":"","sources":["../../packages/adapter/base-workflow.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAA2C,MAAM,QAAQ,CAAC;AAEtF;;;;;;;;;GASG;AACH,MAAM,OAAgB,mBAAmB;IACR,YAAY;IAA3C,YAA+B,YAAiC,EAAE;4BAAnC,YAAY;IAAwB,CAAC;IAEpE;;OAEG;IACO,KAAK,CAAC,eAAe,CAAC,YAA4B,EAAE,GAAa,EAAoB;QAC7F,IAAI,YAAY,GAAG,YAAY,CAAC;QAChC,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;YAEvE,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;gBACtB,KAAK,OAAO;oBACV,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;gBAEjD,KAAK,MAAM;oBACT,YAAY,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;oBACvE,MAAM;gBAER,KAAK,WAAW;oBACd,YAAY,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;oBAC9D,MAAM;gBAER,KAAK,eAAe;oBAClB,YAAY,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;oBAC/E,MAAM;YACV,CAAC;YAED,SAAS,EAAE,CAAC;QACd,CAAC;IAAA,CACF;CAoCF"}
|
|
@@ -47,9 +47,5 @@ export type WithDurableExecution = <TEvent, TResult>(handler: (event: TEvent, ct
|
|
|
47
47
|
* @param app - NestJS application context
|
|
48
48
|
* @param withDurableExecution - The `withDurableExecution` function from `@aws/durable-execution-sdk-js`
|
|
49
49
|
*/
|
|
50
|
-
export declare const DurableLambdaEventHandler: (app: INestApplicationContext, withDurableExecution: WithDurableExecution) => (event: DurableWorkflowEvent, ctx: any) => Promise<
|
|
51
|
-
urn: string | number;
|
|
52
|
-
status: string;
|
|
53
|
-
state: string | number;
|
|
54
|
-
}>;
|
|
50
|
+
export declare const DurableLambdaEventHandler: (app: INestApplicationContext, withDurableExecution: WithDurableExecution) => (event: DurableWorkflowEvent, ctx: any) => Promise<DurableWorkflowResult>;
|
|
55
51
|
//# sourceMappingURL=durable-lambda.adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-lambda.adapter.d.ts","sourceRoot":"","sources":["../../packages/adapter/durable-lambda.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"durable-lambda.adapter.d.ts","sourceRoot":"","sources":["../../packages/adapter/durable-lambda.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAK9D,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,GAAG,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxD,eAAe,CAAC,CAAC,EACf,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EACjD,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,GAC7E,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,IAAI,CAAC,QAAQ,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtF,MAAM,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAA;KAAE,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,OAAO,EACjD,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,OAAO,CAAC,KAC/D,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AA6InD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,yJAOrC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OrchestratorService, RetryBackoff } from '../core/index.js';
|
|
2
2
|
import { UnretriableException } from '../exception/unretriable.exception.js';
|
|
3
|
+
import { BaseWorkflowAdapter } from './base-workflow.adapter.js';
|
|
3
4
|
const DEFAULT_CALLBACK_TIMEOUT = { hours: 24 };
|
|
4
5
|
/**
|
|
5
6
|
* Parse a callback result from the SDK.
|
|
@@ -16,6 +17,84 @@ function parseCallbackResult(raw) {
|
|
|
16
17
|
}
|
|
17
18
|
return raw;
|
|
18
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Concrete adapter that wraps the workflow orchestrator in an AWS durable execution.
|
|
22
|
+
*
|
|
23
|
+
* Each workflow instance runs as a single durable execution spanning multiple Lambda invocations.
|
|
24
|
+
* Steps are checkpointed at event boundaries — on replay, completed steps return stored results.
|
|
25
|
+
*/
|
|
26
|
+
class DurableLambdaWorkflowAdapter extends BaseWorkflowAdapter {
|
|
27
|
+
urn;
|
|
28
|
+
constructor(orchestrator) {
|
|
29
|
+
super(orchestrator);
|
|
30
|
+
}
|
|
31
|
+
async run(event, ctx) {
|
|
32
|
+
this.urn = event.urn;
|
|
33
|
+
const initialEvent = {
|
|
34
|
+
event: event.initialEvent,
|
|
35
|
+
urn: event.urn,
|
|
36
|
+
payload: event.payload,
|
|
37
|
+
attempt: 0,
|
|
38
|
+
};
|
|
39
|
+
return this.runWorkflowLoop(initialEvent, ctx);
|
|
40
|
+
}
|
|
41
|
+
async executeTransit(currentEvent, iteration, ctx) {
|
|
42
|
+
const retryConfig = this.orchestrator.getRetryConfig(currentEvent.event);
|
|
43
|
+
const maxAttempts = retryConfig?.maxAttempts ?? 1;
|
|
44
|
+
let lastError;
|
|
45
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
46
|
+
try {
|
|
47
|
+
return await ctx.step(`transit:${currentEvent.event}:${iteration}:${attempt}`, () => this.orchestrator.transit(currentEvent));
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
if (e instanceof UnretriableException)
|
|
51
|
+
throw e;
|
|
52
|
+
lastError = e;
|
|
53
|
+
if (attempt < maxAttempts - 1) {
|
|
54
|
+
const delay = RetryBackoff.calculateDelay(attempt, retryConfig);
|
|
55
|
+
ctx.logger.info(`Handler ${currentEvent.event} failed (attempt ${attempt + 1}/${maxAttempts}), retrying in ${delay}ms`);
|
|
56
|
+
await ctx.wait({ seconds: Math.ceil(delay / 1000) });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw lastError;
|
|
61
|
+
}
|
|
62
|
+
// TODO: review adapter hooks' logic
|
|
63
|
+
onFinal(result, _event, _ctx) {
|
|
64
|
+
return { urn: this.urn, status: 'completed', state: result.state };
|
|
65
|
+
}
|
|
66
|
+
async onIdle(result, _event, iteration, ctx) {
|
|
67
|
+
const timeout = result.timeout ?? DEFAULT_CALLBACK_TIMEOUT;
|
|
68
|
+
const raw = await ctx.waitForCallback(`idle:${result.state}:${iteration}`, async (callbackId) => {
|
|
69
|
+
ctx.logger.info(`Waiting for callback at idle state ${result.state}`, { callbackId });
|
|
70
|
+
}, { timeout });
|
|
71
|
+
const callbackPayload = parseCallbackResult(raw);
|
|
72
|
+
return {
|
|
73
|
+
event: callbackPayload.event,
|
|
74
|
+
urn: this.urn,
|
|
75
|
+
payload: callbackPayload.payload,
|
|
76
|
+
attempt: 0,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async onContinued(result, iteration, ctx) {
|
|
80
|
+
// Checkpoint the progression — on replay, returns stored result
|
|
81
|
+
await ctx.step(`${result.nextEvent.event}:${iteration}`, async () => result.nextEvent);
|
|
82
|
+
return result.nextEvent;
|
|
83
|
+
}
|
|
84
|
+
async onNoTransition(result, _event, iteration, ctx) {
|
|
85
|
+
const timeout = result.timeout ?? DEFAULT_CALLBACK_TIMEOUT;
|
|
86
|
+
const raw = await ctx.waitForCallback(`awaiting:${result.state}:${iteration}`, async (callbackId) => {
|
|
87
|
+
ctx.logger.info(`No auto-transition from ${result.state}, waiting for explicit event`, { callbackId });
|
|
88
|
+
}, { timeout });
|
|
89
|
+
const noTransitionPayload = parseCallbackResult(raw);
|
|
90
|
+
return {
|
|
91
|
+
event: noTransitionPayload.event,
|
|
92
|
+
urn: this.urn,
|
|
93
|
+
payload: noTransitionPayload.payload,
|
|
94
|
+
attempt: 0,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
19
98
|
/**
|
|
20
99
|
* Creates a Lambda handler that wraps the workflow orchestrator in a durable execution.
|
|
21
100
|
*
|
|
@@ -30,84 +109,9 @@ function parseCallbackResult(raw) {
|
|
|
30
109
|
*/
|
|
31
110
|
export const DurableLambdaEventHandler = (app, withDurableExecution) => {
|
|
32
111
|
const orchestrator = app.get(OrchestratorService);
|
|
112
|
+
const adapter = new DurableLambdaWorkflowAdapter(orchestrator);
|
|
33
113
|
return withDurableExecution(async (event, ctx) => {
|
|
34
|
-
|
|
35
|
-
event: event.initialEvent,
|
|
36
|
-
urn: event.urn,
|
|
37
|
-
payload: event.payload,
|
|
38
|
-
attempt: 0,
|
|
39
|
-
};
|
|
40
|
-
let iteration = 0;
|
|
41
|
-
while (true) {
|
|
42
|
-
const result = await transitWithRetry(orchestrator, currentEvent, iteration, ctx);
|
|
43
|
-
switch (result.status) {
|
|
44
|
-
case 'final':
|
|
45
|
-
return { urn: event.urn, status: 'completed', state: result.state };
|
|
46
|
-
case 'idle': {
|
|
47
|
-
const timeout = result.timeout ?? DEFAULT_CALLBACK_TIMEOUT;
|
|
48
|
-
const raw = await ctx.waitForCallback(`idle:${result.state}:${iteration}`, async (callbackId) => {
|
|
49
|
-
// External systems use this callbackId to resume the workflow
|
|
50
|
-
// via SendDurableExecutionCallbackSuccess Lambda API
|
|
51
|
-
ctx.logger.info(`Waiting for callback at idle state ${result.state}`, { callbackId });
|
|
52
|
-
}, { timeout });
|
|
53
|
-
const callbackPayload = parseCallbackResult(raw);
|
|
54
|
-
currentEvent = {
|
|
55
|
-
event: callbackPayload.event,
|
|
56
|
-
urn: event.urn,
|
|
57
|
-
payload: callbackPayload.payload,
|
|
58
|
-
attempt: 0,
|
|
59
|
-
};
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
case 'continued': {
|
|
63
|
-
// Checkpoint the progression — on replay, returns stored result
|
|
64
|
-
await ctx.step(`${result.nextEvent.event}:${iteration}`, async () => result.nextEvent);
|
|
65
|
-
currentEvent = result.nextEvent;
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
case 'no_transition': {
|
|
69
|
-
const timeout = result.timeout ?? DEFAULT_CALLBACK_TIMEOUT;
|
|
70
|
-
// No unambiguous auto-transition — wait for explicit event via callback
|
|
71
|
-
const raw = await ctx.waitForCallback(`awaiting:${result.state}:${iteration}`, async (callbackId) => {
|
|
72
|
-
ctx.logger.info(`No auto-transition from ${result.state}, waiting for explicit event`, { callbackId });
|
|
73
|
-
}, { timeout });
|
|
74
|
-
const noTransitionPayload = parseCallbackResult(raw);
|
|
75
|
-
currentEvent = {
|
|
76
|
-
event: noTransitionPayload.event,
|
|
77
|
-
urn: event.urn,
|
|
78
|
-
payload: noTransitionPayload.payload,
|
|
79
|
-
attempt: 0,
|
|
80
|
-
};
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
iteration++;
|
|
85
|
-
}
|
|
114
|
+
return adapter.run(event, ctx);
|
|
86
115
|
});
|
|
87
116
|
};
|
|
88
|
-
/**
|
|
89
|
-
* Wraps `orchestrator.transit()` with retry logic when the handler has `@WithRetry()` config.
|
|
90
|
-
* Each attempt is checkpointed via `ctx.step()` so replays skip completed attempts.
|
|
91
|
-
*/
|
|
92
|
-
async function transitWithRetry(orchestrator, currentEvent, iteration, ctx) {
|
|
93
|
-
const retryConfig = orchestrator.getRetryConfig(currentEvent.event);
|
|
94
|
-
const maxAttempts = retryConfig?.maxAttempts ?? 1;
|
|
95
|
-
let lastError;
|
|
96
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
97
|
-
try {
|
|
98
|
-
return await ctx.step(`transit:${currentEvent.event}:${iteration}:${attempt}`, () => orchestrator.transit(currentEvent));
|
|
99
|
-
}
|
|
100
|
-
catch (e) {
|
|
101
|
-
if (e instanceof UnretriableException)
|
|
102
|
-
throw e;
|
|
103
|
-
lastError = e;
|
|
104
|
-
if (attempt < maxAttempts - 1) {
|
|
105
|
-
const delay = RetryBackoff.calculateDelay(attempt, retryConfig);
|
|
106
|
-
ctx.logger.info(`Handler ${currentEvent.event} failed (attempt ${attempt + 1}/${maxAttempts}), retrying in ${delay}ms`);
|
|
107
|
-
await ctx.wait({ seconds: Math.ceil(delay / 1000) });
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
throw lastError;
|
|
112
|
-
}
|
|
113
117
|
//# sourceMappingURL=durable-lambda.adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durable-lambda.adapter.js","sourceRoot":"","sources":["../../packages/adapter/durable-lambda.adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAA2C,MAAM,QAAQ,CAAC;AACpG,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"durable-lambda.adapter.js","sourceRoot":"","sources":["../../packages/adapter/durable-lambda.adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/adapter/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/adapter/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../packages/adapter/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../packages/adapter/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC"}
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/** @internal Metadata key for the default (fallback) handler. */
|
|
1
2
|
export declare const WORKFLOW_DEFAULT_EVENT = "workflow.default";
|
|
3
|
+
/**
|
|
4
|
+
* Method decorator that marks a handler as the fallback for unmatched events.
|
|
5
|
+
*
|
|
6
|
+
* When the orchestrator cannot find a valid transition for an incoming event,
|
|
7
|
+
* it invokes the method decorated with `@OnDefault` before throwing.
|
|
8
|
+
* Only one default handler per workflow class is allowed — subsequent
|
|
9
|
+
* decorations are silently ignored.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* @OnDefault
|
|
14
|
+
* async fallback(entity: Order, event: string, payload?: any) {
|
|
15
|
+
* logger.warn(`Unhandled event ${event} for order ${entity.id}`);
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
2
19
|
export declare const OnDefault: (target: any, _propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
3
20
|
//# sourceMappingURL=default.decorator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default.decorator.d.ts","sourceRoot":"","sources":["../../../packages/core/decorators/default.decorator.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD,eAAO,MAAM,SAAS,2FAMrB,CAAC"}
|
|
1
|
+
{"version":3,"file":"default.decorator.d.ts","sourceRoot":"","sources":["../../../packages/core/decorators/default.decorator.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,SAAS,2FAMrB,CAAC"}
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
+
/** @internal Metadata key for the default (fallback) handler. */
|
|
1
2
|
export const WORKFLOW_DEFAULT_EVENT = 'workflow.default';
|
|
3
|
+
/**
|
|
4
|
+
* Method decorator that marks a handler as the fallback for unmatched events.
|
|
5
|
+
*
|
|
6
|
+
* When the orchestrator cannot find a valid transition for an incoming event,
|
|
7
|
+
* it invokes the method decorated with `@OnDefault` before throwing.
|
|
8
|
+
* Only one default handler per workflow class is allowed — subsequent
|
|
9
|
+
* decorations are silently ignored.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* @OnDefault
|
|
14
|
+
* async fallback(entity: Order, event: string, payload?: any) {
|
|
15
|
+
* logger.warn(`Unhandled event ${event} for order ${entity.id}`);
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
2
19
|
export const OnDefault = (target, _propertyKey, descriptor) => {
|
|
3
20
|
const existingFallback = Reflect.getMetadata(WORKFLOW_DEFAULT_EVENT, target.constructor);
|
|
4
21
|
if (!existingFallback) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default.decorator.js","sourceRoot":"","sources":["../../../packages/core/decorators/default.decorator.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,kBAAkB,CAAC;AAEzD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAW,EAAE,YAAoB,EAAE,UAA8B,EAAE,EAAE,CAAC;IAC9F,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,sBAAsB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACzF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO,CAAC,cAAc,CAAC,sBAAsB,EAAE,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,UAAU,CAAC;AAAA,CACnB,CAAC"}
|
|
1
|
+
{"version":3,"file":"default.decorator.js","sourceRoot":"","sources":["../../../packages/core/decorators/default.decorator.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,CAAC,MAAM,sBAAsB,GAAG,kBAAkB,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,MAAW,EAAE,YAAoB,EAAE,UAA8B,EAAE,EAAE,CAAC;IAC9F,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,sBAAsB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACzF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO,CAAC,cAAc,CAAC,sBAAsB,EAAE,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,UAAU,CAAC;AAAA,CACnB,CAAC"}
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/** @internal Metadata key for the list of event handlers on a workflow class. */
|
|
1
2
|
export declare const WORKFLOW_HANDLER_KEY = "workflow:metadata";
|
|
3
|
+
/**
|
|
4
|
+
* Method decorator that registers a handler for a specific workflow event.
|
|
5
|
+
*
|
|
6
|
+
* The event name must match a transition's `event` field in the workflow
|
|
7
|
+
* definition. The orchestrator discovers these handlers at module init
|
|
8
|
+
* and routes incoming {@link IWorkflowEvent}s to the correct method.
|
|
9
|
+
*
|
|
10
|
+
* @param event - The event name this method handles (e.g. `'order.submit'`).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* @OnEvent('order.submit')
|
|
15
|
+
* async onSubmit(@Entity() order: Order, @Payload() data: SubmitDto) {
|
|
16
|
+
* // transition logic
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
2
20
|
export declare const OnEvent: (event: string) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
3
21
|
//# sourceMappingURL=event.decorator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.decorator.d.ts","sourceRoot":"","sources":["../../../packages/core/decorators/event.decorator.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD,eAAO,MAAM,OAAO,6GAWnB,CAAC"}
|
|
1
|
+
{"version":3,"file":"event.decorator.d.ts","sourceRoot":"","sources":["../../../packages/core/decorators/event.decorator.ts"],"names":[],"mappings":"AAEA,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AAExD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,OAAO,6GAWnB,CAAC"}
|
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
/** @internal Metadata key for the list of event handlers on a workflow class. */
|
|
1
2
|
export const WORKFLOW_HANDLER_KEY = 'workflow:metadata';
|
|
3
|
+
/**
|
|
4
|
+
* Method decorator that registers a handler for a specific workflow event.
|
|
5
|
+
*
|
|
6
|
+
* The event name must match a transition's `event` field in the workflow
|
|
7
|
+
* definition. The orchestrator discovers these handlers at module init
|
|
8
|
+
* and routes incoming {@link IWorkflowEvent}s to the correct method.
|
|
9
|
+
*
|
|
10
|
+
* @param event - The event name this method handles (e.g. `'order.submit'`).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* @OnEvent('order.submit')
|
|
15
|
+
* async onSubmit(@Entity() order: Order, @Payload() data: SubmitDto) {
|
|
16
|
+
* // transition logic
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
2
20
|
export const OnEvent = (event) => (target, propertyKey, descriptor) => {
|
|
3
21
|
let workflowHandlers = Reflect.getMetadata(WORKFLOW_HANDLER_KEY, target.constructor);
|
|
4
22
|
if (!workflowHandlers) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.decorator.js","sourceRoot":"","sources":["../../../packages/core/decorators/event.decorator.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAExD,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,MAAW,EAAE,WAAmB,EAAE,UAA8B,EAAE,EAAE,CAAC;IAC9G,IAAI,gBAAgB,GAAuB,OAAO,CAAC,WAAW,CAAC,oBAAoB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAEzG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,gBAAgB,GAAG,EAAE,CAAC;QACtB,OAAO,CAAC,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACrF,CAAC;IAED,gBAAgB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAE/E,OAAO,UAAU,CAAC;AAAA,CACnB,CAAC"}
|
|
1
|
+
{"version":3,"file":"event.decorator.js","sourceRoot":"","sources":["../../../packages/core/decorators/event.decorator.ts"],"names":[],"mappings":"AAEA,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAExD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,MAAW,EAAE,WAAmB,EAAE,UAA8B,EAAE,EAAE,CAAC;IAC9G,IAAI,gBAAgB,GAAuB,OAAO,CAAC,WAAW,CAAC,oBAAoB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAEzG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,gBAAgB,GAAG,EAAE,CAAC;QACtB,OAAO,CAAC,cAAc,CAAC,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACrF,CAAC;IAED,gBAAgB,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAE/E,OAAO,UAAU,CAAC;AAAA,CACnB,CAAC"}
|
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter decorator that injects the current workflow entity into a handler method.
|
|
3
|
+
*
|
|
4
|
+
* The entity is loaded via the {@link IWorkflowEntity.load} method of the
|
|
5
|
+
* entity service registered in the workflow definition.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* @OnEvent('order.submit')
|
|
10
|
+
* async onSubmit(@Entity() order: Order) { ... }
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
1
13
|
export declare function Entity(): ParameterDecorator;
|
|
14
|
+
/**
|
|
15
|
+
* Parameter decorator that injects the event payload into a handler method.
|
|
16
|
+
*
|
|
17
|
+
* The payload comes from the `payload` field of the incoming
|
|
18
|
+
* {@link IWorkflowEvent}, or from the return value of the previous handler
|
|
19
|
+
* when auto-transitioning (`continued` status).
|
|
20
|
+
*
|
|
21
|
+
* @param dto - Optional DTO class for validation (reserved for future use).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* @OnEvent('order.submit')
|
|
26
|
+
* async onSubmit(@Entity() order: Order, @Payload() data: SubmitDto) { ... }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
2
29
|
export declare function Payload<P>(dto?: P): ParameterDecorator;
|
|
3
30
|
//# sourceMappingURL=params.decorator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"params.decorator.d.ts","sourceRoot":"","sources":["../../../packages/core/decorators/params.decorator.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,IAAI,kBAAkB,CAQ3C;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,CAQtD"}
|
|
1
|
+
{"version":3,"file":"params.decorator.d.ts","sourceRoot":"","sources":["../../../packages/core/decorators/params.decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,IAAI,kBAAkB,CAQ3C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,CAQtD"}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter decorator that injects the current workflow entity into a handler method.
|
|
3
|
+
*
|
|
4
|
+
* The entity is loaded via the {@link IWorkflowEntity.load} method of the
|
|
5
|
+
* entity service registered in the workflow definition.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* @OnEvent('order.submit')
|
|
10
|
+
* async onSubmit(@Entity() order: Order) { ... }
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
1
13
|
export function Entity() {
|
|
2
14
|
return (target, propertyKey, parameterIndex) => {
|
|
3
15
|
if (!propertyKey || parameterIndex === undefined)
|
|
@@ -7,6 +19,21 @@ export function Entity() {
|
|
|
7
19
|
Reflect.defineMetadata('workflow:params', existing, target, propertyKey);
|
|
8
20
|
};
|
|
9
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Parameter decorator that injects the event payload into a handler method.
|
|
24
|
+
*
|
|
25
|
+
* The payload comes from the `payload` field of the incoming
|
|
26
|
+
* {@link IWorkflowEvent}, or from the return value of the previous handler
|
|
27
|
+
* when auto-transitioning (`continued` status).
|
|
28
|
+
*
|
|
29
|
+
* @param dto - Optional DTO class for validation (reserved for future use).
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* @OnEvent('order.submit')
|
|
34
|
+
* async onSubmit(@Entity() order: Order, @Payload() data: SubmitDto) { ... }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
10
37
|
export function Payload(dto) {
|
|
11
38
|
return (target, propertyKey, parameterIndex) => {
|
|
12
39
|
if (!propertyKey || parameterIndex === undefined)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"params.decorator.js","sourceRoot":"","sources":["../../../packages/core/decorators/params.decorator.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,MAAM,GAAuB;IAC3C,OAAO,CAAC,MAAc,EAAE,WAA6B,EAAE,cAAuB,EAAE,EAAE,CAAC;QACjF,IAAI,CAAC,WAAW,IAAI,cAAc,KAAK,SAAS;YAC9C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,MAAM,QAAQ,GAAe,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;QAClG,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAAA,CAC1E,CAAC;AAAA,CACH;AAED,MAAM,UAAU,OAAO,CAAI,GAAO,EAAsB;IACtD,OAAO,CAAC,MAAc,EAAE,WAA6B,EAAE,cAAuB,EAAE,EAAE,CAAC;QACjF,IAAI,CAAC,WAAW,IAAI,cAAc,KAAK,SAAS;YAC9C,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC7E,MAAM,QAAQ,GAAe,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;QAClG,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAAA,CAC1E,CAAC;AAAA,CACH"}
|
|
1
|
+
{"version":3,"file":"params.decorator.js","sourceRoot":"","sources":["../../../packages/core/decorators/params.decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,MAAM,GAAuB;IAC3C,OAAO,CAAC,MAAc,EAAE,WAA6B,EAAE,cAAuB,EAAE,EAAE,CAAC;QACjF,IAAI,CAAC,WAAW,IAAI,cAAc,KAAK,SAAS;YAC9C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,MAAM,QAAQ,GAAe,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;QAClG,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAAA,CAC1E,CAAC;AAAA,CACH;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,OAAO,CAAI,GAAO,EAAsB;IACtD,OAAO,CAAC,MAAc,EAAE,WAA6B,EAAE,cAAuB,EAAE,EAAE,CAAC;QACjF,IAAI,CAAC,WAAW,IAAI,cAAc,KAAK,SAAS;YAC9C,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC7E,MAAM,QAAQ,GAAe,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;QAClG,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,cAAc,CAAC,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAAA,CAC1E,CAAC;AAAA,CACH"}
|