@rotorsoft/act 1.31.0 → 1.31.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2153,6 +2153,10 @@ function compute_backoff_delay(retry, opts) {
2153
2153
  return Math.max(0, Math.floor(delay));
2154
2154
  }
2155
2155
 
2156
+ // src/internal/reacting.ts
2157
+ import { AsyncLocalStorage } from "async_hooks";
2158
+ var reacting = new AsyncLocalStorage();
2159
+
2156
2160
  // src/internal/event-sourcing.ts
2157
2161
  var bare_patch = (_me, state2, partial) => patch(state2, partial);
2158
2162
  var validating_patch = (me, state2, partial, event) => {
@@ -2410,7 +2414,7 @@ async function load(me, target, callback, patch_fn = bare_patch) {
2410
2414
  async function action(me, action2, target, payload, options, patch_fn = bare_patch) {
2411
2415
  const { stream, expectedVersion, actor } = target;
2412
2416
  if (!stream) throw new Error("Missing target stream");
2413
- const reactingTo = options?.reactingTo;
2417
+ const reactingTo = options?.reactingTo ?? reacting.getStore();
2414
2418
  const correlator = options?.correlator ?? default_correlator;
2415
2419
  const validated = validate(action2, payload, me.actions[action2]);
2416
2420
  const opts = me.options?.[action2];
@@ -2477,11 +2481,15 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
2477
2481
  stream,
2478
2482
  emitted,
2479
2483
  meta,
2480
- // Reactions skip optimistic concurrency: they always append against the
2484
+ // Reactions skip the INFERRED guard: they always append against the
2481
2485
  // current head. Stream leasing already serializes concurrent reactions,
2482
2486
  // and forcing version checks here would turn ordinary catch-up into
2483
- // spurious retries.
2484
- reactingTo ? void 0 : expected
2487
+ // spurious retries. An expectedVersion the caller passed explicitly is
2488
+ // still honored — dropping a guard the caller asked for is silent data
2489
+ // loss, and the reaction context can reach further than the handler
2490
+ // (detached timers, settle cycles) where the caller never intended a
2491
+ // reaction's semantics at all.
2492
+ reactingTo ? expectedVersion : expected
2485
2493
  );
2486
2494
  } catch (error) {
2487
2495
  if (error instanceof ConcurrencyError) {
@@ -3107,12 +3115,11 @@ function build_handle(deps) {
3107
3115
  for (let i = 0; i < payloads.length; i++) {
3108
3116
  const payload = payloads[i];
3109
3117
  const { event, handler } = payload;
3110
- scoped_app.do = (action2, target, action_payload, options) => bound_do(action2, target, action_payload, {
3111
- ...options,
3112
- reactingTo: options?.reactingTo ?? event
3113
- });
3114
3118
  try {
3115
- await handler(event, stream, scoped_app);
3119
+ await reacting.run(
3120
+ event,
3121
+ () => handler(event, stream, scoped_app)
3122
+ );
3116
3123
  if (last_index_of.get(event.id) === i) {
3117
3124
  at = event.id;
3118
3125
  handled++;
@@ -3583,15 +3590,15 @@ var Act = class {
3583
3590
  * One DrainController per active lane. The implicit "default" lane is
3584
3591
  * always present unless onlyLanes excludes it. Each controller filters
3585
3592
  * its claim() by its lane name; the legacy single-controller path is the
3586
- * active set === { "default" } case with `lane: undefined` deps so
3587
- * claim() doesn't filter (preserves the single-lane SQL planner shape
3588
- * for apps that never call withLane).
3593
+ * no-lane-declared case with `lane: undefined` deps so claim() doesn't
3594
+ * filter (preserves the single-lane SQL planner shape for apps that never
3595
+ * call withLane).
3589
3596
  */
3590
3597
  _build_drain_controllers(options, lanes) {
3591
3598
  const all_lanes = ["default", ...lanes.map((l) => l.name)];
3592
3599
  const only_set = options.onlyLanes && options.onlyLanes.length > 0 ? new Set(options.onlyLanes) : void 0;
3593
3600
  const active_lanes = only_set ? all_lanes.filter((n) => only_set.has(n)) : all_lanes;
3594
- const single_default_lane = active_lanes.length === 1 && active_lanes[0] === "default";
3601
+ const single_default_lane = lanes.length === 0;
3595
3602
  const controllers = /* @__PURE__ */ new Map();
3596
3603
  for (const name of active_lanes) {
3597
3604
  const cfg = lanes.find((l) => l.name === name);