@rotorsoft/act 1.32.30 → 1.33.1

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 (123) hide show
  1. package/README.md +0 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/act.d.ts +96 -385
  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 +1 -59
  27. package/dist/@types/internal/autoclose-policy.d.ts.map +1 -1
  28. package/dist/@types/internal/autoclose-reaction.d.ts +1 -2
  29. package/dist/@types/internal/autoclose-reaction.d.ts.map +1 -1
  30. package/dist/@types/internal/autoclose-window.d.ts +1 -1
  31. package/dist/@types/internal/autoclose-window.d.ts.map +1 -1
  32. package/dist/@types/internal/circuit-breaker.d.ts +1 -2
  33. package/dist/@types/internal/circuit-breaker.d.ts.map +1 -1
  34. package/dist/@types/{builders/build-classify.d.ts → internal/classify.d.ts} +2 -15
  35. package/dist/@types/internal/classify.d.ts.map +1 -0
  36. package/dist/@types/internal/close-cycle.d.ts.map +1 -1
  37. package/dist/@types/internal/config.d.ts +2 -42
  38. package/dist/@types/internal/config.d.ts.map +1 -1
  39. package/dist/@types/internal/correlate-cycle.d.ts +1 -1
  40. package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
  41. package/dist/@types/internal/{correlator.d.ts → correlation-id.d.ts} +1 -1
  42. package/dist/@types/internal/correlation-id.d.ts.map +1 -0
  43. package/dist/@types/internal/defer-config.d.ts.map +1 -1
  44. package/dist/@types/internal/drain-cycle.d.ts +1 -1
  45. package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
  46. package/dist/@types/internal/{drain.d.ts → drain-ops.d.ts} +1 -1
  47. package/dist/@types/internal/drain-ops.d.ts.map +1 -0
  48. package/dist/@types/internal/event-sourcing.d.ts +1 -8
  49. package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
  50. package/dist/@types/internal/{sensitive.d.ts → pii.d.ts} +4 -71
  51. package/dist/@types/internal/pii.d.ts.map +1 -0
  52. package/dist/@types/internal/projection-fold.d.ts +1 -2
  53. package/dist/@types/internal/projection-fold.d.ts.map +1 -1
  54. package/dist/@types/internal/{close-signal.d.ts → reaction-dispatch.d.ts} +47 -2
  55. package/dist/@types/internal/reaction-dispatch.d.ts.map +1 -0
  56. package/dist/@types/internal/report-once.d.ts +1 -1
  57. package/dist/@types/internal/settle.d.ts +21 -12
  58. package/dist/@types/internal/settle.d.ts.map +1 -1
  59. package/dist/@types/internal/tracing.d.ts +3 -3
  60. package/dist/@types/internal/tracing.d.ts.map +1 -1
  61. package/dist/@types/ports.d.ts +3 -43
  62. package/dist/@types/ports.d.ts.map +1 -1
  63. package/dist/@types/scoped.d.ts +1 -6
  64. package/dist/@types/scoped.d.ts.map +1 -1
  65. package/dist/@types/sensitive.d.ts +56 -0
  66. package/dist/@types/sensitive.d.ts.map +1 -0
  67. package/dist/@types/test/index.d.ts +1 -0
  68. package/dist/@types/test/index.d.ts.map +1 -1
  69. package/dist/@types/test/sandbox.d.ts +2 -26
  70. package/dist/@types/test/sandbox.d.ts.map +1 -1
  71. package/dist/@types/types/act.d.ts +214 -0
  72. package/dist/@types/types/act.d.ts.map +1 -0
  73. package/dist/@types/types/action.d.ts +69 -2
  74. package/dist/@types/types/action.d.ts.map +1 -1
  75. package/dist/@types/types/builders.d.ts +721 -0
  76. package/dist/@types/types/builders.d.ts.map +1 -0
  77. package/dist/@types/types/errors.d.ts +31 -1
  78. package/dist/@types/types/errors.d.ts.map +1 -1
  79. package/dist/@types/types/index.d.ts +38 -0
  80. package/dist/@types/types/index.d.ts.map +1 -1
  81. package/dist/@types/types/ports.d.ts +20 -18
  82. package/dist/@types/types/ports.d.ts.map +1 -1
  83. package/dist/@types/types/reaction.d.ts +11 -1
  84. package/dist/@types/types/reaction.d.ts.map +1 -1
  85. package/dist/@types/types/sandbox.d.ts +31 -0
  86. package/dist/@types/types/sandbox.d.ts.map +1 -0
  87. package/dist/@types/types/schemas.d.ts +12 -17
  88. package/dist/@types/types/schemas.d.ts.map +1 -1
  89. package/dist/chunk-NNWP65YM.cjs +264 -0
  90. package/dist/chunk-NNWP65YM.cjs.map +1 -0
  91. package/dist/{chunk-X3MMQIIP.js → chunk-NYH6FNTM.js} +174 -22
  92. package/dist/chunk-NYH6FNTM.js.map +1 -0
  93. package/dist/{chunk-HHKLVRR4.js → chunk-TJ4JRRUR.js} +38 -57
  94. package/dist/chunk-TJ4JRRUR.js.map +1 -0
  95. package/dist/chunk-YKL44PYX.cjs +1233 -0
  96. package/dist/chunk-YKL44PYX.cjs.map +1 -0
  97. package/dist/index.cjs +1954 -3440
  98. package/dist/index.cjs.map +1 -1
  99. package/dist/index.js +911 -1063
  100. package/dist/index.js.map +1 -1
  101. package/dist/test/index.cjs +16 -1113
  102. package/dist/test/index.cjs.map +1 -1
  103. package/dist/test/index.js +2 -2
  104. package/dist/test/index.js.map +1 -1
  105. package/dist/types/index.cjs +58 -276
  106. package/dist/types/index.cjs.map +1 -1
  107. package/dist/types/index.js +15 -1
  108. package/package.json +2 -2
  109. package/dist/@types/builders/build-classify.d.ts.map +0 -1
  110. package/dist/@types/builders/builder-base.d.ts +0 -70
  111. package/dist/@types/builders/builder-base.d.ts.map +0 -1
  112. package/dist/@types/builders/reaction-builder.d.ts +0 -55
  113. package/dist/@types/builders/reaction-builder.d.ts.map +0 -1
  114. package/dist/@types/internal/close-signal.d.ts.map +0 -1
  115. package/dist/@types/internal/correlator.d.ts.map +0 -1
  116. package/dist/@types/internal/defer-signal.d.ts +0 -32
  117. package/dist/@types/internal/defer-signal.d.ts.map +0 -1
  118. package/dist/@types/internal/drain.d.ts.map +0 -1
  119. package/dist/@types/internal/index.d.ts +0 -38
  120. package/dist/@types/internal/index.d.ts.map +0 -1
  121. package/dist/@types/internal/sensitive.d.ts.map +0 -1
  122. package/dist/chunk-HHKLVRR4.js.map +0 -1
  123. package/dist/chunk-X3MMQIIP.js.map +0 -1
@@ -1,225 +1,18 @@
1
- import { type CircuitBreakerOptions, type CircuitState, type PatchFn } from "./internal/index.js";
2
- export type { CircuitBreakerOptions, CircuitState } from "./internal/index.js";
3
- import { type Scoped } from "./ports.js";
4
- import type { Actor, AsOf, AuditCategory, AuditFinding, AuditOptions, BatchHandler, BlockedLease, CloseResult, CloseTarget, Committed, Correlator, DoOptions, Drain, DrainOptions, EventSink, EventSource, IAct, LaneConfig, Lease, LoadTarget, Query, Registry, ScanOptions, ScanResult, Schema, SchemaRegister, Schemas, SettleOptions, ShutdownOptions, Snapshot, State, StoreNotification, StreamFilter, StreamPosition, Target } from "./types/index.js";
5
- export { type AutocloseConfig, type AutoclosePolicy, DEFAULT_AUTOCLOSE_CYCLE_MINUTES, DEFAULT_CLOSE_BATCH_SIZE, DEFAULT_CLOSE_YIELD_MS, DEFAULT_MAX_SUBSCRIBED_STREAMS, DEFAULT_SETTLE_DEBOUNCE_MS, resolveAutocloseConfig, } from "./internal/index.js";
6
- /**
7
- * Lifecycle events emitted by {@link Act}, mapped to their payload type.
8
- * Drives the typing of `emit` / `on` / `off` — the event-name argument
9
- * narrows its payload at the call site.
10
- *
11
- * The first parameter is kept (unused) for arity compatibility: `committed`
12
- * carries snapshots of whichever state each action targeted, so its honest
13
- * element type is `Snapshot<Schema, TEvents>` — the register map itself was
14
- * never the shape of any snapshot's state.
15
- */
16
- export type ActLifecycleEvents<_TSchemaReg extends SchemaRegister<TActions>, TEvents extends Schemas, TActions extends Schemas> = {
17
- committed: Snapshot<Schema, TEvents>[];
18
- acked: Lease[];
19
- blocked: BlockedLease[];
20
- settled: Drain<TEvents>;
21
- closed: CloseResult;
22
- /**
23
- * A **different process** committed an event to the same backing store.
24
- *
25
- * Fires only when the configured store implements
26
- * {@link Store.notify} and there is at least one registered reaction.
27
- * The orchestrator uses the same signal internally to wake `settle()`
28
- * — listeners get the raw payload for SSE fan-out, dashboards, and
29
- * audit logs.
30
- *
31
- * Local commits do *not* fire `notified` (use `committed` for those):
32
- * stores self-filter their own writes so this channel has a clean
33
- * cross-process semantic.
34
- */
35
- notified: StoreNotification;
36
- /**
37
- * A stream's sensitive-data payload was wiped via {@link Act.forget}.
38
- * Fires exactly once per successful `forget(stream)` call — idempotent
39
- * second calls (no PII left on the stream) return `eventCount: 0` and
40
- * do NOT re-emit. Apps that never call `forget()` never see this event.
41
- *
42
- * Listeners use it for the compliance side of GDPR / CCPA: audit log,
43
- * downstream cache busts, projection-side wipes that the framework
44
- * doesn't reach (e.g., search indexes, ETL caches). The framework's own
45
- * cache is invalidated by `forget()` itself before the event fires.
46
- */
47
- forgotten: {
48
- stream: string;
49
- at: Date;
50
- eventCount: number;
51
- };
52
- /**
53
- * A store operation failed during the drain loop. Fires on
54
- * every failed drain cycle — typically a {@link StoreError} from a
55
- * degraded backend — carrying the orchestrator circuit breaker's state
56
- * after the failure (`open` means the drain loop has backed off and will
57
- * retry after the cooldown). Listen to alert on a degraded store; the
58
- * framework logs the same error regardless. Emitted only when a listener
59
- * is registered (Node's `EventEmitter` throws on an unhandled `"error"`).
60
- */
61
- error: {
62
- error: unknown;
63
- circuit: CircuitState;
64
- };
65
- };
66
- /**
67
- * Options for {@link Act} construction (passed via {@link ActBuilder.build}).
68
- *
69
- * @property maxSubscribedStreams - Cap for the LRU tracking what each
70
- * dynamically resolved reaction target was last subscribed at. Statically
71
- * declared targets are held outside it and never evicted, so their lane
72
- * and priority stay owned by the build-time subscribe.
73
- * Default: {@link DEFAULT_MAX_SUBSCRIBED_STREAMS}.
74
- * @property settleDebounceMs - Debounce window (ms) used by `settle()` when
75
- * the caller doesn't pass `SettleOptions.debounceMs`. Tune this once per
76
- * Act instance instead of threading the value through every call site.
77
- * Default: {@link DEFAULT_SETTLE_DEBOUNCE_MS}.
78
- */
79
- export type ActOptions<TLanes extends string = string> = {
80
- readonly maxSubscribedStreams?: number;
81
- readonly settleDebounceMs?: number;
82
- /**
83
- * Per-Act ports. When set, this Act runs against the
84
- * provided store + cache instead of the singletons — threaded via
85
- * AsyncLocalStorage so internals are unchanged. Both are required
86
- * together (a shared cache across distinct stores would collide on
87
- * stream keys). Omit for the singleton path.
88
- */
89
- readonly scoped?: Scoped;
90
- /**
91
- * Correlation-id generator for originating actions. When
92
- * omitted, Act uses {@link default_correlator}, which produces a
93
- * readable, time-monotonic-within-window, lowercase id of the form
94
- * `{state[:4]}-{action[:4]}-{ts}{rnd}` (18 chars).
95
- *
96
- * Reactions inherit `reactingTo.meta.correlation` so the chain stays
97
- * intact — the delegate is only consulted on originating commits and
98
- * for the close-the-books transaction.
99
- */
100
- readonly correlator?: Correlator;
101
- /** Restrict this process to a subset of declared lanes. */
102
- readonly onlyLanes?: ReadonlyArray<TLanes>;
103
- /**
104
- * Subscribe to {@link Store.notify} on this instance. Defaults
105
- * to `true`. Set `false` on instances that only commit and never
106
- * react — the subscriber-connection budget is the practical scaling
107
- * ceiling for the notify/listen pattern, and writer-only fleets
108
- * spend it for nothing when they subscribe to a channel they never
109
- * read. Commits still emit notifications (that's part of the
110
- * store's commit protocol); only the subscriber side is gated.
111
- */
112
- readonly listen?: boolean;
113
- /**
114
- * Run the local reaction pipeline on this instance. Defaults
115
- * to `true`. Set `false` on writer-only or sidecar instances: drain
116
- * controllers' auto-cycle workers don't start, `correlate()` /
117
- * `drain()` / `settle()` become no-ops, and the notify handler
118
- * skips its drain-wakeup arm (but still emits the `notified`
119
- * lifecycle event so observability sidecars work).
120
- */
121
- readonly drain?: boolean;
122
- /**
123
- * Orchestrator circuit breaker for the drain loop. After
124
- * `failureThreshold` consecutive store failures the breaker opens and
125
- * the drain loop skips `claim()` for `cooldownMs` instead of hammering a
126
- * down backend, then allows a half-open trial. Out-of-range values throw
127
- * a `ZodError` at `act().build()`. Defaults: threshold 5, cooldown 30s.
128
- */
129
- readonly circuitBreaker?: CircuitBreakerOptions;
130
- /**
131
- * @deprecated Accepted, validated, and ignored. It paced the off-hours
132
- * re-check of the old autoclose sweep; the synthesized autoclose reaction now derives its re-check
133
- * directly from `autocloseWindow` — a tick landing outside the window
134
- * parks until the exact instant the window opens, so there is no
135
- * polling cadence to configure (and nothing minute-denominated on the
136
- * close surface). Still validated as an integer `[1, 1440]` so typos
137
- * keep failing loudly at `act().build()`. Will be removed in the next
138
- * major.
139
- */
140
- readonly autocloseCycleMinutes?: number;
141
- /**
142
- * @deprecated Unused since the autoclose sweep was replaced by a
143
- * synthesized per-aggregate reaction — nothing pages the store in
144
- * batches anymore, so nothing reads this. Accepted and validated
145
- * (`[1, 1024]`) for compatibility; will be removed in the next major.
146
- */
147
- readonly closeBatchSize?: number;
148
- /**
149
- * @deprecated Unused: the sweep that yielded between
150
- * successive `Store.truncate` calls no longer exists; closes are
151
- * staged per stream by the autoclose reaction. Accepted and validated
152
- * (`[0, 1000]`) for compatibility; will be removed in the next major.
153
- */
154
- readonly closeYieldMs?: number;
155
- /**
156
- * @deprecated Unused: the sweep-side predicate try/catch
157
- * this flag steered no longer exists; a throwing policy predicate now
158
- * follows the reaction retry path (`blockOnError: false`, three
159
- * retries). Accepted for compatibility; will be removed in the next
160
- * major.
161
- */
162
- readonly closeOnError?: boolean;
163
- /**
164
- * Optional off-hours window restricting when autoclose evaluates. A
165
- * synthesized autoclose reaction that triggers outside the window
166
- * defers to the next instant the window opens — derived from the
167
- * window itself, no polling cadence. Hours are `[0, 23]` integers in
168
- * `timeZone` (IANA, default `"UTC"`, DST-correct); `start > end` is
169
- * an overnight window (e.g. `{ start: 22, end: 6 }`). Omit to
170
- * evaluate regardless of clock time.
171
- */
172
- readonly autocloseWindow?: {
173
- readonly start: number;
174
- readonly end: number;
175
- readonly timeZone?: string;
176
- };
177
- /**
178
- * Validate folded state against its declared Zod schema after every
179
- * reduction. Off by default.
180
- *
181
- * When `true`, each time an event is folded into state — on the
182
- * command path (`do`), on `load`/replay, and inside projection-fold
183
- * projections — the merged full state is parsed against the owning
184
- * state's `state({ Name: schema })` schema. A reducer that produces
185
- * schema-violating state (the calculator divide-by-zero NaN
186
- * class) throws a {@link ValidationError} at the triggering event,
187
- * whose `target` names the state and the event (`<state>.<event>#<id>`)
188
- * — instead of the bad value propagating and surfacing hops later as a
189
- * confusing downstream error.
190
- *
191
- * This is a **debugging / CI aid, not a production guard**. Turn it on
192
- * in development and CI to catch total-reducer bugs at the source; the
193
- * framework already validates action inputs and emitted events, so the
194
- * reduced state is the one shape it otherwise trusts. The per-event
195
- * patch step is selected **once at `build()`** (the same way the
196
- * orchestrator picks bare vs trace-decorated store ops from the log
197
- * level): when `false` (the default) the fold loop is byte-identical to
198
- * a bare reduction — the validating patch step is never selected, so
199
- * there is no per-event cost, not even a branch.
200
- */
201
- readonly validateFoldedState?: boolean;
202
- };
1
+ import { type ActLifecycleEvents, type ActOptions } from "./types/act.js";
2
+ import type { Actor, AsOf, AuditCategory, AuditFinding, AuditOptions, BatchHandler, CloseResult, CloseTarget, Committed, DoOptions, Drain, DrainOptions, EventSink, EventSource, IAct, LaneConfig, LoadTarget, PatchFn, Query, Registry, ScanOptions, ScanResult, Schema, SchemaRegister, Schemas, SettleOptions, ShutdownOptions, Snapshot, State, StreamFilter, StreamPosition, Target } from "./types/index.js";
203
3
  /**
204
4
  * @category Orchestrator
205
5
  * @see Store
206
6
  *
207
- * Main orchestrator for event-sourced state machines and workflows.
208
- *
209
- * It manages the lifecycle of actions, reactions, and event streams, providing APIs for loading state, executing actions, querying events, and draining reactions.
210
- *
211
- * ## Usage
7
+ * The orchestrator: executes actions, loads state, queries events, and runs
8
+ * the reactions each commit frees (on its own, unless `autoSettle: false`).
212
9
  *
213
10
  * ```typescript
214
11
  * const app = act().withState(Counter).build();
215
12
  * await app.do("increment", { stream: "counter1", actor }, { by: 1 });
216
13
  * const snapshot = await app.load(Counter, "counter1");
217
- * await app.drain();
218
14
  * ```
219
15
  *
220
- * - Register event listeners with `.on("committed", ...)` and `.on("acked", ...)` to react to lifecycle events.
221
- * - Use `.query()` to analyze event streams for analytics or debugging.
222
- *
223
16
  * @template TSchemaReg SchemaRegister for state
224
17
  * @template TEvents Schemas for events
225
18
  * @template TActions Schemas for actions
@@ -231,14 +24,15 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
231
24
  private readonly _breaker;
232
25
  private readonly _listen;
233
26
  private readonly _drain;
27
+ private readonly _auto_settle;
234
28
  /** Whether any event has a reaction; nothing arms without one. */
235
29
  private readonly _reactive;
236
30
  private readonly _drain_controllers;
237
31
  private readonly _correlate;
238
32
  private readonly _settle;
239
33
  private _poll;
240
- /** The deprecated `start_correlations` callback, while the poll runs. */
241
- private _on_discovered;
34
+ /** The poll's correlate `limit`, used by every settle pass while it runs. */
35
+ private _poll_limit;
242
36
  /**
243
37
  * Wired at construction against the store current then, so a store
244
38
  * injected after `build()` is never notified.
@@ -318,21 +112,14 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
318
112
  private _build_settle;
319
113
  private _shutdown_promise;
320
114
  /**
321
- * Per-instance teardown: stop scheduling new work, give drain cycles
322
- * already in flight a bounded chance to finish, then remove lifecycle
323
- * listeners and tear down the cross-process notify subscription.
324
- *
325
- * Scheduling stops first, then in-flight cycles get up to `graceMs` to
326
- * reach their `ack` (an abandoned handler holds its lease until it
327
- * expires). Listeners come off after the wait, so they still see work that
328
- * finished during it. The budget is a ceiling, not a delay; when it runs
329
- * out, teardown proceeds anyway.
330
- *
331
- * Idempotent — repeated calls return the same promise, and the first
332
- * call's `graceMs` is the one that applies. Registered automatically with
333
- * the global `dispose()` registry at construction, so process-wide
334
- * `dispose()()` covers it; test helpers (or operators that mint
335
- * short-lived Acts) call it explicitly for prompt cleanup.
115
+ * Per-instance teardown: stops scheduling, gives the cycles in flight up
116
+ * to `graceMs` to reach their `ack` (an abandoned handler holds its lease
117
+ * until it expires), then removes listeners and the notify subscription.
118
+ * The budget is a ceiling, not a delay.
119
+ *
120
+ * Idempotent: repeated calls return the same promise, with the first
121
+ * call's `graceMs`. Registered with the global `dispose()` registry, so
122
+ * `dispose()()` covers it; call it directly for prompt cleanup.
336
123
  *
337
124
  * @param options - See {@link ShutdownOptions}. Defaults the grace budget
338
125
  * to the largest lane `leaseMillis` (capped at 30s).
@@ -347,15 +134,10 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
347
134
  /** Failures are logged, never thrown: `notify` is a hint. */
348
135
  private _wire_notify;
349
136
  /**
350
- * Executes an action on a state instance, committing resulting events.
351
- *
352
- * This is the primary method for modifying state. It:
353
- * 1. Validates the action payload against the schema
354
- * 2. Loads the current state snapshot
355
- * 3. Checks invariants (business rules)
356
- * 4. Executes the action handler to generate events
357
- * 5. Applies events to create new state
358
- * 6. Commits events to the store with optimistic concurrency control
137
+ * Executes an action: validates the payload, loads the state, checks its
138
+ * invariants, and commits the events its handler emits, with optimistic
139
+ * concurrency on the stream. The reactions to those events settle on
140
+ * their own (unless `autoSettle: false`).
359
141
  *
360
142
  * @template TKey - Action name from registered actions
361
143
  * @param action - The name of the action to execute
@@ -429,9 +211,7 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
429
211
  *
430
212
  * For small result sets, consider using {@link query_array} instead.
431
213
  *
432
- * @param query - Filter criteria — see {@link Query} for available fields
433
- * (`stream`, `name`, `after`, `before`, `created_after`, `created_before`,
434
- * `limit`, `with_snaps`, `stream_exact`)
214
+ * @param query - Filter criteria; see {@link Query} for the fields
435
215
  * @param callback - Optional callback invoked for each matching event
436
216
  * @returns Object with first event, last event, and total count
437
217
  *
@@ -486,31 +266,23 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
486
266
  eventCount: number;
487
267
  }>;
488
268
  /**
489
- * Processes pending reactions by draining uncommitted events from the event store.
490
- *
491
- * Runs a single drain cycle:
492
- * 1. Polls the store for streams with uncommitted events
493
- * 2. Leases streams to prevent concurrent processing
494
- * 3. Fetches events for each leased stream
495
- * 4. Executes matching reaction handlers
496
- * 5. Acknowledges successful reactions or blocks failing ones
269
+ * Runs one drain cycle: claims streams with marked work (leasing them),
270
+ * fetches their events, runs the matching reactions, and acks or blocks
271
+ * each stream.
497
272
  *
498
- * Drain uses a dual-frontier strategy to balance processing of new streams (lagging)
499
- * vs active streams (leading). The ratio adapts based on event pressure.
500
- *
501
- * Call `correlate()` before `drain()`. It is not only how dynamic targets
502
- * are discovered: a stream is claimable while `at < correlated_at`, and
503
- * `correlate` is the only component that raises that mark, so a
504
- * commit no correlate has seen is not drainable, static targets included. For a
505
- * higher-level API that handles debouncing, correlation, and signaling
506
- * automatically, use {@link settle}.
273
+ * A stream is claimable only once `correlate()` has marked it, static
274
+ * targets included, so a commit no correlate has seen is not drainable.
275
+ * Apps rarely call this: each commit settles its reactions on its own.
276
+ * With `autoSettle: false`, tests walk the pipeline with `correlate()`
277
+ * then `drain()` (see the testing guide).
507
278
  *
508
279
  * @param options - Drain configuration — see {@link DrainOptions} for fields
509
280
  * (`streamLimit`, `eventLimit`, `leaseMillis`).
510
281
  * @returns Drain statistics with fetched, leased, acked, and blocked counts
511
282
  *
512
- * @example In tests and scripts
283
+ * @example Walking the pipeline in a test
513
284
  * ```typescript
285
+ * const app = act().withState(User).withProjection(Users).build({ autoSettle: false });
514
286
  * await app.do("createUser", target, payload);
515
287
  * await app.correlate();
516
288
  * await app.drain();
@@ -518,41 +290,36 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
518
290
  *
519
291
  * @see {@link settle} for debounced correlate→drain with lifecycle events
520
292
  * @see {@link correlate} for dynamic stream discovery
521
- * @see {@link start_correlations} for automatic correlation
522
293
  */
523
294
  drain(options?: DrainOptions): Promise<Drain<TEvents>>;
524
295
  private _arm_all;
296
+ /**
297
+ * Settles what a change armed (a commit, a reset, an unblock, a restore,
298
+ * a notification from another process), unless the app walks the
299
+ * pipeline itself (`autoSettle: false`).
300
+ */
301
+ private _settle_armed;
302
+ /** Schedules a settle pass; writer-only instances (`drain: false`) skip it. */
303
+ private _schedule;
525
304
  /** Arms only the lanes these events react on; true when any was armed. */
526
305
  private _arm_for_event_names;
527
306
  /** In parallel, so a slow lane never holds up a fast one. */
528
307
  private _drain_all;
529
308
  /**
530
- * Discovers and registers new streams dynamically based on reaction resolvers.
531
- *
532
- * Correlation enables "dynamic reactions" where target streams are determined at runtime
533
- * based on event content. For example, you might create a stats stream for each user
534
- * when they perform certain actions.
535
- *
536
- * This method scans events matching the query and identifies new target streams based
537
- * on reaction resolvers. It then registers these streams so they'll be picked up by
538
- * the next drain cycle.
309
+ * Scans committed events from the correlation checkpoint, subscribes the
310
+ * target streams their reactions resolve to (static or computed from
311
+ * the event), and marks their work so the next drain can claim it.
539
312
  *
540
313
  * @param query - Scan filter — see {@link Query} for fields (typically
541
- * `{ after: <event-id>, limit: <count> }`)
314
+ * `{ limit: <count> }`; the scan starts at the checkpoint)
542
315
  * @returns The number of newly subscribed streams and the last scanned event id
543
316
  *
544
317
  * @example Manual correlation
545
318
  * ```typescript
546
- * // Scan for new streams
547
- * const { subscribed, last_id } = await app.correlate({ after: 0, limit: 100 });
548
- * console.log(`Subscribed ${subscribed} new streams`);
549
- *
550
- * // Save last_id for next scan
551
- * await saveCheckpoint(last_id);
319
+ * const { subscribed } = await app.correlate({ limit: 100 });
552
320
  * ```
553
321
  *
554
322
  * @see {@link start_correlations} for automatic periodic correlation
555
- * @see {@link stop_correlations} to stop automatic correlation
556
323
  */
557
324
  correlate(query?: Query): Promise<{
558
325
  subscribed: number;
@@ -576,9 +343,6 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
576
343
  * reads (default 100). The scan always starts at the correlation
577
344
  * checkpoint, so `after` is ignored.
578
345
  * @param frequency - Milliseconds between ticks (default: 10000)
579
- * @param callback - Called with the number of newly subscribed streams
580
- * after each correlate pass that found some.
581
- * @deprecated Listen to the `"settled"` lifecycle event instead.
582
346
  * @returns `true` if the poll started, `false` if one is already running
583
347
  *
584
348
  * @example
@@ -591,7 +355,7 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
591
355
  * @see {@link settle} for what each tick runs
592
356
  * @see {@link stop_correlations} to stop the poll
593
357
  */
594
- start_correlations(query?: Query, frequency?: number, callback?: (subscribed: number) => void): boolean;
358
+ start_correlations(query?: Query, frequency?: number): boolean;
595
359
  /**
596
360
  * Stops the poll started by {@link start_correlations}. Idempotent, and
597
361
  * called by `shutdown()`.
@@ -600,25 +364,24 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
600
364
  */
601
365
  stop_correlations(): void;
602
366
  /**
603
- * Cancels any pending or active settle cycle.
367
+ * Drops the settle pass scheduled but not yet started. A cycle already
368
+ * running finishes; later changes schedule new passes.
604
369
  *
605
370
  * @see {@link settle}
606
371
  */
607
372
  stop_settling(): void;
608
373
  /**
609
- * Reset reaction stream watermarks and request a drain on the next
610
- * `drain()` / `settle()` cycle.
374
+ * Reset reaction stream watermarks and settle them, replaying their
375
+ * events through their reactions.
611
376
  *
612
377
  * Use this to replay events through projections (or other reaction targets)
613
- * after changing handler logic. Equivalent to calling `store().reset(streams)`
614
- * directly, but also raises the orchestrator's internal "needs drain" flag —
615
- * `store().reset(...)` alone leaves the flag untouched, so a settled app
616
- * would short-circuit and skip the replay.
617
- *
618
- * Pair with `app.settle()` (or a single `app.drain()` for small streams).
619
- * `settle()` loops correlate→drain until no progress is made, so one call
620
- * fully catches up paginated streams without forcing callers to roll
621
- * their own loop.
378
+ * after changing handler logic. It rewinds the same watermarks as
379
+ * `store().reset(streams)`, and also wakes the drain and schedules a
380
+ * settle, which loops correlate→drain until the replay catches up;
381
+ * `store().reset(...)` alone leaves a settled app idle. Call
382
+ * `app.settle(options)` right after to give that pass batch options, or,
383
+ * with `autoSettle: false`, `correlate()` / `drain()` to drive the replay
384
+ * by hand.
622
385
  *
623
386
  * @param input - Reaction target streams (e.g., projection names) to reset, or a {@link StreamFilter} for bulk operations
624
387
  * @returns Count of streams that were actually reset
@@ -626,7 +389,7 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
626
389
  * @example Rebuild a projection (production)
627
390
  * ```typescript
628
391
  * await app.reset(["my-projection"]);
629
- * app.settle({ eventLimit: 1000 }); // emits "settled" when fully replayed
392
+ * app.settle({ eventLimit: 1000 }); // optional: bigger batches; emits "settled"
630
393
  * ```
631
394
  *
632
395
  * @see {@link Store.reset} for the underlying store primitive
@@ -642,11 +405,9 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
642
405
  * {@link reset}, which rebuilds from event 0 (suitable for projection
643
406
  * rebuilds, wrong for "I fixed the bug, please retry").
644
407
  *
645
- * Wraps `store().unblock(streams)` and raises the orchestrator's
646
- * internal "needs drain" flag so a settled app picks up the now-free
647
- * streams on the next cycle. Equivalent to calling `store().unblock(...)`
648
- * directly, but `store().unblock(...)` alone leaves the flag
649
- * untouched.
408
+ * Wraps `store().unblock(streams)` and settles the now-free streams on
409
+ * its own. `store().unblock(...)` alone leaves them waiting for the next
410
+ * commit.
650
411
  *
651
412
  * @param input - Stream names to unblock, or a {@link StreamFilter} for bulk recovery
652
413
  * @returns Count of streams that were actually flipped (were blocked)
@@ -723,64 +484,30 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
723
484
  limit?: number;
724
485
  }): Promise<StreamPosition[]>;
725
486
  /**
726
- * Operator-driven store audit.
727
- *
728
- * Walks the connected store and yields per-category findings —
729
- * each tagged with the remediation it suggests. Same operator-
730
- * driven category as `app.close()` / `app.reset()` /
731
- * `app.unblock()` / `app.blocked_streams()`: never auto-invoked by
732
- * the framework; the operator decides when to run it (CI gate,
733
- * scheduled job, ad-hoc forensics) and what to do with the
734
- * findings.
487
+ * Operator-driven store audit: walks the store and yields findings per
488
+ * category, each with the remediation it suggests. Never run by the
489
+ * framework. Pass a subset of categories to scope the work; the iterable
490
+ * can be broken early, and findings stream without buffering the report.
735
491
  *
736
- * Categories are independent — pass a subset to scope the work,
737
- * or omit to run everything:
492
+ * @param categories - Subset of categories to run (default: all).
493
+ * @param options - Query window + per-category thresholds.
494
+ * @returns Async iterable of {@link AuditFinding}.
738
495
  *
496
+ * @example Schema drift since the last scan
739
497
  * ```typescript
740
- * // Targeted: schema drift + deprecated-event load only
741
- * for await (const f of app.audit(["schema", "deprecated-load"], {
742
- * query: { created_after: lastScan },
743
- * thresholds: { deprecatedLoadShareMin: 0.10 },
744
- * })) {
498
+ * for await (const f of app.audit(["schema"], { query: { created_after: lastScan } }))
745
499
  * await escalate(f);
746
- * }
747
- *
748
- * // Full audit, default thresholds
749
- * for await (const f of app.audit()) console.log(f);
750
500
  * ```
751
- *
752
- * Returns an `AsyncIterable` so callers can `break` early — the
753
- * underlying store paginations respect the iterator protocol and
754
- * stop cleanly. Each finding is emitted independently, so
755
- * pipelining into Slack / persistence / further analysis works
756
- * without buffering the full report in memory.
757
- *
758
- * Findings shape — see {@link AuditFinding}. The discriminated
759
- * union carries enough context for the operator to act on each
760
- * finding directly: stream id, event id, recommendation hints.
761
- *
762
- * @param categories - Subset of categories to run (default: all).
763
- * @param options - Query window + per-category thresholds.
764
- * @returns Async iterable of {@link AuditFinding}.
765
501
  */
766
502
  audit(categories?: AuditCategory[], options?: AuditOptions): AsyncIterable<AuditFinding>;
767
503
  /**
768
- * Bulk-update scheduling priority for streams matching `filter`.
769
- *
770
- * Operator-grade override of the `claim()` lagging-frontier
771
- * ordering. Useful when a long-running replay needs to
772
- * jump ahead of other lagging streams, or when a no-longer-urgent
773
- * job should yield slots back to the rest. Build-time priorities
774
- * (set via the resolver's `priority` field) are subject to a
775
- * `max()` invariant across reactions; this API ignores that and
776
- * sets the priority outright on every matching row.
777
- *
778
- * Filter shape mirrors {@link query} / {@link Store.query_streams}:
779
- * `stream` / `source` are regex by default, exact with the
780
- * `*_exact` flags; `blocked` restricts to blocked or unblocked
781
- * rows. **An empty filter (`{}`) updates every registered stream.**
782
- *
783
- * @param filter - Selection criteria (regex by default).
504
+ * Sets the scheduling priority of every stream matching `filter`, as-is:
505
+ * unlike build-time priorities (the highest across reactions wins), this
506
+ * overrides them. Useful to let a long replay jump ahead of other lagging
507
+ * streams, or to yield slots back. **An empty filter (`{}`) updates every
508
+ * registered stream.**
509
+ *
510
+ * @param filter - Selection criteria (regex by default, like {@link query}).
784
511
  * @param priority - New priority value. Set as-is — no clamp.
785
512
  * @returns Count of streams whose priority changed.
786
513
  *
@@ -790,36 +517,17 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
790
517
  * ```
791
518
  *
792
519
  * @see {@link Store.prioritize} for the underlying primitive
793
- * @see {@link claim} for how priority biases scheduling
794
520
  */
795
521
  prioritize(filter: StreamFilter, priority: number): Promise<number>;
796
522
  /** Shared by `close()` and reaction-requested closes. */
797
523
  private _run_close;
798
524
  /**
799
- * Close the books — guard, archive, truncate, and optionally restart streams.
800
- *
801
- * Safely removes historical events from the operational store:
802
- *
803
- * 1. **Correlate** — discover pending reaction targets
804
- * 2. **Safety check** — skip streams with pending reactions (skipped when no reactive events)
805
- * 3. **Guard** — commit `__tombstone__` with `expectedVersion` to block concurrent writes
806
- * 4. **Load state** — for streams in `snapshots`, load final state while guarded (no races)
807
- * 5. **Archive** — user callback per stream (abort-all on failure, streams are guarded)
808
- * 6. **Truncate + seed** — atomic: delete all events, insert `__snapshot__` or `__tombstone__`
809
- * 7. **Cache** — invalidate (tombstoned) or warm (restarted)
810
- * 8. **Emit "closed"** — lifecycle event with results
811
- *
812
- * Targets carrying `before` take the **windowed** branch instead —
813
- * close the books on a rolling window: probe the min consumer
814
- * watermark (so the boundary never rises past a lagging reaction),
815
- * run the archive callback against the cutoff, then prune the prefix
816
- * below the closest safe `__snapshot__`. No tombstone guard (the
817
- * pre-cutoff prefix is immutable), no seed, cache untouched — the
818
- * stream stays live and keeps accepting actions. Requires the state
819
- * to snapshot via `.snap(...)`; no qualifying snapshot ⇒ the stream
820
- * lands in `skipped` (retry after the next snapshot). Windowed
821
- * entries in the result echo `before` and carry the surviving
822
- * boundary snapshot as `committed`.
525
+ * Close the books: guard, archive, truncate, and optionally restart
526
+ * streams. A stream with pending reactions is skipped; an archive
527
+ * failure aborts the whole close while the streams are still guarded.
528
+ * Targets carrying `before` prune a rolling window instead and leave the
529
+ * stream live (needs `.snap(...)`). The phases and their failure
530
+ * semantics are in the close-cycle architecture page.
823
531
  *
824
532
  * @param targets - Per-stream close options (stream, restart?, archive?, before?)
825
533
  * @returns `{ truncated: TruncateResult, skipped: string[] }`
@@ -836,25 +544,28 @@ export declare class Act<TSchemaReg extends SchemaRegister<TActions>, TEvents ex
836
544
  /**
837
545
  * Debounced, non-blocking correlate→drain cycle.
838
546
  *
839
- * Call this after `app.do()` (or `app.reset()`) to schedule a background
840
- * drain. Multiple rapid calls within the debounce window are coalesced
841
- * into a single cycle. Runs correlate→drain in a loop until a pass makes
842
- * no progress — no new subscriptions, no events read, no streams leased —
843
- * then emits
844
- * the `"settled"` lifecycle event. This means a single `settle()` call
845
- * fully catches up paginated streams (e.g. after `reset()` on a long
846
- * projection) without forcing callers to loop.
547
+ * A local change that frees reaction work (a commit, `reset()`,
548
+ * `unblock()`, `restore()`) schedules this on its own unless the app
549
+ * sets `autoSettle: false`. Call it at startup, to run what was pending
550
+ * before a restart, or with options for the next pass. A pass already
551
+ * scheduled is never put off: calls before it runs join it (a shorter
552
+ * `debounceMs` brings it forward), and a call during a running cycle
553
+ * queues one more pass after it. Runs correlate→drain in a loop until a pass makes no
554
+ * progress — no new subscriptions, no events read, no streams leased —
555
+ * then emits the `"settled"` lifecycle event, so one call fully catches
556
+ * up paginated streams without forcing callers to loop.
847
557
  *
848
558
  * @param options - Settle configuration — see {@link SettleOptions} for fields:
849
- * `debounceMs` (default 10), `correlate` (default `{ after: -1, limit: 100 }`),
559
+ * `debounceMs` (default 10), `correlate.limit` (default 100, or the
560
+ * poll's limit while `start_correlations` runs; `after` is always the
561
+ * checkpoint),
850
562
  * `maxPasses` (default `Infinity` — kill-switch for runaway loops),
851
563
  * `streamLimit` (default 10), `eventLimit` (default 10),
852
564
  * `leaseMillis` (default 10000).
853
565
  *
854
- * @example API mutations
566
+ * @example Startup
855
567
  * ```typescript
856
- * await app.do("CreateItem", target, input);
857
- * app.settle(); // non-blocking, returns immediately
568
+ * app.settle(); // run what was pending before the restart
858
569
  *
859
570
  * app.on("settled", (drain) => {
860
571
  * // notify SSE clients, invalidate caches, etc.