flowcraft 1.0.0-beta.21 → 1.0.0-beta.23
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 +30 -19
- package/dist/index.d.ts +285 -119
- package/dist/index.js +10 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -39,16 +39,27 @@ npm install flowcraft
|
|
|
39
39
|
Create and run a simple workflow in a few lines of code.
|
|
40
40
|
|
|
41
41
|
```typescript
|
|
42
|
-
import { Flow,
|
|
42
|
+
import { contextKey, Flow, Node, TypedContext } from 'flowcraft'
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
const
|
|
46
|
-
// Functional helpers make common tasks easy.
|
|
47
|
-
.tap(console.log)
|
|
44
|
+
// 1. Define a type-safe key for our data
|
|
45
|
+
const MESSAGE = contextKey<string>('message')
|
|
48
46
|
|
|
47
|
+
// 2. Create nodes using the fluent API
|
|
48
|
+
const greetNode = new Node()
|
|
49
|
+
.exec(async ({ params }) => `Hello, ${params.name}!`)
|
|
50
|
+
.toContext(MESSAGE) // Stores the result in the context
|
|
51
|
+
|
|
52
|
+
const farewellNode = new Node()
|
|
53
|
+
.exec(async ({ ctx }) => `${await ctx.get(MESSAGE)} Goodbye!`)
|
|
54
|
+
.tap(console.log) // Prints the final message
|
|
55
|
+
|
|
56
|
+
// 3. Chain the nodes and create a flow
|
|
57
|
+
greetNode.next(farewellNode)
|
|
49
58
|
const flow = new Flow(greetNode)
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
|
|
60
|
+
// 4. Run the flow with a context and params
|
|
61
|
+
await flow.withParams({ name: 'World' }).run(new TypedContext())
|
|
62
|
+
// Output: "Hello, World! Goodbye!"
|
|
52
63
|
```
|
|
53
64
|
|
|
54
65
|
## Learn by Example
|
|
@@ -171,12 +182,13 @@ graph TD
|
|
|
171
182
|
|
|
172
183
|
The `Node` is the fundamental building block of a workflow. It represents a single unit of work and is fully generic, allowing you to define types for its lifecycle results and its static parameters.
|
|
173
184
|
|
|
174
|
-
The class signature is: `Node<PrepRes, ExecRes, PostRes, TParams>`
|
|
185
|
+
The class signature is: `Node<PrepRes, ExecRes, PostRes, TParams, TContext>`
|
|
175
186
|
|
|
176
187
|
- `PrepRes`: The type of data returned by the `prep` phase.
|
|
177
188
|
- `ExecRes`: The type of data returned by the `exec` phase.
|
|
178
189
|
- `PostRes`: The type of the "action" returned by the `post` phase.
|
|
179
190
|
- `TParams`: The type of the static parameters object for the node, accessible via `.withParams()`.
|
|
191
|
+
- `TContext`: The type of the `Context` object.
|
|
180
192
|
|
|
181
193
|
**Example: A Type-Safe GreetNode**
|
|
182
194
|
|
|
@@ -192,7 +204,7 @@ interface GreetParams {
|
|
|
192
204
|
// 2. Define the node with the TParams generic.
|
|
193
205
|
class GreetNode extends Node<void, string, any, GreetParams> {
|
|
194
206
|
// 3. The 'exec' method can safely access typed parameters.
|
|
195
|
-
async exec({ params }: NodeArgs<GreetParams>): Promise<string> {
|
|
207
|
+
async exec({ params }: NodeArgs<void, void, GreetParams>): Promise<string> {
|
|
196
208
|
let message = `${params.greeting}, World!`
|
|
197
209
|
if (params.loudly) {
|
|
198
210
|
message = message.toUpperCase()
|
|
@@ -252,8 +264,8 @@ const COUNTER = contextKey<number>('counter')
|
|
|
252
264
|
class IncrementCounterNode extends PreNode {
|
|
253
265
|
// You only need to implement the 'prep' method for context mutations.
|
|
254
266
|
async prep({ ctx }: NodeArgs): Promise<void> {
|
|
255
|
-
const current = ctx.get(COUNTER) ?? 0
|
|
256
|
-
ctx.set(COUNTER, current + 1)
|
|
267
|
+
const current = (await ctx.get(COUNTER)) ?? 0
|
|
268
|
+
await ctx.set(COUNTER, current + 1)
|
|
257
269
|
}
|
|
258
270
|
}
|
|
259
271
|
```
|
|
@@ -270,7 +282,7 @@ type UserAction = 'grant_access' | 'deny_access'
|
|
|
270
282
|
class CheckAdminNode extends PostNode<UserAction> {
|
|
271
283
|
// You only need to implement the 'post' method for branching logic.
|
|
272
284
|
async post({ ctx }: NodeArgs): Promise<UserAction> {
|
|
273
|
-
const role = ctx.get(USER_ROLE)
|
|
285
|
+
const role = await ctx.get(USER_ROLE)
|
|
274
286
|
return role === 'admin' ? 'grant_access' : 'deny_access'
|
|
275
287
|
}
|
|
276
288
|
}
|
|
@@ -283,7 +295,7 @@ checkAdmin.next(new GuestPageNode(), 'deny_access')
|
|
|
283
295
|
|
|
284
296
|
### Flow
|
|
285
297
|
|
|
286
|
-
A `Flow` is a special type of `Node` that orchestrates a sequence of other nodes. It is also generic (`Flow<PrepRes, ExecRes, TParams>`) and can be configured with its own parameters and middleware. It contains the logic for traversing its own graph of operations, making it a powerful, self-contained unit of work.
|
|
298
|
+
A `Flow` is a special type of `Node` that orchestrates a sequence of other nodes. It is also generic (`Flow<PrepRes, ExecRes, TParams, TContext>`) and can be configured with its own parameters and middleware. It contains the logic for traversing its own graph of operations, making it a powerful, self-contained unit of work.
|
|
287
299
|
|
|
288
300
|
### Executor
|
|
289
301
|
|
|
@@ -295,7 +307,7 @@ A `Flow` can be configured with middleware functions that wrap the execution of
|
|
|
295
307
|
|
|
296
308
|
### Context
|
|
297
309
|
|
|
298
|
-
The `Context` is a shared, type-safe `Map`-like object passed through every node in a flow. It acts as a shared memory space, allowing nodes to pass data and share state.
|
|
310
|
+
The `Context` is a shared, type-safe `Map`-like object passed through every node in a flow. It acts as a shared memory space, allowing nodes to pass data and share state. All its methods are asynchronous.
|
|
299
311
|
|
|
300
312
|
### Actions & Branching
|
|
301
313
|
|
|
@@ -322,8 +334,8 @@ To simplify the creation of common and complex patterns, the framework provides
|
|
|
322
334
|
|
|
323
335
|
### Core Classes
|
|
324
336
|
|
|
325
|
-
- `Node`: The base class for a unit of work: `Node<PrepRes, ExecRes, PostRes, TParams>`. It has built-in retry logic and a fluent API (`.map`, `.filter`, etc.).
|
|
326
|
-
- `Flow`: Orchestrates a sequence of nodes: `Flow<PrepRes, ExecRes, TParams>`. Supports middleware via `.use()`.
|
|
337
|
+
- `Node`: The base class for a unit of work: `Node<PrepRes, ExecRes, PostRes, TParams, TContext>`. It has built-in retry logic and a fluent API (`.map`, `.filter`, etc.).
|
|
338
|
+
- `Flow`: Orchestrates a sequence of nodes: `Flow<PrepRes, ExecRes, TParams, TContext>`. Supports middleware via `.use()`.
|
|
327
339
|
- `InMemoryExecutor`: The default `IExecutor` implementation for running flows in-memory.
|
|
328
340
|
- `TypedContext`: The standard `Map`-based implementation for the `Context` interface.
|
|
329
341
|
- `ConsoleLogger`, `NullLogger`: Pre-built logger implementations.
|
|
@@ -343,9 +355,8 @@ A collection of functions for creating nodes and pipelines in a more functional
|
|
|
343
355
|
### Builder Classes
|
|
344
356
|
|
|
345
357
|
- `SequenceFlow`: A `Flow` that creates a linear flow from a sequence of nodes.
|
|
346
|
-
- `
|
|
347
|
-
-
|
|
348
|
-
- `GraphBuilder`: Constructs a `Flow` from a declarative graph definition. Its `.build()` method returns a `BuildResult` object containing:
|
|
358
|
+
- **`BatchFlow` / `ParallelBatchFlow`**: Process a collection of items sequentially or concurrently.
|
|
359
|
+
- **`GraphBuilder`**: Constructs a `Flow` from a declarative graph definition. Its `.build()` method returns a `BuildResult` object containing:
|
|
349
360
|
- `flow`: The executable `Flow` instance.
|
|
350
361
|
- `nodeMap`: A `Map` of all instantiated nodes, keyed by their ID.
|
|
351
362
|
- `predecessorCountMap`: A `Map` of node IDs to their total number of incoming connections.
|