1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
export interface Logger {
log: (...args: unknown[]) => void;
debug: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
error: (...args: unknown[]) => void;
}
type Supplier<T> = () => T;
type TraceSupplier = Supplier<string>;
export interface ITraceableLogger<L extends ITraceableLogger<L>>
extends Logger {
addTracer: (traceSupplier: TraceSupplier) => L;
}
export type ITraceableTuple<T> = [T, TraceSupplier];
export type ITraceableMapper<T, L extends ITraceableLogger<L>, U> = (
t: ITraceable<T, L>,
) => U;
export interface ITraceable<T, L extends ITraceableLogger<L>> {
item: T;
logger: L;
map: <U>(mapper: ITraceableMapper<T, L, U>) => ITraceable<U, L>;
bimap: <U>(
mapper: ITraceableMapper<T, L, ITraceableTuple<U>>,
) => ITraceable<U, L>;
peek: (peek: ITraceableMapper<T, L, void>) => ITraceable<T, L>;
flatMap: <U>(mapper: ITraceableMapper<T, L, ITraceable<U, L>>) => ITraceable<U, L>;
flatMapAsync<U>(mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>): ITraceable<Promise<U>, L>;
}
export class TraceableLogger
implements ITraceableLogger<TraceableLogger> {
private readonly logger: Logger = console;
constructor(
private readonly traces = [() => `[${new Date().toISOString()}]`],
) {
}
public debug(...args: unknown[]) {
this.logger.debug("[DEBUG]", ...this.getPrefix(), args);
}
public log(...args: unknown[]) {
this.logger.log("[INFO]", ...this.getPrefix(), args);
}
public warn(...args: unknown[]) {
this.logger.warn("[WARN]", ...this.getPrefix(), args);
}
public error(...args: unknown[]) {
this.logger.error("[ERROR]", ...this.getPrefix(), args);
}
public addTracer(traceSupplier: TraceSupplier) {
return new TraceableLogger(this.traces.concat(traceSupplier));
}
private getPrefix() {
return this.traces.map((tracer) => tracer());
}
}
export class TraceableImpl<
T,
L extends ITraceableLogger<L>,
> implements ITraceable<T, L> {
private constructor(readonly item: T, readonly logger: L) {}
public map<U>(mapper: ITraceableMapper<T, L, U>) {
const result = mapper(this);
return new TraceableImpl(result, this.logger);
}
public flatMap<U>(mapper: ITraceableMapper<T, L, ITraceable<U, L>>): ITraceable<U, L> {
return mapper(this);
}
public flatMapAsync<U>(mapper: ITraceableMapper<T, L, Promise<ITraceable<U, L>>>): ITraceable<Promise<U>, L> {
return new TraceableImpl(mapper(this).then((i) => )
}
public peek(peek: ITraceableMapper<T, L, void>) {
peek(this);
return this;
}
public bimap<U>(mapper: ITraceableMapper<T, L, ITraceableTuple<U>>) {
const [item, trace] = mapper(this);
return new TraceableImpl(item, this.logger.addTracer(trace));
}
static promiseify<T, L extends ITraceableLogger<L>, U>(
mapper: ITraceableMapper<T, L, U>,
): ITraceableMapper<Promise<T>, L, Promise<U>> {
return (traceablePromise) => traceablePromise.map(
async ({ item: promise }) => {
const t = await promise;
return traceablePromise.map(() => t).map(mapper).item;
});
// return (traceable) =>
// traceable.item.then((item) => mapper(new TraceableImpl(item, traceable.logger)));
}
static from<T>(t: T) {
return new TraceableImpl(t, new TraceableLogger());
}
}
export interface Traceable<T> extends ITraceable<T, TraceableLogger>
|