@rotorsoft/act 0.37.0 → 0.39.0
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/README.md +54 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +27 -0
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/ports.d.ts +10 -51
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/@types/test/index.d.ts +2 -0
- package/dist/@types/test/index.d.ts.map +1 -0
- package/dist/@types/test/sandbox.d.ts +113 -0
- package/dist/@types/test/sandbox.d.ts.map +1 -0
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-5WRI5ZAA.js.map +1 -0
- package/dist/chunk-RHS57BUR.js +877 -0
- package/dist/chunk-RHS57BUR.js.map +1 -0
- package/dist/dist-INXH4GUE.js +546 -0
- package/dist/dist-INXH4GUE.js.map +1 -0
- package/dist/index.cjs +175 -121
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +160 -933
- package/dist/index.js.map +1 -1
- package/dist/magic-string.es-ZGO77SPS.js +1309 -0
- package/dist/magic-string.es-ZGO77SPS.js.map +1 -0
- package/dist/test/index.cjs +4833 -0
- package/dist/test/index.cjs.map +1 -0
- package/dist/test/index.js +3912 -0
- package/dist/test/index.js.map +1 -0
- package/dist/types/index.js +1 -0
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -55,7 +55,7 @@ __export(index_exports, {
|
|
|
55
55
|
ValidationError: () => ValidationError,
|
|
56
56
|
ZodEmpty: () => ZodEmpty,
|
|
57
57
|
act: () => act,
|
|
58
|
-
cache: () =>
|
|
58
|
+
cache: () => cache2,
|
|
59
59
|
config: () => config,
|
|
60
60
|
dispose: () => dispose,
|
|
61
61
|
disposeAndExit: () => disposeAndExit,
|
|
@@ -63,14 +63,18 @@ __export(index_exports, {
|
|
|
63
63
|
log: () => log,
|
|
64
64
|
port: () => port,
|
|
65
65
|
projection: () => projection,
|
|
66
|
+
scoped: () => scoped,
|
|
66
67
|
sleep: () => sleep,
|
|
67
68
|
slice: () => slice,
|
|
68
69
|
state: () => state,
|
|
69
|
-
store: () =>
|
|
70
|
+
store: () => store2,
|
|
70
71
|
validate: () => validate
|
|
71
72
|
});
|
|
72
73
|
module.exports = __toCommonJS(index_exports);
|
|
73
74
|
|
|
75
|
+
// src/ports.ts
|
|
76
|
+
var import_node_async_hooks = require("async_hooks");
|
|
77
|
+
|
|
74
78
|
// src/adapters/console-logger.ts
|
|
75
79
|
var LEVEL_VALUES = {
|
|
76
80
|
fatal: 60,
|
|
@@ -971,6 +975,7 @@ var InMemoryStore = class {
|
|
|
971
975
|
};
|
|
972
976
|
|
|
973
977
|
// src/ports.ts
|
|
978
|
+
var scoped = new import_node_async_hooks.AsyncLocalStorage();
|
|
974
979
|
var ExitCodes = ["ERROR", "EXIT"];
|
|
975
980
|
var adapters = /* @__PURE__ */ new Map();
|
|
976
981
|
function port(injector) {
|
|
@@ -990,11 +995,17 @@ var log = port(function log2(adapter) {
|
|
|
990
995
|
pretty: cfg.env !== "production"
|
|
991
996
|
});
|
|
992
997
|
});
|
|
993
|
-
var
|
|
994
|
-
return adapter
|
|
998
|
+
var _store = port(function store(adapter) {
|
|
999
|
+
return adapter ?? new InMemoryStore();
|
|
1000
|
+
});
|
|
1001
|
+
var store2 = ((adapter) => {
|
|
1002
|
+
return scoped.getStore()?.store ?? _store(adapter);
|
|
995
1003
|
});
|
|
996
|
-
var
|
|
997
|
-
return adapter
|
|
1004
|
+
var _cache = port(function cache(adapter) {
|
|
1005
|
+
return adapter ?? new InMemoryCache();
|
|
1006
|
+
});
|
|
1007
|
+
var cache2 = ((adapter) => {
|
|
1008
|
+
return scoped.getStore()?.cache ?? _cache(adapter);
|
|
998
1009
|
});
|
|
999
1010
|
var disposers = [];
|
|
1000
1011
|
async function disposeAndExit(code = "EXIT") {
|
|
@@ -1124,7 +1135,7 @@ async function scanStreamHeads(streams) {
|
|
|
1124
1135
|
let maxId = -1;
|
|
1125
1136
|
let version = -1;
|
|
1126
1137
|
let lastEventName = "";
|
|
1127
|
-
await
|
|
1138
|
+
await store2().query(
|
|
1128
1139
|
(e) => {
|
|
1129
1140
|
if (e.name === TOMBSTONE_EVENT || maxId !== -1) return;
|
|
1130
1141
|
maxId = e.id;
|
|
@@ -1141,7 +1152,7 @@ async function scanStreamHeads(streams) {
|
|
|
1141
1152
|
async function partitionBySafety(streamInfo, reactiveEventsSize, skipped) {
|
|
1142
1153
|
if (reactiveEventsSize === 0) return [...streamInfo.keys()];
|
|
1143
1154
|
const pendingSet = /* @__PURE__ */ new Set();
|
|
1144
|
-
await
|
|
1155
|
+
await store2().query_streams((position) => {
|
|
1145
1156
|
const sourceRe = position.source ? RegExp(position.source) : void 0;
|
|
1146
1157
|
for (const [stream, info] of streamInfo) {
|
|
1147
1158
|
if ((!sourceRe || sourceRe.test(stream)) && position.at < info.maxId) {
|
|
@@ -1212,13 +1223,13 @@ async function truncateAndWarmCache(guarded, seedStates, guardEvents, correlatio
|
|
|
1212
1223
|
}
|
|
1213
1224
|
};
|
|
1214
1225
|
});
|
|
1215
|
-
const truncated = await
|
|
1226
|
+
const truncated = await store2().truncate(truncTargets);
|
|
1216
1227
|
await Promise.all(
|
|
1217
1228
|
guarded.map(async (stream) => {
|
|
1218
1229
|
const entry = truncated.get(stream);
|
|
1219
1230
|
const state2 = seedStates.get(stream);
|
|
1220
1231
|
if (state2 && entry) {
|
|
1221
|
-
await
|
|
1232
|
+
await cache2().set(stream, {
|
|
1222
1233
|
state: state2,
|
|
1223
1234
|
version: entry.committed.version,
|
|
1224
1235
|
event_id: entry.committed.id,
|
|
@@ -1226,7 +1237,7 @@ async function truncateAndWarmCache(guarded, seedStates, guardEvents, correlatio
|
|
|
1226
1237
|
snaps: 1
|
|
1227
1238
|
});
|
|
1228
1239
|
} else {
|
|
1229
|
-
await
|
|
1240
|
+
await cache2().invalidate(stream);
|
|
1230
1241
|
}
|
|
1231
1242
|
})
|
|
1232
1243
|
);
|
|
@@ -1261,7 +1272,7 @@ var CorrelateCycle = class {
|
|
|
1261
1272
|
async init() {
|
|
1262
1273
|
if (this._initialized) return;
|
|
1263
1274
|
this._initialized = true;
|
|
1264
|
-
const { watermark } = await
|
|
1275
|
+
const { watermark } = await store2().subscribe([...this.staticTargets]);
|
|
1265
1276
|
this._checkpoint = watermark;
|
|
1266
1277
|
this.onInit?.();
|
|
1267
1278
|
for (const { stream } of this.staticTargets) {
|
|
@@ -1280,7 +1291,7 @@ var CorrelateCycle = class {
|
|
|
1280
1291
|
const after = Math.max(this._checkpoint, query.after || -1);
|
|
1281
1292
|
const correlated = /* @__PURE__ */ new Map();
|
|
1282
1293
|
let last_id = after;
|
|
1283
|
-
await
|
|
1294
|
+
await store2().query(
|
|
1284
1295
|
(event) => {
|
|
1285
1296
|
last_id = event.id;
|
|
1286
1297
|
const register = this.registry.events[event.name];
|
|
@@ -1800,12 +1811,12 @@ var SettleLoop = class {
|
|
|
1800
1811
|
};
|
|
1801
1812
|
|
|
1802
1813
|
// src/internal/drain.ts
|
|
1803
|
-
var claim = (lagging, leading, by, millis) =>
|
|
1814
|
+
var claim = (lagging, leading, by, millis) => store2().claim(lagging, leading, by, millis);
|
|
1804
1815
|
async function fetch(leased, eventLimit) {
|
|
1805
1816
|
return Promise.all(
|
|
1806
1817
|
leased.map(async ({ stream, source, at, lagging }) => {
|
|
1807
1818
|
const events = [];
|
|
1808
|
-
await
|
|
1819
|
+
await store2().query((e) => events.push(e), {
|
|
1809
1820
|
stream: source,
|
|
1810
1821
|
after: at,
|
|
1811
1822
|
limit: eventLimit
|
|
@@ -1814,9 +1825,9 @@ async function fetch(leased, eventLimit) {
|
|
|
1814
1825
|
})
|
|
1815
1826
|
);
|
|
1816
1827
|
}
|
|
1817
|
-
var ack = (leases) =>
|
|
1818
|
-
var block = (leases) =>
|
|
1819
|
-
var subscribe = (streams) =>
|
|
1828
|
+
var ack = (leases) => store2().ack(leases);
|
|
1829
|
+
var block = (leases) => store2().block(leases);
|
|
1830
|
+
var subscribe = (streams) => store2().subscribe(streams);
|
|
1820
1831
|
|
|
1821
1832
|
// src/internal/event-sourcing.ts
|
|
1822
1833
|
var import_node_crypto3 = require("crypto");
|
|
@@ -1824,7 +1835,7 @@ var import_act_patch = require("@rotorsoft/act-patch");
|
|
|
1824
1835
|
async function snap(snapshot) {
|
|
1825
1836
|
try {
|
|
1826
1837
|
const { id, stream, name, meta, version } = snapshot.event;
|
|
1827
|
-
await
|
|
1838
|
+
await store2().commit(
|
|
1828
1839
|
stream,
|
|
1829
1840
|
[{ name: SNAP_EVENT, data: snapshot.state }],
|
|
1830
1841
|
{
|
|
@@ -1840,7 +1851,7 @@ async function snap(snapshot) {
|
|
|
1840
1851
|
}
|
|
1841
1852
|
async function tombstone(stream, expectedVersion, correlation) {
|
|
1842
1853
|
try {
|
|
1843
|
-
const [committed] = await
|
|
1854
|
+
const [committed] = await store2().commit(
|
|
1844
1855
|
stream,
|
|
1845
1856
|
[{ name: TOMBSTONE_EVENT, data: {} }],
|
|
1846
1857
|
{ correlation, causation: {} },
|
|
@@ -1854,7 +1865,7 @@ async function tombstone(stream, expectedVersion, correlation) {
|
|
|
1854
1865
|
}
|
|
1855
1866
|
async function load(me, stream, callback, asOf) {
|
|
1856
1867
|
const timeTravel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
|
|
1857
|
-
const cached = timeTravel ? void 0 : await
|
|
1868
|
+
const cached = timeTravel ? void 0 : await cache2().get(stream);
|
|
1858
1869
|
const cache_hit = !!cached;
|
|
1859
1870
|
let state2 = cached?.state ?? (me.init ? me.init() : {});
|
|
1860
1871
|
let patches = cached?.patches ?? 0;
|
|
@@ -1862,7 +1873,7 @@ async function load(me, stream, callback, asOf) {
|
|
|
1862
1873
|
let version = cached?.version ?? -1;
|
|
1863
1874
|
let replayed = 0;
|
|
1864
1875
|
let event;
|
|
1865
|
-
await
|
|
1876
|
+
await store2().query(
|
|
1866
1877
|
(e) => {
|
|
1867
1878
|
event = e;
|
|
1868
1879
|
version = e.version;
|
|
@@ -1897,7 +1908,7 @@ async function load(me, stream, callback, asOf) {
|
|
|
1897
1908
|
}
|
|
1898
1909
|
);
|
|
1899
1910
|
if (replayed > 0 && !timeTravel && event) {
|
|
1900
|
-
await
|
|
1911
|
+
await cache2().set(stream, {
|
|
1901
1912
|
state: state2,
|
|
1902
1913
|
version,
|
|
1903
1914
|
event_id: event.id,
|
|
@@ -1970,7 +1981,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1970
1981
|
};
|
|
1971
1982
|
let committed;
|
|
1972
1983
|
try {
|
|
1973
|
-
committed = await
|
|
1984
|
+
committed = await store2().commit(
|
|
1974
1985
|
stream,
|
|
1975
1986
|
emitted,
|
|
1976
1987
|
meta,
|
|
@@ -1982,7 +1993,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1982
1993
|
);
|
|
1983
1994
|
} catch (error) {
|
|
1984
1995
|
if (error instanceof ConcurrencyError) {
|
|
1985
|
-
await
|
|
1996
|
+
await cache2().invalidate(stream);
|
|
1986
1997
|
}
|
|
1987
1998
|
throw error;
|
|
1988
1999
|
}
|
|
@@ -2004,7 +2015,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
2004
2015
|
});
|
|
2005
2016
|
const last = snapshots.at(-1);
|
|
2006
2017
|
const snapped = me.snap?.(last);
|
|
2007
|
-
|
|
2018
|
+
cache2().set(stream, {
|
|
2008
2019
|
state: last.state,
|
|
2009
2020
|
version: last.event.version,
|
|
2010
2021
|
event_id: last.event.id,
|
|
@@ -2199,6 +2210,7 @@ var Act = class {
|
|
|
2199
2210
|
this.registry = registry;
|
|
2200
2211
|
this._states = _states;
|
|
2201
2212
|
this._batch_handlers = batchHandlers;
|
|
2213
|
+
this._scoped = options.scoped ? (fn) => scoped.run(options.scoped, fn) : (fn) => fn();
|
|
2202
2214
|
this._es = buildEs(this._logger);
|
|
2203
2215
|
this._cd = buildDrain(this._logger);
|
|
2204
2216
|
this._handle = buildHandle({
|
|
@@ -2244,14 +2256,8 @@ var Act = class {
|
|
|
2244
2256
|
},
|
|
2245
2257
|
options.settleDebounceMs ?? DEFAULT_SETTLE_DEBOUNCE_MS
|
|
2246
2258
|
);
|
|
2247
|
-
this._notify_disposer = this._wireNotify();
|
|
2248
|
-
dispose(
|
|
2249
|
-
this._emitter.removeAllListeners();
|
|
2250
|
-
this.stop_correlations();
|
|
2251
|
-
this.stop_settling();
|
|
2252
|
-
const disposer = await this._notify_disposer;
|
|
2253
|
-
if (disposer) await disposer();
|
|
2254
|
-
});
|
|
2259
|
+
this._notify_disposer = this._wireNotify(options.scoped?.store ?? store2());
|
|
2260
|
+
dispose(() => this.shutdown());
|
|
2255
2261
|
}
|
|
2256
2262
|
_emitter = new import_node_events.default();
|
|
2257
2263
|
/** Event names with at least one registered reaction (computed at build time) */
|
|
@@ -2313,6 +2319,11 @@ var Act = class {
|
|
|
2313
2319
|
_event_to_state;
|
|
2314
2320
|
/** Logger resolved at construction time (after user port configuration) */
|
|
2315
2321
|
_logger = log();
|
|
2322
|
+
/** Wraps a public-method body so internal `store()`/`cache()` resolve to the
|
|
2323
|
+
* per-Act ports (ACT-501). No-op when the Act is unscoped — so the singleton
|
|
2324
|
+
* path keeps reading fresh `store()`/`cache()` per call, which matters for
|
|
2325
|
+
* tests that dispose and re-seed mid-suite. */
|
|
2326
|
+
_scoped;
|
|
2316
2327
|
/** Pre-bound IAct methods reused across drain cycles. Only `do` varies per
|
|
2317
2328
|
* payload (it captures the triggering event for reactingTo auto-inject). */
|
|
2318
2329
|
_bound_do = this.do.bind(this);
|
|
@@ -2322,15 +2333,38 @@ var Act = class {
|
|
|
2322
2333
|
/** Reaction dispatchers built once and handed to runDrainCycle each cycle. */
|
|
2323
2334
|
_handle;
|
|
2324
2335
|
_handle_batch;
|
|
2336
|
+
/** True after the first `shutdown()` call. Guards idempotency. */
|
|
2337
|
+
_shutdown_promise;
|
|
2338
|
+
/**
|
|
2339
|
+
* Per-instance teardown: remove lifecycle listeners, stop the
|
|
2340
|
+
* correlation worker, cancel any pending settle cycle, and tear
|
|
2341
|
+
* down the cross-process notify subscription.
|
|
2342
|
+
*
|
|
2343
|
+
* Idempotent — repeated calls return the same promise. Registered
|
|
2344
|
+
* automatically with the global `dispose()` registry at construction,
|
|
2345
|
+
* so process-wide `dispose()()` covers it; test helpers (or operators
|
|
2346
|
+
* that mint short-lived Acts) call it explicitly for prompt cleanup.
|
|
2347
|
+
*/
|
|
2348
|
+
shutdown() {
|
|
2349
|
+
if (!this._shutdown_promise) {
|
|
2350
|
+
this._shutdown_promise = (async () => {
|
|
2351
|
+
this._emitter.removeAllListeners();
|
|
2352
|
+
this.stop_correlations();
|
|
2353
|
+
this.stop_settling();
|
|
2354
|
+
const disposer = await this._notify_disposer;
|
|
2355
|
+
if (disposer) await disposer();
|
|
2356
|
+
})();
|
|
2357
|
+
}
|
|
2358
|
+
return this._shutdown_promise;
|
|
2359
|
+
}
|
|
2325
2360
|
/**
|
|
2326
2361
|
* Subscribe to {@link Store.notify} when both the store and the
|
|
2327
2362
|
* registry support it. Returns the disposer (or `undefined` when no
|
|
2328
2363
|
* subscription was made). Errors during subscription are logged but
|
|
2329
2364
|
* never thrown — `notify` is a hint, not a contract.
|
|
2330
2365
|
*/
|
|
2331
|
-
async _wireNotify() {
|
|
2366
|
+
async _wireNotify(s) {
|
|
2332
2367
|
if (this._reactive_events.size === 0) return void 0;
|
|
2333
|
-
const s = store();
|
|
2334
2368
|
if (!s.notify) return void 0;
|
|
2335
2369
|
try {
|
|
2336
2370
|
return await s.notify((notification) => {
|
|
@@ -2434,35 +2468,39 @@ var Act = class {
|
|
|
2434
2468
|
* @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
|
|
2435
2469
|
*/
|
|
2436
2470
|
async do(action2, target, payload, reactingTo, skipValidation = false) {
|
|
2437
|
-
|
|
2438
|
-
this.
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
this.
|
|
2449
|
-
|
|
2471
|
+
return this._scoped(async () => {
|
|
2472
|
+
const snapshots = await this._es.action(
|
|
2473
|
+
this.registry.actions[action2],
|
|
2474
|
+
action2,
|
|
2475
|
+
target,
|
|
2476
|
+
payload,
|
|
2477
|
+
reactingTo,
|
|
2478
|
+
skipValidation
|
|
2479
|
+
);
|
|
2480
|
+
if (this._reactive_events.size > 0) {
|
|
2481
|
+
for (const snap2 of snapshots) {
|
|
2482
|
+
if (snap2.event?.name && this._reactive_events.has(snap2.event.name)) {
|
|
2483
|
+
this._drain.arm();
|
|
2484
|
+
break;
|
|
2485
|
+
}
|
|
2450
2486
|
}
|
|
2451
2487
|
}
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2488
|
+
this.emit("committed", snapshots);
|
|
2489
|
+
return snapshots;
|
|
2490
|
+
});
|
|
2455
2491
|
}
|
|
2456
2492
|
async load(stateOrName, stream, callback, asOf) {
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2493
|
+
return this._scoped(async () => {
|
|
2494
|
+
let merged;
|
|
2495
|
+
if (typeof stateOrName === "string") {
|
|
2496
|
+
const found = this._states.get(stateOrName);
|
|
2497
|
+
if (!found) throw new Error(`State "${stateOrName}" not found`);
|
|
2498
|
+
merged = found;
|
|
2499
|
+
} else {
|
|
2500
|
+
merged = this._states.get(stateOrName.name) || stateOrName;
|
|
2501
|
+
}
|
|
2502
|
+
return await this._es.load(merged, stream, callback, asOf);
|
|
2503
|
+
});
|
|
2466
2504
|
}
|
|
2467
2505
|
/**
|
|
2468
2506
|
* Queries the event store for events matching a filter.
|
|
@@ -2511,14 +2549,16 @@ var Act = class {
|
|
|
2511
2549
|
* @see {@link query_array} for loading events into memory
|
|
2512
2550
|
*/
|
|
2513
2551
|
async query(query, callback) {
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2552
|
+
return this._scoped(async () => {
|
|
2553
|
+
let first;
|
|
2554
|
+
let last;
|
|
2555
|
+
const count = await store2().query((e) => {
|
|
2556
|
+
if (!first) first = e;
|
|
2557
|
+
last = e;
|
|
2558
|
+
callback?.(e);
|
|
2559
|
+
}, query);
|
|
2560
|
+
return { first, last, count };
|
|
2561
|
+
});
|
|
2522
2562
|
}
|
|
2523
2563
|
/**
|
|
2524
2564
|
* Queries the event store and returns all matching events in memory.
|
|
@@ -2547,9 +2587,11 @@ var Act = class {
|
|
|
2547
2587
|
* @see {@link query} for large result sets
|
|
2548
2588
|
*/
|
|
2549
2589
|
async query_array(query) {
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2590
|
+
return this._scoped(async () => {
|
|
2591
|
+
const events = [];
|
|
2592
|
+
await store2().query((e) => events.push(e), query);
|
|
2593
|
+
return events;
|
|
2594
|
+
});
|
|
2553
2595
|
}
|
|
2554
2596
|
/**
|
|
2555
2597
|
* Processes pending reactions by draining uncommitted events from the event store.
|
|
@@ -2589,7 +2631,7 @@ var Act = class {
|
|
|
2589
2631
|
* @see {@link start_correlations} for automatic correlation
|
|
2590
2632
|
*/
|
|
2591
2633
|
async drain(options = {}) {
|
|
2592
|
-
return this._drain.drain(options);
|
|
2634
|
+
return this._scoped(() => this._drain.drain(options));
|
|
2593
2635
|
}
|
|
2594
2636
|
/**
|
|
2595
2637
|
* Discovers and registers new streams dynamically based on reaction resolvers.
|
|
@@ -2637,7 +2679,7 @@ var Act = class {
|
|
|
2637
2679
|
* @see {@link stop_correlations} to stop automatic correlation
|
|
2638
2680
|
*/
|
|
2639
2681
|
async correlate(query = { after: -1, limit: 10 }) {
|
|
2640
|
-
return this._correlate.correlate(query);
|
|
2682
|
+
return this._scoped(() => this._correlate.correlate(query));
|
|
2641
2683
|
}
|
|
2642
2684
|
/**
|
|
2643
2685
|
* Starts automatic periodic correlation worker for discovering new streams.
|
|
@@ -2758,9 +2800,11 @@ var Act = class {
|
|
|
2758
2800
|
* @see {@link settle} for the debounced full-catch-up loop
|
|
2759
2801
|
*/
|
|
2760
2802
|
async reset(streams) {
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2803
|
+
return this._scoped(async () => {
|
|
2804
|
+
const count = await store2().reset(streams);
|
|
2805
|
+
if (count > 0 && this._reactive_events.size > 0) this._drain.arm();
|
|
2806
|
+
return count;
|
|
2807
|
+
});
|
|
2764
2808
|
}
|
|
2765
2809
|
/**
|
|
2766
2810
|
* Bulk-update scheduling priority for streams matching `filter`.
|
|
@@ -2801,7 +2845,7 @@ var Act = class {
|
|
|
2801
2845
|
* @see {@link claim} for how priority biases scheduling
|
|
2802
2846
|
*/
|
|
2803
2847
|
async prioritize(filter, priority) {
|
|
2804
|
-
return
|
|
2848
|
+
return this._scoped(() => store2().prioritize(filter, priority));
|
|
2805
2849
|
}
|
|
2806
2850
|
/**
|
|
2807
2851
|
* Close the books — guard, archive, truncate, and optionally restart streams.
|
|
@@ -2838,16 +2882,18 @@ var Act = class {
|
|
|
2838
2882
|
*/
|
|
2839
2883
|
async close(targets) {
|
|
2840
2884
|
if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2885
|
+
return this._scoped(async () => {
|
|
2886
|
+
await this.correlate({ limit: 1e3 });
|
|
2887
|
+
const result = await runCloseCycle(targets, {
|
|
2888
|
+
reactiveEventsSize: this._reactive_events.size,
|
|
2889
|
+
eventToState: this._event_to_state,
|
|
2890
|
+
load: this._es.load,
|
|
2891
|
+
tombstone: this._es.tombstone,
|
|
2892
|
+
logger: this._logger
|
|
2893
|
+
});
|
|
2894
|
+
this.emit("closed", result);
|
|
2895
|
+
return result;
|
|
2848
2896
|
});
|
|
2849
|
-
this.emit("closed", result);
|
|
2850
|
-
return result;
|
|
2851
2897
|
}
|
|
2852
2898
|
/**
|
|
2853
2899
|
* Debounced, non-blocking correlate→drain cycle.
|
|
@@ -2901,6 +2947,41 @@ function act() {
|
|
|
2901
2947
|
};
|
|
2902
2948
|
const pendingProjections = [];
|
|
2903
2949
|
const batchHandlers = /* @__PURE__ */ new Map();
|
|
2950
|
+
let _built = false;
|
|
2951
|
+
const finalizeDeprecations = () => {
|
|
2952
|
+
const deprecationSummary = [];
|
|
2953
|
+
for (const state2 of states.values()) {
|
|
2954
|
+
const eventNames = Object.keys(state2.events);
|
|
2955
|
+
const deprecated = deprecatedEventNames(eventNames);
|
|
2956
|
+
if (deprecated.size === 0) continue;
|
|
2957
|
+
state2._deprecated = deprecated;
|
|
2958
|
+
for (const name of deprecated) {
|
|
2959
|
+
const current = currentVersionOf(name, eventNames);
|
|
2960
|
+
deprecationSummary.push({
|
|
2961
|
+
stateName: state2.name,
|
|
2962
|
+
deprecated: name,
|
|
2963
|
+
current
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
for (const [actionName, handler] of Object.entries(state2.on)) {
|
|
2967
|
+
const staticTarget = handler?._staticEmit;
|
|
2968
|
+
if (staticTarget && deprecated.has(staticTarget)) {
|
|
2969
|
+
const current = currentVersionOf(staticTarget, eventNames);
|
|
2970
|
+
throw new Error(
|
|
2971
|
+
`Action "${actionName}" in state "${state2.name}" emits deprecated event "${staticTarget}". A newer version exists: "${current}". Update the .emit() call to target the current version. The reducer (.patch) for "${staticTarget}" stays as-is \u2014 historical events still need it.`
|
|
2972
|
+
);
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
if (deprecationSummary.length > 0) {
|
|
2977
|
+
const list = deprecationSummary.map(
|
|
2978
|
+
(d) => `"${d.deprecated}" (current: "${d.current}", state: "${d.stateName}")`
|
|
2979
|
+
).join(", ");
|
|
2980
|
+
log().info(
|
|
2981
|
+
`Act registered ${deprecationSummary.length} deprecated event(s): ${list}. These are legacy versions kept for the read path. Consider truncating closed streams via app.close() when feasible to reduce historical event load. See docs/docs/architecture/event-schema-evolution.md.`
|
|
2982
|
+
);
|
|
2983
|
+
}
|
|
2984
|
+
};
|
|
2904
2985
|
const builder = {
|
|
2905
2986
|
withState: (state2) => {
|
|
2906
2987
|
registerState(state2, states, registry.actions, registry.events);
|
|
@@ -2944,41 +3025,13 @@ function act() {
|
|
|
2944
3025
|
}
|
|
2945
3026
|
}),
|
|
2946
3027
|
build: (options) => {
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
const deprecationSummary = [];
|
|
2952
|
-
for (const state2 of states.values()) {
|
|
2953
|
-
const eventNames = Object.keys(state2.events);
|
|
2954
|
-
const deprecated = deprecatedEventNames(eventNames);
|
|
2955
|
-
if (deprecated.size === 0) continue;
|
|
2956
|
-
state2._deprecated = deprecated;
|
|
2957
|
-
for (const name of deprecated) {
|
|
2958
|
-
const current = currentVersionOf(name, eventNames);
|
|
2959
|
-
deprecationSummary.push({
|
|
2960
|
-
stateName: state2.name,
|
|
2961
|
-
deprecated: name,
|
|
2962
|
-
current
|
|
2963
|
-
});
|
|
3028
|
+
if (!_built) {
|
|
3029
|
+
for (const proj of pendingProjections) {
|
|
3030
|
+
mergeProjection(proj, registry.events);
|
|
3031
|
+
registerBatchHandler(proj, batchHandlers);
|
|
2964
3032
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
if (staticTarget && deprecated.has(staticTarget)) {
|
|
2968
|
-
const current = currentVersionOf(staticTarget, eventNames);
|
|
2969
|
-
throw new Error(
|
|
2970
|
-
`Action "${actionName}" in state "${state2.name}" emits deprecated event "${staticTarget}". A newer version exists: "${current}". Update the .emit() call to target the current version. The reducer (.patch) for "${staticTarget}" stays as-is \u2014 historical events still need it.`
|
|
2971
|
-
);
|
|
2972
|
-
}
|
|
2973
|
-
}
|
|
2974
|
-
}
|
|
2975
|
-
if (deprecationSummary.length > 0) {
|
|
2976
|
-
const list = deprecationSummary.map(
|
|
2977
|
-
(d) => `"${d.deprecated}" (current: "${d.current}", state: "${d.stateName}")`
|
|
2978
|
-
).join(", ");
|
|
2979
|
-
log().info(
|
|
2980
|
-
`Act registered ${deprecationSummary.length} deprecated event(s): ${list}. These are legacy versions kept for the read path. Consider truncating closed streams via app.close() when feasible to reduce historical event load. See docs/docs/architecture/event-schema-evolution.md.`
|
|
2981
|
-
);
|
|
3033
|
+
finalizeDeprecations();
|
|
3034
|
+
_built = true;
|
|
2982
3035
|
}
|
|
2983
3036
|
return new Act(
|
|
2984
3037
|
registry,
|
|
@@ -3222,6 +3275,7 @@ function action_builder(state2) {
|
|
|
3222
3275
|
log,
|
|
3223
3276
|
port,
|
|
3224
3277
|
projection,
|
|
3278
|
+
scoped,
|
|
3225
3279
|
sleep,
|
|
3226
3280
|
slice,
|
|
3227
3281
|
state,
|