@rotorsoft/act 1.33.11 → 1.33.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.
Files changed (37) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/@types/act.d.ts +6 -7
  3. package/dist/@types/act.d.ts.map +1 -1
  4. package/dist/@types/adapters/in-memory-store.d.ts +1 -1
  5. package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
  6. package/dist/@types/internal/config.d.ts +1 -1
  7. package/dist/@types/internal/correlate-cycle.d.ts +4 -3
  8. package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
  9. package/dist/@types/internal/drain-cycle.d.ts +6 -3
  10. package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
  11. package/dist/@types/internal/settle.d.ts +2 -3
  12. package/dist/@types/internal/settle.d.ts.map +1 -1
  13. package/dist/@types/types/ports.d.ts +4 -6
  14. package/dist/@types/types/ports.d.ts.map +1 -1
  15. package/dist/@types/types/reaction.d.ts +2 -1
  16. package/dist/@types/types/reaction.d.ts.map +1 -1
  17. package/dist/{chunk-2FOO4S2J.js → chunk-CAL4XEVN.js} +1 -1
  18. package/dist/chunk-CAL4XEVN.js.map +1 -0
  19. package/dist/{chunk-F4F7CYBN.js → chunk-MSD67APM.js} +61 -72
  20. package/dist/chunk-MSD67APM.js.map +1 -0
  21. package/dist/{chunk-KB7D46CC.cjs → chunk-PMS6BMIU.cjs} +1 -1
  22. package/dist/chunk-PMS6BMIU.cjs.map +1 -0
  23. package/dist/{chunk-HKC663W4.cjs → chunk-U54EW4DX.cjs} +82 -93
  24. package/dist/chunk-U54EW4DX.cjs.map +1 -0
  25. package/dist/index.cjs +252 -263
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +62 -73
  28. package/dist/index.js.map +1 -1
  29. package/dist/test/index.cjs +4 -4
  30. package/dist/test/index.js +2 -2
  31. package/dist/types/index.cjs +2 -2
  32. package/dist/types/index.js +1 -1
  33. package/package.json +1 -1
  34. package/dist/chunk-2FOO4S2J.js.map +0 -1
  35. package/dist/chunk-F4F7CYBN.js.map +0 -1
  36. package/dist/chunk-HKC663W4.cjs.map +0 -1
  37. package/dist/chunk-KB7D46CC.cjs.map +0 -1
package/dist/index.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  sleep,
41
41
  store,
42
42
  validate
43
- } from "./chunk-F4F7CYBN.js";
43
+ } from "./chunk-MSD67APM.js";
44
44
  import {
45
45
  ActorSchema,
46
46
  CausationEventSchema,
@@ -70,7 +70,7 @@ import {
70
70
  pii_fields,
71
71
  pii_schemas,
72
72
  sensitive
73
- } from "./chunk-2FOO4S2J.js";
73
+ } from "./chunk-CAL4XEVN.js";
74
74
 
75
75
  // src/signals.ts
76
76
  process.once("SIGINT", async (arg) => {
@@ -1156,7 +1156,6 @@ function report_lane_conflict(seen, handler, target, kept, dropped) {
1156
1156
  }
1157
1157
  var CorrelateCycle = class {
1158
1158
  _checkpoint = -1;
1159
- _initialized = false;
1160
1159
  /** Lease holder id; per instance, so a restart never inherits a stale claim. */
1161
1160
  _by = randomUUID();
1162
1161
  _key;
@@ -1210,16 +1209,12 @@ var CorrelateCycle = class {
1210
1209
  arm() {
1211
1210
  this._raised++;
1212
1211
  }
1213
- async init() {
1214
- if (this._initialized) return;
1215
- if (!this._init_promise) {
1216
- this._init_promise = this._run_init().catch((error) => {
1217
- this._init_promise = void 0;
1218
- throw error;
1219
- });
1220
- }
1212
+ async _init() {
1213
+ this._init_promise ??= this._run_init().catch((error) => {
1214
+ this._init_promise = void 0;
1215
+ throw error;
1216
+ });
1221
1217
  await this._init_promise;
1222
- this._initialized = true;
1223
1218
  }
1224
1219
  async _run_init() {
1225
1220
  const { watermark, correlated_at } = await this._cd.subscribe([
@@ -1237,7 +1232,6 @@ var CorrelateCycle = class {
1237
1232
  }
1238
1233
  /** After `restore` replaced the log: forget everything and cold-start again. */
1239
1234
  restart() {
1240
- this._initialized = false;
1241
1235
  this._init_promise = void 0;
1242
1236
  this._checkpoint = -1;
1243
1237
  this._dynamic_subscriptions.clear();
@@ -1261,12 +1255,13 @@ var CorrelateCycle = class {
1261
1255
  return pattern.test(stream);
1262
1256
  }
1263
1257
  /** Scan past the checkpoint, subscribe new targets and raise their work marks. */
1264
- async correlate(query = { after: -1, limit: 10 }, lease = false) {
1265
- await this.init();
1258
+ async correlate(query, lease = false) {
1259
+ await this._init();
1266
1260
  const epoch = this._epoch;
1267
1261
  const idle = () => ({
1268
1262
  subscribed: 0,
1269
1263
  last_id: this._checkpoint,
1264
+ read: false,
1270
1265
  marked: 0,
1271
1266
  scanned: false
1272
1267
  });
@@ -1360,6 +1355,7 @@ var CorrelateCycle = class {
1360
1355
  { ...query, after, with_pii: false }
1361
1356
  );
1362
1357
  if (this._declared_lanes.size > 1) await this._recall(correlated);
1358
+ const read = last_id > after;
1363
1359
  if (this._epoch !== epoch) return idle();
1364
1360
  const streams = [];
1365
1361
  for (const [stream, entry] of correlated) {
@@ -1393,11 +1389,11 @@ var CorrelateCycle = class {
1393
1389
  lane
1394
1390
  });
1395
1391
  }
1396
- return { subscribed, last_id, marked, scanned: true };
1392
+ return { subscribed, last_id, read, marked, scanned: true };
1397
1393
  }
1398
1394
  this._checkpoint = last_id;
1399
1395
  if (!stopped && found !== query.limit) this._lowered = raised;
1400
- return { subscribed: 0, last_id, marked: 0, scanned: true };
1396
+ return { subscribed: 0, last_id, read, marked: 0, scanned: true };
1401
1397
  }
1402
1398
  /**
1403
1399
  * Read the row of each target this scan had no record of (new or evicted),
@@ -2354,6 +2350,14 @@ var EMPTY_DRAIN = {
2354
2350
  acked: [],
2355
2351
  blocked: []
2356
2352
  };
2353
+ function merge_drains(drains) {
2354
+ return {
2355
+ fetched: drains.flatMap((d) => d.fetched),
2356
+ leased: drains.flatMap((d) => d.leased),
2357
+ acked: drains.flatMap((d) => d.acked),
2358
+ blocked: drains.flatMap((d) => d.blocked)
2359
+ };
2360
+ }
2357
2361
  var RETRY = "";
2358
2362
  var DrainController = class {
2359
2363
  /**
@@ -2380,9 +2384,8 @@ var DrainController = class {
2380
2384
  _worker;
2381
2385
  _misrouted = /* @__PURE__ */ new Set();
2382
2386
  _stopped = false;
2383
- /** Never rejects; graceful shutdown waits on it. */
2387
+ /** The pass in flight. Never rejects; graceful shutdown waits on it. */
2384
2388
  _inflight;
2385
- _inflight_done;
2386
2389
  _deps;
2387
2390
  constructor(deps) {
2388
2391
  this._deps = deps;
@@ -2438,10 +2441,17 @@ var DrainController = class {
2438
2441
  const leaseMillis = d.leaseMillis ?? options.leaseMillis ?? DEFAULT_LEASE_MILLIS;
2439
2442
  if (this._deps.breaker.state(Date.now()) === "open")
2440
2443
  return EMPTY_DRAIN;
2444
+ const pass = this._pass(streamLimit, eventLimit, leaseMillis).finally(
2445
+ () => {
2446
+ this._inflight = void 0;
2447
+ }
2448
+ );
2449
+ this._inflight = pass;
2450
+ return pass;
2451
+ }
2452
+ /** One claim → fetch → handle → ack pass. Never throws. */
2453
+ async _pass(streamLimit, eventLimit, leaseMillis) {
2441
2454
  try {
2442
- this._inflight = new Promise((done) => {
2443
- this._inflight_done = done;
2444
- });
2445
2455
  const lagging = Math.ceil(streamLimit * this._ratio);
2446
2456
  const leading = streamLimit - lagging;
2447
2457
  const started = Date.now();
@@ -2482,9 +2492,6 @@ var DrainController = class {
2482
2492
  return EMPTY_DRAIN;
2483
2493
  } finally {
2484
2494
  if (!this._stopped) this._defer.schedule();
2485
- this._inflight = void 0;
2486
- this._inflight_done?.();
2487
- this._inflight_done = void 0;
2488
2495
  }
2489
2496
  }
2490
2497
  };
@@ -2852,32 +2859,23 @@ var SettleLoop = class {
2852
2859
  this._timer = void 0;
2853
2860
  const {
2854
2861
  debounceMs: _,
2855
- correlate: correlate_query = {},
2862
+ // Every pass scans from correlation's own checkpoint.
2863
+ correlate: { after: _after, ...correlate_query } = {},
2856
2864
  maxPasses = Infinity,
2857
2865
  ...drain_options
2858
2866
  } = this._next ?? {};
2859
2867
  this._next = void 0;
2860
2868
  this._inflight = (async () => {
2861
- await this._deps.init();
2862
- let settled_drain;
2869
+ const passes = [];
2863
2870
  for (let i = 0; i < maxPasses && !this._stopped; i++) {
2864
- const after_before = this._deps.checkpoint();
2865
- const { subscribed, last_id, scanned } = await this._deps.correlate({
2866
- ...correlate_query,
2867
- after: after_before
2868
- });
2871
+ const { subscribed, read, scanned } = await this._deps.correlate(correlate_query);
2869
2872
  if (scanned) this._deps.breaker.passed();
2870
2873
  const drain = await this._deps.drain(drain_options);
2871
- settled_drain = settled_drain ? {
2872
- fetched: [...settled_drain.fetched, ...drain.fetched],
2873
- leased: [...settled_drain.leased, ...drain.leased],
2874
- acked: [...settled_drain.acked, ...drain.acked],
2875
- blocked: [...settled_drain.blocked, ...drain.blocked]
2876
- } : drain;
2877
- const made_progress = subscribed > 0 || drain.leased.length > 0 || last_id > after_before;
2874
+ passes.push(drain);
2875
+ const made_progress = subscribed > 0 || drain.leased.length > 0 || read;
2878
2876
  if (!made_progress) break;
2879
2877
  }
2880
- if (settled_drain) this._deps.on_settled(settled_drain);
2878
+ if (passes.length) this._deps.on_settled(merge_drains(passes));
2881
2879
  })().catch((err) => {
2882
2880
  this._deps.breaker.failed(Date.now(), err);
2883
2881
  }).finally(() => {
@@ -3182,8 +3180,6 @@ var Act = class {
3182
3180
  _build_settle(options) {
3183
3181
  return new SettleLoop(
3184
3182
  {
3185
- init: () => this._scoped(() => this._correlate.init()),
3186
- checkpoint: () => this._correlate.checkpoint,
3187
3183
  correlate: (q) => this._correlate_scanned(
3188
3184
  { ...q, limit: q.limit ?? this._poll_limit ?? 100 },
3189
3185
  true
@@ -3199,9 +3195,10 @@ var Act = class {
3199
3195
  }
3200
3196
  _shutdown_promise;
3201
3197
  /**
3202
- * Per-instance teardown: stops scheduling, gives the cycles in flight up
3203
- * to `graceMs` to reach their `ack` (an abandoned handler holds its lease
3204
- * until it expires), then removes listeners and the notify subscription.
3198
+ * Per-instance teardown: stops scheduling, removes the notify
3199
+ * subscription, gives the cycles in flight up to `graceMs` to reach their
3200
+ * `ack` (an abandoned handler holds its lease until it expires), then
3201
+ * hands back the correlation lease and removes listeners.
3205
3202
  * The budget is a ceiling, not a delay.
3206
3203
  *
3207
3204
  * Idempotent: repeated calls return the same promise, with the first
@@ -3513,27 +3510,20 @@ var Act = class {
3513
3510
  }
3514
3511
  /** In parallel, so a slow lane never holds up a fast one. */
3515
3512
  async _drain_all(options) {
3516
- const results = await Promise.all(
3517
- [...this._drain_controllers.values()].map((c) => c.drain(options))
3513
+ const merged = merge_drains(
3514
+ await Promise.all(
3515
+ [...this._drain_controllers.values()].map((c) => c.drain(options))
3516
+ )
3518
3517
  );
3519
- const fetched = [];
3520
- const leased = [];
3521
- const acked = [];
3522
- const blocked = [];
3523
- for (const r of results) {
3524
- fetched.push(
3525
- ...r.fetched.map((f) => ({
3526
- ...f,
3527
- events: f.events.map(
3528
- (e) => this.registry.query_gate(e.name)(e)
3529
- )
3530
- }))
3531
- );
3532
- leased.push(...r.leased);
3533
- acked.push(...r.acked);
3534
- blocked.push(...r.blocked);
3535
- }
3536
- return { fetched, leased, acked, blocked };
3518
+ return {
3519
+ ...merged,
3520
+ fetched: merged.fetched.map((f) => ({
3521
+ ...f,
3522
+ events: f.events.map(
3523
+ (e) => this.registry.query_gate(e.name)(e)
3524
+ )
3525
+ }))
3526
+ };
3537
3527
  }
3538
3528
  /**
3539
3529
  * Scans committed events from the correlation checkpoint, subscribes the
@@ -3557,11 +3547,12 @@ var Act = class {
3557
3547
  }
3558
3548
  /** `correlate` plus `scanned`, which settle needs for the breaker. */
3559
3549
  async _correlate_scanned(query, lease = false) {
3560
- if (!this._drain) return { subscribed: 0, last_id: -1, scanned: false };
3550
+ if (!this._drain)
3551
+ return { subscribed: 0, last_id: -1, read: false, scanned: false };
3561
3552
  return this._scoped(async () => {
3562
- const { subscribed, last_id, marked, scanned } = await this._correlate.correlate(query, lease);
3553
+ const { subscribed, last_id, read, marked, scanned } = await this._correlate.correlate(query, lease);
3563
3554
  if (subscribed > 0 || marked > 0) this._arm_all();
3564
- return { subscribed, last_id, scanned };
3555
+ return { subscribed, last_id, read, scanned };
3565
3556
  });
3566
3557
  }
3567
3558
  /**
@@ -3885,10 +3876,8 @@ var Act = class {
3885
3876
  * A local change that frees reaction work (a commit, `reset()`,
3886
3877
  * `unblock()`, `restore()`) schedules this on its own unless the app
3887
3878
  * sets `autoSettle: false`. Call it at startup, to run what was pending
3888
- * before a restart, or with options for the next pass. A pass already
3889
- * scheduled is never put off: calls before it runs join it (a shorter
3890
- * `debounceMs` brings it forward), and a call during a running cycle
3891
- * queues one more pass after it. Runs correlate→drain in a loop until a pass makes no
3879
+ * before a restart, or with options for the next pass; the scheduling
3880
+ * rule is in the event-sourcing docs (§ settle). Runs correlate→drain in a loop until a pass makes no
3892
3881
  * progress — no new subscriptions, no events read, no streams leased —
3893
3882
  * then emits the `"settled"` lifecycle event, so one call fully catches
3894
3883
  * up paginated streams without forcing callers to loop.