@rotorsoft/act 0.36.0 → 0.38.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 +33 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +14 -0
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/event-versions.d.ts +44 -0
- package/dist/@types/internal/event-versions.d.ts.map +1 -0
- package/dist/@types/internal/index.d.ts +1 -0
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/ports.d.ts +10 -51
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/index.cjs +206 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +205 -87
- package/dist/index.js.map +1 -1
- package/package.json +1 -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];
|
|
@@ -1485,6 +1496,43 @@ var DrainController = class {
|
|
|
1485
1496
|
}
|
|
1486
1497
|
};
|
|
1487
1498
|
|
|
1499
|
+
// src/internal/event-versions.ts
|
|
1500
|
+
var VERSION_SUFFIX = /^(.+?)_v(\d+)$/;
|
|
1501
|
+
function parse(name) {
|
|
1502
|
+
const m = name.match(VERSION_SUFFIX);
|
|
1503
|
+
if (m) {
|
|
1504
|
+
const v = Number.parseInt(m[2], 10);
|
|
1505
|
+
if (v >= 2) return { base: m[1], version: v };
|
|
1506
|
+
}
|
|
1507
|
+
return { base: name, version: 1 };
|
|
1508
|
+
}
|
|
1509
|
+
function deprecatedEventNames(names) {
|
|
1510
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1511
|
+
for (const name of names) {
|
|
1512
|
+
const { base, version } = parse(name);
|
|
1513
|
+
const list = groups.get(base);
|
|
1514
|
+
if (list) list.push({ version, name });
|
|
1515
|
+
else groups.set(base, [{ version, name }]);
|
|
1516
|
+
}
|
|
1517
|
+
const deprecated = /* @__PURE__ */ new Set();
|
|
1518
|
+
for (const list of groups.values()) {
|
|
1519
|
+
if (list.length < 2) continue;
|
|
1520
|
+
list.sort((a, b) => b.version - a.version);
|
|
1521
|
+
for (let i = 1; i < list.length; i++) deprecated.add(list[i].name);
|
|
1522
|
+
}
|
|
1523
|
+
return deprecated;
|
|
1524
|
+
}
|
|
1525
|
+
function currentVersionOf(deprecatedName, allNames) {
|
|
1526
|
+
const target = parse(deprecatedName);
|
|
1527
|
+
let highest;
|
|
1528
|
+
for (const name of allNames) {
|
|
1529
|
+
const { base, version } = parse(name);
|
|
1530
|
+
if (base !== target.base) continue;
|
|
1531
|
+
if (!highest || version > highest.version) highest = { version, name };
|
|
1532
|
+
}
|
|
1533
|
+
return highest && highest.version > target.version ? highest.name : void 0;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1488
1536
|
// src/internal/merge.ts
|
|
1489
1537
|
var import_zod4 = require("zod");
|
|
1490
1538
|
function baseTypeName(zodType) {
|
|
@@ -1763,12 +1811,12 @@ var SettleLoop = class {
|
|
|
1763
1811
|
};
|
|
1764
1812
|
|
|
1765
1813
|
// src/internal/drain.ts
|
|
1766
|
-
var claim = (lagging, leading, by, millis) =>
|
|
1814
|
+
var claim = (lagging, leading, by, millis) => store2().claim(lagging, leading, by, millis);
|
|
1767
1815
|
async function fetch(leased, eventLimit) {
|
|
1768
1816
|
return Promise.all(
|
|
1769
1817
|
leased.map(async ({ stream, source, at, lagging }) => {
|
|
1770
1818
|
const events = [];
|
|
1771
|
-
await
|
|
1819
|
+
await store2().query((e) => events.push(e), {
|
|
1772
1820
|
stream: source,
|
|
1773
1821
|
after: at,
|
|
1774
1822
|
limit: eventLimit
|
|
@@ -1777,9 +1825,9 @@ async function fetch(leased, eventLimit) {
|
|
|
1777
1825
|
})
|
|
1778
1826
|
);
|
|
1779
1827
|
}
|
|
1780
|
-
var ack = (leases) =>
|
|
1781
|
-
var block = (leases) =>
|
|
1782
|
-
var subscribe = (streams) =>
|
|
1828
|
+
var ack = (leases) => store2().ack(leases);
|
|
1829
|
+
var block = (leases) => store2().block(leases);
|
|
1830
|
+
var subscribe = (streams) => store2().subscribe(streams);
|
|
1783
1831
|
|
|
1784
1832
|
// src/internal/event-sourcing.ts
|
|
1785
1833
|
var import_node_crypto3 = require("crypto");
|
|
@@ -1787,7 +1835,7 @@ var import_act_patch = require("@rotorsoft/act-patch");
|
|
|
1787
1835
|
async function snap(snapshot) {
|
|
1788
1836
|
try {
|
|
1789
1837
|
const { id, stream, name, meta, version } = snapshot.event;
|
|
1790
|
-
await
|
|
1838
|
+
await store2().commit(
|
|
1791
1839
|
stream,
|
|
1792
1840
|
[{ name: SNAP_EVENT, data: snapshot.state }],
|
|
1793
1841
|
{
|
|
@@ -1803,7 +1851,7 @@ async function snap(snapshot) {
|
|
|
1803
1851
|
}
|
|
1804
1852
|
async function tombstone(stream, expectedVersion, correlation) {
|
|
1805
1853
|
try {
|
|
1806
|
-
const [committed] = await
|
|
1854
|
+
const [committed] = await store2().commit(
|
|
1807
1855
|
stream,
|
|
1808
1856
|
[{ name: TOMBSTONE_EVENT, data: {} }],
|
|
1809
1857
|
{ correlation, causation: {} },
|
|
@@ -1817,7 +1865,7 @@ async function tombstone(stream, expectedVersion, correlation) {
|
|
|
1817
1865
|
}
|
|
1818
1866
|
async function load(me, stream, callback, asOf) {
|
|
1819
1867
|
const timeTravel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
|
|
1820
|
-
const cached = timeTravel ? void 0 : await
|
|
1868
|
+
const cached = timeTravel ? void 0 : await cache2().get(stream);
|
|
1821
1869
|
const cache_hit = !!cached;
|
|
1822
1870
|
let state2 = cached?.state ?? (me.init ? me.init() : {});
|
|
1823
1871
|
let patches = cached?.patches ?? 0;
|
|
@@ -1825,7 +1873,7 @@ async function load(me, stream, callback, asOf) {
|
|
|
1825
1873
|
let version = cached?.version ?? -1;
|
|
1826
1874
|
let replayed = 0;
|
|
1827
1875
|
let event;
|
|
1828
|
-
await
|
|
1876
|
+
await store2().query(
|
|
1829
1877
|
(e) => {
|
|
1830
1878
|
event = e;
|
|
1831
1879
|
version = e.version;
|
|
@@ -1860,7 +1908,7 @@ async function load(me, stream, callback, asOf) {
|
|
|
1860
1908
|
}
|
|
1861
1909
|
);
|
|
1862
1910
|
if (replayed > 0 && !timeTravel && event) {
|
|
1863
|
-
await
|
|
1911
|
+
await cache2().set(stream, {
|
|
1864
1912
|
state: state2,
|
|
1865
1913
|
version,
|
|
1866
1914
|
event_id: event.id,
|
|
@@ -1897,6 +1945,20 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1897
1945
|
return [snapshot];
|
|
1898
1946
|
}
|
|
1899
1947
|
const tuples = Array.isArray(result[0]) ? result : [result];
|
|
1948
|
+
const deprecated = me._deprecated;
|
|
1949
|
+
if (deprecated && deprecated.size > 0) {
|
|
1950
|
+
const me_ = me;
|
|
1951
|
+
const warned = me_._warned ?? (me_._warned = /* @__PURE__ */ new Set());
|
|
1952
|
+
for (const [name] of tuples) {
|
|
1953
|
+
const evt = name;
|
|
1954
|
+
if (deprecated.has(evt) && !warned.has(evt)) {
|
|
1955
|
+
warned.add(evt);
|
|
1956
|
+
log().warn(
|
|
1957
|
+
`Action "${String(action2)}" emitted deprecated event "${evt}". A newer version exists in the registry \u2014 update the action's .emit() to target the current version. (warned once per process)`
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1900
1962
|
const emitted = tuples.map(([name, data]) => ({
|
|
1901
1963
|
name,
|
|
1902
1964
|
data: skipValidation ? data : validate(name, data, me.events[name])
|
|
@@ -1919,7 +1981,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1919
1981
|
};
|
|
1920
1982
|
let committed;
|
|
1921
1983
|
try {
|
|
1922
|
-
committed = await
|
|
1984
|
+
committed = await store2().commit(
|
|
1923
1985
|
stream,
|
|
1924
1986
|
emitted,
|
|
1925
1987
|
meta,
|
|
@@ -1931,7 +1993,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1931
1993
|
);
|
|
1932
1994
|
} catch (error) {
|
|
1933
1995
|
if (error instanceof ConcurrencyError) {
|
|
1934
|
-
await
|
|
1996
|
+
await cache2().invalidate(stream);
|
|
1935
1997
|
}
|
|
1936
1998
|
throw error;
|
|
1937
1999
|
}
|
|
@@ -1953,7 +2015,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1953
2015
|
});
|
|
1954
2016
|
const last = snapshots.at(-1);
|
|
1955
2017
|
const snapped = me.snap?.(last);
|
|
1956
|
-
|
|
2018
|
+
cache2().set(stream, {
|
|
1957
2019
|
state: last.state,
|
|
1958
2020
|
version: last.event.version,
|
|
1959
2021
|
event_id: last.event.id,
|
|
@@ -2148,6 +2210,7 @@ var Act = class {
|
|
|
2148
2210
|
this.registry = registry;
|
|
2149
2211
|
this._states = _states;
|
|
2150
2212
|
this._batch_handlers = batchHandlers;
|
|
2213
|
+
this._scoped = options.scoped ? (fn) => scoped.run(options.scoped, fn) : (fn) => fn();
|
|
2151
2214
|
this._es = buildEs(this._logger);
|
|
2152
2215
|
this._cd = buildDrain(this._logger);
|
|
2153
2216
|
this._handle = buildHandle({
|
|
@@ -2193,7 +2256,7 @@ var Act = class {
|
|
|
2193
2256
|
},
|
|
2194
2257
|
options.settleDebounceMs ?? DEFAULT_SETTLE_DEBOUNCE_MS
|
|
2195
2258
|
);
|
|
2196
|
-
this._notify_disposer = this._wireNotify();
|
|
2259
|
+
this._notify_disposer = this._wireNotify(options.scoped?.store ?? store2());
|
|
2197
2260
|
dispose(async () => {
|
|
2198
2261
|
this._emitter.removeAllListeners();
|
|
2199
2262
|
this.stop_correlations();
|
|
@@ -2262,6 +2325,11 @@ var Act = class {
|
|
|
2262
2325
|
_event_to_state;
|
|
2263
2326
|
/** Logger resolved at construction time (after user port configuration) */
|
|
2264
2327
|
_logger = log();
|
|
2328
|
+
/** Wraps a public-method body so internal `store()`/`cache()` resolve to the
|
|
2329
|
+
* per-Act ports (ACT-501). No-op when the Act is unscoped — so the singleton
|
|
2330
|
+
* path keeps reading fresh `store()`/`cache()` per call, which matters for
|
|
2331
|
+
* tests that dispose and re-seed mid-suite. */
|
|
2332
|
+
_scoped;
|
|
2265
2333
|
/** Pre-bound IAct methods reused across drain cycles. Only `do` varies per
|
|
2266
2334
|
* payload (it captures the triggering event for reactingTo auto-inject). */
|
|
2267
2335
|
_bound_do = this.do.bind(this);
|
|
@@ -2277,9 +2345,8 @@ var Act = class {
|
|
|
2277
2345
|
* subscription was made). Errors during subscription are logged but
|
|
2278
2346
|
* never thrown — `notify` is a hint, not a contract.
|
|
2279
2347
|
*/
|
|
2280
|
-
async _wireNotify() {
|
|
2348
|
+
async _wireNotify(s) {
|
|
2281
2349
|
if (this._reactive_events.size === 0) return void 0;
|
|
2282
|
-
const s = store();
|
|
2283
2350
|
if (!s.notify) return void 0;
|
|
2284
2351
|
try {
|
|
2285
2352
|
return await s.notify((notification) => {
|
|
@@ -2383,35 +2450,39 @@ var Act = class {
|
|
|
2383
2450
|
* @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
|
|
2384
2451
|
*/
|
|
2385
2452
|
async do(action2, target, payload, reactingTo, skipValidation = false) {
|
|
2386
|
-
|
|
2387
|
-
this.
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
this.
|
|
2398
|
-
|
|
2453
|
+
return this._scoped(async () => {
|
|
2454
|
+
const snapshots = await this._es.action(
|
|
2455
|
+
this.registry.actions[action2],
|
|
2456
|
+
action2,
|
|
2457
|
+
target,
|
|
2458
|
+
payload,
|
|
2459
|
+
reactingTo,
|
|
2460
|
+
skipValidation
|
|
2461
|
+
);
|
|
2462
|
+
if (this._reactive_events.size > 0) {
|
|
2463
|
+
for (const snap2 of snapshots) {
|
|
2464
|
+
if (snap2.event?.name && this._reactive_events.has(snap2.event.name)) {
|
|
2465
|
+
this._drain.arm();
|
|
2466
|
+
break;
|
|
2467
|
+
}
|
|
2399
2468
|
}
|
|
2400
2469
|
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2470
|
+
this.emit("committed", snapshots);
|
|
2471
|
+
return snapshots;
|
|
2472
|
+
});
|
|
2404
2473
|
}
|
|
2405
2474
|
async load(stateOrName, stream, callback, asOf) {
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2475
|
+
return this._scoped(async () => {
|
|
2476
|
+
let merged;
|
|
2477
|
+
if (typeof stateOrName === "string") {
|
|
2478
|
+
const found = this._states.get(stateOrName);
|
|
2479
|
+
if (!found) throw new Error(`State "${stateOrName}" not found`);
|
|
2480
|
+
merged = found;
|
|
2481
|
+
} else {
|
|
2482
|
+
merged = this._states.get(stateOrName.name) || stateOrName;
|
|
2483
|
+
}
|
|
2484
|
+
return await this._es.load(merged, stream, callback, asOf);
|
|
2485
|
+
});
|
|
2415
2486
|
}
|
|
2416
2487
|
/**
|
|
2417
2488
|
* Queries the event store for events matching a filter.
|
|
@@ -2460,14 +2531,16 @@ var Act = class {
|
|
|
2460
2531
|
* @see {@link query_array} for loading events into memory
|
|
2461
2532
|
*/
|
|
2462
2533
|
async query(query, callback) {
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2534
|
+
return this._scoped(async () => {
|
|
2535
|
+
let first;
|
|
2536
|
+
let last;
|
|
2537
|
+
const count = await store2().query((e) => {
|
|
2538
|
+
if (!first) first = e;
|
|
2539
|
+
last = e;
|
|
2540
|
+
callback?.(e);
|
|
2541
|
+
}, query);
|
|
2542
|
+
return { first, last, count };
|
|
2543
|
+
});
|
|
2471
2544
|
}
|
|
2472
2545
|
/**
|
|
2473
2546
|
* Queries the event store and returns all matching events in memory.
|
|
@@ -2496,9 +2569,11 @@ var Act = class {
|
|
|
2496
2569
|
* @see {@link query} for large result sets
|
|
2497
2570
|
*/
|
|
2498
2571
|
async query_array(query) {
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2572
|
+
return this._scoped(async () => {
|
|
2573
|
+
const events = [];
|
|
2574
|
+
await store2().query((e) => events.push(e), query);
|
|
2575
|
+
return events;
|
|
2576
|
+
});
|
|
2502
2577
|
}
|
|
2503
2578
|
/**
|
|
2504
2579
|
* Processes pending reactions by draining uncommitted events from the event store.
|
|
@@ -2538,7 +2613,7 @@ var Act = class {
|
|
|
2538
2613
|
* @see {@link start_correlations} for automatic correlation
|
|
2539
2614
|
*/
|
|
2540
2615
|
async drain(options = {}) {
|
|
2541
|
-
return this._drain.drain(options);
|
|
2616
|
+
return this._scoped(() => this._drain.drain(options));
|
|
2542
2617
|
}
|
|
2543
2618
|
/**
|
|
2544
2619
|
* Discovers and registers new streams dynamically based on reaction resolvers.
|
|
@@ -2586,7 +2661,7 @@ var Act = class {
|
|
|
2586
2661
|
* @see {@link stop_correlations} to stop automatic correlation
|
|
2587
2662
|
*/
|
|
2588
2663
|
async correlate(query = { after: -1, limit: 10 }) {
|
|
2589
|
-
return this._correlate.correlate(query);
|
|
2664
|
+
return this._scoped(() => this._correlate.correlate(query));
|
|
2590
2665
|
}
|
|
2591
2666
|
/**
|
|
2592
2667
|
* Starts automatic periodic correlation worker for discovering new streams.
|
|
@@ -2707,9 +2782,11 @@ var Act = class {
|
|
|
2707
2782
|
* @see {@link settle} for the debounced full-catch-up loop
|
|
2708
2783
|
*/
|
|
2709
2784
|
async reset(streams) {
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2785
|
+
return this._scoped(async () => {
|
|
2786
|
+
const count = await store2().reset(streams);
|
|
2787
|
+
if (count > 0 && this._reactive_events.size > 0) this._drain.arm();
|
|
2788
|
+
return count;
|
|
2789
|
+
});
|
|
2713
2790
|
}
|
|
2714
2791
|
/**
|
|
2715
2792
|
* Bulk-update scheduling priority for streams matching `filter`.
|
|
@@ -2750,7 +2827,7 @@ var Act = class {
|
|
|
2750
2827
|
* @see {@link claim} for how priority biases scheduling
|
|
2751
2828
|
*/
|
|
2752
2829
|
async prioritize(filter, priority) {
|
|
2753
|
-
return
|
|
2830
|
+
return this._scoped(() => store2().prioritize(filter, priority));
|
|
2754
2831
|
}
|
|
2755
2832
|
/**
|
|
2756
2833
|
* Close the books — guard, archive, truncate, and optionally restart streams.
|
|
@@ -2787,16 +2864,18 @@ var Act = class {
|
|
|
2787
2864
|
*/
|
|
2788
2865
|
async close(targets) {
|
|
2789
2866
|
if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2867
|
+
return this._scoped(async () => {
|
|
2868
|
+
await this.correlate({ limit: 1e3 });
|
|
2869
|
+
const result = await runCloseCycle(targets, {
|
|
2870
|
+
reactiveEventsSize: this._reactive_events.size,
|
|
2871
|
+
eventToState: this._event_to_state,
|
|
2872
|
+
load: this._es.load,
|
|
2873
|
+
tombstone: this._es.tombstone,
|
|
2874
|
+
logger: this._logger
|
|
2875
|
+
});
|
|
2876
|
+
this.emit("closed", result);
|
|
2877
|
+
return result;
|
|
2797
2878
|
});
|
|
2798
|
-
this.emit("closed", result);
|
|
2799
|
-
return result;
|
|
2800
2879
|
}
|
|
2801
2880
|
/**
|
|
2802
2881
|
* Debounced, non-blocking correlate→drain cycle.
|
|
@@ -2850,6 +2929,41 @@ function act() {
|
|
|
2850
2929
|
};
|
|
2851
2930
|
const pendingProjections = [];
|
|
2852
2931
|
const batchHandlers = /* @__PURE__ */ new Map();
|
|
2932
|
+
let _built = false;
|
|
2933
|
+
const finalizeDeprecations = () => {
|
|
2934
|
+
const deprecationSummary = [];
|
|
2935
|
+
for (const state2 of states.values()) {
|
|
2936
|
+
const eventNames = Object.keys(state2.events);
|
|
2937
|
+
const deprecated = deprecatedEventNames(eventNames);
|
|
2938
|
+
if (deprecated.size === 0) continue;
|
|
2939
|
+
state2._deprecated = deprecated;
|
|
2940
|
+
for (const name of deprecated) {
|
|
2941
|
+
const current = currentVersionOf(name, eventNames);
|
|
2942
|
+
deprecationSummary.push({
|
|
2943
|
+
stateName: state2.name,
|
|
2944
|
+
deprecated: name,
|
|
2945
|
+
current
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
for (const [actionName, handler] of Object.entries(state2.on)) {
|
|
2949
|
+
const staticTarget = handler?._staticEmit;
|
|
2950
|
+
if (staticTarget && deprecated.has(staticTarget)) {
|
|
2951
|
+
const current = currentVersionOf(staticTarget, eventNames);
|
|
2952
|
+
throw new Error(
|
|
2953
|
+
`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.`
|
|
2954
|
+
);
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
if (deprecationSummary.length > 0) {
|
|
2959
|
+
const list = deprecationSummary.map(
|
|
2960
|
+
(d) => `"${d.deprecated}" (current: "${d.current}", state: "${d.stateName}")`
|
|
2961
|
+
).join(", ");
|
|
2962
|
+
log().info(
|
|
2963
|
+
`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.`
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
};
|
|
2853
2967
|
const builder = {
|
|
2854
2968
|
withState: (state2) => {
|
|
2855
2969
|
registerState(state2, states, registry.actions, registry.events);
|
|
@@ -2893,9 +3007,13 @@ function act() {
|
|
|
2893
3007
|
}
|
|
2894
3008
|
}),
|
|
2895
3009
|
build: (options) => {
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
3010
|
+
if (!_built) {
|
|
3011
|
+
for (const proj of pendingProjections) {
|
|
3012
|
+
mergeProjection(proj, registry.events);
|
|
3013
|
+
registerBatchHandler(proj, batchHandlers);
|
|
3014
|
+
}
|
|
3015
|
+
finalizeDeprecations();
|
|
3016
|
+
_built = true;
|
|
2899
3017
|
}
|
|
2900
3018
|
return new Act(
|
|
2901
3019
|
registry,
|
|
@@ -3083,10 +3201,10 @@ function action_builder(state2) {
|
|
|
3083
3201
|
function emit(handler) {
|
|
3084
3202
|
if (typeof handler === "string") {
|
|
3085
3203
|
const eventName = handler;
|
|
3086
|
-
|
|
3087
|
-
eventName
|
|
3088
|
-
|
|
3089
|
-
];
|
|
3204
|
+
const emitFn = Object.assign((payload) => [eventName, payload], {
|
|
3205
|
+
_staticEmit: eventName
|
|
3206
|
+
});
|
|
3207
|
+
internal.on[action2] = emitFn;
|
|
3090
3208
|
} else {
|
|
3091
3209
|
internal.on[action2] = handler;
|
|
3092
3210
|
}
|
|
@@ -3139,6 +3257,7 @@ function action_builder(state2) {
|
|
|
3139
3257
|
log,
|
|
3140
3258
|
port,
|
|
3141
3259
|
projection,
|
|
3260
|
+
scoped,
|
|
3142
3261
|
sleep,
|
|
3143
3262
|
slice,
|
|
3144
3263
|
state,
|