@rotorsoft/act 1.32.16 → 1.32.18
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/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +10 -46
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/builders/builder-base.d.ts +1 -1
- package/dist/@types/builders/builder-base.d.ts.map +1 -1
- package/dist/@types/internal/autoclose-reaction.d.ts +0 -8
- package/dist/@types/internal/autoclose-reaction.d.ts.map +1 -1
- package/dist/@types/internal/close-cycle.d.ts +45 -3
- package/dist/@types/internal/close-cycle.d.ts.map +1 -1
- package/dist/@types/internal/config.d.ts +0 -6
- package/dist/@types/internal/config.d.ts.map +1 -1
- package/dist/@types/internal/drain-cycle.d.ts +1 -42
- package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts +2 -1
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/index.d.ts +2 -2
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/internal/sensitive.d.ts +0 -21
- package/dist/@types/internal/sensitive.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +15 -27
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +8 -20
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/{chunk-PZP5ZEZ4.js → chunk-QOSQI3CA.js} +2 -2
- package/dist/{chunk-JRTZGWVS.js → chunk-XSG2NW4J.js} +1 -1
- package/dist/{chunk-JRTZGWVS.js.map → chunk-XSG2NW4J.js.map} +1 -1
- package/dist/index.cjs +68 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +70 -112
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +2 -2
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-PZP5ZEZ4.js.map → chunk-QOSQI3CA.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
sleep,
|
|
27
27
|
store,
|
|
28
28
|
validate
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-QOSQI3CA.js";
|
|
30
30
|
import {
|
|
31
31
|
ActorSchema,
|
|
32
32
|
CausationEventSchema,
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
pii_split,
|
|
54
54
|
pii_strip,
|
|
55
55
|
sensitive
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-XSG2NW4J.js";
|
|
57
57
|
|
|
58
58
|
// src/signals.ts
|
|
59
59
|
process.once("SIGINT", async (arg) => {
|
|
@@ -1199,6 +1199,31 @@ var CircuitBreaker = class {
|
|
|
1199
1199
|
};
|
|
1200
1200
|
|
|
1201
1201
|
// src/internal/close-cycle.ts
|
|
1202
|
+
var CLOSE_CATCH_UP_LIMIT = 1e3;
|
|
1203
|
+
var CLOSE_CATCH_UP_PASSES = 20;
|
|
1204
|
+
async function catch_up_correlation(cursor, correlate, until) {
|
|
1205
|
+
for (let pass = 0; pass < CLOSE_CATCH_UP_PASSES && cursor.checkpoint < until; pass++) {
|
|
1206
|
+
const before = cursor.checkpoint;
|
|
1207
|
+
cursor.arm();
|
|
1208
|
+
await correlate(CLOSE_CATCH_UP_LIMIT);
|
|
1209
|
+
if (cursor.checkpoint <= before) break;
|
|
1210
|
+
}
|
|
1211
|
+
return cursor.checkpoint;
|
|
1212
|
+
}
|
|
1213
|
+
var CloseLock = class {
|
|
1214
|
+
_tails = /* @__PURE__ */ new Map();
|
|
1215
|
+
/** Run `work` after every earlier close of `stream` has settled. */
|
|
1216
|
+
run(stream, work) {
|
|
1217
|
+
const prev = this._tails.get(stream) ?? Promise.resolve();
|
|
1218
|
+
const next = prev.then(work, work);
|
|
1219
|
+
this._tails.set(stream, next);
|
|
1220
|
+
const cleanup = () => {
|
|
1221
|
+
if (this._tails.get(stream) === next) this._tails.delete(stream);
|
|
1222
|
+
};
|
|
1223
|
+
next.then(cleanup, cleanup);
|
|
1224
|
+
return next;
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1202
1227
|
var SAFETY_PROBE_PAGE_SIZE = 1e3;
|
|
1203
1228
|
async function run_close_cycle(targets, deps) {
|
|
1204
1229
|
const target_map = new Map(targets.map((t) => [t.stream, t]));
|
|
@@ -1220,6 +1245,8 @@ async function run_close_cycle(targets, deps) {
|
|
|
1220
1245
|
deps,
|
|
1221
1246
|
() => run_full_closes(full, target_map, deps, skipped)
|
|
1222
1247
|
);
|
|
1248
|
+
const retired = [...truncated.entries()].filter(([, r]) => r.committed.name === TOMBSTONE_EVENT).map(([stream]) => stream);
|
|
1249
|
+
if (retired.length) deps.forget_subscribed?.(retired);
|
|
1223
1250
|
for (const [stream, entry] of windowed_result) truncated.set(stream, entry);
|
|
1224
1251
|
return { truncated, skipped };
|
|
1225
1252
|
}
|
|
@@ -1378,12 +1405,12 @@ async function scan_stream_heads(streams) {
|
|
|
1378
1405
|
}
|
|
1379
1406
|
return out;
|
|
1380
1407
|
}
|
|
1381
|
-
async function partition_by_safety(stream_info, reactive_events_size, skipped, page_size,
|
|
1408
|
+
async function partition_by_safety(stream_info, reactive_events_size, skipped, page_size, catch_up_correlation2) {
|
|
1382
1409
|
if (reactive_events_size === 0) return [...stream_info.keys()];
|
|
1383
1410
|
let needed = -1;
|
|
1384
1411
|
for (const info of stream_info.values())
|
|
1385
1412
|
needed = Math.max(needed, info.max_id);
|
|
1386
|
-
const checkpoint = await
|
|
1413
|
+
const checkpoint = await catch_up_correlation2(needed);
|
|
1387
1414
|
const uncorrelated = /* @__PURE__ */ new Set();
|
|
1388
1415
|
for (const [stream, info] of stream_info) {
|
|
1389
1416
|
if (checkpoint < info.max_id) uncorrelated.add(stream);
|
|
@@ -3314,8 +3341,6 @@ function build_handle_batch(logger) {
|
|
|
3314
3341
|
}
|
|
3315
3342
|
|
|
3316
3343
|
// src/act.ts
|
|
3317
|
-
var CLOSE_CATCH_UP_LIMIT = 1e3;
|
|
3318
|
-
var CLOSE_CATCH_UP_PASSES = 20;
|
|
3319
3344
|
function validate_only_lanes(options, lanes) {
|
|
3320
3345
|
if (!options.onlyLanes || options.onlyLanes.length === 0) return;
|
|
3321
3346
|
const declared = /* @__PURE__ */ new Set(["default", ...lanes.map((l) => l.name)]);
|
|
@@ -3468,28 +3493,10 @@ var Act = class {
|
|
|
3468
3493
|
_handle_batch;
|
|
3469
3494
|
/** Declared drain lanes. */
|
|
3470
3495
|
_lanes;
|
|
3471
|
-
/**
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
*/
|
|
3476
|
-
_close_locks = /* @__PURE__ */ new Map();
|
|
3477
|
-
/**
|
|
3478
|
-
* Run `work` under the per-stream close lock. Serializes close
|
|
3479
|
-
* critical sections for the same stream (windowed prunes and full
|
|
3480
|
-
* closes) while letting different streams proceed in parallel.
|
|
3481
|
-
*/
|
|
3482
|
-
_with_close_lock(stream, work) {
|
|
3483
|
-
const prev = this._close_locks.get(stream) ?? Promise.resolve();
|
|
3484
|
-
const next = prev.then(work, work);
|
|
3485
|
-
this._close_locks.set(stream, next);
|
|
3486
|
-
const cleanup = () => {
|
|
3487
|
-
if (this._close_locks.get(stream) === next)
|
|
3488
|
-
this._close_locks.delete(stream);
|
|
3489
|
-
};
|
|
3490
|
-
next.then(cleanup, cleanup);
|
|
3491
|
-
return next;
|
|
3492
|
-
}
|
|
3496
|
+
/** Serializes close critical sections per stream. */
|
|
3497
|
+
_close_lock = new CloseLock();
|
|
3498
|
+
/** Every lane a controller can exist for: `"default"` plus each declared one. */
|
|
3499
|
+
_declared_lanes;
|
|
3493
3500
|
/** Drain lanes declared via `.withLane(...)`. Implicit default not included. */
|
|
3494
3501
|
get lanes() {
|
|
3495
3502
|
return this._lanes;
|
|
@@ -3519,6 +3526,7 @@ var Act = class {
|
|
|
3519
3526
|
this._states = states;
|
|
3520
3527
|
this._batch_handlers = batch_handlers;
|
|
3521
3528
|
this._lanes = lanes;
|
|
3529
|
+
this._declared_lanes = ["default", ...lanes.map((l) => l.name)];
|
|
3522
3530
|
validate_only_lanes(options, lanes);
|
|
3523
3531
|
this._ports = options.scoped ?? default_scope();
|
|
3524
3532
|
const ports = this._ports;
|
|
@@ -3556,7 +3564,7 @@ var Act = class {
|
|
|
3556
3564
|
this._drain = options.drain !== false;
|
|
3557
3565
|
this._breaker = this._build_breaker(options);
|
|
3558
3566
|
this._drain_controllers = this._build_drain_controllers(options, lanes);
|
|
3559
|
-
this._advise_orphaned_lanes(options
|
|
3567
|
+
this._advise_orphaned_lanes(options);
|
|
3560
3568
|
this._audit_deps = this._build_audit_deps();
|
|
3561
3569
|
this._correlate = this._build_correlate(options, classification);
|
|
3562
3570
|
this._settle = this._build_settle(options);
|
|
@@ -3587,7 +3595,7 @@ var Act = class {
|
|
|
3587
3595
|
* to `claim`.
|
|
3588
3596
|
*/
|
|
3589
3597
|
_build_drain_controllers(options, lanes) {
|
|
3590
|
-
const all_lanes =
|
|
3598
|
+
const all_lanes = this._declared_lanes;
|
|
3591
3599
|
const only_set = options.onlyLanes && options.onlyLanes.length > 0 ? new Set(options.onlyLanes) : void 0;
|
|
3592
3600
|
const active_lanes = only_set ? all_lanes.filter((n) => only_set.has(n)) : all_lanes;
|
|
3593
3601
|
const single_default_lane = lanes.length === 0;
|
|
@@ -3605,19 +3613,7 @@ var Act = class {
|
|
|
3605
3613
|
on_blocked: (blocked) => this.emit("blocked", blocked),
|
|
3606
3614
|
// Reaction-requested close: the same machinery as `app.close`.
|
|
3607
3615
|
on_close: async (targets) => {
|
|
3608
|
-
|
|
3609
|
-
const result = await run_close_cycle(targets, {
|
|
3610
|
-
reactive_events_size: this._reactive_events.size,
|
|
3611
|
-
catch_up_correlation: (until) => this._catch_up_correlation(until),
|
|
3612
|
-
event_to_state: this._event_to_state,
|
|
3613
|
-
load: this._es.load,
|
|
3614
|
-
tombstone: this._es.tombstone,
|
|
3615
|
-
logger: this._logger,
|
|
3616
|
-
correlation: close_correlation(this._correlator, close_actor),
|
|
3617
|
-
with_stream_lock: (stream, work) => this._with_close_lock(stream, work)
|
|
3618
|
-
});
|
|
3619
|
-
this._forget_closed_subscriptions(result);
|
|
3620
|
-
this.emit("closed", result);
|
|
3616
|
+
await this._run_close(targets);
|
|
3621
3617
|
},
|
|
3622
3618
|
breaker: this._breaker,
|
|
3623
3619
|
run_scoped: this._scoped,
|
|
@@ -3638,12 +3634,10 @@ var Act = class {
|
|
|
3638
3634
|
* controller here. One process can't check that the cluster covers every
|
|
3639
3635
|
* lane, so it reports its own gaps.
|
|
3640
3636
|
*/
|
|
3641
|
-
_advise_orphaned_lanes(options
|
|
3637
|
+
_advise_orphaned_lanes(options) {
|
|
3642
3638
|
if (!options.onlyLanes || options.onlyLanes.length === 0) return;
|
|
3643
3639
|
const active = new Set(this._drain_controllers.keys());
|
|
3644
|
-
const orphaned =
|
|
3645
|
-
(name) => !active.has(name)
|
|
3646
|
-
);
|
|
3640
|
+
const orphaned = this._declared_lanes.filter((name) => !active.has(name));
|
|
3647
3641
|
if (orphaned.length === 0) return;
|
|
3648
3642
|
const list = orphaned.map((name) => `"${name}"`).join(", ");
|
|
3649
3643
|
this._logger.info(
|
|
@@ -3659,7 +3653,7 @@ var Act = class {
|
|
|
3659
3653
|
states: this._states,
|
|
3660
3654
|
// Declared lanes, not this process's controllers: a lane another
|
|
3661
3655
|
// worker drains is not `unknown-lane`.
|
|
3662
|
-
declared_lanes:
|
|
3656
|
+
declared_lanes: new Set(this._declared_lanes),
|
|
3663
3657
|
routed_events: new Set(this._event_to_lanes.keys())
|
|
3664
3658
|
};
|
|
3665
3659
|
}
|
|
@@ -3676,10 +3670,7 @@ var Act = class {
|
|
|
3676
3670
|
max_subscribed_streams: options.maxSubscribedStreams ?? DEFAULT_MAX_SUBSCRIBED_STREAMS,
|
|
3677
3671
|
// Declared lanes (not active ones): a lane another process claims is
|
|
3678
3672
|
// still a valid destination.
|
|
3679
|
-
declared_lanes:
|
|
3680
|
-
"default",
|
|
3681
|
-
...this._lanes.map((l) => l.name)
|
|
3682
|
-
]),
|
|
3673
|
+
declared_lanes: new Set(this._declared_lanes),
|
|
3683
3674
|
on_init: () => {
|
|
3684
3675
|
if (this._drain && this._reactive_events.size > 0) this._arm_all();
|
|
3685
3676
|
},
|
|
@@ -4569,6 +4560,31 @@ var Act = class {
|
|
|
4569
4560
|
async prioritize(filter, priority) {
|
|
4570
4561
|
return this._scoped(() => store().prioritize(filter, priority));
|
|
4571
4562
|
}
|
|
4563
|
+
/**
|
|
4564
|
+
* Run one close: the cycle with this Act's ports, then emit `closed`. Shared by `close()` and the drain's
|
|
4565
|
+
* reaction-requested closes. The close actor lets a custom correlator
|
|
4566
|
+
* still tag tenant context or trace ids.
|
|
4567
|
+
*/
|
|
4568
|
+
async _run_close(targets) {
|
|
4569
|
+
const close_actor = { id: "$close", name: "close" };
|
|
4570
|
+
const result = await run_close_cycle(targets, {
|
|
4571
|
+
reactive_events_size: this._reactive_events.size,
|
|
4572
|
+
catch_up_correlation: (until) => catch_up_correlation(
|
|
4573
|
+
this._correlate,
|
|
4574
|
+
(limit) => this.correlate({ limit }),
|
|
4575
|
+
until
|
|
4576
|
+
),
|
|
4577
|
+
event_to_state: this._event_to_state,
|
|
4578
|
+
load: this._es.load,
|
|
4579
|
+
tombstone: this._es.tombstone,
|
|
4580
|
+
logger: this._logger,
|
|
4581
|
+
correlation: close_correlation(this._correlator, close_actor),
|
|
4582
|
+
with_stream_lock: (stream, work) => this._close_lock.run(stream, work),
|
|
4583
|
+
forget_subscribed: (streams) => this._correlate.forget_subscribed(streams)
|
|
4584
|
+
});
|
|
4585
|
+
this.emit("closed", result);
|
|
4586
|
+
return result;
|
|
4587
|
+
}
|
|
4572
4588
|
/**
|
|
4573
4589
|
* Close the books — guard, archive, truncate, and optionally restart streams.
|
|
4574
4590
|
*
|
|
@@ -4627,69 +4643,11 @@ var Act = class {
|
|
|
4627
4643
|
* ]);
|
|
4628
4644
|
* ```
|
|
4629
4645
|
*/
|
|
4630
|
-
/**
|
|
4631
|
-
* After a close, forget any target whose subscription row the truncate
|
|
4632
|
-
* removed, so a later correlate can re-subscribe it. Restart
|
|
4633
|
-
* targets keep their row, so only fully-retired streams are forgotten.
|
|
4634
|
-
*/
|
|
4635
|
-
/**
|
|
4636
|
-
* Advance correlation until the read cursor reaches `until`, and report
|
|
4637
|
-
* where it landed. Used by the close cycle's safety probe, which
|
|
4638
|
-
* cannot judge a subscription's pending work over events correlate has
|
|
4639
|
-
* not resolved yet.
|
|
4640
|
-
*
|
|
4641
|
-
* Bounded on both ends: it stops as soon as a pass makes no progress (the
|
|
4642
|
-
* log has no more to give), and after {@link CLOSE_CATCH_UP_PASSES}
|
|
4643
|
-
* windows, so a close behind an enormous backlog degrades to skipping the
|
|
4644
|
-
* stream — the documented retryable outcome — instead of scanning the
|
|
4645
|
-
* whole log inside an operator call.
|
|
4646
|
-
*/
|
|
4647
|
-
async _catch_up_correlation(until) {
|
|
4648
|
-
for (let pass = 0; pass < CLOSE_CATCH_UP_PASSES && this._correlate.checkpoint < until; pass++) {
|
|
4649
|
-
const before = this._correlate.checkpoint;
|
|
4650
|
-
this._correlate.arm();
|
|
4651
|
-
await this.correlate({ limit: CLOSE_CATCH_UP_LIMIT });
|
|
4652
|
-
if (this._correlate.checkpoint <= before) break;
|
|
4653
|
-
}
|
|
4654
|
-
return this._correlate.checkpoint;
|
|
4655
|
-
}
|
|
4656
|
-
/**
|
|
4657
|
-
* Drop retired streams from correlate's in-process "already subscribed"
|
|
4658
|
-
* set, so a later scan re-issues `subscribe()` for them.
|
|
4659
|
-
*
|
|
4660
|
-
* A tombstone seed means the stream was retired; a snapshot seed means it
|
|
4661
|
-
* was restarted and is still consuming.
|
|
4662
|
-
*
|
|
4663
|
-
* `truncate` no longer removes the subscription row, so this is no
|
|
4664
|
-
* longer repairing damage the close itself did. It still matters, because
|
|
4665
|
-
* the row can disappear later: reclaiming retired subscriptions is an
|
|
4666
|
-
* operator job now, and that `DELETE` can land while this process is
|
|
4667
|
-
* running. Forgetting here keeps the in-process view from outliving a row
|
|
4668
|
-
* an operator removed, which is a silent-no-delivery failure — a reaction whose target is named after the stream would never
|
|
4669
|
-
* be re-registered.
|
|
4670
|
-
*/
|
|
4671
|
-
_forget_closed_subscriptions(result) {
|
|
4672
|
-
const retired = [...result.truncated.entries()].filter(([, r]) => r.committed.name === TOMBSTONE_EVENT).map(([stream]) => stream);
|
|
4673
|
-
if (retired.length) this._correlate.forget_subscribed(retired);
|
|
4674
|
-
}
|
|
4675
4646
|
async close(targets) {
|
|
4676
4647
|
if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
|
|
4677
4648
|
return this._scoped(async () => {
|
|
4678
|
-
await this.correlate({ limit:
|
|
4679
|
-
|
|
4680
|
-
const result = await run_close_cycle(targets, {
|
|
4681
|
-
reactive_events_size: this._reactive_events.size,
|
|
4682
|
-
catch_up_correlation: (until) => this._catch_up_correlation(until),
|
|
4683
|
-
event_to_state: this._event_to_state,
|
|
4684
|
-
load: this._es.load,
|
|
4685
|
-
tombstone: this._es.tombstone,
|
|
4686
|
-
logger: this._logger,
|
|
4687
|
-
correlation: close_correlation(this._correlator, close_actor),
|
|
4688
|
-
with_stream_lock: (stream, work) => this._with_close_lock(stream, work)
|
|
4689
|
-
});
|
|
4690
|
-
this._forget_closed_subscriptions(result);
|
|
4691
|
-
this.emit("closed", result);
|
|
4692
|
-
return result;
|
|
4649
|
+
await this.correlate({ limit: CLOSE_CATCH_UP_LIMIT });
|
|
4650
|
+
return this._run_close(targets);
|
|
4693
4651
|
});
|
|
4694
4652
|
}
|
|
4695
4653
|
/**
|