@rotorsoft/act 1.10.1 → 1.11.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/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +71 -9
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/builders/slice-builder.d.ts +1 -1
- package/dist/@types/builders/state-builder.d.ts +88 -1
- package/dist/@types/builders/state-builder.d.ts.map +1 -1
- package/dist/@types/internal/autoclose-config.d.ts +72 -0
- package/dist/@types/internal/autoclose-config.d.ts.map +1 -0
- package/dist/@types/internal/autoclose-cycle.d.ts +171 -0
- package/dist/@types/internal/autoclose-cycle.d.ts.map +1 -0
- package/dist/@types/internal/event-sourcing.d.ts +7 -7
- package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
- package/dist/@types/internal/index.d.ts +3 -1
- package/dist/@types/internal/index.d.ts.map +1 -1
- package/dist/@types/internal/reactions.d.ts.map +1 -1
- package/dist/@types/internal/sensitive.d.ts +6 -23
- package/dist/@types/internal/sensitive.d.ts.map +1 -1
- package/dist/@types/internal/tracing.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +141 -16
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/registry.d.ts +35 -3
- package/dist/@types/types/registry.d.ts.map +1 -1
- package/dist/{chunk-3Z2HU726.js → chunk-5PWLGL5E.js} +2 -2
- package/dist/{chunk-BY5JPOZR.js → chunk-NGVWJXEF.js} +9 -24
- package/dist/{chunk-BY5JPOZR.js.map → chunk-NGVWJXEF.js.map} +1 -1
- package/dist/index.cjs +411 -195
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +399 -174
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +2 -2
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +4 -4
- package/dist/test/index.js.map +1 -1
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.js +1 -1
- package/package.json +2 -2
- /package/dist/{chunk-3Z2HU726.js.map → chunk-5PWLGL5E.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
sleep,
|
|
20
20
|
store,
|
|
21
21
|
validate
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-5PWLGL5E.js";
|
|
23
23
|
import {
|
|
24
24
|
ActorSchema,
|
|
25
25
|
CausationEventSchema,
|
|
@@ -40,11 +40,10 @@ import {
|
|
|
40
40
|
ZodEmpty,
|
|
41
41
|
pii_fields,
|
|
42
42
|
pii_gate,
|
|
43
|
-
pii_merge,
|
|
44
43
|
pii_split,
|
|
45
44
|
pii_strip,
|
|
46
45
|
sensitive
|
|
47
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-NGVWJXEF.js";
|
|
48
47
|
import "./chunk-5WRI5ZAA.js";
|
|
49
48
|
|
|
50
49
|
// src/signals.ts
|
|
@@ -487,57 +486,24 @@ var PASS_FACTORIES = {
|
|
|
487
486
|
"clock-anomalies": make_clock_anomalies_pass
|
|
488
487
|
};
|
|
489
488
|
|
|
490
|
-
// src/internal/
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const set = existing_lanes ?? /* @__PURE__ */ new Set();
|
|
509
|
-
set.add(lane_name);
|
|
510
|
-
event_to_lanes.set(name, set);
|
|
511
|
-
}
|
|
512
|
-
const key = `${target}|${source ?? ""}`;
|
|
513
|
-
const existing = statics.get(key);
|
|
514
|
-
if (!existing) {
|
|
515
|
-
statics.set(key, { stream: target, source, priority, lane });
|
|
516
|
-
} else {
|
|
517
|
-
if ((existing.lane ?? void 0) !== (lane ?? void 0))
|
|
518
|
-
throw new Error(
|
|
519
|
-
`Stream "${target}" has conflicting lane assignments ("${existing.lane ?? "default"}" vs "${lane ?? "default"}")`
|
|
520
|
-
);
|
|
521
|
-
if (priority > existing.priority) {
|
|
522
|
-
statics.set(key, { ...existing, priority });
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
const event_to_state = /* @__PURE__ */ new Map();
|
|
529
|
-
for (const merged of states.values()) {
|
|
530
|
-
for (const event_name of Object.keys(merged.events)) {
|
|
531
|
-
event_to_state.set(event_name, merged);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
return {
|
|
535
|
-
static_targets: [...statics.values()],
|
|
536
|
-
has_dynamic_resolvers,
|
|
537
|
-
reactive_events,
|
|
538
|
-
event_to_state,
|
|
539
|
-
event_to_lanes
|
|
540
|
-
};
|
|
489
|
+
// src/internal/autoclose-config.ts
|
|
490
|
+
import { z } from "zod";
|
|
491
|
+
var DEFAULT_AUTOCLOSE_CYCLE_MS = 6e4;
|
|
492
|
+
var DEFAULT_CLOSE_BATCH_SIZE = 64;
|
|
493
|
+
var DEFAULT_CLOSE_YIELD_MS = 0;
|
|
494
|
+
var AutocloseOptionsSchema = z.object({
|
|
495
|
+
autocloseCycleMs: z.number().min(1e4).max(36e5).default(DEFAULT_AUTOCLOSE_CYCLE_MS),
|
|
496
|
+
closeBatchSize: z.number().int().min(1).max(1024).default(DEFAULT_CLOSE_BATCH_SIZE),
|
|
497
|
+
closeYieldMs: z.number().min(0).max(1e3).default(DEFAULT_CLOSE_YIELD_MS),
|
|
498
|
+
closeOnError: z.boolean().default(false)
|
|
499
|
+
});
|
|
500
|
+
function resolveAutocloseConfig(options) {
|
|
501
|
+
return AutocloseOptionsSchema.parse({
|
|
502
|
+
autocloseCycleMs: options?.autocloseCycleMs,
|
|
503
|
+
closeBatchSize: options?.closeBatchSize,
|
|
504
|
+
closeYieldMs: options?.closeYieldMs,
|
|
505
|
+
closeOnError: options?.closeOnError
|
|
506
|
+
});
|
|
541
507
|
}
|
|
542
508
|
|
|
543
509
|
// src/internal/close-cycle.ts
|
|
@@ -639,7 +605,7 @@ async function load_restart_seeds(guarded, target_map, stream_info, event_to_sta
|
|
|
639
605
|
);
|
|
640
606
|
return;
|
|
641
607
|
}
|
|
642
|
-
const snap2 = await load2(owner_state, stream);
|
|
608
|
+
const snap2 = await load2(owner_state, { stream });
|
|
643
609
|
seed_states.set(stream, snap2.state);
|
|
644
610
|
})
|
|
645
611
|
);
|
|
@@ -687,6 +653,233 @@ async function truncate_and_warm_cache(guarded, seed_states, guard_events, corre
|
|
|
687
653
|
return truncated;
|
|
688
654
|
}
|
|
689
655
|
|
|
656
|
+
// src/internal/correlator.ts
|
|
657
|
+
import { randomInt } from "crypto";
|
|
658
|
+
var BASE = 36;
|
|
659
|
+
var SEG_WIDTH = 4;
|
|
660
|
+
var SEG_SPACE = BASE ** SEG_WIDTH;
|
|
661
|
+
function seg(n) {
|
|
662
|
+
return n.toString(BASE).padStart(SEG_WIDTH, "0");
|
|
663
|
+
}
|
|
664
|
+
var default_correlator = ({ state: state2, action: action2 }) => {
|
|
665
|
+
const s = state2.slice(0, SEG_WIDTH).toLowerCase();
|
|
666
|
+
const a = action2.slice(0, SEG_WIDTH).toLowerCase();
|
|
667
|
+
const ts = seg(Date.now() % SEG_SPACE);
|
|
668
|
+
const rnd = seg(randomInt(SEG_SPACE));
|
|
669
|
+
return `${s}-${a}-${ts}${rnd}`;
|
|
670
|
+
};
|
|
671
|
+
function close_correlation(correlator, actor) {
|
|
672
|
+
return correlator({
|
|
673
|
+
state: "$close",
|
|
674
|
+
action: "close",
|
|
675
|
+
stream: "$close",
|
|
676
|
+
actor
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// src/internal/autoclose-cycle.ts
|
|
681
|
+
function yield_between_batches(ms) {
|
|
682
|
+
if (ms <= 0) return Promise.resolve();
|
|
683
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
684
|
+
}
|
|
685
|
+
async function run_autoclose_cycle(deps) {
|
|
686
|
+
const s = store();
|
|
687
|
+
let inspected = 0;
|
|
688
|
+
let evaluated = 0;
|
|
689
|
+
let predicate_errors = 0;
|
|
690
|
+
const truncated = /* @__PURE__ */ new Map();
|
|
691
|
+
const skipped = [];
|
|
692
|
+
const stats = await s.query_stats(
|
|
693
|
+
{},
|
|
694
|
+
{ count: true, exclude: [TOMBSTONE_EVENT] }
|
|
695
|
+
);
|
|
696
|
+
const flush_batch = async (candidates2) => {
|
|
697
|
+
if (candidates2.length === 0) return;
|
|
698
|
+
const result = await run_close_cycle(candidates2, {
|
|
699
|
+
reactive_events_size: deps.reactive_events_size,
|
|
700
|
+
event_to_state: deps.event_to_state,
|
|
701
|
+
load: deps.load,
|
|
702
|
+
tombstone: deps.tombstone,
|
|
703
|
+
logger: deps.logger,
|
|
704
|
+
correlation: deps.correlation
|
|
705
|
+
});
|
|
706
|
+
for (const [stream, entry] of result.truncated) {
|
|
707
|
+
truncated.set(stream, entry);
|
|
708
|
+
}
|
|
709
|
+
for (const stream of result.skipped) skipped.push(stream);
|
|
710
|
+
await yield_between_batches(deps.config.closeYieldMs);
|
|
711
|
+
};
|
|
712
|
+
let candidates = [];
|
|
713
|
+
for (const [stream, { head, count }] of stats) {
|
|
714
|
+
inspected += 1;
|
|
715
|
+
const owner = deps.event_to_state.get(head.name);
|
|
716
|
+
if (!owner) continue;
|
|
717
|
+
const predicate = deps.autoclose_policy(owner.name);
|
|
718
|
+
if (!predicate) continue;
|
|
719
|
+
let close = false;
|
|
720
|
+
try {
|
|
721
|
+
close = predicate(stream, head, count ?? 0);
|
|
722
|
+
evaluated += 1;
|
|
723
|
+
} catch (err) {
|
|
724
|
+
predicate_errors += 1;
|
|
725
|
+
deps.logger.error(
|
|
726
|
+
`Autoclose predicate for state "${owner.name}" threw on stream "${stream}": ${err instanceof Error ? err.message : String(err)}`
|
|
727
|
+
);
|
|
728
|
+
close = deps.config.closeOnError;
|
|
729
|
+
}
|
|
730
|
+
if (!close) continue;
|
|
731
|
+
const archiver = deps.autoclose_archiver(owner.name);
|
|
732
|
+
const archive = archiver ? () => archiver(stream, head) : void 0;
|
|
733
|
+
candidates.push({ stream, archive });
|
|
734
|
+
if (candidates.length >= deps.config.closeBatchSize) {
|
|
735
|
+
await flush_batch(candidates);
|
|
736
|
+
candidates = [];
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
await flush_batch(candidates);
|
|
740
|
+
return {
|
|
741
|
+
inspected,
|
|
742
|
+
evaluated,
|
|
743
|
+
predicate_errors,
|
|
744
|
+
close_result: { truncated, skipped }
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
var AutocloseController = class {
|
|
748
|
+
deps;
|
|
749
|
+
_timer;
|
|
750
|
+
/**
|
|
751
|
+
* Reentrancy guard. The interval fires the next tick on cadence
|
|
752
|
+
* regardless of how long the previous one took; this flag drops
|
|
753
|
+
* overlapping ticks so the cycle stays sequential per controller.
|
|
754
|
+
*/
|
|
755
|
+
_running = false;
|
|
756
|
+
constructor(deps) {
|
|
757
|
+
this.deps = deps;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Start the cycle ticker. Returns `false` if already running so
|
|
761
|
+
* accidental double-start doesn't stack timers (matches
|
|
762
|
+
* `_correlate.start_polling` semantics).
|
|
763
|
+
*/
|
|
764
|
+
start() {
|
|
765
|
+
if (this._timer) return false;
|
|
766
|
+
this._timer = setInterval(() => {
|
|
767
|
+
this.run_once().catch((err) => {
|
|
768
|
+
this.deps.logger.error(
|
|
769
|
+
`Autoclose cycle errored: ${err instanceof Error ? err.message : String(err)}`
|
|
770
|
+
);
|
|
771
|
+
});
|
|
772
|
+
}, this.deps.config.autocloseCycleMs);
|
|
773
|
+
this._timer.unref();
|
|
774
|
+
return true;
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Stop the cycle ticker. Idempotent.
|
|
778
|
+
*/
|
|
779
|
+
stop() {
|
|
780
|
+
if (this._timer) {
|
|
781
|
+
clearInterval(this._timer);
|
|
782
|
+
this._timer = void 0;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Whether the ticker is currently running.
|
|
787
|
+
*/
|
|
788
|
+
get is_running() {
|
|
789
|
+
return this._timer !== void 0;
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Run one cycle synchronously. The controller's interval callback
|
|
793
|
+
* delegates here; tests can invoke it directly to deterministically
|
|
794
|
+
* exercise the cycle without spinning real timers.
|
|
795
|
+
*
|
|
796
|
+
* Overlapping invocations short-circuit: if a previous tick is
|
|
797
|
+
* still running, this one drops and returns `null`. The orchestrator
|
|
798
|
+
* never sees a queue of pending ticks regardless of how slow the
|
|
799
|
+
* predicate / archive / truncate are.
|
|
800
|
+
*/
|
|
801
|
+
async run_once() {
|
|
802
|
+
if (this._running) return null;
|
|
803
|
+
this._running = true;
|
|
804
|
+
try {
|
|
805
|
+
const correlation = close_correlation(
|
|
806
|
+
this.deps.correlator,
|
|
807
|
+
this.deps.close_actor
|
|
808
|
+
);
|
|
809
|
+
const result = await run_autoclose_cycle({
|
|
810
|
+
autoclose_policy: this.deps.autoclose_policy,
|
|
811
|
+
autoclose_archiver: this.deps.autoclose_archiver,
|
|
812
|
+
event_to_state: this.deps.event_to_state,
|
|
813
|
+
reactive_events_size: this.deps.reactive_events_size,
|
|
814
|
+
load: this.deps.load,
|
|
815
|
+
tombstone: this.deps.tombstone,
|
|
816
|
+
logger: this.deps.logger,
|
|
817
|
+
config: this.deps.config,
|
|
818
|
+
correlation
|
|
819
|
+
});
|
|
820
|
+
if (result.close_result.truncated.size > 0) {
|
|
821
|
+
this.deps.on_closed(result.close_result);
|
|
822
|
+
}
|
|
823
|
+
return result;
|
|
824
|
+
} finally {
|
|
825
|
+
this._running = false;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
// src/internal/build-classify.ts
|
|
831
|
+
var ALL_LANES = /* @__PURE__ */ Symbol("act-1103/all-lanes");
|
|
832
|
+
function classify_registry(registry, states) {
|
|
833
|
+
const statics = /* @__PURE__ */ new Map();
|
|
834
|
+
const reactive_events = /* @__PURE__ */ new Set();
|
|
835
|
+
const event_to_lanes = /* @__PURE__ */ new Map();
|
|
836
|
+
let has_dynamic_resolvers = false;
|
|
837
|
+
for (const [name, register] of Object.entries(registry.events)) {
|
|
838
|
+
if (register.reactions.size > 0) reactive_events.add(name);
|
|
839
|
+
for (const reaction of register.reactions.values()) {
|
|
840
|
+
if (typeof reaction.resolver === "function") {
|
|
841
|
+
has_dynamic_resolvers = true;
|
|
842
|
+
event_to_lanes.set(name, ALL_LANES);
|
|
843
|
+
} else {
|
|
844
|
+
const { target, source, priority = 0, lane } = reaction.resolver;
|
|
845
|
+
const lane_name = lane ?? "default";
|
|
846
|
+
const existing_lanes = event_to_lanes.get(name);
|
|
847
|
+
if (existing_lanes !== ALL_LANES) {
|
|
848
|
+
const set = existing_lanes ?? /* @__PURE__ */ new Set();
|
|
849
|
+
set.add(lane_name);
|
|
850
|
+
event_to_lanes.set(name, set);
|
|
851
|
+
}
|
|
852
|
+
const key = `${target}|${source ?? ""}`;
|
|
853
|
+
const existing = statics.get(key);
|
|
854
|
+
if (!existing) {
|
|
855
|
+
statics.set(key, { stream: target, source, priority, lane });
|
|
856
|
+
} else {
|
|
857
|
+
if ((existing.lane ?? void 0) !== (lane ?? void 0))
|
|
858
|
+
throw new Error(
|
|
859
|
+
`Stream "${target}" has conflicting lane assignments ("${existing.lane ?? "default"}" vs "${lane ?? "default"}")`
|
|
860
|
+
);
|
|
861
|
+
if (priority > existing.priority) {
|
|
862
|
+
statics.set(key, { ...existing, priority });
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
const event_to_state = /* @__PURE__ */ new Map();
|
|
869
|
+
for (const merged of states.values()) {
|
|
870
|
+
for (const event_name of Object.keys(merged.events)) {
|
|
871
|
+
event_to_state.set(event_name, merged);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return {
|
|
875
|
+
static_targets: [...statics.values()],
|
|
876
|
+
has_dynamic_resolvers,
|
|
877
|
+
reactive_events,
|
|
878
|
+
event_to_state,
|
|
879
|
+
event_to_lanes
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
|
|
690
883
|
// src/internal/correlate-cycle.ts
|
|
691
884
|
var CorrelateCycle = class {
|
|
692
885
|
_checkpoint = -1;
|
|
@@ -815,30 +1008,6 @@ var CorrelateCycle = class {
|
|
|
815
1008
|
}
|
|
816
1009
|
};
|
|
817
1010
|
|
|
818
|
-
// src/internal/correlator.ts
|
|
819
|
-
import { randomInt } from "crypto";
|
|
820
|
-
var BASE = 36;
|
|
821
|
-
var SEG_WIDTH = 4;
|
|
822
|
-
var SEG_SPACE = BASE ** SEG_WIDTH;
|
|
823
|
-
function seg(n) {
|
|
824
|
-
return n.toString(BASE).padStart(SEG_WIDTH, "0");
|
|
825
|
-
}
|
|
826
|
-
var default_correlator = ({ state: state2, action: action2 }) => {
|
|
827
|
-
const s = state2.slice(0, SEG_WIDTH).toLowerCase();
|
|
828
|
-
const a = action2.slice(0, SEG_WIDTH).toLowerCase();
|
|
829
|
-
const ts = seg(Date.now() % SEG_SPACE);
|
|
830
|
-
const rnd = seg(randomInt(SEG_SPACE));
|
|
831
|
-
return `${s}-${a}-${ts}${rnd}`;
|
|
832
|
-
};
|
|
833
|
-
function close_correlation(correlator, actor) {
|
|
834
|
-
return correlator({
|
|
835
|
-
state: "$close",
|
|
836
|
-
action: "close",
|
|
837
|
-
stream: "$close",
|
|
838
|
-
actor
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
|
|
842
1011
|
// src/internal/drain-cycle.ts
|
|
843
1012
|
import { randomUUID } from "crypto";
|
|
844
1013
|
|
|
@@ -1003,7 +1172,6 @@ async function scan(source, opts = {}, callback) {
|
|
|
1003
1172
|
migrated = {
|
|
1004
1173
|
...event,
|
|
1005
1174
|
name: migration.to,
|
|
1006
|
-
// biome-ignore lint/suspicious/noExplicitAny: migration target shape is caller-defined
|
|
1007
1175
|
data: new_data
|
|
1008
1176
|
};
|
|
1009
1177
|
migrated_count++;
|
|
@@ -1052,39 +1220,37 @@ async function scan(source, opts = {}, callback) {
|
|
|
1052
1220
|
}
|
|
1053
1221
|
};
|
|
1054
1222
|
}
|
|
1055
|
-
async function load(me,
|
|
1223
|
+
async function load(me, target, callback) {
|
|
1224
|
+
const { stream, actor, asOf } = target;
|
|
1056
1225
|
const time_travel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
|
|
1057
1226
|
const cached = time_travel ? void 0 : await cache().get(stream);
|
|
1058
1227
|
const cache_hit = !!cached;
|
|
1059
1228
|
let state2 = cached?.state ?? (me.init ? me.init() : {});
|
|
1060
1229
|
let patches = cached?.patches ?? 0;
|
|
1061
1230
|
let snaps = cached?.snaps ?? 0;
|
|
1062
|
-
let version = cached?.version ?? -1;
|
|
1063
1231
|
let replayed = 0;
|
|
1064
1232
|
let event;
|
|
1065
1233
|
await store().query(
|
|
1066
|
-
(
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
if (e.name === SNAP_EVENT) {
|
|
1071
|
-
state2 = e.data;
|
|
1234
|
+
(raw) => {
|
|
1235
|
+
event = me.view(raw, actor);
|
|
1236
|
+
if (event.name === SNAP_EVENT) {
|
|
1237
|
+
state2 = event.data;
|
|
1072
1238
|
snaps++;
|
|
1073
1239
|
patches = 0;
|
|
1074
1240
|
replayed++;
|
|
1075
|
-
} else if (me.patch[
|
|
1076
|
-
state2 = patch(state2, me.patch[
|
|
1241
|
+
} else if (me.patch[event.name]) {
|
|
1242
|
+
state2 = patch(state2, me.patch[event.name](event, state2));
|
|
1077
1243
|
patches++;
|
|
1078
1244
|
replayed++;
|
|
1079
|
-
} else if (
|
|
1245
|
+
} else if (event.name !== TOMBSTONE_EVENT) {
|
|
1080
1246
|
log().warn(
|
|
1081
|
-
`Skipping unknown event "${String(
|
|
1247
|
+
`Skipping unknown event "${String(event.name)}" on stream "${stream}" (id=${event.id}) \u2014 no reducer in state "${me.name}"`
|
|
1082
1248
|
);
|
|
1083
1249
|
}
|
|
1084
1250
|
callback?.({
|
|
1085
1251
|
event,
|
|
1086
1252
|
state: state2,
|
|
1087
|
-
version,
|
|
1253
|
+
version: event.version,
|
|
1088
1254
|
patches,
|
|
1089
1255
|
snaps,
|
|
1090
1256
|
cache_hit,
|
|
@@ -1097,39 +1263,43 @@ async function load(me, stream, callback, asOf, actor) {
|
|
|
1097
1263
|
...cached ? { after: cached.event_id } : { with_snaps: true, ...asOf }
|
|
1098
1264
|
}
|
|
1099
1265
|
);
|
|
1100
|
-
if (replayed > 0 && !time_travel && event) {
|
|
1266
|
+
if (replayed > 0 && !time_travel && event && !me.pii_aware) {
|
|
1101
1267
|
await cache().set(stream, {
|
|
1102
1268
|
state: state2,
|
|
1103
|
-
version,
|
|
1269
|
+
version: event.version,
|
|
1104
1270
|
event_id: event.id,
|
|
1105
1271
|
patches,
|
|
1106
1272
|
snaps
|
|
1107
1273
|
});
|
|
1108
1274
|
}
|
|
1109
|
-
return {
|
|
1275
|
+
return {
|
|
1276
|
+
event,
|
|
1277
|
+
state: state2,
|
|
1278
|
+
version: event?.version ?? cached?.version ?? -1,
|
|
1279
|
+
patches,
|
|
1280
|
+
snaps,
|
|
1281
|
+
cache_hit,
|
|
1282
|
+
replayed
|
|
1283
|
+
};
|
|
1110
1284
|
}
|
|
1111
|
-
async function action(me, action2, target, payload,
|
|
1285
|
+
async function action(me, action2, target, payload, options) {
|
|
1112
1286
|
const { stream, expectedVersion, actor } = target;
|
|
1113
1287
|
if (!stream) throw new Error("Missing target stream");
|
|
1114
|
-
const
|
|
1288
|
+
const reactingTo = options?.reactingTo;
|
|
1289
|
+
const correlator = options?.correlator ?? default_correlator;
|
|
1290
|
+
const validated = validate(action2, payload, me.actions[action2]);
|
|
1115
1291
|
const opts = me.options?.[action2];
|
|
1116
1292
|
const max_retries = opts?.maxRetries ?? 0;
|
|
1117
1293
|
for (let attempt = 0; ; attempt++) {
|
|
1118
1294
|
try {
|
|
1119
|
-
const snapshot = await load(
|
|
1120
|
-
me,
|
|
1121
|
-
stream,
|
|
1122
|
-
void 0,
|
|
1123
|
-
void 0,
|
|
1124
|
-
target.actor
|
|
1125
|
-
);
|
|
1295
|
+
const snapshot = await load(me, { stream, actor: target.actor });
|
|
1126
1296
|
if (snapshot.event?.name === TOMBSTONE_EVENT)
|
|
1127
1297
|
throw new StreamClosedError(stream);
|
|
1128
1298
|
const expected = expectedVersion ?? snapshot.event?.version;
|
|
1129
1299
|
if (me.given) {
|
|
1130
1300
|
const invariants = me.given[action2] || [];
|
|
1131
|
-
invariants.forEach(({ valid, description }) => {
|
|
1132
|
-
if (!
|
|
1301
|
+
invariants.forEach(({ valid: valid2, description }) => {
|
|
1302
|
+
if (!valid2(snapshot.state, actor))
|
|
1133
1303
|
throw new InvariantError(
|
|
1134
1304
|
action2,
|
|
1135
1305
|
validated,
|
|
@@ -1145,24 +1315,11 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1145
1315
|
return [snapshot];
|
|
1146
1316
|
}
|
|
1147
1317
|
const tuples = Array.isArray(result[0]) ? result : [result];
|
|
1148
|
-
const
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
const evt = name;
|
|
1154
|
-
if (deprecated.has(evt) && !warned.has(evt)) {
|
|
1155
|
-
warned.add(evt);
|
|
1156
|
-
log().warn(
|
|
1157
|
-
`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)`
|
|
1158
|
-
);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
const emitted = tuples.map(([name, data]) => {
|
|
1163
|
-
const validated2 = skipValidation ? data : validate(name, data, me.events[name]);
|
|
1164
|
-
return me.message({ name, data: validated2 });
|
|
1165
|
-
});
|
|
1318
|
+
const valid = tuples.map(([name, data]) => ({
|
|
1319
|
+
name,
|
|
1320
|
+
data: validate(name, data, me.events[name])
|
|
1321
|
+
}));
|
|
1322
|
+
const emitted = valid.map((e) => me.message(e));
|
|
1166
1323
|
const meta = {
|
|
1167
1324
|
correlation: reactingTo?.meta.correlation || correlator({
|
|
1168
1325
|
action: action2,
|
|
@@ -1203,12 +1360,13 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1203
1360
|
throw error;
|
|
1204
1361
|
}
|
|
1205
1362
|
let { state: state2, patches } = snapshot;
|
|
1206
|
-
const snapshots = committed.map((
|
|
1363
|
+
const snapshots = committed.map((row, i) => {
|
|
1364
|
+
const event = { ...row, data: valid[i].data };
|
|
1207
1365
|
const p = me.patch[event.name](event, state2);
|
|
1208
1366
|
state2 = patch(state2, p);
|
|
1209
1367
|
patches++;
|
|
1210
1368
|
return {
|
|
1211
|
-
event
|
|
1369
|
+
event,
|
|
1212
1370
|
state: state2,
|
|
1213
1371
|
version: event.version,
|
|
1214
1372
|
patches,
|
|
@@ -1220,13 +1378,15 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
1220
1378
|
});
|
|
1221
1379
|
const last = snapshots.at(-1);
|
|
1222
1380
|
const snapped = me.snap?.(last);
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1381
|
+
if (!me.pii_aware) {
|
|
1382
|
+
cache().set(stream, {
|
|
1383
|
+
state: last.state,
|
|
1384
|
+
version: last.event.version,
|
|
1385
|
+
event_id: last.event.id,
|
|
1386
|
+
patches: snapped ? 0 : last.patches,
|
|
1387
|
+
snaps: snapped ? last.snaps + 1 : last.snaps
|
|
1388
|
+
}).catch((err) => log().error(err));
|
|
1389
|
+
}
|
|
1230
1390
|
if (snapped) void snap(last);
|
|
1231
1391
|
return snapshots;
|
|
1232
1392
|
} catch (error) {
|
|
@@ -1293,15 +1453,7 @@ var traced = (inner, exit, entry) => (async (...args) => {
|
|
|
1293
1453
|
return result;
|
|
1294
1454
|
});
|
|
1295
1455
|
function build_es(logger, correlator = default_correlator) {
|
|
1296
|
-
const bound_action = (me, action_name, target, payload,
|
|
1297
|
-
me,
|
|
1298
|
-
action_name,
|
|
1299
|
-
target,
|
|
1300
|
-
payload,
|
|
1301
|
-
reactingTo,
|
|
1302
|
-
skipValidation,
|
|
1303
|
-
correlator
|
|
1304
|
-
);
|
|
1456
|
+
const bound_action = (me, action_name, target, payload, options) => action(me, action_name, target, payload, { correlator, ...options });
|
|
1305
1457
|
if (logger.level !== "trace") {
|
|
1306
1458
|
return {
|
|
1307
1459
|
snap,
|
|
@@ -1320,7 +1472,7 @@ function build_es(logger, correlator = default_correlator) {
|
|
|
1320
1472
|
)
|
|
1321
1473
|
);
|
|
1322
1474
|
}),
|
|
1323
|
-
load: traced(load, (result, _me,
|
|
1475
|
+
load: traced(load, (result, _me, target) => {
|
|
1324
1476
|
const stats = stats_marker(
|
|
1325
1477
|
result.version,
|
|
1326
1478
|
result.replayed,
|
|
@@ -1331,7 +1483,7 @@ function build_es(logger, correlator = default_correlator) {
|
|
|
1331
1483
|
es_caption(
|
|
1332
1484
|
"load",
|
|
1333
1485
|
C_GREEN,
|
|
1334
|
-
`${stream}${as_of_marker(asOf)} ${cache_marker(result.cache_hit)} ${stats}`
|
|
1486
|
+
`${target.stream}${as_of_marker(target.asOf)} ${cache_marker(result.cache_hit)} ${stats}`
|
|
1335
1487
|
)
|
|
1336
1488
|
);
|
|
1337
1489
|
}),
|
|
@@ -1819,13 +1971,10 @@ function build_handle(deps) {
|
|
|
1819
1971
|
};
|
|
1820
1972
|
for (const payload of payloads) {
|
|
1821
1973
|
const { event, handler } = payload;
|
|
1822
|
-
scoped_app.do = (action2, target, action_payload,
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
reactingTo ?? event,
|
|
1827
|
-
skipValidation
|
|
1828
|
-
);
|
|
1974
|
+
scoped_app.do = (action2, target, action_payload, options) => bound_do(action2, target, action_payload, {
|
|
1975
|
+
...options,
|
|
1976
|
+
reactingTo: options?.reactingTo ?? event
|
|
1977
|
+
});
|
|
1829
1978
|
try {
|
|
1830
1979
|
await handler(event, stream, scoped_app);
|
|
1831
1980
|
at = event.id;
|
|
@@ -1962,6 +2111,22 @@ var Act = class {
|
|
|
1962
2111
|
registry;
|
|
1963
2112
|
/** Map of state name → state definition; populated by the builder. */
|
|
1964
2113
|
_states;
|
|
2114
|
+
/**
|
|
2115
|
+
* Resolved autoclose configuration (#837 / epic #802). Frozen at
|
|
2116
|
+
* `act().build()` via {@link resolveAutocloseConfig}.
|
|
2117
|
+
*
|
|
2118
|
+
* @internal
|
|
2119
|
+
*/
|
|
2120
|
+
_autoclose_config;
|
|
2121
|
+
/**
|
|
2122
|
+
* App-level autoclose controller. `undefined` when no state declares
|
|
2123
|
+
* `.autocloses(...)` — the controller is never constructed, so
|
|
2124
|
+
* `start_correlations()` / `stop_correlations()` skip the autoclose
|
|
2125
|
+
* lifecycle and the orchestrator pays zero per-tick cost.
|
|
2126
|
+
*
|
|
2127
|
+
* @internal
|
|
2128
|
+
*/
|
|
2129
|
+
_autoclose;
|
|
1965
2130
|
/**
|
|
1966
2131
|
* Emit a lifecycle event. The payload type is inferred from the event name
|
|
1967
2132
|
* via {@link ActLifecycleEvents}.
|
|
@@ -2096,6 +2261,7 @@ var Act = class {
|
|
|
2096
2261
|
this._reactive_events = reactive_events;
|
|
2097
2262
|
this._listen = options.listen !== false;
|
|
2098
2263
|
this._drain = options.drain !== false;
|
|
2264
|
+
this._autoclose_config = resolveAutocloseConfig(options);
|
|
2099
2265
|
this._event_to_state = event_to_state;
|
|
2100
2266
|
this._event_to_lanes = event_to_lanes;
|
|
2101
2267
|
const all_lanes = ["default", ...lanes.map((l) => l.name)];
|
|
@@ -2159,6 +2325,26 @@ var Act = class {
|
|
|
2159
2325
|
},
|
|
2160
2326
|
options.settleDebounceMs ?? DEFAULT_SETTLE_DEBOUNCE_MS
|
|
2161
2327
|
);
|
|
2328
|
+
const states_with_policy = [...this._states.values()].filter(
|
|
2329
|
+
(s) => s.autoclose
|
|
2330
|
+
);
|
|
2331
|
+
this._autoclose = states_with_policy.length ? new AutocloseController({
|
|
2332
|
+
// Internal cycle takes the loose `(string) => …` signature; the
|
|
2333
|
+
// public `registry.autoclose_policy` narrows to
|
|
2334
|
+
// `keyof TStateMap & string`. Thin lambdas bridge across the
|
|
2335
|
+
// narrowing — runtime semantics are unchanged.
|
|
2336
|
+
autoclose_policy: (name) => this.registry.autoclose_policy(name),
|
|
2337
|
+
autoclose_archiver: (name) => this.registry.autoclose_archiver(name),
|
|
2338
|
+
event_to_state: this._event_to_state,
|
|
2339
|
+
reactive_events_size: this._reactive_events.size,
|
|
2340
|
+
load: this._es.load,
|
|
2341
|
+
tombstone: this._es.tombstone,
|
|
2342
|
+
logger: this._logger,
|
|
2343
|
+
config: this._autoclose_config,
|
|
2344
|
+
correlator: this._correlator,
|
|
2345
|
+
close_actor: { id: "$autoclose", name: "autoclose" },
|
|
2346
|
+
on_closed: (result) => this.emit("closed", result)
|
|
2347
|
+
}) : void 0;
|
|
2162
2348
|
this._notify_disposer = this._wire_notify(options.scoped?.store ?? store());
|
|
2163
2349
|
dispose(() => this.shutdown());
|
|
2164
2350
|
}
|
|
@@ -2231,8 +2417,9 @@ var Act = class {
|
|
|
2231
2417
|
* @param action - The name of the action to execute
|
|
2232
2418
|
* @param target - Target specification with stream ID and actor context
|
|
2233
2419
|
* @param payload - Action payload matching the action's schema
|
|
2234
|
-
* @param
|
|
2235
|
-
*
|
|
2420
|
+
* @param options - Per-call dispatch options ({@link DoOptions}) —
|
|
2421
|
+
* `reactingTo` to thread correlation, `correlator` to override the
|
|
2422
|
+
* framework or orchestrator-level correlator for this call only.
|
|
2236
2423
|
* @returns Array of snapshots for all affected states (usually one)
|
|
2237
2424
|
*
|
|
2238
2425
|
* @throws {ValidationError} If payload doesn't match action schema
|
|
@@ -2287,7 +2474,7 @@ var Act = class {
|
|
|
2287
2474
|
* { amount: event.data.items.length }
|
|
2288
2475
|
* );
|
|
2289
2476
|
* // To use a different correlation, pass reactingTo explicitly:
|
|
2290
|
-
* // await app.do("reduceStock", target, payload, customEvent);
|
|
2477
|
+
* // await app.do("reduceStock", target, payload, { reactingTo: customEvent });
|
|
2291
2478
|
* })
|
|
2292
2479
|
* .to("inventory-1")
|
|
2293
2480
|
* .build();
|
|
@@ -2297,15 +2484,14 @@ var Act = class {
|
|
|
2297
2484
|
* @see {@link Snapshot} for return value structure
|
|
2298
2485
|
* @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
|
|
2299
2486
|
*/
|
|
2300
|
-
async do(action2, target, payload,
|
|
2487
|
+
async do(action2, target, payload, options) {
|
|
2301
2488
|
return this._scoped(async () => {
|
|
2302
2489
|
const snapshots = await this._es.action(
|
|
2303
2490
|
this.registry.actions[action2],
|
|
2304
2491
|
action2,
|
|
2305
2492
|
target,
|
|
2306
2493
|
payload,
|
|
2307
|
-
|
|
2308
|
-
skipValidation
|
|
2494
|
+
options
|
|
2309
2495
|
);
|
|
2310
2496
|
if (this._reactive_events.size > 0)
|
|
2311
2497
|
this._arm_for_event_names(
|
|
@@ -2315,7 +2501,7 @@ var Act = class {
|
|
|
2315
2501
|
return snapshots;
|
|
2316
2502
|
});
|
|
2317
2503
|
}
|
|
2318
|
-
async load(stateOrName,
|
|
2504
|
+
async load(stateOrName, streamOrTarget, callback, asOf) {
|
|
2319
2505
|
return this._scoped(async () => {
|
|
2320
2506
|
let merged;
|
|
2321
2507
|
if (typeof stateOrName === "string") {
|
|
@@ -2325,7 +2511,12 @@ var Act = class {
|
|
|
2325
2511
|
} else {
|
|
2326
2512
|
merged = this._states.get(stateOrName.name) || stateOrName;
|
|
2327
2513
|
}
|
|
2328
|
-
|
|
2514
|
+
const target = typeof streamOrTarget === "string" ? {
|
|
2515
|
+
stream: streamOrTarget,
|
|
2516
|
+
actor: void 0,
|
|
2517
|
+
asOf
|
|
2518
|
+
} : streamOrTarget;
|
|
2519
|
+
return await this._es.load(merged, target, callback);
|
|
2329
2520
|
});
|
|
2330
2521
|
}
|
|
2331
2522
|
/**
|
|
@@ -2645,7 +2836,9 @@ var Act = class {
|
|
|
2645
2836
|
* @see {@link stop_correlations} to stop the worker
|
|
2646
2837
|
*/
|
|
2647
2838
|
start_correlations(query = {}, frequency = 1e4, callback) {
|
|
2648
|
-
|
|
2839
|
+
const started = this._correlate.start_polling(query, frequency, callback);
|
|
2840
|
+
this._autoclose?.start();
|
|
2841
|
+
return started;
|
|
2649
2842
|
}
|
|
2650
2843
|
/**
|
|
2651
2844
|
* Stops the automatic correlation worker.
|
|
@@ -2666,6 +2859,7 @@ var Act = class {
|
|
|
2666
2859
|
*/
|
|
2667
2860
|
stop_correlations() {
|
|
2668
2861
|
this._correlate.stop_polling();
|
|
2862
|
+
this._autoclose?.stop();
|
|
2669
2863
|
}
|
|
2670
2864
|
/**
|
|
2671
2865
|
* Cancels any pending or active settle cycle.
|
|
@@ -3049,11 +3243,18 @@ function act() {
|
|
|
3049
3243
|
const states = /* @__PURE__ */ new Map();
|
|
3050
3244
|
const _sf = /* @__PURE__ */ new Map();
|
|
3051
3245
|
const _dp = /* @__PURE__ */ new Map();
|
|
3246
|
+
const _de = /* @__PURE__ */ new Map();
|
|
3247
|
+
const _ac = /* @__PURE__ */ new Map();
|
|
3248
|
+
const _aa = /* @__PURE__ */ new Map();
|
|
3249
|
+
const EMPTY_DEPRECATED = /* @__PURE__ */ new Set();
|
|
3052
3250
|
const registry = {
|
|
3053
3251
|
actions: {},
|
|
3054
3252
|
events: {},
|
|
3055
3253
|
sensitive_fields: (event_name) => _sf.get(event_name) ?? [],
|
|
3056
|
-
disclosure_predicate: (state_name) => _dp.get(state_name) ?? null
|
|
3254
|
+
disclosure_predicate: (state_name) => _dp.get(state_name) ?? null,
|
|
3255
|
+
deprecated_events: (state_name) => _de.get(state_name) ?? EMPTY_DEPRECATED,
|
|
3256
|
+
autoclose_policy: (state_name) => _ac.get(state_name) ?? null,
|
|
3257
|
+
autoclose_archiver: (state_name) => _aa.get(state_name) ?? null
|
|
3057
3258
|
};
|
|
3058
3259
|
const pending_projections = [];
|
|
3059
3260
|
const batch_handlers = /* @__PURE__ */ new Map();
|
|
@@ -3065,7 +3266,7 @@ function act() {
|
|
|
3065
3266
|
const event_names = Object.keys(state2.events);
|
|
3066
3267
|
const deprecated = deprecated_event_names(event_names);
|
|
3067
3268
|
if (deprecated.size === 0) continue;
|
|
3068
|
-
state2.
|
|
3269
|
+
_de.set(state2.name, deprecated);
|
|
3069
3270
|
for (const name of deprecated) {
|
|
3070
3271
|
const current = current_version_of(name, event_names);
|
|
3071
3272
|
deprecation_summary.push({
|
|
@@ -3180,6 +3381,8 @@ function act() {
|
|
|
3180
3381
|
}
|
|
3181
3382
|
for (const state2 of states.values()) {
|
|
3182
3383
|
if (state2.disclose) _dp.set(state2.name, state2.disclose);
|
|
3384
|
+
if (state2.autoclose) _ac.set(state2.name, state2.autoclose);
|
|
3385
|
+
if (state2.archive) _aa.set(state2.name, state2.archive);
|
|
3183
3386
|
const fields_by_event = /* @__PURE__ */ new Map();
|
|
3184
3387
|
for (const event_name of Object.keys(state2.events)) {
|
|
3185
3388
|
const fields = _sf.get(event_name);
|
|
@@ -3193,18 +3396,17 @@ function act() {
|
|
|
3193
3396
|
);
|
|
3194
3397
|
}
|
|
3195
3398
|
const disclose = state2.disclose ?? null;
|
|
3196
|
-
state2.
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3399
|
+
state2.pii_aware = true;
|
|
3400
|
+
state2.view = (event, actor) => pii_gate(
|
|
3401
|
+
event,
|
|
3402
|
+
fields_by_event.get(event.name) ?? [],
|
|
3403
|
+
disclose,
|
|
3404
|
+
actor
|
|
3405
|
+
);
|
|
3200
3406
|
state2.message = (validated) => {
|
|
3201
3407
|
const fields = fields_by_event.get(validated.name);
|
|
3202
3408
|
return fields ? pii_split(validated, fields) : validated;
|
|
3203
3409
|
};
|
|
3204
|
-
for (const [event_name, fields] of fields_by_event) {
|
|
3205
|
-
const original = state2.patch[event_name];
|
|
3206
|
-
state2.patch[event_name] = (event, s) => original(pii_merge(event, fields), s);
|
|
3207
|
-
}
|
|
3208
3410
|
}
|
|
3209
3411
|
for (const [target, original] of batch_handlers) {
|
|
3210
3412
|
const wrapped = async (events, stream) => {
|
|
@@ -3384,9 +3586,10 @@ function state(entry) {
|
|
|
3384
3586
|
init,
|
|
3385
3587
|
patch: default_patch,
|
|
3386
3588
|
on: {},
|
|
3387
|
-
// Step delegates initialized as identity
|
|
3388
|
-
//
|
|
3389
|
-
// the gate / split.
|
|
3589
|
+
// Step delegates initialized as identity, `pii_aware` false.
|
|
3590
|
+
// `act().build()` flips both on states with `sensitive(...)`
|
|
3591
|
+
// events to bake in the gate / split and gate the cache write.
|
|
3592
|
+
pii_aware: false,
|
|
3390
3593
|
view: (event) => event,
|
|
3391
3594
|
message: (validated) => validated
|
|
3392
3595
|
};
|
|
@@ -3447,6 +3650,24 @@ function action_builder(state2) {
|
|
|
3447
3650
|
internal.disclose = disclose;
|
|
3448
3651
|
return builder;
|
|
3449
3652
|
},
|
|
3653
|
+
autocloses(predicate) {
|
|
3654
|
+
if (typeof predicate !== "function") {
|
|
3655
|
+
throw new Error(
|
|
3656
|
+
".autocloses(predicate) requires a function; got " + typeof predicate
|
|
3657
|
+
);
|
|
3658
|
+
}
|
|
3659
|
+
internal.autoclose = predicate;
|
|
3660
|
+
return builder;
|
|
3661
|
+
},
|
|
3662
|
+
archives(archive) {
|
|
3663
|
+
if (typeof archive !== "function") {
|
|
3664
|
+
throw new Error(
|
|
3665
|
+
".archives(archive) requires a function; got " + typeof archive
|
|
3666
|
+
);
|
|
3667
|
+
}
|
|
3668
|
+
internal.archive = archive;
|
|
3669
|
+
return builder;
|
|
3670
|
+
},
|
|
3450
3671
|
build() {
|
|
3451
3672
|
return internal;
|
|
3452
3673
|
}
|
|
@@ -3622,6 +3843,9 @@ export {
|
|
|
3622
3843
|
ConcurrencyError,
|
|
3623
3844
|
ConsoleLogger,
|
|
3624
3845
|
CsvFile,
|
|
3846
|
+
DEFAULT_AUTOCLOSE_CYCLE_MS,
|
|
3847
|
+
DEFAULT_CLOSE_BATCH_SIZE,
|
|
3848
|
+
DEFAULT_CLOSE_YIELD_MS,
|
|
3625
3849
|
DEFAULT_LANE,
|
|
3626
3850
|
DEFAULT_MAX_SUBSCRIBED_STREAMS,
|
|
3627
3851
|
DEFAULT_SETTLE_DEBOUNCE_MS,
|
|
@@ -3653,6 +3877,7 @@ export {
|
|
|
3653
3877
|
log,
|
|
3654
3878
|
port,
|
|
3655
3879
|
projection,
|
|
3880
|
+
resolveAutocloseConfig,
|
|
3656
3881
|
scoped,
|
|
3657
3882
|
sensitive,
|
|
3658
3883
|
sleep,
|