@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/README.md +23 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/ConsoleLogger.d.ts +39 -0
- package/dist/@types/adapters/ConsoleLogger.d.ts.map +1 -0
- package/dist/@types/adapters/index.d.ts +1 -0
- package/dist/@types/adapters/index.d.ts.map +1 -1
- package/dist/@types/event-sourcing.d.ts.map +1 -1
- package/dist/@types/merge.d.ts.map +1 -1
- package/dist/@types/ports.d.ts +118 -146
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/@types/state-builder.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +28 -0
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/index.cjs +165 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +163 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,104 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
1
|
+
// src/adapters/ConsoleLogger.ts
|
|
2
|
+
var LEVEL_VALUES = {
|
|
3
|
+
fatal: 60,
|
|
4
|
+
error: 50,
|
|
5
|
+
warn: 40,
|
|
6
|
+
info: 30,
|
|
7
|
+
debug: 20,
|
|
8
|
+
trace: 10
|
|
9
|
+
};
|
|
10
|
+
var LEVEL_COLORS = {
|
|
11
|
+
fatal: "\x1B[41m\x1B[37m",
|
|
12
|
+
// white on red bg
|
|
13
|
+
error: "\x1B[31m",
|
|
14
|
+
// red
|
|
15
|
+
warn: "\x1B[33m",
|
|
16
|
+
// yellow
|
|
17
|
+
info: "\x1B[32m",
|
|
18
|
+
// green
|
|
19
|
+
debug: "\x1B[36m",
|
|
20
|
+
// cyan
|
|
21
|
+
trace: "\x1B[90m"
|
|
22
|
+
// gray
|
|
23
|
+
};
|
|
24
|
+
var RESET = "\x1B[0m";
|
|
25
|
+
var noop = () => {
|
|
26
|
+
};
|
|
27
|
+
var ConsoleLogger = class _ConsoleLogger {
|
|
28
|
+
level;
|
|
29
|
+
_pretty;
|
|
30
|
+
fatal;
|
|
31
|
+
error;
|
|
32
|
+
warn;
|
|
33
|
+
info;
|
|
34
|
+
debug;
|
|
35
|
+
trace;
|
|
36
|
+
constructor(options = {}) {
|
|
37
|
+
const {
|
|
38
|
+
level = "info",
|
|
39
|
+
pretty = process.env.NODE_ENV !== "production",
|
|
40
|
+
bindings
|
|
41
|
+
} = options;
|
|
42
|
+
this._pretty = pretty;
|
|
43
|
+
this.level = level;
|
|
44
|
+
const threshold = LEVEL_VALUES[level] ?? 30;
|
|
45
|
+
const write = pretty ? this._prettyWrite.bind(this, bindings) : this._jsonWrite.bind(this, bindings);
|
|
46
|
+
this.fatal = write.bind(this, "fatal", 60);
|
|
47
|
+
this.error = threshold <= 50 ? write.bind(this, "error", 50) : noop;
|
|
48
|
+
this.warn = threshold <= 40 ? write.bind(this, "warn", 40) : noop;
|
|
49
|
+
this.info = threshold <= 30 ? write.bind(this, "info", 30) : noop;
|
|
50
|
+
this.debug = threshold <= 20 ? write.bind(this, "debug", 20) : noop;
|
|
51
|
+
this.trace = threshold <= 10 ? write.bind(this, "trace", 10) : noop;
|
|
52
|
+
}
|
|
53
|
+
async dispose() {
|
|
54
|
+
}
|
|
55
|
+
child(bindings) {
|
|
56
|
+
return new _ConsoleLogger({
|
|
57
|
+
level: this.level,
|
|
58
|
+
pretty: this._pretty,
|
|
59
|
+
bindings
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
_jsonWrite(bindings, level, _num, objOrMsg, msg) {
|
|
63
|
+
let obj;
|
|
64
|
+
let message;
|
|
65
|
+
if (typeof objOrMsg === "string") {
|
|
66
|
+
message = objOrMsg;
|
|
67
|
+
obj = {};
|
|
68
|
+
} else if (objOrMsg !== null && typeof objOrMsg === "object") {
|
|
69
|
+
message = msg;
|
|
70
|
+
obj = Object.fromEntries(Object.entries(objOrMsg));
|
|
71
|
+
} else {
|
|
72
|
+
message = msg;
|
|
73
|
+
obj = { value: objOrMsg };
|
|
74
|
+
}
|
|
75
|
+
const entry = Object.assign({ level, time: Date.now() }, bindings, obj);
|
|
76
|
+
if (message) entry.msg = message;
|
|
77
|
+
process.stdout.write(JSON.stringify(entry) + "\n");
|
|
78
|
+
}
|
|
79
|
+
_prettyWrite(bindings, level, _num, objOrMsg, msg) {
|
|
80
|
+
const color = LEVEL_COLORS[level];
|
|
81
|
+
const tag = `${color}${level.toUpperCase().padEnd(5)}${RESET}`;
|
|
82
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
83
|
+
let message;
|
|
84
|
+
let data;
|
|
85
|
+
if (typeof objOrMsg === "string") {
|
|
86
|
+
message = objOrMsg;
|
|
87
|
+
} else {
|
|
88
|
+
message = msg ?? "";
|
|
89
|
+
if (objOrMsg !== void 0 && objOrMsg !== null) {
|
|
90
|
+
try {
|
|
91
|
+
data = JSON.stringify(objOrMsg);
|
|
92
|
+
} catch {
|
|
93
|
+
data = "[unserializable]";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const bindStr = bindings && Object.keys(bindings).length ? ` ${JSON.stringify(bindings)}` : "";
|
|
98
|
+
const parts = [ts, tag, message, data, bindStr].filter(Boolean);
|
|
99
|
+
process.stdout.write(parts.join(" ") + "\n");
|
|
100
|
+
}
|
|
101
|
+
};
|
|
3
102
|
|
|
4
103
|
// src/adapters/InMemoryCache.ts
|
|
5
104
|
var InMemoryCache = class {
|
|
@@ -467,28 +566,30 @@ var InMemoryStore = class {
|
|
|
467
566
|
|
|
468
567
|
// src/ports.ts
|
|
469
568
|
var ExitCodes = ["ERROR", "EXIT"];
|
|
470
|
-
var logger = pino({
|
|
471
|
-
transport: config().env !== "production" ? {
|
|
472
|
-
target: "pino-pretty",
|
|
473
|
-
options: {
|
|
474
|
-
ignore: "pid,hostname",
|
|
475
|
-
singleLine: config().logSingleLine,
|
|
476
|
-
colorize: true
|
|
477
|
-
}
|
|
478
|
-
} : void 0,
|
|
479
|
-
level: config().logLevel
|
|
480
|
-
});
|
|
481
569
|
var adapters = /* @__PURE__ */ new Map();
|
|
482
570
|
function port(injector) {
|
|
483
571
|
return function(adapter) {
|
|
484
572
|
if (!adapters.has(injector.name)) {
|
|
485
573
|
const injected = injector(adapter);
|
|
486
574
|
adapters.set(injector.name, injected);
|
|
487
|
-
|
|
575
|
+
console.log(`[act] + ${injector.name}:${injected.constructor.name}`);
|
|
488
576
|
}
|
|
489
577
|
return adapters.get(injector.name);
|
|
490
578
|
};
|
|
491
579
|
}
|
|
580
|
+
var log = port(function log2(adapter) {
|
|
581
|
+
const cfg = config();
|
|
582
|
+
return adapter || new ConsoleLogger({
|
|
583
|
+
level: cfg.logLevel,
|
|
584
|
+
pretty: cfg.env !== "production"
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
var store = port(function store2(adapter) {
|
|
588
|
+
return adapter || new InMemoryStore();
|
|
589
|
+
});
|
|
590
|
+
var cache = port(function cache2(adapter) {
|
|
591
|
+
return adapter || new InMemoryCache();
|
|
592
|
+
});
|
|
492
593
|
var disposers = [];
|
|
493
594
|
async function disposeAndExit(code = "EXIT") {
|
|
494
595
|
if (code === "ERROR" && config().env === "production") return;
|
|
@@ -496,7 +597,7 @@ async function disposeAndExit(code = "EXIT") {
|
|
|
496
597
|
await Promise.all(
|
|
497
598
|
[...adapters.values()].reverse().map(async (adapter) => {
|
|
498
599
|
await adapter.dispose();
|
|
499
|
-
|
|
600
|
+
console.log(`[act] - ${adapter.constructor.name}`);
|
|
500
601
|
})
|
|
501
602
|
);
|
|
502
603
|
adapters.clear();
|
|
@@ -507,14 +608,9 @@ function dispose(disposer) {
|
|
|
507
608
|
return disposeAndExit;
|
|
508
609
|
}
|
|
509
610
|
var SNAP_EVENT = "__snapshot__";
|
|
510
|
-
var store = port(function store2(adapter) {
|
|
511
|
-
return adapter || new InMemoryStore();
|
|
512
|
-
});
|
|
513
|
-
var cache = port(function cache2(adapter) {
|
|
514
|
-
return adapter || new InMemoryCache();
|
|
515
|
-
});
|
|
516
611
|
function build_tracer(logLevel2) {
|
|
517
612
|
if (logLevel2 === "trace") {
|
|
613
|
+
const logger4 = log();
|
|
518
614
|
return {
|
|
519
615
|
fetched: (fetched) => {
|
|
520
616
|
const data = Object.fromEntries(
|
|
@@ -526,23 +622,23 @@ function build_tracer(logLevel2) {
|
|
|
526
622
|
return [key, value];
|
|
527
623
|
})
|
|
528
624
|
);
|
|
529
|
-
|
|
625
|
+
logger4.trace(data, ">> fetch");
|
|
530
626
|
},
|
|
531
627
|
correlated: (streams) => {
|
|
532
628
|
const data = streams.map(({ stream }) => stream).join(" ");
|
|
533
|
-
|
|
629
|
+
logger4.trace(`>> correlate ${data}`);
|
|
534
630
|
},
|
|
535
631
|
leased: (leases) => {
|
|
536
632
|
const data = Object.fromEntries(
|
|
537
633
|
leases.map(({ stream, at, retry }) => [stream, { at, retry }])
|
|
538
634
|
);
|
|
539
|
-
|
|
635
|
+
logger4.trace(data, ">> lease");
|
|
540
636
|
},
|
|
541
637
|
acked: (leases) => {
|
|
542
638
|
const data = Object.fromEntries(
|
|
543
639
|
leases.map(({ stream, at, retry }) => [stream, { at, retry }])
|
|
544
640
|
);
|
|
545
|
-
|
|
641
|
+
logger4.trace(data, ">> ack");
|
|
546
642
|
},
|
|
547
643
|
blocked: (leases) => {
|
|
548
644
|
const data = Object.fromEntries(
|
|
@@ -551,7 +647,7 @@ function build_tracer(logLevel2) {
|
|
|
551
647
|
{ at, retry, error }
|
|
552
648
|
])
|
|
553
649
|
);
|
|
554
|
-
|
|
650
|
+
logger4.trace(data, ">> block");
|
|
555
651
|
}
|
|
556
652
|
};
|
|
557
653
|
} else {
|
|
@@ -571,6 +667,7 @@ function build_tracer(logLevel2) {
|
|
|
571
667
|
}
|
|
572
668
|
|
|
573
669
|
// src/signals.ts
|
|
670
|
+
var logger = log();
|
|
574
671
|
process.once("SIGINT", async (arg) => {
|
|
575
672
|
logger.info(arg, "SIGINT");
|
|
576
673
|
await disposeAndExit("EXIT");
|
|
@@ -595,6 +692,7 @@ import EventEmitter from "events";
|
|
|
595
692
|
// src/event-sourcing.ts
|
|
596
693
|
import { patch } from "@rotorsoft/act-patch";
|
|
597
694
|
import { randomUUID } from "crypto";
|
|
695
|
+
var logger2 = log();
|
|
598
696
|
async function snap(snapshot) {
|
|
599
697
|
try {
|
|
600
698
|
const { id, stream, name, meta, version } = snapshot.event;
|
|
@@ -608,9 +706,9 @@ async function snap(snapshot) {
|
|
|
608
706
|
version
|
|
609
707
|
// IMPORTANT! - state events are committed right after the snapshot event
|
|
610
708
|
);
|
|
611
|
-
|
|
709
|
+
logger2.trace(snapped, "\u{1F7E0} snap");
|
|
612
710
|
} catch (error) {
|
|
613
|
-
|
|
711
|
+
logger2.error(error);
|
|
614
712
|
}
|
|
615
713
|
}
|
|
616
714
|
async function load(me, stream, callback) {
|
|
@@ -634,7 +732,7 @@ async function load(me, stream, callback) {
|
|
|
634
732
|
},
|
|
635
733
|
{ stream, with_snaps: !cached, after: cached?.event_id }
|
|
636
734
|
);
|
|
637
|
-
|
|
735
|
+
logger2.trace(
|
|
638
736
|
state2,
|
|
639
737
|
`\u{1F7E2} load ${stream}${cached && count === 0 ? " (cached)" : ""}`
|
|
640
738
|
);
|
|
@@ -646,7 +744,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
646
744
|
payload = skipValidation ? payload : validate(action2, payload, me.actions[action2]);
|
|
647
745
|
const snapshot = await load(me, stream);
|
|
648
746
|
const expected = expectedVersion || snapshot.event?.version;
|
|
649
|
-
|
|
747
|
+
logger2.trace(
|
|
650
748
|
payload,
|
|
651
749
|
`\u{1F535} ${stream}.${action2}${typeof expected === "number" ? `.${expected}` : ""}`
|
|
652
750
|
);
|
|
@@ -689,7 +787,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
689
787
|
} : void 0
|
|
690
788
|
}
|
|
691
789
|
};
|
|
692
|
-
|
|
790
|
+
logger2.trace(
|
|
693
791
|
emitted.map((e) => e.data),
|
|
694
792
|
`\u{1F534} commit ${stream}.${emitted.map((e) => e.name).join(", ")}`
|
|
695
793
|
);
|
|
@@ -729,6 +827,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
729
827
|
}
|
|
730
828
|
|
|
731
829
|
// src/act.ts
|
|
830
|
+
var logger3 = log();
|
|
732
831
|
var tracer = build_tracer(config().logLevel);
|
|
733
832
|
var Act = class {
|
|
734
833
|
constructor(registry, _states = /* @__PURE__ */ new Map()) {
|
|
@@ -1009,7 +1108,7 @@ var Act = class {
|
|
|
1009
1108
|
if (payloads.length === 0) return { lease, handled: 0, at: lease.at };
|
|
1010
1109
|
const stream = lease.stream;
|
|
1011
1110
|
let at = payloads.at(0).event.id, handled = 0;
|
|
1012
|
-
lease.retry > 0 &&
|
|
1111
|
+
lease.retry > 0 && logger3.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
|
|
1013
1112
|
for (const payload of payloads) {
|
|
1014
1113
|
const { event, handler, options } = payload;
|
|
1015
1114
|
try {
|
|
@@ -1017,9 +1116,9 @@ var Act = class {
|
|
|
1017
1116
|
at = event.id;
|
|
1018
1117
|
handled++;
|
|
1019
1118
|
} catch (error) {
|
|
1020
|
-
|
|
1119
|
+
logger3.error(error);
|
|
1021
1120
|
const block = lease.retry >= options.maxRetries && options.blockOnError;
|
|
1022
|
-
block &&
|
|
1121
|
+
block && logger3.error(`Blocking ${stream} after ${lease.retry} retries.`);
|
|
1023
1122
|
return {
|
|
1024
1123
|
lease,
|
|
1025
1124
|
handled,
|
|
@@ -1163,7 +1262,7 @@ var Act = class {
|
|
|
1163
1262
|
this._needs_drain = false;
|
|
1164
1263
|
return result;
|
|
1165
1264
|
} catch (error) {
|
|
1166
|
-
|
|
1265
|
+
logger3.error(error);
|
|
1167
1266
|
} finally {
|
|
1168
1267
|
this._drain_locked = false;
|
|
1169
1268
|
}
|
|
@@ -1436,7 +1535,7 @@ var Act = class {
|
|
|
1436
1535
|
if (!lastDrain.acked.length && !lastDrain.blocked.length) break;
|
|
1437
1536
|
}
|
|
1438
1537
|
if (lastDrain) this.emit("settled", lastDrain);
|
|
1439
|
-
})().catch((err) =>
|
|
1538
|
+
})().catch((err) => logger3.error(err)).finally(() => {
|
|
1440
1539
|
this._settling = false;
|
|
1441
1540
|
});
|
|
1442
1541
|
}, debounceMs);
|
|
@@ -1483,15 +1582,35 @@ function registerState(state2, states, actions, events) {
|
|
|
1483
1582
|
}
|
|
1484
1583
|
for (const name of Object.keys(state2.events)) {
|
|
1485
1584
|
if (existing.events[name] === state2.events[name]) continue;
|
|
1585
|
+
if (existing.events[name]) continue;
|
|
1486
1586
|
if (events[name]) throw new Error(`Duplicate event "${name}"`);
|
|
1487
1587
|
}
|
|
1588
|
+
const mergedPatch = { ...existing.patch };
|
|
1589
|
+
for (const name of Object.keys(state2.patch)) {
|
|
1590
|
+
const existingP = existing.patch[name];
|
|
1591
|
+
const incomingP = state2.patch[name];
|
|
1592
|
+
if (!existingP) {
|
|
1593
|
+
mergedPatch[name] = incomingP;
|
|
1594
|
+
} else {
|
|
1595
|
+
const existingIsDefault = existingP._passthrough;
|
|
1596
|
+
const incomingIsDefault = incomingP._passthrough;
|
|
1597
|
+
if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
|
|
1598
|
+
throw new Error(
|
|
1599
|
+
`Duplicate custom patch for event "${name}" in state "${state2.name}"`
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
if (existingIsDefault && !incomingIsDefault) {
|
|
1603
|
+
mergedPatch[name] = incomingP;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1488
1607
|
const merged = {
|
|
1489
1608
|
...existing,
|
|
1490
1609
|
state: mergeSchemas(existing.state, state2.state, state2.name),
|
|
1491
1610
|
init: mergeInits(existing.init, state2.init),
|
|
1492
1611
|
events: { ...existing.events, ...state2.events },
|
|
1493
1612
|
actions: { ...existing.actions, ...state2.actions },
|
|
1494
|
-
patch:
|
|
1613
|
+
patch: mergedPatch,
|
|
1495
1614
|
on: { ...existing.on, ...state2.on },
|
|
1496
1615
|
given: { ...existing.given, ...state2.given },
|
|
1497
1616
|
snap: state2.snap || existing.snap
|
|
@@ -1772,10 +1891,11 @@ function state(entry) {
|
|
|
1772
1891
|
return {
|
|
1773
1892
|
emits(events) {
|
|
1774
1893
|
const defaultPatch = Object.fromEntries(
|
|
1775
|
-
Object.keys(events).map((k) =>
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1894
|
+
Object.keys(events).map((k) => {
|
|
1895
|
+
const fn = ({ data }) => data;
|
|
1896
|
+
fn._passthrough = true;
|
|
1897
|
+
return [k, fn];
|
|
1898
|
+
})
|
|
1779
1899
|
);
|
|
1780
1900
|
const builder = action_builder({
|
|
1781
1901
|
events,
|
|
@@ -1856,6 +1976,7 @@ export {
|
|
|
1856
1976
|
CausationEventSchema,
|
|
1857
1977
|
CommittedMetaSchema,
|
|
1858
1978
|
ConcurrencyError,
|
|
1979
|
+
ConsoleLogger,
|
|
1859
1980
|
Environments,
|
|
1860
1981
|
Errors,
|
|
1861
1982
|
EventMetaSchema,
|
|
@@ -1877,7 +1998,7 @@ export {
|
|
|
1877
1998
|
dispose,
|
|
1878
1999
|
disposeAndExit,
|
|
1879
2000
|
extend,
|
|
1880
|
-
|
|
2001
|
+
log,
|
|
1881
2002
|
port,
|
|
1882
2003
|
projection,
|
|
1883
2004
|
sleep,
|