@rotorsoft/act 0.31.1 → 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.cjs CHANGED
@@ -53,7 +53,6 @@ __export(index_exports, {
53
53
  ValidationError: () => ValidationError,
54
54
  ZodEmpty: () => ZodEmpty,
55
55
  act: () => act,
56
- build_tracer: () => build_tracer,
57
56
  cache: () => cache,
58
57
  config: () => config,
59
58
  dispose: () => dispose,
@@ -815,63 +814,6 @@ function dispose(disposer) {
815
814
  }
816
815
  var SNAP_EVENT = "__snapshot__";
817
816
  var TOMBSTONE_EVENT = "__tombstone__";
818
- function build_tracer(logLevel2) {
819
- if (logLevel2 === "trace") {
820
- const logger4 = log();
821
- return {
822
- fetched: (fetched) => {
823
- const data = Object.fromEntries(
824
- fetched.map(({ stream, source, events }) => {
825
- const key = source ? `${stream}<-${source}` : stream;
826
- const value = Object.fromEntries(
827
- events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
828
- );
829
- return [key, value];
830
- })
831
- );
832
- logger4.trace(data, ">> fetch");
833
- },
834
- correlated: (streams) => {
835
- const data = streams.map(({ stream }) => stream).join(" ");
836
- logger4.trace(`>> correlate ${data}`);
837
- },
838
- leased: (leases) => {
839
- const data = Object.fromEntries(
840
- leases.map(({ stream, at, retry }) => [stream, { at, retry }])
841
- );
842
- logger4.trace(data, ">> lease");
843
- },
844
- acked: (leases) => {
845
- const data = Object.fromEntries(
846
- leases.map(({ stream, at, retry }) => [stream, { at, retry }])
847
- );
848
- logger4.trace(data, ">> ack");
849
- },
850
- blocked: (leases) => {
851
- const data = Object.fromEntries(
852
- leases.map(({ stream, at, retry, error }) => [
853
- stream,
854
- { at, retry, error }
855
- ])
856
- );
857
- logger4.trace(data, ">> block");
858
- }
859
- };
860
- } else {
861
- return {
862
- fetched: () => {
863
- },
864
- correlated: () => {
865
- },
866
- leased: () => {
867
- },
868
- acked: () => {
869
- },
870
- blocked: () => {
871
- }
872
- };
873
- }
874
- }
875
817
 
876
818
  // src/signals.ts
877
819
  var logger = log();
@@ -896,14 +838,33 @@ process.once("unhandledRejection", async (arg) => {
896
838
  var import_crypto2 = require("crypto");
897
839
  var import_events = __toESM(require("events"), 1);
898
840
 
899
- // src/event-sourcing.ts
841
+ // src/internal/drain.ts
842
+ var claim = (lagging, leading, by, millis) => store().claim(lagging, leading, by, millis);
843
+ async function fetch(leased, eventLimit) {
844
+ return Promise.all(
845
+ leased.map(async ({ stream, source, at, lagging }) => {
846
+ const events = [];
847
+ await store().query((e) => events.push(e), {
848
+ stream: source,
849
+ after: at,
850
+ limit: eventLimit
851
+ });
852
+ return { stream, source, at, lagging, events };
853
+ })
854
+ );
855
+ }
856
+ var ack = (leases) => store().ack(leases);
857
+ var block = (leases) => store().block(leases);
858
+ var subscribe = (streams) => store().subscribe(streams);
859
+
860
+ // src/internal/event-sourcing.ts
900
861
  var import_act_patch = require("@rotorsoft/act-patch");
901
862
  var import_crypto = require("crypto");
902
863
  var logger2 = log();
903
864
  async function snap(snapshot) {
904
865
  try {
905
866
  const { id, stream, name, meta, version } = snapshot.event;
906
- const snapped = await store().commit(
867
+ await store().commit(
907
868
  stream,
908
869
  [{ name: SNAP_EVENT, data: snapshot.state }],
909
870
  {
@@ -913,7 +874,6 @@ async function snap(snapshot) {
913
874
  version
914
875
  // IMPORTANT! - state events are committed right after the snapshot event
915
876
  );
916
- logger2.trace(snapped, "\u{1F7E0} snap");
917
877
  } catch (error) {
918
878
  logger2.error(error);
919
879
  }
@@ -925,7 +885,7 @@ async function load(me, stream, callback, asOf) {
925
885
  let patches = cached?.patches ?? 0;
926
886
  let snaps = cached?.snaps ?? 0;
927
887
  let event;
928
- const count = await store().query(
888
+ await store().query(
929
889
  (e) => {
930
890
  event = e;
931
891
  if (e.name === SNAP_EVENT) {
@@ -944,10 +904,6 @@ async function load(me, stream, callback, asOf) {
944
904
  ...cached ? { after: cached.event_id } : { with_snaps: true, ...asOf }
945
905
  }
946
906
  );
947
- logger2.trace(
948
- state2,
949
- `\u{1F7E2} load ${stream}${cached && count === 0 ? " (cached)" : ""}${timeTravel ? " (as-of)" : ""}`
950
- );
951
907
  return { event, state: state2, patches, snaps };
952
908
  }
953
909
  async function action(me, action2, target, payload, reactingTo, skipValidation = false) {
@@ -958,10 +914,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
958
914
  if (snapshot.event?.name === TOMBSTONE_EVENT)
959
915
  throw new StreamClosedError(stream);
960
916
  const expected = expectedVersion ?? snapshot.event?.version;
961
- logger2.trace(
962
- payload,
963
- `\u{1F535} ${stream}.${action2}${typeof expected === "number" ? `.${expected}` : ""}`
964
- );
965
917
  if (me.given) {
966
918
  const invariants = me.given[action2] || [];
967
919
  invariants.forEach(({ valid, description }) => {
@@ -1001,10 +953,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1001
953
  } : void 0
1002
954
  }
1003
955
  };
1004
- logger2.trace(
1005
- emitted.map((e) => e.data),
1006
- `\u{1F534} commit ${stream}.${emitted.map((e) => e.name).join(", ")}`
1007
- );
1008
956
  let committed;
1009
957
  try {
1010
958
  committed = await store().commit(
@@ -1040,14 +988,257 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1040
988
  return snapshots;
1041
989
  }
1042
990
 
1043
- // src/act.ts
991
+ // src/internal/merge.ts
992
+ var import_zod4 = require("zod");
993
+ function baseTypeName(zodType) {
994
+ let t = zodType;
995
+ while (typeof t.unwrap === "function") {
996
+ t = t.unwrap();
997
+ }
998
+ return t.constructor.name;
999
+ }
1000
+ function mergeSchemas(existing, incoming, stateName) {
1001
+ if (existing instanceof import_zod4.ZodObject && incoming instanceof import_zod4.ZodObject) {
1002
+ const existingShape = existing.shape;
1003
+ const incomingShape = incoming.shape;
1004
+ for (const key of Object.keys(incomingShape)) {
1005
+ if (key in existingShape) {
1006
+ const existingBase = baseTypeName(existingShape[key]);
1007
+ const incomingBase = baseTypeName(incomingShape[key]);
1008
+ if (existingBase !== incomingBase) {
1009
+ throw new Error(
1010
+ `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
1011
+ );
1012
+ }
1013
+ }
1014
+ }
1015
+ return existing.extend(incomingShape);
1016
+ }
1017
+ return existing;
1018
+ }
1019
+ function mergeInits(existing, incoming) {
1020
+ return () => ({ ...existing(), ...incoming() });
1021
+ }
1022
+ function registerState(state2, states, actions, events) {
1023
+ if (states.has(state2.name)) {
1024
+ const existing = states.get(state2.name);
1025
+ for (const name of Object.keys(state2.actions)) {
1026
+ if (existing.actions[name] === state2.actions[name]) continue;
1027
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
1028
+ }
1029
+ for (const name of Object.keys(state2.events)) {
1030
+ if (existing.events[name] === state2.events[name]) continue;
1031
+ if (existing.events[name]) continue;
1032
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
1033
+ }
1034
+ const mergedPatch = { ...existing.patch };
1035
+ for (const name of Object.keys(state2.patch)) {
1036
+ const existingP = existing.patch[name];
1037
+ const incomingP = state2.patch[name];
1038
+ if (!existingP) {
1039
+ mergedPatch[name] = incomingP;
1040
+ } else {
1041
+ const existingIsDefault = existingP._passthrough;
1042
+ const incomingIsDefault = incomingP._passthrough;
1043
+ if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
1044
+ throw new Error(
1045
+ `Duplicate custom patch for event "${name}" in state "${state2.name}"`
1046
+ );
1047
+ }
1048
+ if (existingIsDefault && !incomingIsDefault) {
1049
+ mergedPatch[name] = incomingP;
1050
+ }
1051
+ }
1052
+ }
1053
+ const merged = {
1054
+ ...existing,
1055
+ state: mergeSchemas(existing.state, state2.state, state2.name),
1056
+ init: mergeInits(existing.init, state2.init),
1057
+ events: { ...existing.events, ...state2.events },
1058
+ actions: { ...existing.actions, ...state2.actions },
1059
+ patch: mergedPatch,
1060
+ on: { ...existing.on, ...state2.on },
1061
+ given: { ...existing.given, ...state2.given },
1062
+ snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
1063
+ throw new Error(
1064
+ `Duplicate snap strategy for state "${state2.name}"`
1065
+ );
1066
+ })() : state2.snap || existing.snap
1067
+ };
1068
+ states.set(state2.name, merged);
1069
+ for (const name of Object.keys(merged.actions)) {
1070
+ actions[name] = merged;
1071
+ }
1072
+ for (const name of Object.keys(state2.events)) {
1073
+ if (events[name]) continue;
1074
+ events[name] = {
1075
+ schema: state2.events[name],
1076
+ reactions: /* @__PURE__ */ new Map()
1077
+ };
1078
+ }
1079
+ } else {
1080
+ states.set(state2.name, state2);
1081
+ for (const name of Object.keys(state2.actions)) {
1082
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
1083
+ actions[name] = state2;
1084
+ }
1085
+ for (const name of Object.keys(state2.events)) {
1086
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
1087
+ events[name] = {
1088
+ schema: state2.events[name],
1089
+ reactions: /* @__PURE__ */ new Map()
1090
+ };
1091
+ }
1092
+ }
1093
+ }
1094
+ function mergeProjection(proj, events) {
1095
+ for (const eventName of Object.keys(proj.events)) {
1096
+ const projRegister = proj.events[eventName];
1097
+ const existing = events[eventName];
1098
+ if (!existing) {
1099
+ events[eventName] = {
1100
+ schema: projRegister.schema,
1101
+ reactions: new Map(projRegister.reactions)
1102
+ };
1103
+ } else {
1104
+ for (const [name, reaction] of projRegister.reactions) {
1105
+ let key = name;
1106
+ while (existing.reactions.has(key)) key = `${key}_p`;
1107
+ existing.reactions.set(key, reaction);
1108
+ }
1109
+ }
1110
+ }
1111
+ }
1112
+ var _this_ = ({ stream }) => ({
1113
+ source: stream,
1114
+ target: stream
1115
+ });
1116
+
1117
+ // src/internal/tracing.ts
1044
1118
  var logger3 = log();
1045
- var tracer = build_tracer(config().logLevel);
1119
+ var withSnapTrace = (inner) => async (snapshot) => {
1120
+ logger3.trace(
1121
+ `\u{1F7E0} snap ${snapshot.event.stream}@${snapshot.event.version}`
1122
+ );
1123
+ return inner(snapshot);
1124
+ };
1125
+ var withLoadTrace = (inner) => async (me, stream, callback, asOf) => {
1126
+ logger3.trace(`\u{1F7E2} load ${stream}${asOf ? " (as-of)" : ""}`);
1127
+ return inner(me, stream, callback, asOf);
1128
+ };
1129
+ var withActionTrace = (inner) => async (me, action2, target, payload, reactingTo, skipValidation) => {
1130
+ logger3.trace(payload, `\u{1F535} ${target.stream}.${action2}`);
1131
+ const snapshots = await inner(
1132
+ me,
1133
+ action2,
1134
+ target,
1135
+ payload,
1136
+ reactingTo,
1137
+ skipValidation
1138
+ );
1139
+ const committed = snapshots.filter((s) => s.event);
1140
+ if (committed.length) {
1141
+ logger3.trace(
1142
+ committed.map((s) => s.event.data),
1143
+ `\u{1F534} commit ${target.stream}.${committed.map((s) => s.event.name).join(", ")}`
1144
+ );
1145
+ }
1146
+ return snapshots;
1147
+ };
1148
+ function buildEs(level) {
1149
+ if (level !== "trace") {
1150
+ return { snap, load, action };
1151
+ }
1152
+ return {
1153
+ snap: withSnapTrace(snap),
1154
+ load: withLoadTrace(load),
1155
+ action: withActionTrace(action)
1156
+ };
1157
+ }
1158
+ var withClaimTrace = (inner) => async (lagging, leading, by, millis) => {
1159
+ const leased = await inner(lagging, leading, by, millis);
1160
+ if (leased.length) {
1161
+ const data = Object.fromEntries(
1162
+ leased.map(({ stream, at, retry }) => [stream, { at, retry }])
1163
+ );
1164
+ logger3.trace(data, ">> lease");
1165
+ }
1166
+ return leased;
1167
+ };
1168
+ var withFetchTrace = (inner) => async (leased, eventLimit) => {
1169
+ const fetched = await inner(leased, eventLimit);
1170
+ const data = Object.fromEntries(
1171
+ fetched.map(({ stream, source, events }) => {
1172
+ const key = source ? `${stream}<-${source}` : stream;
1173
+ const value = Object.fromEntries(
1174
+ events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
1175
+ );
1176
+ return [key, value];
1177
+ })
1178
+ );
1179
+ logger3.trace(data, ">> fetch");
1180
+ return fetched;
1181
+ };
1182
+ var withAckTrace = (inner) => async (leases) => {
1183
+ const acked = await inner(leases);
1184
+ if (acked.length) {
1185
+ const data = Object.fromEntries(
1186
+ acked.map(({ stream, at, retry }) => [stream, { at, retry }])
1187
+ );
1188
+ logger3.trace(data, ">> ack");
1189
+ }
1190
+ return acked;
1191
+ };
1192
+ var withBlockTrace = (inner) => async (leases) => {
1193
+ const blocked = await inner(leases);
1194
+ if (blocked.length) {
1195
+ const data = Object.fromEntries(
1196
+ blocked.map(({ stream, at, retry, error }) => [
1197
+ stream,
1198
+ { at, retry, error }
1199
+ ])
1200
+ );
1201
+ logger3.trace(data, ">> block");
1202
+ }
1203
+ return blocked;
1204
+ };
1205
+ var withSubscribeTrace = (inner) => async (streams) => {
1206
+ const result = await inner(streams);
1207
+ if (result.subscribed) {
1208
+ const data = streams.map(({ stream }) => stream).join(" ");
1209
+ logger3.trace(`>> correlate ${data}`);
1210
+ }
1211
+ return result;
1212
+ };
1213
+ function buildDrain(level) {
1214
+ if (level !== "trace") {
1215
+ return {
1216
+ claim,
1217
+ fetch,
1218
+ ack,
1219
+ block,
1220
+ subscribe
1221
+ };
1222
+ }
1223
+ return {
1224
+ claim: withClaimTrace(claim),
1225
+ fetch: withFetchTrace(fetch),
1226
+ ack: withAckTrace(ack),
1227
+ block: withBlockTrace(block),
1228
+ subscribe: withSubscribeTrace(subscribe)
1229
+ };
1230
+ }
1231
+
1232
+ // src/act.ts
1233
+ var logger4 = log();
1046
1234
  var Act = class {
1047
1235
  constructor(registry, _states = /* @__PURE__ */ new Map(), batchHandlers = /* @__PURE__ */ new Map()) {
1048
1236
  this.registry = registry;
1049
1237
  this._states = _states;
1050
1238
  this._batch_handlers = batchHandlers;
1239
+ const level = log().level;
1240
+ this._es = buildEs(level);
1241
+ this._cd = buildDrain(level);
1051
1242
  const statics = [];
1052
1243
  for (const [name, register] of Object.entries(this.registry.events)) {
1053
1244
  if (register.reactions.size > 0) {
@@ -1107,6 +1298,10 @@ var Act = class {
1107
1298
  _static_targets;
1108
1299
  /** Batch handlers for static-target projections (target → handler) */
1109
1300
  _batch_handlers;
1301
+ /** Event-sourcing handlers, optionally wrapped with trace decorators */
1302
+ _es;
1303
+ /** Correlate/drain pipeline ops, optionally wrapped with trace decorators */
1304
+ _cd;
1110
1305
  /**
1111
1306
  * Executes an action on a state instance, committing resulting events.
1112
1307
  *
@@ -1189,7 +1384,7 @@ var Act = class {
1189
1384
  * @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
1190
1385
  */
1191
1386
  async do(action2, target, payload, reactingTo, skipValidation = false) {
1192
- const snapshots = await action(
1387
+ const snapshots = await this._es.action(
1193
1388
  this.registry.actions[action2],
1194
1389
  action2,
1195
1390
  target,
@@ -1215,7 +1410,7 @@ var Act = class {
1215
1410
  } else {
1216
1411
  merged = this._states.get(stateOrName.name) || stateOrName;
1217
1412
  }
1218
- return await load(merged, stream, callback, asOf);
1413
+ return await this._es.load(merged, stream, callback, asOf);
1219
1414
  }
1220
1415
  /**
1221
1416
  * Queries the event store for events matching a filter.
@@ -1328,7 +1523,7 @@ var Act = class {
1328
1523
  if (payloads.length === 0) return { lease, handled: 0, at: lease.at };
1329
1524
  const stream = lease.stream;
1330
1525
  let at = payloads.at(0).event.id, handled = 0;
1331
- lease.retry > 0 && logger3.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1526
+ lease.retry > 0 && logger4.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1332
1527
  const doAction = this.do.bind(this);
1333
1528
  const scopedApp = {
1334
1529
  do: doAction,
@@ -1350,16 +1545,16 @@ var Act = class {
1350
1545
  at = event.id;
1351
1546
  handled++;
1352
1547
  } catch (error) {
1353
- logger3.error(error);
1354
- const block = lease.retry >= options.maxRetries && options.blockOnError;
1355
- block && logger3.error(`Blocking ${stream} after ${lease.retry} retries.`);
1548
+ logger4.error(error);
1549
+ const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1550
+ block2 && logger4.error(`Blocking ${stream} after ${lease.retry} retries.`);
1356
1551
  return {
1357
1552
  lease,
1358
1553
  handled,
1359
1554
  at,
1360
1555
  // only report error when nothing was handled
1361
1556
  error: handled === 0 ? error.message : void 0,
1362
- block
1557
+ block: block2
1363
1558
  };
1364
1559
  }
1365
1560
  }
@@ -1382,21 +1577,21 @@ var Act = class {
1382
1577
  const stream = lease.stream;
1383
1578
  const events = payloads.map((p) => p.event);
1384
1579
  const at = events.at(-1).id;
1385
- lease.retry > 0 && logger3.warn(`Retrying batch ${stream}@${events[0].id} (${lease.retry}).`);
1580
+ lease.retry > 0 && logger4.warn(`Retrying batch ${stream}@${events[0].id} (${lease.retry}).`);
1386
1581
  try {
1387
1582
  await batchHandler(events, stream);
1388
1583
  return { lease, handled: events.length, at };
1389
1584
  } catch (error) {
1390
- logger3.error(error);
1585
+ logger4.error(error);
1391
1586
  const { options } = payloads[0];
1392
- const block = lease.retry >= options.maxRetries && options.blockOnError;
1393
- block && logger3.error(`Blocking ${stream} after ${lease.retry} retries.`);
1587
+ const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1588
+ block2 && logger4.error(`Blocking ${stream} after ${lease.retry} retries.`);
1394
1589
  return {
1395
1590
  lease,
1396
1591
  handled: 0,
1397
1592
  at: lease.at,
1398
1593
  error: error.message,
1399
- block
1594
+ block: block2
1400
1595
  };
1401
1596
  }
1402
1597
  }
@@ -1452,7 +1647,7 @@ var Act = class {
1452
1647
  this._drain_locked = true;
1453
1648
  const lagging = Math.ceil(streamLimit * this._drain_lag2lead_ratio);
1454
1649
  const leading = streamLimit - lagging;
1455
- const leased = await store().claim(
1650
+ const leased = await this._cd.claim(
1456
1651
  lagging,
1457
1652
  leading,
1458
1653
  (0, import_crypto2.randomUUID)(),
@@ -1462,17 +1657,7 @@ var Act = class {
1462
1657
  this._needs_drain = false;
1463
1658
  return { fetched: [], leased: [], acked: [], blocked: [] };
1464
1659
  }
1465
- const fetched = await Promise.all(
1466
- leased.map(async ({ stream, source, at, lagging: lagging2 }) => {
1467
- const events = await this.query_array({
1468
- stream: source,
1469
- after: at,
1470
- limit: eventLimit
1471
- });
1472
- return { stream, source, at, lagging: lagging2, events };
1473
- })
1474
- );
1475
- tracer.fetched(fetched);
1660
+ const fetched = await this._cd.fetch(leased, eventLimit);
1476
1661
  const payloadsMap = /* @__PURE__ */ new Map();
1477
1662
  const fetch_window_at = fetched.reduce(
1478
1663
  (max, { at, events }) => Math.max(max, events.at(-1)?.id || at),
@@ -1489,7 +1674,6 @@ var Act = class {
1489
1674
  });
1490
1675
  payloadsMap.set(stream, payloads);
1491
1676
  });
1492
- tracer.leased(leased);
1493
1677
  const handled = await Promise.all(
1494
1678
  leased.map((lease) => {
1495
1679
  const streamFetch = fetched.find((f) => f.stream === lease.stream);
@@ -1513,27 +1697,21 @@ var Act = class {
1513
1697
  const leading_avg = leading > 0 ? leading_handled / leading : 0;
1514
1698
  const total = lagging_avg + leading_avg;
1515
1699
  this._drain_lag2lead_ratio = total > 0 ? Math.max(0.2, Math.min(0.8, lagging_avg / total)) : 0.5;
1516
- const acked = await store().ack(
1700
+ const acked = await this._cd.ack(
1517
1701
  handled.filter(({ error }) => !error).map(({ at, lease }) => ({ ...lease, at }))
1518
1702
  );
1519
- if (acked.length) {
1520
- tracer.acked(acked);
1521
- this.emit("acked", acked);
1522
- }
1523
- const blocked = await store().block(
1524
- handled.filter(({ block }) => block).map(({ lease, error }) => ({ ...lease, error }))
1703
+ if (acked.length) this.emit("acked", acked);
1704
+ const blocked = await this._cd.block(
1705
+ handled.filter(({ block: block2 }) => block2).map(({ lease, error }) => ({ ...lease, error }))
1525
1706
  );
1526
- if (blocked.length) {
1527
- tracer.blocked(blocked);
1528
- this.emit("blocked", blocked);
1529
- }
1707
+ if (blocked.length) this.emit("blocked", blocked);
1530
1708
  const result = { fetched, leased, acked, blocked };
1531
1709
  const hasErrors = handled.some(({ error }) => error);
1532
1710
  if (!acked.length && !blocked.length && !hasErrors)
1533
1711
  this._needs_drain = false;
1534
1712
  return result;
1535
1713
  } catch (error) {
1536
- logger3.error(error);
1714
+ logger4.error(error);
1537
1715
  } finally {
1538
1716
  this._drain_locked = false;
1539
1717
  }
@@ -1639,10 +1817,9 @@ var Act = class {
1639
1817
  stream,
1640
1818
  source
1641
1819
  }));
1642
- const { subscribed } = await store().subscribe(streams);
1820
+ const { subscribed } = await this._cd.subscribe(streams);
1643
1821
  this._correlation_checkpoint = last_id;
1644
1822
  if (subscribed) {
1645
- tracer.correlated(streams);
1646
1823
  for (const { stream } of streams) {
1647
1824
  this._subscribed_statics.add(stream);
1648
1825
  }
@@ -1907,7 +2084,7 @@ var Act = class {
1907
2084
  if (mergedState) {
1908
2085
  await Promise.all(
1909
2086
  guarded.filter((s) => targetMap.get(s)?.restart).map(async (stream) => {
1910
- const snap2 = await load(mergedState, stream);
2087
+ const snap2 = await this._es.load(mergedState, stream);
1911
2088
  seedStates.set(stream, snap2.state);
1912
2089
  })
1913
2090
  );
@@ -2014,139 +2191,13 @@ var Act = class {
2014
2191
  if (!made_progress) break;
2015
2192
  }
2016
2193
  if (lastDrain) this.emit("settled", lastDrain);
2017
- })().catch((err) => logger3.error(err)).finally(() => {
2194
+ })().catch((err) => logger4.error(err)).finally(() => {
2018
2195
  this._settling = false;
2019
2196
  });
2020
2197
  }, debounceMs);
2021
2198
  }
2022
2199
  };
2023
2200
 
2024
- // src/merge.ts
2025
- var import_zod4 = require("zod");
2026
- function baseTypeName(zodType) {
2027
- let t = zodType;
2028
- while (typeof t.unwrap === "function") {
2029
- t = t.unwrap();
2030
- }
2031
- return t.constructor.name;
2032
- }
2033
- function mergeSchemas(existing, incoming, stateName) {
2034
- if (existing instanceof import_zod4.ZodObject && incoming instanceof import_zod4.ZodObject) {
2035
- const existingShape = existing.shape;
2036
- const incomingShape = incoming.shape;
2037
- for (const key of Object.keys(incomingShape)) {
2038
- if (key in existingShape) {
2039
- const existingBase = baseTypeName(existingShape[key]);
2040
- const incomingBase = baseTypeName(incomingShape[key]);
2041
- if (existingBase !== incomingBase) {
2042
- throw new Error(
2043
- `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
2044
- );
2045
- }
2046
- }
2047
- }
2048
- return existing.extend(incomingShape);
2049
- }
2050
- return existing;
2051
- }
2052
- function mergeInits(existing, incoming) {
2053
- return () => ({ ...existing(), ...incoming() });
2054
- }
2055
- function registerState(state2, states, actions, events) {
2056
- if (states.has(state2.name)) {
2057
- const existing = states.get(state2.name);
2058
- for (const name of Object.keys(state2.actions)) {
2059
- if (existing.actions[name] === state2.actions[name]) continue;
2060
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
2061
- }
2062
- for (const name of Object.keys(state2.events)) {
2063
- if (existing.events[name] === state2.events[name]) continue;
2064
- if (existing.events[name]) continue;
2065
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
2066
- }
2067
- const mergedPatch = { ...existing.patch };
2068
- for (const name of Object.keys(state2.patch)) {
2069
- const existingP = existing.patch[name];
2070
- const incomingP = state2.patch[name];
2071
- if (!existingP) {
2072
- mergedPatch[name] = incomingP;
2073
- } else {
2074
- const existingIsDefault = existingP._passthrough;
2075
- const incomingIsDefault = incomingP._passthrough;
2076
- if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
2077
- throw new Error(
2078
- `Duplicate custom patch for event "${name}" in state "${state2.name}"`
2079
- );
2080
- }
2081
- if (existingIsDefault && !incomingIsDefault) {
2082
- mergedPatch[name] = incomingP;
2083
- }
2084
- }
2085
- }
2086
- const merged = {
2087
- ...existing,
2088
- state: mergeSchemas(existing.state, state2.state, state2.name),
2089
- init: mergeInits(existing.init, state2.init),
2090
- events: { ...existing.events, ...state2.events },
2091
- actions: { ...existing.actions, ...state2.actions },
2092
- patch: mergedPatch,
2093
- on: { ...existing.on, ...state2.on },
2094
- given: { ...existing.given, ...state2.given },
2095
- snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
2096
- throw new Error(
2097
- `Duplicate snap strategy for state "${state2.name}"`
2098
- );
2099
- })() : state2.snap || existing.snap
2100
- };
2101
- states.set(state2.name, merged);
2102
- for (const name of Object.keys(merged.actions)) {
2103
- actions[name] = merged;
2104
- }
2105
- for (const name of Object.keys(state2.events)) {
2106
- if (events[name]) continue;
2107
- events[name] = {
2108
- schema: state2.events[name],
2109
- reactions: /* @__PURE__ */ new Map()
2110
- };
2111
- }
2112
- } else {
2113
- states.set(state2.name, state2);
2114
- for (const name of Object.keys(state2.actions)) {
2115
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
2116
- actions[name] = state2;
2117
- }
2118
- for (const name of Object.keys(state2.events)) {
2119
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
2120
- events[name] = {
2121
- schema: state2.events[name],
2122
- reactions: /* @__PURE__ */ new Map()
2123
- };
2124
- }
2125
- }
2126
- }
2127
- function mergeProjection(proj, events) {
2128
- for (const eventName of Object.keys(proj.events)) {
2129
- const projRegister = proj.events[eventName];
2130
- const existing = events[eventName];
2131
- if (!existing) {
2132
- events[eventName] = {
2133
- schema: projRegister.schema,
2134
- reactions: new Map(projRegister.reactions)
2135
- };
2136
- } else {
2137
- for (const [name, reaction] of projRegister.reactions) {
2138
- let key = name;
2139
- while (existing.reactions.has(key)) key = `${key}_p`;
2140
- existing.reactions.set(key, reaction);
2141
- }
2142
- }
2143
- }
2144
- }
2145
- var _this_ = ({ stream }) => ({
2146
- source: stream,
2147
- target: stream
2148
- });
2149
-
2150
2201
  // src/act-builder.ts
2151
2202
  function act(states = /* @__PURE__ */ new Map(), registry = {
2152
2203
  actions: {},
@@ -2490,7 +2541,6 @@ function action_builder(state2) {
2490
2541
  ValidationError,
2491
2542
  ZodEmpty,
2492
2543
  act,
2493
- build_tracer,
2494
2544
  cache,
2495
2545
  config,
2496
2546
  dispose,