@rotorsoft/act 0.32.2 → 0.32.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"event-sourcing.d.ts","sourceRoot":"","sources":["../../../src/internal/event-sourcing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAUH,OAAO,KAAK,EACV,IAAI,EACJ,SAAS,EAGT,MAAM,EACN,OAAO,EACP,QAAQ,EACR,KAAK,EACL,MAAM,EACP,MAAM,mBAAmB,CAAC;AAG3B,gBAAgB;AAChB,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,MAAM,EAAE,OAAO,MAAM,CAAC;CACvB;AAED;;;GAGG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAsB,IAAI,CAAC,MAAM,SAAS,MAAM,EAAE,OAAO,SAAS,OAAO,EACvE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,IAAI,CACxB,MAAM,SAAS,MAAM,EACrB,OAAO,SAAS,OAAO,EACvB,QAAQ,SAAS,OAAO,EAExB,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EACxD,IAAI,CAAC,EAAE,IAAI,GACV,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA6BpC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,MAAM,CAC1B,MAAM,SAAS,MAAM,EACrB,OAAO,SAAS,OAAO,EACvB,QAAQ,SAAS,OAAO,EACxB,IAAI,SAAS,MAAM,QAAQ,EAE3B,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EACpC,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EACjC,UAAU,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,EAC9C,cAAc,UAAQ,GACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CA+GtC"}
1
+ {"version":3,"file":"event-sourcing.d.ts","sourceRoot":"","sources":["../../../src/internal/event-sourcing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAUH,OAAO,KAAK,EACV,IAAI,EACJ,SAAS,EAGT,MAAM,EACN,OAAO,EACP,QAAQ,EACR,KAAK,EACL,MAAM,EACP,MAAM,mBAAmB,CAAC;AAG3B,gBAAgB;AAChB,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,MAAM,EAAE,OAAO,MAAM,CAAC;CACvB;AAED;;;GAGG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAsB,IAAI,CAAC,MAAM,SAAS,MAAM,EAAE,OAAO,SAAS,OAAO,EACvE,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,IAAI,CACxB,MAAM,SAAS,MAAM,EACrB,OAAO,SAAS,OAAO,EACvB,QAAQ,SAAS,OAAO,EAExB,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EACxD,IAAI,CAAC,EAAE,IAAI,GACV,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAqCpC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,MAAM,CAC1B,MAAM,SAAS,MAAM,EACrB,OAAO,SAAS,OAAO,EACvB,QAAQ,SAAS,OAAO,EACxB,IAAI,SAAS,MAAM,QAAQ,EAE3B,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EACpC,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EACjC,UAAU,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,EAC9C,cAAc,UAAQ,GACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CA+GtC"}
@@ -8,8 +8,15 @@
8
8
  * at well-defined moments — entry points for {@link "event-sourcing"} (`load`,
9
9
  * `snap`, `action`) and exit points for the {@link "drain"} pipeline (`claim`,
10
10
  * `fetch`, `ack`, `block`, `subscribe`). `action` carries both an entry log
11
- * (🔵) and a post-commit log (🔴) to preserve the diagnostic value of the
12
- * historical mid-function trace points.
11
+ * and a post-commit log to preserve the diagnostic value of the historical
12
+ * mid-function trace points.
13
+ *
14
+ * Output styles:
15
+ * - **Pretty mode** (`config().env !== "production"`) — event-sourcing logs
16
+ * show only the colored target body (color carries the operation/phase),
17
+ * drain logs keep a colored caption.
18
+ * - **Plain mode** (production / log aggregators) — every log gets a textual
19
+ * prefix; event-sourcing uses `caption: body`, drain uses `caption body`.
13
20
  *
14
21
  * The two factories — {@link buildEs} and {@link buildDrain} — let the
15
22
  * orchestrator choose bare or traced variants once at `.build()` time based
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../../src/internal/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAwBjD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CA+B7C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,OAAO,EAChD,MAAM,EAAE,MAAM,GACb,QAAQ,CAAC,OAAO,CAAC,CAyDnB"}
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../../src/internal/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAqDjD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CA0C7C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,OAAO,EAChD,MAAM,EAAE,MAAM,GACb,QAAQ,CAAC,OAAO,CAAC,CAyDnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/ports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAI5D,OAAO,KAAK,EACV,KAAK,EACL,UAAU,EACV,QAAQ,EACR,MAAM,EACN,KAAK,EACN,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;GAaG;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,4BAA6B,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAMlD;;;GAGG;AACH,KAAK,QAAQ,CAAC,IAAI,SAAS,UAAU,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,KAAK,IAAI,CAAC;AAKlE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,IAAI,CAAC,IAAI,SAAS,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,IACnD,UAAU,IAAI,KAAG,IAAI,CAQvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,GAAG,0EASd,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,KAAK,wCAEhB,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK,wCAEhB,CAAC;AASH;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAAC,IAAI,GAAE,QAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3E;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CACrB,QAAQ,CAAC,EAAE,QAAQ,GAClB,CAAC,IAAI,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAGpC;AAMD;;;;GAIG;AACH,eAAO,MAAM,UAAU,iBAAiB,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/ports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAI5D,OAAO,KAAK,EACV,KAAK,EACL,UAAU,EACV,QAAQ,EACR,MAAM,EACN,KAAK,EACN,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;GAaG;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS,4BAA6B,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAMlD;;;GAGG;AACH,KAAK,QAAQ,CAAC,IAAI,SAAS,UAAU,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,KAAK,IAAI,CAAC;AAKlE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,IAAI,CAAC,IAAI,SAAS,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,IACnD,UAAU,IAAI,KAAG,IAAI,CAWvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,GAAG,0EASd,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,KAAK,wCAEhB,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK,wCAEhB,CAAC;AASH;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAAC,IAAI,GAAE,QAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3E;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CACrB,QAAQ,CAAC,EAAE,QAAQ,GAClB,CAAC,IAAI,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAGpC;AAMD;;;;GAIG;AACH,eAAO,MAAM,UAAU,iBAAiB,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,OAAO,EAAiB,MAAM,KAAK,CAAC;AAIjE;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,QAAQ,MAAM,EACd,SAAS,QAAQ,CAAC,CAAC,CAAC,EACpB,SAAS,OAAO,CAAC,CAAC,CAAC,KAClB,QAAQ,CAAC,CAAC,CAaZ,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwHG;AACH,eAAO,MAAM,MAAM,GACjB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjC,QAAQ,QAAQ,CAAC,CAAC,CAAC,EACnB,QAAQ,OAAO,CAAC,CAAC,CAAC,EAClB,SAAS,QAAQ,CAAC,CAAC,CAAC,KACnB,QAAQ,CAAC,CAAC,GAAG,CAAC,CAGhB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuFG;AACH,wBAAsB,KAAK,CAAC,EAAE,CAAC,EAAE,MAAM,oBAEtC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,OAAO,EAAiB,MAAM,KAAK,CAAC;AAI5D;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,EACxB,QAAQ,MAAM,EACd,SAAS,QAAQ,CAAC,CAAC,CAAC,EACpB,SAAS,OAAO,CAAC,CAAC,CAAC,KAClB,QAAQ,CAAC,CAAC,CASZ,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwHG;AACH,eAAO,MAAM,MAAM,GACjB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjC,QAAQ,QAAQ,CAAC,CAAC,CAAC,EACnB,QAAQ,OAAO,CAAC,CAAC,CAAC,EAClB,SAAS,QAAQ,CAAC,CAAC,CAAC,KACnB,QAAQ,CAAC,CAAC,GAAG,CAAC,CAGhB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuFG;AACH,wBAAsB,KAAK,CAAC,EAAE,CAAC,EAAE,MAAM,oBAEtC"}
package/dist/index.cjs CHANGED
@@ -345,12 +345,19 @@ var BaseSchema = PackageSchema.extend({
345
345
  });
346
346
  var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
347
347
  var env = NODE_ENV || "development";
348
- var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "error" : NODE_ENV === "production" ? "info" : "trace");
348
+ var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "fatal" : NODE_ENV === "production" ? "info" : "trace");
349
349
  var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
350
- var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100");
350
+ var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100", 10);
351
351
  var pkg = getPackage();
352
+ var _validated;
352
353
  var config = () => {
353
- return extend({ ...pkg, env, logLevel, logSingleLine, sleepMs }, BaseSchema);
354
+ if (!_validated) {
355
+ _validated = extend(
356
+ { ...pkg, env, logLevel, logSingleLine, sleepMs },
357
+ BaseSchema
358
+ );
359
+ }
360
+ return _validated;
354
361
  };
355
362
 
356
363
  // src/utils.ts
@@ -358,19 +365,15 @@ var validate = (target, payload, schema) => {
358
365
  try {
359
366
  return schema ? schema.parse(payload) : payload;
360
367
  } catch (error) {
361
- if (error instanceof Error && error.name === "ZodError") {
362
- throw new ValidationError(
363
- target,
364
- payload,
365
- (0, import_zod3.prettifyError)(error)
366
- );
368
+ if (error instanceof import_zod3.ZodError) {
369
+ throw new ValidationError(target, payload, (0, import_zod3.prettifyError)(error));
367
370
  }
368
371
  throw new ValidationError(target, payload, error);
369
372
  }
370
373
  };
371
374
  var extend = (source, schema, target) => {
372
375
  const value = validate("config", source, schema);
373
- return Object.assign(target || {}, value);
376
+ return { ...target, ...value };
374
377
  };
375
378
  async function sleep(ms) {
376
379
  return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));
@@ -777,7 +780,7 @@ function port(injector) {
777
780
  if (!adapters.has(injector.name)) {
778
781
  const injected = injector(adapter);
779
782
  adapters.set(injector.name, injected);
780
- console.log(`[act] + ${injector.name}:${injected.constructor.name}`);
783
+ log().info(`[act] + ${injector.name}:${injected.constructor.name}`);
781
784
  }
782
785
  return adapters.get(injector.name);
783
786
  };
@@ -798,13 +801,13 @@ var cache = port(function cache2(adapter) {
798
801
  var disposers = [];
799
802
  async function disposeAndExit(code = "EXIT") {
800
803
  if (code === "ERROR" && config().env === "production") return;
801
- await Promise.all(disposers.map((disposer) => disposer()));
802
- await Promise.all(
803
- [...adapters.values()].reverse().map(async (adapter) => {
804
- await adapter.dispose();
805
- console.log(`[act] - ${adapter.constructor.name}`);
806
- })
807
- );
804
+ for (const disposer of [...disposers].reverse()) {
805
+ await disposer();
806
+ }
807
+ for (const adapter of [...adapters.values()].reverse()) {
808
+ await adapter.dispose();
809
+ log().info(`[act] - ${adapter.constructor.name}`);
810
+ }
808
811
  adapters.clear();
809
812
  config().env !== "test" && process.exit(code === "ERROR" ? 1 : 0);
810
813
  }
@@ -816,21 +819,20 @@ var SNAP_EVENT = "__snapshot__";
816
819
  var TOMBSTONE_EVENT = "__tombstone__";
817
820
 
818
821
  // src/signals.ts
819
- var logger = log();
820
822
  process.once("SIGINT", async (arg) => {
821
- logger.info(arg, "SIGINT");
823
+ log().info(arg, "SIGINT");
822
824
  await disposeAndExit("EXIT");
823
825
  });
824
826
  process.once("SIGTERM", async (arg) => {
825
- logger.info(arg, "SIGTERM");
827
+ log().info(arg, "SIGTERM");
826
828
  await disposeAndExit("EXIT");
827
829
  });
828
830
  process.once("uncaughtException", async (arg) => {
829
- logger.error(arg, "Uncaught Exception");
831
+ log().error(arg, "Uncaught Exception");
830
832
  await disposeAndExit("ERROR");
831
833
  });
832
834
  process.once("unhandledRejection", async (arg) => {
833
- logger.error(arg, "Unhandled Rejection");
835
+ log().error(arg, "Unhandled Rejection");
834
836
  await disposeAndExit("ERROR");
835
837
  });
836
838
 
@@ -1024,6 +1026,10 @@ async function load(me, stream, callback, asOf) {
1024
1026
  } else if (me.patch[e.name]) {
1025
1027
  state2 = (0, import_act_patch.patch)(state2, me.patch[e.name](event, state2));
1026
1028
  patches++;
1029
+ } else if (e.name !== TOMBSTONE_EVENT) {
1030
+ log().warn(
1031
+ `Skipping unknown event "${String(e.name)}" on stream "${stream}" (id=${e.id}) \u2014 no reducer in state "${me.name}"`
1032
+ );
1027
1033
  }
1028
1034
  callback && callback({ event, state: state2, patches, snaps });
1029
1035
  },
@@ -1118,44 +1124,69 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1118
1124
  }
1119
1125
 
1120
1126
  // src/internal/tracing.ts
1127
+ var PRETTY = config().env !== "production";
1128
+ var C_BLUE = "\x1B[38;5;39m";
1129
+ var C_ORANGE = "\x1B[38;5;208m";
1130
+ var C_GREEN = "\x1B[38;5;42m";
1131
+ var C_MAGENTA = "\x1B[38;5;165m";
1132
+ var C_DRAIN = "\x1B[38;5;244m";
1133
+ var C_RESET = "\x1B[0m";
1134
+ var es_caption = (caption, color, body) => PRETTY ? `${color}${body}${C_RESET}` : `${caption}: ${body}`;
1135
+ var drain_caption = (caption) => {
1136
+ const tag = `>> ${caption}`;
1137
+ return PRETTY ? `${C_DRAIN}${tag}${C_RESET}` : tag;
1138
+ };
1121
1139
  var traced = (inner, exit, entry) => (async (...args) => {
1122
1140
  entry?.(...args);
1123
1141
  const result = await inner(...args);
1124
1142
  exit?.(result, ...args);
1125
1143
  return result;
1126
1144
  });
1127
- function buildEs(logger2) {
1128
- if (logger2.level !== "trace") {
1145
+ function buildEs(logger) {
1146
+ if (logger.level !== "trace") {
1129
1147
  return { snap, load, action };
1130
1148
  }
1131
1149
  return {
1132
1150
  snap: traced(snap, void 0, (snapshot) => {
1133
- logger2.trace(
1134
- `\u{1F7E0} snap ${snapshot.event.stream}@${snapshot.event.version}`
1151
+ logger.trace(
1152
+ es_caption(
1153
+ "snap",
1154
+ C_MAGENTA,
1155
+ `${snapshot.event.stream}@${snapshot.event.version}`
1156
+ )
1135
1157
  );
1136
1158
  }),
1137
1159
  load: traced(load, void 0, (_me, stream, _cb, asOf) => {
1138
- logger2.trace(`\u{1F7E2} load ${stream}${asOf ? " (as-of)" : ""}`);
1160
+ logger.trace(
1161
+ es_caption("load", C_GREEN, `${stream}${asOf ? " (as-of)" : ""}`)
1162
+ );
1139
1163
  }),
1140
1164
  action: traced(
1141
1165
  action,
1142
1166
  (snapshots, _me, _action, target) => {
1143
1167
  const committed = snapshots.filter((s) => s.event);
1144
1168
  if (committed.length) {
1145
- logger2.trace(
1169
+ logger.trace(
1146
1170
  committed.map((s) => s.event.data),
1147
- `\u{1F534} commit ${target.stream}.${committed.map((s) => s.event.name).join(", ")}`
1171
+ es_caption(
1172
+ "committed",
1173
+ C_ORANGE,
1174
+ `${target.stream}.${committed.map((s) => s.event.name).join(", ")}`
1175
+ )
1148
1176
  );
1149
1177
  }
1150
1178
  },
1151
1179
  (_me, action2, target, payload) => {
1152
- logger2.trace(payload, `\u{1F535} ${target.stream}.${action2}`);
1180
+ logger.trace(
1181
+ payload,
1182
+ es_caption("action", C_BLUE, `${target.stream}.${action2}`)
1183
+ );
1153
1184
  }
1154
1185
  )
1155
1186
  };
1156
1187
  }
1157
- function buildDrain(logger2) {
1158
- if (logger2.level !== "trace") {
1188
+ function buildDrain(logger) {
1189
+ if (logger.level !== "trace") {
1159
1190
  return {
1160
1191
  claim,
1161
1192
  fetch,
@@ -1170,7 +1201,7 @@ function buildDrain(logger2) {
1170
1201
  const data = Object.fromEntries(
1171
1202
  leased.map(({ stream, at, retry }) => [stream, { at, retry }])
1172
1203
  );
1173
- logger2.trace(data, ">> lease");
1204
+ logger.trace(data, drain_caption("claimed"));
1174
1205
  }
1175
1206
  }),
1176
1207
  fetch: traced(fetch, (fetched) => {
@@ -1183,14 +1214,14 @@ function buildDrain(logger2) {
1183
1214
  return [key, value];
1184
1215
  })
1185
1216
  );
1186
- logger2.trace(data, ">> fetch");
1217
+ logger.trace(data, drain_caption("fetched"));
1187
1218
  }),
1188
1219
  ack: traced(ack, (acked) => {
1189
1220
  if (acked.length) {
1190
1221
  const data = Object.fromEntries(
1191
1222
  acked.map(({ stream, at, retry }) => [stream, { at, retry }])
1192
1223
  );
1193
- logger2.trace(data, ">> ack");
1224
+ logger.trace(data, drain_caption("acked"));
1194
1225
  }
1195
1226
  }),
1196
1227
  block: traced(block, (blocked) => {
@@ -1201,13 +1232,13 @@ function buildDrain(logger2) {
1201
1232
  { at, retry, error }
1202
1233
  ])
1203
1234
  );
1204
- logger2.trace(data, ">> block");
1235
+ logger.trace(data, drain_caption("blocked"));
1205
1236
  }
1206
1237
  }),
1207
1238
  subscribe: traced(subscribe, (result, streams) => {
1208
1239
  if (result.subscribed) {
1209
1240
  const data = streams.map(({ stream }) => stream).join(" ");
1210
- logger2.trace(`>> correlate ${data}`);
1241
+ logger.trace(`${drain_caption("correlated")} ${data}`);
1211
1242
  }
1212
1243
  })
1213
1244
  };
@@ -1221,7 +1252,7 @@ var Act = class {
1221
1252
  this._batch_handlers = batchHandlers;
1222
1253
  this._es = buildEs(this._logger);
1223
1254
  this._cd = buildDrain(this._logger);
1224
- const statics = [];
1255
+ const statics = /* @__PURE__ */ new Map();
1225
1256
  for (const [name, register] of Object.entries(this.registry.events)) {
1226
1257
  if (register.reactions.size > 0) {
1227
1258
  this._reactive_events.add(name);
@@ -1230,14 +1261,18 @@ var Act = class {
1230
1261
  if (typeof reaction.resolver === "function") {
1231
1262
  this._has_dynamic_resolvers = true;
1232
1263
  } else {
1233
- statics.push({
1234
- stream: reaction.resolver.target,
1235
- source: reaction.resolver.source
1236
- });
1264
+ const { target, source } = reaction.resolver;
1265
+ const key = `${target}|${source ?? ""}`;
1266
+ if (!statics.has(key)) statics.set(key, { stream: target, source });
1237
1267
  }
1238
1268
  }
1239
1269
  }
1240
- this._static_targets = statics;
1270
+ this._static_targets = [...statics.values()];
1271
+ for (const merged of this._states.values()) {
1272
+ for (const eventName of Object.keys(merged.events)) {
1273
+ this._event_to_state.set(eventName, merged);
1274
+ }
1275
+ }
1241
1276
  dispose(() => {
1242
1277
  this._emitter.removeAllListeners();
1243
1278
  this.stop_correlations();
@@ -1284,8 +1319,21 @@ var Act = class {
1284
1319
  _es;
1285
1320
  /** Correlate/drain pipeline ops, optionally wrapped with trace decorators */
1286
1321
  _cd;
1322
+ /**
1323
+ * Event-name → owning state, computed at build time. The duplicate-event
1324
+ * guard in merge.ts ensures one event name maps to at most one state, so
1325
+ * this lookup is unambiguous. Used by `close()` to pick the right reducer
1326
+ * set when seeding a `restart` snapshot in multi-state apps.
1327
+ */
1328
+ _event_to_state = /* @__PURE__ */ new Map();
1287
1329
  /** Logger resolved at construction time (after user port configuration) */
1288
1330
  _logger = log();
1331
+ /** Pre-bound IAct methods reused across drain cycles. Only `do` varies per
1332
+ * payload (it captures the triggering event for reactingTo auto-inject). */
1333
+ _bound_do = this.do.bind(this);
1334
+ _bound_load = this.load.bind(this);
1335
+ _bound_query = this.query.bind(this);
1336
+ _bound_query_array = this.query_array.bind(this);
1289
1337
  /**
1290
1338
  * Executes an action on a state instance, committing resulting events.
1291
1339
  *
@@ -1508,12 +1556,12 @@ var Act = class {
1508
1556
  const stream = lease.stream;
1509
1557
  let at = payloads.at(0).event.id, handled = 0;
1510
1558
  lease.retry > 0 && this._logger.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1511
- const doAction = this.do.bind(this);
1559
+ const doAction = this._bound_do;
1512
1560
  const scopedApp = {
1513
1561
  do: doAction,
1514
- load: this.load.bind(this),
1515
- query: this.query.bind(this),
1516
- query_array: this.query_array.bind(this)
1562
+ load: this._bound_load,
1563
+ query: this._bound_query,
1564
+ query_array: this._bound_query_array
1517
1565
  };
1518
1566
  for (const payload of payloads) {
1519
1567
  const { event, handler, options } = payload;
@@ -1646,12 +1694,13 @@ var Act = class {
1646
1694
  return { fetched: [], leased: [], acked: [], blocked: [] };
1647
1695
  }
1648
1696
  const fetched = await this._cd.fetch(leased, eventLimit);
1649
- const payloadsMap = /* @__PURE__ */ new Map();
1697
+ const fetchMap = /* @__PURE__ */ new Map();
1650
1698
  const fetch_window_at = fetched.reduce(
1651
1699
  (max, { at, events }) => Math.max(max, events.at(-1)?.id || at),
1652
1700
  0
1653
1701
  );
1654
- fetched.forEach(({ stream, events }) => {
1702
+ for (const f of fetched) {
1703
+ const { stream, events } = f;
1655
1704
  const payloads = events.flatMap((event) => {
1656
1705
  const register = this.registry.events[event.name];
1657
1706
  if (!register) return [];
@@ -1660,13 +1709,13 @@ var Act = class {
1660
1709
  return resolved && resolved.target === stream;
1661
1710
  }).map((reaction) => ({ ...reaction, event }));
1662
1711
  });
1663
- payloadsMap.set(stream, payloads);
1664
- });
1712
+ fetchMap.set(stream, { fetch: f, payloads });
1713
+ }
1665
1714
  const handled = await Promise.all(
1666
1715
  leased.map((lease) => {
1667
- const streamFetch = fetched.find((f) => f.stream === lease.stream);
1668
- const at = streamFetch?.events.at(-1)?.id || fetch_window_at;
1669
- const payloads = payloadsMap.get(lease.stream);
1716
+ const entry = fetchMap.get(lease.stream);
1717
+ const at = entry?.fetch.events.at(-1)?.id || fetch_window_at;
1718
+ const payloads = entry?.payloads ?? [];
1670
1719
  const batchHandler = this._batch_handlers.get(lease.stream);
1671
1720
  if (batchHandler && payloads.length > 0) {
1672
1721
  return this.handleBatch({ ...lease, at }, payloads, batchHandler);
@@ -2000,16 +2049,24 @@ var Act = class {
2000
2049
  streams.map(async (s) => {
2001
2050
  let maxId = -1;
2002
2051
  let version = -1;
2052
+ let lastEventName;
2003
2053
  await store().query(
2004
2054
  (e) => {
2005
- if (e.name !== TOMBSTONE_EVENT) {
2055
+ if (e.name === TOMBSTONE_EVENT) return;
2056
+ if (maxId === -1) {
2006
2057
  maxId = e.id;
2007
2058
  version = e.version;
2008
2059
  }
2060
+ if (e.name !== SNAP_EVENT && lastEventName === void 0) {
2061
+ lastEventName = e.name;
2062
+ }
2009
2063
  },
2010
- { stream: s, stream_exact: true, backward: true, limit: 1 }
2064
+ // limit: 2 covers the typical snapshot-at-head case (snapshot is
2065
+ // always preceded by the domain event it captured). Streams with
2066
+ // unusual layouts fall back to no-seed via the lookup miss path.
2067
+ { stream: s, stream_exact: true, backward: true, limit: 2 }
2011
2068
  );
2012
- if (maxId >= 0) streamInfo.set(s, { maxId, version });
2069
+ if (maxId >= 0) streamInfo.set(s, { maxId, version, lastEventName });
2013
2070
  })
2014
2071
  );
2015
2072
  const skipped = [];
@@ -2018,16 +2075,14 @@ var Act = class {
2018
2075
  safe = [...streamInfo.keys()];
2019
2076
  } else {
2020
2077
  const pendingSet = /* @__PURE__ */ new Set();
2021
- const leases = await store().claim(1e3, 1e3, (0, import_crypto2.randomUUID)(), 1);
2022
- if (leases.length) await store().ack(leases);
2023
- for (const lease of leases) {
2024
- const sourceRe = lease.source ? RegExp(lease.source) : void 0;
2078
+ await store().query_streams((position) => {
2079
+ const sourceRe = position.source ? RegExp(position.source) : void 0;
2025
2080
  for (const [stream, info] of streamInfo) {
2026
- if ((!sourceRe || sourceRe.test(stream)) && lease.at < info.maxId) {
2081
+ if ((!sourceRe || sourceRe.test(stream)) && position.at < info.maxId) {
2027
2082
  pendingSet.add(stream);
2028
2083
  }
2029
2084
  }
2030
- }
2085
+ });
2031
2086
  safe = [];
2032
2087
  for (const [stream] of streamInfo) {
2033
2088
  if (pendingSet.has(stream)) {
@@ -2067,16 +2122,21 @@ var Act = class {
2067
2122
  this.emit("closed", result2);
2068
2123
  return result2;
2069
2124
  }
2070
- const mergedState = [...this._states.values()][0];
2071
2125
  const seedStates = /* @__PURE__ */ new Map();
2072
- if (mergedState) {
2073
- await Promise.all(
2074
- guarded.filter((s) => targetMap.get(s)?.restart).map(async (stream) => {
2075
- const snap2 = await this._es.load(mergedState, stream);
2076
- seedStates.set(stream, snap2.state);
2077
- })
2078
- );
2079
- }
2126
+ await Promise.all(
2127
+ guarded.filter((s) => targetMap.get(s)?.restart).map(async (stream) => {
2128
+ const lastEventName = streamInfo.get(stream)?.lastEventName;
2129
+ const ownerState = lastEventName ? this._event_to_state.get(lastEventName) : void 0;
2130
+ if (!ownerState) {
2131
+ this._logger.error(
2132
+ `Cannot seed restart for "${stream}": no registered state owns event "${lastEventName ?? "<none>"}". Stream will be tombstoned instead.`
2133
+ );
2134
+ return;
2135
+ }
2136
+ const snap2 = await this._es.load(ownerState, stream);
2137
+ seedStates.set(stream, snap2.state);
2138
+ })
2139
+ );
2080
2140
  for (const stream of guarded) {
2081
2141
  const archiveFn = targetMap.get(stream)?.archive;
2082
2142
  if (archiveFn) await archiveFn();
@@ -2187,6 +2247,14 @@ var Act = class {
2187
2247
  };
2188
2248
 
2189
2249
  // src/act-builder.ts
2250
+ function registerBatchHandler(proj, batchHandlers) {
2251
+ if (!proj.batchHandler || !proj.target) return;
2252
+ const existing = batchHandlers.get(proj.target);
2253
+ if (existing && existing !== proj.batchHandler) {
2254
+ throw new Error(`Duplicate batch handler for target "${proj.target}"`);
2255
+ }
2256
+ batchHandlers.set(proj.target, proj.batchHandler);
2257
+ }
2190
2258
  function act(states = /* @__PURE__ */ new Map(), registry = {
2191
2259
  actions: {},
2192
2260
  events: {}
@@ -2221,9 +2289,7 @@ function act(states = /* @__PURE__ */ new Map(), registry = {
2221
2289
  },
2222
2290
  withProjection: (proj) => {
2223
2291
  mergeProjection(proj, registry.events);
2224
- if (proj.batchHandler && proj.target) {
2225
- batchHandlers.set(proj.target, proj.batchHandler);
2226
- }
2292
+ registerBatchHandler(proj, batchHandlers);
2227
2293
  return act(
2228
2294
  states,
2229
2295
  registry,
@@ -2269,9 +2335,7 @@ function act(states = /* @__PURE__ */ new Map(), registry = {
2269
2335
  build: () => {
2270
2336
  for (const proj of pendingProjections) {
2271
2337
  mergeProjection(proj, registry.events);
2272
- if (proj.batchHandler && proj.target) {
2273
- batchHandlers.set(proj.target, proj.batchHandler);
2274
- }
2338
+ registerBatchHandler(proj, batchHandlers);
2275
2339
  }
2276
2340
  return new Act(
2277
2341
  registry,