@rotorsoft/act 1.33.7 → 1.33.9
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 +0 -8
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +11 -7
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/@types/internal/drain-cycle.d.ts +7 -2
- package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
- package/dist/@types/internal/reaction-dispatch.d.ts.map +1 -1
- package/dist/index.cjs +164 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +80 -60
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1162,8 +1162,14 @@ var CorrelateCycle = class {
|
|
|
1162
1162
|
_key;
|
|
1163
1163
|
/** Local expiry of the held lease (0 = none); stale only costs a duplicate scan. */
|
|
1164
1164
|
_lease_until = 0;
|
|
1165
|
-
/**
|
|
1166
|
-
|
|
1165
|
+
/**
|
|
1166
|
+
* Armed while `_raised` (bumped by a commit or notification) is ahead of
|
|
1167
|
+
* `_lowered` (the raise count the last short page saw). Starts armed.
|
|
1168
|
+
*/
|
|
1169
|
+
_raised = 1;
|
|
1170
|
+
_lowered = 0;
|
|
1171
|
+
/** Counts releases, so a lease reply that lands after one is dropped. */
|
|
1172
|
+
_releases = 0;
|
|
1167
1173
|
_init_promise;
|
|
1168
1174
|
// Bounded by `maxSubscribedStreams`; static records live apart so eviction
|
|
1169
1175
|
// never drops their +Infinity floor.
|
|
@@ -1174,8 +1180,6 @@ var CorrelateCycle = class {
|
|
|
1174
1180
|
_static_targets;
|
|
1175
1181
|
_cd;
|
|
1176
1182
|
_on_init;
|
|
1177
|
-
/** Runs once after `on_init`; re-seeds defer timers across a restart. */
|
|
1178
|
-
_on_init_async;
|
|
1179
1183
|
_declared_lanes;
|
|
1180
1184
|
/** Report-once keys; per instance because `internal/` holds no module state. */
|
|
1181
1185
|
_reported = /* @__PURE__ */ new Set();
|
|
@@ -1186,8 +1190,7 @@ var CorrelateCycle = class {
|
|
|
1186
1190
|
max_subscribed_streams,
|
|
1187
1191
|
declared_lanes,
|
|
1188
1192
|
shard,
|
|
1189
|
-
on_init
|
|
1190
|
-
on_init_async
|
|
1193
|
+
on_init
|
|
1191
1194
|
}) {
|
|
1192
1195
|
this._key = shard ? `${registry_key(registry.events)}:${[...shard].sort().join(",")}` : registry_key(registry.events);
|
|
1193
1196
|
this._dynamic_subscriptions = new LruMap(max_subscribed_streams);
|
|
@@ -1196,14 +1199,13 @@ var CorrelateCycle = class {
|
|
|
1196
1199
|
this._static_targets = static_targets;
|
|
1197
1200
|
this._cd = cd;
|
|
1198
1201
|
this._on_init = on_init;
|
|
1199
|
-
this._on_init_async = on_init_async;
|
|
1200
1202
|
}
|
|
1201
1203
|
get checkpoint() {
|
|
1202
1204
|
return this._checkpoint;
|
|
1203
1205
|
}
|
|
1204
1206
|
/** A commit (local or remote) may have produced uncorrelated events. */
|
|
1205
1207
|
arm() {
|
|
1206
|
-
this.
|
|
1208
|
+
this._raised++;
|
|
1207
1209
|
}
|
|
1208
1210
|
async init() {
|
|
1209
1211
|
if (this._initialized) return;
|
|
@@ -1221,7 +1223,6 @@ var CorrelateCycle = class {
|
|
|
1221
1223
|
...this._static_targets
|
|
1222
1224
|
]);
|
|
1223
1225
|
this._checkpoint = correlated_at >= 0 ? correlated_at : Math.max(-1, watermark - COLD_START_BACK_SCAN);
|
|
1224
|
-
this._on_init?.();
|
|
1225
1226
|
for (const { stream, priority = 0, lane } of this._static_targets) {
|
|
1226
1227
|
this._static_subscriptions.set(stream, {
|
|
1227
1228
|
floor: Number.POSITIVE_INFINITY,
|
|
@@ -1229,7 +1230,7 @@ var CorrelateCycle = class {
|
|
|
1229
1230
|
lane
|
|
1230
1231
|
});
|
|
1231
1232
|
}
|
|
1232
|
-
await this.
|
|
1233
|
+
await this._on_init?.();
|
|
1233
1234
|
}
|
|
1234
1235
|
/** After `restore` replaced the log: forget everything and cold-start again. */
|
|
1235
1236
|
restart() {
|
|
@@ -1238,7 +1239,7 @@ var CorrelateCycle = class {
|
|
|
1238
1239
|
this._checkpoint = -1;
|
|
1239
1240
|
this._dynamic_subscriptions.clear();
|
|
1240
1241
|
this._static_subscriptions.clear();
|
|
1241
|
-
this.
|
|
1242
|
+
this._raised++;
|
|
1242
1243
|
}
|
|
1243
1244
|
/** Forget dynamic targets whose rows a full close deleted, so they re-subscribe. */
|
|
1244
1245
|
forget_subscribed(streams) {
|
|
@@ -1258,22 +1259,26 @@ var CorrelateCycle = class {
|
|
|
1258
1259
|
/** Scan past the checkpoint, subscribe new targets and raise their work marks. */
|
|
1259
1260
|
async correlate(query = { after: -1, limit: 10 }, lease = false) {
|
|
1260
1261
|
await this.init();
|
|
1261
|
-
if (
|
|
1262
|
+
if (this._raised === this._lowered)
|
|
1262
1263
|
return {
|
|
1263
1264
|
subscribed: 0,
|
|
1264
1265
|
last_id: this._checkpoint,
|
|
1265
1266
|
marked: 0,
|
|
1266
1267
|
scanned: false
|
|
1267
1268
|
};
|
|
1269
|
+
const raised = this._raised;
|
|
1268
1270
|
const now = Date.now();
|
|
1269
1271
|
if (lease && now >= this._lease_until - CORRELATION_LEASE_MS / 2) {
|
|
1272
|
+
const releases = this._releases;
|
|
1270
1273
|
const { correlating } = await this._cd.subscribe([], void 0, {
|
|
1271
1274
|
key: this._key,
|
|
1272
1275
|
by: this._by,
|
|
1273
1276
|
millis: CORRELATION_LEASE_MS
|
|
1274
1277
|
});
|
|
1275
|
-
|
|
1276
|
-
if (
|
|
1278
|
+
const released = this._releases !== releases;
|
|
1279
|
+
if (!released)
|
|
1280
|
+
this._lease_until = correlating ? now + CORRELATION_LEASE_MS : 0;
|
|
1281
|
+
if (correlating === false || released)
|
|
1277
1282
|
return {
|
|
1278
1283
|
subscribed: 0,
|
|
1279
1284
|
last_id: this._checkpoint,
|
|
@@ -1394,7 +1399,7 @@ var CorrelateCycle = class {
|
|
|
1394
1399
|
return { subscribed, last_id, marked, scanned: true };
|
|
1395
1400
|
}
|
|
1396
1401
|
this._checkpoint = last_id;
|
|
1397
|
-
|
|
1402
|
+
if (!stopped && found !== query.limit) this._lowered = raised;
|
|
1398
1403
|
return { subscribed: 0, last_id, marked: 0, scanned: true };
|
|
1399
1404
|
}
|
|
1400
1405
|
/**
|
|
@@ -1440,6 +1445,7 @@ var CorrelateCycle = class {
|
|
|
1440
1445
|
async release_correlation() {
|
|
1441
1446
|
try {
|
|
1442
1447
|
this._lease_until = 0;
|
|
1448
|
+
this._releases++;
|
|
1443
1449
|
await this._cd.subscribe([], void 0, {
|
|
1444
1450
|
key: this._key,
|
|
1445
1451
|
by: this._by,
|
|
@@ -2353,8 +2359,13 @@ var EMPTY_DRAIN = {
|
|
|
2353
2359
|
};
|
|
2354
2360
|
var RETRY = "";
|
|
2355
2361
|
var DrainController = class {
|
|
2356
|
-
|
|
2357
|
-
|
|
2362
|
+
/**
|
|
2363
|
+
* Armed while `_raised` is ahead of `_lowered` (the raise count the last
|
|
2364
|
+
* empty claim saw), so a claim never lowers over a raise that landed
|
|
2365
|
+
* during it.
|
|
2366
|
+
*/
|
|
2367
|
+
_raised = 0;
|
|
2368
|
+
_lowered = 0;
|
|
2358
2369
|
_ratio = 0.5;
|
|
2359
2370
|
/**
|
|
2360
2371
|
* Local wake at the earliest parked (backoff or defer) visit, or at the
|
|
@@ -2364,7 +2375,7 @@ var DrainController = class {
|
|
|
2364
2375
|
* stays until handled or until a later pass claims nothing.
|
|
2365
2376
|
*/
|
|
2366
2377
|
_defer = new DeferTimer(() => {
|
|
2367
|
-
this.
|
|
2378
|
+
this.arm();
|
|
2368
2379
|
void this._deps.run_scoped(async () => {
|
|
2369
2380
|
while ((await this.drain()).leased.length > 0) ;
|
|
2370
2381
|
});
|
|
@@ -2380,7 +2391,7 @@ var DrainController = class {
|
|
|
2380
2391
|
this._deps = deps;
|
|
2381
2392
|
}
|
|
2382
2393
|
arm() {
|
|
2383
|
-
this.
|
|
2394
|
+
this._raised++;
|
|
2384
2395
|
}
|
|
2385
2396
|
/** Cold start: re-seed a persisted `deferred_at` into the local wake. */
|
|
2386
2397
|
seed_defer(stream, at) {
|
|
@@ -2388,7 +2399,7 @@ var DrainController = class {
|
|
|
2388
2399
|
this._defer.schedule();
|
|
2389
2400
|
}
|
|
2390
2401
|
get armed() {
|
|
2391
|
-
return this.
|
|
2402
|
+
return this._raised !== this._lowered;
|
|
2392
2403
|
}
|
|
2393
2404
|
get inflight() {
|
|
2394
2405
|
return this._inflight;
|
|
@@ -2405,7 +2416,7 @@ var DrainController = class {
|
|
|
2405
2416
|
if (this._worker || this._stopped) return;
|
|
2406
2417
|
const run = this._deps.run_scoped;
|
|
2407
2418
|
const tick = async () => {
|
|
2408
|
-
if (this.
|
|
2419
|
+
if (this.armed) await run(() => this.drain());
|
|
2409
2420
|
if (this._stopped) return;
|
|
2410
2421
|
this._worker = setTimeout(tick, cycleMs);
|
|
2411
2422
|
this._worker.unref();
|
|
@@ -2422,8 +2433,8 @@ var DrainController = class {
|
|
|
2422
2433
|
this._defer.stop();
|
|
2423
2434
|
}
|
|
2424
2435
|
async drain(options = {}) {
|
|
2425
|
-
if (!this.
|
|
2426
|
-
|
|
2436
|
+
if (!this.armed || this._stopped || this._inflight)
|
|
2437
|
+
return EMPTY_DRAIN;
|
|
2427
2438
|
const d = this._deps.defaults ?? {};
|
|
2428
2439
|
const streamLimit = d.streamLimit ?? options.streamLimit ?? DEFAULT_STREAM_LIMIT;
|
|
2429
2440
|
const eventLimit = options.eventLimit ?? DEFAULT_EVENT_LIMIT;
|
|
@@ -2431,13 +2442,13 @@ var DrainController = class {
|
|
|
2431
2442
|
if (this._deps.breaker.state(Date.now()) === "open")
|
|
2432
2443
|
return EMPTY_DRAIN;
|
|
2433
2444
|
try {
|
|
2434
|
-
this._locked = true;
|
|
2435
2445
|
this._inflight = new Promise((done) => {
|
|
2436
2446
|
this._inflight_done = done;
|
|
2437
2447
|
});
|
|
2438
2448
|
const lagging = Math.ceil(streamLimit * this._ratio);
|
|
2439
2449
|
const leading = streamLimit - lagging;
|
|
2440
2450
|
const started = Date.now();
|
|
2451
|
+
const raised = this._raised;
|
|
2441
2452
|
const cycle = await run_drain_cycle(this._deps, {
|
|
2442
2453
|
lagging,
|
|
2443
2454
|
leading,
|
|
@@ -2449,7 +2460,7 @@ var DrainController = class {
|
|
|
2449
2460
|
if (!cycle) {
|
|
2450
2461
|
this._defer.prune(started);
|
|
2451
2462
|
this._deps.breaker.passed();
|
|
2452
|
-
this.
|
|
2463
|
+
this._lowered = raised;
|
|
2453
2464
|
return EMPTY_DRAIN;
|
|
2454
2465
|
}
|
|
2455
2466
|
const { leased, fetched, handled, acked, blocked, closeable } = cycle;
|
|
@@ -2474,7 +2485,6 @@ var DrainController = class {
|
|
|
2474
2485
|
return EMPTY_DRAIN;
|
|
2475
2486
|
} finally {
|
|
2476
2487
|
if (!this._stopped) this._defer.schedule();
|
|
2477
|
-
this._locked = false;
|
|
2478
2488
|
this._inflight = void 0;
|
|
2479
2489
|
this._inflight_done?.();
|
|
2480
2490
|
this._inflight_done = void 0;
|
|
@@ -2716,13 +2726,23 @@ function build_handle(deps) {
|
|
|
2716
2726
|
handled++;
|
|
2717
2727
|
}
|
|
2718
2728
|
} catch (error) {
|
|
2719
|
-
if (error instanceof DeferSignal)
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2729
|
+
if (error instanceof DeferSignal) {
|
|
2730
|
+
let defer;
|
|
2731
|
+
try {
|
|
2732
|
+
defer = resolve_defer_at(error.when, event);
|
|
2733
|
+
} catch (invalid) {
|
|
2734
|
+
return finalize(
|
|
2735
|
+
lease,
|
|
2736
|
+
handled,
|
|
2737
|
+
at,
|
|
2738
|
+
invalid,
|
|
2739
|
+
payload.options,
|
|
2740
|
+
logger,
|
|
2741
|
+
event.id
|
|
2742
|
+
);
|
|
2743
|
+
}
|
|
2744
|
+
return { lease, handled, acked_at: at, defer };
|
|
2745
|
+
}
|
|
2726
2746
|
if (error instanceof CloseSignal)
|
|
2727
2747
|
return {
|
|
2728
2748
|
lease,
|
|
@@ -2908,7 +2928,6 @@ function validate_only_lanes(options, lanes) {
|
|
|
2908
2928
|
var Act = class {
|
|
2909
2929
|
_emitter = new EventEmitter();
|
|
2910
2930
|
_breaker;
|
|
2911
|
-
_listen;
|
|
2912
2931
|
_drain;
|
|
2913
2932
|
_auto_settle;
|
|
2914
2933
|
/** Whether any event has a reaction; nothing arms without one. */
|
|
@@ -2989,14 +3008,6 @@ var Act = class {
|
|
|
2989
3008
|
/** Runs a body so internal `store()`/`cache()` calls resolve to `_ports`. */
|
|
2990
3009
|
_scoped;
|
|
2991
3010
|
_correlator;
|
|
2992
|
-
// Bound once, reused by every reaction payload.
|
|
2993
|
-
_bound_do = this.do.bind(this);
|
|
2994
|
-
_bound_load = this.load.bind(this);
|
|
2995
|
-
_bound_query = this.query.bind(this);
|
|
2996
|
-
_bound_query_array = this.query_array.bind(this);
|
|
2997
|
-
_bound_forget = this.forget.bind(this);
|
|
2998
|
-
_handle;
|
|
2999
|
-
_handle_batch;
|
|
3000
3011
|
_lanes;
|
|
3001
3012
|
_close_lock = new CloseLock();
|
|
3002
3013
|
/** Every lane a controller can exist for: `"default"` plus each declared one. */
|
|
@@ -3030,31 +3041,38 @@ var Act = class {
|
|
|
3030
3041
|
this._correlator = options.correlator ?? default_correlator;
|
|
3031
3042
|
this._es = build_es(this._logger, this._correlator, patch_fn);
|
|
3032
3043
|
this._cd = build_drain(this._logger);
|
|
3033
|
-
|
|
3044
|
+
const handle = build_handle({
|
|
3034
3045
|
logger: this._logger,
|
|
3035
3046
|
reaction_scope: make_reaction_scope({
|
|
3036
|
-
do: this.
|
|
3037
|
-
load: this.
|
|
3038
|
-
query: this.
|
|
3039
|
-
query_array: this.
|
|
3040
|
-
forget: this.
|
|
3047
|
+
do: this.do.bind(this),
|
|
3048
|
+
load: this.load.bind(this),
|
|
3049
|
+
query: this.query.bind(this),
|
|
3050
|
+
query_array: this.query_array.bind(this),
|
|
3051
|
+
forget: this.forget.bind(this)
|
|
3041
3052
|
})
|
|
3042
3053
|
});
|
|
3043
|
-
|
|
3054
|
+
const handle_batch = build_handle_batch(this._logger);
|
|
3044
3055
|
const classification = classify_registry(this.registry, this._states);
|
|
3045
3056
|
this._reactive = classification.event_to_lanes.size > 0;
|
|
3046
3057
|
this._event_to_state = classification.event_to_state;
|
|
3047
3058
|
this._event_to_lanes = classification.event_to_lanes;
|
|
3048
|
-
this._listen = options.listen !== false;
|
|
3049
3059
|
this._drain = options.drain !== false;
|
|
3050
3060
|
this._auto_settle = options.autoSettle !== false;
|
|
3051
3061
|
this._breaker = this._build_breaker(options);
|
|
3052
|
-
this._drain_controllers = this._build_drain_controllers(
|
|
3062
|
+
this._drain_controllers = this._build_drain_controllers(
|
|
3063
|
+
options,
|
|
3064
|
+
lanes,
|
|
3065
|
+
handle,
|
|
3066
|
+
handle_batch
|
|
3067
|
+
);
|
|
3053
3068
|
this._advise_orphaned_lanes(options);
|
|
3054
3069
|
this._audit_deps = this._build_audit_deps();
|
|
3055
3070
|
this._correlate = this._build_correlate(options, classification);
|
|
3056
3071
|
this._settle = this._build_settle(options);
|
|
3057
|
-
this._notify_disposer = this._wire_notify(
|
|
3072
|
+
this._notify_disposer = this._wire_notify(
|
|
3073
|
+
this._ports.store,
|
|
3074
|
+
options.listen !== false
|
|
3075
|
+
);
|
|
3058
3076
|
register_weak_disposer(new WeakRef(this), (self) => self.shutdown());
|
|
3059
3077
|
}
|
|
3060
3078
|
/** Shared by drain, settle and autoclose; bad knobs throw at build. */
|
|
@@ -3070,7 +3088,7 @@ var Act = class {
|
|
|
3070
3088
|
);
|
|
3071
3089
|
}
|
|
3072
3090
|
/** Apps that declare no lanes pass no lane filter to `claim`. */
|
|
3073
|
-
_build_drain_controllers(options, lanes) {
|
|
3091
|
+
_build_drain_controllers(options, lanes, handle, handle_batch) {
|
|
3074
3092
|
const all_lanes = this._declared_lanes;
|
|
3075
3093
|
const only_set = options.onlyLanes && options.onlyLanes.length > 0 ? new Set(options.onlyLanes) : void 0;
|
|
3076
3094
|
const active_lanes = only_set ? all_lanes.filter((n) => only_set.has(n)) : all_lanes;
|
|
@@ -3083,8 +3101,8 @@ var Act = class {
|
|
|
3083
3101
|
ops: this._cd,
|
|
3084
3102
|
registry: this.registry,
|
|
3085
3103
|
batch_handlers: this._batch_handlers,
|
|
3086
|
-
handle
|
|
3087
|
-
handle_batch
|
|
3104
|
+
handle,
|
|
3105
|
+
handle_batch,
|
|
3088
3106
|
on_acked: (acked) => this.emit("acked", acked),
|
|
3089
3107
|
on_blocked: (blocked) => this.emit("blocked", blocked),
|
|
3090
3108
|
// Reaction-requested close: the same machinery as `app.close`.
|
|
@@ -3149,8 +3167,10 @@ var Act = class {
|
|
|
3149
3167
|
// Built after the controllers, so their keys are the active lanes.
|
|
3150
3168
|
shard: this._drain_controllers.size < this._declared_lanes.length ? [...this._drain_controllers.keys()] : void 0,
|
|
3151
3169
|
// Only reached on a draining app: writer-only ones never correlate.
|
|
3152
|
-
on_init: () =>
|
|
3153
|
-
|
|
3170
|
+
on_init: async () => {
|
|
3171
|
+
this._arm_all();
|
|
3172
|
+
await this._seed_persisted_defers();
|
|
3173
|
+
}
|
|
3154
3174
|
});
|
|
3155
3175
|
}
|
|
3156
3176
|
/** Cold start: an idle deferred stream still fires on time after a restart. */
|
|
@@ -3243,10 +3263,10 @@ var Act = class {
|
|
|
3243
3263
|
}
|
|
3244
3264
|
}
|
|
3245
3265
|
/** Failures are logged, never thrown: `notify` is a hint. */
|
|
3246
|
-
async _wire_notify(s) {
|
|
3266
|
+
async _wire_notify(s, listen) {
|
|
3247
3267
|
if (!this._reactive) return void 0;
|
|
3248
3268
|
if (!s.notify) return void 0;
|
|
3249
|
-
if (!
|
|
3269
|
+
if (!listen) return void 0;
|
|
3250
3270
|
try {
|
|
3251
3271
|
return await s.notify((notification) => {
|
|
3252
3272
|
try {
|
|
@@ -4483,7 +4503,7 @@ function build_events(registry, states, lanes, owners) {
|
|
|
4483
4503
|
throw new Error(
|
|
4484
4504
|
`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`
|
|
4485
4505
|
);
|
|
4486
|
-
if (resolver.lane && !declared.has(resolver.lane))
|
|
4506
|
+
if (resolver.lane !== void 0 && !declared.has(resolver.lane))
|
|
4487
4507
|
lane_errors.push(
|
|
4488
4508
|
`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.`
|
|
4489
4509
|
);
|