@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.js CHANGED
@@ -1,3 +1,20 @@
1
+ import {
2
+ ActorSchema,
3
+ CausationEventSchema,
4
+ CommittedMetaSchema,
5
+ ConcurrencyError,
6
+ Environments,
7
+ Errors,
8
+ EventMetaSchema,
9
+ InvariantError,
10
+ LogLevels,
11
+ QuerySchema,
12
+ StreamClosedError,
13
+ TargetSchema,
14
+ ValidationError,
15
+ ZodEmpty
16
+ } from "./chunk-JBKZJXQZ.js";
17
+
1
18
  // src/adapters/ConsoleLogger.ts
2
19
  var LEVEL_VALUES = {
3
20
  fatal: 60,
@@ -136,141 +153,29 @@ var InMemoryCache = class {
136
153
  }
137
154
  };
138
155
 
139
- // src/types/errors.ts
140
- var Errors = {
141
- ValidationError: "ERR_VALIDATION",
142
- InvariantError: "ERR_INVARIANT",
143
- ConcurrencyError: "ERR_CONCURRENCY",
144
- StreamClosedError: "ERR_STREAM_CLOSED"
145
- };
146
- var ValidationError = class extends Error {
147
- constructor(target, payload, details) {
148
- super(`Invalid ${target} payload`);
149
- this.target = target;
150
- this.payload = payload;
151
- this.details = details;
152
- this.name = Errors.ValidationError;
153
- }
154
- };
155
- var InvariantError = class extends Error {
156
- constructor(action2, payload, target, snapshot, description) {
157
- super(`${action2} failed invariant: ${description}`);
158
- this.action = action2;
159
- this.payload = payload;
160
- this.target = target;
161
- this.snapshot = snapshot;
162
- this.description = description;
163
- this.name = Errors.InvariantError;
164
- }
165
- };
166
- var StreamClosedError = class extends Error {
167
- constructor(stream) {
168
- super(`Stream "${stream}" is closed (tombstoned)`);
169
- this.stream = stream;
170
- this.name = Errors.StreamClosedError;
171
- }
172
- };
173
- var ConcurrencyError = class extends Error {
174
- constructor(stream, lastVersion, events, expectedVersion) {
175
- super(
176
- `Concurrency error committing "${events.map((e) => `${stream}.${e.name}.${JSON.stringify(e.data)}`).join(
177
- ", "
178
- )}". Expected version ${expectedVersion} but found version ${lastVersion}.`
179
- );
180
- this.stream = stream;
181
- this.lastVersion = lastVersion;
182
- this.events = events;
183
- this.expectedVersion = expectedVersion;
184
- this.name = Errors.ConcurrencyError;
185
- }
186
- };
187
-
188
156
  // src/utils.ts
189
157
  import { prettifyError } from "zod";
190
158
 
191
159
  // src/config.ts
192
160
  import * as fs from "fs";
193
- import { z as z2 } from "zod";
194
-
195
- // src/types/schemas.ts
196
161
  import { z } from "zod";
197
- var ZodEmpty = z.record(z.string(), z.never());
198
- var ActorSchema = z.object({
199
- id: z.string(),
200
- name: z.string()
201
- }).loose().readonly();
202
- var TargetSchema = z.object({
203
- stream: z.string(),
204
- actor: ActorSchema,
205
- expectedVersion: z.number().optional()
206
- }).loose().readonly();
207
- var CausationEventSchema = z.object({
208
- id: z.number(),
209
- name: z.string(),
210
- stream: z.string()
211
- });
212
- var EventMetaSchema = z.object({
213
- correlation: z.string(),
214
- causation: z.object({
215
- action: TargetSchema.and(z.object({ name: z.string() })).optional(),
216
- event: CausationEventSchema.optional()
217
- })
218
- }).readonly();
219
- var CommittedMetaSchema = z.object({
220
- id: z.number(),
221
- stream: z.string(),
222
- version: z.number(),
223
- created: z.date(),
224
- meta: EventMetaSchema
225
- }).readonly();
226
- var QuerySchema = z.object({
227
- stream: z.string().optional(),
228
- names: z.string().array().optional(),
229
- before: z.number().optional(),
230
- after: z.number().optional(),
231
- limit: z.number().optional(),
232
- created_before: z.date().optional(),
233
- created_after: z.date().optional(),
234
- backward: z.boolean().optional(),
235
- correlation: z.string().optional(),
236
- with_snaps: z.boolean().optional(),
237
- stream_exact: z.boolean().optional()
238
- }).readonly();
239
-
240
- // src/types/index.ts
241
- var Environments = [
242
- "development",
243
- "test",
244
- "staging",
245
- "production"
246
- ];
247
- var LogLevels = [
248
- "fatal",
249
- "error",
250
- "warn",
251
- "info",
252
- "debug",
253
- "trace"
254
- ];
255
-
256
- // src/config.ts
257
- var PackageSchema = z2.object({
258
- name: z2.string().min(1),
259
- version: z2.string().min(1),
260
- description: z2.string().min(1).optional(),
261
- author: z2.object({ name: z2.string().min(1), email: z2.string().optional() }).optional().or(z2.string().min(1)).optional(),
262
- license: z2.string().min(1).optional(),
263
- dependencies: z2.record(z2.string(), z2.string()).optional()
162
+ var PackageSchema = z.object({
163
+ name: z.string().min(1),
164
+ version: z.string().min(1),
165
+ description: z.string().min(1).optional(),
166
+ author: z.object({ name: z.string().min(1), email: z.string().optional() }).optional().or(z.string().min(1)).optional(),
167
+ license: z.string().min(1).optional(),
168
+ dependencies: z.record(z.string(), z.string()).optional()
264
169
  });
265
170
  var getPackage = () => {
266
171
  const pkg2 = fs.readFileSync("package.json");
267
172
  return JSON.parse(pkg2.toString());
268
173
  };
269
174
  var BaseSchema = PackageSchema.extend({
270
- env: z2.enum(Environments),
271
- logLevel: z2.enum(LogLevels),
272
- logSingleLine: z2.boolean(),
273
- sleepMs: z2.number().int().min(0).max(5e3)
175
+ env: z.enum(Environments),
176
+ logLevel: z.enum(LogLevels),
177
+ logSingleLine: z.boolean(),
178
+ sleepMs: z.number().int().min(0).max(5e3)
274
179
  });
275
180
  var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
276
181
  var env = NODE_ENV || "development";
@@ -743,63 +648,6 @@ function dispose(disposer) {
743
648
  }
744
649
  var SNAP_EVENT = "__snapshot__";
745
650
  var TOMBSTONE_EVENT = "__tombstone__";
746
- function build_tracer(logLevel2) {
747
- if (logLevel2 === "trace") {
748
- const logger4 = log();
749
- return {
750
- fetched: (fetched) => {
751
- const data = Object.fromEntries(
752
- fetched.map(({ stream, source, events }) => {
753
- const key = source ? `${stream}<-${source}` : stream;
754
- const value = Object.fromEntries(
755
- events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
756
- );
757
- return [key, value];
758
- })
759
- );
760
- logger4.trace(data, ">> fetch");
761
- },
762
- correlated: (streams) => {
763
- const data = streams.map(({ stream }) => stream).join(" ");
764
- logger4.trace(`>> correlate ${data}`);
765
- },
766
- leased: (leases) => {
767
- const data = Object.fromEntries(
768
- leases.map(({ stream, at, retry }) => [stream, { at, retry }])
769
- );
770
- logger4.trace(data, ">> lease");
771
- },
772
- acked: (leases) => {
773
- const data = Object.fromEntries(
774
- leases.map(({ stream, at, retry }) => [stream, { at, retry }])
775
- );
776
- logger4.trace(data, ">> ack");
777
- },
778
- blocked: (leases) => {
779
- const data = Object.fromEntries(
780
- leases.map(({ stream, at, retry, error }) => [
781
- stream,
782
- { at, retry, error }
783
- ])
784
- );
785
- logger4.trace(data, ">> block");
786
- }
787
- };
788
- } else {
789
- return {
790
- fetched: () => {
791
- },
792
- correlated: () => {
793
- },
794
- leased: () => {
795
- },
796
- acked: () => {
797
- },
798
- blocked: () => {
799
- }
800
- };
801
- }
802
- }
803
651
 
804
652
  // src/signals.ts
805
653
  var logger = log();
@@ -824,14 +672,33 @@ process.once("unhandledRejection", async (arg) => {
824
672
  import { randomUUID as randomUUID2 } from "crypto";
825
673
  import EventEmitter from "events";
826
674
 
827
- // 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
828
695
  import { patch } from "@rotorsoft/act-patch";
829
696
  import { randomUUID } from "crypto";
830
697
  var logger2 = log();
831
698
  async function snap(snapshot) {
832
699
  try {
833
700
  const { id, stream, name, meta, version } = snapshot.event;
834
- const snapped = await store().commit(
701
+ await store().commit(
835
702
  stream,
836
703
  [{ name: SNAP_EVENT, data: snapshot.state }],
837
704
  {
@@ -841,7 +708,6 @@ async function snap(snapshot) {
841
708
  version
842
709
  // IMPORTANT! - state events are committed right after the snapshot event
843
710
  );
844
- logger2.trace(snapped, "\u{1F7E0} snap");
845
711
  } catch (error) {
846
712
  logger2.error(error);
847
713
  }
@@ -853,7 +719,7 @@ async function load(me, stream, callback, asOf) {
853
719
  let patches = cached?.patches ?? 0;
854
720
  let snaps = cached?.snaps ?? 0;
855
721
  let event;
856
- const count = await store().query(
722
+ await store().query(
857
723
  (e) => {
858
724
  event = e;
859
725
  if (e.name === SNAP_EVENT) {
@@ -872,10 +738,6 @@ async function load(me, stream, callback, asOf) {
872
738
  ...cached ? { after: cached.event_id } : { with_snaps: true, ...asOf }
873
739
  }
874
740
  );
875
- logger2.trace(
876
- state2,
877
- `\u{1F7E2} load ${stream}${cached && count === 0 ? " (cached)" : ""}${timeTravel ? " (as-of)" : ""}`
878
- );
879
741
  return { event, state: state2, patches, snaps };
880
742
  }
881
743
  async function action(me, action2, target, payload, reactingTo, skipValidation = false) {
@@ -886,10 +748,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
886
748
  if (snapshot.event?.name === TOMBSTONE_EVENT)
887
749
  throw new StreamClosedError(stream);
888
750
  const expected = expectedVersion ?? snapshot.event?.version;
889
- logger2.trace(
890
- payload,
891
- `\u{1F535} ${stream}.${action2}${typeof expected === "number" ? `.${expected}` : ""}`
892
- );
893
751
  if (me.given) {
894
752
  const invariants = me.given[action2] || [];
895
753
  invariants.forEach(({ valid, description }) => {
@@ -929,10 +787,6 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
929
787
  } : void 0
930
788
  }
931
789
  };
932
- logger2.trace(
933
- emitted.map((e) => e.data),
934
- `\u{1F534} commit ${stream}.${emitted.map((e) => e.name).join(", ")}`
935
- );
936
790
  let committed;
937
791
  try {
938
792
  committed = await store().commit(
@@ -968,14 +822,257 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
968
822
  return snapshots;
969
823
  }
970
824
 
971
- // 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
972
952
  var logger3 = log();
973
- 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();
974
1068
  var Act = class {
975
1069
  constructor(registry, _states = /* @__PURE__ */ new Map(), batchHandlers = /* @__PURE__ */ new Map()) {
976
1070
  this.registry = registry;
977
1071
  this._states = _states;
978
1072
  this._batch_handlers = batchHandlers;
1073
+ const level = log().level;
1074
+ this._es = buildEs(level);
1075
+ this._cd = buildDrain(level);
979
1076
  const statics = [];
980
1077
  for (const [name, register] of Object.entries(this.registry.events)) {
981
1078
  if (register.reactions.size > 0) {
@@ -1035,6 +1132,10 @@ var Act = class {
1035
1132
  _static_targets;
1036
1133
  /** Batch handlers for static-target projections (target → handler) */
1037
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;
1038
1139
  /**
1039
1140
  * Executes an action on a state instance, committing resulting events.
1040
1141
  *
@@ -1117,7 +1218,7 @@ var Act = class {
1117
1218
  * @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
1118
1219
  */
1119
1220
  async do(action2, target, payload, reactingTo, skipValidation = false) {
1120
- const snapshots = await action(
1221
+ const snapshots = await this._es.action(
1121
1222
  this.registry.actions[action2],
1122
1223
  action2,
1123
1224
  target,
@@ -1143,7 +1244,7 @@ var Act = class {
1143
1244
  } else {
1144
1245
  merged = this._states.get(stateOrName.name) || stateOrName;
1145
1246
  }
1146
- return await load(merged, stream, callback, asOf);
1247
+ return await this._es.load(merged, stream, callback, asOf);
1147
1248
  }
1148
1249
  /**
1149
1250
  * Queries the event store for events matching a filter.
@@ -1256,7 +1357,7 @@ var Act = class {
1256
1357
  if (payloads.length === 0) return { lease, handled: 0, at: lease.at };
1257
1358
  const stream = lease.stream;
1258
1359
  let at = payloads.at(0).event.id, handled = 0;
1259
- lease.retry > 0 && logger3.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1360
+ lease.retry > 0 && logger4.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1260
1361
  const doAction = this.do.bind(this);
1261
1362
  const scopedApp = {
1262
1363
  do: doAction,
@@ -1278,16 +1379,16 @@ var Act = class {
1278
1379
  at = event.id;
1279
1380
  handled++;
1280
1381
  } catch (error) {
1281
- logger3.error(error);
1282
- const block = lease.retry >= options.maxRetries && options.blockOnError;
1283
- 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.`);
1284
1385
  return {
1285
1386
  lease,
1286
1387
  handled,
1287
1388
  at,
1288
1389
  // only report error when nothing was handled
1289
1390
  error: handled === 0 ? error.message : void 0,
1290
- block
1391
+ block: block2
1291
1392
  };
1292
1393
  }
1293
1394
  }
@@ -1310,21 +1411,21 @@ var Act = class {
1310
1411
  const stream = lease.stream;
1311
1412
  const events = payloads.map((p) => p.event);
1312
1413
  const at = events.at(-1).id;
1313
- 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}).`);
1314
1415
  try {
1315
1416
  await batchHandler(events, stream);
1316
1417
  return { lease, handled: events.length, at };
1317
1418
  } catch (error) {
1318
- logger3.error(error);
1419
+ logger4.error(error);
1319
1420
  const { options } = payloads[0];
1320
- const block = lease.retry >= options.maxRetries && options.blockOnError;
1321
- 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.`);
1322
1423
  return {
1323
1424
  lease,
1324
1425
  handled: 0,
1325
1426
  at: lease.at,
1326
1427
  error: error.message,
1327
- block
1428
+ block: block2
1328
1429
  };
1329
1430
  }
1330
1431
  }
@@ -1380,7 +1481,7 @@ var Act = class {
1380
1481
  this._drain_locked = true;
1381
1482
  const lagging = Math.ceil(streamLimit * this._drain_lag2lead_ratio);
1382
1483
  const leading = streamLimit - lagging;
1383
- const leased = await store().claim(
1484
+ const leased = await this._cd.claim(
1384
1485
  lagging,
1385
1486
  leading,
1386
1487
  randomUUID2(),
@@ -1390,17 +1491,7 @@ var Act = class {
1390
1491
  this._needs_drain = false;
1391
1492
  return { fetched: [], leased: [], acked: [], blocked: [] };
1392
1493
  }
1393
- const fetched = await Promise.all(
1394
- leased.map(async ({ stream, source, at, lagging: lagging2 }) => {
1395
- const events = await this.query_array({
1396
- stream: source,
1397
- after: at,
1398
- limit: eventLimit
1399
- });
1400
- return { stream, source, at, lagging: lagging2, events };
1401
- })
1402
- );
1403
- tracer.fetched(fetched);
1494
+ const fetched = await this._cd.fetch(leased, eventLimit);
1404
1495
  const payloadsMap = /* @__PURE__ */ new Map();
1405
1496
  const fetch_window_at = fetched.reduce(
1406
1497
  (max, { at, events }) => Math.max(max, events.at(-1)?.id || at),
@@ -1417,7 +1508,6 @@ var Act = class {
1417
1508
  });
1418
1509
  payloadsMap.set(stream, payloads);
1419
1510
  });
1420
- tracer.leased(leased);
1421
1511
  const handled = await Promise.all(
1422
1512
  leased.map((lease) => {
1423
1513
  const streamFetch = fetched.find((f) => f.stream === lease.stream);
@@ -1441,27 +1531,21 @@ var Act = class {
1441
1531
  const leading_avg = leading > 0 ? leading_handled / leading : 0;
1442
1532
  const total = lagging_avg + leading_avg;
1443
1533
  this._drain_lag2lead_ratio = total > 0 ? Math.max(0.2, Math.min(0.8, lagging_avg / total)) : 0.5;
1444
- const acked = await store().ack(
1534
+ const acked = await this._cd.ack(
1445
1535
  handled.filter(({ error }) => !error).map(({ at, lease }) => ({ ...lease, at }))
1446
1536
  );
1447
- if (acked.length) {
1448
- tracer.acked(acked);
1449
- this.emit("acked", acked);
1450
- }
1451
- const blocked = await store().block(
1452
- 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 }))
1453
1540
  );
1454
- if (blocked.length) {
1455
- tracer.blocked(blocked);
1456
- this.emit("blocked", blocked);
1457
- }
1541
+ if (blocked.length) this.emit("blocked", blocked);
1458
1542
  const result = { fetched, leased, acked, blocked };
1459
1543
  const hasErrors = handled.some(({ error }) => error);
1460
1544
  if (!acked.length && !blocked.length && !hasErrors)
1461
1545
  this._needs_drain = false;
1462
1546
  return result;
1463
1547
  } catch (error) {
1464
- logger3.error(error);
1548
+ logger4.error(error);
1465
1549
  } finally {
1466
1550
  this._drain_locked = false;
1467
1551
  }
@@ -1567,10 +1651,9 @@ var Act = class {
1567
1651
  stream,
1568
1652
  source
1569
1653
  }));
1570
- const { subscribed } = await store().subscribe(streams);
1654
+ const { subscribed } = await this._cd.subscribe(streams);
1571
1655
  this._correlation_checkpoint = last_id;
1572
1656
  if (subscribed) {
1573
- tracer.correlated(streams);
1574
1657
  for (const { stream } of streams) {
1575
1658
  this._subscribed_statics.add(stream);
1576
1659
  }
@@ -1835,7 +1918,7 @@ var Act = class {
1835
1918
  if (mergedState) {
1836
1919
  await Promise.all(
1837
1920
  guarded.filter((s) => targetMap.get(s)?.restart).map(async (stream) => {
1838
- const snap2 = await load(mergedState, stream);
1921
+ const snap2 = await this._es.load(mergedState, stream);
1839
1922
  seedStates.set(stream, snap2.state);
1840
1923
  })
1841
1924
  );
@@ -1942,139 +2025,13 @@ var Act = class {
1942
2025
  if (!made_progress) break;
1943
2026
  }
1944
2027
  if (lastDrain) this.emit("settled", lastDrain);
1945
- })().catch((err) => logger3.error(err)).finally(() => {
2028
+ })().catch((err) => logger4.error(err)).finally(() => {
1946
2029
  this._settling = false;
1947
2030
  });
1948
2031
  }, debounceMs);
1949
2032
  }
1950
2033
  };
1951
2034
 
1952
- // src/merge.ts
1953
- import { ZodObject as ZodObject2 } from "zod";
1954
- function baseTypeName(zodType) {
1955
- let t = zodType;
1956
- while (typeof t.unwrap === "function") {
1957
- t = t.unwrap();
1958
- }
1959
- return t.constructor.name;
1960
- }
1961
- function mergeSchemas(existing, incoming, stateName) {
1962
- if (existing instanceof ZodObject2 && incoming instanceof ZodObject2) {
1963
- const existingShape = existing.shape;
1964
- const incomingShape = incoming.shape;
1965
- for (const key of Object.keys(incomingShape)) {
1966
- if (key in existingShape) {
1967
- const existingBase = baseTypeName(existingShape[key]);
1968
- const incomingBase = baseTypeName(incomingShape[key]);
1969
- if (existingBase !== incomingBase) {
1970
- throw new Error(
1971
- `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
1972
- );
1973
- }
1974
- }
1975
- }
1976
- return existing.extend(incomingShape);
1977
- }
1978
- return existing;
1979
- }
1980
- function mergeInits(existing, incoming) {
1981
- return () => ({ ...existing(), ...incoming() });
1982
- }
1983
- function registerState(state2, states, actions, events) {
1984
- if (states.has(state2.name)) {
1985
- const existing = states.get(state2.name);
1986
- for (const name of Object.keys(state2.actions)) {
1987
- if (existing.actions[name] === state2.actions[name]) continue;
1988
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
1989
- }
1990
- for (const name of Object.keys(state2.events)) {
1991
- if (existing.events[name] === state2.events[name]) continue;
1992
- if (existing.events[name]) continue;
1993
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
1994
- }
1995
- const mergedPatch = { ...existing.patch };
1996
- for (const name of Object.keys(state2.patch)) {
1997
- const existingP = existing.patch[name];
1998
- const incomingP = state2.patch[name];
1999
- if (!existingP) {
2000
- mergedPatch[name] = incomingP;
2001
- } else {
2002
- const existingIsDefault = existingP._passthrough;
2003
- const incomingIsDefault = incomingP._passthrough;
2004
- if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
2005
- throw new Error(
2006
- `Duplicate custom patch for event "${name}" in state "${state2.name}"`
2007
- );
2008
- }
2009
- if (existingIsDefault && !incomingIsDefault) {
2010
- mergedPatch[name] = incomingP;
2011
- }
2012
- }
2013
- }
2014
- const merged = {
2015
- ...existing,
2016
- state: mergeSchemas(existing.state, state2.state, state2.name),
2017
- init: mergeInits(existing.init, state2.init),
2018
- events: { ...existing.events, ...state2.events },
2019
- actions: { ...existing.actions, ...state2.actions },
2020
- patch: mergedPatch,
2021
- on: { ...existing.on, ...state2.on },
2022
- given: { ...existing.given, ...state2.given },
2023
- snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
2024
- throw new Error(
2025
- `Duplicate snap strategy for state "${state2.name}"`
2026
- );
2027
- })() : state2.snap || existing.snap
2028
- };
2029
- states.set(state2.name, merged);
2030
- for (const name of Object.keys(merged.actions)) {
2031
- actions[name] = merged;
2032
- }
2033
- for (const name of Object.keys(state2.events)) {
2034
- if (events[name]) continue;
2035
- events[name] = {
2036
- schema: state2.events[name],
2037
- reactions: /* @__PURE__ */ new Map()
2038
- };
2039
- }
2040
- } else {
2041
- states.set(state2.name, state2);
2042
- for (const name of Object.keys(state2.actions)) {
2043
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
2044
- actions[name] = state2;
2045
- }
2046
- for (const name of Object.keys(state2.events)) {
2047
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
2048
- events[name] = {
2049
- schema: state2.events[name],
2050
- reactions: /* @__PURE__ */ new Map()
2051
- };
2052
- }
2053
- }
2054
- }
2055
- function mergeProjection(proj, events) {
2056
- for (const eventName of Object.keys(proj.events)) {
2057
- const projRegister = proj.events[eventName];
2058
- const existing = events[eventName];
2059
- if (!existing) {
2060
- events[eventName] = {
2061
- schema: projRegister.schema,
2062
- reactions: new Map(projRegister.reactions)
2063
- };
2064
- } else {
2065
- for (const [name, reaction] of projRegister.reactions) {
2066
- let key = name;
2067
- while (existing.reactions.has(key)) key = `${key}_p`;
2068
- existing.reactions.set(key, reaction);
2069
- }
2070
- }
2071
- }
2072
- }
2073
- var _this_ = ({ stream }) => ({
2074
- source: stream,
2075
- target: stream
2076
- });
2077
-
2078
2035
  // src/act-builder.ts
2079
2036
  function act(states = /* @__PURE__ */ new Map(), registry = {
2080
2037
  actions: {},
@@ -2417,7 +2374,6 @@ export {
2417
2374
  ValidationError,
2418
2375
  ZodEmpty,
2419
2376
  act,
2420
- build_tracer,
2421
2377
  cache,
2422
2378
  config,
2423
2379
  dispose,