@rotorsoft/act 1.32.29 → 1.33.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.
Files changed (141) hide show
  1. package/README.md +0 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/act.d.ts +41 -233
  4. package/dist/@types/act.d.ts.map +1 -1
  5. package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
  6. package/dist/@types/builders/act-builder.d.ts +2 -115
  7. package/dist/@types/builders/act-builder.d.ts.map +1 -1
  8. package/dist/@types/builders/builder-utils.d.ts +1 -1
  9. package/dist/@types/builders/builder-utils.d.ts.map +1 -1
  10. package/dist/@types/builders/event-builder.d.ts +5 -5
  11. package/dist/@types/builders/event-builder.d.ts.map +1 -1
  12. package/dist/@types/builders/merge.d.ts +1 -1
  13. package/dist/@types/builders/merge.d.ts.map +1 -1
  14. package/dist/@types/builders/projection-builder.d.ts +2 -144
  15. package/dist/@types/builders/projection-builder.d.ts.map +1 -1
  16. package/dist/@types/builders/slice-builder.d.ts +2 -71
  17. package/dist/@types/builders/slice-builder.d.ts.map +1 -1
  18. package/dist/@types/builders/state-builder.d.ts +2 -376
  19. package/dist/@types/builders/state-builder.d.ts.map +1 -1
  20. package/dist/@types/config.d.ts +6 -43
  21. package/dist/@types/config.d.ts.map +1 -1
  22. package/dist/@types/csv.d.ts +1 -15
  23. package/dist/@types/csv.d.ts.map +1 -1
  24. package/dist/@types/index.d.ts +0 -1
  25. package/dist/@types/index.d.ts.map +1 -1
  26. package/dist/@types/internal/autoclose-policy.d.ts +9 -108
  27. package/dist/@types/internal/autoclose-policy.d.ts.map +1 -1
  28. package/dist/@types/internal/autoclose-reaction.d.ts +1 -9
  29. package/dist/@types/internal/autoclose-reaction.d.ts.map +1 -1
  30. package/dist/@types/internal/autoclose-window.d.ts +2 -15
  31. package/dist/@types/internal/autoclose-window.d.ts.map +1 -1
  32. package/dist/@types/internal/backoff.d.ts +1 -6
  33. package/dist/@types/internal/backoff.d.ts.map +1 -1
  34. package/dist/@types/internal/circuit-breaker.d.ts +1 -4
  35. package/dist/@types/internal/circuit-breaker.d.ts.map +1 -1
  36. package/dist/@types/{builders/build-classify.d.ts → internal/classify.d.ts} +2 -15
  37. package/dist/@types/internal/classify.d.ts.map +1 -0
  38. package/dist/@types/internal/close-cycle.d.ts +0 -13
  39. package/dist/@types/internal/close-cycle.d.ts.map +1 -1
  40. package/dist/@types/internal/config.d.ts +9 -81
  41. package/dist/@types/internal/config.d.ts.map +1 -1
  42. package/dist/@types/internal/correlate-cycle.d.ts +1 -1
  43. package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
  44. package/dist/@types/internal/{correlator.d.ts → correlation-id.d.ts} +1 -8
  45. package/dist/@types/internal/correlation-id.d.ts.map +1 -0
  46. package/dist/@types/internal/date-reviver.d.ts +0 -5
  47. package/dist/@types/internal/date-reviver.d.ts.map +1 -1
  48. package/dist/@types/internal/defer-config.d.ts +0 -13
  49. package/dist/@types/internal/defer-config.d.ts.map +1 -1
  50. package/dist/@types/internal/defer-timer.d.ts +1 -5
  51. package/dist/@types/internal/defer-timer.d.ts.map +1 -1
  52. package/dist/@types/internal/drain-cycle.d.ts +1 -1
  53. package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
  54. package/dist/@types/internal/{drain.d.ts → drain-ops.d.ts} +1 -7
  55. package/dist/@types/internal/drain-ops.d.ts.map +1 -0
  56. package/dist/@types/internal/drain-ratio.d.ts +0 -5
  57. package/dist/@types/internal/drain-ratio.d.ts.map +1 -1
  58. package/dist/@types/internal/event-sourcing.d.ts +1 -8
  59. package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
  60. package/dist/@types/internal/event-versions.d.ts +0 -9
  61. package/dist/@types/internal/event-versions.d.ts.map +1 -1
  62. package/dist/@types/internal/lru-map.d.ts +0 -9
  63. package/dist/@types/internal/lru-map.d.ts.map +1 -1
  64. package/dist/@types/internal/pii.d.ts +78 -0
  65. package/dist/@types/internal/pii.d.ts.map +1 -0
  66. package/dist/@types/internal/projection-fold.d.ts +1 -5
  67. package/dist/@types/internal/projection-fold.d.ts.map +1 -1
  68. package/dist/@types/internal/{close-signal.d.ts → reaction-dispatch.d.ts} +46 -6
  69. package/dist/@types/internal/reaction-dispatch.d.ts.map +1 -0
  70. package/dist/@types/internal/report-once.d.ts +1 -8
  71. package/dist/@types/internal/report-once.d.ts.map +1 -1
  72. package/dist/@types/internal/settle.d.ts +21 -21
  73. package/dist/@types/internal/settle.d.ts.map +1 -1
  74. package/dist/@types/internal/tracing.d.ts +3 -14
  75. package/dist/@types/internal/tracing.d.ts.map +1 -1
  76. package/dist/@types/internal/walk-streams.d.ts +0 -2
  77. package/dist/@types/internal/walk-streams.d.ts.map +1 -1
  78. package/dist/@types/ports.d.ts +3 -43
  79. package/dist/@types/ports.d.ts.map +1 -1
  80. package/dist/@types/scoped.d.ts +1 -6
  81. package/dist/@types/scoped.d.ts.map +1 -1
  82. package/dist/@types/sensitive.d.ts +56 -0
  83. package/dist/@types/sensitive.d.ts.map +1 -0
  84. package/dist/@types/test/index.d.ts +1 -0
  85. package/dist/@types/test/index.d.ts.map +1 -1
  86. package/dist/@types/test/sandbox.d.ts +2 -26
  87. package/dist/@types/test/sandbox.d.ts.map +1 -1
  88. package/dist/@types/types/act.d.ts +216 -0
  89. package/dist/@types/types/act.d.ts.map +1 -0
  90. package/dist/@types/types/action.d.ts +67 -0
  91. package/dist/@types/types/action.d.ts.map +1 -1
  92. package/dist/@types/types/builders.d.ts +722 -0
  93. package/dist/@types/types/builders.d.ts.map +1 -0
  94. package/dist/@types/types/errors.d.ts +31 -1
  95. package/dist/@types/types/errors.d.ts.map +1 -1
  96. package/dist/@types/types/index.d.ts +38 -0
  97. package/dist/@types/types/index.d.ts.map +1 -1
  98. package/dist/@types/types/ports.d.ts +16 -10
  99. package/dist/@types/types/ports.d.ts.map +1 -1
  100. package/dist/@types/types/reaction.d.ts +11 -1
  101. package/dist/@types/types/reaction.d.ts.map +1 -1
  102. package/dist/@types/types/sandbox.d.ts +31 -0
  103. package/dist/@types/types/sandbox.d.ts.map +1 -0
  104. package/dist/@types/types/schemas.d.ts +12 -17
  105. package/dist/@types/types/schemas.d.ts.map +1 -1
  106. package/dist/{chunk-F7K7SIG6.js → chunk-2PMUONGF.js} +38 -57
  107. package/dist/chunk-2PMUONGF.js.map +1 -0
  108. package/dist/chunk-HF433MAF.cjs +264 -0
  109. package/dist/chunk-HF433MAF.cjs.map +1 -0
  110. package/dist/{chunk-5FXT63KF.js → chunk-JABYJGT7.js} +184 -24
  111. package/dist/chunk-JABYJGT7.js.map +1 -0
  112. package/dist/chunk-VE3MTY4A.cjs +1233 -0
  113. package/dist/chunk-VE3MTY4A.cjs.map +1 -0
  114. package/dist/index.cjs +1615 -3003
  115. package/dist/index.cjs.map +1 -1
  116. package/dist/index.js +751 -813
  117. package/dist/index.js.map +1 -1
  118. package/dist/test/index.cjs +16 -1105
  119. package/dist/test/index.cjs.map +1 -1
  120. package/dist/test/index.js +2 -2
  121. package/dist/test/index.js.map +1 -1
  122. package/dist/types/index.cjs +58 -276
  123. package/dist/types/index.cjs.map +1 -1
  124. package/dist/types/index.js +15 -1
  125. package/package.json +1 -1
  126. package/dist/@types/builders/build-classify.d.ts.map +0 -1
  127. package/dist/@types/builders/builder-base.d.ts +0 -70
  128. package/dist/@types/builders/builder-base.d.ts.map +0 -1
  129. package/dist/@types/builders/reaction-builder.d.ts +0 -55
  130. package/dist/@types/builders/reaction-builder.d.ts.map +0 -1
  131. package/dist/@types/internal/close-signal.d.ts.map +0 -1
  132. package/dist/@types/internal/correlator.d.ts.map +0 -1
  133. package/dist/@types/internal/defer-signal.d.ts +0 -35
  134. package/dist/@types/internal/defer-signal.d.ts.map +0 -1
  135. package/dist/@types/internal/drain.d.ts.map +0 -1
  136. package/dist/@types/internal/index.d.ts +0 -43
  137. package/dist/@types/internal/index.d.ts.map +0 -1
  138. package/dist/@types/internal/sensitive.d.ts +0 -177
  139. package/dist/@types/internal/sensitive.d.ts.map +0 -1
  140. package/dist/chunk-5FXT63KF.js.map +0 -1
  141. package/dist/chunk-F7K7SIG6.js.map +0 -1
@@ -0,0 +1,722 @@
1
+ /**
2
+ * Public contracts of the fluent builders: what `state()`, `slice()`,
3
+ * `projection()` and `act()` return at each step of the chain. The
4
+ * builders in `../builders/` implement them.
5
+ */
6
+ import type { ZodType } from "zod";
7
+ import type { Act } from "../act.js";
8
+ import type { ActionHandler, ActionOptions, ActOptions, Actor, AutocloseArchiver, AutoclosePolicy, BatchHandler, CacheEntry, Committed, DeferWhen, EventRegister, FoldOptions, IAct, Invariant, LaneConfig, PatchHandlers, ReactionOptions, ReactionResolver, Schema, SchemaRegister, Schemas, Snapshot, State, ZodTypes } from "./index.js";
9
+ import type { DEFAULT_LANE } from "./reaction.js";
10
+ /**
11
+ * The `.do(handler)` step, parameterized by the builder it returns (`TReturn`).
12
+ * Identical for both builders modulo that return type.
13
+ */
14
+ export type ReactionDo<TReturn, TEvents extends Schemas, TActions extends Schemas, TActor extends Actor, TLanes extends string, TKey extends keyof TEvents> = (handler: (event: Committed<TEvents, TKey>, stream: string, app: IAct<TEvents, TActions, TActor>) => Promise<Snapshot<Schema, TEvents> | void>, options?: Partial<ReactionOptions>) => TReturn & {
15
+ to: (resolver: ReactionResolver<TEvents, TKey, TLanes> | string) => TReturn;
16
+ };
17
+ /**
18
+ * The object `.on(event)` returns: run immediately with `.do(...)`, or hold
19
+ * with `.defer(when).do(...)`. Parameterized by the return builder so
20
+ * `act()` and `slice()` share one shape.
21
+ */
22
+ export type ReactionOn<TReturn, TEvents extends Schemas, TActions extends Schemas, TActor extends Actor, TLanes extends string, TKey extends keyof TEvents> = {
23
+ do: ReactionDo<TReturn, TEvents, TActions, TActor, TLanes, TKey>;
24
+ defer: (schedule: DeferWhen | ((event: Committed<TEvents, TKey>) => DeferWhen)) => {
25
+ do: ReactionDo<TReturn, TEvents, TActions, TActor, TLanes, TKey>;
26
+ };
27
+ };
28
+ /**
29
+ * Builder members that return `Self` unchanged — shared by both builders.
30
+ *
31
+ * @template Self - the concrete builder type returned for chaining
32
+ * @template TEvents - event schemas
33
+ * @template TActions - action schemas
34
+ * @template TActor - actor type
35
+ * @template TLanes - declared lane-name union
36
+ */
37
+ export interface BuilderBase<Self, TEvents extends Schemas, TActions extends Schemas, TActor extends Actor, TLanes extends string> {
38
+ /**
39
+ * Registers a standalone projection. Its events must be a subset of events
40
+ * already registered via `.withState()` (or `.withSlice()` on the Act
41
+ * builder); handlers keep their `(event, stream)` signature.
42
+ */
43
+ withProjection: <TNewEvents extends Schemas>(projection: [Exclude<keyof TNewEvents, keyof TEvents>] extends [never] ? Projection<TNewEvents> : never) => Self;
44
+ /**
45
+ * Begins defining a reaction. Chain `.do(...)` to run immediately, or
46
+ * `.defer(when).do(...)` to hold the reaction until a schedule is due.
47
+ * `.to(...)` follows `.do(...)` in both cases and, for a deferred
48
+ * reaction, routes it onto its own target so the hold doesn't stall the
49
+ * stream's other reactions.
50
+ */
51
+ on: <TKey extends keyof TEvents>(event: TKey) => ReactionOn<Self, TEvents, TActions, TActor, TLanes, TKey>;
52
+ /** The registered event schemas and their reaction maps. */
53
+ readonly events: EventRegister<TEvents>;
54
+ }
55
+ /**
56
+ * Fluent builder interface for composing event-sourced applications.
57
+ *
58
+ * Provides a chainable API for:
59
+ * - Registering states via `.withState()`
60
+ * - Registering slices via `.withSlice()`
61
+ * - Registering projections via `.withProjection()`
62
+ * - Locking a custom actor type via `.withActor<TActor>()`
63
+ * - Declaring drain lanes via `.withLane({name, ...})`
64
+ * - Defining event reactions via `.on()` → `.do()` → `.to()`
65
+ * - Building the orchestrator via `.build()`
66
+ *
67
+ * @template TSchemaReg - Schema register for states (maps action names to state schemas)
68
+ * @template TEvents - Event schemas (maps event names to event data schemas)
69
+ * @template TActions - Action schemas (maps action names to action payload schemas)
70
+ * @template TStateMap - Map of state names to state schemas
71
+ * @template TActor - Actor type extending base Actor
72
+ * @template TLanes - Union of declared lane names. Narrowed by
73
+ * `.withLane({name})` calls so `.to({lane})` and `ActOptions.onlyLanes`
74
+ * reject typos at compile time. Starts at `"default"`.
75
+ *
76
+ * @see {@link act} for usage examples
77
+ * @see {@link Act} for the built orchestrator API
78
+ */
79
+ export interface ActBuilder<TSchemaReg extends SchemaRegister<TActions>, TEvents extends Schemas, TActions extends Schemas, TStateMap extends Record<string, Schema> = {}, TActor extends Actor = Actor, TLanes extends string = typeof DEFAULT_LANE> extends BuilderBase<ActBuilder<TSchemaReg, TEvents, TActions, TStateMap, TActor, TLanes>, TEvents, TActions, TActor, TLanes> {
80
+ /**
81
+ * Registers a state definition with the builder.
82
+ *
83
+ * State names, action names, and event names must be unique across the
84
+ * application (partial states with the same name are merged automatically).
85
+ *
86
+ * @throws {Error} If duplicate action or event names are detected
87
+ */
88
+ withState: <TNewState extends Schema, TNewEvents extends Schemas, TNewActions extends Schemas, TNewName extends string = string>(state: State<TNewState, TNewEvents, TNewActions, TNewName>) => ActBuilder<TSchemaReg & {
89
+ [K in keyof TNewActions]: TNewState;
90
+ }, TEvents & TNewEvents, TActions & TNewActions, TStateMap & {
91
+ [K in TNewName]: TNewState;
92
+ }, TActor, TLanes>;
93
+ /**
94
+ * Registers a slice with the builder.
95
+ *
96
+ * Merges all the slice's states and reactions into the application.
97
+ * State names, action names, and event names must be unique across the
98
+ * application (partial states with the same name are merged automatically).
99
+ *
100
+ * @throws {Error} If duplicate action or event names are detected
101
+ */
102
+ withSlice: <TNewSchemaReg extends SchemaRegister<TNewActions>, TNewEvents extends Schemas, TNewActions extends Schemas, TNewMap extends Record<string, Schema>, TNewLanes extends string>(slice: Slice<TNewSchemaReg, TNewEvents, TNewActions, TNewMap, Actor, TNewLanes>) => ActBuilder<TSchemaReg & TNewSchemaReg, TEvents & TNewEvents, TActions & TNewActions, TStateMap & TNewMap, TActor, TLanes | TNewLanes>;
103
+ /**
104
+ * Locks a custom actor type for this application.
105
+ *
106
+ * This is a pure type-level method — it returns the same builder at
107
+ * runtime but narrows the `TActor` generic so that `app.do()` and
108
+ * reaction dispatchers require the richer actor shape.
109
+ *
110
+ * @template TNewActor - Custom actor type extending base Actor
111
+ * @returns The same builder with `TActor` locked to `TNewActor`
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * type MyActor = { id: string; name: string; role: string; tenantId: string };
116
+ *
117
+ * const app = act()
118
+ * .withActor<MyActor>()
119
+ * .withState(Counter)
120
+ * .build();
121
+ *
122
+ * // Now app.do() requires MyActor in the target
123
+ * await app.do("increment", {
124
+ * stream: "counter-1",
125
+ * actor: { id: "1", name: "Alice", role: "admin", tenantId: "t1" }
126
+ * }, { by: 5 });
127
+ * ```
128
+ */
129
+ withActor: <TNewActor extends Actor>() => ActBuilder<TSchemaReg, TEvents, TActions, TStateMap, TNewActor, TLanes>;
130
+ /**
131
+ * Declares a drain lane. Lane name narrows `TLanes` so
132
+ * `.to({lane})` and `ActOptions.onlyLanes` type-check against it.
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const app = act()
137
+ * .withState(Counter)
138
+ * .withLane({ name: "slow", leaseMillis: 60_000, streamLimit: 5 })
139
+ * .on("OrderConfirmed")
140
+ * .do(deliverWebhook)
141
+ * .to({ target: "webhooks-out", lane: "slow" })
142
+ * .build();
143
+ * ```
144
+ */
145
+ withLane: <const TConfig extends LaneConfig>(config: TConfig) => ActBuilder<TSchemaReg, TEvents, TActions, TStateMap, TActor, TLanes | TConfig["name"]>;
146
+ /**
147
+ * Builds and returns the Act orchestrator instance.
148
+ *
149
+ * @param options - Optional runtime overrides (see {@link ActOptions}).
150
+ * `options.onlyLanes` is narrowed to the declared `TLanes` union, so
151
+ * `onlyLanes: ["typo"]` is a compile error when the lane wasn't
152
+ * declared via `.withLane(...)`.
153
+ * @returns The Act orchestrator instance
154
+ *
155
+ * @see {@link Act} for available orchestrator methods
156
+ */
157
+ build: (options?: ActOptions<TLanes>) => Act<TSchemaReg, TEvents, TActions, TStateMap, TActor>;
158
+ }
159
+ /**
160
+ * Builder interface for defining a state with event sourcing.
161
+ *
162
+ * Provides a fluent API to configure the initial state, event types,
163
+ * and event handlers (reducers) before moving to action configuration.
164
+ *
165
+ * @template TState - State schema type
166
+ * @template TName - State name literal type
167
+ *
168
+ * @see {@link state} for usage examples
169
+ * @see {@link ActionBuilder} for action configuration
170
+ */
171
+ export type StateBuilder<TState extends Schema, TName extends string = string> = {
172
+ /**
173
+ * Defines the initial state for new state instances.
174
+ *
175
+ * The init function is called when a new stream is created (first event).
176
+ * It can accept initial data or return a default state.
177
+ *
178
+ * @param init - Function returning the initial state
179
+ * @returns A builder with `.emits()` to declare event types
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * .init(() => ({ count: 0, created: new Date() }))
184
+ * ```
185
+ */
186
+ init: (init: () => Readonly<TState>) => {
187
+ /**
188
+ * Declares the event types that this state can emit.
189
+ *
190
+ * Events represent facts that have happened - they should be named in past tense.
191
+ * Each event is defined with a Zod schema for type safety and runtime validation.
192
+ *
193
+ * @template TEvents - Event schemas type
194
+ * @param events - Object mapping event names to Zod schemas
195
+ * @returns An ActionBuilder (with optional `.patch()` to override specific reducers)
196
+ *
197
+ * @example
198
+ * ```typescript
199
+ * .emits({
200
+ * Incremented: z.object({ amount: z.number() }),
201
+ * Decremented: z.object({ amount: z.number() }),
202
+ * Reset: z.object({})
203
+ * })
204
+ * ```
205
+ */
206
+ emits: <TEvents extends Schemas>(events: ZodTypes<TEvents>) => ActionBuilder<TState, TEvents, {}, TName> & {
207
+ /**
208
+ * Overrides specific event reducers. Events without a custom patch
209
+ * default to passthrough: `({ data }) => data` (event data merges
210
+ * into state).
211
+ *
212
+ * @param patch - Partial map of event names to patch handler functions
213
+ * @returns An ActionBuilder for defining actions
214
+ *
215
+ * @example Only override the events that need custom logic
216
+ * ```typescript
217
+ * .emits({ TicketOpened, TicketClosed, TicketResolved })
218
+ * .patch({
219
+ * TicketOpened: ({ data }) => {
220
+ * const { message, messageId, userId, ...other } = data;
221
+ * return { ...other, userId, messages: { [messageId]: { ... } } };
222
+ * },
223
+ * })
224
+ * // TicketClosed and TicketResolved use passthrough
225
+ * ```
226
+ */
227
+ patch: (patch: Partial<PatchHandlers<TState, TEvents>>) => ActionBuilder<TState, TEvents, {}, TName>;
228
+ };
229
+ };
230
+ };
231
+ /** Helper: a single-key record mapping a state name to its Zod schema. */
232
+ export type StateEntry<TKey extends string = string, TState extends Schema = Schema> = {
233
+ [P in TKey]: ZodType<TState>;
234
+ };
235
+ /** Helper: a single-key record mapping an action name to its Zod schema. */
236
+ export type ActionEntry<TKey extends string = string, TNewActions extends Schema = Schema> = {
237
+ [P in TKey]: ZodType<TNewActions>;
238
+ };
239
+ /**
240
+ * Builder interface for defining actions (commands) on a state.
241
+ *
242
+ * Actions represent user/system intents to modify state. Each action is validated
243
+ * against a schema, can have business rule invariants, and must emit one or more events.
244
+ *
245
+ * @template TState - State schema type
246
+ * @template TEvents - Event schemas type
247
+ * @template TActions - Action schemas type
248
+ * @template TName - State name literal type
249
+ * @template TSnap - `true` once `.snap(...)` has been called. Gates the
250
+ * `.autocloses({ keep })` rolling-window option — a windowed close is
251
+ * meaningless without snapshots, so `keep` only typechecks after
252
+ * `.snap` in the chain.
253
+ *
254
+ * @see {@link state} for complete usage examples
255
+ */
256
+ export type ActionBuilder<TState extends Schema, TEvents extends Schemas, TActions extends Schemas, TName extends string = string, TSnap extends boolean = false> = {
257
+ /**
258
+ * Defines an action (command) that can be executed on this state.
259
+ *
260
+ * Actions represent intents to change state - they should be named in imperative form
261
+ * (e.g., "CreateUser", "IncrementCounter", "PlaceOrder"). Actions are validated against
262
+ * their schema and must emit at least one event.
263
+ *
264
+ * Pass a `{ ActionName: schema }` record — use shorthand `{ ActionName }`
265
+ * when the variable name matches the action name. The key becomes the
266
+ * action name, the value the Zod schema.
267
+ *
268
+ * Pass an optional second argument to declare a per-action retry
269
+ * policy — the orchestrator retries this action on
270
+ * {@link ConcurrencyError} up to `maxRetries` extra times, applying
271
+ * `backoff` between attempts when set. Omit the argument to keep the
272
+ * current single-attempt behavior (`ConcurrencyError` surfaces on
273
+ * first conflict).
274
+ *
275
+ * @template TKey - Action name (string literal type)
276
+ * @template TNewActions - Action payload schema type
277
+ * @param entry - Single-key record `{ ActionName: schema }`
278
+ * @param options - Optional per-action retry policy
279
+ * ({@link ActionOptions}).
280
+ * @returns An object with `.given()` and `.emit()` for further configuration
281
+ *
282
+ * @example Simple action without invariants
283
+ * ```typescript
284
+ * .on({ increment: z.object({ by: z.number() }) })
285
+ * .emit((action) => ["Incremented", { amount: action.by }])
286
+ * ```
287
+ */
288
+ on: <TKey extends string, TNewActions extends Schema>(entry: ActionEntry<TKey, TNewActions>, options?: ActionOptions) => {
289
+ /**
290
+ * Adds business rule invariants that must hold before the action can execute.
291
+ *
292
+ * Invariants are checked after loading the current state but before emitting
293
+ * events. Each invariant pairs a `description` with a `valid(state, actor?)`
294
+ * predicate — when a predicate returns `false`, the action throws
295
+ * `InvariantError` carrying the description. All invariants must pass for
296
+ * the action to succeed.
297
+ *
298
+ * @param rules - Array of {@link Invariant} objects (`{ description, valid }`)
299
+ * @returns An object with `.emit()` to finalize the action
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * .given([
304
+ * { description: "Must be active", valid: (state) => state.status === "active" },
305
+ * { description: "Must be the owner", valid: (state, actor) => state.ownerId === actor?.id }
306
+ * ])
307
+ * ```
308
+ */
309
+ given: (rules: Invariant<TState>[]) => {
310
+ /**
311
+ * Defines the action handler that emits events.
312
+ *
313
+ * The handler receives the action payload and current state snapshot,
314
+ * and must return one or more events to emit. Events are applied to state
315
+ * via the patch handlers defined earlier.
316
+ *
317
+ * Pass a string event name for passthrough: the action payload becomes
318
+ * the event data directly.
319
+ *
320
+ * @param handler - Function that returns events to emit, or event name string for passthrough
321
+ * @returns The ActionBuilder for chaining more actions
322
+ *
323
+ * @example Custom handler
324
+ * ```typescript
325
+ * .emit((action, snapshot) => {
326
+ * const newBalance = snapshot.state.balance + action.amount;
327
+ * return ["Deposited", { amount: action.amount, newBalance }];
328
+ * })
329
+ * ```
330
+ */
331
+ emit: {
332
+ /** Custom handler — receives `(action, snapshot)` and returns one
333
+ * or more `[EventName, data]` tuples (or `undefined`). */
334
+ (handler: ActionHandler<TState, TEvents, {
335
+ [P in TKey]: TNewActions;
336
+ }, TKey>): ActionBuilder<TState, TEvents, TActions & {
337
+ [P in TKey]: TNewActions;
338
+ }, TName, TSnap>;
339
+ /** Passthrough — the action payload becomes the event data
340
+ * directly. Must reference an event declared in `.emits()`. */
341
+ (event_name: keyof TEvents & string): ActionBuilder<TState, TEvents, TActions & {
342
+ [P in TKey]: TNewActions;
343
+ }, TName, TSnap>;
344
+ };
345
+ };
346
+ /**
347
+ * Defines the action handler that emits events. Same two overloads as
348
+ * the post-`.given()` form above:
349
+ *
350
+ * - **Function** — receives `(action, snapshot)` and returns one or
351
+ * more `[EventName, data]` tuples (or `undefined`).
352
+ * - **String** — passthrough: the action payload becomes the event
353
+ * data directly. Must reference an event declared in `.emits()`.
354
+ *
355
+ * The two overloads are kept separate (rather than merged into a
356
+ * `handler | string` union) so that TS contextual typing of the
357
+ * function alternative isn't degraded by considering the string
358
+ * branch — under the union form `TState` could collapse to its
359
+ * `Schema` constraint inside the callback.
360
+ *
361
+ * @example Passthrough (action payload = event data)
362
+ * ```typescript
363
+ * .emit("Incremented")
364
+ * ```
365
+ */
366
+ emit: {
367
+ (handler: ActionHandler<TState, TEvents, {
368
+ [P in TKey]: TNewActions;
369
+ }, TKey>): ActionBuilder<TState, TEvents, TActions & {
370
+ [P in TKey]: TNewActions;
371
+ }, TName, TSnap>;
372
+ (event_name: keyof TEvents & string): ActionBuilder<TState, TEvents, TActions & {
373
+ [P in TKey]: TNewActions;
374
+ }, TName, TSnap>;
375
+ };
376
+ };
377
+ /**
378
+ * Defines a snapshotting strategy to optimize state reconstruction.
379
+ *
380
+ * Snapshots store the current state at a point in time, allowing faster state loading
381
+ * by avoiding replaying all events from the beginning. The snap function is called
382
+ * after each event is applied and should return `true` when a snapshot should be taken.
383
+ *
384
+ * @param snap - Predicate function that returns true when a snapshot should be taken
385
+ * @returns The ActionBuilder for chaining
386
+ *
387
+ * @example Snapshot every 10 events
388
+ * ```typescript
389
+ * .snap((snapshot) => snapshot.patches >= 10)
390
+ * ```
391
+ */
392
+ snap: (snap: (snapshot: Snapshot<TState, TEvents>) => boolean) => ActionBuilder<TState, TEvents, TActions, TName, true>;
393
+ /**
394
+ * Declares the disclosure predicate for `sensitive(...)`-marked event
395
+ * fields. Gates external reads: returning `true` allows the actor to see
396
+ * plaintext on the event; returning `false` substitutes `"[REDACTED]"`.
397
+ * When absent, the framework default-denies on every external read —
398
+ * fail-safe.
399
+ *
400
+ * One predicate per state. A second `.discloses(...)` call replaces the
401
+ * first (same shape as snapshots being state-level, not per-event).
402
+ *
403
+ * The predicate receives the full event including merged PII so it can
404
+ * branch on the payload itself (e.g.
405
+ * `event.data.ownerId === actor.id`). Reducers, projections, and
406
+ * reactions are unaffected — they follow separate visibility rules.
407
+ *
408
+ * @param disclose - Predicate `(event, actor) => boolean`. `true` =
409
+ * plaintext, `false` = `"[REDACTED]"` substitution.
410
+ * @returns The ActionBuilder for chaining.
411
+ *
412
+ * @example Owner-or-admin disclosure
413
+ * ```typescript
414
+ * state({ User: userSchema })
415
+ * .init(() => ({ ... }))
416
+ * .emits({ UserRegistered: z.object({ email: sensitive(z.string()) }) })
417
+ * .discloses((event, actor) =>
418
+ * actor.id === event.stream || actor.roles?.includes("admin"))
419
+ * ```
420
+ */
421
+ discloses: (disclose: (event: Committed<TEvents, keyof TEvents & string>, actor: Actor) => boolean) => ActionBuilder<TState, TEvents, TActions, TName, TSnap>;
422
+ /**
423
+ * Declares the online close predicate for this state. The
424
+ * orchestrator's autoclose cycle iterates the state's streams once
425
+ * per tick and calls the predicate per candidate; truthy results are
426
+ * scheduled for atomic truncate-and-seed via `Store.truncate` on the
427
+ * next batch.
428
+ *
429
+ * One predicate per state. A second `.autocloses(...)` call replaces
430
+ * the first (same shape as `.snap` / `.discloses` — state-level, not
431
+ * per-event). Absent → the state opts out of online close entirely;
432
+ * the cycle skips it and pays zero per-tick cost for it.
433
+ *
434
+ * Pass a declarative {@link AutoclosePolicy} object literal covering the
435
+ * three operational pressure points (`after`, `is`, `reaches`). Top-level
436
+ * fields combine with AND; an optional `or: {...}` block opens an
437
+ * alternative OR path. Validated via Zod at build time; misconfiguration
438
+ * throws before `act().build()` completes.
439
+ *
440
+ * Under the hood this compiles to a synthesized reaction that runs
441
+ * on a per-aggregate synthetic stream: it defers to `head.created + the
442
+ * policy's min after` while the cooldown holds and closes the stream once
443
+ * the policy matches. There is no background sweep.
444
+ *
445
+ * **The function-predicate form was removed.** `.autocloses` no
446
+ * longer accepts `(stream, head, count) => boolean`; an opaque predicate has
447
+ * no derivable due-time or terminal event to react to. For conditions the
448
+ * declarative form can't express, call `app.close(...)` from your own logic.
449
+ *
450
+ * @param policy The declarative {@link AutoclosePolicy} bag.
451
+ * @returns The ActionBuilder for chaining.
452
+ *
453
+ * @example Declarative — cooldown after terminal (a Ticket closes
454
+ * 90 days after resolution).
455
+ * ```typescript
456
+ * .autocloses({ is: "TicketResolved", after: { days: 90 } })
457
+ * ```
458
+ */
459
+ autocloses: (policy: [TSnap] extends [true] ? AutoclosePolicy : Omit<AutoclosePolicy, "keep"> & {
460
+ /** Rolling-window retention requires `.snap(...)` earlier in
461
+ * the builder chain — a windowed close prunes behind a real
462
+ * snapshot, so a state that never snapshots has nothing to
463
+ * prune behind. */
464
+ keep?: never;
465
+ }) => ActionBuilder<TState, TEvents, TActions, TName, TSnap>;
466
+ /**
467
+ * Declares the archiver the online close cycle runs **before**
468
+ * truncating a stream this state's `.autocloses(...)` predicate
469
+ * accepted. Hosts use it to write events to durable storage (S3,
470
+ * an analytics warehouse, cold tier) before the tombstone lands,
471
+ * so the truncate doesn't lose history that the operator still
472
+ * needs.
473
+ *
474
+ * Threads into `CloseTarget.archive` via the same plumbing
475
+ * `app.close({ stream, archive })` already uses — the cycle holds
476
+ * the stream's guard while the archiver runs, and a thrown
477
+ * archiver leaves the stream guarded but un-truncated. No partial
478
+ * truncate state, no data loss. It is not retried automatically;
479
+ * `app.close([{ stream, archive }])` resumes the guarded stream.
480
+ *
481
+ * One archiver per state. A second `.archives(...)` call replaces
482
+ * the first (same shape as `.snap` / `.discloses` /
483
+ * `.autocloses`). Absent → the cycle truncates without an archive
484
+ * step.
485
+ *
486
+ * @param archive `(stream, head) => Promise<void>`. Runs while
487
+ * the stream is locked against new writes; the truncate runs
488
+ * immediately after a successful resolve.
489
+ * @returns The ActionBuilder for chaining.
490
+ *
491
+ * @example Archive to S3 before truncate.
492
+ * ```typescript
493
+ * state({ Ticket: ticketSchema })
494
+ * .emits({ TicketOpened, TicketResolved })
495
+ * // ...
496
+ * .autocloses({ is: "TicketResolved" })
497
+ * .archives(async (stream) => {
498
+ * const events = await loadEvents(stream);
499
+ * await s3.upload(`tickets/${stream}.jsonl`, events);
500
+ * })
501
+ * ```
502
+ */
503
+ archives: (archive: AutocloseArchiver<TEvents>) => ActionBuilder<TState, TEvents, TActions, TName, TSnap>;
504
+ /**
505
+ * Finalizes and builds the state definition.
506
+ *
507
+ * Call this method after defining all actions, invariants, and patches to create
508
+ * the complete State object that can be registered with Act.
509
+ *
510
+ * @returns The complete strongly-typed State definition
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * const Counter = state({ Counter: schema })
515
+ * .init(() => ({ count: 0 }))
516
+ * .emits({ Incremented: z.object({ amount: z.number() }) })
517
+ * .patch({ Incremented: ({ data }, state) => ({ count: state.count + data.amount }) })
518
+ * .on({ increment: z.object({ by: z.number() }) })
519
+ * .emit((action) => ["Incremented", { amount: action.by }])
520
+ * .build(); // Returns State<TState, TEvents, TActions, TName>
521
+ * ```
522
+ */
523
+ build: () => State<TState, TEvents, TActions, TName>;
524
+ };
525
+ /**
526
+ * A self-contained functional slice grouping partial states with their
527
+ * scoped reactions. Slices are composed into an Act orchestrator via
528
+ * `act().withSlice(slice)`.
529
+ *
530
+ * @template TSchemaReg - Schema register for states
531
+ * @template TEvents - Event schemas from this slice's states
532
+ * @template TActions - Action schemas from this slice's states
533
+ * @template TStateMap - Map of state names to state schemas
534
+ * @template TActor - Actor type extending base Actor
535
+ */
536
+ export type Slice<TSchemaReg extends SchemaRegister<TActions>, TEvents extends Schemas, TActions extends Schemas, TStateMap extends Record<string, Schema> = {}, TActor extends Actor = Actor, TLanes extends string = typeof DEFAULT_LANE> = {
537
+ readonly _tag: "Slice";
538
+ readonly states: Map<string, State<any, any, any>>;
539
+ readonly events: EventRegister<TEvents>;
540
+ readonly projections: ReadonlyArray<Projection<any>>;
541
+ /**
542
+ * Drain lanes declared on this slice via `.withLane(...)`.
543
+ * `act().withSlice(slice)` merges these into the Act's lane set so
544
+ * `.to({lane})` is statically checked at the slice's call site against
545
+ * the lanes the slice itself declared.
546
+ */
547
+ readonly lanes: ReadonlyArray<LaneConfig>;
548
+ /** @internal phantom field for type-level state schema tracking */
549
+ readonly _S?: TSchemaReg;
550
+ /** @internal phantom field for type-level state name tracking */
551
+ readonly _M?: TStateMap;
552
+ /** @internal phantom field for type-level actor tracking */
553
+ readonly _TActor?: TActor;
554
+ /** @internal phantom field for type-level lane union tracking */
555
+ readonly _TLanes?: TLanes;
556
+ };
557
+ /**
558
+ * Fluent builder interface for composing functional slices.
559
+ *
560
+ * Provides a chainable API for registering states and projections,
561
+ * and defining reactions scoped to the slice's own events.
562
+ *
563
+ * @template TSchemaReg - Schema register for states
564
+ * @template TEvents - Event schemas
565
+ * @template TActions - Action schemas
566
+ * @template TStateMap - Map of state names to state schemas
567
+ * @template TActor - Actor type extending base Actor
568
+ */
569
+ export interface SliceBuilder<TSchemaReg extends SchemaRegister<TActions>, TEvents extends Schemas, TActions extends Schemas, TStateMap extends Record<string, Schema> = {}, TActor extends Actor = Actor, TLanes extends string = typeof DEFAULT_LANE> extends BuilderBase<SliceBuilder<TSchemaReg, TEvents, TActions, TStateMap, TActor, TLanes>, TEvents, TActions, TActor, TLanes> {
570
+ /**
571
+ * Registers a state definition with the slice.
572
+ *
573
+ * Include every state whose actions your reaction handlers need to
574
+ * dispatch. Duplicate registrations (same state in multiple slices)
575
+ * are handled automatically at composition time.
576
+ */
577
+ withState: <TNewState extends Schema, TNewEvents extends Schemas, TNewActions extends Schemas, TNewName extends string = string>(state: State<TNewState, TNewEvents, TNewActions, TNewName>) => SliceBuilder<TSchemaReg & {
578
+ [K in keyof TNewActions]: TNewState;
579
+ }, TEvents & TNewEvents, TActions & TNewActions, TStateMap & {
580
+ [K in TNewName]: TNewState;
581
+ }, TActor, TLanes>;
582
+ /**
583
+ * Declares a drain lane on this slice. Merged into the
584
+ * parent Act's lane set by `act().withSlice(slice)`.
585
+ */
586
+ withLane: <const TConfig extends LaneConfig>(config: TConfig) => SliceBuilder<TSchemaReg, TEvents, TActions, TStateMap, TActor, TLanes | TConfig["name"]>;
587
+ /**
588
+ * Builds and returns the Slice data structure.
589
+ */
590
+ build: () => Slice<TSchemaReg, TEvents, TActions, TStateMap, TActor, TLanes>;
591
+ }
592
+ /**
593
+ * A self-contained projection grouping read-model update handlers.
594
+ * Projections are composed into an Act orchestrator via `act().withProjection(projection)`.
595
+ *
596
+ * @template TEvents - Event schemas handled by this projection
597
+ */
598
+ export type Projection<TEvents extends Schemas> = {
599
+ readonly _tag: "Projection";
600
+ readonly events: EventRegister<TEvents>;
601
+ readonly target?: string;
602
+ readonly batchHandler?: BatchHandler<TEvents>;
603
+ /**
604
+ * State-fold spec from `.of()`. The builder only records intent — the
605
+ * orchestrator resolves the REGISTRY-MERGED full state at
606
+ * `act().build()` and synthesizes the batch handler there, so the
607
+ * fold always covers every reducer of the state, including partials
608
+ * merged by slices the projection never saw.
609
+ * @internal
610
+ */
611
+ readonly fold?: {
612
+ readonly name: string;
613
+ readonly flush: (rows: ReadonlyArray<CacheEntry<any>>) => Promise<void>;
614
+ readonly config: Required<FoldOptions>;
615
+ };
616
+ };
617
+ /**
618
+ * The `.of()` continuation: state projections flush the cache layer
619
+ * outward — the rows ARE the streams' {@link CacheEntry} values, one
620
+ * per dirty stream per flush round. Must be an idempotent upsert keyed
621
+ * on `stream` (guard with `event_id` for order safety when a rebuild
622
+ * races a live worker).
623
+ */
624
+ export type FoldFlush<TState extends Schema, TE extends Schemas> = {
625
+ flush: (handler: (rows: ReadonlyArray<CacheEntry<TState>>) => Promise<void>) => {
626
+ build: () => Projection<TE>;
627
+ };
628
+ };
629
+ /**
630
+ * `.of()` accepts the partials of ONE state (same name, enforced at the
631
+ * type level via `TName`) purely for typing and event registration —
632
+ * the fold itself always runs on the registry-merged full state,
633
+ * resolved at `act().build()`. Passing every partial is required: the
634
+ * orchestrator validates completeness at build and throws on missing
635
+ * events.
636
+ */
637
+ export type OfSignatures = {
638
+ <TS1 extends Schema, TE1 extends Schemas, TA1 extends Schemas, TN extends string>(s1: State<TS1, TE1, TA1, TN>, options?: FoldOptions): FoldFlush<TS1, TE1>;
639
+ <TS1 extends Schema, TE1 extends Schemas, TA1 extends Schemas, TS2 extends Schema, TE2 extends Schemas, TA2 extends Schemas, TN extends string>(s1: State<TS1, TE1, TA1, TN>, s2: State<TS2, TE2, TA2, TN>, options?: FoldOptions): FoldFlush<TS1 & TS2, TE1 & TE2>;
640
+ <TS1 extends Schema, TE1 extends Schemas, TA1 extends Schemas, TS2 extends Schema, TE2 extends Schemas, TA2 extends Schemas, TS3 extends Schema, TE3 extends Schemas, TA3 extends Schemas, TN extends string>(s1: State<TS1, TE1, TA1, TN>, s2: State<TS2, TE2, TA2, TN>, s3: State<TS3, TE3, TA3, TN>, options?: FoldOptions): FoldFlush<TS1 & TS2 & TS3, TE1 & TE2 & TE3>;
641
+ <TS1 extends Schema, TE1 extends Schemas, TA1 extends Schemas, TS2 extends Schema, TE2 extends Schemas, TA2 extends Schemas, TS3 extends Schema, TE3 extends Schemas, TA3 extends Schemas, TS4 extends Schema, TE4 extends Schemas, TA4 extends Schemas, TN extends string>(s1: State<TS1, TE1, TA1, TN>, s2: State<TS2, TE2, TA2, TN>, s3: State<TS3, TE3, TA3, TN>, s4: State<TS4, TE4, TA4, TN>, options?: FoldOptions): FoldFlush<TS1 & TS2 & TS3 & TS4, TE1 & TE2 & TE3 & TE4>;
642
+ };
643
+ /** Helper: a single-key record mapping an event name to its Zod schema. */
644
+ export type EventEntry<TKey extends string = string, TData extends Schema = Schema> = {
645
+ [P in TKey]: ZodType<TData>;
646
+ };
647
+ /** Infer the handler-result type after registering one event. */
648
+ export type DoResult<TEvents extends Schemas, TKey extends string, TData extends Schema, TTarget extends string | undefined = undefined> = ProjectionBuilder<TEvents & {
649
+ [P in TKey]: TData;
650
+ }, TTarget, true> & {
651
+ to: (resolver: ReactionResolver<TEvents & {
652
+ [P in TKey]: TData;
653
+ }, TKey> | string) => ProjectionBuilder<TEvents & {
654
+ [P in TKey]: TData;
655
+ }, TTarget, true>;
656
+ };
657
+ /**
658
+ * Fluent builder interface for composing projections.
659
+ *
660
+ * When a static target is provided via `projection("target")`, the builder
661
+ * exposes a `.batch()` method for registering a batch handler that processes
662
+ * all events in a single call.
663
+ *
664
+ * @template TEvents - Event schemas
665
+ * @template TTarget - Static target string or undefined
666
+ */
667
+ export type ProjectionBuilder<TEvents extends Schemas, TTarget extends string | undefined = undefined, THasHandlers extends boolean = false> = {
668
+ /**
669
+ * Begins defining a projection handler for a specific event.
670
+ *
671
+ * Pass a `{ EventName: schema }` record — use shorthand `{ EventName }`
672
+ * when the variable name matches the event name. The key becomes the
673
+ * event name, the value the Zod schema.
674
+ */
675
+ on: <TKey extends string, TData extends Schema>(entry: EventEntry<TKey, TData>) => {
676
+ do: (handler: (event: Committed<TEvents & {
677
+ [P in TKey]: TData;
678
+ }, TKey>, stream: string) => Promise<void>) => DoResult<TEvents, TKey, TData, TTarget>;
679
+ };
680
+ /**
681
+ * Builds and returns the Projection data structure.
682
+ */
683
+ build: () => Projection<TEvents>;
684
+ /**
685
+ * The registered event schemas and their reaction maps.
686
+ */
687
+ readonly events: EventRegister<TEvents>;
688
+ } & (TTarget extends string ? {
689
+ /**
690
+ * Registers a batch handler that processes all events in a single call.
691
+ *
692
+ * Only available on projections with a static target (`projection("target")`).
693
+ * The handler receives a discriminated union of all declared events,
694
+ * enabling bulk DB operations in a single transaction.
695
+ *
696
+ * When defined, the batch handler is always called — even for a single event.
697
+ * Individual `.do()` handlers serve as fallback for projections without `.batch()`.
698
+ */
699
+ batch: (handler: BatchHandler<TEvents>) => {
700
+ build: () => Projection<TEvents>;
701
+ };
702
+ } & (THasHandlers extends false ? {
703
+ /**
704
+ * Declares a state projection: fold every event of the given
705
+ * state through its own reducers and flush one row per stream —
706
+ * the queryable list of the aggregates themselves.
707
+ *
708
+ * The state is the filter: the projection consumes exactly the
709
+ * state's event register, so in a multi-state app only that
710
+ * state's streams are folded — and every event of a folded
711
+ * stream reaches the reducer. Write amplification tracks the
712
+ * distinct stream count, not the event count; `app.reset`
713
+ * rebuilds in O(streams) upserts.
714
+ *
715
+ * The fluent chain enforces the shape: `.of()` is only offered
716
+ * before any `.on()` handler, and narrows to `.flush()` +
717
+ * `.build()` — a projection either folds a state or declares
718
+ * handlers, never both.
719
+ */
720
+ of: OfSignatures;
721
+ } : {}) : {});
722
+ //# sourceMappingURL=builders.d.ts.map