@rotorsoft/act 1.32.21 → 1.32.23
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 +1 -2
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/in-memory-store.d.ts +18 -16
- package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/internal/audit.d.ts +1 -4
- package/dist/@types/internal/audit.d.ts.map +1 -1
- package/dist/@types/internal/config.d.ts +3 -9
- package/dist/@types/internal/config.d.ts.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +1 -11
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- package/dist/@types/internal/defer-timer.d.ts +0 -5
- package/dist/@types/internal/defer-timer.d.ts.map +1 -1
- package/dist/@types/internal/drain-cycle.d.ts +3 -5
- package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
- package/dist/@types/internal/index.d.ts +1 -1
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +10 -10
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/utils.d.ts +3 -2
- package/dist/@types/utils.d.ts.map +1 -1
- package/dist/{chunk-QOSQI3CA.js → chunk-OG5O36AJ.js} +50 -141
- package/dist/chunk-OG5O36AJ.js.map +1 -0
- package/dist/index.cjs +75 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +27 -52
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +49 -140
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-QOSQI3CA.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -641,8 +641,7 @@ var InMemoryStream = class {
|
|
|
641
641
|
// in-process backoff, this is durable store state shared across workers.
|
|
642
642
|
_deferred_at = void 0;
|
|
643
643
|
// Work mark: highest event id observed to resolve to this target.
|
|
644
|
-
// `undefined` means
|
|
645
|
-
// back to the legacy has-work probe rather than treating it as "no work".
|
|
644
|
+
// `undefined` means the row was never correlated, so `claim` skips it.
|
|
646
645
|
_correlated_at = void 0;
|
|
647
646
|
constructor(stream, source, priority = 0, lane = DEFAULT_LANE) {
|
|
648
647
|
this.stream = stream;
|
|
@@ -845,12 +844,6 @@ var InMemoryStore = class {
|
|
|
845
844
|
_correlators = /* @__PURE__ */ new Map();
|
|
846
845
|
// last committed version per stream — O(1) replacement for filter-on-commit
|
|
847
846
|
_stream_versions = /* @__PURE__ */ new Map();
|
|
848
|
-
// max non-snapshot event id per stream — drives the has-work probe in
|
|
849
|
-
// claim(): an O(1) lookup for a literal source, and the per-stream max
|
|
850
|
-
// that a pattern source scans, without touching the full event log.
|
|
851
|
-
_max_event_id_by_stream = /* @__PURE__ */ new Map();
|
|
852
|
-
// global max non-snapshot event id — fast pre-check for source-less streams in claim()
|
|
853
|
-
_max_non_snap_event_id = -1;
|
|
854
847
|
// stream → (event_id → cloned sensitive payload). Two-level so `forget_pii`
|
|
855
848
|
// is O(1) — drop the inner Map for the stream and the wipe is done — mirroring
|
|
856
849
|
// the `DELETE WHERE stream = ?` scope that durable adapters get from their
|
|
@@ -862,10 +855,17 @@ var InMemoryStore = class {
|
|
|
862
855
|
this._next_id = 0;
|
|
863
856
|
this._correlated_at = -1;
|
|
864
857
|
this._stream_versions.clear();
|
|
865
|
-
this._max_event_id_by_stream.clear();
|
|
866
|
-
this._max_non_snap_event_id = -1;
|
|
867
858
|
this._pii.clear();
|
|
868
859
|
}
|
|
860
|
+
// Stores a cloned copy, so caller-side mutation can't reach it.
|
|
861
|
+
_put_pii(stream, id, pii) {
|
|
862
|
+
let per_stream = this._pii.get(stream);
|
|
863
|
+
if (!per_stream) {
|
|
864
|
+
per_stream = /* @__PURE__ */ new Map();
|
|
865
|
+
this._pii.set(stream, per_stream);
|
|
866
|
+
}
|
|
867
|
+
per_stream.set(id, structuredClone(pii));
|
|
868
|
+
}
|
|
869
869
|
// First index whose event id is greater than `after`. `_events` stays
|
|
870
870
|
// sorted ascending by id (append-only commits; deletions preserve
|
|
871
871
|
// order), so id-bounded scans binary-search their start instead of
|
|
@@ -985,7 +985,6 @@ var InMemoryStore = class {
|
|
|
985
985
|
);
|
|
986
986
|
}
|
|
987
987
|
let version = current_version + 1;
|
|
988
|
-
let last_non_snap_id = -1;
|
|
989
988
|
const committed = msgs.map(({ name, data, pii }) => {
|
|
990
989
|
const c = {
|
|
991
990
|
id: this._next_id++,
|
|
@@ -997,23 +996,11 @@ var InMemoryStore = class {
|
|
|
997
996
|
meta
|
|
998
997
|
};
|
|
999
998
|
this._events.push(c);
|
|
1000
|
-
if (pii != null)
|
|
1001
|
-
let per_stream = this._pii.get(stream);
|
|
1002
|
-
if (!per_stream) {
|
|
1003
|
-
per_stream = /* @__PURE__ */ new Map();
|
|
1004
|
-
this._pii.set(stream, per_stream);
|
|
1005
|
-
}
|
|
1006
|
-
per_stream.set(c.id, structuredClone(pii));
|
|
1007
|
-
}
|
|
1008
|
-
if (name !== SNAP_EVENT) last_non_snap_id = c.id;
|
|
999
|
+
if (pii != null) this._put_pii(stream, c.id, pii);
|
|
1009
1000
|
version++;
|
|
1010
1001
|
return this._with_pii(c);
|
|
1011
1002
|
});
|
|
1012
1003
|
this._stream_versions.set(stream, version - 1);
|
|
1013
|
-
if (last_non_snap_id >= 0) {
|
|
1014
|
-
this._max_event_id_by_stream.set(stream, last_non_snap_id);
|
|
1015
|
-
this._max_non_snap_event_id = last_non_snap_id;
|
|
1016
|
-
}
|
|
1017
1004
|
return committed;
|
|
1018
1005
|
}
|
|
1019
1006
|
/**
|
|
@@ -1155,25 +1142,19 @@ var InMemoryStore = class {
|
|
|
1155
1142
|
*/
|
|
1156
1143
|
async defer(input, deferred_at) {
|
|
1157
1144
|
await sleep();
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
s.defer(deferred_at);
|
|
1172
|
-
count++;
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
return count;
|
|
1145
|
+
const selected = this._select(input);
|
|
1146
|
+
for (const s of selected) s.defer(deferred_at);
|
|
1147
|
+
return selected.length;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* The streams named by `input`: an explicit list (de-duplicated, so a
|
|
1151
|
+
* repeated name counts once, matching the SQL adapters' set-based
|
|
1152
|
+
* `stream = ANY(...)`; unknown names are skipped) or a {@link StreamFilter}.
|
|
1153
|
+
*/
|
|
1154
|
+
_select(input) {
|
|
1155
|
+
if (Array.isArray(input))
|
|
1156
|
+
return [...new Set(input)].map((name) => this._streams.get(name)).filter((s) => s !== void 0);
|
|
1157
|
+
return [...this._streams.values()].filter(this._filter_predicate(input));
|
|
1177
1158
|
}
|
|
1178
1159
|
/**
|
|
1179
1160
|
* Build a predicate from a {@link StreamFilter}. Compiled regexes are
|
|
@@ -1209,37 +1190,10 @@ var InMemoryStore = class {
|
|
|
1209
1190
|
*/
|
|
1210
1191
|
async reset(input) {
|
|
1211
1192
|
await sleep();
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
const s = this._streams.get(name);
|
|
1216
|
-
if (s) {
|
|
1217
|
-
s.reset();
|
|
1218
|
-
count++;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
} else {
|
|
1222
|
-
const matches = this._filter_predicate(input);
|
|
1223
|
-
for (const s of this._streams.values()) {
|
|
1224
|
-
if (matches(s)) {
|
|
1225
|
-
s.reset();
|
|
1226
|
-
count++;
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
return count;
|
|
1193
|
+
const selected = this._select(input);
|
|
1194
|
+
for (const s of selected) s.reset();
|
|
1195
|
+
return selected.length;
|
|
1231
1196
|
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Clear the blocked flag (and retry / error / lease) on the matched
|
|
1234
|
-
* streams without touching the watermark. Streams that aren't blocked
|
|
1235
|
-
* at call time are silently skipped. Accepts either an explicit list
|
|
1236
|
-
* of names or a {@link StreamFilter}. The filter form always restricts
|
|
1237
|
-
* to blocked streams — passing `blocked: false` matches nothing.
|
|
1238
|
-
* See {@link Store.unblock}.
|
|
1239
|
-
*
|
|
1240
|
-
* @param input - Stream names or a filter selecting the streams to unblock.
|
|
1241
|
-
* @returns Count of streams that were actually flipped (were blocked).
|
|
1242
|
-
*/
|
|
1243
1197
|
/**
|
|
1244
1198
|
* Wipe the sensitive-data payload for every event on the stream — see
|
|
1245
1199
|
* {@link Store.forget_pii}. O(1) drop of the stream's inner Map; the size of
|
|
@@ -1255,21 +1209,23 @@ var InMemoryStore = class {
|
|
|
1255
1209
|
this._pii.delete(stream);
|
|
1256
1210
|
return count;
|
|
1257
1211
|
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Clear the blocked flag (and retry / error / lease) on the matched
|
|
1214
|
+
* streams without touching the watermark. Streams that aren't blocked
|
|
1215
|
+
* at call time are silently skipped. Accepts either an explicit list
|
|
1216
|
+
* of names or a {@link StreamFilter}. The filter form always restricts
|
|
1217
|
+
* to blocked streams — passing `blocked: false` matches nothing.
|
|
1218
|
+
* See {@link Store.unblock}.
|
|
1219
|
+
*
|
|
1220
|
+
* @param input - Stream names or a filter selecting the streams to unblock.
|
|
1221
|
+
* @returns Count of streams that were actually flipped (were blocked).
|
|
1222
|
+
*/
|
|
1258
1223
|
async unblock(input) {
|
|
1259
1224
|
await sleep();
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
if (s?.unblock()) count++;
|
|
1265
|
-
}
|
|
1266
|
-
} else {
|
|
1267
|
-
const matches = this._filter_predicate({ ...input, blocked: true });
|
|
1268
|
-
for (const s of this._streams.values()) {
|
|
1269
|
-
if (matches(s) && s.unblock()) count++;
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
return count;
|
|
1225
|
+
const selected = this._select(
|
|
1226
|
+
Array.isArray(input) ? input : { ...input, blocked: true }
|
|
1227
|
+
);
|
|
1228
|
+
return selected.filter((s) => s.unblock()).length;
|
|
1273
1229
|
}
|
|
1274
1230
|
/**
|
|
1275
1231
|
* Bulk-update priority of streams matching `filter`. Mirrors
|
|
@@ -1282,16 +1238,9 @@ var InMemoryStore = class {
|
|
|
1282
1238
|
*/
|
|
1283
1239
|
async prioritize(filter, priority) {
|
|
1284
1240
|
await sleep();
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
if (!matches(s)) continue;
|
|
1289
|
-
if (s.priority !== priority) {
|
|
1290
|
-
s.set_priority(priority);
|
|
1291
|
-
count++;
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
return count;
|
|
1241
|
+
const changed = this._select(filter).filter((s) => s.priority !== priority);
|
|
1242
|
+
for (const s of changed) s.set_priority(priority);
|
|
1243
|
+
return changed.length;
|
|
1295
1244
|
}
|
|
1296
1245
|
/**
|
|
1297
1246
|
* Streams registered subscription positions to the callback, ordered by
|
|
@@ -1302,10 +1251,8 @@ var InMemoryStore = class {
|
|
|
1302
1251
|
await sleep();
|
|
1303
1252
|
const limit = query?.limit ?? 100;
|
|
1304
1253
|
const after = query?.after;
|
|
1305
|
-
const blocked = query?.blocked;
|
|
1306
1254
|
const source_matches = query?.source_matches;
|
|
1307
|
-
const
|
|
1308
|
-
const source_re = query?.source !== void 0 && !query.source_exact ? new RegExp(query.source) : void 0;
|
|
1255
|
+
const matches = this._filter_predicate(query ?? {});
|
|
1309
1256
|
const reverse_cache = /* @__PURE__ */ new Map();
|
|
1310
1257
|
const reverse_match = (source) => {
|
|
1311
1258
|
let re = reverse_cache.get(source);
|
|
@@ -1319,20 +1266,10 @@ var InMemoryStore = class {
|
|
|
1319
1266
|
let count = 0;
|
|
1320
1267
|
for (const s of sorted) {
|
|
1321
1268
|
if (after !== void 0 && s.stream <= after) continue;
|
|
1322
|
-
if (
|
|
1323
|
-
if (query.stream_exact ? s.stream !== query.stream : !stream_re.test(s.stream))
|
|
1324
|
-
continue;
|
|
1325
|
-
}
|
|
1326
|
-
if (query?.source !== void 0) {
|
|
1327
|
-
if (s.source === void 0) continue;
|
|
1328
|
-
if (query.source_exact ? s.source !== query.source : !source_re.test(s.source))
|
|
1329
|
-
continue;
|
|
1330
|
-
}
|
|
1269
|
+
if (!matches(s)) continue;
|
|
1331
1270
|
if (source_matches !== void 0) {
|
|
1332
1271
|
if (s.source && !reverse_match(s.source)) continue;
|
|
1333
1272
|
}
|
|
1334
|
-
if (blocked !== void 0 && s.blocked !== blocked) continue;
|
|
1335
|
-
if (query?.lane !== void 0 && s.lane !== query.lane) continue;
|
|
1336
1273
|
if (count >= limit) break;
|
|
1337
1274
|
callback({
|
|
1338
1275
|
stream: s.stream,
|
|
@@ -1363,10 +1300,7 @@ var InMemoryStore = class {
|
|
|
1363
1300
|
* Scope rules:
|
|
1364
1301
|
* - Array `input` — explicit stream names, regardless of subscription.
|
|
1365
1302
|
* - Filter `input` — `stream`/`stream_exact` match against event-bearing
|
|
1366
|
-
* stream names
|
|
1367
|
-
* corresponding subscription in `_streams` (those are subscription
|
|
1368
|
-
* concepts, not event concepts). Empty filter `{}` matches every
|
|
1369
|
-
* event-bearing stream.
|
|
1303
|
+
* stream names. Empty filter `{}` matches every event-bearing stream.
|
|
1370
1304
|
*/
|
|
1371
1305
|
async query_stats(input, options) {
|
|
1372
1306
|
await sleep();
|
|
@@ -1472,7 +1406,6 @@ var InMemoryStore = class {
|
|
|
1472
1406
|
return false;
|
|
1473
1407
|
});
|
|
1474
1408
|
this._stream_versions.delete(stream);
|
|
1475
|
-
this._max_event_id_by_stream.delete(stream);
|
|
1476
1409
|
this._pii.delete(stream);
|
|
1477
1410
|
const event = {
|
|
1478
1411
|
id: this._next_id++,
|
|
@@ -1485,15 +1418,8 @@ var InMemoryStore = class {
|
|
|
1485
1418
|
};
|
|
1486
1419
|
this._events.push(event);
|
|
1487
1420
|
this._stream_versions.set(stream, 0);
|
|
1488
|
-
if (event.name !== SNAP_EVENT) {
|
|
1489
|
-
this._max_event_id_by_stream.set(stream, event.id);
|
|
1490
|
-
}
|
|
1491
1421
|
result.set(stream, { deleted, committed: event });
|
|
1492
1422
|
}
|
|
1493
|
-
let max = -1;
|
|
1494
|
-
for (const id of this._max_event_id_by_stream.values())
|
|
1495
|
-
if (id > max) max = id;
|
|
1496
|
-
this._max_non_snap_event_id = max;
|
|
1497
1423
|
return result;
|
|
1498
1424
|
}
|
|
1499
1425
|
/**
|
|
@@ -1514,8 +1440,6 @@ var InMemoryStore = class {
|
|
|
1514
1440
|
const prev_next_id = this._next_id;
|
|
1515
1441
|
const prev_streams = this._streams;
|
|
1516
1442
|
const prev_stream_versions = this._stream_versions;
|
|
1517
|
-
const prev_max_event_id_by_stream = this._max_event_id_by_stream;
|
|
1518
|
-
const prev_max_non_snap_event_id = this._max_non_snap_event_id;
|
|
1519
1443
|
const prev_pii = this._pii;
|
|
1520
1444
|
const prev_correlated_at = this._correlated_at;
|
|
1521
1445
|
const prev_correlators = new Map(
|
|
@@ -1527,8 +1451,6 @@ var InMemoryStore = class {
|
|
|
1527
1451
|
this._next_id = 0;
|
|
1528
1452
|
this._streams = /* @__PURE__ */ new Map();
|
|
1529
1453
|
this._stream_versions = /* @__PURE__ */ new Map();
|
|
1530
|
-
this._max_event_id_by_stream = /* @__PURE__ */ new Map();
|
|
1531
|
-
this._max_non_snap_event_id = -1;
|
|
1532
1454
|
this._pii = /* @__PURE__ */ new Map();
|
|
1533
1455
|
try {
|
|
1534
1456
|
await driver(async (event) => {
|
|
@@ -1536,19 +1458,8 @@ var InMemoryStore = class {
|
|
|
1536
1458
|
const { pii, ...rest } = event;
|
|
1537
1459
|
const committed = { ...rest, id };
|
|
1538
1460
|
this._events.push(committed);
|
|
1539
|
-
if (pii != null)
|
|
1540
|
-
let per_stream = this._pii.get(event.stream);
|
|
1541
|
-
if (!per_stream) {
|
|
1542
|
-
per_stream = /* @__PURE__ */ new Map();
|
|
1543
|
-
this._pii.set(event.stream, per_stream);
|
|
1544
|
-
}
|
|
1545
|
-
per_stream.set(id, structuredClone(pii));
|
|
1546
|
-
}
|
|
1461
|
+
if (pii != null) this._put_pii(event.stream, id, pii);
|
|
1547
1462
|
this._stream_versions.set(event.stream, event.version);
|
|
1548
|
-
if (event.name !== SNAP_EVENT) {
|
|
1549
|
-
this._max_event_id_by_stream.set(event.stream, id);
|
|
1550
|
-
this._max_non_snap_event_id = id;
|
|
1551
|
-
}
|
|
1552
1463
|
return id;
|
|
1553
1464
|
});
|
|
1554
1465
|
} catch (err) {
|
|
@@ -1556,8 +1467,6 @@ var InMemoryStore = class {
|
|
|
1556
1467
|
this._next_id = prev_next_id;
|
|
1557
1468
|
this._streams = prev_streams;
|
|
1558
1469
|
this._stream_versions = prev_stream_versions;
|
|
1559
|
-
this._max_event_id_by_stream = prev_max_event_id_by_stream;
|
|
1560
|
-
this._max_non_snap_event_id = prev_max_non_snap_event_id;
|
|
1561
1470
|
this._pii = prev_pii;
|
|
1562
1471
|
this._correlated_at = prev_correlated_at;
|
|
1563
1472
|
this._correlators = prev_correlators;
|
|
@@ -2561,7 +2470,6 @@ function resolveSettleConfig(options) {
|
|
|
2561
2470
|
return options;
|
|
2562
2471
|
}
|
|
2563
2472
|
var MAX_SHUTDOWN_GRACE_MS = 3e4;
|
|
2564
|
-
var DEFAULT_SHUTDOWN_GRACE_MS = DEFAULT_LEASE_MILLIS;
|
|
2565
2473
|
var ShutdownOptionsSchema = import_zod6.z.object({
|
|
2566
2474
|
// `0` is legal and is exactly today's behavior: stop scheduling and
|
|
2567
2475
|
// return without waiting.
|
|
@@ -3196,8 +3104,8 @@ function report_once(seen, key, message) {
|
|
|
3196
3104
|
}
|
|
3197
3105
|
|
|
3198
3106
|
// src/internal/correlate-cycle.ts
|
|
3199
|
-
var
|
|
3200
|
-
var
|
|
3107
|
+
var COLD_START_BACK_SCAN = 1e4;
|
|
3108
|
+
var CORRELATION_LEASE_MS = 5e3;
|
|
3201
3109
|
var registry_key = (events) => {
|
|
3202
3110
|
const shape = Object.keys(events).filter((name) => events[name].reactions.size > 0).sort().map((name) => [name, [...events[name].reactions.keys()].sort()]);
|
|
3203
3111
|
return (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(shape)).digest("hex").slice(0, 32);
|
|
@@ -3226,8 +3134,6 @@ var CorrelateCycle = class {
|
|
|
3226
3134
|
* same holder and a restarted process never inherits a stale claim.
|
|
3227
3135
|
*/
|
|
3228
3136
|
_by = (0, import_node_crypto.randomUUID)();
|
|
3229
|
-
/** How long the correlation lease is taken for. */
|
|
3230
|
-
_lease_millis;
|
|
3231
3137
|
/**
|
|
3232
3138
|
* Which correlator this is. Computed once from the registry so identical
|
|
3233
3139
|
* workers share a lease and unrelated applications never do.
|
|
@@ -3280,12 +3186,6 @@ var CorrelateCycle = class {
|
|
|
3280
3186
|
* here, so an idle deferred stream re-arms across a restart.
|
|
3281
3187
|
*/
|
|
3282
3188
|
_on_init_async;
|
|
3283
|
-
/**
|
|
3284
|
-
* Tail re-scan window applied to the cold-start checkpoint.
|
|
3285
|
-
* See {@link DEFAULT_COLD_START_BACK_SCAN}. Constructor arg (not a
|
|
3286
|
-
* public option) so tests can shrink it; defaults otherwise.
|
|
3287
|
-
*/
|
|
3288
|
-
_cold_start_back_scan;
|
|
3289
3189
|
/** Lanes a controller exists for. See {@link CorrelateCycleDeps}. */
|
|
3290
3190
|
_declared_lanes;
|
|
3291
3191
|
/**
|
|
@@ -3302,11 +3202,8 @@ var CorrelateCycle = class {
|
|
|
3302
3202
|
declared_lanes,
|
|
3303
3203
|
shard,
|
|
3304
3204
|
on_init,
|
|
3305
|
-
on_init_async
|
|
3306
|
-
cold_start_back_scan = DEFAULT_COLD_START_BACK_SCAN,
|
|
3307
|
-
lease_millis = DEFAULT_CORRELATION_LEASE_MS
|
|
3205
|
+
on_init_async
|
|
3308
3206
|
}) {
|
|
3309
|
-
this._lease_millis = lease_millis;
|
|
3310
3207
|
this._key = shard ? `${registry_key(registry.events)}:${[...shard].sort().join(",")}` : registry_key(registry.events);
|
|
3311
3208
|
this._dynamic_subscriptions = new LruMap(max_subscribed_streams);
|
|
3312
3209
|
this._registry = registry;
|
|
@@ -3315,7 +3212,6 @@ var CorrelateCycle = class {
|
|
|
3315
3212
|
this._cd = cd;
|
|
3316
3213
|
this._on_init = on_init;
|
|
3317
3214
|
this._on_init_async = on_init_async;
|
|
3318
|
-
this._cold_start_back_scan = cold_start_back_scan;
|
|
3319
3215
|
}
|
|
3320
3216
|
/** Last correlated event id. */
|
|
3321
3217
|
get checkpoint() {
|
|
@@ -3354,7 +3250,7 @@ var CorrelateCycle = class {
|
|
|
3354
3250
|
const { watermark, correlated_at } = await this._cd.subscribe([
|
|
3355
3251
|
...this._static_targets
|
|
3356
3252
|
]);
|
|
3357
|
-
this._checkpoint = correlated_at >= 0 ? correlated_at : Math.max(-1, watermark -
|
|
3253
|
+
this._checkpoint = correlated_at >= 0 ? correlated_at : Math.max(-1, watermark - COLD_START_BACK_SCAN);
|
|
3358
3254
|
this._on_init?.();
|
|
3359
3255
|
for (const { stream, priority = 0, lane } of this._static_targets) {
|
|
3360
3256
|
this._static_subscriptions.set(stream, {
|
|
@@ -3428,13 +3324,13 @@ var CorrelateCycle = class {
|
|
|
3428
3324
|
scanned: false
|
|
3429
3325
|
};
|
|
3430
3326
|
const now = Date.now();
|
|
3431
|
-
if (lease && now >= this._lease_until -
|
|
3327
|
+
if (lease && now >= this._lease_until - CORRELATION_LEASE_MS / 2) {
|
|
3432
3328
|
const { correlating } = await this._cd.subscribe([], void 0, {
|
|
3433
3329
|
key: this._key,
|
|
3434
3330
|
by: this._by,
|
|
3435
|
-
millis:
|
|
3331
|
+
millis: CORRELATION_LEASE_MS
|
|
3436
3332
|
});
|
|
3437
|
-
this._lease_until = correlating ? now +
|
|
3333
|
+
this._lease_until = correlating ? now + CORRELATION_LEASE_MS : 0;
|
|
3438
3334
|
if (correlating === false)
|
|
3439
3335
|
return {
|
|
3440
3336
|
subscribed: 0,
|
|
@@ -3525,10 +3421,10 @@ var CorrelateCycle = class {
|
|
|
3525
3421
|
const { subscribed, correlating } = await this._cd.subscribe(
|
|
3526
3422
|
streams,
|
|
3527
3423
|
last_id,
|
|
3528
|
-
lease ? { key: this._key, by: this._by, millis:
|
|
3424
|
+
lease ? { key: this._key, by: this._by, millis: CORRELATION_LEASE_MS } : void 0
|
|
3529
3425
|
);
|
|
3530
3426
|
if (lease && correlating !== false)
|
|
3531
|
-
this._lease_until = renewed_at +
|
|
3427
|
+
this._lease_until = renewed_at + CORRELATION_LEASE_MS;
|
|
3532
3428
|
const marked = streams.filter(
|
|
3533
3429
|
(entry) => entry.correlated_at !== void 0
|
|
3534
3430
|
).length;
|
|
@@ -3608,7 +3504,7 @@ var CorrelateCycle = class {
|
|
|
3608
3504
|
});
|
|
3609
3505
|
} catch (error) {
|
|
3610
3506
|
log().warn(
|
|
3611
|
-
`Could not hand back the correlation lease during shutdown; it expires on its own within ${
|
|
3507
|
+
`Could not hand back the correlation lease during shutdown; it expires on its own within ${CORRELATION_LEASE_MS}ms and correlation resumes normally after that. On a single-writer store this usually means another connection holds the database. Cause: ${String(error)}`
|
|
3612
3508
|
);
|
|
3613
3509
|
}
|
|
3614
3510
|
}
|
|
@@ -3760,14 +3656,6 @@ var DeferTimer = class {
|
|
|
3760
3656
|
get size() {
|
|
3761
3657
|
return this._due.size;
|
|
3762
3658
|
}
|
|
3763
|
-
/**
|
|
3764
|
-
* True while `stream` is parked with a due-time still in the future.
|
|
3765
|
-
* Consumers skip work for deferred streams until their window elapses.
|
|
3766
|
-
*/
|
|
3767
|
-
is_deferred = (stream) => {
|
|
3768
|
-
const next = this._due.get(stream);
|
|
3769
|
-
return next !== void 0 && next > Date.now();
|
|
3770
|
-
};
|
|
3771
3659
|
/**
|
|
3772
3660
|
* Park `stream` for a re-visit at `at` (ms since epoch). A plain
|
|
3773
3661
|
* overwrite: the caller computes the authoritative next-visit for the
|
|
@@ -4450,7 +4338,7 @@ function trace_cycle(logger, leased, fetched, handled, acked, blocked) {
|
|
|
4450
4338
|
function budget_exhausted(lease, options, store_failing) {
|
|
4451
4339
|
if (store_failing || !options?.blockOnError || lease.retry <= options.maxRetries)
|
|
4452
4340
|
return void 0;
|
|
4453
|
-
const error = `Blocking ${lease.stream} after ${lease.retry} claims with no acknowledged progress \u2014 the retry budget (${options.maxRetries}) was spent without the handler ever reporting an error. That means every attempt lost its lease before it could ack:
|
|
4341
|
+
const error = `Blocking ${lease.stream} after ${lease.retry} claims with no acknowledged progress \u2014 the retry budget (${options.maxRetries}) was spent without the handler ever reporting an error. That means every attempt lost its lease before it could ack: move the reaction to a lane whose leaseMillis covers the handler, then unblock the stream.`;
|
|
4454
4342
|
log().error(error);
|
|
4455
4343
|
return { lease, handled: 0, acked_at: lease.at, error, block: true };
|
|
4456
4344
|
}
|
|
@@ -4530,7 +4418,9 @@ async function run_drain_cycle(deps, cycle) {
|
|
|
4530
4418
|
handled: 0,
|
|
4531
4419
|
acked_at: lease.at,
|
|
4532
4420
|
error,
|
|
4533
|
-
...block2 ? { block: true } : {
|
|
4421
|
+
...block2 ? { block: true } : {
|
|
4422
|
+
next_attempt_at: Date.now() + compute_backoff_delay(lease.retry, DEFAULT_BACKOFF)
|
|
4423
|
+
}
|
|
4534
4424
|
};
|
|
4535
4425
|
return Promise.resolve(failed);
|
|
4536
4426
|
}
|
|
@@ -4679,7 +4569,7 @@ var DrainController = class {
|
|
|
4679
4569
|
if (this._locked) return EMPTY_DRAIN;
|
|
4680
4570
|
const d = this._deps.defaults ?? {};
|
|
4681
4571
|
const streamLimit = d.streamLimit ?? options.streamLimit ?? DEFAULT_STREAM_LIMIT;
|
|
4682
|
-
const eventLimit =
|
|
4572
|
+
const eventLimit = options.eventLimit ?? DEFAULT_EVENT_LIMIT;
|
|
4683
4573
|
const leaseMillis = d.leaseMillis ?? options.leaseMillis ?? DEFAULT_LEASE_MILLIS;
|
|
4684
4574
|
if (this._deps.breaker.state(Date.now()) === "open")
|
|
4685
4575
|
return EMPTY_DRAIN;
|
|
@@ -5343,9 +5233,7 @@ var Act = class {
|
|
|
5343
5233
|
_build_audit_deps() {
|
|
5344
5234
|
return {
|
|
5345
5235
|
store: store2,
|
|
5346
|
-
logger: this._logger,
|
|
5347
5236
|
event_to_state: this._event_to_state,
|
|
5348
|
-
states: this._states,
|
|
5349
5237
|
// Declared lanes, not this process's controllers: a lane another
|
|
5350
5238
|
// worker drains is not `unknown-lane`.
|
|
5351
5239
|
declared_lanes: new Set(this._declared_lanes),
|
|
@@ -5452,9 +5340,9 @@ var Act = class {
|
|
|
5452
5340
|
* fallback, capped at the maximum. Idle lanes don't count.
|
|
5453
5341
|
*/
|
|
5454
5342
|
_derive_grace_ms(running, settling) {
|
|
5455
|
-
let max = settling ?
|
|
5343
|
+
let max = settling ? DEFAULT_LEASE_MILLIS : 0;
|
|
5456
5344
|
for (const c of running)
|
|
5457
|
-
max = Math.max(max, c.lease_millis ||
|
|
5345
|
+
max = Math.max(max, c.lease_millis || DEFAULT_LEASE_MILLIS);
|
|
5458
5346
|
return Math.min(max, MAX_SHUTDOWN_GRACE_MS);
|
|
5459
5347
|
}
|
|
5460
5348
|
async _await_inflight(grace_ms) {
|
|
@@ -5764,8 +5652,7 @@ var Act = class {
|
|
|
5764
5652
|
* Call `correlate()` before `drain()`. It is not only how dynamic targets
|
|
5765
5653
|
* are discovered: a stream is claimable while `at < correlated_at`, and
|
|
5766
5654
|
* `correlate` is the only component that raises that mark, so a
|
|
5767
|
-
* commit no correlate has seen is not drainable
|
|
5768
|
-
* targets, which were served by a probe of the event log before. For a
|
|
5655
|
+
* commit no correlate has seen is not drainable, static targets included. For a
|
|
5769
5656
|
* higher-level API that handles debouncing, correlation, and signaling
|
|
5770
5657
|
* automatically, use {@link settle}.
|
|
5771
5658
|
*
|
|
@@ -6771,6 +6658,12 @@ function act() {
|
|
|
6771
6658
|
};
|
|
6772
6659
|
const fold_projections = [];
|
|
6773
6660
|
const batch_handlers = /* @__PURE__ */ new Map();
|
|
6661
|
+
const add_projection = (proj) => {
|
|
6662
|
+
record_projection_reactions(proj);
|
|
6663
|
+
merge_projection(proj, registry.events);
|
|
6664
|
+
register_batch_handler(proj, batch_handlers);
|
|
6665
|
+
if (proj.fold) fold_projections.push(proj);
|
|
6666
|
+
};
|
|
6774
6667
|
const fold_specs = [];
|
|
6775
6668
|
const lanes = [];
|
|
6776
6669
|
let _built = false;
|
|
@@ -6870,11 +6763,7 @@ function act() {
|
|
|
6870
6763
|
},
|
|
6871
6764
|
withProjection: (proj) => {
|
|
6872
6765
|
if (_built) closed("withProjection");
|
|
6873
|
-
|
|
6874
|
-
merge_projection(proj, registry.events);
|
|
6875
|
-
register_batch_handler(proj, batch_handlers);
|
|
6876
|
-
if (proj.fold)
|
|
6877
|
-
fold_projections.push(proj);
|
|
6766
|
+
add_projection(proj);
|
|
6878
6767
|
return builder;
|
|
6879
6768
|
},
|
|
6880
6769
|
withActor: () => builder,
|
|
@@ -6891,12 +6780,7 @@ function act() {
|
|
|
6891
6780
|
resolveActConfig(options);
|
|
6892
6781
|
const patch_fn = options?.validateFoldedState === true ? validating_patch : bare_patch;
|
|
6893
6782
|
if (!_built) {
|
|
6894
|
-
for (const proj of pending_projections)
|
|
6895
|
-
record_projection_reactions(proj);
|
|
6896
|
-
merge_projection(proj, registry.events);
|
|
6897
|
-
register_batch_handler(proj, batch_handlers);
|
|
6898
|
-
if (proj.fold) fold_projections.push(proj);
|
|
6899
|
-
}
|
|
6783
|
+
for (const proj of pending_projections) add_projection(proj);
|
|
6900
6784
|
for (const proj of fold_projections) {
|
|
6901
6785
|
const fold = proj.fold;
|
|
6902
6786
|
const merged = states.get(fold.name);
|