diff options
Diffstat (limited to 'u/server')
-rw-r--r-- | u/server/activity/fourohfour.ts | 28 | ||||
-rw-r--r-- | u/server/activity/health.ts | 49 | ||||
-rw-r--r-- | u/server/activity/index.ts | 8 | ||||
-rw-r--r-- | u/server/filter/index.ts | 34 | ||||
-rw-r--r-- | u/server/filter/json.ts | 42 | ||||
-rw-r--r-- | u/server/filter/method.ts | 30 | ||||
-rw-r--r-- | u/server/http/body.ts | 10 | ||||
-rw-r--r-- | u/server/http/index.ts | 3 | ||||
-rw-r--r-- | u/server/http/method.ts | 1 | ||||
-rw-r--r-- | u/server/http/status.ts | 71 | ||||
-rw-r--r-- | u/server/index.ts | 13 | ||||
-rw-r--r-- | u/server/request/index.ts | 18 | ||||
-rw-r--r-- | u/server/request/pengueno.ts | 44 | ||||
-rw-r--r-- | u/server/response/index.ts | 18 | ||||
-rw-r--r-- | u/server/response/json_pengueno.ts | 29 | ||||
-rw-r--r-- | u/server/response/pengueno.ts | 59 |
16 files changed, 0 insertions, 457 deletions
diff --git a/u/server/activity/fourohfour.ts b/u/server/activity/fourohfour.ts deleted file mode 100644 index cd90ba0..0000000 --- a/u/server/activity/fourohfour.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - 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!', -]; -const randomFourOhFour = () => messages[Math.floor(Math.random() * messages.length)]!; - -export interface IFourOhFourActivity { - 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(); - } -} diff --git a/u/server/activity/health.ts b/u/server/activity/health.ts deleted file mode 100644 index 9396490..0000000 --- a/u/server/activity/health.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - type IActivity, - type IEither, - IMetric, - type ITraceable, - JsonResponse, - LogLevel, - type Mapper, - Metric, - type PenguenoRequest, - type ServerTrace, - TraceUtil, -} from '@emprespresso/pengueno'; - -export enum HealthCheckInput { - CHECK, -} -export enum HealthCheckOutput { - YAASSSLAYQUEEN, -} - -export interface IHealthCheckActivity { - checkHealth: IActivity; -} - -const healthCheckMetric = Metric.fromName('Health').asResult(); -export interface HealthChecker - extends Mapper<ITraceable<HealthCheckInput, ServerTrace>, Promise<IEither<Error, HealthCheckOutput>>> {} -export class HealthCheckActivityImpl implements IHealthCheckActivity { - constructor(private readonly check: HealthChecker) {} - - public checkHealth(req: ITraceable<PenguenoRequest, ServerTrace>) { - return req - .flatMap(TraceUtil.withFunctionTrace(this.checkHealth)) - .flatMap(TraceUtil.withMetricTrace(healthCheckMetric)) - .flatMap((r) => r.move(HealthCheckInput.CHECK).map((input) => this.check(input))) - .peek(TraceUtil.promiseify(TraceUtil.traceResultingEither(healthCheckMetric))) - .map( - TraceUtil.promiseify((h) => { - const { status, message } = h.get().fold( - () => ({ status: 500, message: 'err' }), - () => ({ status: 200, message: 'ok' }), - ); - return new JsonResponse(req, message, { status }); - }), - ) - .get(); - } -} diff --git a/u/server/activity/index.ts b/u/server/activity/index.ts deleted file mode 100644 index fa0a6b2..0000000 --- a/u/server/activity/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -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/filter/index.ts b/u/server/filter/index.ts deleted file mode 100644 index 75168c7..0000000 --- a/u/server/filter/index.ts +++ /dev/null @@ -1,34 +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): IEither<Err, T> | 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 deleted file mode 100644 index bc53d47..0000000 --- a/u/server/filter/json.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - 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>; -} - -const ParseJsonMetric = Metric.fromName('JsonParse').asResult(); -export const jsonModel = - <MessageT>(jsonTransformer: JsonTransformer<MessageT>): RequestFilter<MessageT> => - (r: ITraceable<PenguenoRequest, ServerTrace>) => - r - .flatMap(TraceUtil.withFunctionTrace(jsonModel)) - .flatMap(TraceUtil.withMetricTrace(ParseJsonMetric)) - .map((j) => - Either.fromFailableAsync<Error, MessageT>(<Promise<MessageT>>j.get().req.json()).then((either) => - either.mapLeft((errReason) => { - j.trace.traceScope(LogLevel.WARN).trace(errReason); - return new PenguenoError('seems to be invalid JSON (>//<) can you fix?', 400); - }), - ), - ) - .flatMapAsync(TraceUtil.promiseify(TraceUtil.traceResultingEither(ParseJsonMetric))) - .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 deleted file mode 100644 index 7d6aa76..0000000 --- a/u/server/filter/method.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - Either, - HttpMethod, - IEither, - type ITraceable, - LogLevel, - PenguenoError, - type PenguenoRequest, - type RequestFilter, - type ServerTrace, - TraceUtil, -} from '@emprespresso/pengueno'; - -export const requireMethod = - (methods: Array<HttpMethod>): RequestFilter<HttpMethod> => - (req: ITraceable<PenguenoRequest, ServerTrace>) => - req - .flatMap(TraceUtil.withFunctionTrace(requireMethod)) - .map((t): IEither<PenguenoError, HttpMethod> => { - const { - req: { method }, - } = t.get(); - if (!methods.includes(method)) { - const msg = "that's not how you pet me (âīšâ)~"; - t.trace.traceScope(LogLevel.WARN).trace(msg); - return Either.left(new PenguenoError(msg, 405)); - } - return Either.right(method); - }) - .get(); diff --git a/u/server/http/body.ts b/u/server/http/body.ts deleted file mode 100644 index 5fc4caa..0000000 --- a/u/server/http/body.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type Body = - | ArrayBuffer - | AsyncIterable<Uint8Array> - | Blob - | FormData - | Iterable<Uint8Array> - | NodeJS.ArrayBufferView - | URLSearchParams - | null - | string; diff --git a/u/server/http/index.ts b/u/server/http/index.ts deleted file mode 100644 index ef1c039..0000000 --- a/u/server/http/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './body.js'; -export * from './status.js'; -export * from './method.js'; diff --git a/u/server/http/method.ts b/u/server/http/method.ts deleted file mode 100644 index 172d77a..0000000 --- a/u/server/http/method.ts +++ /dev/null @@ -1 +0,0 @@ -export type HttpMethod = 'POST' | 'GET' | 'HEAD' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH'; diff --git a/u/server/http/status.ts b/u/server/http/status.ts deleted file mode 100644 index 15cb30c..0000000 --- a/u/server/http/status.ts +++ /dev/null @@ -1,71 +0,0 @@ -export const HttpStatusCodes: Record<number, string> = { - 100: 'Continue', - 101: 'Switching Protocols', - 102: 'Processing (WebDAV)', - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non-Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - 207: 'Multi-Status (WebDAV)', - 208: 'Already Reported (WebDAV)', - 226: 'IM Used', - 300: 'Multiple Choices', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 306: '(Unused)', - 307: 'Temporary Redirect', - 308: 'Permanent Redirect (experimental)', - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Request Entity Too Large', - 414: 'Request-URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Requested Range Not Satisfiable', - 417: 'Expectation Failed', - 418: "I'm a teapot (RFC 2324)", - 420: 'Enhance Your Calm (Twitter)', - 422: 'Unprocessable Entity (WebDAV)', - 423: 'Locked (WebDAV)', - 424: 'Failed Dependency (WebDAV)', - 425: 'Reserved for WebDAV', - 426: 'Upgrade Required', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - 444: 'No Response (Nginx)', - 449: 'Retry With (Microsoft)', - 450: 'Blocked by Windows Parental Controls (Microsoft)', - 451: 'Unavailable For Legal Reasons', - 499: 'Client Closed Request (Nginx)', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 506: 'Variant Also Negotiates (Experimental)', - 507: 'Insufficient Storage (WebDAV)', - 508: 'Loop Detected (WebDAV)', - 509: 'Bandwidth Limit Exceeded (Apache)', - 510: 'Not Extended', - 511: 'Network Authentication Required', - 598: 'Network read timeout error', - 599: 'Network connect timeout error', -}; diff --git a/u/server/index.ts b/u/server/index.ts deleted file mode 100644 index 1cefb71..0000000 --- a/u/server/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { ITraceable, LogMetricTraceSupplier, Mapper } from '@emprespresso/pengueno'; -export type ServerTrace = LogMetricTraceSupplier; - -export * from './http/index.js'; -export * from './response/index.js'; -export * from './request/index.js'; -export * from './activity/index.js'; -export * from './filter/index.js'; - -import { PenguenoRequest, PenguenoResponse } from '@emprespresso/pengueno'; -export interface Server { - readonly serve: Mapper<ITraceable<PenguenoRequest, ServerTrace>, Promise<PenguenoResponse>>; -} diff --git a/u/server/request/index.ts b/u/server/request/index.ts deleted file mode 100644 index 41d59b7..0000000 --- a/u/server/request/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { HttpMethod } from '@emprespresso/pengueno'; - -export interface BaseRequest { - url: string; - method: HttpMethod; - - header(): Record<string, string>; - - formData(): Promise<FormData>; - json(): Promise<unknown>; - text(): Promise<string>; - - param(key: string): string | undefined; - query(): Record<string, string>; - queries(): Record<string, string[]>; -} - -export * from './pengueno.js'; diff --git a/u/server/request/pengueno.ts b/u/server/request/pengueno.ts deleted file mode 100644 index 31563e9..0000000 --- a/u/server/request/pengueno.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BaseRequest, ITraceable, ServerTrace } from '@emprespresso/pengueno'; - -const greetings = ['hewwo :D', 'hiya cutie', 'boop!', 'sending virtual hugs!', 'stay pawsitive']; -const penguenoGreeting = () => greetings[Math.floor(Math.random() * greetings.length)]; - -export class PenguenoRequest { - private constructor( - public readonly req: BaseRequest, - private readonly id: string, - private readonly at: Date, - ) {} - - public elapsedTimeMs(after = () => Date.now()): number { - return after() - this.at.getTime(); - } - - public getResponseHeaders(): Record<string, string> { - const RequestId = this.id; - const RequestReceivedUnix = this.at.getTime(); - const RequestHandleUnix = Date.now(); - const DeltaUnix = this.elapsedTimeMs(() => RequestHandleUnix); - const Hai = penguenoGreeting(); - - return Object.entries({ - RequestId, - RequestReceivedUnix, - RequestHandleUnix, - DeltaUnix, - Hai, - }).reduce((acc, [key, val]) => ({ ...acc, [key]: val!.toString() }), {}); - } - - public static from(request: ITraceable<BaseRequest, ServerTrace>): ITraceable<PenguenoRequest, ServerTrace> { - const id = crypto.randomUUID(); - return request.bimap((tRequest) => { - const request = tRequest.get(); - const url = new URL(request.url); - const { pathname } = url; - const trace = `RequestId = ${id}, Method = ${request.method}, Path = ${pathname}`; - - return { item: new PenguenoRequest(request, id, new Date()), trace }; - }); - } -} diff --git a/u/server/response/index.ts b/u/server/response/index.ts deleted file mode 100644 index 17a2d97..0000000 --- a/u/server/response/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Body } from '@emprespresso/pengueno'; - -export interface BaseResponse { - status: number; - statusText: string; - headers: Record<string, string>; - - body(): Body; -} - -export interface ResponseOpts { - status: number; - statusText?: string; - headers?: Record<string, string>; -} - -export * from './pengueno.js'; -export * from './json_pengueno.js'; diff --git a/u/server/response/json_pengueno.ts b/u/server/response/json_pengueno.ts deleted file mode 100644 index d0b74a8..0000000 --- a/u/server/response/json_pengueno.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - isEither, - ITraceable, - PenguenoRequest, - PenguenoResponse, - ResponseOpts, - ServerTrace, -} from '@emprespresso/pengueno'; - -type Jsonable = any; -export class JsonResponse extends PenguenoResponse { - constructor(req: ITraceable<PenguenoRequest, ServerTrace>, e: Jsonable, _opts: ResponseOpts) { - const opts = { ..._opts, headers: { ..._opts.headers, 'Content-Type': 'application/json' } }; - if (isEither<Jsonable, Jsonable>(e)) { - super( - req, - JSON.stringify( - e.fold( - (error) => ({ error, ok: undefined }), - (ok) => ({ ok }), - ), - ), - opts, - ); - return; - } - super(req, JSON.stringify(Math.floor(opts.status / 100) > 4 ? { error: e } : { ok: e }), opts); - } -} diff --git a/u/server/response/pengueno.ts b/u/server/response/pengueno.ts deleted file mode 100644 index 5a953db..0000000 --- a/u/server/response/pengueno.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - BaseResponse, - Body, - HttpStatusCodes, - ITraceable, - Metric, - Optional, - PenguenoRequest, - ResponseOpts, - ServerTrace, -} from '@emprespresso/pengueno'; - -const getHeaders = (req: PenguenoRequest, extraHeaders: Record<string, string>) => { - const optHeaders = { - ...req.getResponseHeaders(), - ...extraHeaders, - }; - optHeaders['Content-Type'] = (optHeaders['Content-Type'] ?? 'text/plain') + '; charset=utf-8'; - return optHeaders; -}; - -const ResponseCodeMetrics = [0, 1, 2, 3, 4, 5].map((x) => Metric.fromName(`response.${x}xx`).asResult()); -export const getResponseMetrics = (status: number, elapsedMs?: number) => { - const index = Math.floor(status / 100); - return ResponseCodeMetrics.flatMap((metric, i) => - Optional.from(i) - .filter((i) => i === index) - .map(() => [metric.count.withValue(1.0)]) - .flatMap((metricValues) => - Optional.from(elapsedMs) - .map((ms) => metricValues.concat(metric.time.withValue(ms))) - .orSome(() => metricValues), - ) - .orSome(() => [metric.count.withValue(0.0)]) - .get(), - ); -}; - -export class PenguenoResponse implements BaseResponse { - public readonly statusText: string; - public readonly status: number; - public readonly headers: Record<string, string>; - - constructor( - req: ITraceable<PenguenoRequest, ServerTrace>, - private readonly _body: Body, - opts: ResponseOpts, - ) { - this.headers = getHeaders(req.get(), opts?.headers ?? {}); - this.status = opts.status; - this.statusText = opts.statusText ?? HttpStatusCodes[this.status]!; - - req.trace.trace(getResponseMetrics(opts.status, req.get().elapsedTimeMs())); - } - - public body() { - return this._body; - } -} |