@rotorsoft/act 1.23.25 → 1.23.27

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.cjs CHANGED
@@ -1259,9 +1259,7 @@ var InMemoryStore = class {
1259
1259
  }
1260
1260
  return source_matches.some((name) => re.test(name));
1261
1261
  };
1262
- const sorted = [...this._streams.values()].sort(
1263
- (a, b) => a.stream.localeCompare(b.stream)
1264
- );
1262
+ const sorted = [...this._streams.keys()].sort().map((stream) => this._streams.get(stream));
1265
1263
  let count = 0;
1266
1264
  for (const s of sorted) {
1267
1265
  if (after !== void 0 && s.stream <= after) continue;
@@ -1637,6 +1635,27 @@ function current_version_of(deprecated_name, all_names) {
1637
1635
  return highest && highest.version > target.version ? highest.name : void 0;
1638
1636
  }
1639
1637
 
1638
+ // src/internal/walk-streams.ts
1639
+ var DEFAULT_STREAM_PAGE = 500;
1640
+ async function walk_streams(store3, callback, query) {
1641
+ const page_size = query?.limit ?? DEFAULT_STREAM_PAGE;
1642
+ let after = query?.after;
1643
+ let total = 0;
1644
+ for (; ; ) {
1645
+ let last;
1646
+ const { count } = await store3.query_streams(
1647
+ (position) => {
1648
+ last = position.stream;
1649
+ callback(position);
1650
+ },
1651
+ { ...query, after, limit: page_size }
1652
+ );
1653
+ total += count;
1654
+ if (count < page_size) return total;
1655
+ after = last;
1656
+ }
1657
+ }
1658
+
1640
1659
  // src/internal/audit.ts
1641
1660
  var DEFAULTS = {
1642
1661
  idle_days: 90,
@@ -1667,13 +1686,16 @@ async function* audit(deps, categories, options = {}) {
1667
1686
  const need_streams = passes.some((p) => p.on_stream !== void 0);
1668
1687
  const need_events = passes.some((p) => p.on_event !== void 0);
1669
1688
  if (need_stats) {
1670
- const stats = await deps.store().query_stats({}, { count: true, names: true });
1689
+ const stats = await deps.store().query_stats(
1690
+ {},
1691
+ { count: true, names: true, exclude: [SNAP_EVENT] }
1692
+ );
1671
1693
  for (const [stream, s] of stats) {
1672
1694
  for (const p of passes) p.on_stat?.(stream, s);
1673
1695
  }
1674
1696
  }
1675
1697
  if (need_streams) {
1676
- await deps.store().query_streams((pos) => {
1698
+ await walk_streams(deps.store(), (pos) => {
1677
1699
  for (const p of passes) p.on_stream?.(pos);
1678
1700
  });
1679
1701
  }
@@ -1807,23 +1829,38 @@ var make_close_candidate_pass = (deps, options) => {
1807
1829
  };
1808
1830
  var make_restart_candidate_pass = (deps, options) => {
1809
1831
  const threshold = options.thresholds?.restart_min ?? DEFAULTS.restart_min;
1832
+ const candidates = [];
1810
1833
  const findings = [];
1811
1834
  return {
1812
1835
  category: "restart-candidate",
1813
- on_stat(stream, { head, count, names }) {
1836
+ on_stat(stream, { head, count }) {
1814
1837
  if (count < threshold) return;
1815
1838
  const head_name = String(head.name);
1816
1839
  if (head_name.startsWith("__")) return;
1817
1840
  if (!restart_is_supported(deps, head_name)) return;
1818
- findings.push({
1819
- category: "restart-candidate",
1820
- stream,
1821
- count,
1822
- // names map is sparse — `__snapshot__` key absent when the
1823
- // stream has never been snapshotted (a common case for the
1824
- // restart-candidate signal).
1825
- snaps: names.__snapshot__ ?? 0
1826
- });
1841
+ candidates.push({ stream, count });
1842
+ },
1843
+ async finalize(deps2) {
1844
+ for (const { stream, count } of candidates) {
1845
+ let snaps = 0;
1846
+ await deps2.store().query(
1847
+ () => {
1848
+ snaps++;
1849
+ },
1850
+ {
1851
+ stream,
1852
+ stream_exact: true,
1853
+ names: [SNAP_EVENT],
1854
+ with_snaps: true
1855
+ }
1856
+ );
1857
+ findings.push({
1858
+ category: "restart-candidate",
1859
+ stream,
1860
+ count,
1861
+ snaps
1862
+ });
1863
+ }
1827
1864
  },
1828
1865
  drain: () => findings
1829
1866
  };
@@ -1878,34 +1915,33 @@ var make_snapshot_drift_pass = (deps, options) => {
1878
1915
  const findings = [];
1879
1916
  return {
1880
1917
  category: "snapshot-drift",
1881
- on_stat(stream, { head, count, names }) {
1918
+ on_stat(stream, { head, count }) {
1882
1919
  if (!restart_is_supported(deps, String(head.name))) return;
1883
1920
  if (count < drift_min) return;
1884
1921
  candidates.push({
1885
1922
  stream,
1886
- total: count,
1887
- snaps: names.__snapshot__ ?? 0
1923
+ total: count
1888
1924
  });
1889
1925
  },
1890
1926
  async finalize(deps2) {
1891
- for (const { stream, total, snaps } of candidates) {
1927
+ for (const { stream, total } of candidates) {
1892
1928
  let events_since_snap = total;
1893
1929
  let snap_at;
1894
- if (snaps > 0) {
1895
- const collected = [];
1896
- await deps2.store().query(
1897
- (e) => {
1898
- collected.push({ id: e.id });
1899
- },
1900
- {
1901
- stream,
1902
- stream_exact: true,
1903
- names: ["__snapshot__"],
1904
- backward: true,
1905
- limit: 1,
1906
- with_snaps: true
1907
- }
1908
- );
1930
+ const collected = [];
1931
+ await deps2.store().query(
1932
+ (e) => {
1933
+ collected.push({ id: e.id });
1934
+ },
1935
+ {
1936
+ stream,
1937
+ stream_exact: true,
1938
+ names: ["__snapshot__"],
1939
+ backward: true,
1940
+ limit: 1,
1941
+ with_snaps: true
1942
+ }
1943
+ );
1944
+ if (collected.length > 0) {
1909
1945
  snap_at = collected[0].id;
1910
1946
  let after = 0;
1911
1947
  await deps2.store().query(
@@ -4865,7 +4901,7 @@ var Act = class {
4865
4901
  */
4866
4902
  async _seed_persisted_defers() {
4867
4903
  const now = Date.now();
4868
- await store2().query_streams((pos) => {
4904
+ await walk_streams(store2(), (pos) => {
4869
4905
  if (pos.deferred_at === void 0 || pos.deferred_at <= now) return;
4870
4906
  const controller = this._drain_controllers.get(pos.lane ?? "default");
4871
4907
  controller?.seed_defer(pos.stream, pos.deferred_at);
@@ -5847,8 +5883,35 @@ function act() {
5847
5883
  const pending_projections = [];
5848
5884
  const fold_projections = [];
5849
5885
  const batch_handlers = /* @__PURE__ */ new Map();
5886
+ const fold_specs = [];
5850
5887
  const lanes = [];
5851
5888
  let _built = false;
5889
+ const pii_wrap = (original) => async (events, stream) => {
5890
+ const stripped = events.map((e) => {
5891
+ const f = _sf.get(e.name);
5892
+ return f ? pii_strip(e, f) : e;
5893
+ });
5894
+ return original(stripped, stream);
5895
+ };
5896
+ const make_batch_handlers = (patch_fn) => {
5897
+ const handlers = new Map(batch_handlers);
5898
+ for (const spec of fold_specs) {
5899
+ handlers.set(
5900
+ spec.target,
5901
+ pii_wrap(
5902
+ make_fold_handler(
5903
+ spec.merged,
5904
+ spec.flush,
5905
+ spec.config,
5906
+ patch_fn,
5907
+ // Head loads strip sensitive keys like the warm path (#1320).
5908
+ (event_name) => _sf.get(event_name) ?? []
5909
+ )
5910
+ )
5911
+ );
5912
+ }
5913
+ return handlers;
5914
+ };
5852
5915
  const finalize_deprecations = () => {
5853
5916
  const deprecation_summary = [];
5854
5917
  for (const state2 of states.values()) {
@@ -5944,19 +6007,12 @@ function act() {
5944
6007
  throw new Error(
5945
6008
  `State projection "${proj.target}" of "${fold.name}" is missing events ${missing.join(", ")} \u2014 pass every partial of the state to .of()`
5946
6009
  );
5947
- batch_handlers.set(
5948
- proj.target,
5949
- make_fold_handler(
5950
- merged,
5951
- fold.flush,
5952
- fold.config,
5953
- patch_fn,
5954
- // Head loads strip sensitive keys like the warm path (#1320).
5955
- // `_sf` is populated in the events pass below; read lazily at
5956
- // fold time, by when the map is complete.
5957
- (event_name) => _sf.get(event_name) ?? []
5958
- )
5959
- );
6010
+ fold_specs.push({
6011
+ target: proj.target,
6012
+ merged,
6013
+ flush: fold.flush,
6014
+ config: fold.config
6015
+ });
5960
6016
  }
5961
6017
  finalize_deprecations();
5962
6018
  validate_lane_references(registry, lanes);
@@ -6007,14 +6063,7 @@ function act() {
6007
6063
  };
6008
6064
  }
6009
6065
  for (const [target, original] of batch_handlers) {
6010
- const wrapped = async (events, stream) => {
6011
- const stripped = events.map((e) => {
6012
- const f = _sf.get(e.name);
6013
- return f ? pii_strip(e, f) : e;
6014
- });
6015
- return original(stripped, stream);
6016
- };
6017
- batch_handlers.set(target, wrapped);
6066
+ batch_handlers.set(target, pii_wrap(original));
6018
6067
  }
6019
6068
  synthesize_autoclose_reactions(
6020
6069
  registry,
@@ -6029,7 +6078,7 @@ function act() {
6029
6078
  return new Act(
6030
6079
  registry,
6031
6080
  states,
6032
- batch_handlers,
6081
+ make_batch_handlers(patch_fn),
6033
6082
  options,
6034
6083
  lanes,
6035
6084
  patch_fn