@rotorsoft/act 1.22.9 → 1.23.1

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.
Files changed (47) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/@types/act.d.ts +33 -2
  3. package/dist/@types/act.d.ts.map +1 -1
  4. package/dist/@types/adapters/console-logger.d.ts.map +1 -1
  5. package/dist/@types/adapters/in-memory-cache.d.ts.map +1 -1
  6. package/dist/@types/adapters/in-memory-store.d.ts +10 -0
  7. package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
  8. package/dist/@types/builders/act-builder.d.ts.map +1 -1
  9. package/dist/@types/config.d.ts +5 -5
  10. package/dist/@types/csv.d.ts.map +1 -1
  11. package/dist/@types/internal/builder-utils.d.ts.map +1 -1
  12. package/dist/@types/internal/circuit-breaker.d.ts.map +1 -1
  13. package/dist/@types/internal/close-signal.d.ts.map +1 -1
  14. package/dist/@types/internal/correlate-cycle.d.ts.map +1 -1
  15. package/dist/@types/internal/defer-signal.d.ts.map +1 -1
  16. package/dist/@types/internal/defer-timer.d.ts.map +1 -1
  17. package/dist/@types/internal/drain-cycle.d.ts.map +1 -1
  18. package/dist/@types/internal/drain.d.ts.map +1 -1
  19. package/dist/@types/internal/event-sourcing.d.ts +59 -2
  20. package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
  21. package/dist/@types/internal/index.d.ts +3 -3
  22. package/dist/@types/internal/index.d.ts.map +1 -1
  23. package/dist/@types/internal/lru-map.d.ts.map +1 -1
  24. package/dist/@types/internal/merge.d.ts.map +1 -1
  25. package/dist/@types/internal/projection-fold.d.ts +46 -0
  26. package/dist/@types/internal/projection-fold.d.ts.map +1 -0
  27. package/dist/@types/internal/settle.d.ts.map +1 -1
  28. package/dist/@types/internal/tracing.d.ts +2 -2
  29. package/dist/@types/internal/tracing.d.ts.map +1 -1
  30. package/dist/@types/ports.d.ts +1 -1
  31. package/dist/@types/ports.d.ts.map +1 -1
  32. package/dist/@types/types/errors.d.ts.map +1 -1
  33. package/dist/@types/types/index.d.ts.map +1 -1
  34. package/dist/@types/utils.d.ts.map +1 -1
  35. package/dist/{chunk-YVLB5MMB.js → chunk-4HYYZOEH.js} +9 -2
  36. package/dist/chunk-4HYYZOEH.js.map +1 -0
  37. package/dist/index.cjs +128 -90
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.js +121 -90
  40. package/dist/index.js.map +1 -1
  41. package/dist/test/index.cjs +8 -1
  42. package/dist/test/index.cjs.map +1 -1
  43. package/dist/test/index.js +1 -1
  44. package/package.json +2 -2
  45. package/dist/@types/internal/state-fold.d.ts +0 -18
  46. package/dist/@types/internal/state-fold.d.ts.map +0 -1
  47. package/dist/chunk-YVLB5MMB.js.map +0 -1
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  sleep,
21
21
  store,
22
22
  validate
23
- } from "./chunk-YVLB5MMB.js";
23
+ } from "./chunk-4HYYZOEH.js";
24
24
  import {
25
25
  ActorSchema,
26
26
  CausationEventSchema,
@@ -1736,6 +1736,15 @@ function compute_backoff_delay(retry, opts) {
1736
1736
  }
1737
1737
 
1738
1738
  // src/internal/event-sourcing.ts
1739
+ var bare_patch = (_me, state2, partial) => patch(state2, partial);
1740
+ var validating_patch = (me, state2, partial, event) => {
1741
+ const next = patch(state2, partial);
1742
+ return validate(
1743
+ `${me.name}.${String(event.name)}#${event.id}`,
1744
+ next,
1745
+ me.state
1746
+ );
1747
+ };
1739
1748
  var DEFAULT_BATCH = 500;
1740
1749
  async function snap(snapshot) {
1741
1750
  const { id, stream, name, meta, version } = snapshot.event;
@@ -1890,7 +1899,7 @@ async function scan(source, opts = {}, callback) {
1890
1899
  }
1891
1900
  };
1892
1901
  }
1893
- async function load(me, target, callback) {
1902
+ async function load(me, target, callback, patch_fn = bare_patch) {
1894
1903
  const { stream, actor, asOf } = target;
1895
1904
  const time_travel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
1896
1905
  const cached = time_travel ? void 0 : await cache().get(stream);
@@ -1909,7 +1918,7 @@ async function load(me, target, callback) {
1909
1918
  patches = 0;
1910
1919
  replayed++;
1911
1920
  } else if (me.patch[event.name]) {
1912
- state2 = patch(state2, me.patch[event.name](event, state2));
1921
+ state2 = patch_fn(me, state2, me.patch[event.name](event, state2), event);
1913
1922
  patches++;
1914
1923
  replayed++;
1915
1924
  } else if (event.name !== TOMBSTONE_EVENT) {
@@ -1960,7 +1969,7 @@ async function load(me, target, callback) {
1960
1969
  replayed
1961
1970
  };
1962
1971
  }
1963
- async function action(me, action2, target, payload, options) {
1972
+ async function action(me, action2, target, payload, options, patch_fn = bare_patch) {
1964
1973
  const { stream, expectedVersion, actor } = target;
1965
1974
  if (!stream) throw new Error("Missing target stream");
1966
1975
  const reactingTo = options?.reactingTo;
@@ -1970,7 +1979,12 @@ async function action(me, action2, target, payload, options) {
1970
1979
  const max_retries = opts?.maxRetries ?? 0;
1971
1980
  for (let attempt = 0; ; attempt++) {
1972
1981
  try {
1973
- const snapshot = await load(me, { stream, actor: target.actor });
1982
+ const snapshot = await load(
1983
+ me,
1984
+ { stream, actor: target.actor },
1985
+ void 0,
1986
+ patch_fn
1987
+ );
1974
1988
  if (snapshot.event?.name === TOMBSTONE_EVENT)
1975
1989
  throw new StreamClosedError(stream);
1976
1990
  const expected = expectedVersion ?? (snapshot.version >= 0 ? snapshot.version : void 0);
@@ -2041,7 +2055,7 @@ async function action(me, action2, target, payload, options) {
2041
2055
  const snapshots = committed.map((row, i) => {
2042
2056
  const event = { ...row, data: valid[i].data };
2043
2057
  const p = me.patch[event.name](event, state2);
2044
- state2 = patch(state2, p);
2058
+ state2 = patch_fn(me, state2, p, event);
2045
2059
  patches++;
2046
2060
  return {
2047
2061
  event,
@@ -2137,12 +2151,20 @@ var traced = (inner, exit, entry) => (async (...args) => {
2137
2151
  exit?.(result, ...args);
2138
2152
  return result;
2139
2153
  });
2140
- function build_es(logger, correlator = default_correlator) {
2141
- const bound_action = (me, action_name, target, payload, options) => action(me, action_name, target, payload, { correlator, ...options });
2154
+ function build_es(logger, correlator = default_correlator, patch_fn = bare_patch) {
2155
+ const bound_action = (me, action_name, target, payload, options) => action(
2156
+ me,
2157
+ action_name,
2158
+ target,
2159
+ payload,
2160
+ { correlator, ...options },
2161
+ patch_fn
2162
+ );
2163
+ const bound_load = (me, target, callback) => load(me, target, callback, patch_fn);
2142
2164
  if (logger.level !== "trace") {
2143
2165
  return {
2144
2166
  snap,
2145
- load,
2167
+ load: bound_load,
2146
2168
  action: bound_action,
2147
2169
  tombstone
2148
2170
  };
@@ -2157,7 +2179,7 @@ function build_es(logger, correlator = default_correlator) {
2157
2179
  )
2158
2180
  );
2159
2181
  }),
2160
- load: traced(load, (result, _me, target) => {
2182
+ load: traced(bound_load, (result, _me, target) => {
2161
2183
  const stats = stats_marker(
2162
2184
  result.version,
2163
2185
  result.replayed,
@@ -2468,6 +2490,83 @@ var DrainController = class {
2468
2490
  }
2469
2491
  };
2470
2492
 
2493
+ // src/internal/projection-fold.ts
2494
+ import { z as z5 } from "zod";
2495
+ var DEFAULT_FOLD_FLUSH_EVERY = 1e3;
2496
+ var DEFAULT_MAX_CACHED_STATES = 1e4;
2497
+ var FoldOptionsSchema = z5.object({
2498
+ flushEvery: z5.number().int().min(1).default(DEFAULT_FOLD_FLUSH_EVERY),
2499
+ maxCachedStates: z5.number().int().min(1).default(DEFAULT_MAX_CACHED_STATES)
2500
+ });
2501
+ function resolveFoldConfig(options) {
2502
+ return FoldOptionsSchema.parse(options);
2503
+ }
2504
+ function make_fold_handler(me, flush, config2, patch_fn = bare_patch) {
2505
+ const cache2 = /* @__PURE__ */ new Map();
2506
+ const row = ({ dirty: _, ...row2 }) => row2;
2507
+ const flush_dirty = async () => {
2508
+ const rows = [];
2509
+ const flushed = [];
2510
+ for (const f of cache2.values())
2511
+ if (f.dirty) {
2512
+ rows.push(row(f));
2513
+ flushed.push(f);
2514
+ }
2515
+ if (rows.length === 0) return;
2516
+ await flush(rows);
2517
+ for (const f of flushed) f.dirty = false;
2518
+ };
2519
+ return async (events) => {
2520
+ let folded_since_flush = 0;
2521
+ for (const event of events) {
2522
+ const stream = event.stream;
2523
+ let fold = cache2.get(stream);
2524
+ if (fold) {
2525
+ cache2.delete(stream);
2526
+ cache2.set(stream, fold);
2527
+ } else {
2528
+ if (cache2.size >= config2.maxCachedStates) {
2529
+ const oldest = cache2.keys().next().value;
2530
+ const evictee = cache2.get(oldest);
2531
+ if (evictee.dirty) await flush([row(evictee)]);
2532
+ cache2.delete(oldest);
2533
+ }
2534
+ const snapshot = await load(me, { stream }, void 0, patch_fn);
2535
+ fold = {
2536
+ stream,
2537
+ state: snapshot.state,
2538
+ version: snapshot.version,
2539
+ event_id: snapshot.id,
2540
+ patches: snapshot.patches,
2541
+ snaps: snapshot.snaps,
2542
+ dirty: true
2543
+ };
2544
+ cache2.set(stream, fold);
2545
+ }
2546
+ if (event.id > fold.event_id) {
2547
+ const reducer = me.patch[event.name];
2548
+ fold.state = patch_fn(
2549
+ me,
2550
+ fold.state,
2551
+ reducer(event, fold.state),
2552
+ event
2553
+ );
2554
+ fold.version = event.version;
2555
+ fold.event_id = event.id;
2556
+ fold.patches++;
2557
+ fold.dirty = true;
2558
+ } else {
2559
+ fold.dirty = true;
2560
+ }
2561
+ if (++folded_since_flush >= config2.flushEvery) {
2562
+ await flush_dirty();
2563
+ folded_since_flush = 0;
2564
+ }
2565
+ }
2566
+ await flush_dirty();
2567
+ };
2568
+ }
2569
+
2471
2570
  // src/internal/reactions.ts
2472
2571
  function finalize(lease, handled, at, error, options, logger, failed_at) {
2473
2572
  if (!error) return { lease, handled, acked_at: at };
@@ -2673,82 +2772,6 @@ var SettleLoop = class {
2673
2772
  }
2674
2773
  };
2675
2774
 
2676
- // src/internal/state-fold.ts
2677
- import { patch as patch2 } from "@rotorsoft/act-patch";
2678
- import { z as z5 } from "zod";
2679
- var DEFAULT_FOLD_FLUSH_EVERY = 1e3;
2680
- var DEFAULT_MAX_CACHED_STATES = 1e4;
2681
- var FoldOptionsSchema = z5.object({
2682
- flushEvery: z5.number().int().min(1).default(DEFAULT_FOLD_FLUSH_EVERY),
2683
- maxCachedStates: z5.number().int().min(1).default(DEFAULT_MAX_CACHED_STATES)
2684
- });
2685
- function resolveFoldConfig(options) {
2686
- return FoldOptionsSchema.parse(options);
2687
- }
2688
- function make_fold_handler(me, flush, config2) {
2689
- const cache2 = /* @__PURE__ */ new Map();
2690
- const row = ({ dirty: _, ...row2 }) => row2;
2691
- const flush_dirty = async () => {
2692
- const rows = [];
2693
- const flushed = [];
2694
- for (const f of cache2.values())
2695
- if (f.dirty) {
2696
- rows.push(row(f));
2697
- flushed.push(f);
2698
- }
2699
- if (rows.length === 0) return;
2700
- await flush(rows);
2701
- for (const f of flushed) f.dirty = false;
2702
- };
2703
- return async (events) => {
2704
- let folded_since_flush = 0;
2705
- for (const event of events) {
2706
- const stream = event.stream;
2707
- let fold = cache2.get(stream);
2708
- if (fold) {
2709
- cache2.delete(stream);
2710
- cache2.set(stream, fold);
2711
- } else {
2712
- if (cache2.size >= config2.maxCachedStates) {
2713
- const oldest = cache2.keys().next().value;
2714
- const evictee = cache2.get(oldest);
2715
- if (evictee.dirty) await flush([row(evictee)]);
2716
- cache2.delete(oldest);
2717
- }
2718
- const snapshot = await load(me, { stream });
2719
- fold = {
2720
- stream,
2721
- state: snapshot.state,
2722
- version: snapshot.version,
2723
- event_id: snapshot.id,
2724
- patches: snapshot.patches,
2725
- snaps: snapshot.snaps,
2726
- dirty: true
2727
- };
2728
- cache2.set(stream, fold);
2729
- }
2730
- if (event.id > fold.event_id) {
2731
- const reducer = me.patch[event.name];
2732
- fold.state = patch2(
2733
- fold.state,
2734
- reducer(event, fold.state)
2735
- );
2736
- fold.version = event.version;
2737
- fold.event_id = event.id;
2738
- fold.patches++;
2739
- fold.dirty = true;
2740
- } else {
2741
- fold.dirty = true;
2742
- }
2743
- if (++folded_since_flush >= config2.flushEvery) {
2744
- await flush_dirty();
2745
- folded_since_flush = 0;
2746
- }
2747
- }
2748
- await flush_dirty();
2749
- };
2750
- }
2751
-
2752
2775
  // src/act.ts
2753
2776
  var DEFAULT_MAX_SUBSCRIBED_STREAMS = 1e3;
2754
2777
  var DEFAULT_SETTLE_DEBOUNCE_MS = 10;
@@ -2905,8 +2928,14 @@ var Act = class {
2905
2928
  * @param lanes Declared drain lanes (ACT-1103). The builder collects
2906
2929
  * these from `.withLane(...)` calls. Slice 1 records them on the
2907
2930
  * instance; later slices fan out one `DrainController` per lane.
2931
+ * @param patch_fn The per-event patch step selected once by the builder
2932
+ * from `ActOptions.validateFoldedState` (ACT-1238) — `bare_patch` by
2933
+ * default, `validating_patch` when the flag is on. The builder uses
2934
+ * the same value for its projection-fold handlers, so there is a
2935
+ * single selection site. Defaults to `bare_patch` for direct
2936
+ * construction.
2908
2937
  */
2909
- constructor(registry, states = /* @__PURE__ */ new Map(), batch_handlers = /* @__PURE__ */ new Map(), options = {}, lanes = []) {
2938
+ constructor(registry, states = /* @__PURE__ */ new Map(), batch_handlers = /* @__PURE__ */ new Map(), options = {}, lanes = [], patch_fn = bare_patch) {
2910
2939
  this.registry = registry;
2911
2940
  this._states = states;
2912
2941
  this._batch_handlers = batch_handlers;
@@ -2914,7 +2943,7 @@ var Act = class {
2914
2943
  validate_only_lanes(options, lanes);
2915
2944
  this._scoped = options.scoped ? (fn) => scoped.run(options.scoped, fn) : (fn) => fn();
2916
2945
  this._correlator = options.correlator ?? default_correlator;
2917
- this._es = build_es(this._logger, this._correlator);
2946
+ this._es = build_es(this._logger, this._correlator, patch_fn);
2918
2947
  this._cd = build_drain(this._logger);
2919
2948
  this._handle = build_handle({
2920
2949
  logger: this._logger,
@@ -4155,6 +4184,7 @@ function act() {
4155
4184
  },
4156
4185
  on: (event) => reaction_on(event, registry.events, builder),
4157
4186
  build: (options) => {
4187
+ const patch_fn = options?.validateFoldedState === true ? validating_patch : bare_patch;
4158
4188
  if (!_built) {
4159
4189
  for (const proj of pending_projections) {
4160
4190
  merge_projection(proj, registry.events);
@@ -4177,7 +4207,7 @@ function act() {
4177
4207
  );
4178
4208
  batch_handlers.set(
4179
4209
  proj.target,
4180
- make_fold_handler(merged, fold.flush, fold.config)
4210
+ make_fold_handler(merged, fold.flush, fold.config, patch_fn)
4181
4211
  );
4182
4212
  }
4183
4213
  finalize_deprecations();
@@ -4250,7 +4280,8 @@ function act() {
4250
4280
  states,
4251
4281
  batch_handlers,
4252
4282
  options,
4253
- lanes
4283
+ lanes,
4284
+ patch_fn
4254
4285
  );
4255
4286
  },
4256
4287
  events: registry.events