@rotorsoft/act 1.31.12 → 1.31.13

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
@@ -1480,13 +1480,33 @@ var CorrelateCycle = class {
1480
1480
  /** In-flight init, memoized for single-flight and cleared on failure. */
1481
1481
  _init_promise;
1482
1482
  _timer = void 0;
1483
- // target → what it was last subscribed at. Every scan re-subscribes the
1483
+ // Dynamically discovered targets → what each was last subscribed at,
1484
+ // bounded by `maxSubscribedStreams`. The static half lives in
1485
+ // `_static_subscriptions` below, which is deliberately not evictable.
1486
+ //
1487
+ // Every scan re-subscribes the
1484
1488
  // targets it resolved (that is how the work mark lands), so this no longer
1485
1489
  // decides *whether* a target is sent — it decides *what* is sent with it:
1486
1490
  // a resolution raises priority/lane only when it beats the recorded floor,
1487
1491
  // and otherwise re-sends the row's own values so the mark changes nothing
1488
1492
  // else. See {@link Subscription}.
1489
- _subscribed;
1493
+ _dynamic_subscriptions;
1494
+ /**
1495
+ * What each static target was subscribed at by `init`. A plain map, never
1496
+ * evicted: the collection is the build-time `_static_targets` list, so it
1497
+ * is already bounded by the registry and costs nothing the registry does
1498
+ * not already hold.
1499
+ *
1500
+ * Keeping these out of the LRU is what makes the `+Infinity` floor an
1501
+ * invariant rather than a race (#1582). Sharing the bounded map meant a
1502
+ * churn of dynamic targets could evict a static record, and the next
1503
+ * dynamic resolution to that target found no record, took the
1504
+ * first-discovery branch, and re-subscribed the target with its own
1505
+ * priority and lane — silently re-laning a stream whose lane the
1506
+ * build-time subscribe owns, and starving it wherever `onlyLanes` had
1507
+ * provisioned a worker for the declared lane.
1508
+ */
1509
+ _static_subscriptions = /* @__PURE__ */ new Map();
1490
1510
  /** Compiled pattern sources, bounded by {@link PATTERN_CACHE_SIZE}. */
1491
1511
  _patterns = new LruMap(PATTERN_CACHE_SIZE);
1492
1512
  _registry;
@@ -1539,7 +1559,7 @@ var CorrelateCycle = class {
1539
1559
  }) {
1540
1560
  this._lease_millis = lease_millis;
1541
1561
  this._key = registry_key(registry.events);
1542
- this._subscribed = new LruMap(max_subscribed_streams);
1562
+ this._dynamic_subscriptions = new LruMap(max_subscribed_streams);
1543
1563
  this._registry = registry;
1544
1564
  this._declared_lanes = declared_lanes;
1545
1565
  this._static_targets = static_targets;
@@ -1589,7 +1609,7 @@ var CorrelateCycle = class {
1589
1609
  this._checkpoint = correlated_at >= 0 ? correlated_at : Math.max(-1, watermark - this._cold_start_back_scan);
1590
1610
  this._on_init?.();
1591
1611
  for (const { stream, priority = 0, lane } of this._static_targets) {
1592
- this._subscribed.set(stream, {
1612
+ this._static_subscriptions.set(stream, {
1593
1613
  floor: Number.POSITIVE_INFINITY,
1594
1614
  priority,
1595
1615
  lane
@@ -1608,14 +1628,12 @@ var CorrelateCycle = class {
1608
1628
  * the documented per-aggregate shape `.to(e => ({target: e.stream}))`
1609
1629
  * makes those two namespaces collide by construction (#1398).
1610
1630
  *
1611
- * Static targets are left alone: they are subscribed once at init and
1612
- * recorded at +Infinity so the dynamic path never re-opens them.
1631
+ * Static targets are left alone: their record lives in
1632
+ * `_static_subscriptions`, which this never touches, so they stay pinned
1633
+ * at +Infinity and the dynamic path never re-opens them.
1613
1634
  */
1614
1635
  forget_subscribed(streams) {
1615
- const statics = new Set(this._static_targets.map((t) => t.stream));
1616
- for (const stream of streams) {
1617
- if (!statics.has(stream)) this._subscribed.delete(stream);
1618
- }
1636
+ for (const stream of streams) this._dynamic_subscriptions.delete(stream);
1619
1637
  }
1620
1638
  /**
1621
1639
  * Would an event from `stream` be fetched for a target subscribed with
@@ -1691,7 +1709,7 @@ var CorrelateCycle = class {
1691
1709
  );
1692
1710
  lane = void 0;
1693
1711
  }
1694
- const recorded = this._subscribed.get(resolved.target);
1712
+ const recorded = this._static_subscriptions.get(resolved.target) ?? this._dynamic_subscriptions.get(resolved.target);
1695
1713
  const priority = resolved.priority ?? 0;
1696
1714
  const upgraded = !recorded || priority > recorded.floor;
1697
1715
  const carried = upgraded ? { priority, lane } : { priority: recorded.priority, lane: recorded.lane };
@@ -1750,7 +1768,7 @@ var CorrelateCycle = class {
1750
1768
  this._checkpoint = last_id;
1751
1769
  for (const { stream, priority, lane } of streams) {
1752
1770
  if (correlated.get(stream)?.upgraded)
1753
- this._subscribed.set(stream, {
1771
+ this._dynamic_subscriptions.set(stream, {
1754
1772
  floor: priority,
1755
1773
  priority,
1756
1774
  lane