@rotorsoft/act 0.24.0 → 0.25.0

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
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  CausationEventSchema: () => CausationEventSchema,
36
36
  CommittedMetaSchema: () => CommittedMetaSchema,
37
37
  ConcurrencyError: () => ConcurrencyError,
38
+ ConsoleLogger: () => ConsoleLogger,
38
39
  Environments: () => Environments,
39
40
  Errors: () => Errors,
40
41
  EventMetaSchema: () => EventMetaSchema,
@@ -56,7 +57,7 @@ __export(index_exports, {
56
57
  dispose: () => dispose,
57
58
  disposeAndExit: () => disposeAndExit,
58
59
  extend: () => extend,
59
- logger: () => logger,
60
+ log: () => log,
60
61
  port: () => port,
61
62
  projection: () => projection,
62
63
  sleep: () => sleep,
@@ -67,8 +68,107 @@ __export(index_exports, {
67
68
  });
68
69
  module.exports = __toCommonJS(index_exports);
69
70
 
70
- // src/ports.ts
71
- var import_pino = require("pino");
71
+ // src/adapters/ConsoleLogger.ts
72
+ var LEVEL_VALUES = {
73
+ fatal: 60,
74
+ error: 50,
75
+ warn: 40,
76
+ info: 30,
77
+ debug: 20,
78
+ trace: 10
79
+ };
80
+ var LEVEL_COLORS = {
81
+ fatal: "\x1B[41m\x1B[37m",
82
+ // white on red bg
83
+ error: "\x1B[31m",
84
+ // red
85
+ warn: "\x1B[33m",
86
+ // yellow
87
+ info: "\x1B[32m",
88
+ // green
89
+ debug: "\x1B[36m",
90
+ // cyan
91
+ trace: "\x1B[90m"
92
+ // gray
93
+ };
94
+ var RESET = "\x1B[0m";
95
+ var noop = () => {
96
+ };
97
+ var ConsoleLogger = class _ConsoleLogger {
98
+ level;
99
+ _pretty;
100
+ fatal;
101
+ error;
102
+ warn;
103
+ info;
104
+ debug;
105
+ trace;
106
+ constructor(options = {}) {
107
+ const {
108
+ level = "info",
109
+ pretty = process.env.NODE_ENV !== "production",
110
+ bindings
111
+ } = options;
112
+ this._pretty = pretty;
113
+ this.level = level;
114
+ const threshold = LEVEL_VALUES[level] ?? 30;
115
+ const write = pretty ? this._prettyWrite.bind(this, bindings) : this._jsonWrite.bind(this, bindings);
116
+ this.fatal = write.bind(this, "fatal", 60);
117
+ this.error = threshold <= 50 ? write.bind(this, "error", 50) : noop;
118
+ this.warn = threshold <= 40 ? write.bind(this, "warn", 40) : noop;
119
+ this.info = threshold <= 30 ? write.bind(this, "info", 30) : noop;
120
+ this.debug = threshold <= 20 ? write.bind(this, "debug", 20) : noop;
121
+ this.trace = threshold <= 10 ? write.bind(this, "trace", 10) : noop;
122
+ }
123
+ async dispose() {
124
+ }
125
+ child(bindings) {
126
+ return new _ConsoleLogger({
127
+ level: this.level,
128
+ pretty: this._pretty,
129
+ bindings
130
+ });
131
+ }
132
+ _jsonWrite(bindings, level, _num, objOrMsg, msg) {
133
+ let obj;
134
+ let message;
135
+ if (typeof objOrMsg === "string") {
136
+ message = objOrMsg;
137
+ obj = {};
138
+ } else if (objOrMsg !== null && typeof objOrMsg === "object") {
139
+ message = msg;
140
+ obj = Object.fromEntries(Object.entries(objOrMsg));
141
+ } else {
142
+ message = msg;
143
+ obj = { value: objOrMsg };
144
+ }
145
+ const entry = Object.assign({ level, time: Date.now() }, bindings, obj);
146
+ if (message) entry.msg = message;
147
+ process.stdout.write(JSON.stringify(entry) + "\n");
148
+ }
149
+ _prettyWrite(bindings, level, _num, objOrMsg, msg) {
150
+ const color = LEVEL_COLORS[level];
151
+ const tag = `${color}${level.toUpperCase().padEnd(5)}${RESET}`;
152
+ const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
153
+ let message;
154
+ let data;
155
+ if (typeof objOrMsg === "string") {
156
+ message = objOrMsg;
157
+ } else {
158
+ message = msg ?? "";
159
+ if (objOrMsg !== void 0 && objOrMsg !== null) {
160
+ try {
161
+ data = JSON.stringify(objOrMsg);
162
+ } catch {
163
+ data = "[unserializable]";
164
+ }
165
+ }
166
+ }
167
+ const bindStr = bindings && Object.keys(bindings).length ? ` ${JSON.stringify(bindings)}` : "";
168
+ const parts = [ts, tag, message, data, bindStr].filter(Boolean);
169
+ process.stdout.write(parts.join(" ") + "\n");
170
+ }
171
+ };
72
172
 
73
173
  // src/adapters/InMemoryCache.ts
74
174
  var InMemoryCache = class {
@@ -536,28 +636,30 @@ var InMemoryStore = class {
536
636
 
537
637
  // src/ports.ts
538
638
  var ExitCodes = ["ERROR", "EXIT"];
539
- var logger = (0, import_pino.pino)({
540
- transport: config().env !== "production" ? {
541
- target: "pino-pretty",
542
- options: {
543
- ignore: "pid,hostname",
544
- singleLine: config().logSingleLine,
545
- colorize: true
546
- }
547
- } : void 0,
548
- level: config().logLevel
549
- });
550
639
  var adapters = /* @__PURE__ */ new Map();
551
640
  function port(injector) {
552
641
  return function(adapter) {
553
642
  if (!adapters.has(injector.name)) {
554
643
  const injected = injector(adapter);
555
644
  adapters.set(injector.name, injected);
556
- logger.info(`\u{1F50C} injected ${injector.name}:${injected.constructor.name}`);
645
+ console.log(`[act] + ${injector.name}:${injected.constructor.name}`);
557
646
  }
558
647
  return adapters.get(injector.name);
559
648
  };
560
649
  }
650
+ var log = port(function log2(adapter) {
651
+ const cfg = config();
652
+ return adapter || new ConsoleLogger({
653
+ level: cfg.logLevel,
654
+ pretty: cfg.env !== "production"
655
+ });
656
+ });
657
+ var store = port(function store2(adapter) {
658
+ return adapter || new InMemoryStore();
659
+ });
660
+ var cache = port(function cache2(adapter) {
661
+ return adapter || new InMemoryCache();
662
+ });
561
663
  var disposers = [];
562
664
  async function disposeAndExit(code = "EXIT") {
563
665
  if (code === "ERROR" && config().env === "production") return;
@@ -565,7 +667,7 @@ async function disposeAndExit(code = "EXIT") {
565
667
  await Promise.all(
566
668
  [...adapters.values()].reverse().map(async (adapter) => {
567
669
  await adapter.dispose();
568
- logger.info(`\u{1F50C} disposed ${adapter.constructor.name}`);
670
+ console.log(`[act] - ${adapter.constructor.name}`);
569
671
  })
570
672
  );
571
673
  adapters.clear();
@@ -576,14 +678,9 @@ function dispose(disposer) {
576
678
  return disposeAndExit;
577
679
  }
578
680
  var SNAP_EVENT = "__snapshot__";
579
- var store = port(function store2(adapter) {
580
- return adapter || new InMemoryStore();
581
- });
582
- var cache = port(function cache2(adapter) {
583
- return adapter || new InMemoryCache();
584
- });
585
681
  function build_tracer(logLevel2) {
586
682
  if (logLevel2 === "trace") {
683
+ const logger4 = log();
587
684
  return {
588
685
  fetched: (fetched) => {
589
686
  const data = Object.fromEntries(
@@ -595,23 +692,23 @@ function build_tracer(logLevel2) {
595
692
  return [key, value];
596
693
  })
597
694
  );
598
- logger.trace(data, "\u26A1\uFE0F fetch");
695
+ logger4.trace(data, ">> fetch");
599
696
  },
600
697
  correlated: (streams) => {
601
698
  const data = streams.map(({ stream }) => stream).join(" ");
602
- logger.trace(`\u26A1\uFE0F correlate ${data}`);
699
+ logger4.trace(`>> correlate ${data}`);
603
700
  },
604
701
  leased: (leases) => {
605
702
  const data = Object.fromEntries(
606
703
  leases.map(({ stream, at, retry }) => [stream, { at, retry }])
607
704
  );
608
- logger.trace(data, "\u26A1\uFE0F lease");
705
+ logger4.trace(data, ">> lease");
609
706
  },
610
707
  acked: (leases) => {
611
708
  const data = Object.fromEntries(
612
709
  leases.map(({ stream, at, retry }) => [stream, { at, retry }])
613
710
  );
614
- logger.trace(data, "\u26A1\uFE0F ack");
711
+ logger4.trace(data, ">> ack");
615
712
  },
616
713
  blocked: (leases) => {
617
714
  const data = Object.fromEntries(
@@ -620,7 +717,7 @@ function build_tracer(logLevel2) {
620
717
  { at, retry, error }
621
718
  ])
622
719
  );
623
- logger.trace(data, "\u26A1\uFE0F block");
720
+ logger4.trace(data, ">> block");
624
721
  }
625
722
  };
626
723
  } else {
@@ -640,6 +737,7 @@ function build_tracer(logLevel2) {
640
737
  }
641
738
 
642
739
  // src/signals.ts
740
+ var logger = log();
643
741
  process.once("SIGINT", async (arg) => {
644
742
  logger.info(arg, "SIGINT");
645
743
  await disposeAndExit("EXIT");
@@ -664,6 +762,7 @@ var import_events = __toESM(require("events"), 1);
664
762
  // src/event-sourcing.ts
665
763
  var import_act_patch = require("@rotorsoft/act-patch");
666
764
  var import_crypto = require("crypto");
765
+ var logger2 = log();
667
766
  async function snap(snapshot) {
668
767
  try {
669
768
  const { id, stream, name, meta, version } = snapshot.event;
@@ -677,9 +776,9 @@ async function snap(snapshot) {
677
776
  version
678
777
  // IMPORTANT! - state events are committed right after the snapshot event
679
778
  );
680
- logger.trace(snapped, "\u{1F7E0} snap");
779
+ logger2.trace(snapped, "\u{1F7E0} snap");
681
780
  } catch (error) {
682
- logger.error(error);
781
+ logger2.error(error);
683
782
  }
684
783
  }
685
784
  async function load(me, stream, callback) {
@@ -703,7 +802,7 @@ async function load(me, stream, callback) {
703
802
  },
704
803
  { stream, with_snaps: !cached, after: cached?.event_id }
705
804
  );
706
- logger.trace(
805
+ logger2.trace(
707
806
  state2,
708
807
  `\u{1F7E2} load ${stream}${cached && count === 0 ? " (cached)" : ""}`
709
808
  );
@@ -715,7 +814,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
715
814
  payload = skipValidation ? payload : validate(action2, payload, me.actions[action2]);
716
815
  const snapshot = await load(me, stream);
717
816
  const expected = expectedVersion || snapshot.event?.version;
718
- logger.trace(
817
+ logger2.trace(
719
818
  payload,
720
819
  `\u{1F535} ${stream}.${action2}${typeof expected === "number" ? `.${expected}` : ""}`
721
820
  );
@@ -758,7 +857,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
758
857
  } : void 0
759
858
  }
760
859
  };
761
- logger.trace(
860
+ logger2.trace(
762
861
  emitted.map((e) => e.data),
763
862
  `\u{1F534} commit ${stream}.${emitted.map((e) => e.name).join(", ")}`
764
863
  );
@@ -798,6 +897,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
798
897
  }
799
898
 
800
899
  // src/act.ts
900
+ var logger3 = log();
801
901
  var tracer = build_tracer(config().logLevel);
802
902
  var Act = class {
803
903
  constructor(registry, _states = /* @__PURE__ */ new Map()) {
@@ -1078,7 +1178,7 @@ var Act = class {
1078
1178
  if (payloads.length === 0) return { lease, handled: 0, at: lease.at };
1079
1179
  const stream = lease.stream;
1080
1180
  let at = payloads.at(0).event.id, handled = 0;
1081
- lease.retry > 0 && logger.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1181
+ lease.retry > 0 && logger3.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1082
1182
  for (const payload of payloads) {
1083
1183
  const { event, handler, options } = payload;
1084
1184
  try {
@@ -1086,9 +1186,9 @@ var Act = class {
1086
1186
  at = event.id;
1087
1187
  handled++;
1088
1188
  } catch (error) {
1089
- logger.error(error);
1189
+ logger3.error(error);
1090
1190
  const block = lease.retry >= options.maxRetries && options.blockOnError;
1091
- block && logger.error(`Blocking ${stream} after ${lease.retry} retries.`);
1191
+ block && logger3.error(`Blocking ${stream} after ${lease.retry} retries.`);
1092
1192
  return {
1093
1193
  lease,
1094
1194
  handled,
@@ -1232,7 +1332,7 @@ var Act = class {
1232
1332
  this._needs_drain = false;
1233
1333
  return result;
1234
1334
  } catch (error) {
1235
- logger.error(error);
1335
+ logger3.error(error);
1236
1336
  } finally {
1237
1337
  this._drain_locked = false;
1238
1338
  }
@@ -1505,7 +1605,7 @@ var Act = class {
1505
1605
  if (!lastDrain.acked.length && !lastDrain.blocked.length) break;
1506
1606
  }
1507
1607
  if (lastDrain) this.emit("settled", lastDrain);
1508
- })().catch((err) => logger.error(err)).finally(() => {
1608
+ })().catch((err) => logger3.error(err)).finally(() => {
1509
1609
  this._settling = false;
1510
1610
  });
1511
1611
  }, debounceMs);
@@ -1552,15 +1652,35 @@ function registerState(state2, states, actions, events) {
1552
1652
  }
1553
1653
  for (const name of Object.keys(state2.events)) {
1554
1654
  if (existing.events[name] === state2.events[name]) continue;
1655
+ if (existing.events[name]) continue;
1555
1656
  if (events[name]) throw new Error(`Duplicate event "${name}"`);
1556
1657
  }
1658
+ const mergedPatch = { ...existing.patch };
1659
+ for (const name of Object.keys(state2.patch)) {
1660
+ const existingP = existing.patch[name];
1661
+ const incomingP = state2.patch[name];
1662
+ if (!existingP) {
1663
+ mergedPatch[name] = incomingP;
1664
+ } else {
1665
+ const existingIsDefault = existingP._passthrough;
1666
+ const incomingIsDefault = incomingP._passthrough;
1667
+ if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
1668
+ throw new Error(
1669
+ `Duplicate custom patch for event "${name}" in state "${state2.name}"`
1670
+ );
1671
+ }
1672
+ if (existingIsDefault && !incomingIsDefault) {
1673
+ mergedPatch[name] = incomingP;
1674
+ }
1675
+ }
1676
+ }
1557
1677
  const merged = {
1558
1678
  ...existing,
1559
1679
  state: mergeSchemas(existing.state, state2.state, state2.name),
1560
1680
  init: mergeInits(existing.init, state2.init),
1561
1681
  events: { ...existing.events, ...state2.events },
1562
1682
  actions: { ...existing.actions, ...state2.actions },
1563
- patch: { ...existing.patch, ...state2.patch },
1683
+ patch: mergedPatch,
1564
1684
  on: { ...existing.on, ...state2.on },
1565
1685
  given: { ...existing.given, ...state2.given },
1566
1686
  snap: state2.snap || existing.snap
@@ -1841,10 +1961,11 @@ function state(entry) {
1841
1961
  return {
1842
1962
  emits(events) {
1843
1963
  const defaultPatch = Object.fromEntries(
1844
- Object.keys(events).map((k) => [
1845
- k,
1846
- ({ data }) => data
1847
- ])
1964
+ Object.keys(events).map((k) => {
1965
+ const fn = ({ data }) => data;
1966
+ fn._passthrough = true;
1967
+ return [k, fn];
1968
+ })
1848
1969
  );
1849
1970
  const builder = action_builder({
1850
1971
  events,
@@ -1926,6 +2047,7 @@ function action_builder(state2) {
1926
2047
  CausationEventSchema,
1927
2048
  CommittedMetaSchema,
1928
2049
  ConcurrencyError,
2050
+ ConsoleLogger,
1929
2051
  Environments,
1930
2052
  Errors,
1931
2053
  EventMetaSchema,
@@ -1947,7 +2069,7 @@ function action_builder(state2) {
1947
2069
  dispose,
1948
2070
  disposeAndExit,
1949
2071
  extend,
1950
- logger,
2072
+ log,
1951
2073
  port,
1952
2074
  projection,
1953
2075
  sleep,