@rotorsoft/act 0.6.27 → 0.6.29

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.
@@ -7,90 +7,335 @@
7
7
  import { Act } from "./act.js";
8
8
  import type { EventRegister, ReactionHandler, ReactionOptions, ReactionResolver, Registry, Schema, SchemaRegister, Schemas, State } from "./types/index.js";
9
9
  /**
10
- * Fluent builder for composing event-sourced state machines with actions and reactions.
10
+ * Fluent builder interface for composing event-sourced applications.
11
11
  *
12
- * Provides a chainable API for registering states, events, and reaction handlers, enabling you to declaratively build complex, reactive applications.
12
+ * Provides a chainable API for:
13
+ * - Registering states via `.with()`
14
+ * - Defining event reactions via `.on()` → `.do()` → `.to()` or `.void()`
15
+ * - Building the orchestrator via `.build()`
13
16
  *
14
- * @template S SchemaRegister for state
15
- * @template E Schemas for events
16
- * @template A Schemas for actions
17
+ * @template S - Schema register for states (maps action names to state schemas)
18
+ * @template E - Event schemas (maps event names to event data schemas)
19
+ * @template A - Action schemas (maps action names to action payload schemas)
17
20
  *
18
- * @example
19
- * const app = act()
20
- * .with(Counter)
21
- * .on("Incremented").do(async (event) => { ... })
22
- * .to(() => "OtherStream")
23
- * .build();
21
+ * @see {@link act} for usage examples
22
+ * @see {@link Act} for the built orchestrator API
24
23
  */
25
24
  export type ActBuilder<S extends SchemaRegister<A>, E extends Schemas, A extends Schemas> = {
26
25
  /**
27
- * Register a state machine with the builder.
26
+ * Registers a state definition with the builder.
27
+ *
28
+ * States define aggregates that process actions and emit events. Each state
29
+ * registration adds its actions and events to the orchestrator's registry.
30
+ * State names, action names, and event names must be unique across the application.
31
+ *
32
+ * @template SX - State schema type
33
+ * @template EX - Event schemas type for this state
34
+ * @template AX - Action schemas type for this state
35
+ * @param state - The state definition to register
36
+ * @returns The builder with updated type information for chaining
37
+ *
38
+ * @throws {Error} If a state with duplicate action or event names is registered
28
39
  *
29
- * @template SX The type of state
30
- * @template EX The type of events
31
- * @template AX The type of actions
32
- * @param state The state machine to add
33
- * @returns The builder (for chaining)
40
+ * @example Register single state
41
+ * ```typescript
42
+ * const app = act()
43
+ * .with(Counter)
44
+ * .build();
45
+ * ```
46
+ *
47
+ * @example Register multiple states
48
+ * ```typescript
49
+ * const app = act()
50
+ * .with(User)
51
+ * .with(Order)
52
+ * .with(Inventory)
53
+ * .build();
54
+ * ```
34
55
  */
35
56
  with: <SX extends Schema, EX extends Schemas, AX extends Schemas>(state: State<SX, EX, AX>) => ActBuilder<S & {
36
57
  [K in keyof AX]: SX;
37
58
  }, E & EX, A & AX>;
38
59
  /**
39
- * Register a reaction handler for a given event.
60
+ * Begins defining a reaction to a specific event.
61
+ *
62
+ * Reactions are event handlers that respond to state changes. They can trigger
63
+ * additional actions, update external systems, or perform side effects. Reactions
64
+ * are processed asynchronously during drain cycles.
65
+ *
66
+ * @template K - Event name (must be a registered event)
67
+ * @param event - The event name to react to
68
+ * @returns An object with `.do()` method to define the reaction handler
40
69
  *
41
- * @template K The event name
42
- * @param event The event to react to
43
- * @returns An object with .do(handler) to register the handler
70
+ * @example
71
+ * ```typescript
72
+ * const app = act()
73
+ * .with(User)
74
+ * .on("UserCreated") // React to UserCreated events
75
+ * .do(async (event) => {
76
+ * await sendWelcomeEmail(event.data.email);
77
+ * })
78
+ * .void()
79
+ * .build();
80
+ * ```
44
81
  */
45
82
  on: <K extends keyof E>(event: K) => {
46
83
  /**
47
- * Register a reaction handler for the event.
84
+ * Defines the reaction handler function for the event.
85
+ *
86
+ * The handler receives the committed event and can:
87
+ * - Perform side effects (send emails, call APIs, etc.)
88
+ * - Return an action tuple `[actionName, payload]` to trigger another action
89
+ * - Return `void` or `undefined` for side-effect-only reactions
90
+ *
91
+ * @param handler - The reaction handler function
92
+ * @param options - Optional reaction configuration
93
+ * @param options.blockOnError - Block this stream if handler fails (default: true)
94
+ * @param options.maxRetries - Maximum retry attempts on failure (default: 3)
95
+ * @returns The builder with `.to()` and `.void()` methods for routing configuration
48
96
  *
49
- * @param handler The reaction handler function
50
- * @param options (Optional) Reaction options (retries, blocking, etc.)
51
- * @returns The builder (for chaining), with .to(resolver) and .void() for advanced routing
97
+ * @example Side effect only (void)
98
+ * ```typescript
99
+ * .on("UserCreated")
100
+ * .do(async (event) => {
101
+ * await analytics.track("user_created", event.data);
102
+ * })
103
+ * .void()
104
+ * ```
105
+ *
106
+ * @example Trigger another action
107
+ * ```typescript
108
+ * .on("OrderPlaced")
109
+ * .do(async (event) => {
110
+ * return ["reduceStock", { amount: event.data.items.length }];
111
+ * })
112
+ * .to("inventory-1")
113
+ * ```
114
+ *
115
+ * @example With retry configuration
116
+ * ```typescript
117
+ * .on("PaymentProcessed")
118
+ * .do(async (event) => {
119
+ * await externalAPI.notify(event.data);
120
+ * }, {
121
+ * blockOnError: false, // Don't block on failure
122
+ * maxRetries: 5 // Retry up to 5 times
123
+ * })
124
+ * .void()
125
+ * ```
52
126
  */
53
127
  do: (handler: ReactionHandler<E, K>, options?: Partial<ReactionOptions>) => ActBuilder<S, E, A> & {
54
128
  /**
55
- * Route the reaction to a specific target and optionally source streams (resolver function).
56
- * @param resolver The resolver function or target stream name (all sources) as a shorthand
57
- * @returns The builder (for chaining)
129
+ * Routes the reaction to a specific target stream.
130
+ *
131
+ * Use this when the reaction triggers an action on a specific state instance.
132
+ * You can provide either a static stream name (string) or a resolver function
133
+ * that dynamically determines the target based on the event.
134
+ *
135
+ * @param resolver - Target stream name (string) or resolver function
136
+ * @returns The builder for chaining
137
+ *
138
+ * @example Static target stream
139
+ * ```typescript
140
+ * .on("OrderPlaced")
141
+ * .do(async (event) => ["reduceStock", { amount: 10 }])
142
+ * .to("inventory-main")
143
+ * ```
144
+ *
145
+ * @example Dynamic target based on event data
146
+ * ```typescript
147
+ * .on("OrderPlaced")
148
+ * .do(async (event) => ["reduceStock", { amount: 10 }])
149
+ * .to((event) => ({
150
+ * target: `inventory-${event.data.warehouseId}`
151
+ * }))
152
+ * ```
153
+ *
154
+ * @example Source and target routing
155
+ * ```typescript
156
+ * .on("UserLoggedIn")
157
+ * .do(async (event) => ["incrementCount", {}])
158
+ * .to(({ stream }) => ({
159
+ * source: stream, // React to events from this user stream
160
+ * target: `stats-${stream}` // Update corresponding stats stream
161
+ * }))
162
+ * ```
58
163
  */
59
164
  to: (resolver: ReactionResolver<E, K> | string) => ActBuilder<S, E, A>;
60
165
  /**
61
- * Mark the reaction as void (no routing).
62
- * @returns The builder (for chaining)
166
+ * Marks the reaction as void (side-effect only, no target stream).
167
+ *
168
+ * Use this when the reaction doesn't trigger any actions - it only performs
169
+ * side effects like logging, sending notifications, or updating external systems.
170
+ *
171
+ * @returns The builder for chaining
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * .on("UserCreated")
176
+ * .do(async (event) => {
177
+ * await sendEmail(event.data.email, "Welcome!");
178
+ * await logger.info("User created", event.data);
179
+ * })
180
+ * .void() // No target stream
181
+ * ```
63
182
  */
64
183
  void: () => ActBuilder<S, E, A>;
65
184
  };
66
185
  };
67
186
  /**
68
- * Build the application and return an Act orchestrator.
187
+ * Builds and returns the Act orchestrator instance.
188
+ *
189
+ * This finalizes the builder configuration and creates the orchestrator that
190
+ * can execute actions, load state, and process reactions.
69
191
  *
70
- * @param drainLimit (Optional) The maximum number of events to drain per cycle (default: 10)
192
+ * @param drainLimit - Deprecated parameter, no longer used
71
193
  * @returns The Act orchestrator instance
194
+ *
195
+ * @example
196
+ * ```typescript
197
+ * const app = act()
198
+ * .with(Counter)
199
+ * .with(User)
200
+ * .on("UserCreated")
201
+ * .do(sendWelcomeEmail)
202
+ * .void()
203
+ * .build();
204
+ *
205
+ * // Now use the app
206
+ * await app.do("createUser", target, payload);
207
+ * await app.drain();
208
+ * ```
209
+ *
210
+ * @see {@link Act} for available orchestrator methods
72
211
  */
73
212
  build: (drainLimit?: number) => Act<S, E, A>;
74
213
  /**
75
- * The registered event schemas and reaction maps.
214
+ * The registered event schemas and their reaction maps.
215
+ *
216
+ * This is an internal registry maintained by the builder. Generally, you don't
217
+ * need to access this directly.
76
218
  */
77
219
  readonly events: EventRegister<E>;
78
220
  };
79
221
  /**
80
- * Creates an ActBuilder instance for composing event-sourced applications.
222
+ * Creates a new Act orchestrator builder for composing event-sourced applications.
223
+ *
224
+ * The Act orchestrator is responsible for:
225
+ * - Managing state instances (aggregates)
226
+ * - Executing actions and committing events
227
+ * - Processing reactions (event handlers)
228
+ * - Coordinating event-driven workflows
81
229
  *
82
- * Use this function to start building your application by chaining `.with()`, `.on()`, and `.build()` calls.
230
+ * Use the fluent API to register states with `.with()`, define event reactions with `.on()`,
231
+ * and build the orchestrator with `.build()`.
83
232
  *
84
- * @template S The type of state
85
- * @template E The type of events
86
- * @template A The type of actions
87
- * @returns An ActBuilder instance
233
+ * @template S - State schema register type
234
+ * @template E - Event schemas type
235
+ * @template A - Action schemas type
236
+ * @returns An ActBuilder instance for fluent API configuration
237
+ *
238
+ * @example Basic application with single state
239
+ * ```typescript
240
+ * import { act, state } from "@rotorsoft/act";
241
+ * import { z } from "zod";
242
+ *
243
+ * const Counter = state("Counter", z.object({ count: z.number() }))
244
+ * .init(() => ({ count: 0 }))
245
+ * .emits({ Incremented: z.object({ amount: z.number() }) })
246
+ * .patch({ Incremented: (event, state) => ({ count: state.count + event.data.amount }) })
247
+ * .on("increment", z.object({ by: z.number() }))
248
+ * .emit((action) => ["Incremented", { amount: action.by }])
249
+ * .build();
88
250
  *
89
- * @example
90
251
  * const app = act()
91
252
  * .with(Counter)
92
- * .on("Incremented").do(async (event) => { ... })
93
253
  * .build();
254
+ *
255
+ * // Execute action
256
+ * await app.do("increment",
257
+ * { stream: "counter1", actor: { id: "user1", name: "Alice" } },
258
+ * { by: 5 }
259
+ * );
260
+ *
261
+ * // Load current state
262
+ * const snapshot = await app.load(Counter, "counter1");
263
+ * console.log(snapshot.state.count); // 5
264
+ * ```
265
+ *
266
+ * @example Application with reactions
267
+ * ```typescript
268
+ * const User = state("User", z.object({ name: z.string(), email: z.string() }))
269
+ * .init((data) => data)
270
+ * .emits({ UserCreated: z.object({ name: z.string(), email: z.string() }) })
271
+ * .patch({ UserCreated: (event) => event.data })
272
+ * .on("createUser", z.object({ name: z.string(), email: z.string() }))
273
+ * .emit((action) => ["UserCreated", action])
274
+ * .build();
275
+ *
276
+ * const app = act()
277
+ * .with(User)
278
+ * .on("UserCreated")
279
+ * .do(async (event) => {
280
+ * // Send welcome email
281
+ * await sendEmail(event.data.email, "Welcome!");
282
+ * logger.info(`Sent welcome email to ${event.data.email}`);
283
+ * })
284
+ * .void() // No target stream, just side effects
285
+ * .build();
286
+ *
287
+ * // Create user (triggers email sending via reaction)
288
+ * await app.do("createUser",
289
+ * { stream: "user-123", actor: { id: "admin", name: "Admin" } },
290
+ * { name: "Alice", email: "alice@example.com" }
291
+ * );
292
+ *
293
+ * // Process reactions
294
+ * await app.drain();
295
+ * ```
296
+ *
297
+ * @example Multi-state application with event correlation
298
+ * ```typescript
299
+ * const Order = state("Order", z.object({ items: z.array(z.string()), total: z.number() }))
300
+ * .init((data) => data)
301
+ * .emits({ OrderPlaced: z.object({ items: z.array(z.string()), total: z.number() }) })
302
+ * .patch({ OrderPlaced: (event) => event.data })
303
+ * .on("placeOrder", z.object({ items: z.array(z.string()), total: z.number() }))
304
+ * .emit((action) => ["OrderPlaced", action])
305
+ * .build();
306
+ *
307
+ * const Inventory = state("Inventory", z.object({ stock: z.number() }))
308
+ * .init(() => ({ stock: 100 }))
309
+ * .emits({ StockReduced: z.object({ amount: z.number() }) })
310
+ * .patch({ StockReduced: (event, state) => ({ stock: state.stock - event.data.amount }) })
311
+ * .on("reduceStock", z.object({ amount: z.number() }))
312
+ * .emit((action) => ["StockReduced", { amount: action.amount }])
313
+ * .build();
314
+ *
315
+ * const app = act()
316
+ * .with(Order)
317
+ * .with(Inventory)
318
+ * .on("OrderPlaced")
319
+ * .do(async (event) => {
320
+ * // Reduce inventory for each item
321
+ * return ["reduceStock", { amount: event.data.items.length }];
322
+ * })
323
+ * .to("inventory-1") // Target specific inventory stream
324
+ * .build();
325
+ *
326
+ * await app.do("placeOrder",
327
+ * { stream: "order-1", actor: { id: "user1", name: "Alice" } },
328
+ * { items: ["item1", "item2"], total: 100 }
329
+ * );
330
+ *
331
+ * // Process reaction (reduces inventory)
332
+ * await app.drain();
333
+ * ```
334
+ *
335
+ * @see {@link ActBuilder} for available builder methods
336
+ * @see {@link Act} for orchestrator API methods
337
+ * @see {@link state} for defining states
338
+ * @see {@link https://rotorsoft.github.io/act-root/docs/intro | Documentation}
94
339
  */
95
340
  export declare function act<S extends SchemaRegister<A> = {}, E extends Schemas = {}, A extends Schemas = {}>(states?: Set<string>, registry?: Registry<S, E, A>): ActBuilder<S, E, A>;
96
341
  //# sourceMappingURL=act-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"act-builder.d.ts","sourceRoot":"","sources":["../../src/act-builder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,KAAK,EACV,aAAa,EAEb,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,cAAc,EACd,OAAO,EACP,KAAK,EACN,MAAM,kBAAkB,CAAC;AAU1B;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,CACpB,CAAC,SAAS,cAAc,CAAC,CAAC,CAAC,EAC3B,CAAC,SAAS,OAAO,EACjB,CAAC,SAAS,OAAO,IACf;IACF;;;;;;;;OAQG;IACH,IAAI,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,OAAO,EAAE,EAAE,SAAS,OAAO,EAC9D,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,KACrB,UAAU,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,MAAM,EAAE,GAAG,EAAE;KAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7D;;;;;;OAMG;IACH,EAAE,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EACpB,KAAK,EAAE,CAAC,KACL;QACH;;;;;;WAMG;QACH,EAAE,EAAE,CACF,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KAC/B,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG;YACzB;;;;eAIG;YACH,EAAE,EAAE,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACvE;;;eAGG;YACH,IAAI,EAAE,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;SACjC,CAAC;KACH,CAAC;IACF;;;;;OAKG;IACH,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CACnC,CAAC;AAIF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,GAAG,CAEjB,CAAC,SAAS,cAAc,CAAC,CAAC,CAAC,GAAG,EAAE,EAChC,CAAC,SAAS,OAAO,GAAG,EAAE,EACtB,CAAC,SAAS,OAAO,GAAG,EAAE,EAEtB,MAAM,GAAE,GAAG,CAAC,MAAM,CAAa,EAC/B,QAAQ,GAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAGzB,GACA,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAsFrB"}
1
+ {"version":3,"file":"act-builder.d.ts","sourceRoot":"","sources":["../../src/act-builder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,KAAK,EACV,aAAa,EAEb,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,cAAc,EACd,OAAO,EACP,KAAK,EACN,MAAM,kBAAkB,CAAC;AAU1B;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,UAAU,CACpB,CAAC,SAAS,cAAc,CAAC,CAAC,CAAC,EAC3B,CAAC,SAAS,OAAO,EACjB,CAAC,SAAS,OAAO,IACf;IACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,IAAI,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,OAAO,EAAE,EAAE,SAAS,OAAO,EAC9D,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,KACrB,UAAU,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,MAAM,EAAE,GAAG,EAAE;KAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7D;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,EAAE,EAAE,CAAC,CAAC,SAAS,MAAM,CAAC,EACpB,KAAK,EAAE,CAAC,KACL;QACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA2CG;QACH,EAAE,EAAE,CACF,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KAC/B,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG;YACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAmCG;YACH,EAAE,EAAE,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACvE;;;;;;;;;;;;;;;;;eAiBG;YACH,IAAI,EAAE,MAAM,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;SACjC,CAAC;KACH,CAAC;IACF;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,KAAK,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CACnC,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsHG;AACH,wBAAgB,GAAG,CAEjB,CAAC,SAAS,cAAc,CAAC,CAAC,CAAC,GAAG,EAAE,EAChC,CAAC,SAAS,OAAO,GAAG,EAAE,EACtB,CAAC,SAAS,OAAO,GAAG,EAAE,EAEtB,MAAM,GAAE,GAAG,CAAC,MAAM,CAAa,EAC/B,QAAQ,GAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAGzB,GACA,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAsFrB"}