summaryrefslogtreecommitdiff
path: root/utils/trace.ts
diff options
context:
space:
mode:
Diffstat (limited to 'utils/trace.ts')
-rw-r--r--utils/trace.ts111
1 files changed, 111 insertions, 0 deletions
diff --git a/utils/trace.ts b/utils/trace.ts
new file mode 100644
index 0000000..737aa60
--- /dev/null
+++ b/utils/trace.ts
@@ -0,0 +1,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>