@rotorsoft/act 1.32.10 → 1.32.12

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
@@ -26,7 +26,7 @@ import {
26
26
  sleep,
27
27
  store,
28
28
  validate
29
- } from "./chunk-F4F5AKM3.js";
29
+ } from "./chunk-UVFP2BQ3.js";
30
30
  import {
31
31
  ActorSchema,
32
32
  CausationEventSchema,
@@ -1589,7 +1589,6 @@ var CorrelateCycle = class {
1589
1589
  _armed = true;
1590
1590
  /** In-flight init, memoized for single-flight and cleared on failure. */
1591
1591
  _init_promise;
1592
- _timer = void 0;
1593
1592
  // Dynamically discovered targets → what each was last subscribed at,
1594
1593
  // bounded by `maxSubscribedStreams`. The static half lives in
1595
1594
  // `_static_subscriptions` below, which is deliberately not evictable.
@@ -1633,14 +1632,6 @@ var CorrelateCycle = class {
1633
1632
  * guarantee without a second gate on the Act side.
1634
1633
  */
1635
1634
  _on_init_async;
1636
- /**
1637
- * Scope runner (#1191). The periodic `start_polling` timer fires
1638
- * outside any caller frame, so its `correlate()` must be re-wrapped in
1639
- * the Act's `_scoped` bag or `store()`/`cache()` resolve to the
1640
- * singleton for a scoped Act. The orchestrator always threads its
1641
- * `_scoped` (identity for a non-scoped Act), so it's required.
1642
- */
1643
- _run_scoped;
1644
1635
  /**
1645
1636
  * Tail re-scan window applied to the cold-start checkpoint (ACT-1207).
1646
1637
  * See {@link DEFAULT_COLD_START_BACK_SCAN}. Constructor arg (not a
@@ -1661,7 +1652,6 @@ var CorrelateCycle = class {
1661
1652
  cd,
1662
1653
  max_subscribed_streams,
1663
1654
  declared_lanes,
1664
- run_scoped,
1665
1655
  on_init,
1666
1656
  on_init_async,
1667
1657
  cold_start_back_scan = DEFAULT_COLD_START_BACK_SCAN,
@@ -1675,7 +1665,6 @@ var CorrelateCycle = class {
1675
1665
  this._static_targets = static_targets;
1676
1666
  this._cd = cd;
1677
1667
  this._on_init = on_init;
1678
- this._run_scoped = run_scoped;
1679
1668
  this._on_init_async = on_init_async;
1680
1669
  this._cold_start_back_scan = cold_start_back_scan;
1681
1670
  }
@@ -1923,33 +1912,6 @@ var CorrelateCycle = class {
1923
1912
  this._armed = found === query.limit;
1924
1913
  return { subscribed: 0, last_id, marked: 0, scanned: true };
1925
1914
  }
1926
- /**
1927
- * Start a periodic correlation worker. Returns false if one is already
1928
- * running. Errors from `correlate()` are routed through `log()` so they
1929
- * land in the configured logger (the timer keeps running on failure).
1930
- */
1931
- start_polling(query = {}, frequency = 1e4, callback) {
1932
- if (this._timer) return false;
1933
- const limit = query.limit || 100;
1934
- this._timer = setInterval(
1935
- () => this._run_scoped(() => {
1936
- this.arm();
1937
- return this.correlate(
1938
- {
1939
- ...query,
1940
- after: this._checkpoint,
1941
- limit
1942
- },
1943
- true
1944
- );
1945
- }).then((result) => {
1946
- if (callback && result.subscribed) callback(result.subscribed);
1947
- }).catch((err) => log().error(err)),
1948
- frequency
1949
- );
1950
- return true;
1951
- }
1952
- /** Stop the periodic correlation worker. Idempotent. */
1953
1915
  /**
1954
1916
  * Hand the correlation lease back early.
1955
1917
  *
@@ -1980,12 +1942,6 @@ var CorrelateCycle = class {
1980
1942
  );
1981
1943
  }
1982
1944
  }
1983
- stop_polling() {
1984
- if (this._timer) {
1985
- clearInterval(this._timer);
1986
- this._timer = void 0;
1987
- }
1988
- }
1989
1945
  };
1990
1946
 
1991
1947
  // src/internal/correlator.ts
@@ -3488,6 +3444,10 @@ var Act = class {
3488
3444
  _correlate;
3489
3445
  /** Debounced correlate→drain catch-up loop. */
3490
3446
  _settle;
3447
+ /** Timer behind {@link start_correlations}. */
3448
+ _poll;
3449
+ /** The deprecated `start_correlations` callback, while the poll runs. */
3450
+ _on_discovered;
3491
3451
  /**
3492
3452
  * Disposer for the cross-process notify subscription, set up eagerly
3493
3453
  * during construction. Held as a promise because the subscription
@@ -3897,9 +3857,6 @@ var Act = class {
3897
3857
  on_init: () => {
3898
3858
  if (this._drain && this._reactive_events.size > 0) this._arm_all();
3899
3859
  },
3900
- // Re-scope the background `start_correlations` timer so its
3901
- // correlate resolves the scoped ports, not the singleton (#1191).
3902
- run_scoped: this._scoped,
3903
3860
  // Cold-start defer re-seed (#1221). Skipped on writer-only instances
3904
3861
  // (`drain: false`) — they run no local controllers to re-arm.
3905
3862
  on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
@@ -4489,86 +4446,63 @@ var Act = class {
4489
4446
  const { subscribed, last_id, marked, scanned } = await this._correlate.correlate(query, lease);
4490
4447
  if ((subscribed > 0 || marked > 0) && this._reactive_events.size > 0)
4491
4448
  this._arm_all();
4449
+ if (subscribed > 0) this._on_discovered?.(subscribed);
4492
4450
  return { subscribed, last_id, scanned };
4493
4451
  });
4494
4452
  }
4495
4453
  /**
4496
- * Starts automatic periodic correlation worker for discovering new streams.
4454
+ * Starts a background poll that settles on a timer: each tick looks for
4455
+ * new events, subscribes the streams their reactions target, and drains
4456
+ * them.
4457
+ *
4458
+ * Use it when this process can miss commits — another process writes to a
4459
+ * store without `notify`, or a notification is lost. It is the safety net
4460
+ * under `notify`: a missed wakeup costs at most one `frequency` of latency.
4461
+ *
4462
+ * Only one poll runs per Act instance. On a writer-only instance
4463
+ * (`drain: false`) each tick is a no-op, like `settle()`.
4464
+ *
4465
+ * @param query - Only `limit` is used: the most events each correlate pass
4466
+ * reads (default 100). The scan always starts at the correlation
4467
+ * checkpoint, so `after` is ignored.
4468
+ * @param frequency - Milliseconds between ticks (default: 10000)
4469
+ * @param callback - Called with the number of newly subscribed streams
4470
+ * after each correlate pass that found some.
4471
+ * @deprecated Listen to the `"settled"` lifecycle event instead.
4472
+ * @returns `true` if the poll started, `false` if one is already running
4497
4473
  *
4498
- * The correlation worker runs in the background, scanning for new events and identifying
4499
- * new target streams based on reaction resolvers. It maintains a sliding window that
4500
- * advances with each scan, ensuring all events are eventually correlated.
4501
- *
4502
- * This is useful for dynamic stream creation patterns where you don't know all streams
4503
- * upfront - they're discovered as events arrive.
4504
- *
4505
- * **Note:** Only one correlation worker can run at a time per Act instance.
4506
- *
4507
- * @param query - Query filter for correlation scans — see {@link Query}
4508
- * (typically `{ after: -1, limit: 100 }`)
4509
- * @param frequency - Correlation frequency in milliseconds (default: 10000)
4510
- * @param callback - Optional callback invoked with newly discovered streams
4511
- * @returns `true` if worker started, `false` if already running
4512
- *
4513
- * @example Start automatic correlation
4474
+ * @example
4514
4475
  * ```typescript
4515
- * // Start correlation worker scanning every 5 seconds
4516
- * app.start_correlations(
4517
- * { after: 0, limit: 100 },
4518
- * 5000,
4519
- * (leased) => {
4520
- * console.log(`Discovered ${leased.length} new streams`);
4521
- * }
4522
- * );
4523
- *
4524
- * // Later, stop it
4476
+ * app.start_correlations({ limit: 100 }, 5000);
4477
+ * // later
4525
4478
  * app.stop_correlations();
4526
4479
  * ```
4527
4480
  *
4528
- * @example With checkpoint persistence
4529
- * ```typescript
4530
- * // Load last checkpoint
4531
- * const lastId = await loadCheckpoint();
4532
- *
4533
- * app.start_correlations(
4534
- * { after: lastId, limit: 100 },
4535
- * 10000,
4536
- * async (leased) => {
4537
- * // Save checkpoint for next restart
4538
- * if (leased.length) {
4539
- * const maxId = Math.max(...leased.map(l => l.at));
4540
- * await saveCheckpoint(maxId);
4541
- * }
4542
- * }
4543
- * );
4544
- * ```
4545
- *
4546
- * @see {@link correlate} for manual one-time correlation
4547
- * @see {@link stop_correlations} to stop the worker
4481
+ * @see {@link settle} for what each tick runs
4482
+ * @see {@link stop_correlations} to stop the poll
4548
4483
  */
4549
4484
  start_correlations(query = {}, frequency = 1e4, callback) {
4550
- const started = this._correlate.start_polling(query, frequency, callback);
4551
- return started;
4485
+ if (this._poll) return false;
4486
+ this._on_discovered = callback;
4487
+ const correlate = { limit: query.limit || 100 };
4488
+ this._poll = setInterval(() => {
4489
+ this._correlate.arm();
4490
+ this.settle({ debounceMs: 0, correlate });
4491
+ }, frequency);
4492
+ return true;
4552
4493
  }
4553
4494
  /**
4554
- * Stops the automatic correlation worker.
4555
- *
4556
- * Call this to stop the background correlation worker started by {@link start_correlations}.
4557
- * This is automatically called when the Act instance is disposed.
4558
- *
4559
- * @example
4560
- * ```typescript
4561
- * // Start correlation
4562
- * app.start_correlations();
4563
- *
4564
- * // Later, stop it
4565
- * app.stop_correlations();
4566
- * ```
4495
+ * Stops the poll started by {@link start_correlations}. Idempotent, and
4496
+ * called by `shutdown()`.
4567
4497
  *
4568
4498
  * @see {@link start_correlations}
4569
4499
  */
4570
4500
  stop_correlations() {
4571
- this._correlate.stop_polling();
4501
+ if (this._poll) {
4502
+ clearInterval(this._poll);
4503
+ this._poll = void 0;
4504
+ }
4505
+ this._on_discovered = void 0;
4572
4506
  void this._scoped(() => this._correlate.release_correlation());
4573
4507
  }
4574
4508
  /**