@rotorsoft/act 1.33.6 → 1.33.8

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
@@ -1174,8 +1174,6 @@ var CorrelateCycle = (_class3 = class {
1174
1174
 
1175
1175
 
1176
1176
 
1177
- /** Runs once after `on_init`; re-seeds defer timers across a restart. */
1178
-
1179
1177
 
1180
1178
  /** Report-once keys; per instance because `internal/` holds no module state. */
1181
1179
  __init10() {this._reported = /* @__PURE__ */ new Set()}
@@ -1186,8 +1184,7 @@ var CorrelateCycle = (_class3 = class {
1186
1184
  max_subscribed_streams,
1187
1185
  declared_lanes,
1188
1186
  shard,
1189
- on_init,
1190
- on_init_async
1187
+ on_init
1191
1188
  }) {;_class3.prototype.__init3.call(this);_class3.prototype.__init4.call(this);_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);
1192
1189
  this._key = shard ? `${registry_key(registry.events)}:${[...shard].sort().join(",")}` : registry_key(registry.events);
1193
1190
  this._dynamic_subscriptions = new (0, _chunkO6QIJQRNcjs.LruMap)(max_subscribed_streams);
@@ -1196,7 +1193,6 @@ var CorrelateCycle = (_class3 = class {
1196
1193
  this._static_targets = static_targets;
1197
1194
  this._cd = cd;
1198
1195
  this._on_init = on_init;
1199
- this._on_init_async = on_init_async;
1200
1196
  }
1201
1197
  get checkpoint() {
1202
1198
  return this._checkpoint;
@@ -1221,7 +1217,6 @@ var CorrelateCycle = (_class3 = class {
1221
1217
  ...this._static_targets
1222
1218
  ]);
1223
1219
  this._checkpoint = correlated_at >= 0 ? correlated_at : Math.max(-1, watermark - COLD_START_BACK_SCAN);
1224
- _optionalChain([this, 'access', _59 => _59._on_init, 'optionalCall', _60 => _60()]);
1225
1220
  for (const { stream, priority = 0, lane } of this._static_targets) {
1226
1221
  this._static_subscriptions.set(stream, {
1227
1222
  floor: Number.POSITIVE_INFINITY,
@@ -1229,7 +1224,7 @@ var CorrelateCycle = (_class3 = class {
1229
1224
  lane
1230
1225
  });
1231
1226
  }
1232
- await _optionalChain([this, 'access', _61 => _61._on_init_async, 'optionalCall', _62 => _62()]);
1227
+ await _optionalChain([this, 'access', _59 => _59._on_init, 'optionalCall', _60 => _60()]);
1233
1228
  }
1234
1229
  /** After `restore` replaced the log: forget everything and cold-start again. */
1235
1230
  restart() {
@@ -1329,8 +1324,8 @@ var CorrelateCycle = (_class3 = class {
1329
1324
  correlated_at: void 0
1330
1325
  };
1331
1326
  const seen_in_scan = correlated.has(resolved.target);
1332
- const held_lane = _nullishCoalesce((seen_in_scan ? entry.lane : _optionalChain([recorded, 'optionalAccess', _63 => _63.lane])), () => ( _chunk7AHW7FCNcjs.DEFAULT_LANE));
1333
- const held_priority = seen_in_scan ? entry.priority : _optionalChain([recorded, 'optionalAccess', _64 => _64.priority]);
1327
+ const held_lane = _nullishCoalesce((seen_in_scan ? entry.lane : _optionalChain([recorded, 'optionalAccess', _61 => _61.lane])), () => ( _chunk7AHW7FCNcjs.DEFAULT_LANE));
1328
+ const held_priority = seen_in_scan ? entry.priority : _optionalChain([recorded, 'optionalAccess', _62 => _62.priority]);
1334
1329
  const resolved_lane = _nullishCoalesce(lane, () => ( _chunk7AHW7FCNcjs.DEFAULT_LANE));
1335
1330
  if (held_priority === priority && held_lane !== resolved_lane)
1336
1331
  report_lane_conflict(
@@ -1384,7 +1379,7 @@ var CorrelateCycle = (_class3 = class {
1384
1379
  ).length;
1385
1380
  this._checkpoint = last_id;
1386
1381
  for (const { stream, priority, lane } of streams) {
1387
- if (_optionalChain([correlated, 'access', _65 => _65.get, 'call', _66 => _66(stream), 'optionalAccess', _67 => _67.upgraded]))
1382
+ if (_optionalChain([correlated, 'access', _63 => _63.get, 'call', _64 => _64(stream), 'optionalAccess', _65 => _65.upgraded]))
1388
1383
  this._dynamic_subscriptions.set(stream, {
1389
1384
  floor: priority,
1390
1385
  priority,
@@ -1619,7 +1614,7 @@ async function fetch(leased, eventLimit) {
1619
1614
  at,
1620
1615
  lagging,
1621
1616
  events: [],
1622
- error: _nullishCoalesce(_optionalChain([error, 'optionalAccess', _68 => _68.message]), () => ( String(error)))
1617
+ error: _nullishCoalesce(_optionalChain([error, 'optionalAccess', _66 => _66.message]), () => ( String(error)))
1623
1618
  };
1624
1619
  }
1625
1620
  return { stream, source, at, lagging, events };
@@ -1748,7 +1743,7 @@ async function scan(source, opts = {}, callback) {
1748
1743
  return;
1749
1744
  }
1750
1745
  let migrated = event;
1751
- const migration = _optionalChain([event_migrations, 'optionalAccess', _69 => _69[event.name]]);
1746
+ const migration = _optionalChain([event_migrations, 'optionalAccess', _67 => _67[event.name]]);
1752
1747
  if (migration) {
1753
1748
  const old_data = migration.from_schema.parse(event.data);
1754
1749
  const new_data = migration.migrate(old_data);
@@ -1770,7 +1765,7 @@ async function scan(source, opts = {}, callback) {
1770
1765
  return;
1771
1766
  }
1772
1767
  let remapped = migrated;
1773
- const caused_by = _optionalChain([migrated, 'access', _70 => _70.meta, 'access', _71 => _71.causation, 'access', _72 => _72.event, 'optionalAccess', _73 => _73.id]);
1768
+ const caused_by = _optionalChain([migrated, 'access', _68 => _68.meta, 'access', _69 => _69.causation, 'access', _70 => _70.event, 'optionalAccess', _71 => _71.id]);
1774
1769
  if (caused_by !== void 0) {
1775
1770
  const new_caused_by = id_map.get(caused_by);
1776
1771
  if (new_caused_by !== void 0 && new_caused_by !== caused_by) {
@@ -1812,9 +1807,9 @@ async function load(me, target, callback, patch_fn = bare_patch) {
1812
1807
  const time_travel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
1813
1808
  const cached = time_travel ? void 0 : await _chunkO6QIJQRNcjs.cache.call(void 0, ).get(stream);
1814
1809
  const cache_hit = !!cached;
1815
- let state2 = _nullishCoalesce(_optionalChain([cached, 'optionalAccess', _74 => _74.state]), () => ( (me.init ? me.init() : {})));
1816
- let patches = _nullishCoalesce(_optionalChain([cached, 'optionalAccess', _75 => _75.patches]), () => ( 0));
1817
- let snaps = _nullishCoalesce(_optionalChain([cached, 'optionalAccess', _76 => _76.snaps]), () => ( 0));
1810
+ let state2 = _nullishCoalesce(_optionalChain([cached, 'optionalAccess', _72 => _72.state]), () => ( (me.init ? me.init() : {})));
1811
+ let patches = _nullishCoalesce(_optionalChain([cached, 'optionalAccess', _73 => _73.patches]), () => ( 0));
1812
+ let snaps = _nullishCoalesce(_optionalChain([cached, 'optionalAccess', _74 => _74.snaps]), () => ( 0));
1818
1813
  let replayed = 0;
1819
1814
  let event;
1820
1815
  await _chunkO6QIJQRNcjs.store.call(void 0, ).query(
@@ -1834,7 +1829,7 @@ async function load(me, target, callback, patch_fn = bare_patch) {
1834
1829
  `Skipping unknown event "${String(event.name)}" on stream "${stream}" (id=${event.id}) \u2014 no reducer in state "${me.name}"`
1835
1830
  );
1836
1831
  }
1837
- _optionalChain([callback, 'optionalCall', _77 => _77({
1832
+ _optionalChain([callback, 'optionalCall', _75 => _75({
1838
1833
  event,
1839
1834
  state: state2,
1840
1835
  version: event.version,
@@ -1854,7 +1849,7 @@ async function load(me, target, callback, patch_fn = bare_patch) {
1854
1849
  ...cached ? { after: cached.event_id, with_snaps: true } : { ...time_travel ? {} : { with_snaps: true }, ...asOf }
1855
1850
  }
1856
1851
  );
1857
- const head_is_tombstone = _optionalChain([event, 'optionalAccess', _78 => _78.name]) === _chunk7AHW7FCNcjs.TOMBSTONE_EVENT;
1852
+ const head_is_tombstone = _optionalChain([event, 'optionalAccess', _76 => _76.name]) === _chunk7AHW7FCNcjs.TOMBSTONE_EVENT;
1858
1853
  if (replayed > 0 && !time_travel && event && !me.pii_aware && !head_is_tombstone) {
1859
1854
  await cache_put(stream, {
1860
1855
  stream,
@@ -1868,9 +1863,9 @@ async function load(me, target, callback, patch_fn = bare_patch) {
1868
1863
  return {
1869
1864
  event,
1870
1865
  state: state2,
1871
- version: _nullishCoalesce(_nullishCoalesce(_optionalChain([event, 'optionalAccess', _79 => _79.version]), () => ( _optionalChain([cached, 'optionalAccess', _80 => _80.version]))), () => ( -1)),
1866
+ version: _nullishCoalesce(_nullishCoalesce(_optionalChain([event, 'optionalAccess', _77 => _77.version]), () => ( _optionalChain([cached, 'optionalAccess', _78 => _78.version]))), () => ( -1)),
1872
1867
  // Captured with `state`, so consumers never race a separate cache lookup.
1873
- id: _nullishCoalesce(_nullishCoalesce(_optionalChain([event, 'optionalAccess', _81 => _81.id]), () => ( _optionalChain([cached, 'optionalAccess', _82 => _82.event_id]))), () => ( -1)),
1868
+ id: _nullishCoalesce(_nullishCoalesce(_optionalChain([event, 'optionalAccess', _79 => _79.id]), () => ( _optionalChain([cached, 'optionalAccess', _80 => _80.event_id]))), () => ( -1)),
1874
1869
  patches,
1875
1870
  snaps,
1876
1871
  cache_hit,
@@ -1880,11 +1875,11 @@ async function load(me, target, callback, patch_fn = bare_patch) {
1880
1875
  async function action(me, action2, target, payload, options, patch_fn = bare_patch) {
1881
1876
  const { stream, expectedVersion, actor } = target;
1882
1877
  if (!stream) throw new Error("Missing target stream");
1883
- const reactingTo = _optionalChain([options, 'optionalAccess', _83 => _83.reactingTo]);
1884
- const correlator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _84 => _84.correlator]), () => ( default_correlator));
1878
+ const reactingTo = _optionalChain([options, 'optionalAccess', _81 => _81.reactingTo]);
1879
+ const correlator = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _82 => _82.correlator]), () => ( default_correlator));
1885
1880
  const validated = _chunkO6QIJQRNcjs.validate.call(void 0, action2, payload, me.actions[action2]);
1886
- const opts = _optionalChain([me, 'access', _85 => _85.options, 'optionalAccess', _86 => _86[action2]]);
1887
- const max_retries = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _87 => _87.maxRetries]), () => ( 0));
1881
+ const opts = _optionalChain([me, 'access', _83 => _83.options, 'optionalAccess', _84 => _84[action2]]);
1882
+ const max_retries = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _85 => _85.maxRetries]), () => ( 0));
1888
1883
  for (let attempt = 0; ; attempt++) {
1889
1884
  try {
1890
1885
  const snapshot = await load(
@@ -1893,7 +1888,7 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
1893
1888
  void 0,
1894
1889
  patch_fn
1895
1890
  );
1896
- if (_optionalChain([snapshot, 'access', _88 => _88.event, 'optionalAccess', _89 => _89.name]) === _chunk7AHW7FCNcjs.TOMBSTONE_EVENT)
1891
+ if (_optionalChain([snapshot, 'access', _86 => _86.event, 'optionalAccess', _87 => _87.name]) === _chunk7AHW7FCNcjs.TOMBSTONE_EVENT)
1897
1892
  throw new (0, _chunk7AHW7FCNcjs.StreamClosedError)(stream);
1898
1893
  const expected = _nullishCoalesce(expectedVersion, () => ( (snapshot.version >= 0 ? snapshot.version : void 0)));
1899
1894
  if (me.given) {
@@ -1921,7 +1916,7 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
1921
1916
  }));
1922
1917
  const emitted = valid.map((e) => me.message(e));
1923
1918
  const meta = {
1924
- correlation: _optionalChain([reactingTo, 'optionalAccess', _90 => _90.meta, 'access', _91 => _91.correlation]) || correlator({
1919
+ correlation: _optionalChain([reactingTo, 'optionalAccess', _88 => _88.meta, 'access', _89 => _89.correlation]) || correlator({
1925
1920
  action: action2,
1926
1921
  state: me.name,
1927
1922
  stream,
@@ -1974,15 +1969,15 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
1974
1969
  });
1975
1970
  const last = snapshots.at(-1);
1976
1971
  const contiguous = committed[0].version === snapshot.version + 1;
1977
- const snapped = contiguous && _optionalChain([me, 'access', _92 => _92.snap, 'optionalCall', _93 => _93(last)]);
1972
+ const snapped = contiguous && _optionalChain([me, 'access', _90 => _90.snap, 'optionalCall', _91 => _91(last)]);
1978
1973
  const snap_event = snapped ? await snap(last) : void 0;
1979
1974
  if (!me.pii_aware) {
1980
1975
  if (contiguous)
1981
1976
  void cache_put(stream, {
1982
1977
  stream,
1983
1978
  state: last.state,
1984
- version: _nullishCoalesce(_optionalChain([snap_event, 'optionalAccess', _94 => _94.version]), () => ( last.event.version)),
1985
- event_id: _nullishCoalesce(_optionalChain([snap_event, 'optionalAccess', _95 => _95.id]), () => ( last.event.id)),
1979
+ version: _nullishCoalesce(_optionalChain([snap_event, 'optionalAccess', _92 => _92.version]), () => ( last.event.version)),
1980
+ event_id: _nullishCoalesce(_optionalChain([snap_event, 'optionalAccess', _93 => _93.id]), () => ( last.event.id)),
1986
1981
  patches: snap_event ? 0 : last.patches,
1987
1982
  snaps: snap_event ? last.snaps + 1 : last.snaps
1988
1983
  });
@@ -1993,7 +1988,7 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
1993
1988
  if (!(error instanceof _chunk7AHW7FCNcjs.ConcurrencyError)) throw error;
1994
1989
  if (expectedVersion !== void 0) throw error;
1995
1990
  if (attempt >= max_retries) throw error;
1996
- if (_optionalChain([opts, 'optionalAccess', _96 => _96.backoff])) {
1991
+ if (_optionalChain([opts, 'optionalAccess', _94 => _94.backoff])) {
1997
1992
  const delay_ms = compute_backoff_delay(attempt, opts.backoff);
1998
1993
  if (delay_ms > 0) await _chunkO6QIJQRNcjs.sleep.call(void 0, delay_ms);
1999
1994
  }
@@ -2048,9 +2043,9 @@ var as_of_marker = (asOf) => {
2048
2043
  return parts.length ? ` (as-of ${parts.join(" ")})` : " (as-of)";
2049
2044
  };
2050
2045
  var traced = (inner, exit, entry) => (async (...args) => {
2051
- _optionalChain([entry, 'optionalCall', _97 => _97(...args)]);
2046
+ _optionalChain([entry, 'optionalCall', _95 => _95(...args)]);
2052
2047
  const result = await inner(...args);
2053
- _optionalChain([exit, 'optionalCall', _98 => _98(result, ...args)]);
2048
+ _optionalChain([exit, 'optionalCall', _96 => _96(result, ...args)]);
2054
2049
  return result;
2055
2050
  });
2056
2051
  function build_es(logger, correlator = default_correlator, patch_fn = bare_patch) {
@@ -2135,7 +2130,7 @@ function build_drain(logger) {
2135
2130
  subscribe: logger.level !== "trace" ? subscribe : traced(subscribe, (result, streams) => {
2136
2131
  if (!result.subscribed) return;
2137
2132
  const lanes = new Set(streams.map((s) => _nullishCoalesce(s.lane, () => ( "default"))));
2138
- const uniform_lane = lanes.size === 1 ? _optionalChain([streams, 'access', _99 => _99[0], 'optionalAccess', _100 => _100.lane]) : void 0;
2133
+ const uniform_lane = lanes.size === 1 ? _optionalChain([streams, 'access', _97 => _97[0], 'optionalAccess', _98 => _98.lane]) : void 0;
2139
2134
  const data = streams.map(
2140
2135
  ({ stream, lane }) => uniform_lane || !lane || lane === "default" ? hue(C_STREAM, stream) : `${hue(C_STREAM, stream)}${dim(`[${lane}]`)}`
2141
2136
  ).join(" ");
@@ -2147,7 +2142,7 @@ function build_drain(logger) {
2147
2142
  }
2148
2143
  function trace_cycle(logger, leased, fetched, handled, acked, blocked) {
2149
2144
  if (logger.level !== "trace" || !leased.length) return;
2150
- const lane = _optionalChain([leased, 'access', _101 => _101[0], 'optionalAccess', _102 => _102.lane]);
2145
+ const lane = _optionalChain([leased, 'access', _99 => _99[0], 'optionalAccess', _100 => _100.lane]);
2151
2146
  const fetch_by_stream = new Map(fetched.map((f) => [f.stream, f]));
2152
2147
  const acked_by_stream = new Map(acked.map((a) => [a.stream, a.at]));
2153
2148
  const blocked_by_stream = new Map(blocked.map((b) => [b.stream, b.error]));
@@ -2156,8 +2151,8 @@ function trace_cycle(logger, leased, fetched, handled, acked, blocked) {
2156
2151
  );
2157
2152
  const detail = leased.map(({ stream, at, retry }) => {
2158
2153
  const f = fetch_by_stream.get(stream);
2159
- const key = _optionalChain([f, 'optionalAccess', _103 => _103.source]) ? `${hue(C_STREAM, stream)}${dim(`<-${f.source}`)}` : hue(C_STREAM, stream);
2160
- const events = _optionalChain([f, 'optionalAccess', _104 => _104.events, 'access', _105 => _105.length]) ? ` ${dim(
2154
+ const key = _optionalChain([f, 'optionalAccess', _101 => _101.source]) ? `${hue(C_STREAM, stream)}${dim(`<-${f.source}`)}` : hue(C_STREAM, stream);
2155
+ const events = _optionalChain([f, 'optionalAccess', _102 => _102.events, 'access', _103 => _103.length]) ? ` ${dim(
2161
2156
  `[${f.events.map(({ id, name }) => `#${id} ${String(name)}`).join(", ")}]`
2162
2157
  )}` : "";
2163
2158
  const acked_at = acked_by_stream.get(stream);
@@ -2185,7 +2180,7 @@ function trace_cycle(logger, leased, fetched, handled, acked, blocked) {
2185
2180
 
2186
2181
  // src/internal/drain-cycle.ts
2187
2182
  function budget_exhausted(lease, options, store_failing) {
2188
- if (store_failing || !_optionalChain([options, 'optionalAccess', _106 => _106.blockOnError]) || lease.retry <= options.maxRetries)
2183
+ if (store_failing || !_optionalChain([options, 'optionalAccess', _104 => _104.blockOnError]) || lease.retry <= options.maxRetries)
2189
2184
  return void 0;
2190
2185
  const error = `Blocking ${lease.stream} after ${lease.retry} claims with no acknowledged progress \u2014 the retry budget (${options.maxRetries}) was spent without the handler ever reporting an error. That means every attempt lost its lease before it could ack: move the reaction to a lane whose leaseMillis covers the handler, then unblock the stream.`;
2191
2186
  _chunkO6QIJQRNcjs.log.call(void 0, ).error(error);
@@ -2249,7 +2244,7 @@ async function run_drain_cycle(deps, cycle) {
2249
2244
  const fetched = await ops.fetch(leased, eventLimit);
2250
2245
  const fetch_map = /* @__PURE__ */ new Map();
2251
2246
  const fetch_window_at = fetched.reduce(
2252
- (max, { at, events }) => Math.max(max, _optionalChain([events, 'access', _107 => _107.at, 'call', _108 => _108(-1), 'optionalAccess', _109 => _109.id]) || at),
2247
+ (max, { at, events }) => Math.max(max, _optionalChain([events, 'access', _105 => _105.at, 'call', _106 => _106(-1), 'optionalAccess', _107 => _107.id]) || at),
2253
2248
  0
2254
2249
  );
2255
2250
  for (const f of fetched) {
@@ -2313,11 +2308,11 @@ async function run_drain_cycle(deps, cycle) {
2313
2308
  deps.logger
2314
2309
  )
2315
2310
  );
2316
- const at = _optionalChain([entry, 'access', _110 => _110.fetch, 'access', _111 => _111.events, 'access', _112 => _112.at, 'call', _113 => _113(-1), 'optionalAccess', _114 => _114.id]) || fetch_window_at;
2311
+ const at = _optionalChain([entry, 'access', _108 => _108.fetch, 'access', _109 => _109.events, 'access', _110 => _110.at, 'call', _111 => _111(-1), 'optionalAccess', _112 => _112.id]) || fetch_window_at;
2317
2312
  const { payloads } = entry;
2318
2313
  const exhausted = budget_exhausted(
2319
2314
  lease,
2320
- _optionalChain([payloads, 'access', _115 => _115[0], 'optionalAccess', _116 => _116.options]),
2315
+ _optionalChain([payloads, 'access', _113 => _113[0], 'optionalAccess', _114 => _114.options]),
2321
2316
  store_failing
2322
2317
  );
2323
2318
  if (exhausted) return Promise.resolve(exhausted);
@@ -2354,8 +2349,7 @@ var EMPTY_DRAIN = {
2354
2349
  var RETRY = "";
2355
2350
  var DrainController = (_class5 = class {
2356
2351
  __init12() {this._armed = false}
2357
- __init13() {this._locked = false}
2358
- __init14() {this._ratio = 0.5}
2352
+ __init13() {this._ratio = 0.5}
2359
2353
  /**
2360
2354
  * Local wake at the earliest parked (backoff or defer) visit, or at the
2361
2355
  * lease expiry after a failed pass (the {@link RETRY} key); the schedule
@@ -2363,20 +2357,20 @@ var DrainController = (_class5 = class {
2363
2357
  * drains until a pass leases nothing or the controller stops, and an entry
2364
2358
  * stays until handled or until a later pass claims nothing.
2365
2359
  */
2366
- __init15() {this._defer = new DeferTimer(() => {
2360
+ __init14() {this._defer = new DeferTimer(() => {
2367
2361
  this._armed = true;
2368
2362
  void this._deps.run_scoped(async () => {
2369
2363
  while ((await this.drain()).leased.length > 0) ;
2370
2364
  });
2371
2365
  })}
2372
2366
 
2373
- __init16() {this._misrouted = /* @__PURE__ */ new Set()}
2374
- __init17() {this._stopped = false}
2367
+ __init15() {this._misrouted = /* @__PURE__ */ new Set()}
2368
+ __init16() {this._stopped = false}
2375
2369
  /** Never rejects; graceful shutdown waits on it. */
2376
2370
 
2377
2371
 
2378
2372
 
2379
- constructor(deps) {;_class5.prototype.__init12.call(this);_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);_class5.prototype.__init17.call(this);
2373
+ constructor(deps) {;_class5.prototype.__init12.call(this);_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);
2380
2374
  this._deps = deps;
2381
2375
  }
2382
2376
  arm() {
@@ -2395,7 +2389,7 @@ var DrainController = (_class5 = class {
2395
2389
  }
2396
2390
  /** This lane's configured lease, or `undefined`; the shutdown grace basis. */
2397
2391
  get lease_millis() {
2398
- return _optionalChain([this, 'access', _117 => _117._deps, 'access', _118 => _118.defaults, 'optionalAccess', _119 => _119.leaseMillis]);
2392
+ return _optionalChain([this, 'access', _115 => _115._deps, 'access', _116 => _116.defaults, 'optionalAccess', _117 => _117.leaseMillis]);
2399
2393
  }
2400
2394
  get lane() {
2401
2395
  return this._deps.lane;
@@ -2422,8 +2416,8 @@ var DrainController = (_class5 = class {
2422
2416
  this._defer.stop();
2423
2417
  }
2424
2418
  async drain(options = {}) {
2425
- if (!this._armed || this._stopped) return EMPTY_DRAIN;
2426
- if (this._locked) return EMPTY_DRAIN;
2419
+ if (!this._armed || this._stopped || this._inflight)
2420
+ return EMPTY_DRAIN;
2427
2421
  const d = _nullishCoalesce(this._deps.defaults, () => ( {}));
2428
2422
  const streamLimit = _nullishCoalesce(_nullishCoalesce(d.streamLimit, () => ( options.streamLimit)), () => ( _chunkO6QIJQRNcjs.DEFAULT_STREAM_LIMIT));
2429
2423
  const eventLimit = _nullishCoalesce(options.eventLimit, () => ( _chunkO6QIJQRNcjs.DEFAULT_EVENT_LIMIT));
@@ -2431,7 +2425,6 @@ var DrainController = (_class5 = class {
2431
2425
  if (this._deps.breaker.state(Date.now()) === "open")
2432
2426
  return EMPTY_DRAIN;
2433
2427
  try {
2434
- this._locked = true;
2435
2428
  this._inflight = new Promise((done) => {
2436
2429
  this._inflight_done = done;
2437
2430
  });
@@ -2474,9 +2467,8 @@ var DrainController = (_class5 = class {
2474
2467
  return EMPTY_DRAIN;
2475
2468
  } finally {
2476
2469
  if (!this._stopped) this._defer.schedule();
2477
- this._locked = false;
2478
2470
  this._inflight = void 0;
2479
- _optionalChain([this, 'access', _120 => _120._inflight_done, 'optionalCall', _121 => _121()]);
2471
+ _optionalChain([this, 'access', _118 => _118._inflight_done, 'optionalCall', _119 => _119()]);
2480
2472
  this._inflight_done = void 0;
2481
2473
  }
2482
2474
  }
@@ -2683,10 +2675,10 @@ var CloseSignal = class extends Error {
2683
2675
  constructor(opts) {
2684
2676
  super("reaction requested close");
2685
2677
  this.name = "CloseSignal";
2686
- this.stream = _optionalChain([opts, 'optionalAccess', _122 => _122.stream]);
2687
- this.archive = _optionalChain([opts, 'optionalAccess', _123 => _123.archive]);
2688
- this.at = _optionalChain([opts, 'optionalAccess', _124 => _124.at]);
2689
- this.before = _optionalChain([opts, 'optionalAccess', _125 => _125.before]);
2678
+ this.stream = _optionalChain([opts, 'optionalAccess', _120 => _120.stream]);
2679
+ this.archive = _optionalChain([opts, 'optionalAccess', _121 => _121.archive]);
2680
+ this.at = _optionalChain([opts, 'optionalAccess', _122 => _122.at]);
2681
+ this.before = _optionalChain([opts, 'optionalAccess', _123 => _123.before]);
2690
2682
  }
2691
2683
  };
2692
2684
  function build_handle(deps) {
@@ -2716,13 +2708,23 @@ function build_handle(deps) {
2716
2708
  handled++;
2717
2709
  }
2718
2710
  } catch (error) {
2719
- if (error instanceof _chunk7AHW7FCNcjs.DeferSignal)
2720
- return {
2721
- lease,
2722
- handled,
2723
- acked_at: at,
2724
- defer: resolve_defer_at(error.when, event)
2725
- };
2711
+ if (error instanceof _chunk7AHW7FCNcjs.DeferSignal) {
2712
+ let defer;
2713
+ try {
2714
+ defer = resolve_defer_at(error.when, event);
2715
+ } catch (invalid) {
2716
+ return finalize(
2717
+ lease,
2718
+ handled,
2719
+ at,
2720
+ invalid,
2721
+ payload.options,
2722
+ logger,
2723
+ event.id
2724
+ );
2725
+ }
2726
+ return { lease, handled, acked_at: at, defer };
2727
+ }
2726
2728
  if (error instanceof CloseSignal)
2727
2729
  return {
2728
2730
  lease,
@@ -2785,24 +2787,24 @@ function build_handle_batch(logger) {
2785
2787
 
2786
2788
  // src/internal/settle.ts
2787
2789
  var SettleLoop = (_class6 = class {
2788
- __init18() {this._timer = void 0}
2790
+ __init17() {this._timer = void 0}
2789
2791
  /**
2790
2792
  * The cycle in flight, set while one runs. Shutdown waits for it
2791
2793
  * (`stop()` cancels scheduling only). Never rejects.
2792
2794
  */
2793
2795
 
2794
2796
  /** A wake-up arrived while a cycle ran: run one more pass after it. */
2795
- __init19() {this._pending = false}
2797
+ __init18() {this._pending = false}
2796
2798
  /** The last caller's options, used by the next pass. */
2797
- __init20() {this._next = void 0}
2799
+ __init19() {this._next = void 0}
2798
2800
  /** When the scheduled pass fires (epoch ms). */
2799
- __init21() {this._due = 0}
2801
+ __init20() {this._due = 0}
2800
2802
  /** Set at shutdown: nothing schedules again. */
2801
- __init22() {this._stopped = false}
2803
+ __init21() {this._stopped = false}
2802
2804
 
2803
2805
  /** Debounce window applied when the caller doesn't override via `SettleOptions.debounceMs`. */
2804
2806
 
2805
- constructor(deps, default_debounce_ms) {;_class6.prototype.__init18.call(this);_class6.prototype.__init19.call(this);_class6.prototype.__init20.call(this);_class6.prototype.__init21.call(this);_class6.prototype.__init22.call(this);
2807
+ constructor(deps, default_debounce_ms) {;_class6.prototype.__init17.call(this);_class6.prototype.__init18.call(this);_class6.prototype.__init19.call(this);_class6.prototype.__init20.call(this);_class6.prototype.__init21.call(this);
2806
2808
  this._deps = deps;
2807
2809
  this._default_debounce_ms = default_debounce_ms;
2808
2810
  }
@@ -2824,7 +2826,7 @@ var SettleLoop = (_class6 = class {
2824
2826
  this._pending = true;
2825
2827
  return;
2826
2828
  }
2827
- const delay = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _126 => _126.debounceMs]), () => ( this._default_debounce_ms));
2829
+ const delay = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _124 => _124.debounceMs]), () => ( this._default_debounce_ms));
2828
2830
  const due = Date.now() + delay;
2829
2831
  if (this._timer) {
2830
2832
  if (due >= this._due) return;
@@ -2906,8 +2908,7 @@ function validate_only_lanes(options, lanes) {
2906
2908
  );
2907
2909
  }
2908
2910
  var Act = (_class7 = class {
2909
- __init23() {this._emitter = new (0, _events2.default)()}
2910
-
2911
+ __init22() {this._emitter = new (0, _events2.default)()}
2911
2912
 
2912
2913
 
2913
2914
 
@@ -2983,22 +2984,14 @@ var Act = (_class7 = class {
2983
2984
  /** Lanes to arm per event; `"all"` when a dynamic resolver hides the lane. */
2984
2985
 
2985
2986
 
2986
- __init24() {this._logger = _chunkO6QIJQRNcjs.log.call(void 0, )}
2987
+ __init23() {this._logger = _chunkO6QIJQRNcjs.log.call(void 0, )}
2987
2988
  /** This Act's ports: its own bag, or the singleton adapters. */
2988
2989
 
2989
2990
  /** Runs a body so internal `store()`/`cache()` calls resolve to `_ports`. */
2990
2991
 
2991
2992
 
2992
- // Bound once, reused by every reaction payload.
2993
- __init25() {this._bound_do = this.do.bind(this)}
2994
- __init26() {this._bound_load = this.load.bind(this)}
2995
- __init27() {this._bound_query = this.query.bind(this)}
2996
- __init28() {this._bound_query_array = this.query_array.bind(this)}
2997
- __init29() {this._bound_forget = this.forget.bind(this)}
2998
2993
 
2999
-
3000
-
3001
- __init30() {this._close_lock = new CloseLock()}
2994
+ __init24() {this._close_lock = new CloseLock()}
3002
2995
  /** Every lane a controller can exist for: `"default"` plus each declared one. */
3003
2996
 
3004
2997
  /** Drain lanes declared via `.withLane(...)`. Implicit default not included. */
@@ -3006,7 +2999,7 @@ var Act = (_class7 = class {
3006
2999
  return this._lanes;
3007
3000
  }
3008
3001
  /** Use the {@link act} builder rather than constructing directly. */
3009
- constructor(registry, states = /* @__PURE__ */ new Map(), batch_handlers = /* @__PURE__ */ new Map(), options = {}, lanes = [], patch_fn = bare_patch) {;_class7.prototype.__init23.call(this);_class7.prototype.__init24.call(this);_class7.prototype.__init25.call(this);_class7.prototype.__init26.call(this);_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);
3002
+ constructor(registry, states = /* @__PURE__ */ new Map(), batch_handlers = /* @__PURE__ */ new Map(), options = {}, lanes = [], patch_fn = bare_patch) {;_class7.prototype.__init22.call(this);_class7.prototype.__init23.call(this);_class7.prototype.__init24.call(this);
3010
3003
  this.registry = registry;
3011
3004
  this._states = states;
3012
3005
  this._batch_handlers = batch_handlers;
@@ -3030,31 +3023,38 @@ var Act = (_class7 = class {
3030
3023
  this._correlator = _nullishCoalesce(options.correlator, () => ( default_correlator));
3031
3024
  this._es = build_es(this._logger, this._correlator, patch_fn);
3032
3025
  this._cd = build_drain(this._logger);
3033
- this._handle = build_handle({
3026
+ const handle = build_handle({
3034
3027
  logger: this._logger,
3035
3028
  reaction_scope: _chunkO6QIJQRNcjs.make_reaction_scope.call(void 0, {
3036
- do: this._bound_do,
3037
- load: this._bound_load,
3038
- query: this._bound_query,
3039
- query_array: this._bound_query_array,
3040
- forget: this._bound_forget
3029
+ do: this.do.bind(this),
3030
+ load: this.load.bind(this),
3031
+ query: this.query.bind(this),
3032
+ query_array: this.query_array.bind(this),
3033
+ forget: this.forget.bind(this)
3041
3034
  })
3042
3035
  });
3043
- this._handle_batch = build_handle_batch(this._logger);
3036
+ const handle_batch = build_handle_batch(this._logger);
3044
3037
  const classification = classify_registry(this.registry, this._states);
3045
3038
  this._reactive = classification.event_to_lanes.size > 0;
3046
3039
  this._event_to_state = classification.event_to_state;
3047
3040
  this._event_to_lanes = classification.event_to_lanes;
3048
- this._listen = options.listen !== false;
3049
3041
  this._drain = options.drain !== false;
3050
3042
  this._auto_settle = options.autoSettle !== false;
3051
3043
  this._breaker = this._build_breaker(options);
3052
- this._drain_controllers = this._build_drain_controllers(options, lanes);
3044
+ this._drain_controllers = this._build_drain_controllers(
3045
+ options,
3046
+ lanes,
3047
+ handle,
3048
+ handle_batch
3049
+ );
3053
3050
  this._advise_orphaned_lanes(options);
3054
3051
  this._audit_deps = this._build_audit_deps();
3055
3052
  this._correlate = this._build_correlate(options, classification);
3056
3053
  this._settle = this._build_settle(options);
3057
- this._notify_disposer = this._wire_notify(this._ports.store);
3054
+ this._notify_disposer = this._wire_notify(
3055
+ this._ports.store,
3056
+ options.listen !== false
3057
+ );
3058
3058
  _chunkO6QIJQRNcjs.register_weak_disposer.call(void 0, new WeakRef(this), (self) => self.shutdown());
3059
3059
  }
3060
3060
  /** Shared by drain, settle and autoclose; bad knobs throw at build. */
@@ -3070,7 +3070,7 @@ var Act = (_class7 = class {
3070
3070
  );
3071
3071
  }
3072
3072
  /** Apps that declare no lanes pass no lane filter to `claim`. */
3073
- _build_drain_controllers(options, lanes) {
3073
+ _build_drain_controllers(options, lanes, handle, handle_batch) {
3074
3074
  const all_lanes = this._declared_lanes;
3075
3075
  const only_set = options.onlyLanes && options.onlyLanes.length > 0 ? new Set(options.onlyLanes) : void 0;
3076
3076
  const active_lanes = only_set ? all_lanes.filter((n) => only_set.has(n)) : all_lanes;
@@ -3083,8 +3083,8 @@ var Act = (_class7 = class {
3083
3083
  ops: this._cd,
3084
3084
  registry: this.registry,
3085
3085
  batch_handlers: this._batch_handlers,
3086
- handle: this._handle,
3087
- handle_batch: this._handle_batch,
3086
+ handle,
3087
+ handle_batch,
3088
3088
  on_acked: (acked) => this.emit("acked", acked),
3089
3089
  on_blocked: (blocked) => this.emit("blocked", blocked),
3090
3090
  // Reaction-requested close: the same machinery as `app.close`.
@@ -3109,7 +3109,7 @@ var Act = (_class7 = class {
3109
3109
  leaseMillis: cfg.leaseMillis
3110
3110
  }
3111
3111
  });
3112
- if (_optionalChain([cfg, 'optionalAccess', _127 => _127.cycleMs]) !== void 0 && options.drain !== false)
3112
+ if (_optionalChain([cfg, 'optionalAccess', _125 => _125.cycleMs]) !== void 0 && options.drain !== false)
3113
3113
  controller.start(cfg.cycleMs);
3114
3114
  controllers.set(name, controller);
3115
3115
  }
@@ -3149,8 +3149,10 @@ var Act = (_class7 = class {
3149
3149
  // Built after the controllers, so their keys are the active lanes.
3150
3150
  shard: this._drain_controllers.size < this._declared_lanes.length ? [...this._drain_controllers.keys()] : void 0,
3151
3151
  // Only reached on a draining app: writer-only ones never correlate.
3152
- on_init: () => this._arm_all(),
3153
- on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
3152
+ on_init: async () => {
3153
+ this._arm_all();
3154
+ await this._seed_persisted_defers();
3155
+ }
3154
3156
  });
3155
3157
  }
3156
3158
  /** Cold start: an idle deferred stream still fires on time after a restart. */
@@ -3159,7 +3161,7 @@ var Act = (_class7 = class {
3159
3161
  await walk_streams(_chunkO6QIJQRNcjs.store.call(void 0, ), (pos) => {
3160
3162
  if (pos.deferred_at === void 0 || pos.deferred_at <= now) return;
3161
3163
  const controller = this._drain_controllers.get(_nullishCoalesce(pos.lane, () => ( "default")));
3162
- _optionalChain([controller, 'optionalAccess', _128 => _128.seed_defer, 'call', _129 => _129(pos.stream, pos.deferred_at)]);
3164
+ _optionalChain([controller, 'optionalAccess', _126 => _126.seed_defer, 'call', _127 => _127(pos.stream, pos.deferred_at)]);
3163
3165
  });
3164
3166
  }
3165
3167
  _build_settle(options) {
@@ -3204,7 +3206,7 @@ var Act = (_class7 = class {
3204
3206
  for (const c of this._drain_controllers.values()) c.stop();
3205
3207
  const disposer = await this._notify_disposer;
3206
3208
  if (disposer) await disposer();
3207
- await this._await_inflight(_optionalChain([options, 'optionalAccess', _130 => _130.graceMs]));
3209
+ await this._await_inflight(_optionalChain([options, 'optionalAccess', _128 => _128.graceMs]));
3208
3210
  await this._scoped(() => this._correlate.release_correlation());
3209
3211
  this._emitter.removeAllListeners();
3210
3212
  })();
@@ -3243,10 +3245,10 @@ var Act = (_class7 = class {
3243
3245
  }
3244
3246
  }
3245
3247
  /** Failures are logged, never thrown: `notify` is a hint. */
3246
- async _wire_notify(s) {
3248
+ async _wire_notify(s, listen) {
3247
3249
  if (!this._reactive) return void 0;
3248
3250
  if (!s.notify) return void 0;
3249
- if (!this._listen) return void 0;
3251
+ if (!listen) return void 0;
3250
3252
  try {
3251
3253
  return await s.notify((notification) => {
3252
3254
  try {
@@ -3299,8 +3301,8 @@ var Act = (_class7 = class {
3299
3301
  * @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
3300
3302
  */
3301
3303
  async do(action2, target, payload, options) {
3302
- const reacting_to = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _131 => _131.reactingTo]), () => ( _chunkO6QIJQRNcjs.current_reacting.call(void 0, )));
3303
- const do_options = reacting_to === _optionalChain([options, 'optionalAccess', _132 => _132.reactingTo]) ? options : { ...options, reactingTo: reacting_to };
3304
+ const reacting_to = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _129 => _129.reactingTo]), () => ( _chunkO6QIJQRNcjs.current_reacting.call(void 0, )));
3305
+ const do_options = reacting_to === _optionalChain([options, 'optionalAccess', _130 => _130.reactingTo]) ? options : { ...options, reactingTo: reacting_to };
3304
3306
  return this._scoped(async () => {
3305
3307
  const snapshots = await this._es.action(
3306
3308
  this.registry.actions[action2],
@@ -3364,7 +3366,7 @@ var Act = (_class7 = class {
3364
3366
  const gated = this.registry.query_gate(e.name)(e);
3365
3367
  if (!first) first = gated;
3366
3368
  last = gated;
3367
- _optionalChain([callback, 'optionalCall', _133 => _133(gated)]);
3369
+ _optionalChain([callback, 'optionalCall', _131 => _131(gated)]);
3368
3370
  }, query);
3369
3371
  return { first, last, count };
3370
3372
  });
@@ -3491,7 +3493,7 @@ var Act = (_class7 = class {
3491
3493
  }
3492
3494
  if (to_arm.size === 0) return false;
3493
3495
  this._correlate.arm();
3494
- for (const lane of to_arm) _optionalChain([this, 'access', _134 => _134._drain_controllers, 'access', _135 => _135.get, 'call', _136 => _136(lane), 'optionalAccess', _137 => _137.arm, 'call', _138 => _138()]);
3496
+ for (const lane of to_arm) _optionalChain([this, 'access', _132 => _132._drain_controllers, 'access', _133 => _133.get, 'call', _134 => _134(lane), 'optionalAccess', _135 => _135.arm, 'call', _136 => _136()]);
3495
3497
  return true;
3496
3498
  }
3497
3499
  /** In parallel, so a slow lane never holds up a fast one. */
@@ -3636,7 +3638,7 @@ var Act = (_class7 = class {
3636
3638
  return this._scoped(async () => {
3637
3639
  const count = await _chunkO6QIJQRNcjs.store.call(void 0, ).reset(input);
3638
3640
  for (const handler of this._batch_handlers.values())
3639
- _optionalChain([handler, 'access', _139 => _139[FOLD_RESET], 'optionalCall', _140 => _140()]);
3641
+ _optionalChain([handler, 'access', _137 => _137[FOLD_RESET], 'optionalCall', _138 => _138()]);
3640
3642
  if (count > 0) {
3641
3643
  this._wake();
3642
3644
  }
@@ -3768,7 +3770,7 @@ var Act = (_class7 = class {
3768
3770
  (p) => {
3769
3771
  positions.push(p);
3770
3772
  },
3771
- { blocked: true, after: _optionalChain([options, 'optionalAccess', _141 => _141.after]), limit: _optionalChain([options, 'optionalAccess', _142 => _142.limit]) }
3773
+ { blocked: true, after: _optionalChain([options, 'optionalAccess', _139 => _139.after]), limit: _optionalChain([options, 'optionalAccess', _140 => _140.limit]) }
3772
3774
  );
3773
3775
  return positions;
3774
3776
  });
@@ -3964,12 +3966,12 @@ function policy_min_after_days(options) {
3964
3966
  const parsed = AutoclosePolicySchema.parse(options);
3965
3967
  const windows = [];
3966
3968
  if (parsed.after) windows.push(parsed.after.days);
3967
- if (_optionalChain([parsed, 'access', _143 => _143.or, 'optionalAccess', _144 => _144.after])) windows.push(parsed.or.after.days);
3969
+ if (_optionalChain([parsed, 'access', _141 => _141.or, 'optionalAccess', _142 => _142.after])) windows.push(parsed.or.after.days);
3968
3970
  return windows.length ? Math.min(...windows) : void 0;
3969
3971
  }
3970
3972
  function policy_keep_days(options) {
3971
3973
  const parsed = AutoclosePolicySchema.parse(options);
3972
- return _optionalChain([parsed, 'access', _145 => _145.keep, 'optionalAccess', _146 => _146.days]);
3974
+ return _optionalChain([parsed, 'access', _143 => _143.keep, 'optionalAccess', _144 => _144.days]);
3973
3975
  }
3974
3976
  function compile_autoclose_policy(options) {
3975
3977
  const parsed = AutoclosePolicySchema.parse(options);
@@ -4001,7 +4003,7 @@ function hour_in_zone(now, timeZone) {
4001
4003
  hour: "2-digit",
4002
4004
  hourCycle: "h23"
4003
4005
  }).formatToParts(now);
4004
- return Number(_optionalChain([parts, 'access', _147 => _147.find, 'call', _148 => _148((p) => p.type === "hour"), 'optionalAccess', _149 => _149.value]));
4006
+ return Number(_optionalChain([parts, 'access', _145 => _145.find, 'call', _146 => _146((p) => p.type === "hour"), 'optionalAccess', _147 => _147.value]));
4005
4007
  }
4006
4008
  function hour_in_range(hour, start, end) {
4007
4009
  return start < end ? hour >= start && hour < end : hour >= start || hour < end;
@@ -4099,7 +4101,7 @@ function synthesize_autoclose_reactions(registry, states, read_window) {
4099
4101
  };
4100
4102
  const key = `__autoclose_${st.name}`;
4101
4103
  for (const event_name of Object.keys(st.events)) {
4102
- _optionalChain([registry, 'access', _150 => _150.events, 'access', _151 => _151[event_name], 'optionalAccess', _152 => _152.reactions, 'access', _153 => _153.set, 'call', _154 => _154(
4104
+ _optionalChain([registry, 'access', _148 => _148.events, 'access', _149 => _149[event_name], 'optionalAccess', _150 => _150.reactions, 'access', _151 => _151.set, 'call', _152 => _152(
4103
4105
  key,
4104
4106
  reaction
4105
4107
  )]);
@@ -4308,37 +4310,49 @@ function register_lane(config2, lanes) {
4308
4310
  `Lane "${config2.name}" was already declared with a different config`
4309
4311
  );
4310
4312
  }
4313
+ function add_reaction(events, event, name, kind, make, builder) {
4314
+ if (!name)
4315
+ throw new Error(
4316
+ `${kind} handler for "${String(event)}" must be a named function`
4317
+ );
4318
+ const label = kind === "Reaction" ? "reaction" : "projection handler";
4319
+ if (events[event].reactions.has(name))
4320
+ throw new Error(
4321
+ `Duplicate ${label} "${name}" for event "${String(event)}". ${kind} handlers are keyed by function name; rename one of them.`
4322
+ );
4323
+ const reaction = make();
4324
+ events[event].reactions.set(name, reaction);
4325
+ return Object.assign(builder, {
4326
+ to(resolver) {
4327
+ reaction.resolver = typeof resolver === "string" ? { target: resolver } : resolver;
4328
+ return builder;
4329
+ }
4330
+ });
4331
+ }
4311
4332
  function reaction_on(event, events, builder) {
4312
- const register = (handler, options, schedule) => {
4313
- if (!handler.name)
4314
- throw new Error(
4315
- `Reaction handler for "${String(event)}" must be a named function`
4316
- );
4317
- if (events[event].reactions.has(handler.name))
4318
- throw new Error(
4319
- `Duplicate reaction "${handler.name}" for event "${String(event)}". Reaction handlers are keyed by function name; rename one of them.`
4320
- );
4321
- if (schedule && typeof schedule !== "function") assert_defer_when(schedule);
4322
- const reaction = {
4323
- handler: schedule ? make_deferred(handler, schedule) : handler,
4324
- resolver: _this_,
4325
- // Validate the whole bag at the declaration site so a bad
4326
- // `maxRetries`/`backoff`/`blockOnError` throws ZodError at build,
4327
- // not a NaN gate on the first retry.
4328
- options: _chunkO6QIJQRNcjs.resolveReactionConfig.call(void 0, {
4329
- blockOnError: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _155 => _155.blockOnError]), () => ( _chunkO6QIJQRNcjs.DEFAULT_BLOCK_ON_ERROR)),
4330
- maxRetries: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _156 => _156.maxRetries]), () => ( _chunkO6QIJQRNcjs.DEFAULT_MAX_RETRIES)),
4331
- backoff: _optionalChain([options, 'optionalAccess', _157 => _157.backoff])
4332
- })
4333
- };
4334
- events[event].reactions.set(handler.name, reaction);
4335
- return Object.assign(builder, {
4336
- to(resolver) {
4337
- reaction.resolver = typeof resolver === "string" ? { target: resolver } : resolver;
4338
- return builder;
4339
- }
4340
- });
4341
- };
4333
+ const register = (handler, options, schedule) => add_reaction(
4334
+ events,
4335
+ event,
4336
+ handler.name,
4337
+ "Reaction",
4338
+ () => {
4339
+ if (schedule && typeof schedule !== "function")
4340
+ assert_defer_when(schedule);
4341
+ return {
4342
+ handler: schedule ? make_deferred(handler, schedule) : handler,
4343
+ resolver: _this_,
4344
+ // Validate the whole bag at the declaration site so a bad
4345
+ // `maxRetries`/`backoff`/`blockOnError` throws ZodError at build,
4346
+ // not a NaN gate on the first retry.
4347
+ options: _chunkO6QIJQRNcjs.resolveReactionConfig.call(void 0, {
4348
+ blockOnError: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _153 => _153.blockOnError]), () => ( _chunkO6QIJQRNcjs.DEFAULT_BLOCK_ON_ERROR)),
4349
+ maxRetries: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _154 => _154.maxRetries]), () => ( _chunkO6QIJQRNcjs.DEFAULT_MAX_RETRIES)),
4350
+ backoff: _optionalChain([options, 'optionalAccess', _155 => _155.backoff])
4351
+ })
4352
+ };
4353
+ },
4354
+ builder
4355
+ );
4342
4356
  return {
4343
4357
  do: (handler, options) => register(handler, options),
4344
4358
  defer: (schedule) => ({
@@ -4352,9 +4366,9 @@ function reaction_on(event, events, builder) {
4352
4366
 
4353
4367
  // src/internal/date-reviver.ts
4354
4368
 
4355
- var def_of = (schema) => _nullishCoalesce(_optionalChain([schema, 'access', _158 => _158._zod, 'optionalAccess', _159 => _159.def]), () => ( schema.def));
4369
+ var def_of = (schema) => _nullishCoalesce(_optionalChain([schema, 'access', _156 => _156._zod, 'optionalAccess', _157 => _157.def]), () => ( schema.def));
4356
4370
  function variant_schema(schema) {
4357
- const shape = _optionalChain([def_of, 'call', _160 => _160(schema), 'optionalAccess', _161 => _161.shape]);
4371
+ const shape = _optionalChain([def_of, 'call', _158 => _158(schema), 'optionalAccess', _159 => _159.shape]);
4358
4372
  if (!shape) {
4359
4373
  const dates = date_reviver_schema(schema);
4360
4374
  return { schema: _nullishCoalesce(dates, () => ( schema)), dated: !!dates };
@@ -4402,7 +4416,7 @@ function date_reviver_schema(schema) {
4402
4416
  return variants.some((v) => v.dated) ? _zod2.z.union(variants.map((v) => v.schema)) : void 0;
4403
4417
  }
4404
4418
  case "nullable":
4405
- return _optionalChain([inner, 'call', _162 => _162(), 'optionalAccess', _163 => _163.nullable, 'call', _164 => _164()]);
4419
+ return _optionalChain([inner, 'call', _160 => _160(), 'optionalAccess', _161 => _161.nullable, 'call', _162 => _162()]);
4406
4420
  case "optional":
4407
4421
  case "nonoptional":
4408
4422
  case "readonly":
@@ -4471,7 +4485,7 @@ function build_events(registry, states, lanes, owners) {
4471
4485
  throw new Error(
4472
4486
  `Reaction on target "${resolver.target}" conflicts with the projection that already serves it \u2014 a target is served by one batch handler or one state projection, and a reaction to it would never run`
4473
4487
  );
4474
- if (resolver.lane && !declared.has(resolver.lane))
4488
+ if (resolver.lane !== void 0 && !declared.has(resolver.lane))
4475
4489
  lane_errors.push(
4476
4490
  `Reaction "${handler_name}" on "${event_name}" targets undeclared lane "${resolver.lane}". Declared lanes: ${[...declared].map((l) => `"${l}"`).join(", ")}. Add \`.withLane({ name: "${resolver.lane}", ... })\` to act() or correct the .to() declaration.`
4477
4491
  );
@@ -4579,7 +4593,7 @@ function act() {
4579
4593
  let _built = false;
4580
4594
  const read_wrap = (original) => {
4581
4595
  const wrapped = async (events, stream) => {
4582
- const read = events.map((e) => _nullishCoalesce(_optionalChain([_hr, 'access', _165 => _165.get, 'call', _166 => _166(e.name), 'optionalCall', _167 => _167(e)]), () => ( e)));
4596
+ const read = events.map((e) => _nullishCoalesce(_optionalChain([_hr, 'access', _163 => _163.get, 'call', _164 => _164(e.name), 'optionalCall', _165 => _165(e)]), () => ( e)));
4583
4597
  return original(read, stream);
4584
4598
  };
4585
4599
  const reset = original[FOLD_RESET];
@@ -4621,7 +4635,7 @@ function act() {
4621
4635
  });
4622
4636
  }
4623
4637
  for (const [action_name, handler] of Object.entries(state2.on)) {
4624
- const static_target = _optionalChain([handler, 'optionalAccess', _168 => _168._static_emit]);
4638
+ const static_target = _optionalChain([handler, 'optionalAccess', _166 => _166._static_emit]);
4625
4639
  if (static_target && deprecated.has(static_target)) {
4626
4640
  const current = current_version_of(static_target, event_names);
4627
4641
  throw new Error(
@@ -4677,7 +4691,7 @@ function act() {
4677
4691
  },
4678
4692
  build: (options) => {
4679
4693
  _chunkO6QIJQRNcjs.resolveActConfig.call(void 0, options);
4680
- const patch_fn = _optionalChain([options, 'optionalAccess', _169 => _169.validateFoldedState]) === true ? validating_patch : bare_patch;
4694
+ const patch_fn = _optionalChain([options, 'optionalAccess', _167 => _167.validateFoldedState]) === true ? validating_patch : bare_patch;
4681
4695
  if (!_built) {
4682
4696
  for (const proj of pending_projections) add_projection(proj);
4683
4697
  for (const proj of fold_projections) {
@@ -4695,7 +4709,7 @@ function act() {
4695
4709
  `State projection "${proj.target}" of "${fold.name}" is missing events ${missing.join(", ")} \u2014 pass every partial of the state to .of()`
4696
4710
  );
4697
4711
  const claimed = fold_specs.find((s) => s.target === proj.target);
4698
- if (_optionalChain([claimed, 'optionalAccess', _170 => _170.projection]) === proj) continue;
4712
+ if (_optionalChain([claimed, 'optionalAccess', _168 => _168.projection]) === proj) continue;
4699
4713
  if (batch_handlers.has(proj.target) || claimed)
4700
4714
  throw new Error(
4701
4715
  `Duplicate projection target "${proj.target}" \u2014 a target is served by one batch handler or one state projection, never both`
@@ -4767,31 +4781,22 @@ function _projection(target) {
4767
4781
  }
4768
4782
  return {
4769
4783
  do: (handler) => {
4770
- const reaction = {
4771
- handler,
4772
- resolver: _nullishCoalesce(default_resolver, () => ( _this_)),
4773
- options: {
4774
- blockOnError: _chunkO6QIJQRNcjs.DEFAULT_BLOCK_ON_ERROR,
4775
- maxRetries: _chunkO6QIJQRNcjs.DEFAULT_MAX_RETRIES
4776
- }
4777
- };
4778
- const register = events[event];
4779
- if (!handler.name)
4780
- throw new Error(
4781
- `Projection handler for "${event}" must be a named function`
4782
- );
4783
- if (register.reactions.has(handler.name))
4784
- throw new Error(
4785
- `Duplicate projection handler "${handler.name}" for event "${event}". Projection handlers are keyed by function name; rename one of them.`
4786
- );
4787
- register.reactions.set(handler.name, reaction);
4788
4784
  const widened = base;
4789
- return Object.assign(widened, {
4790
- to(resolver) {
4791
- reaction.resolver = typeof resolver === "string" ? { target: resolver } : resolver;
4792
- return widened;
4793
- }
4794
- });
4785
+ return add_reaction(
4786
+ events,
4787
+ event,
4788
+ handler.name,
4789
+ "Projection",
4790
+ () => ({
4791
+ handler,
4792
+ resolver: _nullishCoalesce(default_resolver, () => ( _this_)),
4793
+ options: {
4794
+ blockOnError: _chunkO6QIJQRNcjs.DEFAULT_BLOCK_ON_ERROR,
4795
+ maxRetries: _chunkO6QIJQRNcjs.DEFAULT_MAX_RETRIES
4796
+ }
4797
+ }),
4798
+ widened
4799
+ );
4795
4800
  }
4796
4801
  };
4797
4802
  },