@rotorsoft/act 0.32.0 → 0.32.2

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 (35) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/@types/act-builder.d.ts.map +1 -1
  3. package/dist/@types/act.d.ts +10 -10
  4. package/dist/@types/act.d.ts.map +1 -1
  5. package/dist/@types/adapters/InMemoryStore.d.ts +2 -4
  6. package/dist/@types/adapters/InMemoryStore.d.ts.map +1 -1
  7. package/dist/@types/internal/drain.d.ts +39 -0
  8. package/dist/@types/internal/drain.d.ts.map +1 -0
  9. package/dist/@types/{event-sourcing.d.ts → internal/event-sourcing.d.ts} +15 -2
  10. package/dist/@types/internal/event-sourcing.d.ts.map +1 -0
  11. package/dist/@types/internal/index.d.ts +22 -0
  12. package/dist/@types/internal/index.d.ts.map +1 -0
  13. package/dist/@types/internal/merge.d.ts +19 -0
  14. package/dist/@types/internal/merge.d.ts.map +1 -0
  15. package/dist/@types/internal/tracing.d.ts +36 -0
  16. package/dist/@types/internal/tracing.d.ts.map +1 -0
  17. package/dist/@types/ports.d.ts +1 -25
  18. package/dist/@types/ports.d.ts.map +1 -1
  19. package/dist/@types/projection-builder.d.ts.map +1 -1
  20. package/dist/@types/slice-builder.d.ts.map +1 -1
  21. package/dist/@types/state-builder.d.ts.map +1 -1
  22. package/dist/@types/types/action.d.ts +13 -0
  23. package/dist/@types/types/action.d.ts.map +1 -1
  24. package/dist/@types/types/ports.d.ts +2 -6
  25. package/dist/@types/types/ports.d.ts.map +1 -1
  26. package/dist/@types/types/reaction.d.ts +9 -3
  27. package/dist/@types/types/reaction.d.ts.map +1 -1
  28. package/dist/index.cjs +291 -252
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.js +291 -251
  31. package/dist/index.js.map +1 -1
  32. package/package.json +1 -1
  33. package/dist/@types/event-sourcing.d.ts.map +0 -1
  34. package/dist/@types/merge.d.ts +0 -43
  35. package/dist/@types/merge.d.ts.map +0 -1
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,162 @@ 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/merge.ts
842
+ var import_zod4 = require("zod");
843
+ function baseTypeName(zodType) {
844
+ let t = zodType;
845
+ while (typeof t.unwrap === "function") {
846
+ t = t.unwrap();
847
+ }
848
+ return t.constructor.name;
849
+ }
850
+ function mergeSchemas(existing, incoming, stateName) {
851
+ if (existing instanceof import_zod4.ZodObject && incoming instanceof import_zod4.ZodObject) {
852
+ const existingShape = existing.shape;
853
+ const incomingShape = incoming.shape;
854
+ for (const key of Object.keys(incomingShape)) {
855
+ if (key in existingShape) {
856
+ const existingBase = baseTypeName(existingShape[key]);
857
+ const incomingBase = baseTypeName(incomingShape[key]);
858
+ if (existingBase !== incomingBase) {
859
+ throw new Error(
860
+ `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
861
+ );
862
+ }
863
+ }
864
+ }
865
+ return existing.extend(incomingShape);
866
+ }
867
+ return existing;
868
+ }
869
+ function mergeInits(existing, incoming) {
870
+ return () => ({ ...existing(), ...incoming() });
871
+ }
872
+ function registerState(state2, states, actions, events) {
873
+ const existing = states.get(state2.name);
874
+ if (existing) {
875
+ mergeIntoExisting(state2, existing, states, actions, events);
876
+ } else {
877
+ registerNewState(state2, states, actions, events);
878
+ }
879
+ }
880
+ function registerNewState(state2, states, actions, events) {
881
+ states.set(state2.name, state2);
882
+ for (const name of Object.keys(state2.actions)) {
883
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
884
+ actions[name] = state2;
885
+ }
886
+ for (const name of Object.keys(state2.events)) {
887
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
888
+ events[name] = { schema: state2.events[name], reactions: /* @__PURE__ */ new Map() };
889
+ }
890
+ }
891
+ function mergeIntoExisting(state2, existing, states, actions, events) {
892
+ for (const name of Object.keys(state2.actions)) {
893
+ if (existing.actions[name] === state2.actions[name]) continue;
894
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
895
+ }
896
+ for (const name of Object.keys(state2.events)) {
897
+ if (existing.events[name] === state2.events[name]) continue;
898
+ if (existing.events[name]) continue;
899
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
900
+ }
901
+ const mergedPatch = mergePatches(existing.patch, state2.patch, state2.name);
902
+ const merged = {
903
+ ...existing,
904
+ state: mergeSchemas(existing.state, state2.state, state2.name),
905
+ init: mergeInits(existing.init, state2.init),
906
+ events: { ...existing.events, ...state2.events },
907
+ actions: { ...existing.actions, ...state2.actions },
908
+ patch: mergedPatch,
909
+ on: { ...existing.on, ...state2.on },
910
+ given: { ...existing.given, ...state2.given },
911
+ snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
912
+ throw new Error(
913
+ `Duplicate snap strategy for state "${state2.name}"`
914
+ );
915
+ })() : state2.snap || existing.snap
916
+ };
917
+ states.set(state2.name, merged);
918
+ for (const name of Object.keys(merged.actions)) {
919
+ actions[name] = merged;
920
+ }
921
+ for (const name of Object.keys(state2.events)) {
922
+ if (events[name]) continue;
923
+ events[name] = { schema: state2.events[name], reactions: /* @__PURE__ */ new Map() };
924
+ }
925
+ }
926
+ function mergePatches(existing, incoming, stateName) {
927
+ const merged = { ...existing };
928
+ for (const name of Object.keys(incoming)) {
929
+ const existingP = existing[name];
930
+ const incomingP = incoming[name];
931
+ if (!existingP) {
932
+ merged[name] = incomingP;
933
+ continue;
934
+ }
935
+ const existingIsDefault = existingP._passthrough;
936
+ const incomingIsDefault = incomingP._passthrough;
937
+ if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
938
+ throw new Error(
939
+ `Duplicate custom patch for event "${name}" in state "${stateName}"`
940
+ );
941
+ }
942
+ if (existingIsDefault && !incomingIsDefault) {
943
+ merged[name] = incomingP;
944
+ }
945
+ }
946
+ return merged;
947
+ }
948
+ function mergeProjection(proj, events) {
949
+ for (const eventName of Object.keys(proj.events)) {
950
+ const projRegister = proj.events[eventName];
951
+ const existing = events[eventName];
952
+ if (!existing) {
953
+ events[eventName] = {
954
+ schema: projRegister.schema,
955
+ reactions: new Map(projRegister.reactions)
956
+ };
957
+ } else {
958
+ for (const [name, reaction] of projRegister.reactions) {
959
+ let key = name;
960
+ while (existing.reactions.has(key)) key = `${key}_p`;
961
+ existing.reactions.set(key, reaction);
962
+ }
963
+ }
964
+ }
965
+ }
966
+ var _this_ = ({ stream }) => ({
967
+ source: stream,
968
+ target: stream
969
+ });
970
+
971
+ // src/internal/drain.ts
972
+ var claim = (lagging, leading, by, millis) => store().claim(lagging, leading, by, millis);
973
+ async function fetch(leased, eventLimit) {
974
+ return Promise.all(
975
+ leased.map(async ({ stream, source, at, lagging }) => {
976
+ const events = [];
977
+ await store().query((e) => events.push(e), {
978
+ stream: source,
979
+ after: at,
980
+ limit: eventLimit
981
+ });
982
+ return { stream, source, at, lagging, events };
983
+ })
984
+ );
985
+ }
986
+ var ack = (leases) => store().ack(leases);
987
+ var block = (leases) => store().block(leases);
988
+ var subscribe = (streams) => store().subscribe(streams);
989
+
990
+ // src/internal/event-sourcing.ts
900
991
  var import_act_patch = require("@rotorsoft/act-patch");
901
992
  var import_crypto = require("crypto");
902
- var logger2 = log();
903
993
  async function snap(snapshot) {
904
994
  try {
905
995
  const { id, stream, name, meta, version } = snapshot.event;
906
- const snapped = await store().commit(
996
+ await store().commit(
907
997
  stream,
908
998
  [{ name: SNAP_EVENT, data: snapshot.state }],
909
999
  {
@@ -913,19 +1003,18 @@ async function snap(snapshot) {
913
1003
  version
914
1004
  // IMPORTANT! - state events are committed right after the snapshot event
915
1005
  );
916
- logger2.trace(snapped, "\u{1F7E0} snap");
917
1006
  } catch (error) {
918
- logger2.error(error);
1007
+ log().error(error);
919
1008
  }
920
1009
  }
921
1010
  async function load(me, stream, callback, asOf) {
922
- const timeTravel = asOf && (asOf.before !== void 0 || asOf.created_before !== void 0 || asOf.created_after !== void 0 || asOf.limit !== void 0);
1011
+ const timeTravel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
923
1012
  const cached = timeTravel ? void 0 : await cache().get(stream);
924
1013
  let state2 = cached?.state ?? (me.init ? me.init() : {});
925
1014
  let patches = cached?.patches ?? 0;
926
1015
  let snaps = cached?.snaps ?? 0;
927
1016
  let event;
928
- const count = await store().query(
1017
+ await store().query(
929
1018
  (e) => {
930
1019
  event = e;
931
1020
  if (e.name === SNAP_EVENT) {
@@ -944,10 +1033,6 @@ async function load(me, stream, callback, asOf) {
944
1033
  ...cached ? { after: cached.event_id } : { with_snaps: true, ...asOf }
945
1034
  }
946
1035
  );
947
- logger2.trace(
948
- state2,
949
- `\u{1F7E2} load ${stream}${cached && count === 0 ? " (cached)" : ""}${timeTravel ? " (as-of)" : ""}`
950
- );
951
1036
  return { event, state: state2, patches, snaps };
952
1037
  }
953
1038
  async function action(me, action2, target, payload, reactingTo, skipValidation = false) {
@@ -958,10 +1043,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
958
1043
  if (snapshot.event?.name === TOMBSTONE_EVENT)
959
1044
  throw new StreamClosedError(stream);
960
1045
  const expected = expectedVersion ?? snapshot.event?.version;
961
- logger2.trace(
962
- payload,
963
- `\u{1F535} ${stream}.${action2}${typeof expected === "number" ? `.${expected}` : ""}`
964
- );
965
1046
  if (me.given) {
966
1047
  const invariants = me.given[action2] || [];
967
1048
  invariants.forEach(({ valid, description }) => {
@@ -1001,10 +1082,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1001
1082
  } : void 0
1002
1083
  }
1003
1084
  };
1004
- logger2.trace(
1005
- emitted.map((e) => e.data),
1006
- `\u{1F534} commit ${stream}.${emitted.map((e) => e.name).join(", ")}`
1007
- );
1008
1085
  let committed;
1009
1086
  try {
1010
1087
  committed = await store().commit(
@@ -1015,7 +1092,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1015
1092
  reactingTo ? void 0 : expected
1016
1093
  );
1017
1094
  } catch (error) {
1018
- if (error.name === "ERR_CONCURRENCY") {
1095
+ if (error instanceof ConcurrencyError) {
1019
1096
  await cache().invalidate(stream);
1020
1097
  }
1021
1098
  throw error;
@@ -1029,25 +1106,121 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1029
1106
  });
1030
1107
  const last = snapshots.at(-1);
1031
1108
  const snapped = me.snap && me.snap(last);
1032
- void cache().set(stream, {
1109
+ cache().set(stream, {
1033
1110
  state: last.state,
1034
1111
  version: last.event.version,
1035
1112
  event_id: last.event.id,
1036
1113
  patches: snapped ? 0 : last.patches,
1037
1114
  snaps: snapped ? last.snaps + 1 : last.snaps
1038
- });
1115
+ }).catch((err) => log().error(err));
1039
1116
  if (snapped) void snap(last);
1040
1117
  return snapshots;
1041
1118
  }
1042
1119
 
1120
+ // src/internal/tracing.ts
1121
+ var traced = (inner, exit, entry) => (async (...args) => {
1122
+ entry?.(...args);
1123
+ const result = await inner(...args);
1124
+ exit?.(result, ...args);
1125
+ return result;
1126
+ });
1127
+ function buildEs(logger2) {
1128
+ if (logger2.level !== "trace") {
1129
+ return { snap, load, action };
1130
+ }
1131
+ return {
1132
+ snap: traced(snap, void 0, (snapshot) => {
1133
+ logger2.trace(
1134
+ `\u{1F7E0} snap ${snapshot.event.stream}@${snapshot.event.version}`
1135
+ );
1136
+ }),
1137
+ load: traced(load, void 0, (_me, stream, _cb, asOf) => {
1138
+ logger2.trace(`\u{1F7E2} load ${stream}${asOf ? " (as-of)" : ""}`);
1139
+ }),
1140
+ action: traced(
1141
+ action,
1142
+ (snapshots, _me, _action, target) => {
1143
+ const committed = snapshots.filter((s) => s.event);
1144
+ if (committed.length) {
1145
+ logger2.trace(
1146
+ committed.map((s) => s.event.data),
1147
+ `\u{1F534} commit ${target.stream}.${committed.map((s) => s.event.name).join(", ")}`
1148
+ );
1149
+ }
1150
+ },
1151
+ (_me, action2, target, payload) => {
1152
+ logger2.trace(payload, `\u{1F535} ${target.stream}.${action2}`);
1153
+ }
1154
+ )
1155
+ };
1156
+ }
1157
+ function buildDrain(logger2) {
1158
+ if (logger2.level !== "trace") {
1159
+ return {
1160
+ claim,
1161
+ fetch,
1162
+ ack,
1163
+ block,
1164
+ subscribe
1165
+ };
1166
+ }
1167
+ return {
1168
+ claim: traced(claim, (leased) => {
1169
+ if (leased.length) {
1170
+ const data = Object.fromEntries(
1171
+ leased.map(({ stream, at, retry }) => [stream, { at, retry }])
1172
+ );
1173
+ logger2.trace(data, ">> lease");
1174
+ }
1175
+ }),
1176
+ fetch: traced(fetch, (fetched) => {
1177
+ const data = Object.fromEntries(
1178
+ fetched.map(({ stream, source, events }) => {
1179
+ const key = source ? `${stream}<-${source}` : stream;
1180
+ const value = Object.fromEntries(
1181
+ events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
1182
+ );
1183
+ return [key, value];
1184
+ })
1185
+ );
1186
+ logger2.trace(data, ">> fetch");
1187
+ }),
1188
+ ack: traced(ack, (acked) => {
1189
+ if (acked.length) {
1190
+ const data = Object.fromEntries(
1191
+ acked.map(({ stream, at, retry }) => [stream, { at, retry }])
1192
+ );
1193
+ logger2.trace(data, ">> ack");
1194
+ }
1195
+ }),
1196
+ block: traced(block, (blocked) => {
1197
+ if (blocked.length) {
1198
+ const data = Object.fromEntries(
1199
+ blocked.map(({ stream, at, retry, error }) => [
1200
+ stream,
1201
+ { at, retry, error }
1202
+ ])
1203
+ );
1204
+ logger2.trace(data, ">> block");
1205
+ }
1206
+ }),
1207
+ subscribe: traced(subscribe, (result, streams) => {
1208
+ if (result.subscribed) {
1209
+ const data = streams.map(({ stream }) => stream).join(" ");
1210
+ logger2.trace(`>> correlate ${data}`);
1211
+ }
1212
+ })
1213
+ };
1214
+ }
1215
+
1043
1216
  // src/act.ts
1044
- var logger3 = log();
1045
- var tracer = build_tracer(config().logLevel);
1046
1217
  var Act = class {
1047
1218
  constructor(registry, _states = /* @__PURE__ */ new Map(), batchHandlers = /* @__PURE__ */ new Map()) {
1048
1219
  this.registry = registry;
1049
1220
  this._states = _states;
1050
1221
  this._batch_handlers = batchHandlers;
1222
+ this._es = buildEs(this._logger);
1223
+ this._cd = buildDrain(this._logger);
1051
1224
  const statics = [];
1052
1225
  for (const [name, register] of Object.entries(this.registry.events)) {
1053
1226
  if (register.reactions.size > 0) {
@@ -1107,6 +1280,12 @@ var Act = class {
1107
1280
  _static_targets;
1108
1281
  /** Batch handlers for static-target projections (target → handler) */
1109
1282
  _batch_handlers;
1283
+ /** Event-sourcing handlers, optionally wrapped with trace decorators */
1284
+ _es;
1285
+ /** Correlate/drain pipeline ops, optionally wrapped with trace decorators */
1286
+ _cd;
1287
+ /** Logger resolved at construction time (after user port configuration) */
1288
+ _logger = log();
1110
1289
  /**
1111
1290
  * Executes an action on a state instance, committing resulting events.
1112
1291
  *
@@ -1189,7 +1368,7 @@ var Act = class {
1189
1368
  * @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
1190
1369
  */
1191
1370
  async do(action2, target, payload, reactingTo, skipValidation = false) {
1192
- const snapshots = await action(
1371
+ const snapshots = await this._es.action(
1193
1372
  this.registry.actions[action2],
1194
1373
  action2,
1195
1374
  target,
@@ -1215,7 +1394,7 @@ var Act = class {
1215
1394
  } else {
1216
1395
  merged = this._states.get(stateOrName.name) || stateOrName;
1217
1396
  }
1218
- return await load(merged, stream, callback, asOf);
1397
+ return await this._es.load(merged, stream, callback, asOf);
1219
1398
  }
1220
1399
  /**
1221
1400
  * Queries the event store for events matching a filter.
@@ -1328,7 +1507,7 @@ var Act = class {
1328
1507
  if (payloads.length === 0) return { lease, handled: 0, at: lease.at };
1329
1508
  const stream = lease.stream;
1330
1509
  let at = payloads.at(0).event.id, handled = 0;
1331
- lease.retry > 0 && logger3.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1510
+ lease.retry > 0 && this._logger.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1332
1511
  const doAction = this.do.bind(this);
1333
1512
  const scopedApp = {
1334
1513
  do: doAction,
@@ -1350,16 +1529,18 @@ var Act = class {
1350
1529
  at = event.id;
1351
1530
  handled++;
1352
1531
  } 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.`);
1532
+ this._logger.error(error);
1533
+ const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1534
+ block2 && this._logger.error(
1535
+ `Blocking ${stream} after ${lease.retry} retries.`
1536
+ );
1356
1537
  return {
1357
1538
  lease,
1358
1539
  handled,
1359
1540
  at,
1360
1541
  // only report error when nothing was handled
1361
1542
  error: handled === 0 ? error.message : void 0,
1362
- block
1543
+ block: block2
1363
1544
  };
1364
1545
  }
1365
1546
  }
@@ -1382,21 +1563,23 @@ var Act = class {
1382
1563
  const stream = lease.stream;
1383
1564
  const events = payloads.map((p) => p.event);
1384
1565
  const at = events.at(-1).id;
1385
- lease.retry > 0 && logger3.warn(`Retrying batch ${stream}@${events[0].id} (${lease.retry}).`);
1566
+ lease.retry > 0 && this._logger.warn(
1567
+ `Retrying batch ${stream}@${events[0].id} (${lease.retry}).`
1568
+ );
1386
1569
  try {
1387
1570
  await batchHandler(events, stream);
1388
1571
  return { lease, handled: events.length, at };
1389
1572
  } catch (error) {
1390
- logger3.error(error);
1573
+ this._logger.error(error);
1391
1574
  const { options } = payloads[0];
1392
- const block = lease.retry >= options.maxRetries && options.blockOnError;
1393
- block && logger3.error(`Blocking ${stream} after ${lease.retry} retries.`);
1575
+ const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1576
+ block2 && this._logger.error(`Blocking ${stream} after ${lease.retry} retries.`);
1394
1577
  return {
1395
1578
  lease,
1396
1579
  handled: 0,
1397
1580
  at: lease.at,
1398
1581
  error: error.message,
1399
- block
1582
+ block: block2
1400
1583
  };
1401
1584
  }
1402
1585
  }
@@ -1452,7 +1635,7 @@ var Act = class {
1452
1635
  this._drain_locked = true;
1453
1636
  const lagging = Math.ceil(streamLimit * this._drain_lag2lead_ratio);
1454
1637
  const leading = streamLimit - lagging;
1455
- const leased = await store().claim(
1638
+ const leased = await this._cd.claim(
1456
1639
  lagging,
1457
1640
  leading,
1458
1641
  (0, import_crypto2.randomUUID)(),
@@ -1462,17 +1645,7 @@ var Act = class {
1462
1645
  this._needs_drain = false;
1463
1646
  return { fetched: [], leased: [], acked: [], blocked: [] };
1464
1647
  }
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);
1648
+ const fetched = await this._cd.fetch(leased, eventLimit);
1476
1649
  const payloadsMap = /* @__PURE__ */ new Map();
1477
1650
  const fetch_window_at = fetched.reduce(
1478
1651
  (max, { at, events }) => Math.max(max, events.at(-1)?.id || at),
@@ -1489,7 +1662,6 @@ var Act = class {
1489
1662
  });
1490
1663
  payloadsMap.set(stream, payloads);
1491
1664
  });
1492
- tracer.leased(leased);
1493
1665
  const handled = await Promise.all(
1494
1666
  leased.map((lease) => {
1495
1667
  const streamFetch = fetched.find((f) => f.stream === lease.stream);
@@ -1513,27 +1685,21 @@ var Act = class {
1513
1685
  const leading_avg = leading > 0 ? leading_handled / leading : 0;
1514
1686
  const total = lagging_avg + leading_avg;
1515
1687
  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(
1688
+ const acked = await this._cd.ack(
1517
1689
  handled.filter(({ error }) => !error).map(({ at, lease }) => ({ ...lease, at }))
1518
1690
  );
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 }))
1691
+ if (acked.length) this.emit("acked", acked);
1692
+ const blocked = await this._cd.block(
1693
+ handled.filter(({ block: block2 }) => block2).map(({ lease, error }) => ({ ...lease, error }))
1525
1694
  );
1526
- if (blocked.length) {
1527
- tracer.blocked(blocked);
1528
- this.emit("blocked", blocked);
1529
- }
1695
+ if (blocked.length) this.emit("blocked", blocked);
1530
1696
  const result = { fetched, leased, acked, blocked };
1531
1697
  const hasErrors = handled.some(({ error }) => error);
1532
1698
  if (!acked.length && !blocked.length && !hasErrors)
1533
1699
  this._needs_drain = false;
1534
1700
  return result;
1535
1701
  } catch (error) {
1536
- logger3.error(error);
1702
+ this._logger.error(error);
1537
1703
  } finally {
1538
1704
  this._drain_locked = false;
1539
1705
  }
@@ -1639,10 +1805,9 @@ var Act = class {
1639
1805
  stream,
1640
1806
  source
1641
1807
  }));
1642
- const { subscribed } = await store().subscribe(streams);
1808
+ const { subscribed } = await this._cd.subscribe(streams);
1643
1809
  this._correlation_checkpoint = last_id;
1644
1810
  if (subscribed) {
1645
- tracer.correlated(streams);
1646
1811
  for (const { stream } of streams) {
1647
1812
  this._subscribed_statics.add(stream);
1648
1813
  }
@@ -1907,7 +2072,7 @@ var Act = class {
1907
2072
  if (mergedState) {
1908
2073
  await Promise.all(
1909
2074
  guarded.filter((s) => targetMap.get(s)?.restart).map(async (stream) => {
1910
- const snap2 = await load(mergedState, stream);
2075
+ const snap2 = await this._es.load(mergedState, stream);
1911
2076
  seedStates.set(stream, snap2.state);
1912
2077
  })
1913
2078
  );
@@ -2014,139 +2179,13 @@ var Act = class {
2014
2179
  if (!made_progress) break;
2015
2180
  }
2016
2181
  if (lastDrain) this.emit("settled", lastDrain);
2017
- })().catch((err) => logger3.error(err)).finally(() => {
2182
+ })().catch((err) => this._logger.error(err)).finally(() => {
2018
2183
  this._settling = false;
2019
2184
  });
2020
2185
  }, debounceMs);
2021
2186
  }
2022
2187
  };
2023
2188
 
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
2189
  // src/act-builder.ts
2151
2190
  function act(states = /* @__PURE__ */ new Map(), registry = {
2152
2191
  actions: {},
@@ -2387,8 +2426,9 @@ function state(entry) {
2387
2426
  emits(events) {
2388
2427
  const defaultPatch = Object.fromEntries(
2389
2428
  Object.keys(events).map((k) => {
2390
- const fn = ({ data }) => data;
2391
- fn._passthrough = true;
2429
+ const fn = Object.assign(({ data }) => data, {
2430
+ _passthrough: true
2431
+ });
2392
2432
  return [k, fn];
2393
2433
  })
2394
2434
  );
@@ -2490,7 +2530,6 @@ function action_builder(state2) {
2490
2530
  ValidationError,
2491
2531
  ZodEmpty,
2492
2532
  act,
2493
- build_tracer,
2494
2533
  cache,
2495
2534
  config,
2496
2535
  dispose,