@rotorsoft/act 1.32.24 → 1.32.25

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-OG5O36AJ.js";
29
+ } from "./chunk-QXW3JV2H.js";
30
30
  import {
31
31
  ActorSchema,
32
32
  CausationEventSchema,
@@ -1355,37 +1355,34 @@ async function windowed_prune_pending(stream, before, max_id) {
1355
1355
  }
1356
1356
  async function probe_min_watermarks(streams, page_size, checkpoint) {
1357
1357
  const min_at = /* @__PURE__ */ new Map();
1358
- const source_regex = /* @__PURE__ */ new Map();
1359
- const get_regex = (source) => {
1360
- let re = source_regex.get(source);
1358
+ const matches = source_matcher();
1359
+ await walk_streams(
1360
+ store(),
1361
+ (position) => {
1362
+ const pending = position.correlated_at === void 0 || position.at < position.correlated_at;
1363
+ const cap = pending ? position.at : Math.max(position.at, checkpoint);
1364
+ for (const stream of streams) {
1365
+ if (matches(position.source, stream)) {
1366
+ const prev = min_at.get(stream);
1367
+ if (prev === void 0 || cap < prev) min_at.set(stream, cap);
1368
+ }
1369
+ }
1370
+ },
1371
+ { limit: page_size, source_matches: streams }
1372
+ );
1373
+ return min_at;
1374
+ }
1375
+ function source_matcher() {
1376
+ const compiled = /* @__PURE__ */ new Map();
1377
+ return (source, stream) => {
1378
+ if (!source) return true;
1379
+ let re = compiled.get(source);
1361
1380
  if (!re) {
1362
1381
  re = new RegExp(source);
1363
- source_regex.set(source, re);
1382
+ compiled.set(source, re);
1364
1383
  }
1365
- return re;
1384
+ return re.test(stream);
1366
1385
  };
1367
- let after;
1368
- for (; ; ) {
1369
- let last;
1370
- const { count } = await store().query_streams(
1371
- (position) => {
1372
- last = position.stream;
1373
- const source_re = position.source ? get_regex(position.source) : void 0;
1374
- const pending = position.correlated_at === void 0 || position.at < position.correlated_at;
1375
- const cap = pending ? position.at : Math.max(position.at, checkpoint);
1376
- for (const stream of streams) {
1377
- if (!source_re || source_re.test(stream)) {
1378
- const prev = min_at.get(stream);
1379
- if (prev === void 0 || cap < prev) min_at.set(stream, cap);
1380
- }
1381
- }
1382
- },
1383
- { after, limit: page_size, source_matches: streams }
1384
- );
1385
- if (count < page_size) break;
1386
- after = last;
1387
- }
1388
- return min_at;
1389
1386
  }
1390
1387
  async function scan_stream_heads(streams) {
1391
1388
  const stats = await store().query_stats(streams, {
@@ -1419,36 +1416,20 @@ async function partition_by_safety(stream_info, reactive_events_size, skipped, p
1419
1416
  if (checkpoint < info.max_id) uncorrelated.add(stream);
1420
1417
  }
1421
1418
  const pending_set = /* @__PURE__ */ new Set();
1422
- const source_regex = /* @__PURE__ */ new Map();
1423
- const get_regex = (source) => {
1424
- let re = source_regex.get(source);
1425
- if (!re) {
1426
- re = new RegExp(source);
1427
- source_regex.set(source, re);
1428
- }
1429
- return re;
1430
- };
1419
+ const matches = source_matcher();
1431
1420
  const targets = [...stream_info.keys()];
1432
- let after;
1433
- for (; ; ) {
1434
- let last;
1435
- const { count } = await store().query_streams(
1436
- (position) => {
1437
- last = position.stream;
1438
- const source_re = position.source ? get_regex(position.source) : void 0;
1439
- const has_work = position.correlated_at !== void 0 && position.at < position.correlated_at;
1440
- if (!has_work) return;
1441
- for (const [stream, info] of stream_info) {
1442
- if ((!source_re || source_re.test(stream)) && position.at < info.max_id) {
1443
- pending_set.add(stream);
1444
- }
1445
- }
1446
- },
1447
- { after, limit: page_size, source_matches: targets }
1448
- );
1449
- if (count < page_size) break;
1450
- after = last;
1451
- }
1421
+ await walk_streams(
1422
+ store(),
1423
+ (position) => {
1424
+ const has_work = position.correlated_at !== void 0 && position.at < position.correlated_at;
1425
+ if (!has_work) return;
1426
+ for (const [stream, info] of stream_info) {
1427
+ if (matches(position.source, stream) && position.at < info.max_id)
1428
+ pending_set.add(stream);
1429
+ }
1430
+ },
1431
+ { limit: page_size, source_matches: targets }
1432
+ );
1452
1433
  const safe = [];
1453
1434
  for (const [stream] of stream_info) {
1454
1435
  if (pending_set.has(stream) || uncorrelated.has(stream))
@@ -2132,6 +2113,18 @@ var validating_patch = (me, state2, partial, event) => {
2132
2113
  );
2133
2114
  };
2134
2115
  var DEFAULT_BATCH = 500;
2116
+ var cache_put = (stream, entry) => cache().set(stream, entry).catch(
2117
+ (err) => log().warn(
2118
+ err,
2119
+ "cache set skipped; the next load re-folds from the store"
2120
+ )
2121
+ );
2122
+ var cache_drop = (stream) => cache().invalidate(stream).catch(
2123
+ (err) => log().warn(
2124
+ err,
2125
+ "cache invalidate skipped; a stale checkpoint re-folds from its event_id"
2126
+ )
2127
+ );
2135
2128
  async function snap(snapshot) {
2136
2129
  const { id, stream, name, meta, version } = snapshot.event;
2137
2130
  try {
@@ -2334,19 +2327,14 @@ async function load(me, target, callback, patch_fn = bare_patch) {
2334
2327
  );
2335
2328
  const head_is_tombstone = event?.name === TOMBSTONE_EVENT;
2336
2329
  if (replayed > 0 && !time_travel && event && !me.pii_aware && !head_is_tombstone) {
2337
- await cache().set(stream, {
2330
+ await cache_put(stream, {
2338
2331
  stream,
2339
2332
  state: state2,
2340
2333
  version: event.version,
2341
2334
  event_id: event.id,
2342
2335
  patches,
2343
2336
  snaps
2344
- }).catch(
2345
- (err) => log().warn(
2346
- err,
2347
- "cache set skipped; the next load re-folds from the store"
2348
- )
2349
- );
2337
+ });
2350
2338
  }
2351
2339
  return {
2352
2340
  event,
@@ -2434,14 +2422,7 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
2434
2422
  reactingTo ? expectedVersion : expected
2435
2423
  );
2436
2424
  } catch (error) {
2437
- if (error instanceof ConcurrencyError) {
2438
- cache().invalidate(stream).catch(
2439
- (err) => log().warn(
2440
- err,
2441
- "cache invalidate skipped; a stale checkpoint re-folds from its event_id"
2442
- )
2443
- );
2444
- }
2425
+ if (error instanceof ConcurrencyError) void cache_drop(stream);
2445
2426
  throw error;
2446
2427
  }
2447
2428
  let { state: state2, patches } = snapshot;
@@ -2468,26 +2449,15 @@ async function action(me, action2, target, payload, options, patch_fn = bare_pat
2468
2449
  const snap_event = snapped ? await snap(last) : void 0;
2469
2450
  if (!me.pii_aware) {
2470
2451
  if (contiguous)
2471
- cache().set(stream, {
2452
+ void cache_put(stream, {
2472
2453
  stream,
2473
2454
  state: last.state,
2474
2455
  version: snap_event?.version ?? last.event.version,
2475
2456
  event_id: snap_event?.id ?? last.event.id,
2476
2457
  patches: snap_event ? 0 : last.patches,
2477
2458
  snaps: snap_event ? last.snaps + 1 : last.snaps
2478
- }).catch(
2479
- (err) => log().warn(
2480
- err,
2481
- "cache set skipped; the next load re-folds from the store"
2482
- )
2483
- );
2484
- else
2485
- cache().invalidate(stream).catch(
2486
- (err) => log().warn(
2487
- err,
2488
- "cache invalidate skipped; a stale checkpoint re-folds from its event_id"
2489
- )
2490
- );
2459
+ });
2460
+ else void cache_drop(stream);
2491
2461
  }
2492
2462
  return snapshots;
2493
2463
  } catch (error) {
@@ -3250,41 +3220,23 @@ function validate_only_lanes(options, lanes) {
3250
3220
  }
3251
3221
  var Act = class {
3252
3222
  _emitter = new EventEmitter();
3253
- /** Circuit breaker shared by all drain lanes. */
3254
3223
  _breaker;
3255
- /** Whether this instance subscribes to `Store.notify`. */
3256
3224
  _listen;
3257
- /** Whether this instance runs reactions (drain, settle, correlate). */
3258
3225
  _drain;
3259
- /** Event names with at least one registered reaction (computed at build time) */
3260
3226
  _reactive_events;
3261
- /** One DrainController per active lane, keyed by lane name. */
3262
3227
  _drain_controllers;
3263
- /** Correlation: lazy init and the scan that marks work. */
3264
3228
  _correlate;
3265
- /** Debounced correlate→drain catch-up loop. */
3266
3229
  _settle;
3267
- /** Timer behind {@link start_correlations}. */
3268
3230
  _poll;
3269
3231
  /** The deprecated `start_correlations` callback, while the poll runs. */
3270
3232
  _on_discovered;
3271
3233
  /**
3272
- * Disposer for the cross-process notify subscription, set up eagerly
3273
- * during construction. Held as a promise because the subscription
3274
- * itself may be async (the PG adapter checks out a dedicated client
3275
- * and runs `LISTEN` before resolving). Resolves to `undefined` when
3276
- * the store doesn't implement `notify` or there are no registered
3277
- * reactions.
3278
- *
3279
- * **Contract:** the configured store must be injected via
3280
- * {@link store}`(adapter)` *before* calling `act()...build()`. The
3281
- * orchestrator wires notify against whatever store is current at
3282
- * construction time — late injection after build is unsupported.
3234
+ * Wired at construction against the store current then, so a store
3235
+ * injected after `build()` is never notified.
3283
3236
  */
3284
3237
  _notify_disposer;
3285
3238
  /** Public registry — kept as-is per the no-prefix-on-public convention. */
3286
3239
  registry;
3287
- /** Map of state name → state definition; populated by the builder. */
3288
3240
  _states;
3289
3241
  /**
3290
3242
  * Emit a lifecycle event. The payload type is inferred from the event name
@@ -3310,15 +3262,9 @@ var Act = class {
3310
3262
  return listeners.length > 0;
3311
3263
  }
3312
3264
  /**
3313
- * The single store-failure handler: log it, then emit the `error`
3314
- * lifecycle event. Wired into the circuit breaker's `on_error`, so it
3315
- * runs on every `failed()` — drain / settle / autoclose just call
3316
- * `breaker.failed(now, error)` and never log or emit themselves.
3317
- *
3318
- * The emit is guarded against Node's `EventEmitter` contract that an
3319
- * unhandled `"error"` emission is rethrown (which would crash the process
3320
- * from inside the drain catch); logging is unconditional so failures are
3321
- * never silent.
3265
+ * The one place a store failure is logged and emitted (the breaker's
3266
+ * `on_error`). The emit is guarded: an unhandled `"error"` emission
3267
+ * would crash the process.
3322
3268
  */
3323
3269
  _emit_error(error, circuit) {
3324
3270
  this._logger.error(error);
@@ -3340,58 +3286,29 @@ var Act = class {
3340
3286
  this._emitter.off(event, listener);
3341
3287
  return this;
3342
3288
  }
3343
- /** Batch handlers for static-target projections (target → handler) */
3344
3289
  _batch_handlers;
3345
- /** Event-sourcing handlers, optionally wrapped with trace decorators */
3346
3290
  _es;
3347
- /** Correlate/drain pipeline ops, optionally wrapped with trace decorators */
3348
3291
  _cd;
3349
- /**
3350
- * Event-name → owning state, computed at build time. The duplicate-event
3351
- * guard in merge.ts ensures one event name maps to at most one state, so
3352
- * this lookup is unambiguous. Used by `close()` to pick the right reducer
3353
- * set when seeding a `restart` snapshot in multi-state apps.
3354
- */
3292
+ /** Unambiguous: merge rejects an event name declared by two states. */
3355
3293
  _event_to_state;
3356
- /**
3357
- * Event-name → lane fan-in for selective arming. Built by
3358
- * `classify_registry` once per build. `"all"` means at least one of
3359
- * the event's reactions is a dynamic resolver (lane opaque until
3360
- * runtime); a `Set<string>` lists the static lanes only that event's
3361
- * reactions target.
3362
- */
3294
+ /** Lanes to arm per event; `"all"` when a dynamic resolver hides the lane. */
3363
3295
  _event_to_lanes;
3364
- /** What `internal/audit.ts` needs, built once at construction. */
3365
3296
  _audit_deps;
3366
- /** Logger resolved at construction time (after user port configuration) */
3367
3297
  _logger = log();
3368
3298
  /** This Act's ports: its own bag, or the singleton adapters. */
3369
3299
  _ports;
3370
- /**
3371
- * Runs a body inside this Act's ports frame, so internal
3372
- * `store()`/`cache()` calls resolve to `_ports`.
3373
- */
3300
+ /** Runs a body so internal `store()`/`cache()` calls resolve to `_ports`. */
3374
3301
  _scoped;
3375
- /**
3376
- * Correlation-id generator for originating actions. Bound at
3377
- * construction from `options.correlator ?? default_correlator`. The
3378
- * `do()` path passes this into the `_es.action` closure; close-cycle
3379
- * uses it via {@link close_correlation}.
3380
- */
3381
3302
  _correlator;
3382
- /** Pre-bound IAct methods reused across drain cycles. Only `do` varies per
3383
- * payload (it captures the triggering event for reactingTo auto-inject). */
3303
+ // Bound once, reused by every reaction payload.
3384
3304
  _bound_do = this.do.bind(this);
3385
3305
  _bound_load = this.load.bind(this);
3386
3306
  _bound_query = this.query.bind(this);
3387
3307
  _bound_query_array = this.query_array.bind(this);
3388
3308
  _bound_forget = this.forget.bind(this);
3389
- /** Reaction dispatchers built once and handed to run_drain_cycle each cycle. */
3390
3309
  _handle;
3391
3310
  _handle_batch;
3392
- /** Declared drain lanes. */
3393
3311
  _lanes;
3394
- /** Serializes close critical sections per stream. */
3395
3312
  _close_lock = new CloseLock();
3396
3313
  /** Every lane a controller can exist for: `"default"` plus each declared one. */
3397
3314
  _declared_lanes;
@@ -3399,26 +3316,7 @@ var Act = class {
3399
3316
  get lanes() {
3400
3317
  return this._lanes;
3401
3318
  }
3402
- /**
3403
- * Create a new Act orchestrator. Prefer the {@link act} builder over
3404
- * direct construction — `act()...build()` wires the registry, merges
3405
- * partial states, and collects batch handlers from registered slices
3406
- * and projections in one pass.
3407
- *
3408
- * @param registry Schemas for every event and action across registered states
3409
- * @param states Merged map of state name → state definition
3410
- * @param batch_handlers Static-target projection batch handlers (target → handler)
3411
- * @param options Tuning knobs — see {@link ActOptions}
3412
- * @param lanes Declared drain lanes. The builder collects
3413
- * these from `.withLane(...)` calls; one `DrainController` runs
3414
- * per lane, plus the implicit `"default"`.
3415
- * @param patch_fn The per-event patch step selected once by the builder
3416
- * from `ActOptions.validateFoldedState` — `bare_patch` by
3417
- * default, `validating_patch` when the flag is on. The builder uses
3418
- * the same value for its projection-fold handlers, so there is a
3419
- * single selection site. Defaults to `bare_patch` for direct
3420
- * construction.
3421
- */
3319
+ /** Use the {@link act} builder rather than constructing directly. */
3422
3320
  constructor(registry, states = /* @__PURE__ */ new Map(), batch_handlers = /* @__PURE__ */ new Map(), options = {}, lanes = [], patch_fn = bare_patch) {
3423
3321
  this.registry = registry;
3424
3322
  this._states = states;
@@ -3469,11 +3367,7 @@ var Act = class {
3469
3367
  this._notify_disposer = this._wire_notify(this._ports.store);
3470
3368
  register_weak_disposer(new WeakRef(this), (self) => self.shutdown());
3471
3369
  }
3472
- /**
3473
- * Circuit breaker shared by every store-polling loop (drain, the settle
3474
- * correlate, autoclose). Validates the knobs eagerly so out-of-range
3475
- * values throw at build time, not on the first cycle tick.
3476
- */
3370
+ /** Shared by drain, settle and autoclose; bad knobs throw at build. */
3477
3371
  _build_breaker(options) {
3478
3372
  return new CircuitBreaker(
3479
3373
  resolveCircuitBreakerConfig(options.circuitBreaker),
@@ -3487,11 +3381,7 @@ var Act = class {
3487
3381
  }
3488
3382
  );
3489
3383
  }
3490
- /**
3491
- * One DrainController per active lane, plus the implicit "default" unless
3492
- * `onlyLanes` excludes it. Apps that declare no lanes pass no lane filter
3493
- * to `claim`.
3494
- */
3384
+ /** Apps that declare no lanes pass no lane filter to `claim`. */
3495
3385
  _build_drain_controllers(options, lanes) {
3496
3386
  const all_lanes = this._declared_lanes;
3497
3387
  const only_set = options.onlyLanes && options.onlyLanes.length > 0 ? new Set(options.onlyLanes) : void 0;
@@ -3527,11 +3417,7 @@ var Act = class {
3527
3417
  }
3528
3418
  return controllers;
3529
3419
  }
3530
- /**
3531
- * Startup advisory naming declared lanes that `onlyLanes` leaves without a
3532
- * controller here. One process can't check that the cluster covers every
3533
- * lane, so it reports its own gaps.
3534
- */
3420
+ /** One process can't see the cluster, so it reports its own lane gaps. */
3535
3421
  _advise_orphaned_lanes(options) {
3536
3422
  if (!options.onlyLanes || options.onlyLanes.length === 0) return;
3537
3423
  const active = new Set(this._drain_controllers.keys());
@@ -3553,11 +3439,6 @@ var Act = class {
3553
3439
  routed_events: new Set(this._event_to_lanes.keys())
3554
3440
  };
3555
3441
  }
3556
- /**
3557
- * Correlate cycle over the classified registry. The cold-start callback
3558
- * arms every controller — historical events may need processing —
3559
- * except on writer-only instances (`drain: false`).
3560
- */
3561
3442
  _build_correlate(options, classification) {
3562
3443
  return new CorrelateCycle({
3563
3444
  registry: this.registry,
@@ -3575,12 +3456,7 @@ var Act = class {
3575
3456
  on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
3576
3457
  });
3577
3458
  }
3578
- /**
3579
- * At cold start, re-seed each lane controller's local wake from the
3580
- * persisted future `deferred_at`, so an idle deferred stream (an
3581
- * autoclose waiting out its window) still fires on time. Streams on lanes
3582
- * this instance doesn't run are skipped.
3583
- */
3459
+ /** Cold start: an idle deferred stream still fires on time after a restart. */
3584
3460
  async _seed_persisted_defers() {
3585
3461
  const now = Date.now();
3586
3462
  await walk_streams(store(), (pos) => {
@@ -3589,7 +3465,6 @@ var Act = class {
3589
3465
  controller?.seed_defer(pos.stream, pos.deferred_at);
3590
3466
  });
3591
3467
  }
3592
- /** Settle loop driving correlate + drain to quiescence. */
3593
3468
  _build_settle(options) {
3594
3469
  return new SettleLoop(
3595
3470
  {
@@ -3603,7 +3478,6 @@ var Act = class {
3603
3478
  options.settleDebounceMs ?? DEFAULT_SETTLE_DEBOUNCE_MS
3604
3479
  );
3605
3480
  }
3606
- /** True after the first `shutdown()` call. Guards idempotency. */
3607
3481
  _shutdown_promise;
3608
3482
  /**
3609
3483
  * Per-instance teardown: stop scheduling new work, give drain cycles
@@ -3643,14 +3517,8 @@ var Act = class {
3643
3517
  return this._shutdown_promise;
3644
3518
  }
3645
3519
  /**
3646
- * Wait for every in-flight drain cycle, or for the grace budget to elapse,
3647
- * whichever comes first. Cycle promises never reject (`drain()` contains
3648
- * its own errors), so this never throws.
3649
- *
3650
- * An omitted budget is the largest `leaseMillis` among lanes with a cycle
3651
- * in flight (the drain's fallback for lanes that pinned none, or pinned
3652
- * 0, which is the same as none), with an in-flight settle counting on the
3653
- * fallback, capped at the maximum. Idle lanes don't count.
3520
+ * The largest `leaseMillis` among lanes with a cycle in flight (the
3521
+ * default for none or 0, and for a running settle), capped.
3654
3522
  */
3655
3523
  _derive_grace_ms(running, settling) {
3656
3524
  let max = settling ? DEFAULT_LEASE_MILLIS : 0;
@@ -3679,12 +3547,7 @@ var Act = class {
3679
3547
  clearTimeout(timer);
3680
3548
  }
3681
3549
  }
3682
- /**
3683
- * Subscribe to {@link Store.notify} when both the store and the
3684
- * registry support it. Returns the disposer (or `undefined` when no
3685
- * subscription was made). Errors during subscription are logged but
3686
- * never thrown — `notify` is a hint, not a contract.
3687
- */
3550
+ /** Failures are logged, never thrown: `notify` is a hint. */
3688
3551
  async _wire_notify(s) {
3689
3552
  if (this._reactive_events.size === 0) return void 0;
3690
3553
  if (!s.notify) return void 0;
@@ -3996,20 +3859,11 @@ var Act = class {
3996
3859
  return { fetched: [], leased: [], acked: [], blocked: [] };
3997
3860
  return this._scoped(() => this._drain_all(options));
3998
3861
  }
3999
- /** Arm every active lane controller. */
4000
3862
  _arm_all() {
4001
3863
  this._correlate.arm();
4002
3864
  for (const c of this._drain_controllers.values()) c.arm();
4003
3865
  }
4004
- /**
4005
- * Arm only the lane controllers whose reactions match the supplied
4006
- * event names (selective arming). Events with any dynamic
4007
- * resolver fall back to `_arm_all()` via the `"all"` sentinel — the
4008
- * resolver's lane isn't known until correlate runs the function.
4009
- * Events with no reactions are skipped; `_event_to_lanes` doesn't
4010
- * carry them. Returns true when any controller was armed (used by
4011
- * the notify handler to decide whether to schedule a settle).
4012
- */
3866
+ /** Arms only the lanes these events react on; true when any was armed. */
4013
3867
  _arm_for_event_names(names) {
4014
3868
  const to_arm = /* @__PURE__ */ new Set();
4015
3869
  for (const name of names) {
@@ -4026,14 +3880,7 @@ var Act = class {
4026
3880
  for (const lane of to_arm) this._drain_controllers.get(lane)?.arm();
4027
3881
  return true;
4028
3882
  }
4029
- /** Drain every active lane controller in parallel and aggregate.
4030
- *
4031
- * Parallel — not sequential — so a slow lane's in-flight handler does
4032
- * not block a fast lane's claim/dispatch/ack cycle. Each controller's
4033
- * `claim()` is independent (filtered by lane); the store's
4034
- * `SKIP LOCKED` keeps cross-controller races safe. Lifecycle events
4035
- * (`acked`, `blocked`) may interleave by lane — listeners filter via
4036
- * `lease.lane`. */
3883
+ /** In parallel, so a slow lane never holds up a fast one. */
4037
3884
  async _drain_all(options) {
4038
3885
  const results = await Promise.all(
4039
3886
  [...this._drain_controllers.values()].map((c) => c.drain(options))
@@ -4106,14 +3953,7 @@ var Act = class {
4106
3953
  const { subscribed, last_id } = await this._correlate_scanned(query);
4107
3954
  return { subscribed, last_id };
4108
3955
  }
4109
- /**
4110
- * `correlate` plus whether the pass actually read the store.
4111
- *
4112
- * The settle loop needs that extra bit to decide whether the pass carries a
4113
- * circuit-breaker health signal, and a disarmed pass carries none. It stays
4114
- * internal rather than widening the public `correlate` return, which is
4115
- * charter-covered and has no use for it.
4116
- */
3956
+ /** `correlate` plus `scanned`, which settle needs for the breaker. */
4117
3957
  async _correlate_scanned(query, lease = false) {
4118
3958
  if (!this._drain) return { subscribed: 0, last_id: -1, scanned: false };
4119
3959
  return this._scoped(async () => {
@@ -4454,11 +4294,7 @@ var Act = class {
4454
4294
  async prioritize(filter, priority) {
4455
4295
  return this._scoped(() => store().prioritize(filter, priority));
4456
4296
  }
4457
- /**
4458
- * Run one close: the cycle with this Act's ports, then emit `closed`. Shared by `close()` and the drain's
4459
- * reaction-requested closes. The close actor lets a custom correlator
4460
- * still tag tenant context or trace ids.
4461
- */
4297
+ /** Shared by `close()` and reaction-requested closes. */
4462
4298
  async _run_close(targets) {
4463
4299
  const close_actor = { id: "$close", name: "close" };
4464
4300
  const result = await run_close_cycle(targets, {
@@ -4539,10 +4375,7 @@ var Act = class {
4539
4375
  */
4540
4376
  async close(targets) {
4541
4377
  if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
4542
- return this._scoped(async () => {
4543
- await this.correlate({ limit: CLOSE_CATCH_UP_LIMIT });
4544
- return this._run_close(targets);
4545
- });
4378
+ return this._scoped(() => this._run_close(targets));
4546
4379
  }
4547
4380
  /**
4548
4381
  * Debounced, non-blocking correlate→drain cycle.