import { type IEither, isEither, type ITraceable, Metric, type PenguenoRequest, type ServerTrace, } from "@emprespresso/pengueno"; export type ResponseBody = object | string; export type TResponseInit = ResponseInit & { status: number; headers?: Record; }; 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 ResponseCodeMetrics = [1, 2, 3, 4, 5].map((x) => Metric.fromName(`response.${x}xx`) ); export const getResponseMetric = (status: number) => { const index = (Math.floor(status / 100)) + 1; return ResponseCodeMetrics[index] ?? ResponseCodeMetrics[5 - 1]; }; export class PenguenoResponse extends Response { constructor( req: ITraceable, 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); } } export class JsonResponse extends PenguenoResponse { constructor( req: ITraceable, e: BodyInit | IEither, opts: TResponseInit, ) { const optsWithJsonContentType = { ...opts, headers: { ...opts?.headers, "Content-Type": "application/json", }, }; if (isEither(e)) { super( req, JSON.stringify( e.fold((err, ok) => err ? ({ error: err! }) : ({ ok: ok! })), ), optsWithJsonContentType, ); return; } super( req, JSON.stringify( (Math.floor(opts.status / 100) < 4) ? { ok: e } : { error: e }, ), optsWithJsonContentType, ); } }