@rotorsoft/act 1.33.1 → 1.33.3

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
@@ -40,7 +40,7 @@ import {
40
40
  sleep,
41
41
  store,
42
42
  validate
43
- } from "./chunk-NYH6FNTM.js";
43
+ } from "./chunk-NNTF627K.js";
44
44
  import {
45
45
  ActorSchema,
46
46
  CausationEventSchema,
@@ -70,7 +70,7 @@ import {
70
70
  pii_fields,
71
71
  pii_schemas,
72
72
  sensitive
73
- } from "./chunk-TJ4JRRUR.js";
73
+ } from "./chunk-KHFMCJDR.js";
74
74
 
75
75
  // src/signals.ts
76
76
  process.once("SIGINT", async (arg) => {
@@ -2785,10 +2785,9 @@ function build_handle_batch(logger) {
2785
2785
  // src/internal/settle.ts
2786
2786
  var SettleLoop = class {
2787
2787
  _timer = void 0;
2788
- _running = false;
2789
2788
  /**
2790
- * Resolves when the cycle in flight finishes, so shutdown can wait for
2791
- * it (`stop()` cancels scheduling only). Never rejects.
2789
+ * The cycle in flight, set while one runs. Shutdown waits for it
2790
+ * (`stop()` cancels scheduling only). Never rejects.
2792
2791
  */
2793
2792
  _inflight;
2794
2793
  /** A wake-up arrived while a cycle ran: run one more pass after it. */
@@ -2820,7 +2819,7 @@ var SettleLoop = class {
2820
2819
  schedule(options) {
2821
2820
  if (this._stopped) return;
2822
2821
  if (options) this._next = options;
2823
- if (this._running) {
2822
+ if (this._inflight) {
2824
2823
  this._pending = true;
2825
2824
  return;
2826
2825
  }
@@ -2840,15 +2839,10 @@ var SettleLoop = class {
2840
2839
  ...drain_options
2841
2840
  } = this._next ?? {};
2842
2841
  this._next = void 0;
2843
- this._running = true;
2844
- let settle_done;
2845
- this._inflight = new Promise((done) => {
2846
- settle_done = done;
2847
- });
2848
- (async () => {
2842
+ this._inflight = (async () => {
2849
2843
  await this._deps.init();
2850
2844
  let settled_drain;
2851
- for (let i = 0; i < maxPasses; i++) {
2845
+ for (let i = 0; i < maxPasses && !this._stopped; i++) {
2852
2846
  const after_before = this._deps.checkpoint();
2853
2847
  const { subscribed, last_id, scanned } = await this._deps.correlate({
2854
2848
  ...correlate_query,
@@ -2869,9 +2863,7 @@ var SettleLoop = class {
2869
2863
  })().catch((err) => {
2870
2864
  this._deps.breaker.failed(Date.now(), err);
2871
2865
  }).finally(() => {
2872
- this._running = false;
2873
2866
  this._inflight = void 0;
2874
- settle_done();
2875
2867
  if (this._pending) {
2876
2868
  this._pending = false;
2877
2869
  this.schedule(this._next);
@@ -3178,7 +3170,9 @@ var Act = class {
3178
3170
  { ...q, limit: q.limit ?? this._poll_limit ?? 100 },
3179
3171
  true
3180
3172
  ),
3181
- drain: (o) => this.drain(o),
3173
+ // `settle()` validated the options, and `_schedule` skips writer-only
3174
+ // instances, so the loop drains through the internal path.
3175
+ drain: (o) => this._scoped(() => this._drain_all(o)),
3182
3176
  on_settled: (drain) => this.emit("settled", drain),
3183
3177
  breaker: this._breaker
3184
3178
  },
@@ -3256,12 +3250,7 @@ var Act = class {
3256
3250
  return await s.notify((notification) => {
3257
3251
  try {
3258
3252
  this.emit("notified", notification);
3259
- if (this._drain) {
3260
- const armed = this._arm_for_event_names(
3261
- notification.events.map((e) => e.name)
3262
- );
3263
- this._settle_armed(armed);
3264
- }
3253
+ this._wake(notification.events.map((e) => e.name));
3265
3254
  } catch (err) {
3266
3255
  this._logger.error(err, "notified handler threw");
3267
3256
  }
@@ -3319,10 +3308,7 @@ var Act = class {
3319
3308
  payload,
3320
3309
  do_options
3321
3310
  );
3322
- const armed = this._arm_for_event_names(
3323
- snapshots.map((s) => s.event.name)
3324
- );
3325
- this._settle_armed(armed);
3311
+ this._wake(snapshots.map((s) => s.event.name));
3326
3312
  this.emit("committed", snapshots);
3327
3313
  return snapshots;
3328
3314
  });
@@ -3469,17 +3455,21 @@ var Act = class {
3469
3455
  return { fetched: [], leased: [], acked: [], blocked: [] };
3470
3456
  return this._scoped(() => this._drain_all(options));
3471
3457
  }
3458
+ /** Arms correlate and every lane; false on an app with no reactions. */
3472
3459
  _arm_all() {
3473
- if (!this._reactive) return;
3460
+ if (!this._reactive) return false;
3474
3461
  this._correlate.arm();
3475
3462
  for (const c of this._drain_controllers.values()) c.arm();
3463
+ return true;
3476
3464
  }
3477
3465
  /**
3478
- * Settles what a change armed (a commit, a reset, an unblock, a restore,
3479
- * a notification from another process), unless the app walks the
3480
- * pipeline itself (`autoSettle: false`).
3466
+ * Wakes what a change freed (a commit, a reset, an unblock, a restore,
3467
+ * a notification from another process): arms the lanes these events
3468
+ * react on, or every lane without names, and settles them unless the app
3469
+ * walks the pipeline itself (`autoSettle: false`).
3481
3470
  */
3482
- _settle_armed(armed) {
3471
+ _wake(names) {
3472
+ const armed = names ? this._arm_for_event_names(names) : this._arm_all();
3483
3473
  if (armed && this._auto_settle) this._schedule();
3484
3474
  }
3485
3475
  /** Schedules a settle pass; writer-only instances (`drain: false`) skip it. */
@@ -3559,7 +3549,7 @@ var Act = class {
3559
3549
  /**
3560
3550
  * Starts a background poll that settles on a timer: each tick looks for
3561
3551
  * new events, subscribes the streams their reactions target, and drains
3562
- * them.
3552
+ * them, along with any stream whose lease a dead worker let expire.
3563
3553
  *
3564
3554
  * Use it when this process can miss commits — another process writes to a
3565
3555
  * store without `notify`, or a notification is lost. It is the safety net
@@ -3588,7 +3578,7 @@ var Act = class {
3588
3578
  if (this._poll) return false;
3589
3579
  this._poll_limit = query.limit || void 0;
3590
3580
  this._poll = setInterval(() => {
3591
- this._correlate.arm();
3581
+ this._arm_all();
3592
3582
  this._schedule();
3593
3583
  }, frequency);
3594
3584
  return true;
@@ -3647,8 +3637,7 @@ var Act = class {
3647
3637
  for (const handler of this._batch_handlers.values())
3648
3638
  handler[FOLD_RESET]?.();
3649
3639
  if (count > 0) {
3650
- this._arm_all();
3651
- this._settle_armed(this._reactive);
3640
+ this._wake();
3652
3641
  }
3653
3642
  return count;
3654
3643
  });
@@ -3682,8 +3671,7 @@ var Act = class {
3682
3671
  return this._scoped(async () => {
3683
3672
  const count = await store().unblock(input);
3684
3673
  if (count > 0) {
3685
- this._arm_all();
3686
- this._settle_armed(this._reactive);
3674
+ this._wake();
3687
3675
  }
3688
3676
  return count;
3689
3677
  });
@@ -3745,8 +3733,7 @@ var Act = class {
3745
3733
  });
3746
3734
  if (!sink) {
3747
3735
  this._correlate.restart();
3748
- this._arm_all();
3749
- this._settle_armed(this._reactive);
3736
+ this._wake();
3750
3737
  }
3751
3738
  return { kept, migrated, dropped, duration_ms: Date.now() - started };
3752
3739
  });
@@ -3892,7 +3879,8 @@ var Act = class {
3892
3879
  * `debounceMs` (default 10), `correlate.limit` (default 100, or the
3893
3880
  * poll's limit while `start_correlations` runs; `after` is always the
3894
3881
  * checkpoint),
3895
- * `maxPasses` (default `Infinity` — kill-switch for runaway loops),
3882
+ * `maxPasses` (default `Infinity`; caps this settle's passes, not the
3883
+ * passes a reaction chain's own commits schedule),
3896
3884
  * `streamLimit` (default 10), `eventLimit` (default 10),
3897
3885
  * `leaseMillis` (default 10000).
3898
3886
  *