@rotorsoft/act 1.32.11 → 1.32.13
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 +31 -100
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/in-memory-store.d.ts +8 -0
- package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/builders/builder-utils.d.ts.map +1 -1
- package/dist/@types/builders/projection-builder.d.ts.map +1 -1
- package/dist/@types/internal/autoclose-reaction.d.ts +1 -1
- package/dist/@types/internal/autoclose-reaction.d.ts.map +1 -1
- package/dist/@types/internal/config.d.ts +25 -4
- package/dist/@types/internal/config.d.ts.map +1 -1
- package/dist/@types/internal/correlate-cycle.d.ts +1 -19
- package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
- 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/{chunk-UVFP2BQ3.js → chunk-FLAGQMWE.js} +35 -58
- package/dist/chunk-FLAGQMWE.js.map +1 -0
- package/dist/index.cjs +235 -316
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +202 -260
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +34 -57
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +1 -1
- package/package.json +2 -6
- package/dist/chunk-UVFP2BQ3.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
sleep,
|
|
27
27
|
store,
|
|
28
28
|
validate
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-FLAGQMWE.js";
|
|
30
30
|
import {
|
|
31
31
|
ActorSchema,
|
|
32
32
|
CausationEventSchema,
|
|
@@ -851,6 +851,151 @@ var CloseSignal = class extends Error {
|
|
|
851
851
|
}
|
|
852
852
|
};
|
|
853
853
|
|
|
854
|
+
// src/internal/config.ts
|
|
855
|
+
import { z as z2 } from "zod";
|
|
856
|
+
var BackoffOptionsSchema = z2.object({
|
|
857
|
+
strategy: z2.enum(["fixed", "linear", "exponential"]),
|
|
858
|
+
baseMs: z2.number().min(0),
|
|
859
|
+
maxMs: z2.number().gt(0).optional(),
|
|
860
|
+
jitter: z2.boolean().optional()
|
|
861
|
+
});
|
|
862
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
863
|
+
var DEFAULT_BLOCK_ON_ERROR = true;
|
|
864
|
+
var ReactionOptionsSchema = z2.object({
|
|
865
|
+
blockOnError: z2.boolean(),
|
|
866
|
+
maxRetries: z2.number().int().min(0),
|
|
867
|
+
backoff: BackoffOptionsSchema.optional()
|
|
868
|
+
});
|
|
869
|
+
function resolveReactionConfig(options) {
|
|
870
|
+
return ReactionOptionsSchema.parse(options);
|
|
871
|
+
}
|
|
872
|
+
var ActionOptionsSchema = z2.object({
|
|
873
|
+
maxRetries: z2.number().int().min(0).optional(),
|
|
874
|
+
backoff: BackoffOptionsSchema.optional()
|
|
875
|
+
});
|
|
876
|
+
function resolveActionConfig(options) {
|
|
877
|
+
return ActionOptionsSchema.parse(options);
|
|
878
|
+
}
|
|
879
|
+
var LaneConfigSchema = z2.object({
|
|
880
|
+
name: z2.string().min(1),
|
|
881
|
+
leaseMillis: z2.number().min(0).optional(),
|
|
882
|
+
streamLimit: z2.number().int().min(0).optional(),
|
|
883
|
+
cycleMs: z2.number().min(0).optional()
|
|
884
|
+
});
|
|
885
|
+
function resolveLaneConfig(options) {
|
|
886
|
+
LaneConfigSchema.parse(options);
|
|
887
|
+
return options;
|
|
888
|
+
}
|
|
889
|
+
var DEFAULT_STREAM_LIMIT = 10;
|
|
890
|
+
var DEFAULT_EVENT_LIMIT = 10;
|
|
891
|
+
var DEFAULT_LEASE_MILLIS = 1e4;
|
|
892
|
+
var DEFAULT_SETTLE_DEBOUNCE_MS = 10;
|
|
893
|
+
var DEFAULT_MAX_SUBSCRIBED_STREAMS = 1e3;
|
|
894
|
+
var DrainOptionsSchema = z2.object({
|
|
895
|
+
streamLimit: z2.number().int().min(0).optional(),
|
|
896
|
+
eventLimit: z2.number().int().min(0).optional(),
|
|
897
|
+
leaseMillis: z2.number().min(0).optional()
|
|
898
|
+
});
|
|
899
|
+
function resolveDrainConfig(options) {
|
|
900
|
+
if (options === void 0) return void 0;
|
|
901
|
+
DrainOptionsSchema.parse(options);
|
|
902
|
+
return options;
|
|
903
|
+
}
|
|
904
|
+
var SettleOptionsSchema = DrainOptionsSchema.extend({
|
|
905
|
+
debounceMs: z2.number().min(0).optional(),
|
|
906
|
+
// `maxPasses` defaults to Infinity (no cap) when omitted; a present value is
|
|
907
|
+
// a non-negative integer — `0` is legal and means "run no passes".
|
|
908
|
+
//
|
|
909
|
+
// `Infinity` is accepted explicitly. It is the documented default
|
|
910
|
+
// (`Act.settle`'s doc-comment and `SettleOptions.maxPasses` both name it),
|
|
911
|
+
// and `settle.ts` destructures `maxPasses = Infinity`, so the one value the
|
|
912
|
+
// docs advertise was the one value `z.number().int()` refused — Zod 4
|
|
913
|
+
// rejects `±Infinity`, and an integer check cannot pass it either. Wrapper
|
|
914
|
+
// code normalizing an optional cap (`maxPasses: opts.maxPasses ?? Infinity`)
|
|
915
|
+
// is the natural way to hit it. Accepting it also honors this module's own
|
|
916
|
+
// rule that a value which works today is never newly rejected (#1711).
|
|
917
|
+
maxPasses: z2.union([z2.number().int().min(0), z2.literal(Number.POSITIVE_INFINITY)]).optional()
|
|
918
|
+
}).loose();
|
|
919
|
+
function resolveSettleConfig(options) {
|
|
920
|
+
if (options === void 0) return void 0;
|
|
921
|
+
SettleOptionsSchema.parse(options);
|
|
922
|
+
return options;
|
|
923
|
+
}
|
|
924
|
+
var MAX_SHUTDOWN_GRACE_MS = 3e4;
|
|
925
|
+
var DEFAULT_SHUTDOWN_GRACE_MS = DEFAULT_LEASE_MILLIS;
|
|
926
|
+
var ShutdownOptionsSchema = z2.object({
|
|
927
|
+
// `0` is legal and is exactly today's behavior: stop scheduling and
|
|
928
|
+
// return without waiting.
|
|
929
|
+
graceMs: z2.number().min(0).optional()
|
|
930
|
+
}).loose();
|
|
931
|
+
function resolveShutdownConfig(options) {
|
|
932
|
+
if (options === void 0) return void 0;
|
|
933
|
+
ShutdownOptionsSchema.parse(options);
|
|
934
|
+
return options;
|
|
935
|
+
}
|
|
936
|
+
var ActOptionsSchema = z2.object({
|
|
937
|
+
maxSubscribedStreams: z2.number().int().min(1).optional(),
|
|
938
|
+
settleDebounceMs: z2.number().int().min(0).optional()
|
|
939
|
+
}).loose();
|
|
940
|
+
function resolveActConfig(options) {
|
|
941
|
+
if (options === void 0) return void 0;
|
|
942
|
+
ActOptionsSchema.parse(options);
|
|
943
|
+
return options;
|
|
944
|
+
}
|
|
945
|
+
var DEFAULT_AUTOCLOSE_CYCLE_MINUTES = 720;
|
|
946
|
+
var DEFAULT_CLOSE_BATCH_SIZE = 64;
|
|
947
|
+
var DEFAULT_CLOSE_YIELD_MS = 0;
|
|
948
|
+
var DEFAULT_AUTOCLOSE_WINDOW_TZ = "UTC";
|
|
949
|
+
function is_valid_time_zone(tz) {
|
|
950
|
+
try {
|
|
951
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
952
|
+
return true;
|
|
953
|
+
} catch {
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
var AutocloseWindowSchema = z2.object({
|
|
958
|
+
start: z2.number().int().min(0).max(23, { message: "autocloseWindow.start must be an hour in [0, 23]" }),
|
|
959
|
+
end: z2.number().int().min(0).max(23, { message: "autocloseWindow.end must be an hour in [0, 23]" }),
|
|
960
|
+
timeZone: z2.string().refine(is_valid_time_zone, {
|
|
961
|
+
message: "autocloseWindow.timeZone must be a valid IANA time zone"
|
|
962
|
+
}).default(DEFAULT_AUTOCLOSE_WINDOW_TZ)
|
|
963
|
+
}).refine((w) => w.start !== w.end, {
|
|
964
|
+
message: "autocloseWindow.start and end must differ \u2014 an empty window disables autoclose"
|
|
965
|
+
});
|
|
966
|
+
var AutocloseOptionsSchema = z2.object({
|
|
967
|
+
autocloseCycleMinutes: z2.number().int().min(1).max(1440).default(DEFAULT_AUTOCLOSE_CYCLE_MINUTES),
|
|
968
|
+
closeBatchSize: z2.number().int().min(1).max(1024).default(DEFAULT_CLOSE_BATCH_SIZE),
|
|
969
|
+
closeYieldMs: z2.number().min(0).max(1e3).default(DEFAULT_CLOSE_YIELD_MS),
|
|
970
|
+
closeOnError: z2.boolean().default(false),
|
|
971
|
+
autocloseWindow: AutocloseWindowSchema.optional()
|
|
972
|
+
});
|
|
973
|
+
function resolveAutocloseConfig(options) {
|
|
974
|
+
return AutocloseOptionsSchema.parse({
|
|
975
|
+
autocloseCycleMinutes: options?.autocloseCycleMinutes,
|
|
976
|
+
closeBatchSize: options?.closeBatchSize,
|
|
977
|
+
closeYieldMs: options?.closeYieldMs,
|
|
978
|
+
closeOnError: options?.closeOnError,
|
|
979
|
+
autocloseWindow: options?.autocloseWindow
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
var DEFAULT_CIRCUIT_FAILURE_THRESHOLD = 5;
|
|
983
|
+
var DEFAULT_CIRCUIT_COOLDOWN_MS = 3e4;
|
|
984
|
+
var CircuitBreakerOptionsSchema = z2.object({
|
|
985
|
+
failureThreshold: z2.number().int().min(1).default(DEFAULT_CIRCUIT_FAILURE_THRESHOLD),
|
|
986
|
+
cooldownMs: z2.number().int().min(100).max(36e5).default(DEFAULT_CIRCUIT_COOLDOWN_MS)
|
|
987
|
+
});
|
|
988
|
+
var resolveCircuitBreakerConfig = (options) => CircuitBreakerOptionsSchema.parse(options ?? {});
|
|
989
|
+
var DEFAULT_FOLD_FLUSH_EVERY = 1e3;
|
|
990
|
+
var DEFAULT_MAX_CACHED_STATES = 1e4;
|
|
991
|
+
var FoldOptionsSchema = z2.object({
|
|
992
|
+
flushEvery: z2.number().int().min(1).default(DEFAULT_FOLD_FLUSH_EVERY),
|
|
993
|
+
maxCachedStates: z2.number().int().min(1).default(DEFAULT_MAX_CACHED_STATES)
|
|
994
|
+
});
|
|
995
|
+
function resolveFoldConfig(options) {
|
|
996
|
+
return FoldOptionsSchema.parse(options);
|
|
997
|
+
}
|
|
998
|
+
|
|
854
999
|
// src/internal/defer-signal.ts
|
|
855
1000
|
var DeferSignal = class extends Error {
|
|
856
1001
|
/**
|
|
@@ -887,7 +1032,7 @@ function synthesize_autoclose_reactions(registry, states, read_window) {
|
|
|
887
1032
|
}),
|
|
888
1033
|
// Never block on autoclose: a transient query/store error should retry,
|
|
889
1034
|
// not quarantine the synthetic stream.
|
|
890
|
-
options: { blockOnError: false, maxRetries:
|
|
1035
|
+
options: { blockOnError: false, maxRetries: DEFAULT_MAX_RETRIES },
|
|
891
1036
|
handler: async (event) => {
|
|
892
1037
|
const aggregate = event.stream;
|
|
893
1038
|
const window = read_window();
|
|
@@ -1374,144 +1519,6 @@ async function truncate_and_warm_cache(guarded, seed_states, guard_events, corre
|
|
|
1374
1519
|
return truncated;
|
|
1375
1520
|
}
|
|
1376
1521
|
|
|
1377
|
-
// src/internal/config.ts
|
|
1378
|
-
import { z as z2 } from "zod";
|
|
1379
|
-
var BackoffOptionsSchema = z2.object({
|
|
1380
|
-
strategy: z2.enum(["fixed", "linear", "exponential"]),
|
|
1381
|
-
baseMs: z2.number().min(0),
|
|
1382
|
-
maxMs: z2.number().gt(0).optional(),
|
|
1383
|
-
jitter: z2.boolean().optional()
|
|
1384
|
-
});
|
|
1385
|
-
var ReactionOptionsSchema = z2.object({
|
|
1386
|
-
blockOnError: z2.boolean(),
|
|
1387
|
-
maxRetries: z2.number().int().min(0),
|
|
1388
|
-
backoff: BackoffOptionsSchema.optional()
|
|
1389
|
-
});
|
|
1390
|
-
function resolveReactionConfig(options) {
|
|
1391
|
-
return ReactionOptionsSchema.parse(options);
|
|
1392
|
-
}
|
|
1393
|
-
var ActionOptionsSchema = z2.object({
|
|
1394
|
-
maxRetries: z2.number().int().min(0).optional(),
|
|
1395
|
-
backoff: BackoffOptionsSchema.optional()
|
|
1396
|
-
});
|
|
1397
|
-
function resolveActionConfig(options) {
|
|
1398
|
-
return ActionOptionsSchema.parse(options);
|
|
1399
|
-
}
|
|
1400
|
-
var LaneConfigSchema = z2.object({
|
|
1401
|
-
name: z2.string().min(1),
|
|
1402
|
-
leaseMillis: z2.number().min(0).optional(),
|
|
1403
|
-
streamLimit: z2.number().int().min(0).optional(),
|
|
1404
|
-
cycleMs: z2.number().min(0).optional()
|
|
1405
|
-
});
|
|
1406
|
-
function resolveLaneConfig(options) {
|
|
1407
|
-
LaneConfigSchema.parse(options);
|
|
1408
|
-
return options;
|
|
1409
|
-
}
|
|
1410
|
-
var DrainOptionsSchema = z2.object({
|
|
1411
|
-
streamLimit: z2.number().int().min(0).optional(),
|
|
1412
|
-
eventLimit: z2.number().int().min(0).optional(),
|
|
1413
|
-
leaseMillis: z2.number().min(0).optional()
|
|
1414
|
-
});
|
|
1415
|
-
function resolveDrainConfig(options) {
|
|
1416
|
-
if (options === void 0) return void 0;
|
|
1417
|
-
DrainOptionsSchema.parse(options);
|
|
1418
|
-
return options;
|
|
1419
|
-
}
|
|
1420
|
-
var SettleOptionsSchema = DrainOptionsSchema.extend({
|
|
1421
|
-
debounceMs: z2.number().min(0).optional(),
|
|
1422
|
-
// `maxPasses` defaults to Infinity (no cap) when omitted; a present value is
|
|
1423
|
-
// a non-negative integer — `0` is legal and means "run no passes".
|
|
1424
|
-
//
|
|
1425
|
-
// `Infinity` is accepted explicitly. It is the documented default
|
|
1426
|
-
// (`Act.settle`'s doc-comment and `SettleOptions.maxPasses` both name it),
|
|
1427
|
-
// and `settle.ts` destructures `maxPasses = Infinity`, so the one value the
|
|
1428
|
-
// docs advertise was the one value `z.number().int()` refused — Zod 4
|
|
1429
|
-
// rejects `±Infinity`, and an integer check cannot pass it either. Wrapper
|
|
1430
|
-
// code normalizing an optional cap (`maxPasses: opts.maxPasses ?? Infinity`)
|
|
1431
|
-
// is the natural way to hit it. Accepting it also honors this module's own
|
|
1432
|
-
// rule that a value which works today is never newly rejected (#1711).
|
|
1433
|
-
maxPasses: z2.union([z2.number().int().min(0), z2.literal(Number.POSITIVE_INFINITY)]).optional()
|
|
1434
|
-
}).loose();
|
|
1435
|
-
function resolveSettleConfig(options) {
|
|
1436
|
-
if (options === void 0) return void 0;
|
|
1437
|
-
SettleOptionsSchema.parse(options);
|
|
1438
|
-
return options;
|
|
1439
|
-
}
|
|
1440
|
-
var MAX_SHUTDOWN_GRACE_MS = 3e4;
|
|
1441
|
-
var DEFAULT_SHUTDOWN_GRACE_MS = 1e4;
|
|
1442
|
-
var ShutdownOptionsSchema = z2.object({
|
|
1443
|
-
// `0` is legal and is exactly today's behavior: stop scheduling and
|
|
1444
|
-
// return without waiting.
|
|
1445
|
-
graceMs: z2.number().min(0).optional()
|
|
1446
|
-
}).loose();
|
|
1447
|
-
function resolveShutdownConfig(options) {
|
|
1448
|
-
if (options === void 0) return void 0;
|
|
1449
|
-
ShutdownOptionsSchema.parse(options);
|
|
1450
|
-
return options;
|
|
1451
|
-
}
|
|
1452
|
-
var ActOptionsSchema = z2.object({
|
|
1453
|
-
maxSubscribedStreams: z2.number().int().min(1).optional(),
|
|
1454
|
-
settleDebounceMs: z2.number().int().min(0).optional()
|
|
1455
|
-
}).loose();
|
|
1456
|
-
function resolveActConfig(options) {
|
|
1457
|
-
if (options === void 0) return void 0;
|
|
1458
|
-
ActOptionsSchema.parse(options);
|
|
1459
|
-
return options;
|
|
1460
|
-
}
|
|
1461
|
-
var DEFAULT_AUTOCLOSE_CYCLE_MINUTES = 720;
|
|
1462
|
-
var DEFAULT_CLOSE_BATCH_SIZE = 64;
|
|
1463
|
-
var DEFAULT_CLOSE_YIELD_MS = 0;
|
|
1464
|
-
var DEFAULT_AUTOCLOSE_WINDOW_TZ = "UTC";
|
|
1465
|
-
function is_valid_time_zone(tz) {
|
|
1466
|
-
try {
|
|
1467
|
-
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
1468
|
-
return true;
|
|
1469
|
-
} catch {
|
|
1470
|
-
return false;
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
var AutocloseWindowSchema = z2.object({
|
|
1474
|
-
start: z2.number().int().min(0).max(23, { message: "autocloseWindow.start must be an hour in [0, 23]" }),
|
|
1475
|
-
end: z2.number().int().min(0).max(23, { message: "autocloseWindow.end must be an hour in [0, 23]" }),
|
|
1476
|
-
timeZone: z2.string().refine(is_valid_time_zone, {
|
|
1477
|
-
message: "autocloseWindow.timeZone must be a valid IANA time zone"
|
|
1478
|
-
}).default(DEFAULT_AUTOCLOSE_WINDOW_TZ)
|
|
1479
|
-
}).refine((w) => w.start !== w.end, {
|
|
1480
|
-
message: "autocloseWindow.start and end must differ \u2014 an empty window disables autoclose"
|
|
1481
|
-
});
|
|
1482
|
-
var AutocloseOptionsSchema = z2.object({
|
|
1483
|
-
autocloseCycleMinutes: z2.number().int().min(1).max(1440).default(DEFAULT_AUTOCLOSE_CYCLE_MINUTES),
|
|
1484
|
-
closeBatchSize: z2.number().int().min(1).max(1024).default(DEFAULT_CLOSE_BATCH_SIZE),
|
|
1485
|
-
closeYieldMs: z2.number().min(0).max(1e3).default(DEFAULT_CLOSE_YIELD_MS),
|
|
1486
|
-
closeOnError: z2.boolean().default(false),
|
|
1487
|
-
autocloseWindow: AutocloseWindowSchema.optional()
|
|
1488
|
-
});
|
|
1489
|
-
function resolveAutocloseConfig(options) {
|
|
1490
|
-
return AutocloseOptionsSchema.parse({
|
|
1491
|
-
autocloseCycleMinutes: options?.autocloseCycleMinutes,
|
|
1492
|
-
closeBatchSize: options?.closeBatchSize,
|
|
1493
|
-
closeYieldMs: options?.closeYieldMs,
|
|
1494
|
-
closeOnError: options?.closeOnError,
|
|
1495
|
-
autocloseWindow: options?.autocloseWindow
|
|
1496
|
-
});
|
|
1497
|
-
}
|
|
1498
|
-
var DEFAULT_CIRCUIT_FAILURE_THRESHOLD = 5;
|
|
1499
|
-
var DEFAULT_CIRCUIT_COOLDOWN_MS = 3e4;
|
|
1500
|
-
var CircuitBreakerOptionsSchema = z2.object({
|
|
1501
|
-
failureThreshold: z2.number().int().min(1).default(DEFAULT_CIRCUIT_FAILURE_THRESHOLD),
|
|
1502
|
-
cooldownMs: z2.number().int().min(100).max(36e5).default(DEFAULT_CIRCUIT_COOLDOWN_MS)
|
|
1503
|
-
});
|
|
1504
|
-
var resolveCircuitBreakerConfig = (options) => CircuitBreakerOptionsSchema.parse(options ?? {});
|
|
1505
|
-
var DEFAULT_FOLD_FLUSH_EVERY = 1e3;
|
|
1506
|
-
var DEFAULT_MAX_CACHED_STATES = 1e4;
|
|
1507
|
-
var FoldOptionsSchema = z2.object({
|
|
1508
|
-
flushEvery: z2.number().int().min(1).default(DEFAULT_FOLD_FLUSH_EVERY),
|
|
1509
|
-
maxCachedStates: z2.number().int().min(1).default(DEFAULT_MAX_CACHED_STATES)
|
|
1510
|
-
});
|
|
1511
|
-
function resolveFoldConfig(options) {
|
|
1512
|
-
return FoldOptionsSchema.parse(options);
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
1522
|
// src/internal/correlate-cycle.ts
|
|
1516
1523
|
import { createHash, randomUUID } from "crypto";
|
|
1517
1524
|
|
|
@@ -1589,7 +1596,6 @@ var CorrelateCycle = class {
|
|
|
1589
1596
|
_armed = true;
|
|
1590
1597
|
/** In-flight init, memoized for single-flight and cleared on failure. */
|
|
1591
1598
|
_init_promise;
|
|
1592
|
-
_timer = void 0;
|
|
1593
1599
|
// Dynamically discovered targets → what each was last subscribed at,
|
|
1594
1600
|
// bounded by `maxSubscribedStreams`. The static half lives in
|
|
1595
1601
|
// `_static_subscriptions` below, which is deliberately not evictable.
|
|
@@ -1633,14 +1639,6 @@ var CorrelateCycle = class {
|
|
|
1633
1639
|
* guarantee without a second gate on the Act side.
|
|
1634
1640
|
*/
|
|
1635
1641
|
_on_init_async;
|
|
1636
|
-
/**
|
|
1637
|
-
* Scope runner (#1191). The periodic `start_polling` timer fires
|
|
1638
|
-
* outside any caller frame, so its `correlate()` must be re-wrapped in
|
|
1639
|
-
* the Act's `_scoped` bag or `store()`/`cache()` resolve to the
|
|
1640
|
-
* singleton for a scoped Act. The orchestrator always threads its
|
|
1641
|
-
* `_scoped` (identity for a non-scoped Act), so it's required.
|
|
1642
|
-
*/
|
|
1643
|
-
_run_scoped;
|
|
1644
1642
|
/**
|
|
1645
1643
|
* Tail re-scan window applied to the cold-start checkpoint (ACT-1207).
|
|
1646
1644
|
* See {@link DEFAULT_COLD_START_BACK_SCAN}. Constructor arg (not a
|
|
@@ -1661,7 +1659,6 @@ var CorrelateCycle = class {
|
|
|
1661
1659
|
cd,
|
|
1662
1660
|
max_subscribed_streams,
|
|
1663
1661
|
declared_lanes,
|
|
1664
|
-
run_scoped,
|
|
1665
1662
|
on_init,
|
|
1666
1663
|
on_init_async,
|
|
1667
1664
|
cold_start_back_scan = DEFAULT_COLD_START_BACK_SCAN,
|
|
@@ -1675,7 +1672,6 @@ var CorrelateCycle = class {
|
|
|
1675
1672
|
this._static_targets = static_targets;
|
|
1676
1673
|
this._cd = cd;
|
|
1677
1674
|
this._on_init = on_init;
|
|
1678
|
-
this._run_scoped = run_scoped;
|
|
1679
1675
|
this._on_init_async = on_init_async;
|
|
1680
1676
|
this._cold_start_back_scan = cold_start_back_scan;
|
|
1681
1677
|
}
|
|
@@ -1923,33 +1919,6 @@ var CorrelateCycle = class {
|
|
|
1923
1919
|
this._armed = found === query.limit;
|
|
1924
1920
|
return { subscribed: 0, last_id, marked: 0, scanned: true };
|
|
1925
1921
|
}
|
|
1926
|
-
/**
|
|
1927
|
-
* Start a periodic correlation worker. Returns false if one is already
|
|
1928
|
-
* running. Errors from `correlate()` are routed through `log()` so they
|
|
1929
|
-
* land in the configured logger (the timer keeps running on failure).
|
|
1930
|
-
*/
|
|
1931
|
-
start_polling(query = {}, frequency = 1e4, callback) {
|
|
1932
|
-
if (this._timer) return false;
|
|
1933
|
-
const limit = query.limit || 100;
|
|
1934
|
-
this._timer = setInterval(
|
|
1935
|
-
() => this._run_scoped(() => {
|
|
1936
|
-
this.arm();
|
|
1937
|
-
return this.correlate(
|
|
1938
|
-
{
|
|
1939
|
-
...query,
|
|
1940
|
-
after: this._checkpoint,
|
|
1941
|
-
limit
|
|
1942
|
-
},
|
|
1943
|
-
true
|
|
1944
|
-
);
|
|
1945
|
-
}).then((result) => {
|
|
1946
|
-
if (callback && result.subscribed) callback(result.subscribed);
|
|
1947
|
-
}).catch((err) => log().error(err)),
|
|
1948
|
-
frequency
|
|
1949
|
-
);
|
|
1950
|
-
return true;
|
|
1951
|
-
}
|
|
1952
|
-
/** Stop the periodic correlation worker. Idempotent. */
|
|
1953
1922
|
/**
|
|
1954
1923
|
* Hand the correlation lease back early.
|
|
1955
1924
|
*
|
|
@@ -1980,12 +1949,6 @@ var CorrelateCycle = class {
|
|
|
1980
1949
|
);
|
|
1981
1950
|
}
|
|
1982
1951
|
}
|
|
1983
|
-
stop_polling() {
|
|
1984
|
-
if (this._timer) {
|
|
1985
|
-
clearInterval(this._timer);
|
|
1986
|
-
this._timer = void 0;
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
1952
|
};
|
|
1990
1953
|
|
|
1991
1954
|
// src/internal/correlator.ts
|
|
@@ -3083,9 +3046,9 @@ var DrainController = class {
|
|
|
3083
3046
|
if (this._deps.breaker.state(Date.now()) === "open")
|
|
3084
3047
|
return EMPTY_DRAIN;
|
|
3085
3048
|
const d = this._deps.defaults ?? {};
|
|
3086
|
-
const streamLimit = d.streamLimit ?? options.streamLimit ??
|
|
3087
|
-
const eventLimit = d.eventLimit ?? options.eventLimit ??
|
|
3088
|
-
const leaseMillis = d.leaseMillis ?? options.leaseMillis ??
|
|
3049
|
+
const streamLimit = d.streamLimit ?? options.streamLimit ?? DEFAULT_STREAM_LIMIT;
|
|
3050
|
+
const eventLimit = d.eventLimit ?? options.eventLimit ?? DEFAULT_EVENT_LIMIT;
|
|
3051
|
+
const leaseMillis = d.leaseMillis ?? options.leaseMillis ?? DEFAULT_LEASE_MILLIS;
|
|
3089
3052
|
try {
|
|
3090
3053
|
this._locked = true;
|
|
3091
3054
|
this._inflight = new Promise((done) => {
|
|
@@ -3459,10 +3422,8 @@ function build_handle_batch(logger) {
|
|
|
3459
3422
|
}
|
|
3460
3423
|
|
|
3461
3424
|
// src/act.ts
|
|
3462
|
-
var DEFAULT_MAX_SUBSCRIBED_STREAMS = 1e3;
|
|
3463
3425
|
var CLOSE_CATCH_UP_LIMIT = 1e3;
|
|
3464
3426
|
var CLOSE_CATCH_UP_PASSES = 20;
|
|
3465
|
-
var DEFAULT_SETTLE_DEBOUNCE_MS = 10;
|
|
3466
3427
|
function validate_only_lanes(options, lanes) {
|
|
3467
3428
|
if (!options.onlyLanes || options.onlyLanes.length === 0) return;
|
|
3468
3429
|
const declared = /* @__PURE__ */ new Set(["default", ...lanes.map((l) => l.name)]);
|
|
@@ -3488,6 +3449,10 @@ var Act = class {
|
|
|
3488
3449
|
_correlate;
|
|
3489
3450
|
/** Debounced correlate→drain catch-up loop. */
|
|
3490
3451
|
_settle;
|
|
3452
|
+
/** Timer behind {@link start_correlations}. */
|
|
3453
|
+
_poll;
|
|
3454
|
+
/** The deprecated `start_correlations` callback, while the poll runs. */
|
|
3455
|
+
_on_discovered;
|
|
3491
3456
|
/**
|
|
3492
3457
|
* Disposer for the cross-process notify subscription, set up eagerly
|
|
3493
3458
|
* during construction. Held as a promise because the subscription
|
|
@@ -3897,9 +3862,6 @@ var Act = class {
|
|
|
3897
3862
|
on_init: () => {
|
|
3898
3863
|
if (this._drain && this._reactive_events.size > 0) this._arm_all();
|
|
3899
3864
|
},
|
|
3900
|
-
// Re-scope the background `start_correlations` timer so its
|
|
3901
|
-
// correlate resolves the scoped ports, not the singleton (#1191).
|
|
3902
|
-
run_scoped: this._scoped,
|
|
3903
3865
|
// Cold-start defer re-seed (#1221). Skipped on writer-only instances
|
|
3904
3866
|
// (`drain: false`) — they run no local controllers to re-arm.
|
|
3905
3867
|
on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
|
|
@@ -4489,86 +4451,63 @@ var Act = class {
|
|
|
4489
4451
|
const { subscribed, last_id, marked, scanned } = await this._correlate.correlate(query, lease);
|
|
4490
4452
|
if ((subscribed > 0 || marked > 0) && this._reactive_events.size > 0)
|
|
4491
4453
|
this._arm_all();
|
|
4454
|
+
if (subscribed > 0) this._on_discovered?.(subscribed);
|
|
4492
4455
|
return { subscribed, last_id, scanned };
|
|
4493
4456
|
});
|
|
4494
4457
|
}
|
|
4495
4458
|
/**
|
|
4496
|
-
* Starts
|
|
4459
|
+
* Starts a background poll that settles on a timer: each tick looks for
|
|
4460
|
+
* new events, subscribes the streams their reactions target, and drains
|
|
4461
|
+
* them.
|
|
4462
|
+
*
|
|
4463
|
+
* Use it when this process can miss commits — another process writes to a
|
|
4464
|
+
* store without `notify`, or a notification is lost. It is the safety net
|
|
4465
|
+
* under `notify`: a missed wakeup costs at most one `frequency` of latency.
|
|
4466
|
+
*
|
|
4467
|
+
* Only one poll runs per Act instance. On a writer-only instance
|
|
4468
|
+
* (`drain: false`) each tick is a no-op, like `settle()`.
|
|
4469
|
+
*
|
|
4470
|
+
* @param query - Only `limit` is used: the most events each correlate pass
|
|
4471
|
+
* reads (default 100). The scan always starts at the correlation
|
|
4472
|
+
* checkpoint, so `after` is ignored.
|
|
4473
|
+
* @param frequency - Milliseconds between ticks (default: 10000)
|
|
4474
|
+
* @param callback - Called with the number of newly subscribed streams
|
|
4475
|
+
* after each correlate pass that found some.
|
|
4476
|
+
* @deprecated Listen to the `"settled"` lifecycle event instead.
|
|
4477
|
+
* @returns `true` if the poll started, `false` if one is already running
|
|
4497
4478
|
*
|
|
4498
|
-
*
|
|
4499
|
-
* new target streams based on reaction resolvers. It maintains a sliding window that
|
|
4500
|
-
* advances with each scan, ensuring all events are eventually correlated.
|
|
4501
|
-
*
|
|
4502
|
-
* This is useful for dynamic stream creation patterns where you don't know all streams
|
|
4503
|
-
* upfront - they're discovered as events arrive.
|
|
4504
|
-
*
|
|
4505
|
-
* **Note:** Only one correlation worker can run at a time per Act instance.
|
|
4506
|
-
*
|
|
4507
|
-
* @param query - Query filter for correlation scans — see {@link Query}
|
|
4508
|
-
* (typically `{ after: -1, limit: 100 }`)
|
|
4509
|
-
* @param frequency - Correlation frequency in milliseconds (default: 10000)
|
|
4510
|
-
* @param callback - Optional callback invoked with newly discovered streams
|
|
4511
|
-
* @returns `true` if worker started, `false` if already running
|
|
4512
|
-
*
|
|
4513
|
-
* @example Start automatic correlation
|
|
4479
|
+
* @example
|
|
4514
4480
|
* ```typescript
|
|
4515
|
-
*
|
|
4516
|
-
*
|
|
4517
|
-
* { after: 0, limit: 100 },
|
|
4518
|
-
* 5000,
|
|
4519
|
-
* (leased) => {
|
|
4520
|
-
* console.log(`Discovered ${leased.length} new streams`);
|
|
4521
|
-
* }
|
|
4522
|
-
* );
|
|
4523
|
-
*
|
|
4524
|
-
* // Later, stop it
|
|
4481
|
+
* app.start_correlations({ limit: 100 }, 5000);
|
|
4482
|
+
* // later
|
|
4525
4483
|
* app.stop_correlations();
|
|
4526
4484
|
* ```
|
|
4527
4485
|
*
|
|
4528
|
-
* @
|
|
4529
|
-
*
|
|
4530
|
-
* // Load last checkpoint
|
|
4531
|
-
* const lastId = await loadCheckpoint();
|
|
4532
|
-
*
|
|
4533
|
-
* app.start_correlations(
|
|
4534
|
-
* { after: lastId, limit: 100 },
|
|
4535
|
-
* 10000,
|
|
4536
|
-
* async (leased) => {
|
|
4537
|
-
* // Save checkpoint for next restart
|
|
4538
|
-
* if (leased.length) {
|
|
4539
|
-
* const maxId = Math.max(...leased.map(l => l.at));
|
|
4540
|
-
* await saveCheckpoint(maxId);
|
|
4541
|
-
* }
|
|
4542
|
-
* }
|
|
4543
|
-
* );
|
|
4544
|
-
* ```
|
|
4545
|
-
*
|
|
4546
|
-
* @see {@link correlate} for manual one-time correlation
|
|
4547
|
-
* @see {@link stop_correlations} to stop the worker
|
|
4486
|
+
* @see {@link settle} for what each tick runs
|
|
4487
|
+
* @see {@link stop_correlations} to stop the poll
|
|
4548
4488
|
*/
|
|
4549
4489
|
start_correlations(query = {}, frequency = 1e4, callback) {
|
|
4550
|
-
|
|
4551
|
-
|
|
4490
|
+
if (this._poll) return false;
|
|
4491
|
+
this._on_discovered = callback;
|
|
4492
|
+
const correlate = { limit: query.limit || 100 };
|
|
4493
|
+
this._poll = setInterval(() => {
|
|
4494
|
+
this._correlate.arm();
|
|
4495
|
+
this.settle({ debounceMs: 0, correlate });
|
|
4496
|
+
}, frequency);
|
|
4497
|
+
return true;
|
|
4552
4498
|
}
|
|
4553
4499
|
/**
|
|
4554
|
-
* Stops the
|
|
4555
|
-
*
|
|
4556
|
-
* Call this to stop the background correlation worker started by {@link start_correlations}.
|
|
4557
|
-
* This is automatically called when the Act instance is disposed.
|
|
4558
|
-
*
|
|
4559
|
-
* @example
|
|
4560
|
-
* ```typescript
|
|
4561
|
-
* // Start correlation
|
|
4562
|
-
* app.start_correlations();
|
|
4563
|
-
*
|
|
4564
|
-
* // Later, stop it
|
|
4565
|
-
* app.stop_correlations();
|
|
4566
|
-
* ```
|
|
4500
|
+
* Stops the poll started by {@link start_correlations}. Idempotent, and
|
|
4501
|
+
* called by `shutdown()`.
|
|
4567
4502
|
*
|
|
4568
4503
|
* @see {@link start_correlations}
|
|
4569
4504
|
*/
|
|
4570
4505
|
stop_correlations() {
|
|
4571
|
-
this.
|
|
4506
|
+
if (this._poll) {
|
|
4507
|
+
clearInterval(this._poll);
|
|
4508
|
+
this._poll = void 0;
|
|
4509
|
+
}
|
|
4510
|
+
this._on_discovered = void 0;
|
|
4572
4511
|
void this._scoped(() => this._correlate.release_correlation());
|
|
4573
4512
|
}
|
|
4574
4513
|
/**
|
|
@@ -5225,8 +5164,8 @@ function reaction_on(event, events, builder) {
|
|
|
5225
5164
|
// `maxRetries`/`backoff`/`blockOnError` throws ZodError at build,
|
|
5226
5165
|
// not a NaN gate on the first retry.
|
|
5227
5166
|
options: resolveReactionConfig({
|
|
5228
|
-
blockOnError: options?.blockOnError ??
|
|
5229
|
-
maxRetries: options?.maxRetries ??
|
|
5167
|
+
blockOnError: options?.blockOnError ?? DEFAULT_BLOCK_ON_ERROR,
|
|
5168
|
+
maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
5230
5169
|
backoff: options?.backoff
|
|
5231
5170
|
})
|
|
5232
5171
|
};
|
|
@@ -5617,8 +5556,8 @@ function _projection(target) {
|
|
|
5617
5556
|
handler,
|
|
5618
5557
|
resolver: default_resolver ?? _this_,
|
|
5619
5558
|
options: {
|
|
5620
|
-
blockOnError:
|
|
5621
|
-
maxRetries:
|
|
5559
|
+
blockOnError: DEFAULT_BLOCK_ON_ERROR,
|
|
5560
|
+
maxRetries: DEFAULT_MAX_RETRIES
|
|
5622
5561
|
}
|
|
5623
5562
|
};
|
|
5624
5563
|
const register = events[event];
|
|
@@ -5691,7 +5630,10 @@ function _projection(target) {
|
|
|
5691
5630
|
{
|
|
5692
5631
|
handler: noop,
|
|
5693
5632
|
resolver: { target },
|
|
5694
|
-
options: {
|
|
5633
|
+
options: {
|
|
5634
|
+
blockOnError: DEFAULT_BLOCK_ON_ERROR,
|
|
5635
|
+
maxRetries: DEFAULT_MAX_RETRIES
|
|
5636
|
+
}
|
|
5695
5637
|
}
|
|
5696
5638
|
]
|
|
5697
5639
|
])
|