@rotorsoft/act 0.32.0 → 0.32.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.
package/dist/index.js CHANGED
@@ -648,63 +648,6 @@ function dispose(disposer) {
648
648
  }
649
649
  var SNAP_EVENT = "__snapshot__";
650
650
  var TOMBSTONE_EVENT = "__tombstone__";
651
- function build_tracer(logLevel2) {
652
- if (logLevel2 === "trace") {
653
- const logger4 = log();
654
- return {
655
- fetched: (fetched) => {
656
- const data = Object.fromEntries(
657
- fetched.map(({ stream, source, events }) => {
658
- const key = source ? `${stream}<-${source}` : stream;
659
- const value = Object.fromEntries(
660
- events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
661
- );
662
- return [key, value];
663
- })
664
- );
665
- logger4.trace(data, ">> fetch");
666
- },
667
- correlated: (streams) => {
668
- const data = streams.map(({ stream }) => stream).join(" ");
669
- logger4.trace(`>> correlate ${data}`);
670
- },
671
- leased: (leases) => {
672
- const data = Object.fromEntries(
673
- leases.map(({ stream, at, retry }) => [stream, { at, retry }])
674
- );
675
- logger4.trace(data, ">> lease");
676
- },
677
- acked: (leases) => {
678
- const data = Object.fromEntries(
679
- leases.map(({ stream, at, retry }) => [stream, { at, retry }])
680
- );
681
- logger4.trace(data, ">> ack");
682
- },
683
- blocked: (leases) => {
684
- const data = Object.fromEntries(
685
- leases.map(({ stream, at, retry, error }) => [
686
- stream,
687
- { at, retry, error }
688
- ])
689
- );
690
- logger4.trace(data, ">> block");
691
- }
692
- };
693
- } else {
694
- return {
695
- fetched: () => {
696
- },
697
- correlated: () => {
698
- },
699
- leased: () => {
700
- },
701
- acked: () => {
702
- },
703
- blocked: () => {
704
- }
705
- };
706
- }
707
- }
708
651
 
709
652
  // src/signals.ts
710
653
  var logger = log();
@@ -729,14 +672,33 @@ process.once("unhandledRejection", async (arg) => {
729
672
  import { randomUUID as randomUUID2 } from "crypto";
730
673
  import EventEmitter from "events";
731
674
 
732
- // src/event-sourcing.ts
675
+ // src/internal/drain.ts
676
+ var claim = (lagging, leading, by, millis) => store().claim(lagging, leading, by, millis);
677
+ async function fetch(leased, eventLimit) {
678
+ return Promise.all(
679
+ leased.map(async ({ stream, source, at, lagging }) => {
680
+ const events = [];
681
+ await store().query((e) => events.push(e), {
682
+ stream: source,
683
+ after: at,
684
+ limit: eventLimit
685
+ });
686
+ return { stream, source, at, lagging, events };
687
+ })
688
+ );
689
+ }
690
+ var ack = (leases) => store().ack(leases);
691
+ var block = (leases) => store().block(leases);
692
+ var subscribe = (streams) => store().subscribe(streams);
693
+
694
+ // src/internal/event-sourcing.ts
733
695
  import { patch } from "@rotorsoft/act-patch";
734
696
  import { randomUUID } from "crypto";
735
697
  var logger2 = log();
736
698
  async function snap(snapshot) {
737
699
  try {
738
700
  const { id, stream, name, meta, version } = snapshot.event;
739
- const snapped = await store().commit(
701
+ await store().commit(
740
702
  stream,
741
703
  [{ name: SNAP_EVENT, data: snapshot.state }],
742
704
  {
@@ -746,7 +708,6 @@ async function snap(snapshot) {
746
708
  version
747
709
  // IMPORTANT! - state events are committed right after the snapshot event
748
710
  );
749
- logger2.trace(snapped, "\u{1F7E0} snap");
750
711
  } catch (error) {
751
712
  logger2.error(error);
752
713
  }
@@ -758,7 +719,7 @@ async function load(me, stream, callback, asOf) {
758
719
  let patches = cached?.patches ?? 0;
759
720
  let snaps = cached?.snaps ?? 0;
760
721
  let event;
761
- const count = await store().query(
722
+ await store().query(
762
723
  (e) => {
763
724
  event = e;
764
725
  if (e.name === SNAP_EVENT) {
@@ -777,10 +738,6 @@ async function load(me, stream, callback, asOf) {
777
738
  ...cached ? { after: cached.event_id } : { with_snaps: true, ...asOf }
778
739
  }
779
740
  );
780
- logger2.trace(
781
- state2,
782
- `\u{1F7E2} load ${stream}${cached && count === 0 ? " (cached)" : ""}${timeTravel ? " (as-of)" : ""}`
783
- );
784
741
  return { event, state: state2, patches, snaps };
785
742
  }
786
743
  async function action(me, action2, target, payload, reactingTo, skipValidation = false) {
@@ -791,10 +748,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
791
748
  if (snapshot.event?.name === TOMBSTONE_EVENT)
792
749
  throw new StreamClosedError(stream);
793
750
  const expected = expectedVersion ?? snapshot.event?.version;
794
- logger2.trace(
795
- payload,
796
- `\u{1F535} ${stream}.${action2}${typeof expected === "number" ? `.${expected}` : ""}`
797
- );
798
751
  if (me.given) {
799
752
  const invariants = me.given[action2] || [];
800
753
  invariants.forEach(({ valid, description }) => {
@@ -834,10 +787,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
834
787
  } : void 0
835
788
  }
836
789
  };
837
- logger2.trace(
838
- emitted.map((e) => e.data),
839
- `\u{1F534} commit ${stream}.${emitted.map((e) => e.name).join(", ")}`
840
- );
841
790
  let committed;
842
791
  try {
843
792
  committed = await store().commit(
@@ -873,14 +822,257 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
873
822
  return snapshots;
874
823
  }
875
824
 
876
- // src/act.ts
825
+ // src/internal/merge.ts
826
+ import { ZodObject } from "zod";
827
+ function baseTypeName(zodType) {
828
+ let t = zodType;
829
+ while (typeof t.unwrap === "function") {
830
+ t = t.unwrap();
831
+ }
832
+ return t.constructor.name;
833
+ }
834
+ function mergeSchemas(existing, incoming, stateName) {
835
+ if (existing instanceof ZodObject && incoming instanceof ZodObject) {
836
+ const existingShape = existing.shape;
837
+ const incomingShape = incoming.shape;
838
+ for (const key of Object.keys(incomingShape)) {
839
+ if (key in existingShape) {
840
+ const existingBase = baseTypeName(existingShape[key]);
841
+ const incomingBase = baseTypeName(incomingShape[key]);
842
+ if (existingBase !== incomingBase) {
843
+ throw new Error(
844
+ `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
845
+ );
846
+ }
847
+ }
848
+ }
849
+ return existing.extend(incomingShape);
850
+ }
851
+ return existing;
852
+ }
853
+ function mergeInits(existing, incoming) {
854
+ return () => ({ ...existing(), ...incoming() });
855
+ }
856
+ function registerState(state2, states, actions, events) {
857
+ if (states.has(state2.name)) {
858
+ const existing = states.get(state2.name);
859
+ for (const name of Object.keys(state2.actions)) {
860
+ if (existing.actions[name] === state2.actions[name]) continue;
861
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
862
+ }
863
+ for (const name of Object.keys(state2.events)) {
864
+ if (existing.events[name] === state2.events[name]) continue;
865
+ if (existing.events[name]) continue;
866
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
867
+ }
868
+ const mergedPatch = { ...existing.patch };
869
+ for (const name of Object.keys(state2.patch)) {
870
+ const existingP = existing.patch[name];
871
+ const incomingP = state2.patch[name];
872
+ if (!existingP) {
873
+ mergedPatch[name] = incomingP;
874
+ } else {
875
+ const existingIsDefault = existingP._passthrough;
876
+ const incomingIsDefault = incomingP._passthrough;
877
+ if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
878
+ throw new Error(
879
+ `Duplicate custom patch for event "${name}" in state "${state2.name}"`
880
+ );
881
+ }
882
+ if (existingIsDefault && !incomingIsDefault) {
883
+ mergedPatch[name] = incomingP;
884
+ }
885
+ }
886
+ }
887
+ const merged = {
888
+ ...existing,
889
+ state: mergeSchemas(existing.state, state2.state, state2.name),
890
+ init: mergeInits(existing.init, state2.init),
891
+ events: { ...existing.events, ...state2.events },
892
+ actions: { ...existing.actions, ...state2.actions },
893
+ patch: mergedPatch,
894
+ on: { ...existing.on, ...state2.on },
895
+ given: { ...existing.given, ...state2.given },
896
+ snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
897
+ throw new Error(
898
+ `Duplicate snap strategy for state "${state2.name}"`
899
+ );
900
+ })() : state2.snap || existing.snap
901
+ };
902
+ states.set(state2.name, merged);
903
+ for (const name of Object.keys(merged.actions)) {
904
+ actions[name] = merged;
905
+ }
906
+ for (const name of Object.keys(state2.events)) {
907
+ if (events[name]) continue;
908
+ events[name] = {
909
+ schema: state2.events[name],
910
+ reactions: /* @__PURE__ */ new Map()
911
+ };
912
+ }
913
+ } else {
914
+ states.set(state2.name, state2);
915
+ for (const name of Object.keys(state2.actions)) {
916
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
917
+ actions[name] = state2;
918
+ }
919
+ for (const name of Object.keys(state2.events)) {
920
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
921
+ events[name] = {
922
+ schema: state2.events[name],
923
+ reactions: /* @__PURE__ */ new Map()
924
+ };
925
+ }
926
+ }
927
+ }
928
+ function mergeProjection(proj, events) {
929
+ for (const eventName of Object.keys(proj.events)) {
930
+ const projRegister = proj.events[eventName];
931
+ const existing = events[eventName];
932
+ if (!existing) {
933
+ events[eventName] = {
934
+ schema: projRegister.schema,
935
+ reactions: new Map(projRegister.reactions)
936
+ };
937
+ } else {
938
+ for (const [name, reaction] of projRegister.reactions) {
939
+ let key = name;
940
+ while (existing.reactions.has(key)) key = `${key}_p`;
941
+ existing.reactions.set(key, reaction);
942
+ }
943
+ }
944
+ }
945
+ }
946
+ var _this_ = ({ stream }) => ({
947
+ source: stream,
948
+ target: stream
949
+ });
950
+
951
+ // src/internal/tracing.ts
877
952
  var logger3 = log();
878
- var tracer = build_tracer(config().logLevel);
953
+ var withSnapTrace = (inner) => async (snapshot) => {
954
+ logger3.trace(
955
+ `\u{1F7E0} snap ${snapshot.event.stream}@${snapshot.event.version}`
956
+ );
957
+ return inner(snapshot);
958
+ };
959
+ var withLoadTrace = (inner) => async (me, stream, callback, asOf) => {
960
+ logger3.trace(`\u{1F7E2} load ${stream}${asOf ? " (as-of)" : ""}`);
961
+ return inner(me, stream, callback, asOf);
962
+ };
963
+ var withActionTrace = (inner) => async (me, action2, target, payload, reactingTo, skipValidation) => {
964
+ logger3.trace(payload, `\u{1F535} ${target.stream}.${action2}`);
965
+ const snapshots = await inner(
966
+ me,
967
+ action2,
968
+ target,
969
+ payload,
970
+ reactingTo,
971
+ skipValidation
972
+ );
973
+ const committed = snapshots.filter((s) => s.event);
974
+ if (committed.length) {
975
+ logger3.trace(
976
+ committed.map((s) => s.event.data),
977
+ `\u{1F534} commit ${target.stream}.${committed.map((s) => s.event.name).join(", ")}`
978
+ );
979
+ }
980
+ return snapshots;
981
+ };
982
+ function buildEs(level) {
983
+ if (level !== "trace") {
984
+ return { snap, load, action };
985
+ }
986
+ return {
987
+ snap: withSnapTrace(snap),
988
+ load: withLoadTrace(load),
989
+ action: withActionTrace(action)
990
+ };
991
+ }
992
+ var withClaimTrace = (inner) => async (lagging, leading, by, millis) => {
993
+ const leased = await inner(lagging, leading, by, millis);
994
+ if (leased.length) {
995
+ const data = Object.fromEntries(
996
+ leased.map(({ stream, at, retry }) => [stream, { at, retry }])
997
+ );
998
+ logger3.trace(data, ">> lease");
999
+ }
1000
+ return leased;
1001
+ };
1002
+ var withFetchTrace = (inner) => async (leased, eventLimit) => {
1003
+ const fetched = await inner(leased, eventLimit);
1004
+ const data = Object.fromEntries(
1005
+ fetched.map(({ stream, source, events }) => {
1006
+ const key = source ? `${stream}<-${source}` : stream;
1007
+ const value = Object.fromEntries(
1008
+ events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
1009
+ );
1010
+ return [key, value];
1011
+ })
1012
+ );
1013
+ logger3.trace(data, ">> fetch");
1014
+ return fetched;
1015
+ };
1016
+ var withAckTrace = (inner) => async (leases) => {
1017
+ const acked = await inner(leases);
1018
+ if (acked.length) {
1019
+ const data = Object.fromEntries(
1020
+ acked.map(({ stream, at, retry }) => [stream, { at, retry }])
1021
+ );
1022
+ logger3.trace(data, ">> ack");
1023
+ }
1024
+ return acked;
1025
+ };
1026
+ var withBlockTrace = (inner) => async (leases) => {
1027
+ const blocked = await inner(leases);
1028
+ if (blocked.length) {
1029
+ const data = Object.fromEntries(
1030
+ blocked.map(({ stream, at, retry, error }) => [
1031
+ stream,
1032
+ { at, retry, error }
1033
+ ])
1034
+ );
1035
+ logger3.trace(data, ">> block");
1036
+ }
1037
+ return blocked;
1038
+ };
1039
+ var withSubscribeTrace = (inner) => async (streams) => {
1040
+ const result = await inner(streams);
1041
+ if (result.subscribed) {
1042
+ const data = streams.map(({ stream }) => stream).join(" ");
1043
+ logger3.trace(`>> correlate ${data}`);
1044
+ }
1045
+ return result;
1046
+ };
1047
+ function buildDrain(level) {
1048
+ if (level !== "trace") {
1049
+ return {
1050
+ claim,
1051
+ fetch,
1052
+ ack,
1053
+ block,
1054
+ subscribe
1055
+ };
1056
+ }
1057
+ return {
1058
+ claim: withClaimTrace(claim),
1059
+ fetch: withFetchTrace(fetch),
1060
+ ack: withAckTrace(ack),
1061
+ block: withBlockTrace(block),
1062
+ subscribe: withSubscribeTrace(subscribe)
1063
+ };
1064
+ }
1065
+
1066
+ // src/act.ts
1067
+ var logger4 = log();
879
1068
  var Act = class {
880
1069
  constructor(registry, _states = /* @__PURE__ */ new Map(), batchHandlers = /* @__PURE__ */ new Map()) {
881
1070
  this.registry = registry;
882
1071
  this._states = _states;
883
1072
  this._batch_handlers = batchHandlers;
1073
+ const level = log().level;
1074
+ this._es = buildEs(level);
1075
+ this._cd = buildDrain(level);
884
1076
  const statics = [];
885
1077
  for (const [name, register] of Object.entries(this.registry.events)) {
886
1078
  if (register.reactions.size > 0) {
@@ -940,6 +1132,10 @@ var Act = class {
940
1132
  _static_targets;
941
1133
  /** Batch handlers for static-target projections (target → handler) */
942
1134
  _batch_handlers;
1135
+ /** Event-sourcing handlers, optionally wrapped with trace decorators */
1136
+ _es;
1137
+ /** Correlate/drain pipeline ops, optionally wrapped with trace decorators */
1138
+ _cd;
943
1139
  /**
944
1140
  * Executes an action on a state instance, committing resulting events.
945
1141
  *
@@ -1022,7 +1218,7 @@ var Act = class {
1022
1218
  * @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
1023
1219
  */
1024
1220
  async do(action2, target, payload, reactingTo, skipValidation = false) {
1025
- const snapshots = await action(
1221
+ const snapshots = await this._es.action(
1026
1222
  this.registry.actions[action2],
1027
1223
  action2,
1028
1224
  target,
@@ -1048,7 +1244,7 @@ var Act = class {
1048
1244
  } else {
1049
1245
  merged = this._states.get(stateOrName.name) || stateOrName;
1050
1246
  }
1051
- return await load(merged, stream, callback, asOf);
1247
+ return await this._es.load(merged, stream, callback, asOf);
1052
1248
  }
1053
1249
  /**
1054
1250
  * Queries the event store for events matching a filter.
@@ -1161,7 +1357,7 @@ var Act = class {
1161
1357
  if (payloads.length === 0) return { lease, handled: 0, at: lease.at };
1162
1358
  const stream = lease.stream;
1163
1359
  let at = payloads.at(0).event.id, handled = 0;
1164
- lease.retry > 0 && logger3.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1360
+ lease.retry > 0 && logger4.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1165
1361
  const doAction = this.do.bind(this);
1166
1362
  const scopedApp = {
1167
1363
  do: doAction,
@@ -1183,16 +1379,16 @@ var Act = class {
1183
1379
  at = event.id;
1184
1380
  handled++;
1185
1381
  } catch (error) {
1186
- logger3.error(error);
1187
- const block = lease.retry >= options.maxRetries && options.blockOnError;
1188
- block && logger3.error(`Blocking ${stream} after ${lease.retry} retries.`);
1382
+ logger4.error(error);
1383
+ const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1384
+ block2 && logger4.error(`Blocking ${stream} after ${lease.retry} retries.`);
1189
1385
  return {
1190
1386
  lease,
1191
1387
  handled,
1192
1388
  at,
1193
1389
  // only report error when nothing was handled
1194
1390
  error: handled === 0 ? error.message : void 0,
1195
- block
1391
+ block: block2
1196
1392
  };
1197
1393
  }
1198
1394
  }
@@ -1215,21 +1411,21 @@ var Act = class {
1215
1411
  const stream = lease.stream;
1216
1412
  const events = payloads.map((p) => p.event);
1217
1413
  const at = events.at(-1).id;
1218
- lease.retry > 0 && logger3.warn(`Retrying batch ${stream}@${events[0].id} (${lease.retry}).`);
1414
+ lease.retry > 0 && logger4.warn(`Retrying batch ${stream}@${events[0].id} (${lease.retry}).`);
1219
1415
  try {
1220
1416
  await batchHandler(events, stream);
1221
1417
  return { lease, handled: events.length, at };
1222
1418
  } catch (error) {
1223
- logger3.error(error);
1419
+ logger4.error(error);
1224
1420
  const { options } = payloads[0];
1225
- const block = lease.retry >= options.maxRetries && options.blockOnError;
1226
- block && logger3.error(`Blocking ${stream} after ${lease.retry} retries.`);
1421
+ const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1422
+ block2 && logger4.error(`Blocking ${stream} after ${lease.retry} retries.`);
1227
1423
  return {
1228
1424
  lease,
1229
1425
  handled: 0,
1230
1426
  at: lease.at,
1231
1427
  error: error.message,
1232
- block
1428
+ block: block2
1233
1429
  };
1234
1430
  }
1235
1431
  }
@@ -1285,7 +1481,7 @@ var Act = class {
1285
1481
  this._drain_locked = true;
1286
1482
  const lagging = Math.ceil(streamLimit * this._drain_lag2lead_ratio);
1287
1483
  const leading = streamLimit - lagging;
1288
- const leased = await store().claim(
1484
+ const leased = await this._cd.claim(
1289
1485
  lagging,
1290
1486
  leading,
1291
1487
  randomUUID2(),
@@ -1295,17 +1491,7 @@ var Act = class {
1295
1491
  this._needs_drain = false;
1296
1492
  return { fetched: [], leased: [], acked: [], blocked: [] };
1297
1493
  }
1298
- const fetched = await Promise.all(
1299
- leased.map(async ({ stream, source, at, lagging: lagging2 }) => {
1300
- const events = await this.query_array({
1301
- stream: source,
1302
- after: at,
1303
- limit: eventLimit
1304
- });
1305
- return { stream, source, at, lagging: lagging2, events };
1306
- })
1307
- );
1308
- tracer.fetched(fetched);
1494
+ const fetched = await this._cd.fetch(leased, eventLimit);
1309
1495
  const payloadsMap = /* @__PURE__ */ new Map();
1310
1496
  const fetch_window_at = fetched.reduce(
1311
1497
  (max, { at, events }) => Math.max(max, events.at(-1)?.id || at),
@@ -1322,7 +1508,6 @@ var Act = class {
1322
1508
  });
1323
1509
  payloadsMap.set(stream, payloads);
1324
1510
  });
1325
- tracer.leased(leased);
1326
1511
  const handled = await Promise.all(
1327
1512
  leased.map((lease) => {
1328
1513
  const streamFetch = fetched.find((f) => f.stream === lease.stream);
@@ -1346,27 +1531,21 @@ var Act = class {
1346
1531
  const leading_avg = leading > 0 ? leading_handled / leading : 0;
1347
1532
  const total = lagging_avg + leading_avg;
1348
1533
  this._drain_lag2lead_ratio = total > 0 ? Math.max(0.2, Math.min(0.8, lagging_avg / total)) : 0.5;
1349
- const acked = await store().ack(
1534
+ const acked = await this._cd.ack(
1350
1535
  handled.filter(({ error }) => !error).map(({ at, lease }) => ({ ...lease, at }))
1351
1536
  );
1352
- if (acked.length) {
1353
- tracer.acked(acked);
1354
- this.emit("acked", acked);
1355
- }
1356
- const blocked = await store().block(
1357
- handled.filter(({ block }) => block).map(({ lease, error }) => ({ ...lease, error }))
1537
+ if (acked.length) this.emit("acked", acked);
1538
+ const blocked = await this._cd.block(
1539
+ handled.filter(({ block: block2 }) => block2).map(({ lease, error }) => ({ ...lease, error }))
1358
1540
  );
1359
- if (blocked.length) {
1360
- tracer.blocked(blocked);
1361
- this.emit("blocked", blocked);
1362
- }
1541
+ if (blocked.length) this.emit("blocked", blocked);
1363
1542
  const result = { fetched, leased, acked, blocked };
1364
1543
  const hasErrors = handled.some(({ error }) => error);
1365
1544
  if (!acked.length && !blocked.length && !hasErrors)
1366
1545
  this._needs_drain = false;
1367
1546
  return result;
1368
1547
  } catch (error) {
1369
- logger3.error(error);
1548
+ logger4.error(error);
1370
1549
  } finally {
1371
1550
  this._drain_locked = false;
1372
1551
  }
@@ -1472,10 +1651,9 @@ var Act = class {
1472
1651
  stream,
1473
1652
  source
1474
1653
  }));
1475
- const { subscribed } = await store().subscribe(streams);
1654
+ const { subscribed } = await this._cd.subscribe(streams);
1476
1655
  this._correlation_checkpoint = last_id;
1477
1656
  if (subscribed) {
1478
- tracer.correlated(streams);
1479
1657
  for (const { stream } of streams) {
1480
1658
  this._subscribed_statics.add(stream);
1481
1659
  }
@@ -1740,7 +1918,7 @@ var Act = class {
1740
1918
  if (mergedState) {
1741
1919
  await Promise.all(
1742
1920
  guarded.filter((s) => targetMap.get(s)?.restart).map(async (stream) => {
1743
- const snap2 = await load(mergedState, stream);
1921
+ const snap2 = await this._es.load(mergedState, stream);
1744
1922
  seedStates.set(stream, snap2.state);
1745
1923
  })
1746
1924
  );
@@ -1847,139 +2025,13 @@ var Act = class {
1847
2025
  if (!made_progress) break;
1848
2026
  }
1849
2027
  if (lastDrain) this.emit("settled", lastDrain);
1850
- })().catch((err) => logger3.error(err)).finally(() => {
2028
+ })().catch((err) => logger4.error(err)).finally(() => {
1851
2029
  this._settling = false;
1852
2030
  });
1853
2031
  }, debounceMs);
1854
2032
  }
1855
2033
  };
1856
2034
 
1857
- // src/merge.ts
1858
- import { ZodObject } from "zod";
1859
- function baseTypeName(zodType) {
1860
- let t = zodType;
1861
- while (typeof t.unwrap === "function") {
1862
- t = t.unwrap();
1863
- }
1864
- return t.constructor.name;
1865
- }
1866
- function mergeSchemas(existing, incoming, stateName) {
1867
- if (existing instanceof ZodObject && incoming instanceof ZodObject) {
1868
- const existingShape = existing.shape;
1869
- const incomingShape = incoming.shape;
1870
- for (const key of Object.keys(incomingShape)) {
1871
- if (key in existingShape) {
1872
- const existingBase = baseTypeName(existingShape[key]);
1873
- const incomingBase = baseTypeName(incomingShape[key]);
1874
- if (existingBase !== incomingBase) {
1875
- throw new Error(
1876
- `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
1877
- );
1878
- }
1879
- }
1880
- }
1881
- return existing.extend(incomingShape);
1882
- }
1883
- return existing;
1884
- }
1885
- function mergeInits(existing, incoming) {
1886
- return () => ({ ...existing(), ...incoming() });
1887
- }
1888
- function registerState(state2, states, actions, events) {
1889
- if (states.has(state2.name)) {
1890
- const existing = states.get(state2.name);
1891
- for (const name of Object.keys(state2.actions)) {
1892
- if (existing.actions[name] === state2.actions[name]) continue;
1893
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
1894
- }
1895
- for (const name of Object.keys(state2.events)) {
1896
- if (existing.events[name] === state2.events[name]) continue;
1897
- if (existing.events[name]) continue;
1898
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
1899
- }
1900
- const mergedPatch = { ...existing.patch };
1901
- for (const name of Object.keys(state2.patch)) {
1902
- const existingP = existing.patch[name];
1903
- const incomingP = state2.patch[name];
1904
- if (!existingP) {
1905
- mergedPatch[name] = incomingP;
1906
- } else {
1907
- const existingIsDefault = existingP._passthrough;
1908
- const incomingIsDefault = incomingP._passthrough;
1909
- if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
1910
- throw new Error(
1911
- `Duplicate custom patch for event "${name}" in state "${state2.name}"`
1912
- );
1913
- }
1914
- if (existingIsDefault && !incomingIsDefault) {
1915
- mergedPatch[name] = incomingP;
1916
- }
1917
- }
1918
- }
1919
- const merged = {
1920
- ...existing,
1921
- state: mergeSchemas(existing.state, state2.state, state2.name),
1922
- init: mergeInits(existing.init, state2.init),
1923
- events: { ...existing.events, ...state2.events },
1924
- actions: { ...existing.actions, ...state2.actions },
1925
- patch: mergedPatch,
1926
- on: { ...existing.on, ...state2.on },
1927
- given: { ...existing.given, ...state2.given },
1928
- snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
1929
- throw new Error(
1930
- `Duplicate snap strategy for state "${state2.name}"`
1931
- );
1932
- })() : state2.snap || existing.snap
1933
- };
1934
- states.set(state2.name, merged);
1935
- for (const name of Object.keys(merged.actions)) {
1936
- actions[name] = merged;
1937
- }
1938
- for (const name of Object.keys(state2.events)) {
1939
- if (events[name]) continue;
1940
- events[name] = {
1941
- schema: state2.events[name],
1942
- reactions: /* @__PURE__ */ new Map()
1943
- };
1944
- }
1945
- } else {
1946
- states.set(state2.name, state2);
1947
- for (const name of Object.keys(state2.actions)) {
1948
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
1949
- actions[name] = state2;
1950
- }
1951
- for (const name of Object.keys(state2.events)) {
1952
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
1953
- events[name] = {
1954
- schema: state2.events[name],
1955
- reactions: /* @__PURE__ */ new Map()
1956
- };
1957
- }
1958
- }
1959
- }
1960
- function mergeProjection(proj, events) {
1961
- for (const eventName of Object.keys(proj.events)) {
1962
- const projRegister = proj.events[eventName];
1963
- const existing = events[eventName];
1964
- if (!existing) {
1965
- events[eventName] = {
1966
- schema: projRegister.schema,
1967
- reactions: new Map(projRegister.reactions)
1968
- };
1969
- } else {
1970
- for (const [name, reaction] of projRegister.reactions) {
1971
- let key = name;
1972
- while (existing.reactions.has(key)) key = `${key}_p`;
1973
- existing.reactions.set(key, reaction);
1974
- }
1975
- }
1976
- }
1977
- }
1978
- var _this_ = ({ stream }) => ({
1979
- source: stream,
1980
- target: stream
1981
- });
1982
-
1983
2035
  // src/act-builder.ts
1984
2036
  function act(states = /* @__PURE__ */ new Map(), registry = {
1985
2037
  actions: {},
@@ -2322,7 +2374,6 @@ export {
2322
2374
  ValidationError,
2323
2375
  ZodEmpty,
2324
2376
  act,
2325
- build_tracer,
2326
2377
  cache,
2327
2378
  config,
2328
2379
  dispose,