{"version":3,"file":"handler.cjs","names":["Code","StatusCode","hasText","StatusError","Details","Status","HTTPException"],"sources":["../../src/hono/handler.ts"],"sourcesContent":["import { hasText } from '@shware/utils';\nimport type { Context } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\nimport type { RequestIdVariables } from 'hono/request-id';\nimport type { Bindings, HTTPResponseError } from 'hono/types';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\nimport { Details } from '../error/detail';\nimport { Code, Status, StatusCode, StatusError } from '../error/status';\n\nexport type Env = {\n  Variables: RequestIdVariables;\n  Bindings?: Bindings;\n};\n\ntype AxiosError = {\n  code?: string;\n  cause?: unknown;\n  status?: number;\n  message?: string;\n  isAxiosError: boolean;\n  response?: {\n    data: unknown;\n    status: number;\n    statusText: string;\n    headers: Record<string, string>;\n  };\n  config?: { url?: string; data?: unknown; method?: string; headers?: Record<string, string> };\n};\n\n/**\n * `Code` maps a name to an HTTP status, and that mapping is not injective: 400 is INVALID_ARGUMENT,\n * FAILED_PRECONDITION and OUT_OF_RANGE alike. Reversing it therefore needs a tiebreaker for the\n * statuses several codes share — the rest is derived, so new entries in `Code` come along for free.\n */\nconst CANONICAL_CODE: Record<number, keyof typeof Code> = {\n  400: 'INVALID_ARGUMENT',\n  409: 'ALREADY_EXISTS',\n  500: 'INTERNAL',\n};\n\nconst CODE_BY_HTTP_STATUS: Record<number, keyof typeof Code> = {\n  ...Object.fromEntries(Object.entries(Code).map(([name, status]) => [status, name])),\n  ...CANONICAL_CODE,\n};\n\n/** Canonical code for errors that only carry an HTTP status, such as hono's `HTTPException`. */\nfunction toStatusCode(status: number, message?: string): StatusCode {\n  const code = CODE_BY_HTTP_STATUS[status] ?? (status < 500 ? 'INVALID_ARGUMENT' : 'INTERNAL');\n  return StatusCode.of(code, hasText(message) ? message : undefined);\n}\n\nfunction summarize(error: unknown): string {\n  if (error instanceof Error) return `${error.name}: ${error.message}`;\n  return String(error);\n}\n\nexport function isAxiosError(payload: unknown): payload is AxiosError {\n  return (\n    payload !== null &&\n    typeof payload === 'object' &&\n    'isAxiosError' in payload &&\n    payload.isAxiosError === true\n  );\n}\n\n/**\n * `instanceof` alone is not enough: two copies of this package (duplicated in a lockfile, or one\n * bundled into a dependency) produce distinct classes, and `Status.adapter` may swap in a\n * host-defined error class entirely. Fall back to recognizing the error by shape.\n */\nexport function isStatusError(payload: unknown): payload is StatusError {\n  if (payload instanceof StatusError) return true;\n  if (payload === null || typeof payload !== 'object') return false;\n  const { status, body } = payload as { status?: unknown; body?: { error?: { code?: unknown } } };\n  return typeof status === 'number' && typeof body?.error?.code === 'number';\n}\n\nexport function errorHandler<E extends Env = never>(\n  error: Error | HTTPResponseError,\n  c: Context<E>\n): Response | Promise<Response> {\n  const requestId = c.get('requestId');\n  const servingData = `${c.req.method}: ${c.req.path}`;\n  const details = Details.new().requestInfo({ requestId, servingData });\n\n  if (isStatusError(error)) {\n    const body = error.body ?? Status.internal().body();\n    body.error.details.push(...details.list);\n    // Only 5xx is a server fault; logging expected 4xx at error level drowns out the real ones.\n    // Field violations travel inside `details`, so one line carries the whole error.\n    const log = error.status >= 500 ? console.error : console.warn;\n    log(servingData, JSON.stringify(body.error));\n    return c.json(body, error.status as ContentfulStatusCode);\n  }\n\n  // The client vanished mid-request: tab closed, navigation, or an aborted fetch. Whatever we\n  // return is discarded, and nothing failed on our side, so keep it to one terse line. Shows up\n  // in local dev because the request body is streamed straight off the socket — a truncated body\n  // fails to parse; platforms that buffer the whole request before invoking (API Gateway, Lambda\n  // function URLs) never hand us a partial request in the first place.\n  if (c.req.raw.signal.aborted) {\n    console.warn(`Client closed request: ${servingData}`, summarize(error));\n    return Status.cancelled().response(details);\n  }\n\n  if (error instanceof HTTPException) {\n    // Middleware such as `bearerAuth` attaches a prepared response carrying headers the client\n    // needs (`WWW-Authenticate`, …); hand that back untouched instead of re-wrapping it.\n    const log = error.status >= 500 ? console.error : console.warn;\n    log(`HTTP exception: ${servingData}`, error.status, error.message);\n    if (error.res) return error.res;\n    return toStatusCode(error.status, error.message).response(details);\n  }\n\n  if (isAxiosError(error)) {\n    console.error(`Axios error: ${servingData}`, {\n      requestId,\n      code: error.code,\n      status: error.status ?? error.response?.status,\n      message: error.message,\n      request: {\n        method: error.config?.method,\n        url: error.config?.url,\n        data: error.config?.data,\n      },\n      response: { data: error.response?.data },\n    });\n    return Status.internal('Axios error').response(details);\n  }\n\n  console.error(`Unknown error: ${servingData}`, requestId, error);\n  return Status.internal('Unknown error').response(details);\n}\n"],"mappings":";;;;;;;;;;;AAkCA,MAAM,iBAAoD;CACxD,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAEA,MAAM,sBAAyD;CAC7D,GAAG,OAAO,YAAY,OAAO,QAAQA,qBAAAA,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,CAAC,QAAQ,IAAI,CAAC,CAAC;CAClF,GAAG;AACL;;AAGA,SAAS,aAAa,QAAgB,SAA8B;CAClE,MAAM,OAAO,oBAAoB,YAAY,SAAS,MAAM,qBAAqB;CACjF,OAAOC,qBAAAA,WAAW,GAAG,OAAA,GAAMC,cAAAA,QAAAA,CAAQ,OAAO,IAAI,UAAU,KAAA,CAAS;AACnE;AAEA,SAAS,UAAU,OAAwB;CACzC,IAAI,iBAAiB,OAAO,OAAO,GAAG,MAAM,KAAK,IAAI,MAAM;CAC3D,OAAO,OAAO,KAAK;AACrB;AAEA,SAAgB,aAAa,SAAyC;CACpE,OACE,YAAY,QACZ,OAAO,YAAY,YACnB,kBAAkB,WAClB,QAAQ,iBAAiB;AAE7B;;;;;;AAOA,SAAgB,cAAc,SAA0C;CACtE,IAAI,mBAAmBC,qBAAAA,aAAa,OAAO;CAC3C,IAAI,YAAY,QAAQ,OAAO,YAAY,UAAU,OAAO;CAC5D,MAAM,EAAE,QAAQ,SAAS;CACzB,OAAO,OAAO,WAAW,YAAY,OAAO,MAAM,OAAO,SAAS;AACpE;AAEA,SAAgB,aACd,OACA,GAC8B;CAC9B,MAAM,YAAY,EAAE,IAAI,WAAW;CACnC,MAAM,cAAc,GAAG,EAAE,IAAI,OAAO,IAAI,EAAE,IAAI;CAC9C,MAAM,UAAUC,qBAAAA,QAAQ,IAAI,CAAC,CAAC,YAAY;EAAE;EAAW;CAAY,CAAC;CAEpE,IAAI,cAAc,KAAK,GAAG;EACxB,MAAM,OAAO,MAAM,QAAQC,qBAAAA,OAAO,SAAS,CAAC,CAAC,KAAK;EAClD,KAAK,MAAM,QAAQ,KAAK,GAAG,QAAQ,IAAI;EAIvC,CADY,MAAM,UAAU,MAAM,QAAQ,QAAQ,QAAQ,KAAA,CACtD,aAAa,KAAK,UAAU,KAAK,KAAK,CAAC;EAC3C,OAAO,EAAE,KAAK,MAAM,MAAM,MAA8B;CAC1D;CAOA,IAAI,EAAE,IAAI,IAAI,OAAO,SAAS;EAC5B,QAAQ,KAAK,0BAA0B,eAAe,UAAU,KAAK,CAAC;EACtE,OAAOA,qBAAAA,OAAO,UAAU,CAAC,CAAC,SAAS,OAAO;CAC5C;CAEA,IAAI,iBAAiBC,oBAAAA,eAAe;EAIlC,CADY,MAAM,UAAU,MAAM,QAAQ,QAAQ,QAAQ,KAAA,CACtD,mBAAmB,eAAe,MAAM,QAAQ,MAAM,OAAO;EACjE,IAAI,MAAM,KAAK,OAAO,MAAM;EAC5B,OAAO,aAAa,MAAM,QAAQ,MAAM,OAAO,CAAC,CAAC,SAAS,OAAO;CACnE;CAEA,IAAI,aAAa,KAAK,GAAG;EACvB,QAAQ,MAAM,gBAAgB,eAAe;GAC3C;GACA,MAAM,MAAM;GACZ,QAAQ,MAAM,UAAU,MAAM,UAAU;GACxC,SAAS,MAAM;GACf,SAAS;IACP,QAAQ,MAAM,QAAQ;IACtB,KAAK,MAAM,QAAQ;IACnB,MAAM,MAAM,QAAQ;GACtB;GACA,UAAU,EAAE,MAAM,MAAM,UAAU,KAAK;EACzC,CAAC;EACD,OAAOD,qBAAAA,OAAO,SAAS,aAAa,CAAC,CAAC,SAAS,OAAO;CACxD;CAEA,QAAQ,MAAM,kBAAkB,eAAe,WAAW,KAAK;CAC/D,OAAOA,qBAAAA,OAAO,SAAS,eAAe,CAAC,CAAC,SAAS,OAAO;AAC1D"}