summaryrefslogtreecommitdiff
path: root/u
diff options
context:
space:
mode:
Diffstat (limited to 'u')
-rw-r--r--u/deno.json5
-rw-r--r--u/fn/callable.ts8
-rw-r--r--u/fn/either.ts180
-rw-r--r--u/fn/index.ts2
-rw-r--r--u/fn/mod.ts2
-rw-r--r--u/index.ts6
-rw-r--r--u/leftpadesque/debug.ts15
-rw-r--r--u/leftpadesque/index.ts4
-rw-r--r--u/leftpadesque/memoize.ts22
-rw-r--r--u/leftpadesque/mod.ts4
-rw-r--r--u/leftpadesque/object.ts3
-rw-r--r--u/leftpadesque/prepend.ts8
-rw-r--r--u/mod.ts6
-rw-r--r--u/package.json47
-rw-r--r--u/process/argv.ts77
-rw-r--r--u/process/env.ts53
-rw-r--r--u/process/index.ts4
-rw-r--r--u/process/mod.ts4
-rw-r--r--u/process/run.ts102
-rw-r--r--u/process/validate_identifier.ts23
-rw-r--r--u/server/activity/fourohfour.ts37
-rw-r--r--u/server/activity/health.ts108
-rw-r--r--u/server/activity/index.ts8
-rw-r--r--u/server/activity/mod.ts13
-rw-r--r--u/server/filter/index.ts34
-rw-r--r--u/server/filter/json.ts92
-rw-r--r--u/server/filter/method.ts67
-rw-r--r--u/server/filter/mod.ts35
-rw-r--r--u/server/index.ts7
-rw-r--r--u/server/mod.ts7
-rw-r--r--u/server/request.ts74
-rw-r--r--u/server/response.ts139
-rw-r--r--u/trace/index.ts5
-rw-r--r--u/trace/itrace.ts120
-rw-r--r--u/trace/logger.ts169
-rw-r--r--u/trace/metrics.ts229
-rw-r--r--u/trace/mod.ts5
-rw-r--r--u/trace/trace.ts117
-rw-r--r--u/trace/util.ts76
-rw-r--r--u/tsconfig.json15
40 files changed, 915 insertions, 1017 deletions
diff --git a/u/deno.json b/u/deno.json
deleted file mode 100644
index b277873..0000000
--- a/u/deno.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "@emprespresso/pengueno",
- "version": "0.1.0",
- "exports": "./mod.ts"
-}
diff --git a/u/fn/callable.ts b/u/fn/callable.ts
index cfb7d00..8a61057 100644
--- a/u/fn/callable.ts
+++ b/u/fn/callable.ts
@@ -1,18 +1,18 @@
// deno-lint-ignore no-explicit-any
export interface Callable<T = any, ArgT = any> {
- (...args: Array<ArgT>): T;
+ (...args: Array<ArgT>): T;
}
export interface Supplier<T> extends Callable<T, undefined> {
- (): T;
+ (): T;
}
export interface Mapper<T, U> extends Callable<U, T> {
- (t: T): U;
+ (t: T): U;
}
export interface BiMapper<T, U, R> extends Callable {
- (t: T, u: U): R;
+ (t: T, u: U): R;
}
export interface SideEffect<T> extends Mapper<T, void> {}
diff --git a/u/fn/either.ts b/u/fn/either.ts
index ffe8033..8c47b64 100644
--- a/u/fn/either.ts
+++ b/u/fn/either.ts
@@ -1,114 +1,100 @@
-import { type Mapper, type Supplier, isObject } from "@emprespresso/pengueno";
+import { type Mapper, type Supplier, isObject } from '@emprespresso/pengueno';
-type IEitherTag = "IEither";
-const iEitherTag: IEitherTag = "IEither";
+type IEitherTag = 'IEither';
+const iEitherTag: IEitherTag = 'IEither';
export interface _Either<LeftT, RightT, T> {
- readonly isLeft: LeftT;
- readonly isRight: RightT;
- readonly value: T;
+ readonly isLeft: LeftT;
+ readonly isRight: RightT;
+ readonly value: T;
}
export type Left<E> = _Either<true, false, E>;
export type Right<T> = _Either<false, true, T>;
export interface IEither<E, T> {
- readonly _tag: IEitherTag;
-
- mapBoth: <_E, _T>(
- errBranch: Mapper<E, _E>,
- okBranch: Mapper<T, _T>,
- ) => IEither<_E, _T>;
- fold: <_T>(folder: Mapper<Left<E> | Right<T>, _T>) => _T;
- moveRight: <_T>(t: _T) => IEither<E, _T>;
- mapRight: <_T>(mapper: Mapper<T, _T>) => IEither<E, _T>;
- mapLeft: <_E>(mapper: Mapper<E, _E>) => IEither<_E, T>;
- flatMap: <_T>(mapper: Mapper<T, IEither<E, _T>>) => IEither<E, _T>;
- flatMapAsync: <_T>(
- mapper: Mapper<T, Promise<IEither<E, _T>>>,
- ) => Promise<IEither<E, _T>>;
+ readonly _tag: IEitherTag;
+
+ mapBoth: <_E, _T>(errBranch: Mapper<E, _E>, okBranch: Mapper<T, _T>) => IEither<_E, _T>;
+ fold: <_T>(folder: Mapper<Left<E> | Right<T>, _T>) => _T;
+ moveRight: <_T>(t: _T) => IEither<E, _T>;
+ mapRight: <_T>(mapper: Mapper<T, _T>) => IEither<E, _T>;
+ mapLeft: <_E>(mapper: Mapper<E, _E>) => IEither<_E, T>;
+ flatMap: <_T>(mapper: Mapper<T, IEither<E, _T>>) => IEither<E, _T>;
+ flatMapAsync: <_T>(mapper: Mapper<T, Promise<IEither<E, _T>>>) => Promise<IEither<E, _T>>;
}
export class Either<E, T> implements IEither<E, T> {
- private readonly self: Left<E> | Right<T>;
-
- private constructor(
- init: { err?: E; ok?: T },
- public readonly _tag: IEitherTag = iEitherTag,
- ) {
- this.self = <Left<E> | Right<T>>{
- isLeft: "err" in init,
- isRight: "ok" in init,
- value: init.err ?? init.ok!,
- };
- }
-
- public moveRight<_T>(t: _T) {
- return this.mapRight(() => t);
- }
-
- public fold<_T>(folder: Mapper<Left<E> | Right<T>, _T>): _T {
- return folder(this.self);
- }
-
- public mapBoth<_E, _T>(
- errBranch: Mapper<E, _E>,
- okBranch: Mapper<T, _T>,
- ): IEither<_E, _T> {
- if (this.self.isLeft) return Either.left(errBranch(this.self.value));
- return Either.right(okBranch(this.self.value));
- }
-
- public flatMap<_T>(mapper: Mapper<T, IEither<E, _T>>): IEither<E, _T> {
- if (this.self.isRight) return mapper(this.self.value);
- return Either.left<E, _T>(this.self.value);
- }
-
- public mapRight<_T>(mapper: Mapper<T, _T>): IEither<E, _T> {
- if (this.self.isRight) return Either.right<E, _T>(mapper(this.self.value));
- return Either.left<E, _T>(this.self.value);
- }
-
- public mapLeft<_E>(mapper: Mapper<E, _E>): IEither<_E, T> {
- if (this.self.isLeft) return Either.left<_E, T>(mapper(this.self.value));
- return Either.right<_E, T>(this.self.value);
- }
-
- public async flatMapAsync<_T>(
- mapper: Mapper<T, Promise<IEither<E, _T>>>,
- ): Promise<IEither<E, _T>> {
- if (this.self.isLeft) {
- return Promise.resolve(Either.left<E, _T>(this.self.value));
+ private readonly self: Left<E> | Right<T>;
+
+ private constructor(
+ init: { err?: E; ok?: T },
+ public readonly _tag: IEitherTag = iEitherTag,
+ ) {
+ this.self = <Left<E> | Right<T>>{
+ isLeft: 'err' in init,
+ isRight: 'ok' in init,
+ value: init.err ?? init.ok!,
+ };
}
- return await mapper(this.self.value).catch((err) =>
- Either.left<E, _T>(err),
- );
- }
-
- static left<E, T>(e: E): IEither<E, T> {
- return new Either<E, T>({ err: e });
- }
-
- static right<E, T>(t: T): IEither<E, T> {
- return new Either<E, T>({ ok: t });
- }
-
- static fromFailable<E, T>(s: Supplier<T>): IEither<E, T> {
- try {
- return Either.right<E, T>(s());
- } catch (e) {
- return Either.left<E, T>(e as E);
+
+ public moveRight<_T>(t: _T) {
+ return this.mapRight(() => t);
+ }
+
+ public fold<_T>(folder: Mapper<Left<E> | Right<T>, _T>): _T {
+ return folder(this.self);
+ }
+
+ public mapBoth<_E, _T>(errBranch: Mapper<E, _E>, okBranch: Mapper<T, _T>): IEither<_E, _T> {
+ if (this.self.isLeft) return Either.left(errBranch(this.self.value));
+ return Either.right(okBranch(this.self.value));
+ }
+
+ public flatMap<_T>(mapper: Mapper<T, IEither<E, _T>>): IEither<E, _T> {
+ if (this.self.isRight) return mapper(this.self.value);
+ return Either.left<E, _T>(this.self.value);
+ }
+
+ public mapRight<_T>(mapper: Mapper<T, _T>): IEither<E, _T> {
+ if (this.self.isRight) return Either.right<E, _T>(mapper(this.self.value));
+ return Either.left<E, _T>(this.self.value);
+ }
+
+ public mapLeft<_E>(mapper: Mapper<E, _E>): IEither<_E, T> {
+ if (this.self.isLeft) return Either.left<_E, T>(mapper(this.self.value));
+ return Either.right<_E, T>(this.self.value);
+ }
+
+ public async flatMapAsync<_T>(mapper: Mapper<T, Promise<IEither<E, _T>>>): Promise<IEither<E, _T>> {
+ if (this.self.isLeft) {
+ return Promise.resolve(Either.left<E, _T>(this.self.value));
+ }
+ return await mapper(this.self.value).catch((err) => Either.left<E, _T>(err));
+ }
+
+ static left<E, T>(e: E): IEither<E, T> {
+ return new Either<E, T>({ err: e });
+ }
+
+ static right<E, T>(t: T): IEither<E, T> {
+ return new Either<E, T>({ ok: t });
+ }
+
+ static fromFailable<E, T>(s: Supplier<T>): IEither<E, T> {
+ try {
+ return Either.right<E, T>(s());
+ } catch (e) {
+ return Either.left<E, T>(e as E);
+ }
+ }
+
+ static async fromFailableAsync<E, T>(s: Supplier<Promise<T>> | Promise<T>): Promise<IEither<E, T>> {
+ return await (typeof s === 'function' ? s() : s)
+ .then((t: T) => Either.right<E, T>(t))
+ .catch((e: E) => Either.left<E, T>(e));
}
- }
-
- static async fromFailableAsync<E, T>(
- s: Supplier<Promise<T>>,
- ): Promise<IEither<E, T>> {
- return await s()
- .then((t: T) => Either.right<E, T>(t))
- .catch((e: E) => Either.left<E, T>(e));
- }
}
export const isEither = <E, T>(o: unknown): o is IEither<E, T> => {
- return isObject(o) && "_tag" in o && o._tag === "IEither";
+ return isObject(o) && '_tag' in o && o._tag === 'IEither';
};
diff --git a/u/fn/index.ts b/u/fn/index.ts
new file mode 100644
index 0000000..1ec71aa
--- /dev/null
+++ b/u/fn/index.ts
@@ -0,0 +1,2 @@
+export * from './callable.js';
+export * from './either.js';
diff --git a/u/fn/mod.ts b/u/fn/mod.ts
deleted file mode 100644
index f0fbe88..0000000
--- a/u/fn/mod.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from "./callable.ts";
-export * from "./either.ts";
diff --git a/u/index.ts b/u/index.ts
new file mode 100644
index 0000000..0c8c760
--- /dev/null
+++ b/u/index.ts
@@ -0,0 +1,6 @@
+export * from './fn/index.js';
+export * from './leftpadesque/index.js';
+export * from './process/index.js';
+export * from './trace/index.js';
+export * from './server/index.js';
+export * from './history.js';
diff --git a/u/leftpadesque/debug.ts b/u/leftpadesque/debug.ts
index e50b2e0..074e567 100644
--- a/u/leftpadesque/debug.ts
+++ b/u/leftpadesque/debug.ts
@@ -1,13 +1,8 @@
-const _hasEnv = !Deno.permissions.querySync({ name: "env" });
+const _hasEnv = true; // Node.js always has access to environment variables
-const _env: "development" | "production" =
- _hasEnv && (Deno.env.get("ENVIRONMENT") ?? "").toLowerCase().includes("prod")
- ? "production"
- : "development";
-export const isProd = () => _env === "production";
+const _env: 'development' | 'production' =
+ _hasEnv && (process.env.ENVIRONMENT ?? '').toLowerCase().includes('prod') ? 'production' : 'development';
+export const isProd = () => _env === 'production';
-const _debug =
- !isProd() ||
- (_hasEnv &&
- ["y", "t"].some((Deno.env.get("DEBUG") ?? "").toLowerCase().startsWith));
+const _debug = !isProd() || (_hasEnv && ['y', 't'].some((process.env.DEBUG ?? '').toLowerCase().startsWith));
export const isDebug = () => _debug;
diff --git a/u/leftpadesque/index.ts b/u/leftpadesque/index.ts
new file mode 100644
index 0000000..6403e4a
--- /dev/null
+++ b/u/leftpadesque/index.ts
@@ -0,0 +1,4 @@
+export * from './object.js';
+export * from './prepend.js';
+export * from './debug.js';
+export * from './memoize.js';
diff --git a/u/leftpadesque/memoize.ts b/u/leftpadesque/memoize.ts
index 95e6019..541bd20 100644
--- a/u/leftpadesque/memoize.ts
+++ b/u/leftpadesque/memoize.ts
@@ -1,14 +1,14 @@
-import type { Callable } from "@emprespresso/pengueno";
+import type { Callable } from '@emprespresso/pengueno';
export const memoize = <R, F extends Callable<R>>(fn: F): F => {
- const cache = new Map<string, R>();
- return ((...args: unknown[]): R => {
- const key = JSON.stringify(args);
- if (cache.has(key)) {
- return cache.get(key)!;
- }
- const res = fn.apply(args);
- cache.set(key, res);
- return res;
- }) as F;
+ const cache = new Map<string, R>();
+ return ((...args: unknown[]): R => {
+ const key = JSON.stringify(args);
+ if (cache.has(key)) {
+ return cache.get(key)!;
+ }
+ const res = fn.apply(args);
+ cache.set(key, res);
+ return res;
+ }) as F;
};
diff --git a/u/leftpadesque/mod.ts b/u/leftpadesque/mod.ts
deleted file mode 100644
index 63d8d7a..0000000
--- a/u/leftpadesque/mod.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from "./object.ts";
-export * from "./prepend.ts";
-export * from "./debug.ts";
-export * from "./memoize.ts";
diff --git a/u/leftpadesque/object.ts b/u/leftpadesque/object.ts
index 73f7f80..fe97999 100644
--- a/u/leftpadesque/object.ts
+++ b/u/leftpadesque/object.ts
@@ -1,2 +1 @@
-export const isObject = (o: unknown): o is object =>
- typeof o === "object" && !Array.isArray(o) && !!o;
+export const isObject = (o: unknown): o is object => typeof o === 'object' && !Array.isArray(o) && !!o;
diff --git a/u/leftpadesque/prepend.ts b/u/leftpadesque/prepend.ts
index 0f1ce30..d80f6b6 100644
--- a/u/leftpadesque/prepend.ts
+++ b/u/leftpadesque/prepend.ts
@@ -1,5 +1,5 @@
export const prependWith = (arr: string[], prep: string) =>
- Array(arr.length * 2)
- .fill(0)
- .map((_, i) => i % 2 === 0)
- .map((isPrep, i) => (isPrep ? prep : arr[i]));
+ Array(arr.length * 2)
+ .fill(0)
+ .map((_, i) => i % 2 === 0)
+ .map((isPrep, i) => (isPrep ? prep : arr[i]!));
diff --git a/u/mod.ts b/u/mod.ts
deleted file mode 100644
index 2ab8f68..0000000
--- a/u/mod.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-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";
-export * from "./history.ts";
diff --git a/u/package.json b/u/package.json
new file mode 100644
index 0000000..d38ac4c
--- /dev/null
+++ b/u/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "@emprespresso/pengueno",
+ "version": "0.1.0",
+ "type": "module",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ },
+ "./fn": {
+ "types": "./dist/fn/index.d.ts",
+ "import": "./dist/fn/index.js"
+ },
+ "./leftpadesque": {
+ "types": "./dist/leftpadesque/index.d.ts",
+ "import": "./dist/leftpadesque/index.js"
+ },
+ "./process": {
+ "types": "./dist/process/index.d.ts",
+ "import": "./dist/process/index.js"
+ },
+ "./trace": {
+ "types": "./dist/trace/index.d.ts",
+ "import": "./dist/trace/index.js"
+ },
+ "./server": {
+ "types": "./dist/server/index.d.ts",
+ "import": "./dist/server/index.js"
+ }
+ },
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "clean": "rm -rf dist",
+ "type-check": "tsc --noEmit"
+ },
+ "dependencies": {
+ "hono": "^4.8.0"
+ },
+ "files": [
+ "dist/**/*",
+ "package.json",
+ "README.md"
+ ]
+}
diff --git a/u/process/argv.ts b/u/process/argv.ts
index 45f8ff0..dcdba85 100644
--- a/u/process/argv.ts
+++ b/u/process/argv.ts
@@ -1,7 +1,6 @@
-import { Either, type Mapper, type IEither } from "@emprespresso/pengueno";
+import { Either, type Mapper, type IEither } from '@emprespresso/pengueno';
-export const isArgKey = <K extends string>(k: string): k is K =>
- k.startsWith("--");
+export const isArgKey = <K extends string>(k: string): k is K => k.startsWith('--');
interface ArgHandler<V> {
absent?: V;
@@ -10,42 +9,42 @@ interface ArgHandler<V> {
}
export const getArg = <K extends string, V>(
- arg: K,
- argv: Array<string>,
- whenValue: ArgHandler<V>,
+ arg: K,
+ argv: Array<string>,
+ whenValue: ArgHandler<V>,
): IEither<Error, V> => {
- const value = argv.filter((_argv) => isArgKey(_argv) && _argv.split("=")[0] === arg).map((_argv, i) => {
- const next = _argv.includes("=") ? _argv.split("=")[1] : argv.at(i + 1);
- if (next) {
- if (isArgKey(next)) return whenValue.unspecified;
- return whenValue.present(next);
- }
- return whenValue.unspecified;
- }).find(x => x) ?? whenValue.absent;
+ const value =
+ argv
+ .filter((_argv) => isArgKey(_argv) && _argv.split('=')[0] === arg)
+ .map((_argv, i) => {
+ const next = _argv.includes('=') ? _argv.split('=')[1] : argv.at(i + 1);
+ if (next) {
+ if (isArgKey(next)) return whenValue.unspecified;
+ return whenValue.present(next);
+ }
+ return whenValue.unspecified;
+ })
+ .find((x) => x) ?? whenValue.absent;
if (value === undefined) {
- return Either.left(new Error("no value specified for " + arg));
+ return Either.left(new Error('no value specified for ' + arg));
}
return Either.right(value);
};
type MappedArgs<
- Args extends ReadonlyArray<string>,
- Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>
+ Args extends ReadonlyArray<string>,
+ Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>,
> = {
- [K in Args[number]]: K extends keyof Handlers
- ? Handlers[K] extends ArgHandler<infer T>
- ? T
- : string
- : string;
+ [K in Args[number]]: K extends keyof Handlers ? (Handlers[K] extends ArgHandler<infer T> ? T : string) : string;
};
export const argv = <
- const Args extends ReadonlyArray<string>,
- const Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>
+ const Args extends ReadonlyArray<string>,
+ const Handlers extends Partial<Record<Args[number], ArgHandler<unknown>>>,
>(
args: Args,
handlers?: Handlers,
- argv = Deno.args,
+ argv = process.argv.slice(2),
): IEither<Error, MappedArgs<Args, Handlers>> => {
type Result = MappedArgs<Args, Handlers>;
@@ -53,20 +52,20 @@ export const argv = <
const processArg = (arg: Args[number]): IEither<Error, [Args[number], unknown]> => {
const handler = handlers?.[arg] ?? defaultHandler;
- return getArg(arg, argv, handler).mapRight(value => [arg, value] as const);
+ return getArg(arg, argv, handler).mapRight((value) => [arg, value] as const);
};
- const argResults = args.map(processArg);
-
- return argResults.reduce(
- (acc: IEither<Error, Partial<Result>>, current: IEither<Error, readonly [Args[number], unknown]>) => {
- return acc.flatMap(accValue =>
- current.mapRight(([key, value]) => ({
- ...accValue,
- [key]: value
- }))
- );
- },
- Either.right({} as Partial<Result>)
- ).mapRight(result => result as Result);
+ return args
+ .map(processArg)
+ .reduce(
+ (acc: IEither<Error, Partial<Result>>, current: IEither<Error, readonly [Args[number], unknown]>) =>
+ acc.flatMap((accValue) =>
+ current.mapRight(([key, value]) => ({
+ ...accValue,
+ [key]: value,
+ })),
+ ),
+ Either.right(<Partial<Result>>{}),
+ )
+ .mapRight((result) => <Result>result);
};
diff --git a/u/process/env.ts b/u/process/env.ts
index 5ba4189..1e4fd32 100644
--- a/u/process/env.ts
+++ b/u/process/env.ts
@@ -1,37 +1,30 @@
-import { Either, type IEither } from "@emprespresso/pengueno";
+import { Either, type IEither } from '@emprespresso/pengueno';
export const getRequiredEnv = <V extends string>(name: V): IEither<Error, V> =>
- Either.fromFailable<Error, V | undefined>(
- () => Deno.env.get(name) as V | undefined,
- ) // could throw when no permission.
- .flatMap(
- (v) =>
- (v && Either.right(v)) ||
- Either.left(new Error(`environment variable "${name}" is required D:`)),
- );
+ Either.fromFailable<Error, V | undefined>(() => process.env[name] as V | undefined) // could throw when no permission.
+ .flatMap(
+ (v) => (v && Either.right(v)) || Either.left(new Error(`environment variable "${name}" is required D:`)),
+ );
type ObjectFromList<T extends ReadonlyArray<string>, V = string> = {
- [K in T extends ReadonlyArray<infer U> ? U : never]: V;
+ [K in T extends ReadonlyArray<infer U> ? U : never]: V;
};
export const getRequiredEnvVars = <V extends string>(vars: ReadonlyArray<V>) =>
- vars
- .map((envVar) => [envVar, getRequiredEnv(envVar)] as [V, IEither<Error, V>])
- .reduce(
- (
- acc: IEither<Error, ObjectFromList<typeof vars>>,
- x: [V, IEither<Error, V>],
- ) => {
- const [envVar, eitherVal] = x;
- return acc.flatMap((args) => {
- return eitherVal.mapRight(
- (envValue) =>
- ({
- ...args,
- [envVar]: envValue,
- }) as ObjectFromList<typeof vars>,
- );
- });
- },
- Either.right({} as ObjectFromList<typeof vars>),
- );
+ vars
+ .map((envVar) => [envVar, getRequiredEnv(envVar)] as [V, IEither<Error, V>])
+ .reduce(
+ (acc: IEither<Error, ObjectFromList<typeof vars>>, x: [V, IEither<Error, V>]) => {
+ const [envVar, eitherVal] = x;
+ return acc.flatMap((args) => {
+ return eitherVal.mapRight(
+ (envValue) =>
+ ({
+ ...args,
+ [envVar]: envValue,
+ }) as ObjectFromList<typeof vars>,
+ );
+ });
+ },
+ Either.right({} as ObjectFromList<typeof vars>),
+ );
diff --git a/u/process/index.ts b/u/process/index.ts
new file mode 100644
index 0000000..4ffbf2a
--- /dev/null
+++ b/u/process/index.ts
@@ -0,0 +1,4 @@
+export * from './env.js';
+export * from './run.js';
+export * from './validate_identifier.js';
+export * from './argv.js';
diff --git a/u/process/mod.ts b/u/process/mod.ts
deleted file mode 100644
index 211e9a7..0000000
--- a/u/process/mod.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from "./env.ts";
-export * from "./run.ts";
-export * from "./validate_identifier.ts";
-export * from "./argv.ts";
diff --git a/u/process/run.ts b/u/process/run.ts
index abe143c..e3c4c3d 100644
--- a/u/process/run.ts
+++ b/u/process/run.ts
@@ -1,68 +1,46 @@
import {
- Either,
- type IEither,
- type ITraceable,
- LogLevel,
- type LogTraceSupplier,
- TraceUtil,
-} from "@emprespresso/pengueno";
+ Either,
+ type IEither,
+ type ITraceable,
+ LogLevel,
+ type LogTraceSupplier,
+ TraceUtil,
+} from '@emprespresso/pengueno';
+import { promisify } from 'node:util';
+import { exec as execCallback } from 'node:child_process';
+const exec = promisify(execCallback);
export type Command = string[] | string;
-type CommandOutputDecoded = {
- code: number;
- stdoutText: string;
- stderrText: string;
-};
+export type StdStreams = { stdout: string; stderr: string };
export const getStdout = (
- c: ITraceable<Command, LogTraceSupplier>,
- options: Deno.CommandOptions = {},
+ c: ITraceable<Command, LogTraceSupplier>,
+ options: { env?: Record<string, string>; clearEnv?: boolean } = {},
): Promise<IEither<Error, string>> =>
- c
- .bimap(TraceUtil.withFunctionTrace(getStdout))
- .map((tCmd) => {
- const cmd = tCmd.get();
- tCmd.trace.trace(`Command = ${cmd} :> im gonna run this command! `);
- const [exec, ...args] = typeof cmd === "string" ? cmd.split(" ") : cmd;
- return new Deno.Command(exec, {
- args,
- stdout: "piped",
- stderr: "piped",
- ...options,
- });
- })
- .map((tCmd) =>
- Either.fromFailableAsync<Error, Deno.CommandOutput>(() =>
- tCmd.get().output(),
- ),
- )
- .map(
- TraceUtil.promiseify((tEitherOut) =>
- tEitherOut.get().flatMap(({ code, stderr, stdout }) =>
- Either.fromFailable<Error, CommandOutputDecoded>(() => {
- const stdoutText = new TextDecoder().decode(stdout);
- const stderrText = new TextDecoder().decode(stderr);
- return { code, stdoutText, stderrText };
- })
- .mapLeft((e) => {
- tEitherOut.trace.addTrace(LogLevel.ERROR).trace(e);
- return e;
- })
- .flatMap((decodedOutput): IEither<Error, string> => {
- const { code, stdoutText, stderrText } = decodedOutput;
- if (stderrText) {
- tEitherOut.trace
- .addTrace(LogLevel.DEBUG)
- .trace(`stderr: ${stderrText}`);
- }
- if (code !== 0) {
- const msg = `i weceived an exit code of ${code} i wanna zewoooo :<`;
- tEitherOut.trace.addTrace(LogLevel.ERROR).trace(msg);
- return Either.left(new Error(msg));
- }
- return Either.right(stdoutText);
- }),
- ),
- ),
- )
- .get();
+ c
+ .bimap(TraceUtil.withFunctionTrace(getStdout))
+ .bimap((tCmd) => {
+ const cmd = tCmd.get();
+ tCmd.trace.trace(`Command = ${cmd} :> im gonna run this command! `);
+
+ const _exec = typeof cmd === 'string' ? cmd : cmd.join(' ');
+ const env = options.clearEnv ? options.env : { ...process.env, ...options.env };
+
+ const p: Promise<IEither<Error, StdStreams>> = Either.fromFailableAsync(exec(_exec, { env }));
+ return [p, `Command = ${_exec}`];
+ })
+ .map(
+ TraceUtil.promiseify(
+ (tEitherProcess): IEither<Error, string> =>
+ tEitherProcess.get().fold(({ isLeft, value }) => {
+ if (isLeft) {
+ return Either.left(value);
+ }
+ if (value.stderr) {
+ tEitherProcess.trace.addTrace(LogLevel.DEBUG).trace(`StdErr = ${value.stderr}`);
+ }
+ return Either.right(value.stdout);
+ }),
+ ),
+ )
+ .get();
diff --git a/u/process/validate_identifier.ts b/u/process/validate_identifier.ts
index 4e93728..1ff3791 100644
--- a/u/process/validate_identifier.ts
+++ b/u/process/validate_identifier.ts
@@ -1,23 +1,18 @@
-import { Either, type IEither } from "@emprespresso/pengueno";
+import { Either, type IEither } from '@emprespresso/pengueno';
export const validateIdentifier = (token: string) => {
- return /^[a-zA-Z0-9_\-:. \/]+$/.test(token) && !token.includes("..");
+ return /^[a-zA-Z0-9_\-:. \/]+$/.test(token) && !token.includes('..');
};
// ensure {@param obj} is a Record<string, string> with stuff that won't
// have the potential for shell injection, just to be super safe.
type InvalidEntry<K, T> = [K, T];
-export const validateExecutionEntries = <
- T,
- K extends symbol | number | string = symbol | number | string,
->(
- obj: Record<K, T>,
+export const validateExecutionEntries = <T, K extends symbol | number | string = symbol | number | string>(
+ obj: Record<K, T>,
): IEither<Array<InvalidEntry<K, T>>, Record<string, string>> => {
- const invalidEntries = <Array<InvalidEntry<K, T>>>(
- Object.entries(obj).filter(
- (e) => !e.every((x) => typeof x === "string" && validateIdentifier(x)),
- )
- );
- if (invalidEntries.length > 0) return Either.left(invalidEntries);
- return Either.right(<Record<string, string>>obj);
+ const invalidEntries = <Array<InvalidEntry<K, T>>>(
+ Object.entries(obj).filter((e) => !e.every((x) => typeof x === 'string' && validateIdentifier(x)))
+ );
+ if (invalidEntries.length > 0) return Either.left(invalidEntries);
+ return Either.right(<Record<string, string>>obj);
};
diff --git a/u/server/activity/fourohfour.ts b/u/server/activity/fourohfour.ts
index 33cfe5f..cd90ba0 100644
--- a/u/server/activity/fourohfour.ts
+++ b/u/server/activity/fourohfour.ts
@@ -1,29 +1,28 @@
import {
- type IActivity,
- type ITraceable,
- JsonResponse,
- type PenguenoRequest,
- type ServerTrace,
-} from "@emprespresso/pengueno";
+ type IActivity,
+ type ITraceable,
+ JsonResponse,
+ type PenguenoRequest,
+ type ServerTrace,
+} from '@emprespresso/pengueno';
const messages = [
- "D: meow-t found! your api call ran away!",
- "404-bidden! but like...in a cute way >:3 !",
- ":< your data went on a paw-sible vacation!",
- "uwu~ not found, but found our hearts instead!",
+ 'D: meow-t found! your api call ran away!',
+ '404-bidden! but like...in a cute way >:3 !',
+ ':< your data went on a paw-sible vacation!',
+ 'uwu~ not found, but found our hearts instead!',
];
-const randomFourOhFour = () =>
- messages[Math.floor(Math.random() * messages.length)];
+const randomFourOhFour = () => messages[Math.floor(Math.random() * messages.length)]!;
export interface IFourOhFourActivity {
- fourOhFour: IActivity;
+ fourOhFour: IActivity;
}
export class FourOhFourActivityImpl implements IFourOhFourActivity {
- public fourOhFour(req: ITraceable<PenguenoRequest, ServerTrace>) {
- return req
- .move(new JsonResponse(req, randomFourOhFour(), { status: 404 }))
- .map((resp) => Promise.resolve(resp.get()))
- .get();
- }
+ public fourOhFour(req: ITraceable<PenguenoRequest, ServerTrace>) {
+ return req
+ .move(new JsonResponse(req, randomFourOhFour(), { status: 404 }))
+ .map((resp) => Promise.resolve(resp.get()))
+ .get();
+ }
}
diff --git a/u/server/activity/health.ts b/u/server/activity/health.ts
index 95dfa97..b3ae559 100644
--- a/u/server/activity/health.ts
+++ b/u/server/activity/health.ts
@@ -1,71 +1,67 @@
import {
- type IActivity,
- type IEither,
- type ITraceable,
- JsonResponse,
- LogLevel,
- type Mapper,
- Metric,
- type PenguenoRequest,
- type ServerTrace,
- TraceUtil,
-} from "@emprespresso/pengueno";
+ type IActivity,
+ type IEither,
+ IMetric,
+ type ITraceable,
+ JsonResponse,
+ LogLevel,
+ type Mapper,
+ Metric,
+ type PenguenoRequest,
+ type ServerTrace,
+ TraceUtil,
+} from '@emprespresso/pengueno';
export enum HealthCheckInput {
- CHECK,
+ CHECK,
}
export enum HealthCheckOutput {
- YAASSSLAYQUEEN,
+ YAASSSLAYQUEEN,
}
export interface IHealthCheckActivity {
- checkHealth: IActivity;
+ checkHealth: IActivity;
}
-const healthCheckMetric = Metric.fromName("Health");
+const healthCheckMetric: IMetric = Metric.fromName('Health');
export interface HealthChecker
- extends Mapper<
- ITraceable<HealthCheckInput, ServerTrace>,
- Promise<IEither<Error, HealthCheckOutput>>
- > {}
+ extends Mapper<ITraceable<HealthCheckInput, ServerTrace>, Promise<IEither<Error, HealthCheckOutput>>> {}
export class HealthCheckActivityImpl implements IHealthCheckActivity {
- constructor(private readonly check: HealthChecker) {}
+ constructor(private readonly check: HealthChecker) {}
- public checkHealth(req: ITraceable<PenguenoRequest, ServerTrace>) {
- return req
- .bimap(TraceUtil.withFunctionTrace(this.checkHealth))
- .bimap(TraceUtil.withMetricTrace(healthCheckMetric))
- .flatMap((r) =>
- r.move(HealthCheckInput.CHECK).map((input) => this.check(input)),
- )
- .peek(
- TraceUtil.promiseify((h) =>
- h.get().fold(({ isLeft, value }) => {
- if (!isLeft) {
- h.trace.trace(healthCheckMetric.success);
- return;
- }
- h.trace.trace(healthCheckMetric.failure);
- h.trace.addTrace(LogLevel.ERROR).trace(value);
- }),
- ),
- )
- .map(
- TraceUtil.promiseify((h) =>
- h
- .get()
- .mapBoth(
- () => "oh no, i need to eat more vegetables (。•́︿•̀。)...",
- () => "think im healthy!! (✿˘◡˘) ready to do work~",
+ public checkHealth(req: ITraceable<PenguenoRequest, ServerTrace>) {
+ return req
+ .bimap(TraceUtil.withFunctionTrace(this.checkHealth))
+ .bimap(TraceUtil.withMetricTrace(healthCheckMetric))
+ .flatMap((r) => r.move(HealthCheckInput.CHECK).map((input) => this.check(input)))
+ .peek(
+ TraceUtil.promiseify((h) =>
+ h.get().fold(({ isLeft, value }) => {
+ if (!isLeft) {
+ h.trace.trace(healthCheckMetric.success);
+ return;
+ }
+ h.trace.trace(healthCheckMetric.failure);
+ h.trace.addTrace(LogLevel.ERROR).trace(value);
+ }),
+ ),
)
- .fold(
- ({ isLeft, value: message }) =>
- new JsonResponse(req, message, {
- status: isLeft ? 500 : 200,
- }),
- ),
- ),
- )
- .get();
- }
+ .map(
+ TraceUtil.promiseify((h) =>
+ h
+ .get()
+ .mapBoth(
+ () => 'oh no, i need to eat more vegetables (。•́︿•̀。)...',
+ () => 'think im healthy!! (✿˘◡˘) ready to do work~',
+ )
+ .fold(
+ ({ isLeft, value: message }) =>
+ new JsonResponse(req, message, {
+ status: isLeft ? 500 : 200,
+ }),
+ ),
+ ),
+ )
+ .get();
+ }
}
diff --git a/u/server/activity/index.ts b/u/server/activity/index.ts
new file mode 100644
index 0000000..fa0a6b2
--- /dev/null
+++ b/u/server/activity/index.ts
@@ -0,0 +1,8 @@
+import type { ITraceable, PenguenoRequest, PenguenoResponse, ServerTrace } from '@emprespresso/pengueno';
+
+export interface IActivity {
+ (req: ITraceable<PenguenoRequest, ServerTrace>): Promise<PenguenoResponse>;
+}
+
+export * from './health.js';
+export * from './fourohfour.js';
diff --git a/u/server/activity/mod.ts b/u/server/activity/mod.ts
deleted file mode 100644
index 82d8ec4..0000000
--- a/u/server/activity/mod.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import type {
- ITraceable,
- PenguenoRequest,
- PenguenoResponse,
- ServerTrace,
-} from "@emprespresso/pengueno";
-
-export interface IActivity {
- (req: ITraceable<PenguenoRequest, ServerTrace>): Promise<PenguenoResponse>;
-}
-
-export * from "./health.ts";
-export * from "./fourohfour.ts";
diff --git a/u/server/filter/index.ts b/u/server/filter/index.ts
new file mode 100644
index 0000000..62a584d
--- /dev/null
+++ b/u/server/filter/index.ts
@@ -0,0 +1,34 @@
+import {
+ type IEither,
+ type ITraceable,
+ LogLevel,
+ type PenguenoRequest,
+ type ServerTrace,
+} from '@emprespresso/pengueno';
+
+export enum ErrorSource {
+ USER = LogLevel.WARN,
+ SYSTEM = LogLevel.ERROR,
+}
+
+export class PenguenoError extends Error {
+ public readonly source: ErrorSource;
+ constructor(
+ override readonly message: string,
+ public readonly status: number,
+ ) {
+ super(message);
+ this.source = Math.floor(status / 100) === 4 ? ErrorSource.USER : ErrorSource.SYSTEM;
+ }
+}
+
+export interface RequestFilter<
+ T,
+ Err extends PenguenoError = PenguenoError,
+ RIn = ITraceable<PenguenoRequest, ServerTrace>,
+> {
+ (req: RIn): Promise<IEither<Err, T>>;
+}
+
+export * from './method.js';
+export * from './json.js';
diff --git a/u/server/filter/json.ts b/u/server/filter/json.ts
index 145d1be..527d483 100644
--- a/u/server/filter/json.ts
+++ b/u/server/filter/json.ts
@@ -1,54 +1,50 @@
import {
- Either,
- type IEither,
- type ITraceable,
- LogLevel,
- Metric,
- PenguenoError,
- type PenguenoRequest,
- type RequestFilter,
- type ServerTrace,
- TraceUtil,
-} from "@emprespresso/pengueno";
+ Either,
+ type IEither,
+ type ITraceable,
+ LogLevel,
+ Metric,
+ PenguenoError,
+ type PenguenoRequest,
+ type RequestFilter,
+ type ServerTrace,
+ TraceUtil,
+} from '@emprespresso/pengueno';
export interface JsonTransformer<R, ParsedJson = unknown> {
- (json: ITraceable<ParsedJson, ServerTrace>): IEither<PenguenoError, R>;
+ (json: ITraceable<ParsedJson, ServerTrace>): IEither<PenguenoError, R>;
}
-const ParseJsonMetric = Metric.fromName("JsonParse");
+const ParseJsonMetric = Metric.fromName('JsonParse');
export const jsonModel =
- <MessageT>(
- jsonTransformer: JsonTransformer<MessageT>,
- ): RequestFilter<MessageT> =>
- (r: ITraceable<PenguenoRequest, ServerTrace>) =>
- r
- .bimap(TraceUtil.withFunctionTrace(jsonModel))
- .bimap(TraceUtil.withMetricTrace(ParseJsonMetric))
- .map((j) =>
- Either.fromFailableAsync<Error, MessageT>(() => j.get().json()).then(
- (either) =>
- either.mapLeft((errReason) => {
- j.trace.addTrace(LogLevel.WARN).trace(errReason);
- return new PenguenoError(
- "seems to be invalid JSON (>//<) can you fix?",
- 400,
- );
- }),
- ),
- )
- .peek(
- TraceUtil.promiseify((traceableEither) =>
- traceableEither.get().fold(({ isLeft }) =>
- traceableEither.trace.trace(ParseJsonMetric[isLeft ? "failure" : "success"])
- ),
- ),
- )
- .map(
- TraceUtil.promiseify((traceableEitherJson) =>
- traceableEitherJson
- .get()
- .mapRight((j) => traceableEitherJson.move(j))
- .flatMap(jsonTransformer),
- ),
- )
- .get();
+ <MessageT>(jsonTransformer: JsonTransformer<MessageT>): RequestFilter<MessageT> =>
+ (r: ITraceable<PenguenoRequest, ServerTrace>) =>
+ r
+ .bimap(TraceUtil.withFunctionTrace(jsonModel))
+ .bimap(TraceUtil.withMetricTrace(ParseJsonMetric))
+ .map((j) =>
+ Either.fromFailableAsync<Error, MessageT>(<Promise<MessageT>>j.get().json()).then((either) =>
+ either.mapLeft((errReason) => {
+ j.trace.addTrace(LogLevel.WARN).trace(errReason);
+ return new PenguenoError('seems to be invalid JSON (>//<) can you fix?', 400);
+ }),
+ ),
+ )
+ .peek(
+ TraceUtil.promiseify((traceableEither) =>
+ traceableEither
+ .get()
+ .fold(({ isLeft }) =>
+ traceableEither.trace.trace(ParseJsonMetric[isLeft ? 'failure' : 'success']),
+ ),
+ ),
+ )
+ .map(
+ TraceUtil.promiseify((traceableEitherJson) =>
+ traceableEitherJson
+ .get()
+ .mapRight((j) => traceableEitherJson.move(j))
+ .flatMap(jsonTransformer),
+ ),
+ )
+ .get();
diff --git a/u/server/filter/method.ts b/u/server/filter/method.ts
index 9901c6f..5ca5716 100644
--- a/u/server/filter/method.ts
+++ b/u/server/filter/method.ts
@@ -1,43 +1,32 @@
import {
- Either,
- type ITraceable,
- LogLevel,
- PenguenoError,
- type PenguenoRequest,
- type RequestFilter,
- type ServerTrace,
- TraceUtil,
-} from "@emprespresso/pengueno";
+ Either,
+ type ITraceable,
+ LogLevel,
+ PenguenoError,
+ type PenguenoRequest,
+ type RequestFilter,
+ type ServerTrace,
+ TraceUtil,
+} from '@emprespresso/pengueno';
-type HttpMethod =
- | "POST"
- | "GET"
- | "HEAD"
- | "PUT"
- | "DELETE"
- | "CONNECT"
- | "OPTIONS"
- | "TRACE"
- | "PATCH";
+type HttpMethod = 'POST' | 'GET' | 'HEAD' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH';
export const requireMethod =
- (methods: Array<HttpMethod>): RequestFilter<HttpMethod> =>
- (req: ITraceable<PenguenoRequest, ServerTrace>) =>
- req
- .bimap(TraceUtil.withFunctionTrace(requireMethod))
- .move(Promise.resolve(req.get()))
- .map(
- TraceUtil.promiseify((t) => {
- const { method: _method } = t.get();
- const method = <HttpMethod>_method;
- if (!methods.includes(method)) {
- const msg = "that's not how you pet me (⋟﹏⋞)~";
- t.trace.addTrace(LogLevel.WARN).trace(msg);
- return Either.left<PenguenoError, HttpMethod>(
- new PenguenoError(msg, 405),
- );
- }
- return Either.right<PenguenoError, HttpMethod>(method);
- }),
- )
- .get();
+ (methods: Array<HttpMethod>): RequestFilter<HttpMethod> =>
+ (req: ITraceable<PenguenoRequest, ServerTrace>) =>
+ req
+ .bimap(TraceUtil.withFunctionTrace(requireMethod))
+ .move(Promise.resolve(req.get()))
+ .map(
+ TraceUtil.promiseify((t) => {
+ const { method: _method } = t.get();
+ const method = <HttpMethod>_method;
+ if (!methods.includes(method)) {
+ const msg = "that's not how you pet me (⋟﹏⋞)~";
+ t.trace.addTrace(LogLevel.WARN).trace(msg);
+ return Either.left<PenguenoError, HttpMethod>(new PenguenoError(msg, 405));
+ }
+ return Either.right<PenguenoError, HttpMethod>(method);
+ }),
+ )
+ .get();
diff --git a/u/server/filter/mod.ts b/u/server/filter/mod.ts
deleted file mode 100644
index 0e0a4cb..0000000
--- a/u/server/filter/mod.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import {
- type IEither,
- type ITraceable,
- LogLevel,
- type PenguenoRequest,
- type ServerTrace,
-} from "@emprespresso/pengueno";
-
-export enum ErrorSource {
- USER = LogLevel.WARN,
- SYSTEM = LogLevel.ERROR,
-}
-
-export class PenguenoError extends Error {
- public readonly source: ErrorSource;
- constructor(
- override readonly message: string,
- public readonly status: number,
- ) {
- super(message);
- this.source =
- Math.floor(status / 100) === 4 ? ErrorSource.USER : ErrorSource.SYSTEM;
- }
-}
-
-export interface RequestFilter<
- T,
- Err extends PenguenoError = PenguenoError,
- RIn = ITraceable<PenguenoRequest, ServerTrace>,
-> {
- (req: RIn): Promise<IEither<Err, T>>;
-}
-
-export * from "./method.ts";
-export * from "./json.ts";
diff --git a/u/server/index.ts b/u/server/index.ts
new file mode 100644
index 0000000..17cbbdf
--- /dev/null
+++ b/u/server/index.ts
@@ -0,0 +1,7 @@
+import type { LogMetricTraceSupplier } from '@emprespresso/pengueno';
+export type ServerTrace = LogMetricTraceSupplier;
+
+export * from './activity/index.js';
+export * from './filter/index.js';
+export * from './response.js';
+export * from './request.js';
diff --git a/u/server/mod.ts b/u/server/mod.ts
deleted file mode 100644
index 866b5f9..0000000
--- a/u/server/mod.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { LogMetricTraceSupplier } from "@emprespresso/pengueno";
-export type ServerTrace = LogMetricTraceSupplier;
-
-export * from "./activity/mod.ts";
-export * from "./filter/mod.ts";
-export * from "./response.ts";
-export * from "./request.ts";
diff --git a/u/server/request.ts b/u/server/request.ts
index 72e812a..10610f1 100644
--- a/u/server/request.ts
+++ b/u/server/request.ts
@@ -1,49 +1,39 @@
-import { LogMetricTraceable, LogTraceable } from "@emprespresso/pengueno";
-import { TraceUtil } from "../trace/util.ts";
+import { TraceUtil, LogMetricTraceable, LogTraceable } from '@emprespresso/pengueno';
-const greetings = [
- "hewwo :D",
- "hiya cutie",
- "boop!",
- "sending virtual hugs!",
- "stay pawsitive",
-];
-const penguenoGreeting = () =>
- greetings[Math.floor(Math.random() * greetings.length)];
+const greetings = ['hewwo :D', 'hiya cutie', 'boop!', 'sending virtual hugs!', 'stay pawsitive'];
+const penguenoGreeting = () => greetings[Math.floor(Math.random() * greetings.length)];
export class PenguenoRequest extends Request {
- private constructor(
- _input: Request,
- public readonly id: string,
- public readonly at: Date,
- ) {
- super(_input);
- }
+ private constructor(
+ _input: Request,
+ public readonly id: string,
+ public readonly at: Date,
+ ) {
+ super(_input);
+ }
- public baseResponseHeaders(): Record<string, string> {
- const ServerRequestTime = this.at.getTime();
- const ServerResponseTime = Date.now();
- const DeltaTime = ServerResponseTime - ServerRequestTime;
- const RequestId = this.id;
+ public baseResponseHeaders(): Record<string, string> {
+ const ServerRequestTime = this.at.getTime();
+ const ServerResponseTime = Date.now();
+ const DeltaTime = ServerResponseTime - ServerRequestTime;
+ const RequestId = this.id;
- return Object.entries({
- RequestId,
- ServerRequestTime,
- ServerResponseTime,
- DeltaTime,
- Hai: penguenoGreeting(),
- }).reduce((acc, [key, val]) => ({ ...acc, [key]: val.toString() }), {});
- }
+ return Object.entries({
+ RequestId,
+ ServerRequestTime,
+ ServerResponseTime,
+ DeltaTime,
+ Hai: penguenoGreeting(),
+ }).reduce((acc, [key, val]) => ({ ...acc, [key]: val!.toString() }), {});
+ }
- public static from(request: Request): LogMetricTraceable<PenguenoRequest> {
- const id = crypto.randomUUID();
- const url = new URL(request.url);
- const { pathname } = url;
- const logTraceable = LogTraceable.of(
- new PenguenoRequest(request, id, new Date()),
- ).bimap(
- TraceUtil.withTrace(`RequestId = ${id}, Method = ${request.method}, Path = ${pathname}`),
- );
- return LogMetricTraceable.ofLogTraceable(logTraceable);
- }
+ public static from(request: Request): LogMetricTraceable<PenguenoRequest> {
+ const id = crypto.randomUUID();
+ const url = new URL(request.url);
+ const { pathname } = url;
+ const logTraceable = LogTraceable.of(new PenguenoRequest(request, id, new Date())).bimap(
+ TraceUtil.withTrace(`RequestId = ${id}, Method = ${request.method}, Path = ${pathname}`),
+ );
+ return LogMetricTraceable.ofLogTraceable(logTraceable);
+ }
}
diff --git a/u/server/response.ts b/u/server/response.ts
index 629dbb5..18d70b5 100644
--- a/u/server/response.ts
+++ b/u/server/response.ts
@@ -1,86 +1,83 @@
import {
- type IEither,
- isEither,
- type ITraceable,
- Metric,
- type PenguenoRequest,
- type ServerTrace,
-} from "@emprespresso/pengueno";
+ type IEither,
+ isEither,
+ type ITraceable,
+ Metric,
+ type PenguenoRequest,
+ type ServerTrace,
+} from '@emprespresso/pengueno';
+export type BodyInit =
+ | ArrayBuffer
+ | AsyncIterable<Uint8Array>
+ | Blob
+ | FormData
+ | Iterable<Uint8Array>
+ | NodeJS.ArrayBufferView
+ | URLSearchParams
+ | null
+ | string;
export type ResponseBody = object | string;
-export type TResponseInit = ResponseInit & {
- status: number;
- headers?: Record<string, string>;
+export type TResponseInit = Omit<ResponseInit, 'headers'> & {
+ status: number;
+ headers?: Record<string, string>;
};
-const getResponse = (
- req: PenguenoRequest,
- opts: TResponseInit,
-): TResponseInit => {
- return {
- ...opts,
- headers: {
- ...req.baseResponseHeaders(),
- ...opts?.headers,
- "Content-Type":
- (opts?.headers?.["Content-Type"] ?? "text/plain") + "; charset=utf-8",
- },
- };
+const getResponse = (req: PenguenoRequest, opts: TResponseInit): ResponseInit => {
+ const baseHeaders = req.baseResponseHeaders();
+ const optHeaders = opts.headers || {};
+
+ return {
+ ...opts,
+ headers: {
+ ...baseHeaders,
+ ...optHeaders,
+ 'Content-Type': (optHeaders['Content-Type'] ?? 'text/plain') + '; charset=utf-8',
+ } as Record<string, string>,
+ };
};
-const ResponseCodeMetrics = [0, 1, 2, 3, 4, 5].map((x) =>
- Metric.fromName(`response.${x}xx`),
-);
-export const getResponseMetric = (status: number) => {
- const index = Math.floor(status / 100);
- return ResponseCodeMetrics[index] ?? ResponseCodeMetrics[5];
+const ResponseCodeMetrics = [0, 1, 2, 3, 4, 5].map((x) => Metric.fromName(`response.${x}xx`));
+export const getResponseMetrics = (status: number) => {
+ const index = Math.floor(status / 100);
+ return ResponseCodeMetrics.map((metric, i) => metric.count.withValue(i === index ? 1.0 : 0.0));
};
export class PenguenoResponse extends Response {
- constructor(
- req: ITraceable<PenguenoRequest, ServerTrace>,
- msg: BodyInit,
- opts: TResponseInit,
- ) {
- const responseOpts = getResponse(req.get(), opts);
- const resMetric = getResponseMetric(opts.status);
- req.trace.trace(resMetric.count.withValue(1.0));
- responseOpts.headers;
- super(msg, responseOpts);
- }
+ constructor(req: ITraceable<PenguenoRequest, ServerTrace>, msg: BodyInit, opts: TResponseInit) {
+ const responseOpts = getResponse(req.get(), opts);
+ for (const metric of getResponseMetrics(opts.status)) {
+ req.trace.trace(metric);
+ }
+ super(msg, responseOpts);
+ }
}
export class JsonResponse extends PenguenoResponse {
- constructor(
- req: ITraceable<PenguenoRequest, ServerTrace>,
- e: BodyInit | IEither<ResponseBody, ResponseBody>,
- opts: TResponseInit,
- ) {
- const optsWithJsonContentType = {
- ...opts,
- headers: {
- ...opts?.headers,
- "Content-Type": "application/json",
- },
- };
- if (isEither<ResponseBody, ResponseBody>(e)) {
- super(
- req,
- JSON.stringify(
- e.fold(({ isLeft, value }) =>
- isLeft ? { error: value } : { ok: value },
- ),
- ),
- optsWithJsonContentType,
- );
- return;
+ constructor(
+ req: ITraceable<PenguenoRequest, ServerTrace>,
+ e: BodyInit | IEither<ResponseBody, ResponseBody>,
+ opts: TResponseInit,
+ ) {
+ const optsWithJsonContentType: TResponseInit = {
+ ...opts,
+ headers: {
+ ...opts.headers,
+ 'Content-Type': 'application/json',
+ },
+ };
+ if (isEither<ResponseBody, ResponseBody>(e)) {
+ super(
+ req,
+ JSON.stringify(e.fold(({ isLeft, value }) => (isLeft ? { error: value } : { ok: value }))),
+ optsWithJsonContentType,
+ );
+ return;
+ }
+ super(
+ req,
+ JSON.stringify(Math.floor(opts.status / 100) > 4 ? { error: e } : { ok: e }),
+ optsWithJsonContentType,
+ );
}
- super(
- req,
- JSON.stringify(
- Math.floor(opts.status / 100) > 4 ? { error: e } : { ok: e },
- ),
- optsWithJsonContentType,
- );
- }
}
diff --git a/u/trace/index.ts b/u/trace/index.ts
new file mode 100644
index 0000000..18da87a
--- /dev/null
+++ b/u/trace/index.ts
@@ -0,0 +1,5 @@
+export * from './itrace.js';
+export * from './util.js';
+export * from './logger.js';
+export * from './metrics.js';
+export * from './trace.js';
diff --git a/u/trace/itrace.ts b/u/trace/itrace.ts
index fcfbe32..8cf123a 100644
--- a/u/trace/itrace.ts
+++ b/u/trace/itrace.ts
@@ -1,90 +1,72 @@
-import type { Mapper, SideEffect, Supplier } from "@emprespresso/pengueno";
+import type { Mapper, SideEffect, Supplier } from '@emprespresso/pengueno';
// the "thing" every Trace writer must "trace()"
type BaseTraceWith = string;
export type ITraceWith<T> = BaseTraceWith | T;
export interface ITrace<TraceWith> {
- addTrace: Mapper<ITraceWith<TraceWith>, ITrace<TraceWith>>;
- trace: SideEffect<ITraceWith<TraceWith>>;
+ addTrace: Mapper<ITraceWith<TraceWith>, ITrace<TraceWith>>;
+ trace: SideEffect<ITraceWith<TraceWith>>;
}
export type ITraceableTuple<T, TraceWith> = [T, BaseTraceWith | TraceWith];
-export type ITraceableMapper<T, _T, TraceWith, W = ITraceable<T, TraceWith>> = (
- w: W,
-) => _T;
+export type ITraceableMapper<T, _T, TraceWith, W = ITraceable<T, TraceWith>> = (w: W) => _T;
export interface ITraceable<T, Trace = BaseTraceWith> {
- readonly trace: ITrace<Trace>;
- get: Supplier<T>;
- move: <_T>(t: _T) => ITraceable<_T, Trace>;
- map: <_T>(mapper: ITraceableMapper<T, _T, Trace>) => ITraceable<_T, Trace>;
- bimap: <_T>(
- mapper: ITraceableMapper<
- T,
- ITraceableTuple<_T, Array<Trace> | Trace>,
- Trace
- >,
- ) => ITraceable<_T, Trace>;
- peek: (peek: ITraceableMapper<T, void, Trace>) => ITraceable<T, Trace>;
- flatMap: <_T>(
- mapper: ITraceableMapper<T, ITraceable<_T, Trace>, Trace>,
- ) => ITraceable<_T, Trace>;
- flatMapAsync<_T>(
- mapper: ITraceableMapper<T, Promise<ITraceable<_T, Trace>>, Trace>,
- ): ITraceable<Promise<_T>, Trace>;
+ readonly trace: ITrace<Trace>;
+ get: Supplier<T>;
+ move: <_T>(t: _T) => ITraceable<_T, Trace>;
+ map: <_T>(mapper: ITraceableMapper<T, _T, Trace>) => ITraceable<_T, Trace>;
+ bimap: <_T>(mapper: ITraceableMapper<T, ITraceableTuple<_T, Array<Trace> | Trace>, Trace>) => ITraceable<_T, Trace>;
+ peek: (peek: ITraceableMapper<T, void, Trace>) => ITraceable<T, Trace>;
+ flatMap: <_T>(mapper: ITraceableMapper<T, ITraceable<_T, Trace>, Trace>) => ITraceable<_T, Trace>;
+ flatMapAsync<_T>(
+ mapper: ITraceableMapper<T, Promise<ITraceable<_T, Trace>>, Trace>,
+ ): ITraceable<Promise<_T>, Trace>;
}
export class TraceableImpl<T, TraceWith> implements ITraceable<T, TraceWith> {
- protected constructor(
- private readonly item: T,
- public readonly trace: ITrace<TraceWith>,
- ) {}
+ protected constructor(
+ private readonly item: T,
+ public readonly trace: ITrace<TraceWith>,
+ ) {}
- public map<_T>(mapper: ITraceableMapper<T, _T, TraceWith>) {
- const result = mapper(this);
- return new TraceableImpl(result, this.trace);
- }
+ public map<_T>(mapper: ITraceableMapper<T, _T, TraceWith>) {
+ const result = mapper(this);
+ return new TraceableImpl(result, this.trace);
+ }
- public flatMap<_T>(
- mapper: ITraceableMapper<T, ITraceable<_T, TraceWith>, TraceWith>,
- ): ITraceable<_T, TraceWith> {
- return mapper(this);
- }
+ public flatMap<_T>(mapper: ITraceableMapper<T, ITraceable<_T, TraceWith>, TraceWith>): ITraceable<_T, TraceWith> {
+ return mapper(this);
+ }
- public flatMapAsync<_T>(
- mapper: ITraceableMapper<T, Promise<ITraceable<_T, TraceWith>>, TraceWith>,
- ): ITraceable<Promise<_T>, TraceWith> {
- return new TraceableImpl(
- mapper(this).then((t) => t.get()),
- this.trace,
- );
- }
+ public flatMapAsync<_T>(
+ mapper: ITraceableMapper<T, Promise<ITraceable<_T, TraceWith>>, TraceWith>,
+ ): ITraceable<Promise<_T>, TraceWith> {
+ return new TraceableImpl(
+ mapper(this).then((t) => t.get()),
+ this.trace,
+ );
+ }
- public peek(peek: ITraceableMapper<T, void, TraceWith>) {
- peek(this);
- return this;
- }
+ public peek(peek: ITraceableMapper<T, void, TraceWith>) {
+ peek(this);
+ return this;
+ }
- public move<_T>(t: _T): ITraceable<_T, TraceWith> {
- return this.map(() => t);
- }
+ public move<_T>(t: _T): ITraceable<_T, TraceWith> {
+ return this.map(() => t);
+ }
- public bimap<_T>(
- mapper: ITraceableMapper<
- T,
- ITraceableTuple<_T, Array<TraceWith> | TraceWith>,
- TraceWith
- >,
- ) {
- const [item, trace] = mapper(this);
- const traces = Array.isArray(trace) ? trace : [trace];
- return new TraceableImpl(
- item,
- traces.reduce((trace, _trace) => trace.addTrace(_trace), this.trace),
- );
- }
+ public bimap<_T>(mapper: ITraceableMapper<T, ITraceableTuple<_T, Array<TraceWith> | TraceWith>, TraceWith>) {
+ const [item, trace] = mapper(this);
+ const traces = Array.isArray(trace) ? trace : [trace];
+ return new TraceableImpl(
+ item,
+ traces.reduce((trace, _trace) => trace.addTrace(_trace), this.trace),
+ );
+ }
- public get() {
- return this.item;
- }
+ public get() {
+ return this.item;
+ }
}
diff --git a/u/trace/logger.ts b/u/trace/logger.ts
index d8392eb..91432fe 100644
--- a/u/trace/logger.ts
+++ b/u/trace/logger.ts
@@ -1,112 +1,95 @@
-import {
- isDebug,
- type ITrace,
- type ITraceWith,
- type Supplier,
-} from "@emprespresso/pengueno";
+import { isDebug, type ITrace, type ITraceWith, type Supplier } from '@emprespresso/pengueno';
export type LogTraceSupplier = ITraceWith<Supplier<string> | Error>;
const defaultTrace = () => `TimeStamp = ${new Date().toISOString()}`;
export class LogTrace implements ITrace<LogTraceSupplier> {
- constructor(
- private readonly logger: ILogger = new LoggerImpl(),
- private readonly traces: Array<LogTraceSupplier> = [defaultTrace],
- private readonly defaultLevel: LogLevel = LogLevel.INFO,
- private readonly allowedLevels: Supplier<
- Array<LogLevel>
- > = defaultAllowedLevels,
- ) {}
+ constructor(
+ private readonly logger: ILogger = new LoggerImpl(),
+ private readonly traces: Array<LogTraceSupplier> = [defaultTrace],
+ private readonly defaultLevel: LogLevel = LogLevel.INFO,
+ private readonly allowedLevels: Supplier<Array<LogLevel>> = defaultAllowedLevels,
+ ) {}
- public addTrace(trace: LogTraceSupplier): ITrace<LogTraceSupplier> {
- return new LogTrace(
- this.logger,
- this.traces.concat(trace),
- this.defaultLevel,
- this.allowedLevels,
- );
- }
+ public addTrace(trace: LogTraceSupplier): ITrace<LogTraceSupplier> {
+ return new LogTrace(this.logger, this.traces.concat(trace), this.defaultLevel, this.allowedLevels);
+ }
- public trace(trace: LogTraceSupplier) {
- const { traces, level: _level } = this.foldTraces(
- this.traces.concat(trace),
- );
- if (!this.allowedLevels().includes(_level)) return;
+ public trace(trace: LogTraceSupplier) {
+ const { traces, level: _level } = this.foldTraces(this.traces.concat(trace));
+ if (!this.allowedLevels().includes(_level)) return;
- const level = _level === LogLevel.UNKNOWN ? this.defaultLevel : _level;
- this.logger.log(level, ...traces);
- }
+ const level = _level === LogLevel.UNKNOWN ? this.defaultLevel : _level;
+ this.logger.log(level, ...traces);
+ }
- private foldTraces(_traces: Array<LogTraceSupplier>) {
- const _logTraces = _traces.map((trace) =>
- typeof trace === "function" ? trace() : trace,
- );
- const _level = _logTraces
- .filter((trace) => isLogLevel(trace))
- .reduce((acc, level) => Math.max(logLevelOrder.indexOf(level), acc), -1);
- const level = logLevelOrder[_level] ?? LogLevel.UNKNOWN;
+ private foldTraces(_traces: Array<LogTraceSupplier>) {
+ const _logTraces = _traces.map((trace) => (typeof trace === 'function' ? trace() : trace));
+ const _level = _logTraces
+ .filter((trace) => isLogLevel(trace))
+ .reduce((acc, level) => Math.max(logLevelOrder.indexOf(level), acc), -1);
+ const level = logLevelOrder[_level] ?? LogLevel.UNKNOWN;
- const traces = _logTraces.filter((trace) => !isLogLevel(trace)).map((trace) => {
- if (typeof trace === 'object') {
- return `TracedException.Name = ${trace.name}, TracedException.Message = ${trace.message}, TracedException.Stack = ${trace.stack}`
- }
- return trace;
- });
- return {
- level,
- traces,
- };
- }
+ const traces = _logTraces
+ .filter((trace) => !isLogLevel(trace))
+ .map((trace) => {
+ if (typeof trace === 'object') {
+ return `TracedException.Name = ${trace.name}, TracedException.Message = ${trace.message}, TracedException.Stack = ${trace.stack}`;
+ }
+ return trace;
+ });
+ return {
+ level,
+ traces,
+ };
+ }
}
export enum LogLevel {
- UNKNOWN = "UNKNOWN",
- INFO = "INFO",
- WARN = "WARN",
- DEBUG = "DEBUG",
- ERROR = "ERROR",
- SYS = "SYS",
+ UNKNOWN = 'UNKNOWN',
+ INFO = 'INFO',
+ WARN = 'WARN',
+ DEBUG = 'DEBUG',
+ ERROR = 'ERROR',
+ SYS = 'SYS',
}
-const logLevelOrder: Array<LogLevel> = [
- LogLevel.DEBUG,
- LogLevel.INFO,
- LogLevel.WARN,
- LogLevel.ERROR,
- LogLevel.SYS,
-];
+const logLevelOrder: Array<LogLevel> = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR, LogLevel.SYS];
export const isLogLevel = (l: unknown): l is LogLevel =>
- typeof l === "string" && logLevelOrder.some((level) => level === l);
-
+ typeof l === 'string' && logLevelOrder.some((level) => level === l);
const defaultAllowedLevels = () =>
- [
- LogLevel.UNKNOWN,
- ...(isDebug() ? [LogLevel.DEBUG] : []),
- LogLevel.INFO,
- LogLevel.WARN,
- LogLevel.ERROR,
- LogLevel.SYS,
- ] as Array<LogLevel>;
+ [
+ LogLevel.UNKNOWN,
+ ...(isDebug() ? [LogLevel.DEBUG] : []),
+ LogLevel.INFO,
+ LogLevel.WARN,
+ LogLevel.ERROR,
+ LogLevel.SYS,
+ ] as Array<LogLevel>;
export interface ILogger {
- readonly log: (level: LogLevel, ...args: string[]) => void;
+ readonly log: (level: LogLevel, ...args: string[]) => void;
}
class LoggerImpl implements ILogger {
private readonly textEncoder = new TextEncoder();
public log(level: LogLevel, ...trace: string[]) {
- const message = JSON.stringify({
- level,
- trace,
- }, null, 4);
+ const message = JSON.stringify(
+ {
+ level,
+ trace,
+ },
+ null,
+ 4,
+ );
const styled = `${this.getStyle(level)}${message}${ANSI.RESET}\n`;
- this.getStream(level).writeSync(this.textEncoder.encode(styled));
+ this.getStream(level)(this.textEncoder.encode(styled));
}
private getStream(level: LogLevel) {
if (level === LogLevel.ERROR) {
- return Deno.stderr;
+ return console.error;
}
- return Deno.stdout;
+ return console.log;
}
private getStyle(level: LogLevel) {
@@ -127,17 +110,17 @@ class LoggerImpl implements ILogger {
}
export const ANSI = {
- RESET: "\x1b[0m",
- BOLD: "\x1b[1m",
- DIM: "\x1b[2m",
- RED: "\x1b[31m",
- GREEN: "\x1b[32m",
- YELLOW: "\x1b[33m",
- BLUE: "\x1b[34m",
- MAGENTA: "\x1b[35m",
- CYAN: "\x1b[36m",
- WHITE: "\x1b[37m",
- BRIGHT_RED: "\x1b[91m",
- BRIGHT_YELLOW: "\x1b[93m",
- GRAY: "\x1b[90m",
+ RESET: '\x1b[0m',
+ BOLD: '\x1b[1m',
+ DIM: '\x1b[2m',
+ RED: '\x1b[31m',
+ GREEN: '\x1b[32m',
+ YELLOW: '\x1b[33m',
+ BLUE: '\x1b[34m',
+ MAGENTA: '\x1b[35m',
+ CYAN: '\x1b[36m',
+ WHITE: '\x1b[37m',
+ BRIGHT_RED: '\x1b[91m',
+ BRIGHT_YELLOW: '\x1b[93m',
+ GRAY: '\x1b[90m',
};
diff --git a/u/trace/metrics.ts b/u/trace/metrics.ts
index 822fc38..2301afd 100644
--- a/u/trace/metrics.ts
+++ b/u/trace/metrics.ts
@@ -1,151 +1,140 @@
import {
- isObject,
- type ITrace,
- type ITraceWith,
- type Mapper,
- type SideEffect,
- type Supplier,
-} from "@emprespresso/pengueno";
+ isObject,
+ type ITrace,
+ type ITraceWith,
+ type Mapper,
+ type SideEffect,
+ type Supplier,
+} from '@emprespresso/pengueno';
export enum Unit {
- COUNT = "COUNT",
- MILLISECONDS = "MILLISECONDS",
+ COUNT = 'COUNT',
+ MILLISECONDS = 'MILLISECONDS',
}
export interface IMetric {
- readonly count: IEmittableMetric;
- readonly time: IEmittableMetric;
- readonly failure?: IMetric;
- readonly success?: IMetric;
- readonly warn?: IMetric;
- readonly children: Supplier<Array<IMetric>>;
-
- readonly _tag: "IMetric";
+ readonly count: IEmittableMetric;
+ readonly time: IEmittableMetric;
+ readonly failure: undefined | IMetric;
+ readonly success: undefined | IMetric;
+ readonly warn: undefined | IMetric;
+ readonly children: Supplier<Array<IMetric>>;
+
+ readonly _tag: 'IMetric';
}
-export const isIMetric = (t: unknown): t is IMetric =>
- isObject(t) && "_tag" in t && t._tag === "IMetric";
+export const isIMetric = (t: unknown): t is IMetric => isObject(t) && '_tag' in t && t._tag === 'IMetric';
export interface IEmittableMetric {
- readonly name: string;
- readonly unit: Unit;
- withValue: Mapper<number, MetricValue>;
+ readonly name: string;
+ readonly unit: Unit;
+ withValue: Mapper<number, MetricValue>;
}
export class EmittableMetric implements IEmittableMetric {
- constructor(
- public readonly name: string,
- public readonly unit: Unit,
- ) {}
-
- public withValue(value: number): MetricValue {
- return {
- name: this.name,
- unit: this.unit,
- emissionTimestamp: Date.now(),
- value,
- _tag: "MetricValue",
- };
- }
+ constructor(
+ public readonly name: string,
+ public readonly unit: Unit,
+ ) {}
+
+ public withValue(value: number): MetricValue {
+ return {
+ name: this.name,
+ unit: this.unit,
+ emissionTimestamp: Date.now(),
+ value,
+ _tag: 'MetricValue',
+ };
+ }
}
export class Metric implements IMetric {
- constructor(
- public readonly count: IEmittableMetric,
- public readonly time: IEmittableMetric,
- public readonly failure?: Metric,
- public readonly success?: Metric,
- public readonly warn?: Metric,
- public readonly _tag: "IMetric" = "IMetric",
- ) {}
-
- public children() {
- return [this.failure, this.success, this.warn].filter(
- (x) => x,
- ) as IMetric[];
- }
-
- static fromName(name: string, addChildren = true): Metric {
- return new Metric(
- new EmittableMetric(`${name}.count`, Unit.COUNT),
- new EmittableMetric(`${name}.elapsed`, Unit.MILLISECONDS),
- addChildren ? Metric.fromName(`${name}.failure`, false) : undefined,
- addChildren ? Metric.fromName(`${name}.success`, false) : undefined,
- addChildren ? Metric.fromName(`${name}.warn`, false) : undefined,
- );
- }
+ constructor(
+ public readonly count: IEmittableMetric,
+ public readonly time: IEmittableMetric,
+ public readonly failure: undefined | Metric = undefined,
+ public readonly success: undefined | Metric = undefined,
+ public readonly warn: undefined | Metric = undefined,
+ public readonly _tag: 'IMetric' = 'IMetric',
+ ) {}
+
+ public children() {
+ return [this.failure, this.success, this.warn].filter((x) => x) as IMetric[];
+ }
+
+ static fromName(name: string, addChildren = true): Metric {
+ return new Metric(
+ new EmittableMetric(`${name}.count`, Unit.COUNT),
+ new EmittableMetric(`${name}.elapsed`, Unit.MILLISECONDS),
+ addChildren ? Metric.fromName(`${name}.failure`, false) : undefined,
+ addChildren ? Metric.fromName(`${name}.success`, false) : undefined,
+ addChildren ? Metric.fromName(`${name}.warn`, false) : undefined,
+ );
+ }
}
export interface MetricValue {
- readonly name: string;
- readonly unit: Unit;
- readonly value: number;
- readonly emissionTimestamp: number;
- readonly _tag: "MetricValue";
+ readonly name: string;
+ readonly unit: Unit;
+ readonly value: number;
+ readonly emissionTimestamp: number;
+ readonly _tag: 'MetricValue';
}
-export const isMetricValue = (t: unknown): t is MetricValue =>
- isObject(t) && "_tag" in t && t._tag === "MetricValue";
+export const isMetricValue = (t: unknown): t is MetricValue => isObject(t) && '_tag' in t && t._tag === 'MetricValue';
-export const isMetricsTraceSupplier = (t: unknown): t is MetricsTraceSupplier =>
- isMetricValue(t) || isIMetric(t);
+export const isMetricsTraceSupplier = (t: unknown): t is MetricsTraceSupplier => isMetricValue(t) || isIMetric(t);
-export type MetricsTraceSupplier = ITraceWith<
- IMetric | MetricValue | undefined
->;
+export type MetricsTraceSupplier = ITraceWith<IMetric | MetricValue | undefined>;
type MetricTracingTuple = [IMetric, Date];
export class MetricsTrace implements ITrace<MetricsTraceSupplier> {
- constructor(
- private readonly metricConsumer: SideEffect<Array<MetricValue>>,
- private readonly tracing: Array<MetricTracingTuple> = [],
- private readonly flushed: Set<IMetric> = new Set(),
- ) {}
-
- public addTrace(trace: MetricsTraceSupplier) {
- if (!isIMetric(trace)) return this;
- return new MetricsTrace(this.metricConsumer)._nowTracing(trace);
- }
-
- public trace(metric: MetricsTraceSupplier) {
- if (typeof metric === "undefined" || typeof metric === "string")
- return this;
- if (isMetricValue(metric)) {
- this.metricConsumer([metric]);
- return this;
+ constructor(
+ private readonly metricConsumer: SideEffect<Array<MetricValue>>,
+ private readonly tracing: Array<MetricTracingTuple> = [],
+ private readonly flushed: Set<IMetric> = new Set(),
+ ) {}
+
+ public addTrace(trace: MetricsTraceSupplier) {
+ if (!isIMetric(trace)) return this;
+ return new MetricsTrace(this.metricConsumer)._nowTracing(trace);
}
- const foundMetricValues = this.tracing
- .flatMap(([tracing, startedTracing]) =>
- [tracing, ...tracing.children()]
- .filter((_tracing) => metric === _tracing)
- .flatMap((metric) => [
- this.addMetric(metric, startedTracing),
- this.addMetric(tracing, startedTracing),
- ]),
- )
- .flatMap((values) => values);
-
- if (foundMetricValues.length === 0) {
- return this._nowTracing(metric);
+ public trace(metric: MetricsTraceSupplier) {
+ if (typeof metric === 'undefined' || typeof metric === 'string') return this;
+ if (isMetricValue(metric)) {
+ this.metricConsumer([metric]);
+ return this;
+ }
+
+ const foundMetricValues = this.tracing
+ .flatMap(([tracing, startedTracing]) =>
+ [tracing, ...tracing.children()]
+ .filter((_tracing) => metric === _tracing)
+ .flatMap((metric) => [
+ this.addMetric(metric, startedTracing),
+ this.addMetric(tracing, startedTracing),
+ ]),
+ )
+ .flatMap((values) => values);
+
+ if (foundMetricValues.length === 0) {
+ return this._nowTracing(metric);
+ }
+
+ this.metricConsumer(foundMetricValues);
+ return this;
}
- this.metricConsumer(foundMetricValues);
- return this;
- }
+ private addMetric(metric: IMetric, startedTracing: Date): Array<MetricValue> {
+ if (this.flushed.has(metric)) {
+ return [];
+ }
- private addMetric(metric: IMetric, startedTracing: Date): Array<MetricValue> {
- if (this.flushed.has(metric)) {
- return [];
+ this.flushed.add(metric);
+ return [metric.count.withValue(1.0), metric.time.withValue(Date.now() - startedTracing.getTime())];
}
- this.flushed.add(metric);
- return [
- metric.count.withValue(1.0),
- metric.time.withValue(Date.now() - startedTracing.getTime()),
- ];
- }
-
- private _nowTracing(metric?: IMetric): MetricsTrace {
- if (!metric) return this;
- this.tracing.push([metric, new Date()]);
- return this;
- }
+ private _nowTracing(metric?: IMetric): MetricsTrace {
+ if (!metric) return this;
+ this.tracing.push([metric, new Date()]);
+ return this;
+ }
}
diff --git a/u/trace/mod.ts b/u/trace/mod.ts
deleted file mode 100644
index 0f9b61b..0000000
--- a/u/trace/mod.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from "./itrace.ts";
-export * from "./util.ts";
-export * from "./logger.ts";
-export * from "./metrics.ts";
-export * from "./trace.ts";
diff --git a/u/trace/trace.ts b/u/trace/trace.ts
index 5629c28..acc116f 100644
--- a/u/trace/trace.ts
+++ b/u/trace/trace.ts
@@ -1,86 +1,69 @@
import {
- isMetricsTraceSupplier,
- type ITrace,
- type ITraceable,
- type ITraceWith,
- LogLevel,
- LogTrace,
- type LogTraceSupplier,
- MetricsTrace,
- type MetricsTraceSupplier,
- type MetricValue,
- TraceableImpl,
-} from "@emprespresso/pengueno";
+ isMetricsTraceSupplier,
+ type ITrace,
+ type ITraceable,
+ type ITraceWith,
+ LogLevel,
+ LogTrace,
+ type LogTraceSupplier,
+ MetricsTrace,
+ type MetricsTraceSupplier,
+ type MetricValue,
+ TraceableImpl,
+} from '@emprespresso/pengueno';
export class LogTraceable<T> extends TraceableImpl<T, LogTraceSupplier> {
- public static LogTrace = new LogTrace();
- static of<T>(t: T) {
- return new LogTraceable(t, LogTraceable.LogTrace);
- }
+ public static LogTrace = new LogTrace();
+ static of<T>(t: T) {
+ return new LogTraceable(t, LogTraceable.LogTrace);
+ }
}
-const getEmbeddedMetricConsumer =
- (logTrace: ITrace<LogTraceSupplier>) =>
- (metrics: Array<MetricValue>) =>
+const getEmbeddedMetricConsumer = (logTrace: ITrace<LogTraceSupplier>) => (metrics: Array<MetricValue>) =>
logTrace.addTrace(LogLevel.SYS).trace(`Metrics = <metrics>${JSON.stringify(metrics)}</metrics>`);
-export class EmbeddedMetricsTraceable<T> extends TraceableImpl<
- T,
- MetricsTraceSupplier
-> {
- public static MetricsTrace = new MetricsTrace(
- getEmbeddedMetricConsumer(LogTraceable.LogTrace),
- );
+export class EmbeddedMetricsTraceable<T> extends TraceableImpl<T, MetricsTraceSupplier> {
+ public static MetricsTrace = new MetricsTrace(getEmbeddedMetricConsumer(LogTraceable.LogTrace));
- static of<T>(t: T, metricsTrace = EmbeddedMetricsTraceable.MetricsTrace) {
- return new EmbeddedMetricsTraceable(t, metricsTrace);
- }
+ static of<T>(t: T, metricsTrace = EmbeddedMetricsTraceable.MetricsTrace) {
+ return new EmbeddedMetricsTraceable(t, metricsTrace);
+ }
}
-export type LogMetricTraceSupplier = ITraceWith<
- LogTraceSupplier | MetricsTraceSupplier
->;
+export type LogMetricTraceSupplier = ITraceWith<LogTraceSupplier | MetricsTraceSupplier>;
export class LogMetricTrace implements ITrace<LogMetricTraceSupplier> {
- constructor(
- private logTrace: ITrace<LogTraceSupplier>,
- private metricsTrace: ITrace<MetricsTraceSupplier>,
- ) {}
+ constructor(
+ private logTrace: ITrace<LogTraceSupplier>,
+ private metricsTrace: ITrace<MetricsTraceSupplier>,
+ ) {}
- public addTrace(
- trace: LogTraceSupplier | MetricsTraceSupplier,
- ): LogMetricTrace {
- if (isMetricsTraceSupplier(trace)) {
- this.metricsTrace = this.metricsTrace.addTrace(trace);
- return this;
+ public addTrace(trace: LogTraceSupplier | MetricsTraceSupplier): LogMetricTrace {
+ if (isMetricsTraceSupplier(trace)) {
+ this.metricsTrace = this.metricsTrace.addTrace(trace);
+ return this;
+ }
+ this.logTrace = this.logTrace.addTrace(trace);
+ return this;
}
- this.logTrace = this.logTrace.addTrace(trace);
- return this;
- }
- public trace(trace: LogTraceSupplier | MetricsTraceSupplier) {
- if (isMetricsTraceSupplier(trace)) {
- this.metricsTrace.trace(trace);
- return this;
+ public trace(trace: LogTraceSupplier | MetricsTraceSupplier) {
+ if (isMetricsTraceSupplier(trace)) {
+ this.metricsTrace.trace(trace);
+ return this;
+ }
+ this.logTrace.trace(trace);
+ return this;
}
- this.logTrace.trace(trace);
- return this;
- }
}
-export class LogMetricTraceable<T> extends TraceableImpl<
- T,
- MetricsTraceSupplier | LogTraceSupplier
-> {
- static ofLogTraceable<T>(t: ITraceable<T, LogTraceSupplier>) {
- const metricsTrace = new MetricsTrace(getEmbeddedMetricConsumer(t.trace));
- return new LogMetricTraceable(
- t.get(),
- new LogMetricTrace(t.trace, metricsTrace),
- );
- }
+export class LogMetricTraceable<T> extends TraceableImpl<T, MetricsTraceSupplier | LogTraceSupplier> {
+ static ofLogTraceable<T>(t: ITraceable<T, LogTraceSupplier>) {
+ const metricsTrace = new MetricsTrace(getEmbeddedMetricConsumer(t.trace));
+ return new LogMetricTraceable(t.get(), new LogMetricTrace(t.trace, metricsTrace));
+ }
- static of<T>(t: T) {
- const logTrace = LogTraceable.of(t);
- return LogMetricTraceable.ofLogTraceable(logTrace);
- }
+ static of<T>(t: T) {
+ const logTrace = LogTraceable.of(t);
+ return LogMetricTraceable.ofLogTraceable(logTrace);
+ }
}
diff --git a/u/trace/util.ts b/u/trace/util.ts
index e2200b9..db1db63 100644
--- a/u/trace/util.ts
+++ b/u/trace/util.ts
@@ -1,47 +1,45 @@
import {
-ANSI,
- type Callable,
- type IMetric,
- type ITraceableMapper,
- type ITraceableTuple,
- type MetricsTraceSupplier,
-} from "@emprespresso/pengueno";
+ ANSI,
+ type Callable,
+ type IMetric,
+ type ITraceableMapper,
+ type ITraceableTuple,
+ type MetricsTraceSupplier,
+} from '@emprespresso/pengueno';
export class TraceUtil {
- static withTrace<T, Trace>(
- trace: string,
- ansi?: Array<keyof typeof ANSI>
- ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
- if (ansi) {
- return (t) => [t.get(), `${ansi.join("")}${trace}${ANSI.RESET}`];
- }
- return (t) => [t.get(), trace];
- }
+ static withTrace<T, Trace>(
+ trace: string,
+ ansi?: Array<keyof typeof ANSI>,
+ ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
+ if (ansi) {
+ return (t) => [t.get(), `${ansi.join('')}${trace}${ANSI.RESET}`];
+ }
+ return (t) => [t.get(), trace];
+ }
- static withMetricTrace<T, Trace extends MetricsTraceSupplier>(
- metric: IMetric,
- ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
- return (t) => [t.get(), metric as Trace];
- }
+ static withMetricTrace<T, Trace extends MetricsTraceSupplier>(
+ metric: IMetric,
+ ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
+ return (t) => [t.get(), metric as Trace];
+ }
- static withFunctionTrace<F extends Callable, T, Trace>(
- f: F,
- ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
- return TraceUtil.withTrace(`fn.${f.name}`);
- }
+ static withFunctionTrace<F extends Callable, T, Trace>(
+ f: F,
+ ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
+ return TraceUtil.withTrace(`fn.${f.name}`);
+ }
- static withClassTrace<C extends object, T, Trace>(
- c: C,
- ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
- return TraceUtil.withTrace(`class.${c.constructor.name}`);
- }
+ static withClassTrace<C extends object, T, Trace>(
+ c: C,
+ ): ITraceableMapper<T, ITraceableTuple<T, Trace | Array<Trace>>, Trace> {
+ return TraceUtil.withTrace(`class.${c.constructor.name}`);
+ }
- static promiseify<T, U, Trace>(
- mapper: ITraceableMapper<T, U, Trace>,
- ): ITraceableMapper<Promise<T>, Promise<U>, Trace> {
- return (traceablePromise) =>
- traceablePromise
- .flatMapAsync(async (t) => t.move(await t.get()).map(mapper))
- .get();
- }
+ static promiseify<T, U, Trace>(
+ mapper: ITraceableMapper<T, U, Trace>,
+ ): ITraceableMapper<Promise<T>, Promise<U>, Trace> {
+ return (traceablePromise) =>
+ traceablePromise.flatMapAsync(async (t) => t.move(await t.get()).map(mapper)).get();
+ }
}
diff --git a/u/tsconfig.json b/u/tsconfig.json
new file mode 100644
index 0000000..80e5ff4
--- /dev/null
+++ b/u/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./dist",
+ "rootDir": "./",
+ "composite": true,
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "noEmit": false,
+ "moduleResolution": "node"
+ },
+ "include": ["**/*.ts"],
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
+}