@rotorsoft/act 1.32.25 → 1.32.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts +5 -151
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/in-memory-store.d.ts +0 -21
- package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
- package/dist/@types/builders/act-builder.d.ts +0 -25
- package/dist/@types/builders/act-builder.d.ts.map +1 -1
- package/dist/@types/builders/build-classify.d.ts +4 -4
- package/dist/@types/builders/build-classify.d.ts.map +1 -1
- package/dist/@types/builders/projection-builder.d.ts.map +1 -1
- package/dist/@types/builders/slice-builder.d.ts +1 -12
- package/dist/@types/builders/slice-builder.d.ts.map +1 -1
- package/dist/@types/builders/state-builder.d.ts +0 -189
- package/dist/@types/builders/state-builder.d.ts.map +1 -1
- package/dist/@types/config.d.ts +0 -27
- package/dist/@types/config.d.ts.map +1 -1
- package/dist/@types/internal/close-cycle.d.ts +2 -1
- package/dist/@types/internal/close-cycle.d.ts.map +1 -1
- 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/ports.d.ts +0 -7
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/@types/test/sandbox.d.ts +0 -14
- package/dist/@types/test/sandbox.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +0 -27
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/errors.d.ts +1 -92
- package/dist/@types/types/errors.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +0 -70
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +0 -33
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/{chunk-QXW3JV2H.js → chunk-5FXT63KF.js} +2 -2
- package/dist/chunk-5FXT63KF.js.map +1 -0
- package/dist/{chunk-XSG2NW4J.js → chunk-F7K7SIG6.js} +1 -1
- package/dist/chunk-F7K7SIG6.js.map +1 -0
- package/dist/index.cjs +79 -165
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +81 -167
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.js +2 -2
- 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 +1 -4
- package/dist/chunk-QXW3JV2H.js.map +0 -1
- package/dist/chunk-XSG2NW4J.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1406,10 +1406,8 @@ var ALL_LANES = /* @__PURE__ */ Symbol("act-1103/all-lanes");
|
|
|
1406
1406
|
function classify_registry(registry, states) {
|
|
1407
1407
|
const statics = /* @__PURE__ */ new Map();
|
|
1408
1408
|
const target_lanes = /* @__PURE__ */ new Map();
|
|
1409
|
-
const reactive_events = /* @__PURE__ */ new Set();
|
|
1410
1409
|
const event_to_lanes = /* @__PURE__ */ new Map();
|
|
1411
1410
|
for (const [name, register] of Object.entries(registry.events)) {
|
|
1412
|
-
if (register.reactions.size > 0) reactive_events.add(name);
|
|
1413
1411
|
for (const reaction of register.reactions.values()) {
|
|
1414
1412
|
if (typeof reaction.resolver === "function") {
|
|
1415
1413
|
event_to_lanes.set(name, ALL_LANES);
|
|
@@ -1447,7 +1445,6 @@ function classify_registry(registry, states) {
|
|
|
1447
1445
|
}
|
|
1448
1446
|
return {
|
|
1449
1447
|
static_targets: [...statics.values()],
|
|
1450
|
-
reactive_events,
|
|
1451
1448
|
event_to_state,
|
|
1452
1449
|
event_to_lanes
|
|
1453
1450
|
};
|
|
@@ -2604,7 +2601,7 @@ async function run_full_closes(full, target_map, deps, skipped) {
|
|
|
2604
2601
|
}
|
|
2605
2602
|
const safe = await partition_by_safety(
|
|
2606
2603
|
stream_info,
|
|
2607
|
-
deps.
|
|
2604
|
+
deps.reactive,
|
|
2608
2605
|
skipped,
|
|
2609
2606
|
deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE,
|
|
2610
2607
|
deps.catch_up_correlation
|
|
@@ -2641,8 +2638,8 @@ async function run_full_closes(full, target_map, deps, skipped) {
|
|
|
2641
2638
|
);
|
|
2642
2639
|
}
|
|
2643
2640
|
async function run_windowed_closes(windowed, deps, skipped) {
|
|
2644
|
-
const checkpoint = deps.
|
|
2645
|
-
const min_at = deps.
|
|
2641
|
+
const checkpoint = deps.reactive ? await deps.catch_up_correlation(Number.MAX_SAFE_INTEGER) : -1;
|
|
2642
|
+
const min_at = deps.reactive ? await probe_min_watermarks(
|
|
2646
2643
|
windowed.map((t) => t.stream),
|
|
2647
2644
|
deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE,
|
|
2648
2645
|
checkpoint
|
|
@@ -2730,8 +2727,8 @@ async function scan_stream_heads(streams) {
|
|
|
2730
2727
|
}
|
|
2731
2728
|
return out;
|
|
2732
2729
|
}
|
|
2733
|
-
async function partition_by_safety(stream_info,
|
|
2734
|
-
if (
|
|
2730
|
+
async function partition_by_safety(stream_info, reactive, skipped, page_size, catch_up_correlation2) {
|
|
2731
|
+
if (!reactive) return [...stream_info.keys()];
|
|
2735
2732
|
let needed = -1;
|
|
2736
2733
|
for (const info of stream_info.values())
|
|
2737
2734
|
needed = Math.max(needed, info.max_id);
|
|
@@ -3017,13 +3014,25 @@ var CorrelateCycle = class {
|
|
|
3017
3014
|
const after = Math.max(this._checkpoint, query.after || -1);
|
|
3018
3015
|
const correlated = /* @__PURE__ */ new Map();
|
|
3019
3016
|
let last_id = after;
|
|
3017
|
+
let stopped = false;
|
|
3020
3018
|
const found = await store2().query(
|
|
3021
3019
|
(event) => {
|
|
3022
|
-
|
|
3020
|
+
if (stopped) return;
|
|
3023
3021
|
const register = this._registry.events[event.name];
|
|
3024
3022
|
if (register) {
|
|
3025
3023
|
for (const reaction of register.reactions.values()) {
|
|
3026
|
-
|
|
3024
|
+
let resolved;
|
|
3025
|
+
try {
|
|
3026
|
+
resolved = typeof reaction.resolver === "function" ? reaction.resolver(event) : reaction.resolver;
|
|
3027
|
+
} catch (error) {
|
|
3028
|
+
stopped = true;
|
|
3029
|
+
report_once(
|
|
3030
|
+
this._reported,
|
|
3031
|
+
`resolver|${reaction.handler.name}|${event.id}`,
|
|
3032
|
+
`Resolver of reaction "${reaction.handler.name}" threw on ${String(event.name)} #${event.id} (stream "${event.stream}"): ${String(error)}. Correlation stops before this event until the resolver stops throwing; work already found keeps draining.`
|
|
3033
|
+
);
|
|
3034
|
+
return;
|
|
3035
|
+
}
|
|
3027
3036
|
if (!resolved) continue;
|
|
3028
3037
|
let lane = resolved.lane;
|
|
3029
3038
|
if (lane !== void 0 && !this._declared_lanes.has(lane)) {
|
|
@@ -3072,6 +3081,7 @@ var CorrelateCycle = class {
|
|
|
3072
3081
|
correlated.set(resolved.target, entry);
|
|
3073
3082
|
}
|
|
3074
3083
|
}
|
|
3084
|
+
last_id = event.id;
|
|
3075
3085
|
},
|
|
3076
3086
|
// Resolvers see `pii: null`: correlate runs actor-less, and one
|
|
3077
3087
|
// undecryptable row would otherwise stop every stream's reactions.
|
|
@@ -3113,7 +3123,7 @@ var CorrelateCycle = class {
|
|
|
3113
3123
|
return { subscribed, last_id, marked, scanned: true };
|
|
3114
3124
|
}
|
|
3115
3125
|
this._checkpoint = last_id;
|
|
3116
|
-
this._armed = found === query.limit;
|
|
3126
|
+
this._armed = stopped || found === query.limit;
|
|
3117
3127
|
return { subscribed: 0, last_id, marked: 0, scanned: true };
|
|
3118
3128
|
}
|
|
3119
3129
|
/**
|
|
@@ -3997,6 +4007,22 @@ function fetch_failure_policy(registry) {
|
|
|
3997
4007
|
}
|
|
3998
4008
|
return { blockOnError, maxRetries };
|
|
3999
4009
|
}
|
|
4010
|
+
function resolver_failed(lease, { error, options }) {
|
|
4011
|
+
log().error(error);
|
|
4012
|
+
const block2 = options.blockOnError && lease.retry >= options.maxRetries;
|
|
4013
|
+
return {
|
|
4014
|
+
lease,
|
|
4015
|
+
handled: 0,
|
|
4016
|
+
acked_at: lease.at,
|
|
4017
|
+
error,
|
|
4018
|
+
...block2 ? { block: true } : {
|
|
4019
|
+
next_attempt_at: Date.now() + compute_backoff_delay(
|
|
4020
|
+
lease.retry,
|
|
4021
|
+
options.backoff ?? DEFAULT_BACKOFF
|
|
4022
|
+
)
|
|
4023
|
+
}
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4000
4026
|
function warn_misrouted(seen, stream, handler, event) {
|
|
4001
4027
|
report_once(
|
|
4002
4028
|
seen,
|
|
@@ -4030,13 +4056,23 @@ async function run_drain_cycle(deps, cycle) {
|
|
|
4030
4056
|
);
|
|
4031
4057
|
for (const f of fetched) {
|
|
4032
4058
|
const { stream, events } = f;
|
|
4059
|
+
let failed;
|
|
4033
4060
|
const payloads = events.flatMap((event) => {
|
|
4034
4061
|
const register = registry.events[event.name];
|
|
4035
4062
|
if (!register) return [];
|
|
4036
4063
|
return [...register.reactions.values()].filter((reaction) => {
|
|
4037
4064
|
const resolver = reaction.resolver;
|
|
4038
4065
|
const dynamic = typeof resolver === "function";
|
|
4039
|
-
|
|
4066
|
+
let resolved;
|
|
4067
|
+
try {
|
|
4068
|
+
resolved = dynamic ? resolver(event) : resolver;
|
|
4069
|
+
} catch (error) {
|
|
4070
|
+
failed ??= {
|
|
4071
|
+
error: `Resolver of reaction "${reaction.handler.name}" threw on ${String(event.name)} #${event.id} (stream "${event.stream}"): ${String(error)}`,
|
|
4072
|
+
options: reaction.options
|
|
4073
|
+
};
|
|
4074
|
+
return false;
|
|
4075
|
+
}
|
|
4040
4076
|
if (!resolved || resolved.target !== stream) return false;
|
|
4041
4077
|
if (!dynamic || !batch_handlers.has(stream)) return true;
|
|
4042
4078
|
warn_misrouted(
|
|
@@ -4048,7 +4084,7 @@ async function run_drain_cycle(deps, cycle) {
|
|
|
4048
4084
|
return false;
|
|
4049
4085
|
}).map((reaction) => ({ ...reaction, event }));
|
|
4050
4086
|
});
|
|
4051
|
-
fetch_map.set(stream, { fetch: f, payloads });
|
|
4087
|
+
fetch_map.set(stream, { fetch: f, payloads, failed });
|
|
4052
4088
|
}
|
|
4053
4089
|
const handled = await Promise.all(
|
|
4054
4090
|
leased.map((lease) => {
|
|
@@ -4069,6 +4105,8 @@ async function run_drain_cycle(deps, cycle) {
|
|
|
4069
4105
|
};
|
|
4070
4106
|
return Promise.resolve(failed);
|
|
4071
4107
|
}
|
|
4108
|
+
if (entry.failed)
|
|
4109
|
+
return Promise.resolve(resolver_failed(lease, entry.failed));
|
|
4072
4110
|
const at = entry.fetch.events.at(-1)?.id || fetch_window_at;
|
|
4073
4111
|
const { payloads } = entry;
|
|
4074
4112
|
const exhausted = budget_exhausted(
|
|
@@ -4548,7 +4586,8 @@ var Act = class {
|
|
|
4548
4586
|
_breaker;
|
|
4549
4587
|
_listen;
|
|
4550
4588
|
_drain;
|
|
4551
|
-
|
|
4589
|
+
/** Whether any event has a reaction; nothing arms without one. */
|
|
4590
|
+
_reactive;
|
|
4552
4591
|
_drain_controllers;
|
|
4553
4592
|
_correlate;
|
|
4554
4593
|
_settle;
|
|
@@ -4678,7 +4717,7 @@ var Act = class {
|
|
|
4678
4717
|
});
|
|
4679
4718
|
this._handle_batch = build_handle_batch(this._logger);
|
|
4680
4719
|
const classification = classify_registry(this.registry, this._states);
|
|
4681
|
-
this.
|
|
4720
|
+
this._reactive = classification.event_to_lanes.size > 0;
|
|
4682
4721
|
this._event_to_state = classification.event_to_state;
|
|
4683
4722
|
this._event_to_lanes = classification.event_to_lanes;
|
|
4684
4723
|
this._listen = options.listen !== false;
|
|
@@ -4725,8 +4764,18 @@ var Act = class {
|
|
|
4725
4764
|
on_acked: (acked) => this.emit("acked", acked),
|
|
4726
4765
|
on_blocked: (blocked) => this.emit("blocked", blocked),
|
|
4727
4766
|
// Reaction-requested close: the same machinery as `app.close`.
|
|
4767
|
+
// A store failure reaches the breaker; anything else (the archive
|
|
4768
|
+
// callback is app code) is logged here, never counted.
|
|
4728
4769
|
on_close: async (targets) => {
|
|
4729
|
-
|
|
4770
|
+
try {
|
|
4771
|
+
await this._run_close(targets);
|
|
4772
|
+
} catch (error) {
|
|
4773
|
+
if (error instanceof StoreError) throw error;
|
|
4774
|
+
this._logger.error(
|
|
4775
|
+
error,
|
|
4776
|
+
`Reaction-requested close of ${targets.map((t) => `"${t.stream}"`).join(", ")} failed and was not retried; close it with app.close(...) once the cause is fixed.`
|
|
4777
|
+
);
|
|
4778
|
+
}
|
|
4730
4779
|
},
|
|
4731
4780
|
breaker: this._breaker,
|
|
4732
4781
|
run_scoped: this._scoped,
|
|
@@ -4775,9 +4824,8 @@ var Act = class {
|
|
|
4775
4824
|
declared_lanes: new Set(this._declared_lanes),
|
|
4776
4825
|
// Built after the controllers, so their keys are the active lanes.
|
|
4777
4826
|
shard: this._drain_controllers.size < this._declared_lanes.length ? [...this._drain_controllers.keys()] : void 0,
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
},
|
|
4827
|
+
// Only reached on a draining app: writer-only ones never correlate.
|
|
4828
|
+
on_init: () => this._arm_all(),
|
|
4781
4829
|
on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
|
|
4782
4830
|
});
|
|
4783
4831
|
}
|
|
@@ -4874,7 +4922,7 @@ var Act = class {
|
|
|
4874
4922
|
}
|
|
4875
4923
|
/** Failures are logged, never thrown: `notify` is a hint. */
|
|
4876
4924
|
async _wire_notify(s) {
|
|
4877
|
-
if (this.
|
|
4925
|
+
if (!this._reactive) return void 0;
|
|
4878
4926
|
if (!s.notify) return void 0;
|
|
4879
4927
|
if (!this._listen) return void 0;
|
|
4880
4928
|
try {
|
|
@@ -4934,46 +4982,6 @@ var Act = class {
|
|
|
4934
4982
|
* console.log(snapshots[0].state.count); // Current count after increment
|
|
4935
4983
|
* ```
|
|
4936
4984
|
*
|
|
4937
|
-
* @example With error handling
|
|
4938
|
-
* ```typescript
|
|
4939
|
-
* try {
|
|
4940
|
-
* await app.do(
|
|
4941
|
-
* "withdraw",
|
|
4942
|
-
* { stream: "account-123", actor: { id: "user1", name: "Alice" } },
|
|
4943
|
-
* { amount: 1000 }
|
|
4944
|
-
* );
|
|
4945
|
-
* } catch (error) {
|
|
4946
|
-
* if (error instanceof InvariantError) {
|
|
4947
|
-
* console.error("Business rule violated:", error.description);
|
|
4948
|
-
* } else if (error instanceof ConcurrencyError) {
|
|
4949
|
-
* console.error("Concurrent modification detected, retry...");
|
|
4950
|
-
* } else if (error instanceof ValidationError) {
|
|
4951
|
-
* console.error("Invalid payload:", error.details);
|
|
4952
|
-
* }
|
|
4953
|
-
* }
|
|
4954
|
-
* ```
|
|
4955
|
-
*
|
|
4956
|
-
* @example Reaction triggering another action (reactingTo auto-injected)
|
|
4957
|
-
* ```typescript
|
|
4958
|
-
* const app = act()
|
|
4959
|
-
* .withState(Order)
|
|
4960
|
-
* .withState(Inventory)
|
|
4961
|
-
* .on("OrderPlaced")
|
|
4962
|
-
* .do(async function reduceInventory(event, _stream, app) {
|
|
4963
|
-
* // Inside reaction handlers, reactingTo is auto-injected when omitted.
|
|
4964
|
-
* // The triggering event is used by default, maintaining the correlation chain.
|
|
4965
|
-
* await app.do(
|
|
4966
|
-
* "reduceStock",
|
|
4967
|
-
* { stream: "inventory-1", actor: { id: "sys", name: "system" } },
|
|
4968
|
-
* { amount: event.data.items.length }
|
|
4969
|
-
* );
|
|
4970
|
-
* // To use a different correlation, pass reactingTo explicitly:
|
|
4971
|
-
* // await app.do("reduceStock", target, payload, { reactingTo: customEvent });
|
|
4972
|
-
* })
|
|
4973
|
-
* .to("inventory-1")
|
|
4974
|
-
* .build();
|
|
4975
|
-
* ```
|
|
4976
|
-
*
|
|
4977
4985
|
* @see {@link Target} for target structure
|
|
4978
4986
|
* @see {@link Snapshot} for return value structure
|
|
4979
4987
|
* @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
|
|
@@ -4989,10 +4997,9 @@ var Act = class {
|
|
|
4989
4997
|
payload,
|
|
4990
4998
|
do_options
|
|
4991
4999
|
);
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
);
|
|
5000
|
+
this._arm_for_event_names(
|
|
5001
|
+
snapshots.map((s) => s.event.name)
|
|
5002
|
+
);
|
|
4996
5003
|
this.emit("committed", snapshots);
|
|
4997
5004
|
return snapshots;
|
|
4998
5005
|
});
|
|
@@ -5039,26 +5046,6 @@ var Act = class {
|
|
|
5039
5046
|
* console.log(`Found ${count} events from ${first?.id} to ${last?.id}`);
|
|
5040
5047
|
* ```
|
|
5041
5048
|
*
|
|
5042
|
-
* @example Query specific event types
|
|
5043
|
-
* ```typescript
|
|
5044
|
-
* const { count } = await app.query(
|
|
5045
|
-
* { name: "UserCreated", limit: 100 },
|
|
5046
|
-
* (event) => {
|
|
5047
|
-
* console.log("User created:", event.data.email);
|
|
5048
|
-
* }
|
|
5049
|
-
* );
|
|
5050
|
-
* ```
|
|
5051
|
-
*
|
|
5052
|
-
* @example Query events in time range
|
|
5053
|
-
* ```typescript
|
|
5054
|
-
* const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
5055
|
-
* const { count } = await app.query({
|
|
5056
|
-
* created_after: yesterday,
|
|
5057
|
-
* stream: "user-123"
|
|
5058
|
-
* });
|
|
5059
|
-
* console.log(`User had ${count} events in last 24 hours`);
|
|
5060
|
-
* ```
|
|
5061
|
-
*
|
|
5062
5049
|
* @see {@link query_array} for loading events into memory
|
|
5063
5050
|
*/
|
|
5064
5051
|
async query(query, callback) {
|
|
@@ -5090,14 +5077,6 @@ var Act = class {
|
|
|
5090
5077
|
* events.forEach(event => console.log(event.name, event.data));
|
|
5091
5078
|
* ```
|
|
5092
5079
|
*
|
|
5093
|
-
* @example Get recent events
|
|
5094
|
-
* ```typescript
|
|
5095
|
-
* const recent = await app.query_array({
|
|
5096
|
-
* stream: "user-123",
|
|
5097
|
-
* limit: 10
|
|
5098
|
-
* });
|
|
5099
|
-
* ```
|
|
5100
|
-
*
|
|
5101
5080
|
* @see {@link query} for large result sets
|
|
5102
5081
|
*/
|
|
5103
5082
|
async query_array(query) {
|
|
@@ -5168,12 +5147,6 @@ var Act = class {
|
|
|
5168
5147
|
* await app.drain();
|
|
5169
5148
|
* ```
|
|
5170
5149
|
*
|
|
5171
|
-
* @example In production, prefer settle()
|
|
5172
|
-
* ```typescript
|
|
5173
|
-
* await app.do("CreateItem", target, input);
|
|
5174
|
-
* app.settle(); // debounced correlate→drain, emits "settled"
|
|
5175
|
-
* ```
|
|
5176
|
-
*
|
|
5177
5150
|
* @see {@link settle} for debounced correlate→drain with lifecycle events
|
|
5178
5151
|
* @see {@link correlate} for dynamic stream discovery
|
|
5179
5152
|
* @see {@link start_correlations} for automatic correlation
|
|
@@ -5185,6 +5158,7 @@ var Act = class {
|
|
|
5185
5158
|
return this._scoped(() => this._drain_all(options));
|
|
5186
5159
|
}
|
|
5187
5160
|
_arm_all() {
|
|
5161
|
+
if (!this._reactive) return;
|
|
5188
5162
|
this._correlate.arm();
|
|
5189
5163
|
for (const c of this._drain_controllers.values()) c.arm();
|
|
5190
5164
|
}
|
|
@@ -5240,37 +5214,20 @@ var Act = class {
|
|
|
5240
5214
|
* on reaction resolvers. It then registers these streams so they'll be picked up by
|
|
5241
5215
|
* the next drain cycle.
|
|
5242
5216
|
*
|
|
5243
|
-
* @param query - Query filter to scan for new correlations
|
|
5244
5217
|
* @param query - Scan filter — see {@link Query} for fields (typically
|
|
5245
5218
|
* `{ after: <event-id>, limit: <count> }`)
|
|
5246
|
-
* @returns
|
|
5219
|
+
* @returns The number of newly subscribed streams and the last scanned event id
|
|
5247
5220
|
*
|
|
5248
5221
|
* @example Manual correlation
|
|
5249
5222
|
* ```typescript
|
|
5250
5223
|
* // Scan for new streams
|
|
5251
|
-
* const {
|
|
5252
|
-
* console.log(`
|
|
5224
|
+
* const { subscribed, last_id } = await app.correlate({ after: 0, limit: 100 });
|
|
5225
|
+
* console.log(`Subscribed ${subscribed} new streams`);
|
|
5253
5226
|
*
|
|
5254
5227
|
* // Save last_id for next scan
|
|
5255
5228
|
* await saveCheckpoint(last_id);
|
|
5256
5229
|
* ```
|
|
5257
5230
|
*
|
|
5258
|
-
* @example Dynamic stream creation
|
|
5259
|
-
* ```typescript
|
|
5260
|
-
* const app = act()
|
|
5261
|
-
* .withState(User)
|
|
5262
|
-
* .withState(UserStats)
|
|
5263
|
-
* .on("UserLoggedIn")
|
|
5264
|
-
* .do(async (event) => ["incrementLoginCount", {}])
|
|
5265
|
-
* .to((event) => ({
|
|
5266
|
-
* target: `stats-${event.stream}` // Dynamic target per user
|
|
5267
|
-
* }))
|
|
5268
|
-
* .build();
|
|
5269
|
-
*
|
|
5270
|
-
* // Discover stats streams as users log in
|
|
5271
|
-
* await app.correlate();
|
|
5272
|
-
* ```
|
|
5273
|
-
*
|
|
5274
5231
|
* @see {@link start_correlations} for automatic periodic correlation
|
|
5275
5232
|
* @see {@link stop_correlations} to stop automatic correlation
|
|
5276
5233
|
*/
|
|
@@ -5283,8 +5240,7 @@ var Act = class {
|
|
|
5283
5240
|
if (!this._drain) return { subscribed: 0, last_id: -1, scanned: false };
|
|
5284
5241
|
return this._scoped(async () => {
|
|
5285
5242
|
const { subscribed, last_id, marked, scanned } = await this._correlate.correlate(query, lease);
|
|
5286
|
-
if (
|
|
5287
|
-
this._arm_all();
|
|
5243
|
+
if (subscribed > 0 || marked > 0) this._arm_all();
|
|
5288
5244
|
if (subscribed > 0) this._on_discovered?.(subscribed);
|
|
5289
5245
|
return { subscribed, last_id, scanned };
|
|
5290
5246
|
});
|
|
@@ -5376,12 +5332,6 @@ var Act = class {
|
|
|
5376
5332
|
* app.settle({ eventLimit: 1000 }); // emits "settled" when fully replayed
|
|
5377
5333
|
* ```
|
|
5378
5334
|
*
|
|
5379
|
-
* @example Rebuild a projection (tests / scripts)
|
|
5380
|
-
* ```typescript
|
|
5381
|
-
* await app.reset(["my-projection"]);
|
|
5382
|
-
* await app.drain({ eventLimit: 1000 }); // small streams: one pass is enough
|
|
5383
|
-
* ```
|
|
5384
|
-
*
|
|
5385
5335
|
* @see {@link Store.reset} for the underlying store primitive
|
|
5386
5336
|
* @see {@link settle} for the debounced full-catch-up loop
|
|
5387
5337
|
*/
|
|
@@ -5390,7 +5340,7 @@ var Act = class {
|
|
|
5390
5340
|
const count = await store2().reset(input);
|
|
5391
5341
|
for (const handler of this._batch_handlers.values())
|
|
5392
5342
|
handler[FOLD_RESET]?.();
|
|
5393
|
-
if (count > 0
|
|
5343
|
+
if (count > 0) this._arm_all();
|
|
5394
5344
|
return count;
|
|
5395
5345
|
});
|
|
5396
5346
|
}
|
|
@@ -5424,7 +5374,7 @@ var Act = class {
|
|
|
5424
5374
|
async unblock(input) {
|
|
5425
5375
|
return this._scoped(async () => {
|
|
5426
5376
|
const count = await store2().unblock(input);
|
|
5427
|
-
if (count > 0
|
|
5377
|
+
if (count > 0) this._arm_all();
|
|
5428
5378
|
return count;
|
|
5429
5379
|
});
|
|
5430
5380
|
}
|
|
@@ -5460,11 +5410,6 @@ var Act = class {
|
|
|
5460
5410
|
* await cache().clear(); // operator's responsibility
|
|
5461
5411
|
* ```
|
|
5462
5412
|
*
|
|
5463
|
-
* @example Copy one store into another
|
|
5464
|
-
* ```typescript
|
|
5465
|
-
* await app.restore(pg, {}, sqlite);
|
|
5466
|
-
* ```
|
|
5467
|
-
*
|
|
5468
5413
|
* @see {@link Store.restore} for the underlying driver-pattern primitive.
|
|
5469
5414
|
*/
|
|
5470
5415
|
async restore(source, opts = {}, sink) {
|
|
@@ -5603,16 +5548,6 @@ var Act = class {
|
|
|
5603
5548
|
* await app.prioritize({ stream: "^proj-orders$", stream_exact: false }, 10);
|
|
5604
5549
|
* ```
|
|
5605
5550
|
*
|
|
5606
|
-
* @example Drop all audit projections to background
|
|
5607
|
-
* ```typescript
|
|
5608
|
-
* await app.prioritize({ source: "^audit-" }, -5);
|
|
5609
|
-
* ```
|
|
5610
|
-
*
|
|
5611
|
-
* @example Reset everyone to default
|
|
5612
|
-
* ```typescript
|
|
5613
|
-
* await app.prioritize({}, 0);
|
|
5614
|
-
* ```
|
|
5615
|
-
*
|
|
5616
5551
|
* @see {@link Store.prioritize} for the underlying primitive
|
|
5617
5552
|
* @see {@link claim} for how priority biases scheduling
|
|
5618
5553
|
*/
|
|
@@ -5623,7 +5558,7 @@ var Act = class {
|
|
|
5623
5558
|
async _run_close(targets) {
|
|
5624
5559
|
const close_actor = { id: "$close", name: "close" };
|
|
5625
5560
|
const result = await run_close_cycle(targets, {
|
|
5626
|
-
|
|
5561
|
+
reactive: this._reactive,
|
|
5627
5562
|
catch_up_correlation: (until) => catch_up_correlation(
|
|
5628
5563
|
this._correlate,
|
|
5629
5564
|
(limit) => this.correlate({ limit }),
|
|
@@ -5676,27 +5611,6 @@ var Act = class {
|
|
|
5676
5611
|
* { stream: "order-456" },
|
|
5677
5612
|
* ]);
|
|
5678
5613
|
* ```
|
|
5679
|
-
*
|
|
5680
|
-
* @example Close with restart (state loaded automatically after guard)
|
|
5681
|
-
* ```typescript
|
|
5682
|
-
* await app.close([
|
|
5683
|
-
* { stream: "counter-1", restart: true },
|
|
5684
|
-
* { stream: "counter-2" }, // tombstoned
|
|
5685
|
-
* ]);
|
|
5686
|
-
* ```
|
|
5687
|
-
*
|
|
5688
|
-
* @example Windowed close — keep the last 180 days of real events
|
|
5689
|
-
* ```typescript
|
|
5690
|
-
* const cutoff = new Date();
|
|
5691
|
-
* cutoff.setDate(cutoff.getDate() - 180);
|
|
5692
|
-
* await app.close([
|
|
5693
|
-
* {
|
|
5694
|
-
* stream: "ledger-acme",
|
|
5695
|
-
* before: cutoff,
|
|
5696
|
-
* archive: async () => { await archiveToS3("ledger-acme", cutoff); },
|
|
5697
|
-
* },
|
|
5698
|
-
* ]);
|
|
5699
|
-
* ```
|
|
5700
5614
|
*/
|
|
5701
5615
|
async close(targets) {
|
|
5702
5616
|
if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
|