55 lines · 1.9 KB · TypeScript
RawDownload
import * as Sentry from "@sentry/cloudflare";import { Data, Effect } from "effect";import {  HttpServerRespondable,  HttpServerResponse,} from "effect/unstable/http"; // Implements `Respondable` so the framework's default cause→response// pipeline (`HttpServerRespondable.toResponseOrElse`) renders this as the// declared JSON body + status code without an explicit `catchCause` at// every error boundary.export class HttpResponseError extends Data.TaggedError("HttpResponseError")<{  readonly status: number;  readonly code: string;  readonly message: string;}> {  [HttpServerRespondable.symbol](): Effect.Effect<HttpServerResponse.HttpServerResponse> {    if (this.status >= 500) Sentry.captureException(this);    return Effect.succeed(      HttpServerResponse.jsonUnsafe(        { error: this.message, code: this.code },        { status: this.status },      ),    );  }} const toHttpResponseError = (error: unknown): HttpResponseError =>  error instanceof HttpResponseError    ? error    : new HttpResponseError({        status: 500,        code: "internal_server_error",        message: "Internal server error",      }); export const isServerError = (error: unknown): boolean => toHttpResponseError(error).status >= 500; export const toErrorResponse = (error: unknown): Response => {  const mapped = toHttpResponseError(error);  if (mapped.status >= 500) Sentry.captureException(error);  return Response.json({ error: mapped.message, code: mapped.code }, { status: mapped.status });}; export const toErrorServerResponse = (error: unknown): HttpServerResponse.HttpServerResponse => {  const mapped = toHttpResponseError(error);  if (mapped.status >= 500) {    console.error("[api] toErrorServerResponse error:", error instanceof Error ? error.stack : error);    Sentry.captureException(error);  }  return HttpServerResponse.jsonUnsafe(    { error: mapped.message, code: mapped.code },    { status: mapped.status },  );};