@rotorsoft/act 1.37.6 → 1.37.7

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
@@ -416,7 +416,9 @@ var make_restart_candidate_pass = (deps, options) => {
416
416
  stream,
417
417
  stream_exact: true,
418
418
  names: [SNAP_EVENT],
419
- with_snaps: true
419
+ with_snaps: true,
420
+ // From the start: on its own, `with_snaps` resumes at the latest.
421
+ after: -1
420
422
  }
421
423
  );
422
424
  findings.push({
@@ -573,7 +575,7 @@ var make_correlation_gaps_pass = () => {
573
575
  seen_ids.add(e.id);
574
576
  const causation = e.meta?.causation;
575
577
  const parent_id = causation?.event?.id;
576
- if (parent_id !== void 0) {
578
+ if (parent_id !== void 0 && e.name !== TOMBSTONE_EVENT) {
577
579
  checks.push({ stream: e.stream, id: e.id, parent_id });
578
580
  }
579
581
  },
@@ -1837,6 +1839,7 @@ async function scan(source, opts = {}, callback) {
1837
1839
  async function load(me, target, callback, patch_fn = bare_patch) {
1838
1840
  const { stream, actor, asOf } = target;
1839
1841
  const time_travel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
1842
+ const seeded = time_travel && asOf.limit === void 0 && !asOf.created_after;
1840
1843
  const cached = time_travel ? void 0 : await cache().get(stream);
1841
1844
  const cache_hit = !!cached;
1842
1845
  let state2 = cached?.state ?? (me.init ? me.init() : {});
@@ -1846,6 +1849,7 @@ async function load(me, target, callback, patch_fn = bare_patch) {
1846
1849
  let event;
1847
1850
  await store().query(
1848
1851
  (raw) => {
1852
+ if (seeded && raw.name === SNAP_EVENT && replayed > 0) return;
1849
1853
  event = me.view(raw, actor);
1850
1854
  if (event.name === SNAP_EVENT) {
1851
1855
  state2 = event.data;
@@ -1878,7 +1882,11 @@ async function load(me, target, callback, patch_fn = bare_patch) {
1878
1882
  // `with_snaps` on every non-time-travel load: cold, it resumes from the
1879
1883
  // latest snapshot; warm, it keeps a snapshot above a stale checkpoint,
1880
1884
  // so a windowed close's prune can't fold the tail onto stale state.
1881
- ...cached ? { after: cached.event_id, with_snaps: true } : { ...time_travel ? {} : { with_snaps: true }, ...asOf }
1885
+ ...cached ? { after: cached.event_id, with_snaps: true } : seeded ? (
1886
+ // From the start, past the resume floor, which could jump over the
1887
+ // time bound. The snapshots are for the seed.
1888
+ { with_snaps: true, after: -1, ...asOf }
1889
+ ) : time_travel ? asOf : { with_snaps: true }
1882
1890
  }
1883
1891
  );
1884
1892
  const head_is_tombstone = event?.name === TOMBSTONE_EVENT;
@@ -4104,10 +4112,13 @@ function synthesize_autoclose_reactions(registry, states, read_window) {
4104
4112
  // watermark would let autoclose's defer short-circuit the aggregate's
4105
4113
  // other reactions (the "a defer affects all reactions on a stream"
4106
4114
  // hazard). The close still targets the aggregate (`source`).
4107
- resolver: (e) => ({
4115
+ // A stream name with a regex metacharacter would read as a pattern, and a
4116
+ // store may refuse it at subscribe, stopping every reaction. Such a stream
4117
+ // has no autoclose: `app.close` still closes it.
4118
+ resolver: (e) => is_literal_source(e.stream) ? {
4108
4119
  target: `${AUTOCLOSE_TARGET_PREFIX}${e.stream}`,
4109
4120
  source: e.stream
4110
- }),
4121
+ } : void 0,
4111
4122
  // Never block on autoclose: a transient query/store error should retry,
4112
4123
  // not quarantine the synthetic stream.
4113
4124
  options: { blockOnError: false, maxRetries: DEFAULT_MAX_RETRIES },