From 3a3fb9c8ab0c798a278f76d40de216fa96f6e2c4 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Wed, 14 May 2025 18:02:34 -0700 Subject: moar --- hooks/server/activity/health.ts | 0 hooks/server/activity/hook.ts | 0 hooks/server/activity/mod.ts | 8 ---- hooks/server/filter/json.ts | 25 ----------- hooks/server/filter/method.ts | 22 --------- hooks/server/filter/mod.ts | 18 -------- hooks/server/mod.ts | 39 ++++++++++++++++ u/deno.json | 2 +- u/fn/either.ts | 4 +- u/mod.ts | 1 + u/process/env.ts | 2 +- u/process/run.ts | 97 +++++++++++++++++++++++++--------------- u/process/validate_identifier.ts | 19 +++++--- u/server/activity/health.ts | 39 ++++++++++++++++ u/server/activity/mod.ts | 8 ++++ u/server/filter/json.ts | 32 +++++++++++++ u/server/filter/method.ts | 37 +++++++++++++++ u/server/filter/mod.ts | 13 ++++++ u/server/mod.ts | 1 + u/trace/itrace.ts | 68 +++++++++++++++++++--------- u/trace/logger.ts | 67 ++++++++++++++++----------- u/trace/trace.ts | 40 +++++++++++------ 22 files changed, 359 insertions(+), 183 deletions(-) delete mode 100644 hooks/server/activity/health.ts delete mode 100644 hooks/server/activity/hook.ts delete mode 100644 hooks/server/activity/mod.ts delete mode 100644 hooks/server/filter/json.ts delete mode 100644 hooks/server/filter/method.ts delete mode 100644 hooks/server/filter/mod.ts create mode 100644 u/server/activity/health.ts create mode 100644 u/server/activity/mod.ts create mode 100644 u/server/filter/json.ts create mode 100644 u/server/filter/method.ts create mode 100644 u/server/filter/mod.ts diff --git a/hooks/server/activity/health.ts b/hooks/server/activity/health.ts deleted file mode 100644 index e69de29..0000000 diff --git a/hooks/server/activity/hook.ts b/hooks/server/activity/hook.ts deleted file mode 100644 index e69de29..0000000 diff --git a/hooks/server/activity/mod.ts b/hooks/server/activity/mod.ts deleted file mode 100644 index 0dee57b..0000000 --- a/hooks/server/activity/mod.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { RequestFilter } from "../filter/mod.ts"; - -export class r200 extends Response { - public override readonly status = 200; -} - -export interface IActivity extends RequestFilter { -} diff --git a/hooks/server/filter/json.ts b/hooks/server/filter/json.ts deleted file mode 100644 index bcdd3ee..0000000 --- a/hooks/server/filter/json.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Either, type IEither, type Traceable } from "@emprespresso/utils"; -import type { RequestFilter } from "./mod.ts"; - -type JsonTransformer = ( - json: Traceable, -) => IEither; -export const json = ( - jsonTransformer: JsonTransformer, -): RequestFilter => -async (r: Traceable) => { - const { item: request, logger: _logger } = r; - const logger = _logger.addTracer(() => "[jsonVerification]"); - - const getJson = request.json().catch((errReason) => { - const err = "seems to be invalid JSON (>//<) can you fix?"; - logger.warn(err, errReason); - return new Error(err); - }); - return (await Either.fromAsync(getJson)) - .mapRight(r.move).flatMap(jsonTransformer) - .mapLeft((err) => { - logger.warn(err); - return new Response(err.message, { status: 400 }); - }); -}; diff --git a/hooks/server/filter/method.ts b/hooks/server/filter/method.ts deleted file mode 100644 index 07fa5ee..0000000 --- a/hooks/server/filter/method.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Either, type Traceable } from "@liz-ci/utils"; -import type { RequestFilter } from "./mod.ts"; - -type HttpMethod = "POST" | "GET" | "HEAD" | "PUT" | "DELETE"; -export const requireMethod = - (methods: Array): RequestFilter => - (req: Traceable) => { - const { item: request, logger: _logger } = req; - const logger = _logger.addTracer(() => "[aPost]"); - - const { method: _method } = request; - const method = _method; - if (!methods.includes(method)) { - const msg = "that's not how you pet me (⋟﹏⋞)~"; - logger.warn(msg); - return Promise.resolve(Either.left( - new Response(msg + "\n", { status: 405 }), - )); - } - - return Promise.resolve(Either.right(method)); - }; diff --git a/hooks/server/filter/mod.ts b/hooks/server/filter/mod.ts deleted file mode 100644 index bedc678..0000000 --- a/hooks/server/filter/mod.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { - IEither, - ITraceable, - ITraceableLogger, - TraceableLogger, -} from "@liz-ci/utils"; - -export interface RequestFilter< - T, - L extends ITraceableLogger = TraceableLogger, - RIn = ITraceable, - Err = Response, -> { - (req: RIn): Promise>; -} - -export * from "./method.ts"; -export * from "./json.ts"; diff --git a/hooks/server/mod.ts b/hooks/server/mod.ts index e69de29..c484af4 100644 --- a/hooks/server/mod.ts +++ b/hooks/server/mod.ts @@ -0,0 +1,39 @@ +import { Either, type ITraceable } from "@emprespresso/pengueno"; + +const healthCheck = async (in: ITraceable<"healthy?", Trace>) => { + return getRequiredEnv("LAMINAR_HOST").flatMap((_host) => Either.fromFailableAsync( + getStdout(in.move(["laminarc", "show-jobs"])) + )) +} + +export class LizCIServer { + private constructor( + private readonly healthCheckActivity = HealthCheckActivity(healthCheck), + private readonly jobHookActivity = JobHookActivity(jobQueuer) + ) {} + + private async route(req: ITraceable) { + return req.flatMap((req) => { + const { logger, item: { method, pathname } } = req; + if (pathname === "/health") { + return this.healthCheckActivity.healthCheck(req); + } + return this.jobHookActivity.processHook(req); + }); + } + + public async serve(req: Request): Promise { + return LogTraceable(req).bimap(TraceUtil.withClassTrace(this)).map(this.route) + } +} +private route( + req: Traceable, +): Traceable> { + return req.flatMap((req) => { + const { logger, item: { method, pathname } } = req; + if (pathname === "/health") { + return this.healthCheckActivity.healthCheck(req); + } + return this.jobHookActivity.processHook(req); + }); +} diff --git a/u/deno.json b/u/deno.json index 46e74d6..26b08bf 100644 --- a/u/deno.json +++ b/u/deno.json @@ -1,5 +1,5 @@ { - "name": "@emprespresso/utils", + "name": "@emprespresso/pengueno", "version": "0.1.0", "exports": "./mod.ts", "workspace": ["./*"] diff --git a/u/fn/either.ts b/u/fn/either.ts index eaf77fd..12240d0 100644 --- a/u/fn/either.ts +++ b/u/fn/either.ts @@ -1,4 +1,4 @@ -import type { Mapper, Supplier } from "./mod.ts"; +import type { Mapper, Supplier } from "@emprespresso/pengueno"; export interface IEither { mapBoth: ( @@ -21,7 +21,7 @@ export class Either implements IEither { return Either.right(okBranch(this.ok!)); } - public flatMap(mapper: Mapper>) { + public flatMap(mapper: Mapper>): Either { if (this.ok) return mapper(this.ok); return Either.left(this.err!); } diff --git a/u/mod.ts b/u/mod.ts index fab6804..8397ce6 100644 --- a/u/mod.ts +++ b/u/mod.ts @@ -2,3 +2,4 @@ export * from "./fn/mod.ts"; export * from "./leftpadesque/mod.ts"; export * from "./process/mod.ts"; export * from "./trace/mod.ts"; +export * from "./server/mod.ts"; diff --git a/u/process/env.ts b/u/process/env.ts index c3ae800..26e1158 100644 --- a/u/process/env.ts +++ b/u/process/env.ts @@ -1,4 +1,4 @@ -import { Either, type IEither } from "@emprespresso/utils"; +import { Either, type IEither } from "@emprespresso/pengueno"; export const getRequiredEnv = (name: string): IEither => Either diff --git a/u/process/run.ts b/u/process/run.ts index 6dc37d0..670f567 100644 --- a/u/process/run.ts +++ b/u/process/run.ts @@ -1,40 +1,63 @@ -import { Either, type Traceable } from "@emprespresso/utils"; +import { + Either, + type IEither, + type ITraceable, + LogLevel, + TraceUtil, +} from "@emprespresso/pengueno"; + +type Command = string[] | string; +type CommandOutputDecoded = { + code: number; + stdoutText: string; + stderrText: string; +}; export class ProcessError extends Error {} -export const getStdout = async ( - { item: cmd, logger: _logger }: Traceable, +export const getStdout = ( + c: ITraceable, options: Deno.CommandOptions = {}, -): Promise> => { - const logger = _logger.addTracer(() => "[getStdout]"); - - logger.log(`:> im gonna run this command!`, cmd); - const [exec, ...args] = (typeof cmd === "string") ? cmd.split(" ") : cmd; - const command = new Deno.Command(exec, { - args, - stdout: "piped", - stderr: "piped", - ...options, - }); - - try { - const { code, stdout, stderr } = await command.output(); - const stdoutText = new TextDecoder().decode(stdout); - const stderrText = new TextDecoder().decode(stderr); - - if (code !== 0) { - logger.error(`i weceived an exit code of ${code} i wanna zeroooo :<`); - return Either.left( - new ProcessError(`command failed\n${stderrText}`), - ); - } - - logger.log("yay! i got code 0 :3", cmd); - return Either.right(stdoutText); - } catch (e) { - logger.error(`o.o wat`, e); - if (e instanceof Error) { - return Either.left(e); - } - throw new Error("unknown error " + e); - } -}; +): Promise> => + c.bimap(TraceUtil.withFunctionTrace(getStdout)) + .map(({ item: cmd, trace }) => { + trace.trace(`:> im gonna run this command! ${cmd}`); + const [exec, ...args] = (typeof cmd === "string") ? cmd.split(" ") : cmd; + return new Deno.Command(exec, { + args, + stdout: "piped", + stderr: "piped", + ...options, + }).output(); + }) + .map(({ item: p }) => + Either.fromFailableAsync(p) + ) + .map( + TraceUtil.promiseify(({ item: eitherOutput, trace }) => + eitherOutput.flatMap(({ code, stderr, stdout }) => + Either + .fromFailable(() => { + const stdoutText = new TextDecoder().decode(stdout); + const stderrText = new TextDecoder().decode(stderr); + return { code, stdoutText, stderrText }; + }) + .mapLeft((e) => { + trace.addTrace(LogLevel.ERROR).trace(`o.o wat ${e}`); + return new ProcessError(`${e}`); + }) + .flatMap((decodedOutput): Either => { + const { code, stdoutText, stderrText } = decodedOutput; + trace.addTrace(LogLevel.DEBUG).trace( + `stderr hehehe ${stderrText}`, + ); + if (code !== 0) { + const msg = + `i weceived an exit code of ${code} i wanna zewoooo :<`; + trace.addTrace(LogLevel.ERROR).trace(msg); + return Either.left(new ProcessError(msg)); + } + return Either.right(stdoutText); + }) + ) + ), + ).item; diff --git a/u/process/validate_identifier.ts b/u/process/validate_identifier.ts index ec8b77b..32952a6 100644 --- a/u/process/validate_identifier.ts +++ b/u/process/validate_identifier.ts @@ -1,4 +1,4 @@ -import { Either } from "./mod.ts"; +import { Either, type IEither } from "@emprespresso/pengueno"; export const validateIdentifier = (token: string) => { return (/^[a-zA-Z0-9_\-:. \/]+$/).test(token) && !token.includes(".."); @@ -6,11 +6,18 @@ export const validateIdentifier = (token: string) => { // ensure {@param obj} is a Record with stuff that won't // have the potential for shell injection, just to be super safe. -export const validateExecutionEntries = ( - obj: Record, -): Either, Record> => { - const invalidEntries = Object.entries(obj).filter((e) => - !e.every((x) => typeof x === "string" && validateIdentifier(x)) +type InvalidEntry = [K, T]; +export const validateExecutionEntries = < + T, + K extends symbol | number | string = symbol | number | string, +>( + obj: Record, +): IEither< + Array>, + Record +> => { + const invalidEntries = >> Object.entries(obj).filter( + (e) => !e.every((x) => typeof x === "string" && validateIdentifier(x)), ); if (invalidEntries.length > 0) return Either.left(invalidEntries); return Either.right(> obj); diff --git a/u/server/activity/health.ts b/u/server/activity/health.ts new file mode 100644 index 0000000..bf1f52c --- /dev/null +++ b/u/server/activity/health.ts @@ -0,0 +1,39 @@ +import { + Either, + getRequiredEnv, + getStdout, + type ITraceable, + LogLevel, + type Mapper, + TraceUtil, +} from "@emprespresso/pengueno"; + +type HealthCheckInput = "healthy?"; +type HealthCheckOutput = "healthy!"; + +const HealthCheckActivity = ( + check: Mapper< + ITraceable, + Promise> + >, +) => +(req: ITraceable) => + req.bimap(TraceUtil.withFunctionTrace(HealthCheckActivity)) + .flatMap((r) => r.move( "healthy?")) + .map(check) + .map(TraceUtil.promiseify(({ item: health, trace }) => { + health.mapBoth((e) => { + trace.addTrace(LogLevel.ERROR).trace(`${e}`); + return new Response( + "oh no, i need to eat more vegetables (。•́︿•̀。)...\n", + { status: 500 }, + ); + }, (_healthy) => { + const msg = `think im healthy!! (✿˘◡˘) ready to do work~`; + trace.trace(msg); + return new Response( + msg + "\n", + { status: 200 }, + ); + }); + })); diff --git a/u/server/activity/mod.ts b/u/server/activity/mod.ts new file mode 100644 index 0000000..6908c26 --- /dev/null +++ b/u/server/activity/mod.ts @@ -0,0 +1,8 @@ +import type { RequestFilter } from "@emprespresso/pengueno"; + +export class r200 extends Response { + public override readonly status = 200; +} + +export interface IActivity extends RequestFilter { +} diff --git a/u/server/filter/json.ts b/u/server/filter/json.ts new file mode 100644 index 0000000..3f11915 --- /dev/null +++ b/u/server/filter/json.ts @@ -0,0 +1,32 @@ +import { + Either, + type IEither, + type ITraceable, + LogLevel, + type RequestFilter, + TraceUtil, +} from "@emprespresso/pengueno"; + +type JsonTransformer = ( + json: ITraceable, +) => IEither; +export const json = ( + jsonTransformer: JsonTransformer, +): RequestFilter => +(r: ITraceable) => + r.bimap(TraceUtil.withFunctionTrace(json)) + .map(({ item: request }) => Either.fromFailableAsync(request.json())) + .map( + TraceUtil.promiseify(({ item: eitherJson, trace }) => + eitherJson.mapLeft((errReason) => { + trace.addTrace(LogLevel.WARN).trace(`${errReason}`); + const err = "seems to be invalid JSON (>//<) can you fix?"; + return new Error(err); + }) + .flatMap(jsonTransformer) + .mapLeft((err) => { + trace.addTrace(LogLevel.WARN).trace(`${err}`); + return new Response(err.message, { status: 400 }); + }) + ), + ).item; diff --git a/u/server/filter/method.ts b/u/server/filter/method.ts new file mode 100644 index 0000000..2bf45a0 --- /dev/null +++ b/u/server/filter/method.ts @@ -0,0 +1,37 @@ +import { + Either, + type ITraceable, + LogLevel, + type RequestFilter, + TraceUtil, +} from "@emprespresso/pengueno"; + +type HttpMethod = + | "POST" + | "GET" + | "HEAD" + | "PUT" + | "DELETE" + | "CONNECT" + | "OPTIONS" + | "TRACE" + | "PATCH"; + +export const requireMethod = + (methods: Array): RequestFilter => + (req: ITraceable) => + req.bimap(TraceUtil.withFunctionTrace(requireMethod)) + .map(({ item }) => Promise.resolve(item)) + .map(TraceUtil.promiseify(({ item: request, trace }) => { + const { method: _method } = request; + const method = _method; + if (!methods.includes(method)) { + const msg = "that's not how you pet me (⋟﹏⋞)~"; + trace.addTrace(LogLevel.WARN).trace(msg); + return Either.left( + new Response(msg + "\n", { status: 405 }), + ); + } + + return Either.right(method); + })).item; diff --git a/u/server/filter/mod.ts b/u/server/filter/mod.ts new file mode 100644 index 0000000..3256d35 --- /dev/null +++ b/u/server/filter/mod.ts @@ -0,0 +1,13 @@ +import type { IEither, ITraceable } from "@emprespresso/pengueno"; + +export interface RequestFilter< + T, + Trace, + RIn = ITraceable, + Err = Response, +> { + (req: RIn): Promise>; +} + +export * from "./method.ts"; +export * from "./json.ts"; diff --git a/u/server/mod.ts b/u/server/mod.ts index e69de29..556771d 100644 --- a/u/server/mod.ts +++ b/u/server/mod.ts @@ -0,0 +1 @@ +export * from "./filter/mod.ts"; diff --git a/u/trace/itrace.ts b/u/trace/itrace.ts index b483067..b9b750d 100644 --- a/u/trace/itrace.ts +++ b/u/trace/itrace.ts @@ -1,71 +1,95 @@ -import { Mapper, SideEffect } from "../fn/mod.ts"; +import type { Mapper, SideEffect } from "@emprespresso/pengueno"; -export interface ITrace { - addTrace: Mapper>; - trace: SideEffect; +// the "thing" every Trace writer must "trace()" +type BaseTraceWith = string; +export type ITraceWith = BaseTraceWith | T; +export interface ITrace { + addTrace: Mapper, ITrace>; + trace: SideEffect>; } -export type ITraceableTuple = [T, Trace]; -export type ITraceableMapper> = ( +export type ITraceableTuple = [T, BaseTraceWith | TraceWith]; +export type ITraceableMapper< + T, + U, + TraceWith, + W = ITraceable, +> = ( w: W, ) => U; -export interface ITraceable { +export interface ITraceable { readonly item: T; readonly trace: ITrace; move(u: U): ITraceable; map: ( - mapper: ITraceableMapper, + mapper: ITraceableMapper, ) => ITraceable; bimap: ( - mapper: ITraceableMapper>, + mapper: ITraceableMapper, Trace>, ) => ITraceable; - peek: (peek: ITraceableMapper) => ITraceable; + peek: (peek: ITraceableMapper) => ITraceable; flatMap: ( - mapper: ITraceableMapper>, + mapper: ITraceableMapper, Trace>, ) => ITraceable; flatMapAsync( - mapper: ITraceableMapper>>, + mapper: ITraceableMapper>, Trace>, ): ITraceable, Trace>; } -export class TraceableImpl implements ITraceable { +export class TraceableImpl implements ITraceable { protected constructor( readonly item: T, - readonly trace: ITrace, + readonly trace: ITrace, ) {} - public map(mapper: ITraceableMapper) { + public map( + mapper: ITraceableMapper, + ) { const result = mapper(this); return new TraceableImpl(result, this.trace); } public flatMap( - mapper: ITraceableMapper>, - ): ITraceable { + mapper: ITraceableMapper< + T, + ITraceable, + TraceWith + >, + ): ITraceable { return mapper(this); } public flatMapAsync( - mapper: ITraceableMapper>>, - ): ITraceable, L> { + mapper: ITraceableMapper< + T, + Promise>, + TraceWith + >, + ): ITraceable, TraceWith> { return new TraceableImpl( mapper(this).then(({ item }) => item), this.trace, ); } - public peek(peek: ITraceableMapper) { + public peek(peek: ITraceableMapper) { peek(this); return this; } - public move(t: Tt): ITraceable { + public move(t: Tt): ITraceable { return this.map(() => t); } - public bimap(mapper: ITraceableMapper>) { + public bimap( + mapper: ITraceableMapper< + T, + ITraceableTuple, + TraceWith + >, + ) { const [item, trace] = mapper(this); return new TraceableImpl(item, this.trace.addTrace(trace)); } diff --git a/u/trace/logger.ts b/u/trace/logger.ts index 79da367..4f3c856 100644 --- a/u/trace/logger.ts +++ b/u/trace/logger.ts @@ -1,10 +1,9 @@ import { isDebug, - isObject, type ITrace, type SideEffect, type Supplier, -} from "@emprespresso/utils"; +} from "@emprespresso/pengueno"; export interface ILogger { log: (...args: unknown[]) => void; @@ -12,74 +11,88 @@ export interface ILogger { warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void; } -export type ILoggerLevel = "UNKNOWN" | "INFO" | "WARN" | "DEBUG" | "ERROR"; -const logLevelOrder: Array = ["DEBUG", "INFO", "WARN", "ERROR"]; +export enum LogLevel { + UNKNOWN = "UNKNOWN", + INFO = "INFO", + WARN = "WARN", + DEBUG = "DEBUG", + ERROR = "ERROR", +} +const logLevelOrder: Array = [ + LogLevel.DEBUG, + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, +]; +export const isLogLevel = (l: string): l is LogLevel => + logLevelOrder.some((level) => level === l); + const defaultAllowedLevels = () => [ - "UNKNOWN", - ...(isDebug() ? ["DEBUG"] : []), - "INFO", - "WARN", - "ERROR", - ] as Array; + LogLevel.UNKNOWN, + ...(isDebug() ? [LogLevel.DEBUG] : []), + LogLevel.INFO, + LogLevel.WARN, + LogLevel.ERROR, + ] as Array; export const logWithLevel = ( logger: ILogger, - level: ILoggerLevel, + level: LogLevel, ): SideEffect => { switch (level) { - case "UNKNOWN": - case "INFO": + case LogLevel.UNKNOWN: + case LogLevel.INFO: return logger.log; - case "DEBUG": + case LogLevel.DEBUG: return logger.debug; - case "WARN": + case LogLevel.WARN: return logger.warn; - case "ERROR": + case LogLevel.ERROR: return logger.error; } }; export const LoggerImpl = console; -export type LogTraceSupplier = string | Supplier | { - level: ILoggerLevel; -}; +export type LogTraceSupplier = string | Supplier; const foldTraces = (traces: Array) => { const { line, level } = traces.reduce( (acc: { line: string; level: number }, t) => { - if (isObject(t) && "level" in t) { + const val = typeof t === "function" ? t() : t; + if (isLogLevel(val)) { return { ...acc, - level: Math.max(logLevelOrder.indexOf(t.level), acc.level), + level: Math.max(logLevelOrder.indexOf(val), acc.level), }; } const prefix = [ acc.line, - typeof t === "function" ? t() : t, + val, ].join(" "); return { ...acc, prefix }; }, { line: "", level: -1 }, ); - return { line, level: logLevelOrder[level] ?? "UNKNOWN" }; + return { line, level: logLevelOrder[level] ?? LogLevel.UNKNOWN }; }; const defaultTrace = () => `[${new Date().toISOString()}]`; export const LogTrace = ( logger: ILogger, traces: Array = [defaultTrace], - allowedLevels: Supplier> = defaultAllowedLevels, - defaultLevel: ILoggerLevel = "INFO", + allowedLevels: Supplier> = defaultAllowedLevels, + defaultLevel: LogLevel = LogLevel.INFO, ): ITrace => { return { addTrace: (trace: LogTraceSupplier) => - LogTrace(logger, traces.concat(trace)), + LogTrace(logger, traces.concat(trace), allowedLevels, defaultLevel), trace: (trace: LogTraceSupplier) => { const { line, level: _level } = foldTraces(traces.concat(trace)); if (!allowedLevels().includes(_level)) return; - const level = _level === "UNKNOWN" ? defaultLevel : _level; + + const level = _level === LogLevel.UNKNOWN ? defaultLevel : _level; logWithLevel(logger, level)(`[${level}]${line}`); }, }; diff --git a/u/trace/trace.ts b/u/trace/trace.ts index 5d5c59b..1d3d2d8 100644 --- a/u/trace/trace.ts +++ b/u/trace/trace.ts @@ -1,31 +1,43 @@ -import type { Callable } from "@emprespresso/utils"; import { + type Callable, type ITraceableMapper, type ITraceableTuple, + LoggerImpl, + LogTrace, + type LogTraceSupplier, TraceableImpl, - TraceableLogger, -} from "./mod.ts"; +} from "@emprespresso/pengueno"; -export class Traceable extends TraceableImpl { +export class LogTraceable extends TraceableImpl { static from(t: T) { - return new Traceable(t, new TraceableLogger()); + return new LogTraceable(t, LogTrace(LoggerImpl)); } +} - static withFunctionTrace( +export class TraceUtil { + static withFunctionTrace( f: F, - ): ITraceableMapper> { - return (t) => [t.item, f.name]; + ): ITraceableMapper< + T, + ITraceableTuple, + Trace + > { + return (t) => [t.item, `[${f.name}]`]; } - static withClassTrace( + static withClassTrace( c: C, - ): ITraceableMapper> { - return (t) => [t.item, c.constructor.name]; + ): ITraceableMapper< + T, + ITraceableTuple, + Trace + > { + return (t) => [t.item, `[${c.constructor.name}]`]; } - static promiseify( - mapper: ITraceableMapper, - ): ITraceableMapper, TraceableLogger, Promise> { + static promiseify( + mapper: ITraceableMapper, + ): ITraceableMapper, Promise, Trace> { return (traceablePromise) => traceablePromise.flatMapAsync(async (t) => t.move(await t.item).map(mapper) -- cgit v1.2.3-70-g09d2