@rotorsoft/act 1.23.32 → 1.23.34
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 +6 -0
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/internal/close-cycle.d.ts.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +18 -0
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/{chunk-5B45U2IS.js → chunk-TTOT5CRM.js} +5 -2
- package/dist/{chunk-5B45U2IS.js.map → chunk-TTOT5CRM.js.map} +1 -1
- package/dist/index.cjs +59 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +56 -4
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +4 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1418,8 +1418,11 @@ var InMemoryStore = class {
|
|
|
1418
1418
|
}
|
|
1419
1419
|
}
|
|
1420
1420
|
this._events = this._events.filter((e) => !stream_set.has(e.stream));
|
|
1421
|
+
const restarted = new Set(
|
|
1422
|
+
full.filter((t) => t.snapshot !== void 0).map((t) => t.stream)
|
|
1423
|
+
);
|
|
1421
1424
|
for (const stream of stream_set) {
|
|
1422
|
-
this._streams.delete(stream);
|
|
1425
|
+
if (!restarted.has(stream)) this._streams.delete(stream);
|
|
1423
1426
|
this._stream_versions.delete(stream);
|
|
1424
1427
|
this._max_event_id_by_stream.delete(stream);
|
|
1425
1428
|
this._pii.delete(stream);
|
|
@@ -2952,14 +2955,19 @@ async function scan_stream_heads(streams) {
|
|
|
2952
2955
|
const stats = await store2().query_stats(streams, {
|
|
2953
2956
|
exclude: [SNAP_EVENT]
|
|
2954
2957
|
});
|
|
2958
|
+
const domain_heads = await store2().query_stats(streams, {
|
|
2959
|
+
exclude: [SNAP_EVENT, TOMBSTONE_EVENT]
|
|
2960
|
+
});
|
|
2955
2961
|
const true_heads = await store2().query_stats(streams, {});
|
|
2956
2962
|
const out = /* @__PURE__ */ new Map();
|
|
2957
|
-
for (const [stream, { head }] of
|
|
2958
|
-
|
|
2963
|
+
for (const [stream, { head }] of domain_heads) {
|
|
2964
|
+
const marker_head = stats.get(stream)?.head;
|
|
2965
|
+
const interrupted = marker_head?.name === TOMBSTONE_EVENT;
|
|
2959
2966
|
out.set(stream, {
|
|
2960
2967
|
max_id: head.id,
|
|
2961
2968
|
version: true_heads.get(stream).head.version,
|
|
2962
|
-
last_event_name: head.name
|
|
2969
|
+
last_event_name: head.name,
|
|
2970
|
+
...interrupted ? { resumed_guard: { id: marker_head.id } } : {}
|
|
2963
2971
|
});
|
|
2964
2972
|
}
|
|
2965
2973
|
return out;
|
|
@@ -3008,6 +3016,11 @@ async function guard_with_tombstones(safe, stream_info, correlation, tombstone2,
|
|
|
3008
3016
|
await Promise.all(
|
|
3009
3017
|
safe.map(async (stream) => {
|
|
3010
3018
|
const info = stream_info.get(stream);
|
|
3019
|
+
if (info.resumed_guard) {
|
|
3020
|
+
guarded.push(stream);
|
|
3021
|
+
guard_events.set(stream, { id: info.resumed_guard.id, stream });
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3011
3024
|
const committed = await tombstone2(stream, info.version, correlation);
|
|
3012
3025
|
if (committed) {
|
|
3013
3026
|
guarded.push(stream);
|
|
@@ -3085,6 +3098,8 @@ var DEFAULT_COLD_START_BACK_SCAN = 1e4;
|
|
|
3085
3098
|
var CorrelateCycle = class {
|
|
3086
3099
|
_checkpoint = -1;
|
|
3087
3100
|
_initialized = false;
|
|
3101
|
+
/** In-flight init, memoized for single-flight and cleared on failure. */
|
|
3102
|
+
_init_promise;
|
|
3088
3103
|
_timer = void 0;
|
|
3089
3104
|
// target → the priority it was last subscribed at. A dynamic target is
|
|
3090
3105
|
// re-subscribed (so the store's `GREATEST` upsert runs) only when a later
|
|
@@ -3167,7 +3182,16 @@ var CorrelateCycle = class {
|
|
|
3167
3182
|
*/
|
|
3168
3183
|
async init() {
|
|
3169
3184
|
if (this._initialized) return;
|
|
3185
|
+
if (!this._init_promise) {
|
|
3186
|
+
this._init_promise = this._run_init().catch((error) => {
|
|
3187
|
+
this._init_promise = void 0;
|
|
3188
|
+
throw error;
|
|
3189
|
+
});
|
|
3190
|
+
}
|
|
3191
|
+
await this._init_promise;
|
|
3170
3192
|
this._initialized = true;
|
|
3193
|
+
}
|
|
3194
|
+
async _run_init() {
|
|
3171
3195
|
const { watermark } = await store2().subscribe([...this._static_targets]);
|
|
3172
3196
|
this._checkpoint = this._has_dynamic_resolvers ? Math.max(-1, watermark - this._cold_start_back_scan) : watermark;
|
|
3173
3197
|
this._on_init?.();
|
|
@@ -3176,6 +3200,26 @@ var CorrelateCycle = class {
|
|
|
3176
3200
|
}
|
|
3177
3201
|
await this._on_init_async?.();
|
|
3178
3202
|
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Forget targets whose subscription rows no longer exist, so a later
|
|
3205
|
+
* scan can re-subscribe them.
|
|
3206
|
+
*
|
|
3207
|
+
* A full close deletes the closed stream's subscription row. The
|
|
3208
|
+
* in-process dedup would otherwise still believe the target is
|
|
3209
|
+
* subscribed and never re-issue `subscribe()`, silently stopping
|
|
3210
|
+
* delivery for any reaction whose target is named after the stream —
|
|
3211
|
+
* the documented per-aggregate shape `.to(e => ({target: e.stream}))`
|
|
3212
|
+
* makes those two namespaces collide by construction (#1398).
|
|
3213
|
+
*
|
|
3214
|
+
* Static targets are left alone: they are subscribed once at init and
|
|
3215
|
+
* recorded at +Infinity so the dynamic path never re-opens them.
|
|
3216
|
+
*/
|
|
3217
|
+
forget_subscribed(streams) {
|
|
3218
|
+
const statics = new Set(this._static_targets.map((t) => t.stream));
|
|
3219
|
+
for (const stream of streams) {
|
|
3220
|
+
if (!statics.has(stream)) this._subscribed.delete(stream);
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3179
3223
|
/**
|
|
3180
3224
|
* Discover dynamic-resolver targets in the events past the checkpoint
|
|
3181
3225
|
* and register any new streams via `cd.subscribe`. Static targets are
|
|
@@ -4798,6 +4842,7 @@ var Act = class {
|
|
|
4798
4842
|
correlation: close_correlation(this._correlator, close_actor),
|
|
4799
4843
|
with_stream_lock: (stream, work) => this._with_close_lock(stream, work)
|
|
4800
4844
|
});
|
|
4845
|
+
this._forget_closed_subscriptions(result);
|
|
4801
4846
|
try {
|
|
4802
4847
|
this.emit("closed", result);
|
|
4803
4848
|
} catch (err) {
|
|
@@ -5792,6 +5837,15 @@ var Act = class {
|
|
|
5792
5837
|
* ]);
|
|
5793
5838
|
* ```
|
|
5794
5839
|
*/
|
|
5840
|
+
/**
|
|
5841
|
+
* After a close, forget any target whose subscription row the truncate
|
|
5842
|
+
* removed, so a later correlate can re-subscribe it (#1398). Restart
|
|
5843
|
+
* targets keep their row, so only fully-retired streams are forgotten.
|
|
5844
|
+
*/
|
|
5845
|
+
_forget_closed_subscriptions(result) {
|
|
5846
|
+
const retired = [...result.truncated.entries()].filter(([, r]) => r.committed.name === TOMBSTONE_EVENT).map(([stream]) => stream);
|
|
5847
|
+
if (retired.length) this._correlate.forget_subscribed(retired);
|
|
5848
|
+
}
|
|
5795
5849
|
async close(targets) {
|
|
5796
5850
|
if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
|
|
5797
5851
|
return this._scoped(async () => {
|
|
@@ -5806,6 +5860,7 @@ var Act = class {
|
|
|
5806
5860
|
correlation: close_correlation(this._correlator, close_actor),
|
|
5807
5861
|
with_stream_lock: (stream, work) => this._with_close_lock(stream, work)
|
|
5808
5862
|
});
|
|
5863
|
+
this._forget_closed_subscriptions(result);
|
|
5809
5864
|
this.emit("closed", result);
|
|
5810
5865
|
return result;
|
|
5811
5866
|
});
|