@rotorsoft/act 1.32.25 → 1.32.26
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.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/index.cjs +57 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +57 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1692,13 +1692,25 @@ var CorrelateCycle = class {
|
|
|
1692
1692
|
const after = Math.max(this._checkpoint, query.after || -1);
|
|
1693
1693
|
const correlated = /* @__PURE__ */ new Map();
|
|
1694
1694
|
let last_id = after;
|
|
1695
|
+
let stopped = false;
|
|
1695
1696
|
const found = await store().query(
|
|
1696
1697
|
(event) => {
|
|
1697
|
-
|
|
1698
|
+
if (stopped) return;
|
|
1698
1699
|
const register = this._registry.events[event.name];
|
|
1699
1700
|
if (register) {
|
|
1700
1701
|
for (const reaction of register.reactions.values()) {
|
|
1701
|
-
|
|
1702
|
+
let resolved;
|
|
1703
|
+
try {
|
|
1704
|
+
resolved = typeof reaction.resolver === "function" ? reaction.resolver(event) : reaction.resolver;
|
|
1705
|
+
} catch (error) {
|
|
1706
|
+
stopped = true;
|
|
1707
|
+
report_once(
|
|
1708
|
+
this._reported,
|
|
1709
|
+
`resolver|${reaction.handler.name}|${event.id}`,
|
|
1710
|
+
`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.`
|
|
1711
|
+
);
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1702
1714
|
if (!resolved) continue;
|
|
1703
1715
|
let lane = resolved.lane;
|
|
1704
1716
|
if (lane !== void 0 && !this._declared_lanes.has(lane)) {
|
|
@@ -1747,6 +1759,7 @@ var CorrelateCycle = class {
|
|
|
1747
1759
|
correlated.set(resolved.target, entry);
|
|
1748
1760
|
}
|
|
1749
1761
|
}
|
|
1762
|
+
last_id = event.id;
|
|
1750
1763
|
},
|
|
1751
1764
|
// Resolvers see `pii: null`: correlate runs actor-less, and one
|
|
1752
1765
|
// undecryptable row would otherwise stop every stream's reactions.
|
|
@@ -1788,7 +1801,7 @@ var CorrelateCycle = class {
|
|
|
1788
1801
|
return { subscribed, last_id, marked, scanned: true };
|
|
1789
1802
|
}
|
|
1790
1803
|
this._checkpoint = last_id;
|
|
1791
|
-
this._armed = found === query.limit;
|
|
1804
|
+
this._armed = stopped || found === query.limit;
|
|
1792
1805
|
return { subscribed: 0, last_id, marked: 0, scanned: true };
|
|
1793
1806
|
}
|
|
1794
1807
|
/**
|
|
@@ -2672,6 +2685,22 @@ function fetch_failure_policy(registry) {
|
|
|
2672
2685
|
}
|
|
2673
2686
|
return { blockOnError, maxRetries };
|
|
2674
2687
|
}
|
|
2688
|
+
function resolver_failed(lease, { error, options }) {
|
|
2689
|
+
log().error(error);
|
|
2690
|
+
const block2 = options.blockOnError && lease.retry >= options.maxRetries;
|
|
2691
|
+
return {
|
|
2692
|
+
lease,
|
|
2693
|
+
handled: 0,
|
|
2694
|
+
acked_at: lease.at,
|
|
2695
|
+
error,
|
|
2696
|
+
...block2 ? { block: true } : {
|
|
2697
|
+
next_attempt_at: Date.now() + compute_backoff_delay(
|
|
2698
|
+
lease.retry,
|
|
2699
|
+
options.backoff ?? DEFAULT_BACKOFF
|
|
2700
|
+
)
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
}
|
|
2675
2704
|
function warn_misrouted(seen, stream, handler, event) {
|
|
2676
2705
|
report_once(
|
|
2677
2706
|
seen,
|
|
@@ -2705,13 +2734,23 @@ async function run_drain_cycle(deps, cycle) {
|
|
|
2705
2734
|
);
|
|
2706
2735
|
for (const f of fetched) {
|
|
2707
2736
|
const { stream, events } = f;
|
|
2737
|
+
let failed;
|
|
2708
2738
|
const payloads = events.flatMap((event) => {
|
|
2709
2739
|
const register = registry.events[event.name];
|
|
2710
2740
|
if (!register) return [];
|
|
2711
2741
|
return [...register.reactions.values()].filter((reaction) => {
|
|
2712
2742
|
const resolver = reaction.resolver;
|
|
2713
2743
|
const dynamic = typeof resolver === "function";
|
|
2714
|
-
|
|
2744
|
+
let resolved;
|
|
2745
|
+
try {
|
|
2746
|
+
resolved = dynamic ? resolver(event) : resolver;
|
|
2747
|
+
} catch (error) {
|
|
2748
|
+
failed ??= {
|
|
2749
|
+
error: `Resolver of reaction "${reaction.handler.name}" threw on ${String(event.name)} #${event.id} (stream "${event.stream}"): ${String(error)}`,
|
|
2750
|
+
options: reaction.options
|
|
2751
|
+
};
|
|
2752
|
+
return false;
|
|
2753
|
+
}
|
|
2715
2754
|
if (!resolved || resolved.target !== stream) return false;
|
|
2716
2755
|
if (!dynamic || !batch_handlers.has(stream)) return true;
|
|
2717
2756
|
warn_misrouted(
|
|
@@ -2723,7 +2762,7 @@ async function run_drain_cycle(deps, cycle) {
|
|
|
2723
2762
|
return false;
|
|
2724
2763
|
}).map((reaction) => ({ ...reaction, event }));
|
|
2725
2764
|
});
|
|
2726
|
-
fetch_map.set(stream, { fetch: f, payloads });
|
|
2765
|
+
fetch_map.set(stream, { fetch: f, payloads, failed });
|
|
2727
2766
|
}
|
|
2728
2767
|
const handled = await Promise.all(
|
|
2729
2768
|
leased.map((lease) => {
|
|
@@ -2744,6 +2783,8 @@ async function run_drain_cycle(deps, cycle) {
|
|
|
2744
2783
|
};
|
|
2745
2784
|
return Promise.resolve(failed);
|
|
2746
2785
|
}
|
|
2786
|
+
if (entry.failed)
|
|
2787
|
+
return Promise.resolve(resolver_failed(lease, entry.failed));
|
|
2747
2788
|
const at = entry.fetch.events.at(-1)?.id || fetch_window_at;
|
|
2748
2789
|
const { payloads } = entry;
|
|
2749
2790
|
const exhausted = budget_exhausted(
|
|
@@ -3400,8 +3441,18 @@ var Act = class {
|
|
|
3400
3441
|
on_acked: (acked) => this.emit("acked", acked),
|
|
3401
3442
|
on_blocked: (blocked) => this.emit("blocked", blocked),
|
|
3402
3443
|
// Reaction-requested close: the same machinery as `app.close`.
|
|
3444
|
+
// A store failure reaches the breaker; anything else (the archive
|
|
3445
|
+
// callback is app code) is logged here, never counted.
|
|
3403
3446
|
on_close: async (targets) => {
|
|
3404
|
-
|
|
3447
|
+
try {
|
|
3448
|
+
await this._run_close(targets);
|
|
3449
|
+
} catch (error) {
|
|
3450
|
+
if (error instanceof StoreError) throw error;
|
|
3451
|
+
this._logger.error(
|
|
3452
|
+
error,
|
|
3453
|
+
`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.`
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3405
3456
|
},
|
|
3406
3457
|
breaker: this._breaker,
|
|
3407
3458
|
run_scoped: this._scoped,
|