@rotorsoft/act 0.13.0 → 0.14.0

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.
@@ -46,7 +46,7 @@ export type StateBuilder<S extends Schema, N extends string = string> = {
46
46
  *
47
47
  * @template E - Event schemas type
48
48
  * @param events - Object mapping event names to Zod schemas
49
- * @returns A builder with `.patch()` to define event handlers
49
+ * @returns An ActionBuilder (with optional `.patch()` to override specific reducers)
50
50
  *
51
51
  * @example
52
52
  * ```typescript
@@ -57,27 +57,28 @@ export type StateBuilder<S extends Schema, N extends string = string> = {
57
57
  * })
58
58
  * ```
59
59
  */
60
- emits: <E extends Schemas>(events: ZodTypes<E>) => {
60
+ emits: <E extends Schemas>(events: ZodTypes<E>) => ActionBuilder<S, E, {}, N> & {
61
61
  /**
62
- * Defines how each event updates (patches) the state.
62
+ * Overrides specific event reducers. Events without a custom patch
63
+ * default to passthrough: `({ data }) => data` (event data merges
64
+ * into state).
63
65
  *
64
- * Patch handlers are reducers - pure functions that take an event and current state,
65
- * and return the changes to apply. Return partial state objects; unchanged fields
66
- * are preserved automatically.
67
- *
68
- * @param patch - Object mapping event names to patch handler functions
66
+ * @param patch - Partial map of event names to patch handler functions
69
67
  * @returns An ActionBuilder for defining actions
70
68
  *
71
- * @example
69
+ * @example Only override the events that need custom logic
72
70
  * ```typescript
71
+ * .emits({ TicketOpened, TicketClosed, TicketResolved })
73
72
  * .patch({
74
- * Incremented: (event, state) => ({ count: state.count + event.data.amount }),
75
- * Decremented: (event, state) => ({ count: state.count - event.data.amount }),
76
- * Reset: () => ({ count: 0 })
73
+ * TicketOpened: ({ data }) => {
74
+ * const { message, messageId, userId, ...other } = data;
75
+ * return { ...other, userId, messages: { [messageId]: { ... } } };
76
+ * },
77
77
  * })
78
+ * // TicketClosed and TicketResolved use passthrough
78
79
  * ```
79
80
  */
80
- patch: (patch: PatchHandlers<S, E>) => ActionBuilder<S, E, {}, N>;
81
+ patch: (patch: Partial<PatchHandlers<S, E>>) => ActionBuilder<S, E, {}, N>;
81
82
  };
82
83
  };
83
84
  };
@@ -168,20 +169,28 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
168
169
  * and must return one or more events to emit. Events are applied to state
169
170
  * via the patch handlers defined earlier.
170
171
  *
171
- * @param handler - Function that returns events to emit
172
+ * Pass a string event name for passthrough: the action payload becomes
173
+ * the event data directly.
174
+ *
175
+ * @param handler - Function that returns events to emit, or event name string for passthrough
172
176
  * @returns The ActionBuilder for chaining more actions
173
177
  *
174
- * @example
178
+ * @example Custom handler
175
179
  * ```typescript
176
180
  * .emit((action, snapshot) => {
177
181
  * const newBalance = snapshot.state.balance + action.amount;
178
182
  * return ["Deposited", { amount: action.amount, newBalance }];
179
183
  * })
180
184
  * ```
185
+ *
186
+ * @example Passthrough (action payload = event data)
187
+ * ```typescript
188
+ * .emit("TicketAssigned")
189
+ * ```
181
190
  */
182
191
  emit: (handler: ActionHandler<S, E, {
183
192
  [P in K]: AX;
184
- }, K>) => ActionBuilder<S, E, A & {
193
+ }, K> | (keyof E & string)) => ActionBuilder<S, E, A & {
185
194
  [P in K]: AX;
186
195
  }, N>;
187
196
  };
@@ -192,9 +201,17 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
192
201
  * and must return one or more events to emit. Return a single event as
193
202
  * `["EventName", data]` or multiple events as an array of event tuples.
194
203
  *
195
- * @param handler - Function that returns events to emit
204
+ * Pass a string event name for passthrough: the action payload becomes
205
+ * the event data directly.
206
+ *
207
+ * @param handler - Function that returns events to emit, or event name string for passthrough
196
208
  * @returns The ActionBuilder for chaining more actions
197
209
  *
210
+ * @example Passthrough (action payload = event data)
211
+ * ```typescript
212
+ * .emit("Incremented")
213
+ * ```
214
+ *
198
215
  * @example Single event
199
216
  * ```typescript
200
217
  * .emit((action) => ["Incremented", { amount: action.by }])
@@ -207,23 +224,10 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
207
224
  * ["LogUpdated", { message: `Incremented by ${action.by}` }]
208
225
  * ])
209
226
  * ```
210
- *
211
- * @example Conditional events
212
- * ```typescript
213
- * .emit((action, snapshot) => {
214
- * if (snapshot.state.count + action.by >= 100) {
215
- * return [
216
- * ["Incremented", { amount: action.by }],
217
- * ["MilestoneReached", { milestone: 100 }]
218
- * ];
219
- * }
220
- * return ["Incremented", { amount: action.by }];
221
- * })
222
- * ```
223
227
  */
224
228
  emit: (handler: ActionHandler<S, E, {
225
229
  [P in K]: AX;
226
- }, K>) => ActionBuilder<S, E, A & {
230
+ }, K> | (keyof E & string)) => ActionBuilder<S, E, A & {
227
231
  [P in K]: AX;
228
232
  }, N>;
229
233
  };
@@ -272,7 +276,7 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
272
276
  * const Counter = state({ Counter: schema })
273
277
  * .init(() => ({ count: 0 }))
274
278
  * .emits({ Incremented: z.object({ amount: z.number() }) })
275
- * .patch({ Incremented: (event, state) => ({ count: state.count + event.data.amount }) })
279
+ * .patch({ Incremented: ({ data }, state) => ({ count: state.count + data.amount }) })
276
280
  * .on({ increment: z.object({ by: z.number() }) })
277
281
  * .emit((action) => ["Incremented", { amount: action.by }])
278
282
  * .build(); // Returns State<S, E, A, N>
@@ -290,9 +294,9 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
290
294
  *
291
295
  * The state builder provides a fluent API for defining:
292
296
  * 1. Initial state via `.init()`
293
- * 2. Event types via `.emits()`
294
- * 3. Event handlers (reducers) via `.patch()`
295
- * 4. Actions (commands) via `.on()` → `.emit()`
297
+ * 2. Event types via `.emits()` — all events default to passthrough (`({ data }) => data`)
298
+ * 3. Custom event reducers via `.patch()` (optional — only for events that need custom logic)
299
+ * 4. Actions (commands) via `.on()` → `.emit()` — pass an event name string for passthrough
296
300
  * 5. Business rules (invariants) via `.given()`
297
301
  * 6. Snapshotting strategy via `.snap()`
298
302
  *
@@ -300,7 +304,7 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
300
304
  * @param entry - Single-key record mapping state name to Zod schema (e.g., `{ Counter: z.object({ count: z.number() }) }`)
301
305
  * @returns A StateBuilder instance for fluent API configuration
302
306
  *
303
- * @example Basic counter state
307
+ * @example Basic counter state (with custom patch)
304
308
  * ```typescript
305
309
  * import { state } from "@rotorsoft/act";
306
310
  * import { z } from "zod";
@@ -310,14 +314,25 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
310
314
  * .emits({
311
315
  * Incremented: z.object({ amount: z.number() })
312
316
  * })
313
- * .patch({
314
- * Incremented: (event, state) => ({ count: state.count + event.data.amount })
317
+ * .patch({ // optional — only for events needing custom reducers
318
+ * Incremented: ({ data }, state) => ({ count: state.count + data.amount })
315
319
  * })
316
320
  * .on({ increment: z.object({ by: z.number() }) })
317
321
  * .emit((action) => ["Incremented", { amount: action.by }])
318
322
  * .build();
319
323
  * ```
320
324
  *
325
+ * @example Passthrough state (no custom patch or emit needed)
326
+ * ```typescript
327
+ * const DigitBoard = state({ DigitBoard: z.object({ digit: z.string() }) })
328
+ * .init(() => ({ digit: "" }))
329
+ * .emits({ DigitCounted: z.object({ digit: z.string() }) })
330
+ * // no .patch() — passthrough is the default (event data merges into state)
331
+ * .on({ CountDigit: z.object({ digit: z.string() }) })
332
+ * .emit("DigitCounted") // string passthrough — action payload becomes event data
333
+ * .build();
334
+ * ```
335
+ *
321
336
  * @example State with multiple events and invariants
322
337
  * ```typescript
323
338
  * const BankAccount = state({ BankAccount: z.object({
@@ -331,29 +346,29 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
331
346
  * Withdrawn: z.object({ amount: z.number() }),
332
347
  * Closed: z.object({})
333
348
  * })
334
- * .patch({
335
- * Deposited: (event, state) => ({ balance: state.balance + event.data.amount }),
336
- * Withdrawn: (event, state) => ({ balance: state.balance - event.data.amount }),
349
+ * .patch({ // only override events needing custom logic
350
+ * Deposited: ({ data }, state) => ({ balance: state.balance + data.amount }),
351
+ * Withdrawn: ({ data }, state) => ({ balance: state.balance - data.amount }),
337
352
  * Closed: () => ({ status: "closed", balance: 0 })
338
353
  * })
339
354
  * .on({ deposit: z.object({ amount: z.number() }) })
340
355
  * .given([
341
356
  * (_, snap) => snap.state.status === "open" || "Account must be open"
342
357
  * ])
343
- * .emit((action) => ["Deposited", { amount: action.amount }])
358
+ * .emit("Deposited") // passthrough action payload { amount } becomes event data
344
359
  * .on({ withdraw: z.object({ amount: z.number() }) })
345
360
  * .given([
346
361
  * (_, snap) => snap.state.status === "open" || "Account must be open",
347
362
  * (_, snap, action) =>
348
363
  * snap.state.balance >= action.amount || "Insufficient funds"
349
364
  * ])
350
- * .emit((action) => ["Withdrawn", { amount: action.amount }])
365
+ * .emit("Withdrawn")
351
366
  * .on({ close: z.object({}) })
352
367
  * .given([
353
368
  * (_, snap) => snap.state.status === "open" || "Already closed",
354
369
  * (_, snap) => snap.state.balance === 0 || "Balance must be zero"
355
370
  * ])
356
- * .emit(() => ["Closed", {}])
371
+ * .emit("Closed")
357
372
  * .build();
358
373
  * ```
359
374
  *
@@ -369,14 +384,14 @@ export type ActionBuilder<S extends Schema, E extends Schemas, A extends Schemas
369
384
  * UserCreated: z.object({ name: z.string(), email: z.string() }),
370
385
  * UserLoggedIn: z.object({})
371
386
  * })
372
- * .patch({
373
- * UserCreated: (event) => event.data,
387
+ * .patch({ // only override events needing custom logic
374
388
  * UserLoggedIn: (_, state) => ({ loginCount: state.loginCount + 1 })
375
389
  * })
390
+ * // UserCreated uses passthrough — event data merges into state
376
391
  * .on({ createUser: z.object({ name: z.string(), email: z.string() }) })
377
- * .emit((action) => ["UserCreated", action])
392
+ * .emit("UserCreated") // passthrough
378
393
  * .on({ login: z.object({}) })
379
- * .emit(() => ["UserLoggedIn", {}])
394
+ * .emit("UserLoggedIn")
380
395
  * .snap((snap) => snap.patches >= 10) // Snapshot every 10 events
381
396
  * .build();
382
397
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"state-builder.d.ts","sourceRoot":"","sources":["../../src/state-builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state-builder.d.ts","sourceRoot":"","sources":["../../src/state-builder.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1652,19 +1652,34 @@ function state(entry) {
1652
1652
  init(init) {
1653
1653
  return {
1654
1654
  emits(events) {
1655
- return {
1656
- patch(patch2) {
1655
+ const defaultPatch = Object.fromEntries(
1656
+ Object.keys(events).map((k) => [
1657
+ k,
1658
+ ({ data }) => data
1659
+ ])
1660
+ );
1661
+ const builder = action_builder({
1662
+ events,
1663
+ actions: {},
1664
+ state: stateSchema,
1665
+ name,
1666
+ init,
1667
+ patch: defaultPatch,
1668
+ on: {}
1669
+ });
1670
+ return Object.assign(builder, {
1671
+ patch(customPatch) {
1657
1672
  return action_builder({
1658
1673
  events,
1659
1674
  actions: {},
1660
1675
  state: stateSchema,
1661
1676
  name,
1662
1677
  init,
1663
- patch: patch2,
1678
+ patch: { ...defaultPatch, ...customPatch },
1664
1679
  on: {}
1665
1680
  });
1666
1681
  }
1667
- };
1682
+ });
1668
1683
  }
1669
1684
  };
1670
1685
  }
@@ -1687,7 +1702,12 @@ function action_builder(state2) {
1687
1702
  return { emit };
1688
1703
  }
1689
1704
  function emit(handler) {
1690
- on[action2] = handler;
1705
+ if (typeof handler === "string") {
1706
+ const eventName = handler;
1707
+ on[action2] = ((payload) => [eventName, payload]);
1708
+ } else {
1709
+ on[action2] = handler;
1710
+ }
1691
1711
  return action_builder({
1692
1712
  ...state2,
1693
1713
  actions,