hono-ban 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # hono-ban
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`dd1a1b1`](https://github.com/ali-issa/hono-ban/commit/dd1a1b1188aa722d263ab8592c26e1f097b63a51) Thanks [@ali-issa](https://github.com/ali-issa)! - `ban.validation()` accepts `cause`, and `hono-ban/postgresql` keeps its reporting contract for every
8
+ mapping kind.
9
+
10
+ - `ValidationOptions` gains an optional `cause`, stored on the error like every other `cause` and
11
+ never rendered, so a converted validator or driver failure reaches `ErrorReport.error.cause`.
12
+ - `postgresMapper()` passes the driver error as `cause` for `{ issue }` mappings too. Previously an
13
+ ordinary mapping put it on `report.error.cause` and an issue mapping left that member undefined.
14
+ - `postgresMapper()` reads `codes` entries as own properties, like `constraints` and `columns`, and
15
+ rejects a `retryAfter` above `Number.MAX_SAFE_INTEGER`, whose `String()` form is exponential and
16
+ not RFC 9110 `delay-seconds`.
17
+
3
18
  ## 1.2.0
4
19
 
5
20
  ### Minor Changes
package/README.md CHANGED
@@ -122,6 +122,18 @@ no row, such as a syntax error, a missing table, or a `RAISE EXCEPTION`, fall th
122
122
  constant 500 with `handled: false`. The full table and the precedence rules are in SPEC section
123
123
  6.10.
124
124
 
125
+ How each driver's support is verified (versions are the pins in `e2e/package.json`; the dated matrix
126
+ is in ADR 0015):
127
+
128
+ | Driver or ORM | Verified by |
129
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------ |
130
+ | node-postgres (`pg`) | Real server errors over the wire protocol (PGlite behind `pglite-socket`) in the e2e suite |
131
+ | postgres.js (`postgres`) | The same, plus a type-level check of its error type against the mapper's input type |
132
+ | PGlite | Real server errors in-process in the e2e suite |
133
+ | Drizzle | Real server errors through its wrapper in the e2e suite |
134
+ | Neon, Bun SQL, Sequelize 6 | Error shapes built from the driver source, driven over HTTP |
135
+ | Kysely, TypeORM, MikroORM, Slonik, Objection | Source inspection of how each rethrows or wraps the driver error (ADR 0015) |
136
+
125
137
  To put an application-authored `RAISE` message on the wire, compose your own `map`:
126
138
 
127
139
  ```ts
@@ -1,5 +1,5 @@
1
1
  import { f as ValidationIssue, n as BanError, u as HookTarget } from "./types-WuQ-karh.js";
2
- import { g as ValidationOptions } from "./types-DYr4CtCC.js";
2
+ import { g as ValidationOptions } from "./types-DH_JfdBQ.js";
3
3
  //#region src/validation/hook.d.ts
4
4
  /** The part of a `Ban` instance the validator hooks need. */
5
5
  interface ValidationSource {
@@ -12,4 +12,4 @@ interface HookResultBase {
12
12
  }
13
13
  //#endregion
14
14
  export { ValidationSource as n, HookResultBase as t };
15
- //# sourceMappingURL=hook-TWCLnCmZ.d.ts.map
15
+ //# sourceMappingURL=hook-RdzR-1NX.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-RdzR-1NX.d.ts","names":[],"sources":["../src/validation/hook.ts"],"mappings":";;;;UAOiB;WACN,aACP,QAAQ,cAAc,kBACtB,SAAS,sBACN;;;UAIU;WACN;WACA,QAAQ"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { _ as RenderContext, a as RenderedError, b as SchemaDialect, c as ErrorDefinition, d as IssueLocation, f as ValidationIssue, g as JsonSchema, h as pointerFromPath, i as BanErrorOptions, l as ResolvedDefinition, m as nameFromPath, n as BanError, o as Renderer, p as locationFromTarget, r as BanErrorInit, s as isBanError, t as ErrorFormat, u as HookTarget, v as RenderOptions, y as SchemaContext } from "./types-WuQ-karh.js";
2
2
  import { t as ValidationEntry } from "./validation-entries-nUGoBPKm.js";
3
3
  import { ProblemDetailsBody, ProblemDetailsOptions, problemDetails } from "./formats/problem-details.js";
4
- import { _ as BUILTIN_CATALOG, a as Catalog, b as FACTORY_NAMES, c as EmptyCatalog, d as ErrorReport, f as Factory, g as ValidationOptions, h as ReservedKey, i as BuiltinFactories, l as ErrorKey, m as NoReserved, n as BanInstance, o as CustomErrorInit, p as HandlerOptions, r as BanOptions, s as CustomFactories, t as Ban, u as ErrorMapper, v as BuiltinCatalog, x as FactoryName, y as BuiltinKey } from "./types-DYr4CtCC.js";
4
+ import { _ as BUILTIN_CATALOG, a as Catalog, b as FACTORY_NAMES, c as EmptyCatalog, d as ErrorReport, f as Factory, g as ValidationOptions, h as ReservedKey, i as BuiltinFactories, l as ErrorKey, m as NoReserved, n as BanInstance, o as CustomErrorInit, p as HandlerOptions, r as BanOptions, s as CustomFactories, t as Ban, u as ErrorMapper, v as BuiltinCatalog, x as FactoryName, y as BuiltinKey } from "./types-DH_JfdBQ.js";
5
5
  import { a as StandardJsonSchemaProps, c as StandardSchemaPathSegment, i as StandardJsonSchemaOptions, n as StandardJsonSchema, o as StandardJsonSchemaTarget, r as StandardJsonSchemaConverter, s as StandardSchemaIssue, t as InferStandardOutput } from "./standard-schema-types-Aa3pYPY1.js";
6
6
  //#region src/formats/define-format.d.ts
7
7
  /** Hand-written format: you provide the JSON Schema (SPEC 7.5, first form). */
package/dist/index.js CHANGED
@@ -269,7 +269,8 @@ function createValidation(core) {
269
269
  meta: {
270
270
  ...options.meta,
271
271
  location: options.location
272
- }
272
+ },
273
+ cause: options.cause
273
274
  }, issues);
274
275
  }
275
276
  //#endregion
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["SERVER_ERROR","BanErrorClass","BanErrorClass"],"sources":["../src/core/assert.ts","../src/handler/body-cap.ts","../src/handler/report.ts","../src/handler/request-id.ts","../src/handler/resolve.ts","../src/handler/traceparent.ts","../src/handler/on-error.ts","../src/validation/validation-error.ts","../src/core/factories.ts","../src/core/from.ts","../src/internal/sanitize-meta.ts","../src/core/render.ts","../src/core/create-ban.ts","../src/formats/define-format.ts","../src/headers/bearer-challenge.ts"],"sourcesContent":["import type { BanError } from './ban-error';\n\n/**\n * Throws the produced error when `value` is `null`, `undefined`, or `false`\n * and narrows it otherwise. Other falsy values (`0`, `''`) pass: the guard is\n * about presence, not truthiness.\n *\n * A standalone function rather than `ban.assert()` because TypeScript only\n * applies assertion signatures when every name in the call target has an\n * explicit type annotation (TS2775); `const ban = createBan()` has none.\n */\nexport function assert<T>(\n value: T,\n error: () => BanError,\n): asserts value is Exclude<NonNullable<T>, false> {\n if (value === undefined || value === null || value === false) {\n throw error();\n }\n}\n","import type { Renderer } from '../core/ban-error';\nimport type { RenderOptions } from '../formats/context';\n\nimport { BanError } from '../core/ban-error';\nimport { TRUNCATED_DETAIL_LENGTH } from '../internal/constants';\nimport { safeStringify } from '../internal/json';\n\nconst MAX_UTF8_BYTES_PER_CHAR = 3;\nconst encoder = new TextEncoder();\n\n/**\n * Serializes the body, or re-renders a minimal variant of the same error when\n * the serialized body exceeds `max` bytes (SPEC 6.8). `transform` is the\n * handler's `transform` option bound to the error and context; it runs on the\n * minimal body too, so a redaction applied to the first body is not undone.\n */\nexport function capBody(\n body: unknown,\n error: BanError,\n options: RenderOptions,\n render: Renderer,\n max: number,\n transform: (body: unknown) => unknown,\n): string {\n const text = safeStringify(body);\n // A UTF-16 unit never encodes to more than 3 bytes, so short strings skip encoding.\n if (\n text.length * MAX_UTF8_BYTES_PER_CHAR <= max ||\n encoder.encode(text).byteLength <= max\n ) {\n return text;\n }\n const init = error.toInit();\n const minimal = new BanError({\n ...init,\n meta: {},\n issues: undefined,\n detail: init.detail?.slice(0, TRUNCATED_DETAIL_LENGTH),\n });\n // The stack is the one remaining member a request can inflate (deep\n // recursion), so the minimal render never carries it.\n const rendered = render(minimal, {\n ...options,\n includeStack: false,\n truncated: true,\n });\n return safeStringify(transform(rendered.body));\n}\n","import type { BanError } from '../core/ban-error';\nimport type { ErrorReport } from '../core/types';\n\nexport interface ReportInput {\n readonly error: BanError;\n readonly thrown: unknown;\n readonly handled: boolean;\n readonly handlerFailure: unknown;\n readonly requestId: string | undefined;\n readonly traceId: string | undefined;\n readonly spanId: string | undefined;\n readonly method: string;\n readonly path: string;\n}\n\nexport function buildReport(input: ReportInput): ErrorReport {\n const { error } = input;\n return {\n id: error.id,\n status: error.status,\n code: error.code,\n error,\n cause: input.thrown === error ? error.cause : input.thrown,\n handled: input.handled,\n handlerFailure: input.handlerFailure,\n context: {\n requestId: input.requestId,\n traceId: input.traceId,\n spanId: input.spanId,\n method: input.method,\n path: input.path,\n },\n };\n}\n","import { REQUEST_ID_PATTERN } from '../internal/constants';\n\n/**\n * Echoes a client request id only when it is safe to log and render\n * (SPEC 6.5). Never fabricates one: the error id is the correlation key.\n */\nexport function readRequestId(value: string | undefined): string | undefined {\n return value !== undefined && REQUEST_ID_PATTERN.test(value)\n ? value\n : undefined;\n}\n","import type { BanError } from '../core/ban-error';\nimport type { Ban, Catalog, ErrorMapper } from '../core/types';\n\nimport { HTTPException } from 'hono/http-exception';\n\nimport { isBanError } from '../core/ban-error';\n\nexport interface Resolved {\n readonly error: BanError;\n readonly handled: boolean;\n}\n\n/**\n * Thrown value to `BanError` (SPEC 6.3). A `map` that throws or returns\n * something other than `undefined` or a `BanError` is a handler failure and\n * propagates to the fallback.\n */\nexport function resolveThrown<TErrors extends Catalog>(\n thrown: unknown,\n ban: Ban<TErrors>,\n from: (value: unknown) => BanError,\n map: ErrorMapper<TErrors> | undefined,\n): Resolved {\n if (isBanError(thrown) || thrown instanceof HTTPException) {\n return { error: from(thrown), handled: true };\n }\n if (map !== undefined) {\n const mapped: unknown = map(thrown, ban);\n if (isBanError(mapped)) {\n return { error: mapped, handled: true };\n }\n if (mapped !== undefined) {\n throw new TypeError(\n 'map() must return a BanError or undefined; use ban.custom() for other shapes',\n );\n }\n }\n return { error: from(thrown), handled: false };\n}\n","import { TRACEPARENT_LENGTH, TRACEPARENT_PATTERN } from '../internal/constants';\n\nexport interface TraceContext {\n readonly traceId: string;\n readonly parentId: string;\n readonly sampled: boolean;\n}\n\nconst ALL_ZERO = /^0+$/u;\nconst HEX = 16;\nconst SAMPLED_FLAG = 0x01;\n\n/**\n * W3C Trace Context `traceparent` parser (SPEC 6.6). Invalid headers are\n * ignored rather than rejected, as the specification requires. Versions other\n * than `00` are accepted when the first 55 characters parse and are followed\n * by `-` or nothing (section 3.2.4, forward compatibility).\n * @ref https://www.w3.org/TR/trace-context/#traceparent-header\n * @ref https://www.w3.org/TR/trace-context/#versioning-of-traceparent\n */\nexport function parseTraceparent(\n value: string | undefined,\n): TraceContext | undefined {\n if (value === undefined) {\n return undefined;\n }\n const match = TRACEPARENT_PATTERN.exec(value);\n const version = match?.[1];\n const traceId = match?.[2];\n const parentId = match?.[3];\n const flags = match?.[4];\n if (\n version === undefined ||\n traceId === undefined ||\n parentId === undefined ||\n flags === undefined\n ) {\n return undefined;\n }\n if (version === 'ff') {\n return undefined;\n }\n if (version === '00') {\n if (value.length !== TRACEPARENT_LENGTH) {\n return undefined;\n }\n } else if (\n value.length > TRACEPARENT_LENGTH &&\n value[TRACEPARENT_LENGTH] !== '-'\n ) {\n return undefined;\n }\n if (ALL_ZERO.test(traceId) || ALL_ZERO.test(parentId)) {\n return undefined;\n }\n return {\n traceId,\n parentId,\n sampled: (Number.parseInt(flags, HEX) & SAMPLED_FLAG) === SAMPLED_FLAG,\n };\n}\n","import type { Context, Env, ErrorHandler } from 'hono';\n\nimport type { BanError } from '../core/ban-error';\nimport type { BanCore } from '../core/internal';\nimport type {\n Ban,\n Catalog,\n ErrorMapper,\n ErrorReport,\n HandlerOptions,\n} from '../core/types';\nimport type { RenderOptions } from '../formats/context';\n\nimport { BanError as BanErrorClass } from '../core/ban-error';\nimport {\n DEFAULT_ERROR_ID_HEADER,\n DEFAULT_MAX_BODY_BYTES,\n DEFAULT_REQUEST_ID_HEADER,\n UNEXPECTED_DETAIL,\n} from '../internal/constants';\nimport { safeStringify } from '../internal/json';\nimport { capBody } from './body-cap';\nimport {\n assertHeaderName,\n assertHeadersInit,\n lastResortHeaders,\n mergeHeaders,\n} from './headers';\nimport { buildReport } from './report';\nimport { readRequestId } from './request-id';\nimport { resolveThrown } from './resolve';\nimport { parseTraceparent } from './traceparent';\n\nconst SERVER_ERROR = 500;\n\ninterface Trace {\n readonly traceId: string | undefined;\n readonly spanId: string | undefined;\n}\n\nfunction readTrace<E extends Env>(\n c: Context<E>,\n override: ((c: Context<E>) => string | undefined) | undefined,\n): Trace {\n if (override !== undefined) {\n return { traceId: override(c), spanId: undefined };\n }\n const parsed = parseTraceparent(c.req.header('traceparent'));\n return { traceId: parsed?.traceId, spanId: parsed?.parentId };\n}\n\n/**\n * Calls `onReport` once and swallows anything it throws: a broken sink must\n * not turn a resolved 404 into a 500, and there is nothing left to report to.\n */\nasync function report<E extends Env>(\n onReport: HandlerOptions<E>['onReport'],\n input: ErrorReport,\n c: Context<E>,\n): Promise<void> {\n if (onReport === undefined) {\n return;\n }\n try {\n await onReport(input, c);\n } catch {\n // Intentionally ignored; see the doc comment.\n }\n}\n\n/**\n * Last resort when rendering, mapping, or the format itself failed\n * (SPEC 6.4). Tries the format once more with a bare 500, then a hand-built\n * JSON body.\n */\nasync function fallback<E extends Env, TErrors extends Catalog>(\n failure: unknown,\n thrown: unknown,\n c: Context<E>,\n core: BanCore,\n options: HandlerOptions<E, TErrors>,\n): Promise<Response> {\n let error: BanError;\n let response: Response;\n try {\n error = core.build(core.definition('INTERNAL_SERVER_ERROR'), {\n detail: UNEXPECTED_DETAIL,\n cause: thrown,\n });\n const rendered = core.render(error);\n response = new Response(safeStringify(rendered.body), {\n status: SERVER_ERROR,\n headers: mergeHeaders(\n options.headers,\n rendered.headers,\n options.errorIdHeader ?? DEFAULT_ERROR_ID_HEADER,\n error.id,\n ),\n });\n } catch {\n error = new BanErrorClass({\n status: SERVER_ERROR,\n code: 'INTERNAL_SERVER_ERROR',\n title: 'Internal Server Error',\n detail: UNEXPECTED_DETAIL,\n cause: thrown,\n });\n // The header merge still applies so the id reaches the header (SPEC 10.2).\n response = new Response(\n safeStringify({\n status: SERVER_ERROR,\n title: error.title,\n detail: error.detail,\n id: error.id,\n }),\n {\n status: SERVER_ERROR,\n headers: lastResortHeaders(\n options.headers,\n options.errorIdHeader ?? DEFAULT_ERROR_ID_HEADER,\n error.id,\n ),\n },\n );\n }\n await report(\n options.onReport,\n buildReport({\n error,\n thrown,\n handled: false,\n handlerFailure: failure,\n requestId: undefined,\n traceId: undefined,\n spanId: undefined,\n method: c.req.method,\n path: c.req.path,\n }),\n c,\n );\n return response;\n}\n\n/**\n * Builds `ban.onError()` (SPEC 6). Throws `TypeError` for a header option\n * `Headers` would reject; the returned handler resolves to a `Response` for\n * every input and never rejects (ADR 0002).\n */\nexport function createOnError<TErrors extends Catalog>(\n core: BanCore,\n ban: Ban<TErrors>,\n from: (value: unknown) => BanError,\n instanceMap: ErrorMapper<TErrors> | undefined,\n): <E extends Env = Env>(\n options?: HandlerOptions<E, TErrors>,\n) => ErrorHandler<E> {\n return <E extends Env = Env>(\n options: HandlerOptions<E, TErrors> = {},\n ): ErrorHandler<E> => {\n const map = options.map ?? instanceMap;\n const includeStack = options.includeStack ?? false;\n const requestIdHeader =\n options.requestIdHeader ?? DEFAULT_REQUEST_ID_HEADER;\n const errorIdHeader = options.errorIdHeader ?? DEFAULT_ERROR_ID_HEADER;\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n assertHeaderName('requestIdHeader', requestIdHeader);\n assertHeaderName('errorIdHeader', errorIdHeader);\n assertHeadersInit(options.headers);\n\n return async (thrown: unknown, c: Context<E>): Promise<Response> => {\n try {\n const requestId =\n options.requestId === undefined\n ? readRequestId(\n requestIdHeader === false\n ? undefined\n : c.req.header(requestIdHeader),\n )\n : options.requestId(c);\n const trace = readTrace(c, options.traceId);\n const { error, handled } = resolveThrown(thrown, ban, from, map);\n const renderOptions: RenderOptions = {\n requestId,\n traceId: trace.traceId,\n instance: c.req.path,\n method: c.req.method,\n includeStack,\n };\n const rendered = core.render(error, renderOptions);\n const transform = (body: unknown): unknown =>\n options.transform === undefined\n ? body\n : options.transform(body, error, c);\n const text = capBody(\n transform(rendered.body),\n error,\n renderOptions,\n core.render,\n maxBodyBytes,\n transform,\n );\n const headers = mergeHeaders(\n options.headers,\n rendered.headers,\n errorIdHeader,\n error.id,\n );\n await report(\n options.onReport,\n buildReport({\n error,\n thrown,\n handled,\n handlerFailure: undefined,\n requestId,\n traceId: trace.traceId,\n spanId: trace.spanId,\n method: c.req.method,\n path: c.req.path,\n }),\n c,\n );\n return new Response(text, { status: error.status, headers });\n } catch (failure) {\n return fallback(failure, thrown, c, core, options);\n }\n };\n };\n}\n","import type { BanError } from '../core/ban-error';\nimport type { BanCore } from '../core/internal';\nimport type { ValidationOptions } from '../core/types';\nimport type { ValidationIssue } from './issue';\n\n/**\n * `ban.validation()` (SPEC 8.2). The location travels in `meta.location` so\n * every format can label issues without a dedicated field on `BanError`.\n */\nexport function createValidation(\n core: BanCore,\n): (\n issues: ReadonlyArray<ValidationIssue>,\n options: ValidationOptions,\n) => BanError {\n return (issues, options) =>\n core.build(\n core.definition(core.validationKey),\n {\n detail: options.detail,\n meta: { ...options.meta, location: options.location },\n },\n issues,\n );\n}\n","import type { BanError, BanErrorOptions } from './ban-error';\nimport type { BanCore } from './internal';\nimport type { Factory } from './types';\n\nfunction isOptions(value: unknown): value is BanErrorOptions {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Resolves the two call forms (SPEC 5.1). The first argument is `unknown` on\n * purpose: the runtime check protects JavaScript callers.\n */\nexport function parseFactoryArgs(\n first: unknown,\n second: BanErrorOptions | undefined,\n): BanErrorOptions {\n if (first === undefined) {\n return second ?? {};\n }\n if (typeof first === 'string') {\n return { ...second, detail: first };\n }\n if (isOptions(first)) {\n return first;\n }\n throw new TypeError(\n `Expected a detail string or an options object, received ${typeof first}`,\n );\n}\n\nexport function createFactory(core: BanCore, key: string): Factory {\n const definition = core.definition(key);\n return (\n first?: string | BanErrorOptions,\n second?: BanErrorOptions,\n ): BanError => core.build(definition, parseFactoryArgs(first, second));\n}\n","import type { BanError } from './ban-error';\nimport type { BanCore } from './internal';\n\nimport { HTTPException } from 'hono/http-exception';\n\nimport {\n BODY_HEADERS,\n MALFORMED_JSON_MESSAGE,\n UNEXPECTED_DETAIL,\n} from '../internal/constants';\nimport { isBanError } from './ban-error';\n\nconst BAD_REQUEST = 400;\nconst RANGE_NOT_SATISFIABLE = 416;\nconst CONTENT_RANGE = 'content-range';\n\n/**\n * The exception's body is discarded, so the headers describing it go with it\n * (SPEC 5.5): kept, a `Content-Length` sized for the old body truncates the\n * rendered JSON and a `Content-Encoding` makes clients decompress plain text.\n * `Content-Range` is the one that changes meaning with the status: on a 416\n * it carries the selected representation's complete length (an asterisk, a\n * slash, and the length), not a description of the body, so it stays.\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-14.4\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.5.17\n */\nfunction withoutBodyHeaders(\n source: Headers | undefined,\n status: number,\n): Headers | undefined {\n if (source === undefined) {\n return undefined;\n }\n const headers = new Headers(source);\n const keepContentRange = status === RANGE_NOT_SATISFIABLE;\n for (const name of BODY_HEADERS) {\n if (!(keepContentRange && name === CONTENT_RANGE)) {\n headers.delete(name);\n }\n }\n return headers;\n}\n\n/**\n * Hono middleware writes `HTTPException.message` for clients, so it is kept as\n * `detail`; `res` headers (for example `WWW-Authenticate` from\n * `hono/bearer-auth`) are preserved, except those describing the discarded\n * body (SPEC 5.5).\n * @ref https://hono.dev/docs/api/exception\n */\nfunction fromHttpException(core: BanCore, error: HTTPException): BanError {\n const status = error.status;\n const definition =\n status === BAD_REQUEST && error.message === MALFORMED_JSON_MESSAGE\n ? core.definition('MALFORMED_JSON')\n : core.byStatus.get(status);\n const detail = error.message === '' ? undefined : error.message;\n const headers = withoutBodyHeaders(error.res?.headers, status);\n if (definition === undefined) {\n return core.custom({\n status,\n title: 'Error',\n detail,\n headers,\n cause: error,\n });\n }\n return core.build(definition, { detail, headers, cause: error });\n}\n\nexport function createFrom(core: BanCore): (value: unknown) => BanError {\n return (value: unknown): BanError => {\n if (isBanError(value)) {\n return value;\n }\n if (value instanceof HTTPException) {\n return fromHttpException(core, value);\n }\n return core.build(core.definition('INTERNAL_SERVER_ERROR'), {\n detail: UNEXPECTED_DETAIL,\n cause: value,\n });\n };\n}\n","import { PROTO_KEYS } from './constants';\n\n/**\n * Shallow copy of own enumerable string keys with prototype-pollution keys\n * removed (ADR 0007). Runs once per render, before any format sees `meta`.\n */\nexport function sanitizeMeta(\n meta: Readonly<Record<string, unknown>>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(meta)) {\n if (!PROTO_KEYS.has(key)) {\n out[key] = meta[key];\n }\n }\n return out;\n}\n","import type { RenderContext, RenderOptions } from '../formats/context';\nimport type { ErrorFormat } from '../formats/types';\nimport type { BanError, RenderedError, Renderer } from './ban-error';\n\nimport { sanitizeMeta } from '../internal/sanitize-meta';\n\nconst SERVER_ERROR = 500;\n\n/**\n * The one place a `BanError` becomes a body (SPEC 6.7). Shared by the handler\n * and `BanError.getResponse()` so both apply the same sanitization.\n */\nexport function createRenderer(\n format: ErrorFormat,\n docsBaseUrl: string | undefined,\n): Renderer {\n return (error: BanError, options: RenderOptions = {}): RenderedError => {\n const includeStack = options.includeStack ?? false;\n const source = error.cause instanceof Error ? error.cause : error;\n const ctx: RenderContext = {\n requestId: options.requestId,\n traceId: options.traceId,\n instance: error.instance ?? options.instance,\n method: options.method,\n includeStack,\n docsBaseUrl: options.docsBaseUrl ?? docsBaseUrl,\n meta: sanitizeMeta(error.meta),\n stack:\n includeStack && error.status >= SERVER_ERROR ? source.stack : undefined,\n truncated: options.truncated ?? false,\n };\n const body =\n error.issues === undefined\n ? format.render(error, ctx)\n : format.renderValidation(error, error.issues, ctx);\n const headers = new Headers(error.headers);\n headers.set('Content-Type', format.contentType);\n return { status: error.status, headers, body };\n };\n}\n","import type { ErrorFormat } from '../formats/types';\nimport type { BanError, BanErrorOptions } from './ban-error';\nimport type { ResolvedDefinition } from './definition';\nimport type { BanCore } from './internal';\nimport type {\n Ban,\n BanInstance,\n BanOptions,\n Catalog,\n CustomErrorInit,\n EmptyCatalog,\n NoReserved,\n} from './types';\n\nimport { problemDetails } from '../formats/problem-details';\nimport { createOnError } from '../handler/on-error';\nimport { generateErrorId } from '../internal/id';\nimport { createValidation } from '../validation/validation-error';\nimport { BanError as BanErrorClass } from './ban-error';\nimport { BUILTIN_CATALOG, FACTORY_NAMES } from './catalog';\nimport { indexByStatus, reasonPhrase, resolveDefinition } from './definition';\nimport { createFactory } from './factories';\nimport { createFrom } from './from';\nimport { createRenderer } from './render';\n\nconst INSTANCE_KEYS = [\n 'format',\n 'catalog',\n 'docsBaseUrl',\n 'validationKey',\n 'error',\n 'custom',\n 'validation',\n 'from',\n 'onError',\n 'render',\n] as const satisfies ReadonlyArray<keyof BanInstance<Catalog>>;\n\nconst RESERVED_KEYS: ReadonlySet<string> = new Set<string>([\n ...INSTANCE_KEYS,\n ...Object.keys(FACTORY_NAMES),\n]);\n\nfunction buildCatalog(custom: Catalog): {\n readonly catalog: Record<string, ResolvedDefinition>;\n readonly byStatus: Map<number, ResolvedDefinition>;\n} {\n const catalog: Record<string, ResolvedDefinition> = {};\n for (const [key, definition] of Object.entries(BUILTIN_CATALOG)) {\n catalog[key] = resolveDefinition(key, definition);\n }\n for (const [key, definition] of Object.entries(custom)) {\n catalog[key] = resolveDefinition(key, definition);\n }\n return { catalog, byStatus: indexByStatus(catalog) };\n}\n\n/**\n * Creates an instance: built-in and custom factories, the handler, and the\n * helpers, all sharing one catalog and one format (SPEC 2, 5).\n */\nexport function createBan<const TErrors extends Catalog = EmptyCatalog>(\n options: BanOptions<TErrors> & {\n readonly errors?: NoReserved<TErrors> | undefined;\n } = {},\n): Ban<TErrors> {\n const custom = (options.errors ?? {}) as Catalog;\n for (const key of Object.keys(custom)) {\n if (RESERVED_KEYS.has(key)) {\n throw new TypeError(\n `Catalog key \"${key}\" collides with a hono-ban instance member`,\n );\n }\n }\n const format: ErrorFormat = options.format ?? problemDetails();\n const docsBaseUrl = options.docsBaseUrl;\n const generateId = options.id ?? generateErrorId;\n const { catalog, byStatus } = buildCatalog(custom);\n const render = createRenderer(format, docsBaseUrl);\n\n const core: BanCore = {\n catalog,\n byStatus,\n format,\n docsBaseUrl,\n validationKey: options.validationKey ?? 'VALIDATION_FAILED',\n generateId,\n render,\n definition(key: string): ResolvedDefinition {\n const definition = catalog[key];\n if (definition === undefined) {\n throw new RangeError(`Unknown error key \"${key}\"`);\n }\n return definition;\n },\n build(definition, errorOptions: BanErrorOptions, issues): BanError {\n return new BanErrorClass({\n ...errorOptions,\n status: definition.status,\n code: definition.code,\n title: definition.title,\n type: errorOptions.type ?? definition.type,\n id: errorOptions.id ?? generateId(),\n definition,\n issues,\n render,\n });\n },\n custom(init: CustomErrorInit): BanError {\n return new BanErrorClass({\n ...init,\n code: init.code ?? 'CUSTOM',\n title: init.title ?? reasonPhrase(init.status) ?? 'Error',\n id: init.id ?? generateId(),\n render,\n });\n },\n };\n if (!Object.hasOwn(catalog, core.validationKey)) {\n throw new RangeError(\n `validationKey \"${core.validationKey}\" is not in the catalog`,\n );\n }\n\n const from = createFrom(core);\n const instance: Record<string, unknown> = {\n format,\n catalog,\n docsBaseUrl,\n validationKey: core.validationKey,\n error: (key: string, errorOptions: BanErrorOptions = {}): BanError =>\n core.build(core.definition(key), errorOptions),\n custom: (init: CustomErrorInit): BanError => core.custom(init),\n validation: createValidation(core),\n from,\n render,\n };\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- reason: the object is assembled from the catalog at runtime; create-ban.test-d.ts proves the shape\n const ban = instance as unknown as Ban<TErrors>;\n instance['onError'] = createOnError(core, ban, from, options.map);\n for (const [name, key] of Object.entries(FACTORY_NAMES)) {\n instance[name] = createFactory(core, key);\n }\n for (const key of Object.keys(custom)) {\n instance[key] = createFactory(core, key);\n }\n return ban;\n}\n","import type { BanError } from '../core/ban-error';\nimport type { ResolvedDefinition } from '../core/definition';\nimport type {\n InferStandardOutput,\n StandardJsonSchema,\n} from '../internal/standard-schema-types';\nimport type { ValidationIssue } from '../validation/issue';\nimport type { JsonSchema, RenderContext, SchemaContext } from './context';\nimport type { ErrorFormat } from './types';\n\n/** Hand-written format: you provide the JSON Schema (SPEC 7.5, first form). */\nexport interface FormatSpec<TBody> {\n readonly name: string;\n readonly contentType: string;\n render(error: BanError, ctx: RenderContext): TBody;\n /** Defaults to `render`. */\n renderValidation?(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): TBody;\n schema(definition: ResolvedDefinition, ctx: SchemaContext): JsonSchema;\n /** Defaults to `schema`. */\n validationSchema?(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema;\n}\n\n/**\n * Schema-first format: a Standard JSON Schema (Zod 4, Valibot, ArkType, and\n * others) types the body and produces the JSON Schema (SPEC 7.5, second form).\n * @ref https://standardschema.dev/json-schema\n */\nexport interface StandardFormatSpec<TSchema extends StandardJsonSchema> {\n readonly name: string;\n readonly contentType: string;\n readonly schema: TSchema;\n render(error: BanError, ctx: RenderContext): InferStandardOutput<TSchema>;\n renderValidation?(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): InferStandardOutput<TSchema>;\n}\n\nfunction isStandardSpec(\n spec: FormatSpec<unknown> | StandardFormatSpec<StandardJsonSchema>,\n): spec is StandardFormatSpec<StandardJsonSchema> {\n return typeof spec.schema !== 'function';\n}\n\nexport function defineFormat<TBody>(\n spec: FormatSpec<TBody>,\n): ErrorFormat<TBody>;\nexport function defineFormat<TSchema extends StandardJsonSchema>(\n spec: StandardFormatSpec<TSchema>,\n): ErrorFormat<InferStandardOutput<TSchema>>;\nexport function defineFormat(\n spec: FormatSpec<unknown> | StandardFormatSpec<StandardJsonSchema>,\n): ErrorFormat {\n if (isStandardSpec(spec)) {\n const jsonSchema = (\n _definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema =>\n spec.schema['~standard'].jsonSchema.output({ target: ctx.dialect });\n const renderValidation = spec.renderValidation?.bind(spec);\n return {\n name: spec.name,\n contentType: spec.contentType,\n render: (error, ctx) => spec.render(error, ctx),\n renderValidation:\n renderValidation ?? ((error, _issues, ctx) => spec.render(error, ctx)),\n schema: jsonSchema,\n validationSchema: jsonSchema,\n };\n }\n const renderValidation = spec.renderValidation?.bind(spec);\n const validationSchema = spec.validationSchema?.bind(spec);\n return {\n name: spec.name,\n contentType: spec.contentType,\n render: (error, ctx) => spec.render(error, ctx),\n renderValidation:\n renderValidation ?? ((error, _issues, ctx) => spec.render(error, ctx)),\n schema: (definition, ctx) => spec.schema(definition, ctx),\n validationSchema:\n validationSchema ?? ((definition, ctx) => spec.schema(definition, ctx)),\n };\n}\n","/**\n * `WWW-Authenticate: Bearer ...` challenge builder (SPEC 3.2). A 401 MUST\n * carry a challenge (RFC 9110 15.5.2) and RFC 6750 fixes the grammar of every\n * Bearer attribute, so each value is checked against its character class and\n * a value taken from a request cannot break the header or smuggle a second\n * attribute. `hono/bearer-auth` emits the same shape.\n * @ref https://www.rfc-editor.org/rfc/rfc6750#section-3\n * @ref https://www.rfc-editor.org/rfc/rfc6749#appendix-A\n * @ref https://www.rfc-editor.org/rfc/rfc9728#section-5.1\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-11.2\n * @packageDocumentation\n */\n\nexport interface BearerChallengeOptions {\n /**\n * Protection space (RFC 9110 11.5). Printable ASCII; double quotes and\n * backslashes are escaped as a quoted-pair. When no attribute at all is\n * given the challenge is `Bearer realm=\"\"`, as `hono/bearer-auth` sends,\n * because RFC 6750 requires at least one attribute.\n */\n readonly realm?: string | undefined;\n /**\n * Scopes the resource requires, joined by single spaces on the wire. Each\n * token is RFC 6749 `scope-token`; an empty list omits the attribute.\n */\n readonly scope?: string | ReadonlyArray<string> | undefined;\n /**\n * `invalid_request` (400), `invalid_token` (401), `insufficient_scope`\n * (403), or an extension code such as RFC 9470 `insufficient_user_authentication`.\n * RFC 6750 3.1 says to omit it when the request carried no token at all.\n */\n readonly error?: string | undefined;\n /** Developer-facing explanation, not meant for end users. */\n readonly errorDescription?: string | undefined;\n /** Absolute URI of a human-readable page explaining the error. */\n readonly errorUri?: string | undefined;\n /** URL of the protected resource metadata document (RFC 9728 5.1). */\n readonly resourceMetadata?: string | undefined;\n}\n\n// RFC 6749 appendix A: NQCHAR = %x21 / %x23-5B / %x5D-7E (no space, quote,\n// or backslash); NQSCHAR adds the space. RFC 6750 assigns NQSCHAR to `error`\n// and `error_description` and NQCHAR to `scope-token` and `error_uri`.\nconst NQCHAR_PATTERN = /^[\\u0021\\u0023-\\u005B\\u005D-\\u007E]+$/u;\nconst NQSCHAR_PATTERN = /^[\\u0020\\u0021\\u0023-\\u005B\\u005D-\\u007E]+$/u;\n// RFC 9110 5.6.4 quoted-string, restricted to printable ASCII; `\"` and `\\`\n// become quoted-pairs.\nconst PRINTABLE_ASCII_PATTERN = /^[\\u0020-\\u007E]*$/u;\nconst QUOTED_PAIR_PATTERN = /[\"\\\\]/gu;\n\nfunction reject(name: string, value: string, grammar: string): never {\n throw new TypeError(\n `bearerChallenge: ${name} must be ${grammar}; received ${JSON.stringify(value)}`,\n );\n}\n\nfunction realmParam(realm: string): string {\n if (!PRINTABLE_ASCII_PATTERN.test(realm)) {\n reject('realm', realm, 'printable ASCII (RFC 9110 quoted-string)');\n }\n return `realm=\"${realm.replaceAll(QUOTED_PAIR_PATTERN, '\\\\$&')}\"`;\n}\n\nfunction scopeParam(\n scope: string | ReadonlyArray<string> | undefined,\n): string | undefined {\n if (scope === undefined) {\n return undefined;\n }\n const tokens = typeof scope === 'string' ? scope.split(' ') : scope;\n if (tokens.length === 0) {\n return undefined;\n }\n for (const token of tokens) {\n if (!NQCHAR_PATTERN.test(token)) {\n reject(\n 'scope',\n token,\n 'a list of RFC 6749 scope-tokens (printable ASCII without space, quote, or backslash)',\n );\n }\n }\n return `scope=\"${tokens.join(' ')}\"`;\n}\n\nfunction param(name: string, value: string, pattern: RegExp): string {\n if (!pattern.test(value)) {\n reject(\n name,\n value,\n pattern === NQCHAR_PATTERN\n ? 'printable ASCII without space, double quote, or backslash (RFC 6749 NQCHAR)'\n : 'printable ASCII without double quote or backslash (RFC 6749 NQSCHAR)',\n );\n }\n return `${name}=\"${value}\"`;\n}\n\n/**\n * Builds the value of a `WWW-Authenticate` header for the Bearer scheme.\n * Attributes appear in RFC 6750 order: `realm`, `scope`, `error`,\n * `error_description`, `error_uri`, then RFC 9728 `resource_metadata`. Throws\n * a `TypeError` when a value falls outside the grammar its RFC assigns to it.\n *\n * @example\n * throw ban.unauthorized({\n * headers: {\n * 'WWW-Authenticate': bearerChallenge({\n * realm: 'api',\n * error: 'invalid_token',\n * errorDescription: 'The access token expired',\n * }),\n * },\n * });\n */\nexport function bearerChallenge(options: BearerChallengeOptions = {}): string {\n const params: Array<string> = [];\n if (options.realm !== undefined) {\n params.push(realmParam(options.realm));\n }\n const scope = scopeParam(options.scope);\n if (scope !== undefined) {\n params.push(scope);\n }\n const attributes: ReadonlyArray<\n readonly [string, string | undefined, RegExp]\n > = [\n ['error', options.error, NQSCHAR_PATTERN],\n ['error_description', options.errorDescription, NQSCHAR_PATTERN],\n ['error_uri', options.errorUri, NQCHAR_PATTERN],\n ['resource_metadata', options.resourceMetadata, NQCHAR_PATTERN],\n ];\n for (const [name, value, pattern] of attributes) {\n if (value !== undefined) {\n params.push(param(name, value, pattern));\n }\n }\n return `Bearer ${params.length === 0 ? 'realm=\"\"' : params.join(', ')}`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["SERVER_ERROR","BanErrorClass","BanErrorClass"],"sources":["../src/core/assert.ts","../src/handler/body-cap.ts","../src/handler/report.ts","../src/handler/request-id.ts","../src/handler/resolve.ts","../src/handler/traceparent.ts","../src/handler/on-error.ts","../src/validation/validation-error.ts","../src/core/factories.ts","../src/core/from.ts","../src/internal/sanitize-meta.ts","../src/core/render.ts","../src/core/create-ban.ts","../src/formats/define-format.ts","../src/headers/bearer-challenge.ts"],"sourcesContent":["import type { BanError } from './ban-error';\n\n/**\n * Throws the produced error when `value` is `null`, `undefined`, or `false`\n * and narrows it otherwise. Other falsy values (`0`, `''`) pass: the guard is\n * about presence, not truthiness.\n *\n * A standalone function rather than `ban.assert()` because TypeScript only\n * applies assertion signatures when every name in the call target has an\n * explicit type annotation (TS2775); `const ban = createBan()` has none.\n */\nexport function assert<T>(\n value: T,\n error: () => BanError,\n): asserts value is Exclude<NonNullable<T>, false> {\n if (value === undefined || value === null || value === false) {\n throw error();\n }\n}\n","import type { Renderer } from '../core/ban-error';\nimport type { RenderOptions } from '../formats/context';\n\nimport { BanError } from '../core/ban-error';\nimport { TRUNCATED_DETAIL_LENGTH } from '../internal/constants';\nimport { safeStringify } from '../internal/json';\n\nconst MAX_UTF8_BYTES_PER_CHAR = 3;\nconst encoder = new TextEncoder();\n\n/**\n * Serializes the body, or re-renders a minimal variant of the same error when\n * the serialized body exceeds `max` bytes (SPEC 6.8). `transform` is the\n * handler's `transform` option bound to the error and context; it runs on the\n * minimal body too, so a redaction applied to the first body is not undone.\n */\nexport function capBody(\n body: unknown,\n error: BanError,\n options: RenderOptions,\n render: Renderer,\n max: number,\n transform: (body: unknown) => unknown,\n): string {\n const text = safeStringify(body);\n // A UTF-16 unit never encodes to more than 3 bytes, so short strings skip encoding.\n if (\n text.length * MAX_UTF8_BYTES_PER_CHAR <= max ||\n encoder.encode(text).byteLength <= max\n ) {\n return text;\n }\n const init = error.toInit();\n const minimal = new BanError({\n ...init,\n meta: {},\n issues: undefined,\n detail: init.detail?.slice(0, TRUNCATED_DETAIL_LENGTH),\n });\n // The stack is the one remaining member a request can inflate (deep\n // recursion), so the minimal render never carries it.\n const rendered = render(minimal, {\n ...options,\n includeStack: false,\n truncated: true,\n });\n return safeStringify(transform(rendered.body));\n}\n","import type { BanError } from '../core/ban-error';\nimport type { ErrorReport } from '../core/types';\n\nexport interface ReportInput {\n readonly error: BanError;\n readonly thrown: unknown;\n readonly handled: boolean;\n readonly handlerFailure: unknown;\n readonly requestId: string | undefined;\n readonly traceId: string | undefined;\n readonly spanId: string | undefined;\n readonly method: string;\n readonly path: string;\n}\n\nexport function buildReport(input: ReportInput): ErrorReport {\n const { error } = input;\n return {\n id: error.id,\n status: error.status,\n code: error.code,\n error,\n cause: input.thrown === error ? error.cause : input.thrown,\n handled: input.handled,\n handlerFailure: input.handlerFailure,\n context: {\n requestId: input.requestId,\n traceId: input.traceId,\n spanId: input.spanId,\n method: input.method,\n path: input.path,\n },\n };\n}\n","import { REQUEST_ID_PATTERN } from '../internal/constants';\n\n/**\n * Echoes a client request id only when it is safe to log and render\n * (SPEC 6.5). Never fabricates one: the error id is the correlation key.\n */\nexport function readRequestId(value: string | undefined): string | undefined {\n return value !== undefined && REQUEST_ID_PATTERN.test(value)\n ? value\n : undefined;\n}\n","import type { BanError } from '../core/ban-error';\nimport type { Ban, Catalog, ErrorMapper } from '../core/types';\n\nimport { HTTPException } from 'hono/http-exception';\n\nimport { isBanError } from '../core/ban-error';\n\nexport interface Resolved {\n readonly error: BanError;\n readonly handled: boolean;\n}\n\n/**\n * Thrown value to `BanError` (SPEC 6.3). A `map` that throws or returns\n * something other than `undefined` or a `BanError` is a handler failure and\n * propagates to the fallback.\n */\nexport function resolveThrown<TErrors extends Catalog>(\n thrown: unknown,\n ban: Ban<TErrors>,\n from: (value: unknown) => BanError,\n map: ErrorMapper<TErrors> | undefined,\n): Resolved {\n if (isBanError(thrown) || thrown instanceof HTTPException) {\n return { error: from(thrown), handled: true };\n }\n if (map !== undefined) {\n const mapped: unknown = map(thrown, ban);\n if (isBanError(mapped)) {\n return { error: mapped, handled: true };\n }\n if (mapped !== undefined) {\n throw new TypeError(\n 'map() must return a BanError or undefined; use ban.custom() for other shapes',\n );\n }\n }\n return { error: from(thrown), handled: false };\n}\n","import { TRACEPARENT_LENGTH, TRACEPARENT_PATTERN } from '../internal/constants';\n\nexport interface TraceContext {\n readonly traceId: string;\n readonly parentId: string;\n readonly sampled: boolean;\n}\n\nconst ALL_ZERO = /^0+$/u;\nconst HEX = 16;\nconst SAMPLED_FLAG = 0x01;\n\n/**\n * W3C Trace Context `traceparent` parser (SPEC 6.6). Invalid headers are\n * ignored rather than rejected, as the specification requires. Versions other\n * than `00` are accepted when the first 55 characters parse and are followed\n * by `-` or nothing (section 3.2.4, forward compatibility).\n * @ref https://www.w3.org/TR/trace-context/#traceparent-header\n * @ref https://www.w3.org/TR/trace-context/#versioning-of-traceparent\n */\nexport function parseTraceparent(\n value: string | undefined,\n): TraceContext | undefined {\n if (value === undefined) {\n return undefined;\n }\n const match = TRACEPARENT_PATTERN.exec(value);\n const version = match?.[1];\n const traceId = match?.[2];\n const parentId = match?.[3];\n const flags = match?.[4];\n if (\n version === undefined ||\n traceId === undefined ||\n parentId === undefined ||\n flags === undefined\n ) {\n return undefined;\n }\n if (version === 'ff') {\n return undefined;\n }\n if (version === '00') {\n if (value.length !== TRACEPARENT_LENGTH) {\n return undefined;\n }\n } else if (\n value.length > TRACEPARENT_LENGTH &&\n value[TRACEPARENT_LENGTH] !== '-'\n ) {\n return undefined;\n }\n if (ALL_ZERO.test(traceId) || ALL_ZERO.test(parentId)) {\n return undefined;\n }\n return {\n traceId,\n parentId,\n sampled: (Number.parseInt(flags, HEX) & SAMPLED_FLAG) === SAMPLED_FLAG,\n };\n}\n","import type { Context, Env, ErrorHandler } from 'hono';\n\nimport type { BanError } from '../core/ban-error';\nimport type { BanCore } from '../core/internal';\nimport type {\n Ban,\n Catalog,\n ErrorMapper,\n ErrorReport,\n HandlerOptions,\n} from '../core/types';\nimport type { RenderOptions } from '../formats/context';\n\nimport { BanError as BanErrorClass } from '../core/ban-error';\nimport {\n DEFAULT_ERROR_ID_HEADER,\n DEFAULT_MAX_BODY_BYTES,\n DEFAULT_REQUEST_ID_HEADER,\n UNEXPECTED_DETAIL,\n} from '../internal/constants';\nimport { safeStringify } from '../internal/json';\nimport { capBody } from './body-cap';\nimport {\n assertHeaderName,\n assertHeadersInit,\n lastResortHeaders,\n mergeHeaders,\n} from './headers';\nimport { buildReport } from './report';\nimport { readRequestId } from './request-id';\nimport { resolveThrown } from './resolve';\nimport { parseTraceparent } from './traceparent';\n\nconst SERVER_ERROR = 500;\n\ninterface Trace {\n readonly traceId: string | undefined;\n readonly spanId: string | undefined;\n}\n\nfunction readTrace<E extends Env>(\n c: Context<E>,\n override: ((c: Context<E>) => string | undefined) | undefined,\n): Trace {\n if (override !== undefined) {\n return { traceId: override(c), spanId: undefined };\n }\n const parsed = parseTraceparent(c.req.header('traceparent'));\n return { traceId: parsed?.traceId, spanId: parsed?.parentId };\n}\n\n/**\n * Calls `onReport` once and swallows anything it throws: a broken sink must\n * not turn a resolved 404 into a 500, and there is nothing left to report to.\n */\nasync function report<E extends Env>(\n onReport: HandlerOptions<E>['onReport'],\n input: ErrorReport,\n c: Context<E>,\n): Promise<void> {\n if (onReport === undefined) {\n return;\n }\n try {\n await onReport(input, c);\n } catch {\n // Intentionally ignored; see the doc comment.\n }\n}\n\n/**\n * Last resort when rendering, mapping, or the format itself failed\n * (SPEC 6.4). Tries the format once more with a bare 500, then a hand-built\n * JSON body.\n */\nasync function fallback<E extends Env, TErrors extends Catalog>(\n failure: unknown,\n thrown: unknown,\n c: Context<E>,\n core: BanCore,\n options: HandlerOptions<E, TErrors>,\n): Promise<Response> {\n let error: BanError;\n let response: Response;\n try {\n error = core.build(core.definition('INTERNAL_SERVER_ERROR'), {\n detail: UNEXPECTED_DETAIL,\n cause: thrown,\n });\n const rendered = core.render(error);\n response = new Response(safeStringify(rendered.body), {\n status: SERVER_ERROR,\n headers: mergeHeaders(\n options.headers,\n rendered.headers,\n options.errorIdHeader ?? DEFAULT_ERROR_ID_HEADER,\n error.id,\n ),\n });\n } catch {\n error = new BanErrorClass({\n status: SERVER_ERROR,\n code: 'INTERNAL_SERVER_ERROR',\n title: 'Internal Server Error',\n detail: UNEXPECTED_DETAIL,\n cause: thrown,\n });\n // The header merge still applies so the id reaches the header (SPEC 10.2).\n response = new Response(\n safeStringify({\n status: SERVER_ERROR,\n title: error.title,\n detail: error.detail,\n id: error.id,\n }),\n {\n status: SERVER_ERROR,\n headers: lastResortHeaders(\n options.headers,\n options.errorIdHeader ?? DEFAULT_ERROR_ID_HEADER,\n error.id,\n ),\n },\n );\n }\n await report(\n options.onReport,\n buildReport({\n error,\n thrown,\n handled: false,\n handlerFailure: failure,\n requestId: undefined,\n traceId: undefined,\n spanId: undefined,\n method: c.req.method,\n path: c.req.path,\n }),\n c,\n );\n return response;\n}\n\n/**\n * Builds `ban.onError()` (SPEC 6). Throws `TypeError` for a header option\n * `Headers` would reject; the returned handler resolves to a `Response` for\n * every input and never rejects (ADR 0002).\n */\nexport function createOnError<TErrors extends Catalog>(\n core: BanCore,\n ban: Ban<TErrors>,\n from: (value: unknown) => BanError,\n instanceMap: ErrorMapper<TErrors> | undefined,\n): <E extends Env = Env>(\n options?: HandlerOptions<E, TErrors>,\n) => ErrorHandler<E> {\n return <E extends Env = Env>(\n options: HandlerOptions<E, TErrors> = {},\n ): ErrorHandler<E> => {\n const map = options.map ?? instanceMap;\n const includeStack = options.includeStack ?? false;\n const requestIdHeader =\n options.requestIdHeader ?? DEFAULT_REQUEST_ID_HEADER;\n const errorIdHeader = options.errorIdHeader ?? DEFAULT_ERROR_ID_HEADER;\n const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;\n assertHeaderName('requestIdHeader', requestIdHeader);\n assertHeaderName('errorIdHeader', errorIdHeader);\n assertHeadersInit(options.headers);\n\n return async (thrown: unknown, c: Context<E>): Promise<Response> => {\n try {\n const requestId =\n options.requestId === undefined\n ? readRequestId(\n requestIdHeader === false\n ? undefined\n : c.req.header(requestIdHeader),\n )\n : options.requestId(c);\n const trace = readTrace(c, options.traceId);\n const { error, handled } = resolveThrown(thrown, ban, from, map);\n const renderOptions: RenderOptions = {\n requestId,\n traceId: trace.traceId,\n instance: c.req.path,\n method: c.req.method,\n includeStack,\n };\n const rendered = core.render(error, renderOptions);\n const transform = (body: unknown): unknown =>\n options.transform === undefined\n ? body\n : options.transform(body, error, c);\n const text = capBody(\n transform(rendered.body),\n error,\n renderOptions,\n core.render,\n maxBodyBytes,\n transform,\n );\n const headers = mergeHeaders(\n options.headers,\n rendered.headers,\n errorIdHeader,\n error.id,\n );\n await report(\n options.onReport,\n buildReport({\n error,\n thrown,\n handled,\n handlerFailure: undefined,\n requestId,\n traceId: trace.traceId,\n spanId: trace.spanId,\n method: c.req.method,\n path: c.req.path,\n }),\n c,\n );\n return new Response(text, { status: error.status, headers });\n } catch (failure) {\n return fallback(failure, thrown, c, core, options);\n }\n };\n };\n}\n","import type { BanError } from '../core/ban-error';\nimport type { BanCore } from '../core/internal';\nimport type { ValidationOptions } from '../core/types';\nimport type { ValidationIssue } from './issue';\n\n/**\n * `ban.validation()` (SPEC 8.2). The location travels in `meta.location` so\n * every format can label issues without a dedicated field on `BanError`.\n */\nexport function createValidation(\n core: BanCore,\n): (\n issues: ReadonlyArray<ValidationIssue>,\n options: ValidationOptions,\n) => BanError {\n return (issues, options) =>\n core.build(\n core.definition(core.validationKey),\n {\n detail: options.detail,\n meta: { ...options.meta, location: options.location },\n cause: options.cause,\n },\n issues,\n );\n}\n","import type { BanError, BanErrorOptions } from './ban-error';\nimport type { BanCore } from './internal';\nimport type { Factory } from './types';\n\nfunction isOptions(value: unknown): value is BanErrorOptions {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Resolves the two call forms (SPEC 5.1). The first argument is `unknown` on\n * purpose: the runtime check protects JavaScript callers.\n */\nexport function parseFactoryArgs(\n first: unknown,\n second: BanErrorOptions | undefined,\n): BanErrorOptions {\n if (first === undefined) {\n return second ?? {};\n }\n if (typeof first === 'string') {\n return { ...second, detail: first };\n }\n if (isOptions(first)) {\n return first;\n }\n throw new TypeError(\n `Expected a detail string or an options object, received ${typeof first}`,\n );\n}\n\nexport function createFactory(core: BanCore, key: string): Factory {\n const definition = core.definition(key);\n return (\n first?: string | BanErrorOptions,\n second?: BanErrorOptions,\n ): BanError => core.build(definition, parseFactoryArgs(first, second));\n}\n","import type { BanError } from './ban-error';\nimport type { BanCore } from './internal';\n\nimport { HTTPException } from 'hono/http-exception';\n\nimport {\n BODY_HEADERS,\n MALFORMED_JSON_MESSAGE,\n UNEXPECTED_DETAIL,\n} from '../internal/constants';\nimport { isBanError } from './ban-error';\n\nconst BAD_REQUEST = 400;\nconst RANGE_NOT_SATISFIABLE = 416;\nconst CONTENT_RANGE = 'content-range';\n\n/**\n * The exception's body is discarded, so the headers describing it go with it\n * (SPEC 5.5): kept, a `Content-Length` sized for the old body truncates the\n * rendered JSON and a `Content-Encoding` makes clients decompress plain text.\n * `Content-Range` is the one that changes meaning with the status: on a 416\n * it carries the selected representation's complete length (an asterisk, a\n * slash, and the length), not a description of the body, so it stays.\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-14.4\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.5.17\n */\nfunction withoutBodyHeaders(\n source: Headers | undefined,\n status: number,\n): Headers | undefined {\n if (source === undefined) {\n return undefined;\n }\n const headers = new Headers(source);\n const keepContentRange = status === RANGE_NOT_SATISFIABLE;\n for (const name of BODY_HEADERS) {\n if (!(keepContentRange && name === CONTENT_RANGE)) {\n headers.delete(name);\n }\n }\n return headers;\n}\n\n/**\n * Hono middleware writes `HTTPException.message` for clients, so it is kept as\n * `detail`; `res` headers (for example `WWW-Authenticate` from\n * `hono/bearer-auth`) are preserved, except those describing the discarded\n * body (SPEC 5.5).\n * @ref https://hono.dev/docs/api/exception\n */\nfunction fromHttpException(core: BanCore, error: HTTPException): BanError {\n const status = error.status;\n const definition =\n status === BAD_REQUEST && error.message === MALFORMED_JSON_MESSAGE\n ? core.definition('MALFORMED_JSON')\n : core.byStatus.get(status);\n const detail = error.message === '' ? undefined : error.message;\n const headers = withoutBodyHeaders(error.res?.headers, status);\n if (definition === undefined) {\n return core.custom({\n status,\n title: 'Error',\n detail,\n headers,\n cause: error,\n });\n }\n return core.build(definition, { detail, headers, cause: error });\n}\n\nexport function createFrom(core: BanCore): (value: unknown) => BanError {\n return (value: unknown): BanError => {\n if (isBanError(value)) {\n return value;\n }\n if (value instanceof HTTPException) {\n return fromHttpException(core, value);\n }\n return core.build(core.definition('INTERNAL_SERVER_ERROR'), {\n detail: UNEXPECTED_DETAIL,\n cause: value,\n });\n };\n}\n","import { PROTO_KEYS } from './constants';\n\n/**\n * Shallow copy of own enumerable string keys with prototype-pollution keys\n * removed (ADR 0007). Runs once per render, before any format sees `meta`.\n */\nexport function sanitizeMeta(\n meta: Readonly<Record<string, unknown>>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(meta)) {\n if (!PROTO_KEYS.has(key)) {\n out[key] = meta[key];\n }\n }\n return out;\n}\n","import type { RenderContext, RenderOptions } from '../formats/context';\nimport type { ErrorFormat } from '../formats/types';\nimport type { BanError, RenderedError, Renderer } from './ban-error';\n\nimport { sanitizeMeta } from '../internal/sanitize-meta';\n\nconst SERVER_ERROR = 500;\n\n/**\n * The one place a `BanError` becomes a body (SPEC 6.7). Shared by the handler\n * and `BanError.getResponse()` so both apply the same sanitization.\n */\nexport function createRenderer(\n format: ErrorFormat,\n docsBaseUrl: string | undefined,\n): Renderer {\n return (error: BanError, options: RenderOptions = {}): RenderedError => {\n const includeStack = options.includeStack ?? false;\n const source = error.cause instanceof Error ? error.cause : error;\n const ctx: RenderContext = {\n requestId: options.requestId,\n traceId: options.traceId,\n instance: error.instance ?? options.instance,\n method: options.method,\n includeStack,\n docsBaseUrl: options.docsBaseUrl ?? docsBaseUrl,\n meta: sanitizeMeta(error.meta),\n stack:\n includeStack && error.status >= SERVER_ERROR ? source.stack : undefined,\n truncated: options.truncated ?? false,\n };\n const body =\n error.issues === undefined\n ? format.render(error, ctx)\n : format.renderValidation(error, error.issues, ctx);\n const headers = new Headers(error.headers);\n headers.set('Content-Type', format.contentType);\n return { status: error.status, headers, body };\n };\n}\n","import type { ErrorFormat } from '../formats/types';\nimport type { BanError, BanErrorOptions } from './ban-error';\nimport type { ResolvedDefinition } from './definition';\nimport type { BanCore } from './internal';\nimport type {\n Ban,\n BanInstance,\n BanOptions,\n Catalog,\n CustomErrorInit,\n EmptyCatalog,\n NoReserved,\n} from './types';\n\nimport { problemDetails } from '../formats/problem-details';\nimport { createOnError } from '../handler/on-error';\nimport { generateErrorId } from '../internal/id';\nimport { createValidation } from '../validation/validation-error';\nimport { BanError as BanErrorClass } from './ban-error';\nimport { BUILTIN_CATALOG, FACTORY_NAMES } from './catalog';\nimport { indexByStatus, reasonPhrase, resolveDefinition } from './definition';\nimport { createFactory } from './factories';\nimport { createFrom } from './from';\nimport { createRenderer } from './render';\n\nconst INSTANCE_KEYS = [\n 'format',\n 'catalog',\n 'docsBaseUrl',\n 'validationKey',\n 'error',\n 'custom',\n 'validation',\n 'from',\n 'onError',\n 'render',\n] as const satisfies ReadonlyArray<keyof BanInstance<Catalog>>;\n\nconst RESERVED_KEYS: ReadonlySet<string> = new Set<string>([\n ...INSTANCE_KEYS,\n ...Object.keys(FACTORY_NAMES),\n]);\n\nfunction buildCatalog(custom: Catalog): {\n readonly catalog: Record<string, ResolvedDefinition>;\n readonly byStatus: Map<number, ResolvedDefinition>;\n} {\n const catalog: Record<string, ResolvedDefinition> = {};\n for (const [key, definition] of Object.entries(BUILTIN_CATALOG)) {\n catalog[key] = resolveDefinition(key, definition);\n }\n for (const [key, definition] of Object.entries(custom)) {\n catalog[key] = resolveDefinition(key, definition);\n }\n return { catalog, byStatus: indexByStatus(catalog) };\n}\n\n/**\n * Creates an instance: built-in and custom factories, the handler, and the\n * helpers, all sharing one catalog and one format (SPEC 2, 5).\n */\nexport function createBan<const TErrors extends Catalog = EmptyCatalog>(\n options: BanOptions<TErrors> & {\n readonly errors?: NoReserved<TErrors> | undefined;\n } = {},\n): Ban<TErrors> {\n const custom = (options.errors ?? {}) as Catalog;\n for (const key of Object.keys(custom)) {\n if (RESERVED_KEYS.has(key)) {\n throw new TypeError(\n `Catalog key \"${key}\" collides with a hono-ban instance member`,\n );\n }\n }\n const format: ErrorFormat = options.format ?? problemDetails();\n const docsBaseUrl = options.docsBaseUrl;\n const generateId = options.id ?? generateErrorId;\n const { catalog, byStatus } = buildCatalog(custom);\n const render = createRenderer(format, docsBaseUrl);\n\n const core: BanCore = {\n catalog,\n byStatus,\n format,\n docsBaseUrl,\n validationKey: options.validationKey ?? 'VALIDATION_FAILED',\n generateId,\n render,\n definition(key: string): ResolvedDefinition {\n const definition = catalog[key];\n if (definition === undefined) {\n throw new RangeError(`Unknown error key \"${key}\"`);\n }\n return definition;\n },\n build(definition, errorOptions: BanErrorOptions, issues): BanError {\n return new BanErrorClass({\n ...errorOptions,\n status: definition.status,\n code: definition.code,\n title: definition.title,\n type: errorOptions.type ?? definition.type,\n id: errorOptions.id ?? generateId(),\n definition,\n issues,\n render,\n });\n },\n custom(init: CustomErrorInit): BanError {\n return new BanErrorClass({\n ...init,\n code: init.code ?? 'CUSTOM',\n title: init.title ?? reasonPhrase(init.status) ?? 'Error',\n id: init.id ?? generateId(),\n render,\n });\n },\n };\n if (!Object.hasOwn(catalog, core.validationKey)) {\n throw new RangeError(\n `validationKey \"${core.validationKey}\" is not in the catalog`,\n );\n }\n\n const from = createFrom(core);\n const instance: Record<string, unknown> = {\n format,\n catalog,\n docsBaseUrl,\n validationKey: core.validationKey,\n error: (key: string, errorOptions: BanErrorOptions = {}): BanError =>\n core.build(core.definition(key), errorOptions),\n custom: (init: CustomErrorInit): BanError => core.custom(init),\n validation: createValidation(core),\n from,\n render,\n };\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- reason: the object is assembled from the catalog at runtime; create-ban.test-d.ts proves the shape\n const ban = instance as unknown as Ban<TErrors>;\n instance['onError'] = createOnError(core, ban, from, options.map);\n for (const [name, key] of Object.entries(FACTORY_NAMES)) {\n instance[name] = createFactory(core, key);\n }\n for (const key of Object.keys(custom)) {\n instance[key] = createFactory(core, key);\n }\n return ban;\n}\n","import type { BanError } from '../core/ban-error';\nimport type { ResolvedDefinition } from '../core/definition';\nimport type {\n InferStandardOutput,\n StandardJsonSchema,\n} from '../internal/standard-schema-types';\nimport type { ValidationIssue } from '../validation/issue';\nimport type { JsonSchema, RenderContext, SchemaContext } from './context';\nimport type { ErrorFormat } from './types';\n\n/** Hand-written format: you provide the JSON Schema (SPEC 7.5, first form). */\nexport interface FormatSpec<TBody> {\n readonly name: string;\n readonly contentType: string;\n render(error: BanError, ctx: RenderContext): TBody;\n /** Defaults to `render`. */\n renderValidation?(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): TBody;\n schema(definition: ResolvedDefinition, ctx: SchemaContext): JsonSchema;\n /** Defaults to `schema`. */\n validationSchema?(\n definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema;\n}\n\n/**\n * Schema-first format: a Standard JSON Schema (Zod 4, Valibot, ArkType, and\n * others) types the body and produces the JSON Schema (SPEC 7.5, second form).\n * @ref https://standardschema.dev/json-schema\n */\nexport interface StandardFormatSpec<TSchema extends StandardJsonSchema> {\n readonly name: string;\n readonly contentType: string;\n readonly schema: TSchema;\n render(error: BanError, ctx: RenderContext): InferStandardOutput<TSchema>;\n renderValidation?(\n error: BanError,\n issues: ReadonlyArray<ValidationIssue>,\n ctx: RenderContext,\n ): InferStandardOutput<TSchema>;\n}\n\nfunction isStandardSpec(\n spec: FormatSpec<unknown> | StandardFormatSpec<StandardJsonSchema>,\n): spec is StandardFormatSpec<StandardJsonSchema> {\n return typeof spec.schema !== 'function';\n}\n\nexport function defineFormat<TBody>(\n spec: FormatSpec<TBody>,\n): ErrorFormat<TBody>;\nexport function defineFormat<TSchema extends StandardJsonSchema>(\n spec: StandardFormatSpec<TSchema>,\n): ErrorFormat<InferStandardOutput<TSchema>>;\nexport function defineFormat(\n spec: FormatSpec<unknown> | StandardFormatSpec<StandardJsonSchema>,\n): ErrorFormat {\n if (isStandardSpec(spec)) {\n const jsonSchema = (\n _definition: ResolvedDefinition,\n ctx: SchemaContext,\n ): JsonSchema =>\n spec.schema['~standard'].jsonSchema.output({ target: ctx.dialect });\n const renderValidation = spec.renderValidation?.bind(spec);\n return {\n name: spec.name,\n contentType: spec.contentType,\n render: (error, ctx) => spec.render(error, ctx),\n renderValidation:\n renderValidation ?? ((error, _issues, ctx) => spec.render(error, ctx)),\n schema: jsonSchema,\n validationSchema: jsonSchema,\n };\n }\n const renderValidation = spec.renderValidation?.bind(spec);\n const validationSchema = spec.validationSchema?.bind(spec);\n return {\n name: spec.name,\n contentType: spec.contentType,\n render: (error, ctx) => spec.render(error, ctx),\n renderValidation:\n renderValidation ?? ((error, _issues, ctx) => spec.render(error, ctx)),\n schema: (definition, ctx) => spec.schema(definition, ctx),\n validationSchema:\n validationSchema ?? ((definition, ctx) => spec.schema(definition, ctx)),\n };\n}\n","/**\n * `WWW-Authenticate: Bearer ...` challenge builder (SPEC 3.2). A 401 MUST\n * carry a challenge (RFC 9110 15.5.2) and RFC 6750 fixes the grammar of every\n * Bearer attribute, so each value is checked against its character class and\n * a value taken from a request cannot break the header or smuggle a second\n * attribute. `hono/bearer-auth` emits the same shape.\n * @ref https://www.rfc-editor.org/rfc/rfc6750#section-3\n * @ref https://www.rfc-editor.org/rfc/rfc6749#appendix-A\n * @ref https://www.rfc-editor.org/rfc/rfc9728#section-5.1\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-11.2\n * @packageDocumentation\n */\n\nexport interface BearerChallengeOptions {\n /**\n * Protection space (RFC 9110 11.5). Printable ASCII; double quotes and\n * backslashes are escaped as a quoted-pair. When no attribute at all is\n * given the challenge is `Bearer realm=\"\"`, as `hono/bearer-auth` sends,\n * because RFC 6750 requires at least one attribute.\n */\n readonly realm?: string | undefined;\n /**\n * Scopes the resource requires, joined by single spaces on the wire. Each\n * token is RFC 6749 `scope-token`; an empty list omits the attribute.\n */\n readonly scope?: string | ReadonlyArray<string> | undefined;\n /**\n * `invalid_request` (400), `invalid_token` (401), `insufficient_scope`\n * (403), or an extension code such as RFC 9470 `insufficient_user_authentication`.\n * RFC 6750 3.1 says to omit it when the request carried no token at all.\n */\n readonly error?: string | undefined;\n /** Developer-facing explanation, not meant for end users. */\n readonly errorDescription?: string | undefined;\n /** Absolute URI of a human-readable page explaining the error. */\n readonly errorUri?: string | undefined;\n /** URL of the protected resource metadata document (RFC 9728 5.1). */\n readonly resourceMetadata?: string | undefined;\n}\n\n// RFC 6749 appendix A: NQCHAR = %x21 / %x23-5B / %x5D-7E (no space, quote,\n// or backslash); NQSCHAR adds the space. RFC 6750 assigns NQSCHAR to `error`\n// and `error_description` and NQCHAR to `scope-token` and `error_uri`.\nconst NQCHAR_PATTERN = /^[\\u0021\\u0023-\\u005B\\u005D-\\u007E]+$/u;\nconst NQSCHAR_PATTERN = /^[\\u0020\\u0021\\u0023-\\u005B\\u005D-\\u007E]+$/u;\n// RFC 9110 5.6.4 quoted-string, restricted to printable ASCII; `\"` and `\\`\n// become quoted-pairs.\nconst PRINTABLE_ASCII_PATTERN = /^[\\u0020-\\u007E]*$/u;\nconst QUOTED_PAIR_PATTERN = /[\"\\\\]/gu;\n\nfunction reject(name: string, value: string, grammar: string): never {\n throw new TypeError(\n `bearerChallenge: ${name} must be ${grammar}; received ${JSON.stringify(value)}`,\n );\n}\n\nfunction realmParam(realm: string): string {\n if (!PRINTABLE_ASCII_PATTERN.test(realm)) {\n reject('realm', realm, 'printable ASCII (RFC 9110 quoted-string)');\n }\n return `realm=\"${realm.replaceAll(QUOTED_PAIR_PATTERN, '\\\\$&')}\"`;\n}\n\nfunction scopeParam(\n scope: string | ReadonlyArray<string> | undefined,\n): string | undefined {\n if (scope === undefined) {\n return undefined;\n }\n const tokens = typeof scope === 'string' ? scope.split(' ') : scope;\n if (tokens.length === 0) {\n return undefined;\n }\n for (const token of tokens) {\n if (!NQCHAR_PATTERN.test(token)) {\n reject(\n 'scope',\n token,\n 'a list of RFC 6749 scope-tokens (printable ASCII without space, quote, or backslash)',\n );\n }\n }\n return `scope=\"${tokens.join(' ')}\"`;\n}\n\nfunction param(name: string, value: string, pattern: RegExp): string {\n if (!pattern.test(value)) {\n reject(\n name,\n value,\n pattern === NQCHAR_PATTERN\n ? 'printable ASCII without space, double quote, or backslash (RFC 6749 NQCHAR)'\n : 'printable ASCII without double quote or backslash (RFC 6749 NQSCHAR)',\n );\n }\n return `${name}=\"${value}\"`;\n}\n\n/**\n * Builds the value of a `WWW-Authenticate` header for the Bearer scheme.\n * Attributes appear in RFC 6750 order: `realm`, `scope`, `error`,\n * `error_description`, `error_uri`, then RFC 9728 `resource_metadata`. Throws\n * a `TypeError` when a value falls outside the grammar its RFC assigns to it.\n *\n * @example\n * throw ban.unauthorized({\n * headers: {\n * 'WWW-Authenticate': bearerChallenge({\n * realm: 'api',\n * error: 'invalid_token',\n * errorDescription: 'The access token expired',\n * }),\n * },\n * });\n */\nexport function bearerChallenge(options: BearerChallengeOptions = {}): string {\n const params: Array<string> = [];\n if (options.realm !== undefined) {\n params.push(realmParam(options.realm));\n }\n const scope = scopeParam(options.scope);\n if (scope !== undefined) {\n params.push(scope);\n }\n const attributes: ReadonlyArray<\n readonly [string, string | undefined, RegExp]\n > = [\n ['error', options.error, NQSCHAR_PATTERN],\n ['error_description', options.errorDescription, NQSCHAR_PATTERN],\n ['error_uri', options.errorUri, NQCHAR_PATTERN],\n ['resource_metadata', options.resourceMetadata, NQCHAR_PATTERN],\n ];\n for (const [name, value, pattern] of attributes) {\n if (value !== undefined) {\n params.push(param(name, value, pattern));\n }\n }\n return `Bearer ${params.length === 0 ? 'realm=\"\"' : params.join(', ')}`;\n}\n"],"mappings":"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"}
@@ -1,5 +1,49 @@
1
1
  import { c as ErrorDefinition, d as IssueLocation, n as BanError } from "./types-WuQ-karh.js";
2
- import { a as Catalog, t as Ban, y as BuiltinKey } from "./types-DYr4CtCC.js";
2
+ import { a as Catalog, t as Ban, y as BuiltinKey } from "./types-DH_JfdBQ.js";
3
+ //#region src/postgresql/options.d.ts
4
+ /** What a SQLSTATE, a class, a constraint, or a column maps to. Unset members keep the default. */
5
+ interface PostgresMapping<TKeys extends string = never> {
6
+ readonly key?: BuiltinKey | TKeys | undefined;
7
+ /** Constant client-facing text; never derived from the driver error. */
8
+ readonly detail?: string | undefined;
9
+ readonly meta?: Readonly<Record<string, unknown>> | undefined;
10
+ readonly headers?: HeadersInit | undefined;
11
+ readonly issue?: never;
12
+ }
13
+ interface PostgresIssue {
14
+ readonly path: ReadonlyArray<string | number>;
15
+ readonly message: string;
16
+ /** Default `'body'`. */
17
+ readonly location?: IssueLocation | undefined;
18
+ readonly code?: string | undefined;
19
+ }
20
+ /**
21
+ * Maps a constraint or column to `ban.validation()` with one issue, so a
22
+ * violation reaches the client the way a validator failure does.
23
+ */
24
+ interface PostgresIssueMapping {
25
+ readonly issue: PostgresIssue;
26
+ }
27
+ /** A string is the `detail`. */
28
+ type PostgresConstraintMapping<TKeys extends string = never> = string | PostgresMapping<TKeys> | PostgresIssueMapping;
29
+ interface PostgresMapOptions<TKeys extends string = never> {
30
+ /** By constraint name (field `n`; an index counts as a constraint). Wins over everything below. */
31
+ readonly constraints?: Readonly<Record<string, PostgresConstraintMapping<TKeys>>> | undefined;
32
+ /** By `table.column` or bare `column` (fields `t` and `c`), for errors that name no constraint. */
33
+ readonly columns?: Readonly<Record<string, PostgresConstraintMapping<TKeys>>> | undefined;
34
+ /** By five-character SQLSTATE or two-character class; a code wins over its class. */
35
+ readonly codes?: Readonly<Record<string, PostgresMapping<TKeys>>> | undefined;
36
+ /** Seconds for `Retry-After` on every 503 the mapper produces. Default: no header. */
37
+ readonly retryAfter?: number | undefined;
38
+ }
39
+ /** A catalog that defines every custom key a mapper names. */
40
+ type CatalogWith<TKeys extends string> = Catalog & Readonly<Record<Exclude<TKeys, BuiltinKey>, ErrorDefinition>>;
41
+ /**
42
+ * Accepted as `map` by every ban whose catalog defines the custom keys the
43
+ * options name; a mapper naming only built-in keys fits every ban.
44
+ */
45
+ type PostgresMapper<TKeys extends string = never> = <TErrors extends CatalogWith<TKeys>>(thrown: unknown, ban: Ban<TErrors>) => BanError | undefined;
46
+ //#endregion
3
47
  //#region src/postgresql/error.d.ts
4
48
  /**
5
49
  * Recognizing a Postgres error (SPEC 6.10). The server's ErrorResponse
@@ -93,48 +137,6 @@ export declare function findPostgresError(value: unknown): PostgresErrorLike | u
93
137
  export declare function readPostgresFields(error: PostgresErrorLike): PostgresErrorFields;
94
138
  //#endregion
95
139
  //#region src/postgresql/index.d.ts
96
- /** What a SQLSTATE, a class, a constraint, or a column maps to. Unset members keep the default. */
97
- export interface PostgresMapping<TKeys extends string = never> {
98
- readonly key?: BuiltinKey | TKeys | undefined;
99
- /** Constant client-facing text; never derived from the driver error. */
100
- readonly detail?: string | undefined;
101
- readonly meta?: Readonly<Record<string, unknown>> | undefined;
102
- readonly headers?: HeadersInit | undefined;
103
- readonly issue?: never;
104
- }
105
- export interface PostgresIssue {
106
- readonly path: ReadonlyArray<string | number>;
107
- readonly message: string;
108
- /** Default `'body'`. */
109
- readonly location?: IssueLocation | undefined;
110
- readonly code?: string | undefined;
111
- }
112
- /**
113
- * Maps a constraint or column to `ban.validation()` with one issue, so a
114
- * violation reaches the client the way a validator failure does.
115
- */
116
- export interface PostgresIssueMapping {
117
- readonly issue: PostgresIssue;
118
- }
119
- /** A string is the `detail`. */
120
- export type PostgresConstraintMapping<TKeys extends string = never> = string | PostgresMapping<TKeys> | PostgresIssueMapping;
121
- export interface PostgresMapOptions<TKeys extends string = never> {
122
- /** By constraint name (field `n`; an index counts as a constraint). Wins over everything below. */
123
- readonly constraints?: Readonly<Record<string, PostgresConstraintMapping<TKeys>>> | undefined;
124
- /** By `table.column` or bare `column` (fields `t` and `c`), for errors that name no constraint. */
125
- readonly columns?: Readonly<Record<string, PostgresConstraintMapping<TKeys>>> | undefined;
126
- /** By five-character SQLSTATE or two-character class; a code wins over its class. */
127
- readonly codes?: Readonly<Record<string, PostgresMapping<TKeys>>> | undefined;
128
- /** Seconds for `Retry-After` on every 503 the mapper produces. Default: no header. */
129
- readonly retryAfter?: number | undefined;
130
- }
131
- /** A catalog that defines every custom key a mapper names. */
132
- export type CatalogWith<TKeys extends string> = Catalog & Readonly<Record<Exclude<TKeys, BuiltinKey>, ErrorDefinition>>;
133
- /**
134
- * Accepted as `map` by every ban whose catalog defines the custom keys the
135
- * options name; a mapper naming only built-in keys fits every ban.
136
- */
137
- export type PostgresMapper<TKeys extends string = never> = <TErrors extends CatalogWith<TKeys>>(thrown: unknown, ban: Ban<TErrors>) => BanError | undefined;
138
140
  /**
139
141
  * Builds the mapper. For a recognized Postgres error (`findPostgresError`)
140
142
  * the entry, detail, meta, and headers come, member by member, from the
@@ -148,10 +150,11 @@ export type PostgresMapper<TKeys extends string = never> = <TErrors extends Cata
148
150
  * a `codes` key outside the SQLSTATE grammar, a mapping that is neither a
149
151
  * string nor an object, an `issue` without a path array and a message
150
152
  * string, or `headers` the `Headers` constructor rejects, and `RangeError`
151
- * for a negative or fractional `retryAfter`. A `key` the ban's catalog
152
- * lacks fails at map time with the `RangeError` `ban.error` throws.
153
+ * for a `retryAfter` that is not a non-negative safe integer. A `key` the
154
+ * ban's catalog lacks fails at map time with the `RangeError` `ban.error`
155
+ * throws.
153
156
  */
154
157
  export declare function postgresMapper<TKeys extends string = never>(options?: PostgresMapOptions<TKeys>): PostgresMapper<TKeys>;
155
158
  //#endregion
156
- export type { PostgresErrorFields, PostgresErrorLike };
159
+ export type { CatalogWith, PostgresConstraintMapping, PostgresErrorFields, PostgresErrorLike, PostgresIssue, PostgresIssueMapping, PostgresMapOptions, PostgresMapper, PostgresMapping };
157
160
  //# sourceMappingURL=postgresql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgresql.d.ts","names":[],"sources":["../src/postgresql/error.ts","../src/postgresql/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA+CiB;;WAEN;;WAEA;;WAEA;;WAEA;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;;UAIM;WACN;;WAEA;WACA;WACA;WACA;WACA;;;;;;;wBAgEK,kBACd,iBACC;;;;;;wBAmBa,mBACd,OAAO,oBACN;;;;iBCzIc,gBAAgB;WACtB,MAAM,aAAa;;WAEnB;WACA,OAAO,SAAS;WAChB,UAAU;WACV;;iBAGM;WACN,MAAM;WACN;;WAEA,WAAW;WACX;;;;;;iBAOM;WACN,OAAO;;;YAIN,0BAA0B,yCAElC,gBAAgB,SAChB;iBAEa,mBAAmB;;WAEzB,cACL,SAAS,eAAe,0BAA0B;;WAG7C,UACL,SAAS,eAAe,0BAA0B;;WAG7C,QAAQ,SAAS,eAAe,gBAAgB;;WAEhD;;;YAIC,YAAY,wBAAwB,UAC9C,SAAS,OAAO,QAAQ,OAAO,aAAa;;;;;YAMlC,eAAe,iCACzB,gBAAgB,YAAY,QAE5B,iBACA,KAAK,IAAI,aACN;;;;;;;;;;;;;;;;;wBAoKW,eAAe,8BAC7B,UAAS,mBAAmB,SAC3B,eAAe"}
1
+ {"version":3,"file":"postgresql.d.ts","names":[],"sources":["../src/postgresql/options.ts","../src/postgresql/error.ts","../src/postgresql/index.ts"],"mappings":";;;;UAeiB,gBAAgB;WACtB,MAAM,aAAa;;WAEnB;WACA,OAAO,SAAS;WAChB,UAAU;WACV;;UAGM;WACN,MAAM;WACN;;WAEA,WAAW;WACX;;;;;;UAOM;WACN,OAAO;;;KAIN,0BAA0B,yCAElC,gBAAgB,SAChB;UAEa,mBAAmB;;WAEzB,cACL,SAAS,eAAe,0BAA0B;;WAG7C,UACL,SAAS,eAAe,0BAA0B;;WAG7C,QAAQ,SAAS,eAAe,gBAAgB;;WAEhD;;;KAIC,YAAY,wBAAwB,UAC9C,SAAS,OAAO,QAAQ,OAAO,aAAa;;;;;KAMlC,eAAe,iCACzB,gBAAgB,YAAY,QAE5B,iBACA,KAAK,IAAI,aACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UC3BY;;WAEN;;WAEA;;WAEA;;WAEA;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;WAEA;;WAEA;;;UAIM;WACN;;WAEA;WACA;WACA;WACA;WACA;;;;;;;wBAgEK,kBACd,iBACC;;;;;;wBAmBa,mBACd,OAAO,oBACN;;;;;;;;;;;;;;;;;;;;wBCba,eAAe,8BAC7B,UAAS,mBAAmB,SAC3B,eAAe"}
@@ -74,6 +74,38 @@ function readPostgresFields(error) {
74
74
  };
75
75
  }
76
76
  //#endregion
77
+ //#region src/postgresql/options.ts
78
+ /** A SQLSTATE or a class. */
79
+ const CODE_KEY_PATTERN = /^(?:[0-9A-Z]{5}|[0-9A-Z]{2})$/u;
80
+ function isIssueMapping(mapping) {
81
+ return "issue" in mapping && mapping.issue !== void 0;
82
+ }
83
+ function assertMapping(group, name, mapping) {
84
+ if (typeof mapping === "string") return;
85
+ if (typeof mapping !== "object" || mapping === null) throw new TypeError(`${group} "${name}" must be a string or an object`);
86
+ if (isIssueMapping(mapping)) {
87
+ const { issue } = mapping;
88
+ if (!Array.isArray(issue.path) || typeof issue.message !== "string") throw new TypeError(`${group} "${name}" issue needs a path array and a message string`);
89
+ return;
90
+ }
91
+ if ("headers" in mapping) try {
92
+ assertHeadersInit(mapping.headers);
93
+ } catch (cause) {
94
+ throw new TypeError(`${group} "${name}" headers holds an invalid header name or value`, { cause });
95
+ }
96
+ }
97
+ function assertOptions(options) {
98
+ for (const [name, mapping] of Object.entries(options.constraints ?? {})) assertMapping("constraints", name, mapping);
99
+ for (const [name, mapping] of Object.entries(options.columns ?? {})) assertMapping("columns", name, mapping);
100
+ for (const [code, mapping] of Object.entries(options.codes ?? {})) {
101
+ if (!CODE_KEY_PATTERN.test(code)) throw new TypeError(`codes "${code}" is not a five-character SQLSTATE or a two-character class`);
102
+ if (typeof mapping === "string" || isIssueMapping(mapping)) throw new TypeError(`codes "${code}" must be a mapping object`);
103
+ assertMapping("codes", code, mapping);
104
+ }
105
+ const { retryAfter } = options;
106
+ if (retryAfter !== void 0 && (!Number.isSafeInteger(retryAfter) || retryAfter < 0)) throw new RangeError("retryAfter must be a non-negative safe integer");
107
+ }
108
+ //#endregion
77
109
  //#region src/postgresql/sqlstate.ts
78
110
  const UNAVAILABLE_DETAIL = "Temporarily unavailable, try again later";
79
111
  const CONSTRAINT_DETAIL = "A value violates a constraint";
@@ -167,37 +199,12 @@ function sqlstateDefault(sqlstate, errorClass) {
167
199
  const SERVICE_UNAVAILABLE = 503;
168
200
  const RETRY_AFTER = "Retry-After";
169
201
  const INTERNAL_KEY = "INTERNAL_SERVER_ERROR";
170
- /** A SQLSTATE or a class. */
171
- const CODE_KEY_PATTERN = /^(?:[0-9A-Z]{5}|[0-9A-Z]{2})$/u;
172
- function isIssueMapping(mapping) {
173
- return "issue" in mapping && mapping.issue !== void 0;
174
- }
175
- function assertMapping(group, name, mapping) {
176
- if (typeof mapping === "string") return;
177
- if (typeof mapping !== "object" || mapping === null) throw new TypeError(`${group} "${name}" must be a string or an object`);
178
- if (isIssueMapping(mapping)) {
179
- const { issue } = mapping;
180
- if (!Array.isArray(issue.path) || typeof issue.message !== "string") throw new TypeError(`${group} "${name}" issue needs a path array and a message string`);
181
- return;
182
- }
183
- if ("headers" in mapping) try {
184
- assertHeadersInit(mapping.headers);
185
- } catch (cause) {
186
- throw new TypeError(`${group} "${name}" headers holds an invalid header name or value`, { cause });
187
- }
188
- }
189
- function assertOptions(options) {
190
- for (const [name, mapping] of Object.entries(options.constraints ?? {})) assertMapping("constraints", name, mapping);
191
- for (const [name, mapping] of Object.entries(options.columns ?? {})) assertMapping("columns", name, mapping);
192
- for (const [code, mapping] of Object.entries(options.codes ?? {})) {
193
- if (!CODE_KEY_PATTERN.test(code)) throw new TypeError(`codes "${code}" is not a five-character SQLSTATE or a two-character class`);
194
- if (typeof mapping === "string" || isIssueMapping(mapping)) throw new TypeError(`codes "${code}" must be a mapping object`);
195
- assertMapping("codes", code, mapping);
196
- }
197
- const { retryAfter } = options;
198
- if (retryAfter !== void 0 && (!Number.isInteger(retryAfter) || retryAfter < 0)) throw new RangeError("retryAfter must be a non-negative integer");
199
- }
200
- /** Own properties only: a constraint named `constructor` must not hit the prototype. */
202
+ /**
203
+ * Own properties only: a constraint named `constructor` must not hit the
204
+ * prototype, and an entry inherited by a `codes` object built on a custom
205
+ * prototype was never validated by `assertOptions`, which reads own
206
+ * properties too.
207
+ */
201
208
  function lookup(table, name) {
202
209
  return name !== void 0 && Object.hasOwn(table, name) ? table[name] : void 0;
203
210
  }
@@ -205,14 +212,21 @@ function columnMapping(columns, fields) {
205
212
  if (fields.column === void 0) return;
206
213
  return (fields.table === void 0 ? void 0 : lookup(columns, `${fields.table}.${fields.column}`)) ?? lookup(columns, fields.column);
207
214
  }
208
- function toValidation(ban, mapping) {
215
+ /**
216
+ * The driver error travels as `cause` here as well, so
217
+ * `ErrorReport.error.cause` holds it for every mapping kind (SPEC 6.10).
218
+ */
219
+ function toValidation(ban, mapping, cause) {
209
220
  const { issue } = mapping;
210
221
  const validationIssue = {
211
222
  path: issue.path,
212
223
  message: issue.message,
213
224
  code: issue.code
214
225
  };
215
- return ban.validation([validationIssue], { location: issue.location ?? "body" });
226
+ return ban.validation([validationIssue], {
227
+ location: issue.location ?? "body",
228
+ cause
229
+ });
216
230
  }
217
231
  function build(ban, mappings, fallback, retryAfter, cause) {
218
232
  if (mappings.length === 0 && fallback === void 0) return;
@@ -240,8 +254,9 @@ function build(ban, mappings, fallback, retryAfter, cause) {
240
254
  * a `codes` key outside the SQLSTATE grammar, a mapping that is neither a
241
255
  * string nor an object, an `issue` without a path array and a message
242
256
  * string, or `headers` the `Headers` constructor rejects, and `RangeError`
243
- * for a negative or fractional `retryAfter`. A `key` the ban's catalog
244
- * lacks fails at map time with the `RangeError` `ban.error` throws.
257
+ * for a `retryAfter` that is not a non-negative safe integer. A `key` the
258
+ * ban's catalog lacks fails at map time with the `RangeError` `ban.error`
259
+ * throws.
245
260
  */
246
261
  function postgresMapper(options = {}) {
247
262
  assertOptions(options);
@@ -253,13 +268,13 @@ function postgresMapper(options = {}) {
253
268
  if (error === void 0) return;
254
269
  const fields = readPostgresFields(error);
255
270
  const named = lookup(constraints, fields.constraint) ?? columnMapping(columns, fields);
256
- if (typeof named === "object" && isIssueMapping(named)) return toValidation(ban, named);
271
+ if (typeof named === "object" && isIssueMapping(named)) return toValidation(ban, named, error);
257
272
  const mappings = [];
258
273
  if (typeof named === "string") mappings.push({ detail: named });
259
274
  else if (named !== void 0) mappings.push(named);
260
- const byCode = codes[fields.sqlstate];
275
+ const byCode = lookup(codes, fields.sqlstate);
261
276
  if (byCode !== void 0) mappings.push(byCode);
262
- const byClass = codes[fields.errorClass];
277
+ const byClass = lookup(codes, fields.errorClass);
263
278
  if (byClass !== void 0) mappings.push(byClass);
264
279
  return build(ban, mappings, sqlstateDefault(fields.sqlstate, fields.errorClass), options.retryAfter, error);
265
280
  };
@@ -1 +1 @@
1
- {"version":3,"file":"postgresql.js","names":[],"sources":["../src/postgresql/error.ts","../src/postgresql/sqlstate.ts","../src/postgresql/index.ts"],"sourcesContent":["/**\n * Recognizing a Postgres error (SPEC 6.10). The server's ErrorResponse\n * carries the SQLSTATE in field `C` and the severity in field `S`, both\n * \"always present\"; field `V` repeats the severity unlocalized on servers\n * from 9.6 on. What the drivers expose:\n *\n * - node-postgres (`DatabaseError`), PGlite (the same class from\n * `@electric-sql/pg-protocol`), and Neon (`NeonDbError`): `code` is the\n * SQLSTATE, `severity` is field `S`; `constraint`, `table`, `schema`,\n * `column` are fields `n`, `t`, `s`, `c`.\n * - postgres.js (`PostgresError`): `code`, `severity` from field `V`,\n * `severity_local` from field `S`, and `constraint_name`, `table_name`,\n * `schema_name`, `column_name`.\n * - Bun (`PostgresError`): the SQLSTATE is `errno`; `code` is the constant\n * `ERR_POSTGRES_SERVER_ERROR` (or `ERR_POSTGRES_SYNTAX_ERROR` for 42601);\n * `severity` and the object fields follow node-postgres.\n *\n * Wrappers: Drizzle's `DrizzleQueryError` and Slonik keep the driver error\n * on `cause`; Sequelize 6 on `original` (and `parent`, the same object);\n * `db-errors` (Objection) on `nativeError`. Kysely rethrows the driver error\n * itself; TypeORM's `QueryFailedError` and MikroORM's `DriverException` copy\n * the driver error's own properties onto themselves, so they match directly.\n * Typed structurally, no driver is imported (ADR 0009).\n * @ref https://www.postgresql.org/docs/current/protocol-error-fields.html\n * @ref https://www.postgresql.org/docs/current/errcodes-appendix.html\n * @ref https://github.com/brianc/node-postgres/blob/master/packages/pg-protocol/src/messages.ts\n * @ref https://github.com/electric-sql/pglite/blob/main/packages/pglite/src/errors.ts\n * @ref https://github.com/porsager/postgres/blob/master/src/connection.js\n * @ref https://github.com/porsager/postgres/blob/master/src/errors.js\n * @ref https://github.com/neondatabase/serverless/blob/main/src/httpQuery.ts\n * @ref https://github.com/oven-sh/bun/blob/main/src/js/internal/sql/errors.ts\n * @ref https://github.com/oven-sh/bun/blob/main/src/sql_jsc/postgres/protocol/error_response_jsc.rs\n * @ref https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/errors.ts\n * @ref https://github.com/kysely-org/kysely/blob/master/src/dialect/postgres/postgres-driver.ts\n * @ref https://github.com/kysely-org/kysely/blob/master/src/util/stack-trace-utils.ts\n * @ref https://github.com/sequelize/sequelize/blob/v6/src/errors/database-error.ts\n * @ref https://github.com/Vincit/db-errors/blob/master/lib/errors/DBError.js\n * @ref https://github.com/typeorm/typeorm/blob/master/src/error/QueryFailedError.ts\n * @ref https://github.com/mikro-orm/mikro-orm/blob/master/packages/core/src/exceptions.ts\n * @packageDocumentation\n */\n\n/**\n * The members `findPostgresError` recognizes and `readPostgresFields`\n * reads. A recognized value has a SQLSTATE in `code` or `errno` and a\n * string in `severity` or `severity_local`.\n */\nexport interface PostgresErrorLike {\n /** The SQLSTATE; on Bun, an `ERR_POSTGRES_*` constant instead. */\n readonly code?: string | undefined;\n /** Bun: the SQLSTATE. */\n readonly errno?: string | undefined;\n /** Field `S` (node-postgres, PGlite, Neon, Bun) or field `V` (postgres.js). */\n readonly severity?: string | undefined;\n /** Field `S` on postgres.js. */\n readonly severity_local?: string | undefined;\n readonly constraint?: string | undefined;\n readonly table?: string | undefined;\n readonly schema?: string | undefined;\n readonly column?: string | undefined;\n /** postgres.js spelling of `constraint`. */\n readonly constraint_name?: string | undefined;\n /** postgres.js spelling of `table`. */\n readonly table_name?: string | undefined;\n /** postgres.js spelling of `schema`. */\n readonly schema_name?: string | undefined;\n /** postgres.js spelling of `column`. */\n readonly column_name?: string | undefined;\n}\n\n/** The fields normalized across drivers. */\nexport interface PostgresErrorFields {\n readonly sqlstate: string;\n /** The first two characters of the SQLSTATE. */\n readonly errorClass: string;\n readonly constraint: string | undefined;\n readonly table: string | undefined;\n readonly schema: string | undefined;\n readonly column: string | undefined;\n}\n\n/**\n * SQLSTATE grammar: five digits or uppercase letters. Node's system errors\n * carry a `code` in the same grammar (`EPIPE`) and Prisma's client errors\n * too (`P2002`), which is why a severity string is required as well: the\n * protocol always sends one and every driver keeps it.\n */\nconst SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/u;\nconst CLASS_LENGTH = 2;\n/** Links `findPostgresError` follows, in order, at each level. */\nconst LINKS: ReadonlyArray<string> = ['cause', 'original', 'nativeError'];\n/** How many links `findPostgresError` follows. */\nconst MAX_DEPTH = 8;\n\n/**\n * Any non-null object. Driver errors are `Error` subclasses, so\n * `isPlainRecord` from `internal/json.ts` would reject them.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isSqlstate(value: unknown): value is string {\n return typeof value === 'string' && SQLSTATE_PATTERN.test(value);\n}\n\nfunction sqlstateOf(code: unknown, errno: unknown): string | undefined {\n if (isSqlstate(code)) {\n return code;\n }\n return isSqlstate(errno) ? errno : undefined;\n}\n\n/** True when `value` carries a SQLSTATE and a severity string. */\nexport function isPostgresError(value: unknown): value is PostgresErrorLike {\n return (\n isRecord(value) &&\n sqlstateOf(value['code'], value['errno']) !== undefined &&\n (typeof value['severity'] === 'string' ||\n typeof value['severity_local'] === 'string')\n );\n}\n\nfunction nextLink(record: Record<string, unknown>): unknown {\n for (const name of LINKS) {\n const linked = record[name];\n if (isRecord(linked)) {\n return linked;\n }\n }\n return undefined;\n}\n\n/**\n * The value itself or the first Postgres error reached through `cause`,\n * `original`, or `nativeError` (the first of those that is an object, at\n * each level), at most eight links deep.\n */\nexport function findPostgresError(\n value: unknown,\n): PostgresErrorLike | undefined {\n let current: unknown = value;\n for (let depth = 0; depth <= MAX_DEPTH; depth += 1) {\n if (isPostgresError(current)) {\n return current;\n }\n if (!isRecord(current)) {\n return undefined;\n }\n current = nextLink(current);\n }\n return undefined;\n}\n\n/**\n * Normalizes the driver spellings. The SQLSTATE comes from `code`, else\n * `errno`; the object fields from the node-postgres names, else the\n * postgres.js names.\n */\nexport function readPostgresFields(\n error: PostgresErrorLike,\n): PostgresErrorFields {\n // `isPostgresError` guarantees one of the two; the empty string keeps the\n // return type honest for a structurally typed caller that bypassed it.\n const sqlstate = sqlstateOf(error.code, error.errno) ?? '';\n return {\n sqlstate,\n errorClass: sqlstate.slice(0, CLASS_LENGTH),\n constraint:\n readString(error.constraint) ?? readString(error.constraint_name),\n table: readString(error.table) ?? readString(error.table_name),\n schema: readString(error.schema) ?? readString(error.schema_name),\n column: readString(error.column) ?? readString(error.column_name),\n };\n}\n","/**\n * Default SQLSTATE mapping (SPEC 6.10, ADR 0015). A code is looked up\n * first, then its two-character class; a code or class with no row maps to\n * nothing, so the handler's own fallthrough applies (a constant 500 with\n * `handled: false`). Every detail here is a constant: nothing from the\n * server's `message`, `detail`, or `hint` is ever forwarded (ADR 0007).\n *\n * The rules behind the rows:\n * - 409 for a change that conflicts with the current state (a duplicate, a\n * missing or still-referenced row); 422 for a value the server understood\n * but cannot accept (missing, out of range, failing a check).\n * - 503 only where the same request is expected to succeed after a delay:\n * serialization failures and deadlocks (Postgres documents retrying the\n * transaction as the remedy), a lock a `NOWAIT` could not take, a\n * read-only replica during failover, and the connection, resource, and\n * operator classes. `Retry-After` is attached only to these.\n * - Excluded from their class default (`UNMAPPED`): the two codes meaning\n * the commit outcome is unknown (a retry could apply a write twice),\n * `temp_file_limit` (per query, fails again), `query_canceled` (a\n * statement timeout, an operator cancel, or the client's own abort; the\n * SQLSTATE cannot tell overload from a query that is always too slow), and\n * the class 22 codes raised by the application's own SQL rather than by a\n * client value (sequence exhaustion, `COPY` input, arithmetic).\n * @ref https://www.postgresql.org/docs/current/errcodes-appendix.html\n * @ref https://www.postgresql.org/docs/current/transaction-iso.html\n * @ref https://www.postgresql.org/docs/current/runtime-config-client.html\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.5.10\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.5.21\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.6.4\n * @ref https://docs.postgrest.org/en/stable/references/errors.html\n * @ref https://github.com/pgbouncer/pgbouncer/blob/master/src/proto.c\n * @packageDocumentation\n */\nimport type { BuiltinKey } from '../core/catalog';\n\nexport interface SqlstateDefault {\n readonly key: BuiltinKey;\n readonly detail: string;\n}\n\nconst UNAVAILABLE_DETAIL = 'Temporarily unavailable, try again later';\nconst CONSTRAINT_DETAIL = 'A value violates a constraint';\n\nconst CONFLICT_DUPLICATE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A record with the same value already exists',\n};\nconst CONFLICT_OVERLAP: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A record overlaps an existing one',\n};\nconst CONFLICT_REFERENCE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A referenced record does not exist or is still in use',\n};\nconst CONFLICT_IN_USE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A record is still in use',\n};\nconst CONFLICT_CHANGE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'The change conflicts with existing records',\n};\nconst MISSING_VALUE: SqlstateDefault = {\n key: 'UNPROCESSABLE_CONTENT',\n detail: 'A required value is missing',\n};\nconst INVALID_VALUE: SqlstateDefault = {\n key: 'UNPROCESSABLE_CONTENT',\n detail: 'A value is invalid or out of range',\n};\nconst UNPROCESSABLE_CONSTRAINT: SqlstateDefault = {\n key: 'UNPROCESSABLE_CONTENT',\n detail: CONSTRAINT_DETAIL,\n};\nconst UNAVAILABLE: SqlstateDefault = {\n key: 'SERVICE_UNAVAILABLE',\n detail: UNAVAILABLE_DETAIL,\n};\n\n/**\n * Per-code rows. Class 23 splits by condition. 23001 (`restrict_violation`)\n * is raised from PostgreSQL 18 for `ON DELETE RESTRICT`; earlier versions\n * raise 23503 for the same action. 40002 is defined by the standard but not\n * raised by PostgreSQL, which reports deferred constraint failures at commit\n * under their class 23 codes; the row serves wire-compatible servers.\n */\nconst BY_CODE: Readonly<Record<string, SqlstateDefault>> = {\n // unique_violation\n '23505': CONFLICT_DUPLICATE,\n // exclusion_violation\n '23P01': CONFLICT_OVERLAP,\n // foreign_key_violation, in both directions\n '23503': CONFLICT_REFERENCE,\n // restrict_violation\n '23001': CONFLICT_IN_USE,\n // not_null_violation\n '23502': MISSING_VALUE,\n // check_violation\n '23514': UNPROCESSABLE_CONSTRAINT,\n // transaction_integrity_constraint_violation\n '40002': CONFLICT_CHANGE,\n // serialization_failure\n '40001': UNAVAILABLE,\n // deadlock_detected\n '40P01': UNAVAILABLE,\n // lock_not_available\n '55P03': UNAVAILABLE,\n // read_only_sql_transaction\n '25006': UNAVAILABLE,\n};\n\n/** Codes excluded from their class row; see the module comment. */\nconst UNMAPPED: ReadonlySet<string> = new Set([\n // transaction_resolution_unknown\n '08007',\n // statement_completion_unknown\n '40003',\n // configuration_limit_exceeded\n '53400',\n // query_canceled\n '57014',\n // sequence_generator_limit_exceeded\n '2200H',\n // bad_copy_file_format\n '22P04',\n // division_by_zero\n '22012',\n]);\n\n/**\n * Per-class rows. Class 08 includes 08P01 (`protocol_violation`), which\n * PostgreSQL raises for a client protocol mistake but which PgBouncer sends\n * as the default SQLSTATE of every pooler error (queue and login timeouts,\n * connection limits, shutdown); the transient reading wins by default and\n * `codes: { '08P01': ... }` selects the other.\n */\nconst BY_CLASS: Readonly<Record<string, SqlstateDefault>> = {\n // data_exception\n '22': INVALID_VALUE,\n // integrity_constraint_violation\n '23': UNPROCESSABLE_CONSTRAINT,\n // connection_exception\n '08': UNAVAILABLE,\n // transaction_rollback\n '40': UNAVAILABLE,\n // insufficient_resources\n '53': UNAVAILABLE,\n // operator_intervention\n '57': UNAVAILABLE,\n};\n\nexport function sqlstateDefault(\n sqlstate: string,\n errorClass: string,\n): SqlstateDefault | undefined {\n if (UNMAPPED.has(sqlstate)) {\n return undefined;\n }\n return BY_CODE[sqlstate] ?? BY_CLASS[errorClass];\n}\n","/**\n * `postgresMapper(options?)` (SPEC 6.10, ADR 0015): an error mapper for\n * `createBan({ map })` or `ban.onError({ map })` that turns a Postgres\n * driver error into a catalog entry by SQLSTATE. The driver's `message`,\n * `detail`, and `hint` never reach `detail`, `meta`, or `headers` (ADR\n * 0007); under `includeStack` a 5xx body carries the driver error's stack\n * as SPEC 6.7 describes. The thrown value stays on `ErrorReport.cause` and\n * the driver error on `ErrorReport.error.cause`.\n * @ref https://www.postgresql.org/docs/current/errcodes-appendix.html\n * @packageDocumentation\n */\nimport type { BanError, BanErrorOptions } from '../core/ban-error';\nimport type { BuiltinKey } from '../core/catalog';\nimport type { ErrorDefinition } from '../core/definition';\nimport type { Ban, Catalog } from '../core/types';\nimport type { IssueLocation, ValidationIssue } from '../validation/issue';\nimport type { PostgresErrorFields } from './error';\nimport type { SqlstateDefault } from './sqlstate';\n\nimport { assertHeadersInit } from '../handler/headers';\nimport { UNEXPECTED_DETAIL } from '../internal/constants';\nimport { findPostgresError, readPostgresFields } from './error';\nimport { sqlstateDefault } from './sqlstate';\n\nexport type { PostgresErrorFields, PostgresErrorLike } from './error';\nexport { findPostgresError, readPostgresFields } from './error';\n\n/** What a SQLSTATE, a class, a constraint, or a column maps to. Unset members keep the default. */\nexport interface PostgresMapping<TKeys extends string = never> {\n readonly key?: BuiltinKey | TKeys | undefined;\n /** Constant client-facing text; never derived from the driver error. */\n readonly detail?: string | undefined;\n readonly meta?: Readonly<Record<string, unknown>> | undefined;\n readonly headers?: HeadersInit | undefined;\n readonly issue?: never;\n}\n\nexport interface PostgresIssue {\n readonly path: ReadonlyArray<string | number>;\n readonly message: string;\n /** Default `'body'`. */\n readonly location?: IssueLocation | undefined;\n readonly code?: string | undefined;\n}\n\n/**\n * Maps a constraint or column to `ban.validation()` with one issue, so a\n * violation reaches the client the way a validator failure does.\n */\nexport interface PostgresIssueMapping {\n readonly issue: PostgresIssue;\n}\n\n/** A string is the `detail`. */\nexport type PostgresConstraintMapping<TKeys extends string = never> =\n | string\n | PostgresMapping<TKeys>\n | PostgresIssueMapping;\n\nexport interface PostgresMapOptions<TKeys extends string = never> {\n /** By constraint name (field `n`; an index counts as a constraint). Wins over everything below. */\n readonly constraints?:\n | Readonly<Record<string, PostgresConstraintMapping<TKeys>>>\n | undefined;\n /** By `table.column` or bare `column` (fields `t` and `c`), for errors that name no constraint. */\n readonly columns?:\n | Readonly<Record<string, PostgresConstraintMapping<TKeys>>>\n | undefined;\n /** By five-character SQLSTATE or two-character class; a code wins over its class. */\n readonly codes?: Readonly<Record<string, PostgresMapping<TKeys>>> | undefined;\n /** Seconds for `Retry-After` on every 503 the mapper produces. Default: no header. */\n readonly retryAfter?: number | undefined;\n}\n\n/** A catalog that defines every custom key a mapper names. */\nexport type CatalogWith<TKeys extends string> = Catalog &\n Readonly<Record<Exclude<TKeys, BuiltinKey>, ErrorDefinition>>;\n\n/**\n * Accepted as `map` by every ban whose catalog defines the custom keys the\n * options name; a mapper naming only built-in keys fits every ban.\n */\nexport type PostgresMapper<TKeys extends string = never> = <\n TErrors extends CatalogWith<TKeys>,\n>(\n thrown: unknown,\n ban: Ban<TErrors>,\n) => BanError | undefined;\n\nconst SERVICE_UNAVAILABLE = 503;\nconst RETRY_AFTER = 'Retry-After';\nconst INTERNAL_KEY: BuiltinKey = 'INTERNAL_SERVER_ERROR';\n/** A SQLSTATE or a class. */\nconst CODE_KEY_PATTERN = /^(?:[0-9A-Z]{5}|[0-9A-Z]{2})$/u;\n\ntype Lookup<TKeys extends string> = Readonly<\n Record<string, PostgresConstraintMapping<TKeys>>\n>;\n\nfunction isIssueMapping(mapping: object): mapping is PostgresIssueMapping {\n return 'issue' in mapping && mapping.issue !== undefined;\n}\n\nfunction assertMapping(group: string, name: string, mapping: unknown): void {\n if (typeof mapping === 'string') {\n return;\n }\n if (typeof mapping !== 'object' || mapping === null) {\n throw new TypeError(`${group} \"${name}\" must be a string or an object`);\n }\n if (isIssueMapping(mapping)) {\n const { issue } = mapping;\n if (!Array.isArray(issue.path) || typeof issue.message !== 'string') {\n throw new TypeError(\n `${group} \"${name}\" issue needs a path array and a message string`,\n );\n }\n return;\n }\n if ('headers' in mapping) {\n try {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- reason: the Headers constructor is the validator; a value of the wrong shape is exactly what this check reports\n assertHeadersInit(mapping.headers as HeadersInit | undefined);\n } catch (cause) {\n throw new TypeError(\n `${group} \"${name}\" headers holds an invalid header name or value`,\n { cause },\n );\n }\n }\n}\n\nfunction assertOptions<TKeys extends string>(\n options: PostgresMapOptions<TKeys>,\n): void {\n for (const [name, mapping] of Object.entries(options.constraints ?? {})) {\n assertMapping('constraints', name, mapping);\n }\n for (const [name, mapping] of Object.entries(options.columns ?? {})) {\n assertMapping('columns', name, mapping);\n }\n for (const [code, mapping] of Object.entries(options.codes ?? {})) {\n if (!CODE_KEY_PATTERN.test(code)) {\n throw new TypeError(\n `codes \"${code}\" is not a five-character SQLSTATE or a two-character class`,\n );\n }\n if (typeof mapping === 'string' || isIssueMapping(mapping)) {\n throw new TypeError(`codes \"${code}\" must be a mapping object`);\n }\n assertMapping('codes', code, mapping);\n }\n const { retryAfter } = options;\n if (\n retryAfter !== undefined &&\n (!Number.isInteger(retryAfter) || retryAfter < 0)\n ) {\n throw new RangeError('retryAfter must be a non-negative integer');\n }\n}\n\n/** Own properties only: a constraint named `constructor` must not hit the prototype. */\nfunction lookup<TKeys extends string>(\n table: Lookup<TKeys>,\n name: string | undefined,\n): PostgresConstraintMapping<TKeys> | undefined {\n return name !== undefined && Object.hasOwn(table, name)\n ? table[name]\n : undefined;\n}\n\nfunction columnMapping<TKeys extends string>(\n columns: Lookup<TKeys>,\n fields: PostgresErrorFields,\n): PostgresConstraintMapping<TKeys> | undefined {\n if (fields.column === undefined) {\n return undefined;\n }\n const qualified =\n fields.table === undefined\n ? undefined\n : lookup(columns, `${fields.table}.${fields.column}`);\n return qualified ?? lookup(columns, fields.column);\n}\n\nfunction toValidation<TErrors extends Catalog>(\n ban: Ban<TErrors>,\n mapping: PostgresIssueMapping,\n): BanError {\n const { issue } = mapping;\n const validationIssue: ValidationIssue = {\n path: issue.path,\n message: issue.message,\n code: issue.code,\n };\n return ban.validation([validationIssue], {\n location: issue.location ?? 'body',\n });\n}\n\nfunction build<TKeys extends string, TErrors extends CatalogWith<TKeys>>(\n ban: Ban<TErrors>,\n mappings: ReadonlyArray<PostgresMapping<TKeys>>,\n fallback: SqlstateDefault | undefined,\n retryAfter: number | undefined,\n cause: unknown,\n): BanError | undefined {\n if (mappings.length === 0 && fallback === undefined) {\n return undefined;\n }\n const key: BuiltinKey | TKeys =\n mappings.find((mapping) => mapping.key !== undefined)?.key ??\n fallback?.key ??\n INTERNAL_KEY;\n const detail =\n mappings.find((mapping) => mapping.detail !== undefined)?.detail ??\n fallback?.detail ??\n UNEXPECTED_DETAIL;\n const options: BanErrorOptions = {\n detail,\n meta: mappings.find((mapping) => mapping.meta !== undefined)?.meta,\n headers: mappings.find((mapping) => mapping.headers !== undefined)?.headers,\n cause,\n };\n const error = ban.error(key, options);\n if (\n retryAfter !== undefined &&\n error.status === SERVICE_UNAVAILABLE &&\n !error.headers.has(RETRY_AFTER)\n ) {\n error.headers.set(RETRY_AFTER, String(retryAfter));\n }\n return error;\n}\n\n/**\n * Builds the mapper. For a recognized Postgres error (`findPostgresError`)\n * the entry, detail, meta, and headers come, member by member, from the\n * first source that sets them: the `constraints` entry for its constraint\n * name, the `columns` entry for its `table.column` or `column`, `codes` for\n * its SQLSTATE, `codes` for its class, then the built-in table\n * (`sqlstate.ts`). A `constraints` or `columns` entry with `issue` returns\n * `ban.validation()` instead. A SQLSTATE with no source at any level maps\n * to `undefined`, as does a value that is not a Postgres error, so the\n * handler's own fallthrough applies. Throws `TypeError` at construction for\n * a `codes` key outside the SQLSTATE grammar, a mapping that is neither a\n * string nor an object, an `issue` without a path array and a message\n * string, or `headers` the `Headers` constructor rejects, and `RangeError`\n * for a negative or fractional `retryAfter`. A `key` the ban's catalog\n * lacks fails at map time with the `RangeError` `ban.error` throws.\n */\nexport function postgresMapper<TKeys extends string = never>(\n options: PostgresMapOptions<TKeys> = {},\n): PostgresMapper<TKeys> {\n assertOptions(options);\n const constraints: Lookup<TKeys> = options.constraints ?? {};\n const columns: Lookup<TKeys> = options.columns ?? {};\n const codes: Readonly<Record<string, PostgresMapping<TKeys>>> =\n options.codes ?? {};\n return <TErrors extends CatalogWith<TKeys>>(\n thrown: unknown,\n ban: Ban<TErrors>,\n ): BanError | undefined => {\n const error = findPostgresError(thrown);\n if (error === undefined) {\n return undefined;\n }\n const fields = readPostgresFields(error);\n const named =\n lookup(constraints, fields.constraint) ?? columnMapping(columns, fields);\n if (typeof named === 'object' && isIssueMapping(named)) {\n return toValidation(ban, named);\n }\n const mappings: Array<PostgresMapping<TKeys>> = [];\n if (typeof named === 'string') {\n mappings.push({ detail: named });\n } else if (named !== undefined) {\n mappings.push(named);\n }\n const byCode = codes[fields.sqlstate];\n if (byCode !== undefined) {\n mappings.push(byCode);\n }\n const byClass = codes[fields.errorClass];\n if (byClass !== undefined) {\n mappings.push(byClass);\n }\n return build(\n ban,\n mappings,\n sqlstateDefault(fields.sqlstate, fields.errorClass),\n options.retryAfter,\n error,\n );\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"postgresql.js","names":[],"sources":["../src/postgresql/error.ts","../src/postgresql/options.ts","../src/postgresql/sqlstate.ts","../src/postgresql/index.ts"],"sourcesContent":["/**\n * Recognizing a Postgres error (SPEC 6.10). The server's ErrorResponse\n * carries the SQLSTATE in field `C` and the severity in field `S`, both\n * \"always present\"; field `V` repeats the severity unlocalized on servers\n * from 9.6 on. What the drivers expose:\n *\n * - node-postgres (`DatabaseError`), PGlite (the same class from\n * `@electric-sql/pg-protocol`), and Neon (`NeonDbError`): `code` is the\n * SQLSTATE, `severity` is field `S`; `constraint`, `table`, `schema`,\n * `column` are fields `n`, `t`, `s`, `c`.\n * - postgres.js (`PostgresError`): `code`, `severity` from field `V`,\n * `severity_local` from field `S`, and `constraint_name`, `table_name`,\n * `schema_name`, `column_name`.\n * - Bun (`PostgresError`): the SQLSTATE is `errno`; `code` is the constant\n * `ERR_POSTGRES_SERVER_ERROR` (or `ERR_POSTGRES_SYNTAX_ERROR` for 42601);\n * `severity` and the object fields follow node-postgres.\n *\n * Wrappers: Drizzle's `DrizzleQueryError` and Slonik keep the driver error\n * on `cause`; Sequelize 6 on `original` (and `parent`, the same object);\n * `db-errors` (Objection) on `nativeError`. Kysely rethrows the driver error\n * itself; TypeORM's `QueryFailedError` and MikroORM's `DriverException` copy\n * the driver error's own properties onto themselves, so they match directly.\n * Typed structurally, no driver is imported (ADR 0009).\n * @ref https://www.postgresql.org/docs/current/protocol-error-fields.html\n * @ref https://www.postgresql.org/docs/current/errcodes-appendix.html\n * @ref https://github.com/brianc/node-postgres/blob/master/packages/pg-protocol/src/messages.ts\n * @ref https://github.com/electric-sql/pglite/blob/main/packages/pglite/src/errors.ts\n * @ref https://github.com/porsager/postgres/blob/master/src/connection.js\n * @ref https://github.com/porsager/postgres/blob/master/src/errors.js\n * @ref https://github.com/neondatabase/serverless/blob/main/src/httpQuery.ts\n * @ref https://github.com/oven-sh/bun/blob/main/src/js/internal/sql/errors.ts\n * @ref https://github.com/oven-sh/bun/blob/main/src/sql_jsc/postgres/protocol/error_response_jsc.rs\n * @ref https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/errors.ts\n * @ref https://github.com/kysely-org/kysely/blob/master/src/dialect/postgres/postgres-driver.ts\n * @ref https://github.com/kysely-org/kysely/blob/master/src/util/stack-trace-utils.ts\n * @ref https://github.com/sequelize/sequelize/blob/v6/src/errors/database-error.ts\n * @ref https://github.com/Vincit/db-errors/blob/master/lib/errors/DBError.js\n * @ref https://github.com/typeorm/typeorm/blob/master/src/error/QueryFailedError.ts\n * @ref https://github.com/mikro-orm/mikro-orm/blob/master/packages/core/src/exceptions.ts\n * @packageDocumentation\n */\n\n/**\n * The members `findPostgresError` recognizes and `readPostgresFields`\n * reads. A recognized value has a SQLSTATE in `code` or `errno` and a\n * string in `severity` or `severity_local`.\n */\nexport interface PostgresErrorLike {\n /** The SQLSTATE; on Bun, an `ERR_POSTGRES_*` constant instead. */\n readonly code?: string | undefined;\n /** Bun: the SQLSTATE. */\n readonly errno?: string | undefined;\n /** Field `S` (node-postgres, PGlite, Neon, Bun) or field `V` (postgres.js). */\n readonly severity?: string | undefined;\n /** Field `S` on postgres.js. */\n readonly severity_local?: string | undefined;\n readonly constraint?: string | undefined;\n readonly table?: string | undefined;\n readonly schema?: string | undefined;\n readonly column?: string | undefined;\n /** postgres.js spelling of `constraint`. */\n readonly constraint_name?: string | undefined;\n /** postgres.js spelling of `table`. */\n readonly table_name?: string | undefined;\n /** postgres.js spelling of `schema`. */\n readonly schema_name?: string | undefined;\n /** postgres.js spelling of `column`. */\n readonly column_name?: string | undefined;\n}\n\n/** The fields normalized across drivers. */\nexport interface PostgresErrorFields {\n readonly sqlstate: string;\n /** The first two characters of the SQLSTATE. */\n readonly errorClass: string;\n readonly constraint: string | undefined;\n readonly table: string | undefined;\n readonly schema: string | undefined;\n readonly column: string | undefined;\n}\n\n/**\n * SQLSTATE grammar: five digits or uppercase letters. Node's system errors\n * carry a `code` in the same grammar (`EPIPE`) and Prisma's client errors\n * too (`P2002`), which is why a severity string is required as well: the\n * protocol always sends one and every driver keeps it.\n */\nconst SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/u;\nconst CLASS_LENGTH = 2;\n/** Links `findPostgresError` follows, in order, at each level. */\nconst LINKS: ReadonlyArray<string> = ['cause', 'original', 'nativeError'];\n/** How many links `findPostgresError` follows. */\nconst MAX_DEPTH = 8;\n\n/**\n * Any non-null object. Driver errors are `Error` subclasses, so\n * `isPlainRecord` from `internal/json.ts` would reject them.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction isSqlstate(value: unknown): value is string {\n return typeof value === 'string' && SQLSTATE_PATTERN.test(value);\n}\n\nfunction sqlstateOf(code: unknown, errno: unknown): string | undefined {\n if (isSqlstate(code)) {\n return code;\n }\n return isSqlstate(errno) ? errno : undefined;\n}\n\n/** True when `value` carries a SQLSTATE and a severity string. */\nexport function isPostgresError(value: unknown): value is PostgresErrorLike {\n return (\n isRecord(value) &&\n sqlstateOf(value['code'], value['errno']) !== undefined &&\n (typeof value['severity'] === 'string' ||\n typeof value['severity_local'] === 'string')\n );\n}\n\nfunction nextLink(record: Record<string, unknown>): unknown {\n for (const name of LINKS) {\n const linked = record[name];\n if (isRecord(linked)) {\n return linked;\n }\n }\n return undefined;\n}\n\n/**\n * The value itself or the first Postgres error reached through `cause`,\n * `original`, or `nativeError` (the first of those that is an object, at\n * each level), at most eight links deep.\n */\nexport function findPostgresError(\n value: unknown,\n): PostgresErrorLike | undefined {\n let current: unknown = value;\n for (let depth = 0; depth <= MAX_DEPTH; depth += 1) {\n if (isPostgresError(current)) {\n return current;\n }\n if (!isRecord(current)) {\n return undefined;\n }\n current = nextLink(current);\n }\n return undefined;\n}\n\n/**\n * Normalizes the driver spellings. The SQLSTATE comes from `code`, else\n * `errno`; the object fields from the node-postgres names, else the\n * postgres.js names.\n */\nexport function readPostgresFields(\n error: PostgresErrorLike,\n): PostgresErrorFields {\n // `isPostgresError` guarantees one of the two; the empty string keeps the\n // return type honest for a structurally typed caller that bypassed it.\n const sqlstate = sqlstateOf(error.code, error.errno) ?? '';\n return {\n sqlstate,\n errorClass: sqlstate.slice(0, CLASS_LENGTH),\n constraint:\n readString(error.constraint) ?? readString(error.constraint_name),\n table: readString(error.table) ?? readString(error.table_name),\n schema: readString(error.schema) ?? readString(error.schema_name),\n column: readString(error.column) ?? readString(error.column_name),\n };\n}\n","/**\n * `postgresMapper()` option types and their construction-time checks (SPEC\n * 6.10, \"Construction\"): every `TypeError` and `RangeError` the mapper\n * throws before it sees a first error is raised here.\n * @packageDocumentation\n */\nimport type { BanError } from '../core/ban-error';\nimport type { BuiltinKey } from '../core/catalog';\nimport type { ErrorDefinition } from '../core/definition';\nimport type { Ban, Catalog } from '../core/types';\nimport type { IssueLocation } from '../validation/issue';\n\nimport { assertHeadersInit } from '../handler/headers';\n\n/** What a SQLSTATE, a class, a constraint, or a column maps to. Unset members keep the default. */\nexport interface PostgresMapping<TKeys extends string = never> {\n readonly key?: BuiltinKey | TKeys | undefined;\n /** Constant client-facing text; never derived from the driver error. */\n readonly detail?: string | undefined;\n readonly meta?: Readonly<Record<string, unknown>> | undefined;\n readonly headers?: HeadersInit | undefined;\n readonly issue?: never;\n}\n\nexport interface PostgresIssue {\n readonly path: ReadonlyArray<string | number>;\n readonly message: string;\n /** Default `'body'`. */\n readonly location?: IssueLocation | undefined;\n readonly code?: string | undefined;\n}\n\n/**\n * Maps a constraint or column to `ban.validation()` with one issue, so a\n * violation reaches the client the way a validator failure does.\n */\nexport interface PostgresIssueMapping {\n readonly issue: PostgresIssue;\n}\n\n/** A string is the `detail`. */\nexport type PostgresConstraintMapping<TKeys extends string = never> =\n | string\n | PostgresMapping<TKeys>\n | PostgresIssueMapping;\n\nexport interface PostgresMapOptions<TKeys extends string = never> {\n /** By constraint name (field `n`; an index counts as a constraint). Wins over everything below. */\n readonly constraints?:\n | Readonly<Record<string, PostgresConstraintMapping<TKeys>>>\n | undefined;\n /** By `table.column` or bare `column` (fields `t` and `c`), for errors that name no constraint. */\n readonly columns?:\n | Readonly<Record<string, PostgresConstraintMapping<TKeys>>>\n | undefined;\n /** By five-character SQLSTATE or two-character class; a code wins over its class. */\n readonly codes?: Readonly<Record<string, PostgresMapping<TKeys>>> | undefined;\n /** Seconds for `Retry-After` on every 503 the mapper produces. Default: no header. */\n readonly retryAfter?: number | undefined;\n}\n\n/** A catalog that defines every custom key a mapper names. */\nexport type CatalogWith<TKeys extends string> = Catalog &\n Readonly<Record<Exclude<TKeys, BuiltinKey>, ErrorDefinition>>;\n\n/**\n * Accepted as `map` by every ban whose catalog defines the custom keys the\n * options name; a mapper naming only built-in keys fits every ban.\n */\nexport type PostgresMapper<TKeys extends string = never> = <\n TErrors extends CatalogWith<TKeys>,\n>(\n thrown: unknown,\n ban: Ban<TErrors>,\n) => BanError | undefined;\n\n/** A SQLSTATE or a class. */\nconst CODE_KEY_PATTERN = /^(?:[0-9A-Z]{5}|[0-9A-Z]{2})$/u;\n\nexport function isIssueMapping(\n mapping: object,\n): mapping is PostgresIssueMapping {\n return 'issue' in mapping && mapping.issue !== undefined;\n}\n\nfunction assertMapping(group: string, name: string, mapping: unknown): void {\n if (typeof mapping === 'string') {\n return;\n }\n if (typeof mapping !== 'object' || mapping === null) {\n throw new TypeError(`${group} \"${name}\" must be a string or an object`);\n }\n if (isIssueMapping(mapping)) {\n const { issue } = mapping;\n if (!Array.isArray(issue.path) || typeof issue.message !== 'string') {\n throw new TypeError(\n `${group} \"${name}\" issue needs a path array and a message string`,\n );\n }\n return;\n }\n if ('headers' in mapping) {\n try {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- reason: the Headers constructor is the validator; a value of the wrong shape is exactly what this check reports\n assertHeadersInit(mapping.headers as HeadersInit | undefined);\n } catch (cause) {\n throw new TypeError(\n `${group} \"${name}\" headers holds an invalid header name or value`,\n { cause },\n );\n }\n }\n}\n\nexport function assertOptions<TKeys extends string>(\n options: PostgresMapOptions<TKeys>,\n): void {\n for (const [name, mapping] of Object.entries(options.constraints ?? {})) {\n assertMapping('constraints', name, mapping);\n }\n for (const [name, mapping] of Object.entries(options.columns ?? {})) {\n assertMapping('columns', name, mapping);\n }\n for (const [code, mapping] of Object.entries(options.codes ?? {})) {\n if (!CODE_KEY_PATTERN.test(code)) {\n throw new TypeError(\n `codes \"${code}\" is not a five-character SQLSTATE or a two-character class`,\n );\n }\n if (typeof mapping === 'string' || isIssueMapping(mapping)) {\n throw new TypeError(`codes \"${code}\" must be a mapping object`);\n }\n assertMapping('codes', code, mapping);\n }\n // `delay-seconds` is 1*DIGIT (RFC 9110 section 10.2.3); `String(1e21)` is\n // `1e+21`, so integers above the safe range are rejected too.\n // @ref https://www.rfc-editor.org/rfc/rfc9110#section-10.2.3\n const { retryAfter } = options;\n if (\n retryAfter !== undefined &&\n (!Number.isSafeInteger(retryAfter) || retryAfter < 0)\n ) {\n throw new RangeError('retryAfter must be a non-negative safe integer');\n }\n}\n","/**\n * Default SQLSTATE mapping (SPEC 6.10, ADR 0015). A code is looked up\n * first, then its two-character class; a code or class with no row maps to\n * nothing, so the handler's own fallthrough applies (a constant 500 with\n * `handled: false`). Every detail here is a constant: nothing from the\n * server's `message`, `detail`, or `hint` is ever forwarded (ADR 0007).\n *\n * The rules behind the rows:\n * - 409 for a change that conflicts with the current state (a duplicate, a\n * missing or still-referenced row); 422 for a value the server understood\n * but cannot accept (missing, out of range, failing a check).\n * - 503 only where the same request is expected to succeed after a delay:\n * serialization failures and deadlocks (Postgres documents retrying the\n * transaction as the remedy), a lock a `NOWAIT` could not take, a\n * read-only replica during failover, and the connection, resource, and\n * operator classes. `Retry-After` is attached only to these.\n * - Excluded from their class default (`UNMAPPED`): the two codes meaning\n * the commit outcome is unknown (a retry could apply a write twice),\n * `temp_file_limit` (per query, fails again), `query_canceled` (a\n * statement timeout, an operator cancel, or the client's own abort; the\n * SQLSTATE cannot tell overload from a query that is always too slow), and\n * the class 22 codes raised by the application's own SQL rather than by a\n * client value (sequence exhaustion, `COPY` input, arithmetic).\n * @ref https://www.postgresql.org/docs/current/errcodes-appendix.html\n * @ref https://www.postgresql.org/docs/current/transaction-iso.html\n * @ref https://www.postgresql.org/docs/current/runtime-config-client.html\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.5.10\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.5.21\n * @ref https://www.rfc-editor.org/rfc/rfc9110#section-15.6.4\n * @ref https://docs.postgrest.org/en/stable/references/errors.html\n * @ref https://github.com/pgbouncer/pgbouncer/blob/master/src/proto.c\n * @packageDocumentation\n */\nimport type { BuiltinKey } from '../core/catalog';\n\nexport interface SqlstateDefault {\n readonly key: BuiltinKey;\n readonly detail: string;\n}\n\nconst UNAVAILABLE_DETAIL = 'Temporarily unavailable, try again later';\nconst CONSTRAINT_DETAIL = 'A value violates a constraint';\n\nconst CONFLICT_DUPLICATE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A record with the same value already exists',\n};\nconst CONFLICT_OVERLAP: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A record overlaps an existing one',\n};\nconst CONFLICT_REFERENCE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A referenced record does not exist or is still in use',\n};\nconst CONFLICT_IN_USE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'A record is still in use',\n};\nconst CONFLICT_CHANGE: SqlstateDefault = {\n key: 'CONFLICT',\n detail: 'The change conflicts with existing records',\n};\nconst MISSING_VALUE: SqlstateDefault = {\n key: 'UNPROCESSABLE_CONTENT',\n detail: 'A required value is missing',\n};\nconst INVALID_VALUE: SqlstateDefault = {\n key: 'UNPROCESSABLE_CONTENT',\n detail: 'A value is invalid or out of range',\n};\nconst UNPROCESSABLE_CONSTRAINT: SqlstateDefault = {\n key: 'UNPROCESSABLE_CONTENT',\n detail: CONSTRAINT_DETAIL,\n};\nconst UNAVAILABLE: SqlstateDefault = {\n key: 'SERVICE_UNAVAILABLE',\n detail: UNAVAILABLE_DETAIL,\n};\n\n/**\n * Per-code rows. Class 23 splits by condition. 23001 (`restrict_violation`)\n * is raised from PostgreSQL 18 for `ON DELETE RESTRICT`; earlier versions\n * raise 23503 for the same action. 40002 is defined by the standard but not\n * raised by PostgreSQL, which reports deferred constraint failures at commit\n * under their class 23 codes; the row serves wire-compatible servers.\n */\nconst BY_CODE: Readonly<Record<string, SqlstateDefault>> = {\n // unique_violation\n '23505': CONFLICT_DUPLICATE,\n // exclusion_violation\n '23P01': CONFLICT_OVERLAP,\n // foreign_key_violation, in both directions\n '23503': CONFLICT_REFERENCE,\n // restrict_violation\n '23001': CONFLICT_IN_USE,\n // not_null_violation\n '23502': MISSING_VALUE,\n // check_violation\n '23514': UNPROCESSABLE_CONSTRAINT,\n // transaction_integrity_constraint_violation\n '40002': CONFLICT_CHANGE,\n // serialization_failure\n '40001': UNAVAILABLE,\n // deadlock_detected\n '40P01': UNAVAILABLE,\n // lock_not_available\n '55P03': UNAVAILABLE,\n // read_only_sql_transaction\n '25006': UNAVAILABLE,\n};\n\n/** Codes excluded from their class row; see the module comment. */\nconst UNMAPPED: ReadonlySet<string> = new Set([\n // transaction_resolution_unknown\n '08007',\n // statement_completion_unknown\n '40003',\n // configuration_limit_exceeded\n '53400',\n // query_canceled\n '57014',\n // sequence_generator_limit_exceeded\n '2200H',\n // bad_copy_file_format\n '22P04',\n // division_by_zero\n '22012',\n]);\n\n/**\n * Per-class rows. Class 08 includes 08P01 (`protocol_violation`), which\n * PostgreSQL raises for a client protocol mistake but which PgBouncer sends\n * as the default SQLSTATE of every pooler error (queue and login timeouts,\n * connection limits, shutdown); the transient reading wins by default and\n * `codes: { '08P01': ... }` selects the other.\n */\nconst BY_CLASS: Readonly<Record<string, SqlstateDefault>> = {\n // data_exception\n '22': INVALID_VALUE,\n // integrity_constraint_violation\n '23': UNPROCESSABLE_CONSTRAINT,\n // connection_exception\n '08': UNAVAILABLE,\n // transaction_rollback\n '40': UNAVAILABLE,\n // insufficient_resources\n '53': UNAVAILABLE,\n // operator_intervention\n '57': UNAVAILABLE,\n};\n\nexport function sqlstateDefault(\n sqlstate: string,\n errorClass: string,\n): SqlstateDefault | undefined {\n if (UNMAPPED.has(sqlstate)) {\n return undefined;\n }\n return BY_CODE[sqlstate] ?? BY_CLASS[errorClass];\n}\n","/**\n * `postgresMapper(options?)` (SPEC 6.10, ADR 0015): an error mapper for\n * `createBan({ map })` or `ban.onError({ map })` that turns a Postgres\n * driver error into a catalog entry by SQLSTATE. The driver's `message`,\n * `detail`, and `hint` never reach `detail`, `meta`, or `headers` (ADR\n * 0007); under `includeStack` a 5xx body carries the driver error's stack\n * as SPEC 6.7 describes. The thrown value stays on `ErrorReport.cause` and\n * the driver error on `ErrorReport.error.cause`.\n * @ref https://www.postgresql.org/docs/current/errcodes-appendix.html\n * @packageDocumentation\n */\nimport type { BanError, BanErrorOptions } from '../core/ban-error';\nimport type { BuiltinKey } from '../core/catalog';\nimport type { Ban, Catalog } from '../core/types';\nimport type { ValidationIssue } from '../validation/issue';\nimport type { PostgresErrorFields } from './error';\nimport type {\n CatalogWith,\n PostgresConstraintMapping,\n PostgresIssueMapping,\n PostgresMapOptions,\n PostgresMapper,\n PostgresMapping,\n} from './options';\nimport type { SqlstateDefault } from './sqlstate';\n\nimport { UNEXPECTED_DETAIL } from '../internal/constants';\nimport { findPostgresError, readPostgresFields } from './error';\nimport { assertOptions, isIssueMapping } from './options';\nimport { sqlstateDefault } from './sqlstate';\n\nexport type { PostgresErrorFields, PostgresErrorLike } from './error';\nexport { findPostgresError, readPostgresFields } from './error';\nexport type {\n CatalogWith,\n PostgresConstraintMapping,\n PostgresIssue,\n PostgresIssueMapping,\n PostgresMapOptions,\n PostgresMapper,\n PostgresMapping,\n} from './options';\n\nconst SERVICE_UNAVAILABLE = 503;\nconst RETRY_AFTER = 'Retry-After';\nconst INTERNAL_KEY: BuiltinKey = 'INTERNAL_SERVER_ERROR';\ntype Lookup<TKeys extends string> = Readonly<\n Record<string, PostgresConstraintMapping<TKeys>>\n>;\n\n/**\n * Own properties only: a constraint named `constructor` must not hit the\n * prototype, and an entry inherited by a `codes` object built on a custom\n * prototype was never validated by `assertOptions`, which reads own\n * properties too.\n */\nfunction lookup<TValue>(\n table: Readonly<Record<string, TValue>>,\n name: string | undefined,\n): TValue | undefined {\n return name !== undefined && Object.hasOwn(table, name)\n ? table[name]\n : undefined;\n}\n\nfunction columnMapping<TKeys extends string>(\n columns: Lookup<TKeys>,\n fields: PostgresErrorFields,\n): PostgresConstraintMapping<TKeys> | undefined {\n if (fields.column === undefined) {\n return undefined;\n }\n const qualified =\n fields.table === undefined\n ? undefined\n : lookup(columns, `${fields.table}.${fields.column}`);\n return qualified ?? lookup(columns, fields.column);\n}\n\n/**\n * The driver error travels as `cause` here as well, so\n * `ErrorReport.error.cause` holds it for every mapping kind (SPEC 6.10).\n */\nfunction toValidation<TErrors extends Catalog>(\n ban: Ban<TErrors>,\n mapping: PostgresIssueMapping,\n cause: unknown,\n): BanError {\n const { issue } = mapping;\n const validationIssue: ValidationIssue = {\n path: issue.path,\n message: issue.message,\n code: issue.code,\n };\n return ban.validation([validationIssue], {\n location: issue.location ?? 'body',\n cause,\n });\n}\n\nfunction build<TKeys extends string, TErrors extends CatalogWith<TKeys>>(\n ban: Ban<TErrors>,\n mappings: ReadonlyArray<PostgresMapping<TKeys>>,\n fallback: SqlstateDefault | undefined,\n retryAfter: number | undefined,\n cause: unknown,\n): BanError | undefined {\n if (mappings.length === 0 && fallback === undefined) {\n return undefined;\n }\n const key: BuiltinKey | TKeys =\n mappings.find((mapping) => mapping.key !== undefined)?.key ??\n fallback?.key ??\n INTERNAL_KEY;\n const detail =\n mappings.find((mapping) => mapping.detail !== undefined)?.detail ??\n fallback?.detail ??\n UNEXPECTED_DETAIL;\n const options: BanErrorOptions = {\n detail,\n meta: mappings.find((mapping) => mapping.meta !== undefined)?.meta,\n headers: mappings.find((mapping) => mapping.headers !== undefined)?.headers,\n cause,\n };\n const error = ban.error(key, options);\n if (\n retryAfter !== undefined &&\n error.status === SERVICE_UNAVAILABLE &&\n !error.headers.has(RETRY_AFTER)\n ) {\n error.headers.set(RETRY_AFTER, String(retryAfter));\n }\n return error;\n}\n\n/**\n * Builds the mapper. For a recognized Postgres error (`findPostgresError`)\n * the entry, detail, meta, and headers come, member by member, from the\n * first source that sets them: the `constraints` entry for its constraint\n * name, the `columns` entry for its `table.column` or `column`, `codes` for\n * its SQLSTATE, `codes` for its class, then the built-in table\n * (`sqlstate.ts`). A `constraints` or `columns` entry with `issue` returns\n * `ban.validation()` instead. A SQLSTATE with no source at any level maps\n * to `undefined`, as does a value that is not a Postgres error, so the\n * handler's own fallthrough applies. Throws `TypeError` at construction for\n * a `codes` key outside the SQLSTATE grammar, a mapping that is neither a\n * string nor an object, an `issue` without a path array and a message\n * string, or `headers` the `Headers` constructor rejects, and `RangeError`\n * for a `retryAfter` that is not a non-negative safe integer. A `key` the\n * ban's catalog lacks fails at map time with the `RangeError` `ban.error`\n * throws.\n */\nexport function postgresMapper<TKeys extends string = never>(\n options: PostgresMapOptions<TKeys> = {},\n): PostgresMapper<TKeys> {\n assertOptions(options);\n const constraints: Lookup<TKeys> = options.constraints ?? {};\n const columns: Lookup<TKeys> = options.columns ?? {};\n const codes: Readonly<Record<string, PostgresMapping<TKeys>>> =\n options.codes ?? {};\n return <TErrors extends CatalogWith<TKeys>>(\n thrown: unknown,\n ban: Ban<TErrors>,\n ): BanError | undefined => {\n const error = findPostgresError(thrown);\n if (error === undefined) {\n return undefined;\n }\n const fields = readPostgresFields(error);\n const named =\n lookup(constraints, fields.constraint) ?? columnMapping(columns, fields);\n if (typeof named === 'object' && isIssueMapping(named)) {\n return toValidation(ban, named, error);\n }\n const mappings: Array<PostgresMapping<TKeys>> = [];\n if (typeof named === 'string') {\n mappings.push({ detail: named });\n } else if (named !== undefined) {\n mappings.push(named);\n }\n const byCode = lookup(codes, fields.sqlstate);\n if (byCode !== undefined) {\n mappings.push(byCode);\n }\n const byClass = lookup(codes, fields.errorClass);\n if (byClass !== undefined) {\n mappings.push(byClass);\n }\n return build(\n ban,\n mappings,\n sqlstateDefault(fields.sqlstate, fields.errorClass),\n options.retryAfter,\n error,\n );\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { d as IssueLocation, f as ValidationIssue, n as BanError } from "./types-WuQ-karh.js";
2
- import { g as ValidationOptions } from "./types-DYr4CtCC.js";
2
+ import { g as ValidationOptions } from "./types-DH_JfdBQ.js";
3
3
  import { s as StandardSchemaIssue } from "./standard-schema-types-Aa3pYPY1.js";
4
- import { n as ValidationSource, t as HookResultBase } from "./hook-TWCLnCmZ.js";
4
+ import { n as ValidationSource, t as HookResultBase } from "./hook-RdzR-1NX.js";
5
5
  //#region src/validation/standard-schema.d.ts
6
6
  /** Matches `@hono/standard-validator` hook results. */
7
7
  export interface StandardHookResult extends HookResultBase {
package/dist/testing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as RenderedError, g as JsonSchema, l as ResolvedDefinition, n as BanError, t as ErrorFormat, v as RenderOptions } from "./types-WuQ-karh.js";
2
- import { a as Catalog, t as Ban } from "./types-DYr4CtCC.js";
2
+ import { a as Catalog, t as Ban } from "./types-DH_JfdBQ.js";
3
3
  //#region src/testing/index.d.ts
4
4
  export declare function renderError<TErrors extends Catalog>(ban: Ban<TErrors>, error: BanError, options?: RenderOptions): RenderedError;
5
5
  export interface ExpectedError {
@@ -270,6 +270,8 @@ interface ValidationOptions {
270
270
  readonly location: IssueLocation;
271
271
  readonly detail?: string | undefined;
272
272
  readonly meta?: Readonly<Record<string, unknown>> | undefined;
273
+ /** The underlying failure, for `ErrorReport.error.cause`; never rendered (ADR 0007). */
274
+ readonly cause?: unknown;
273
275
  }
274
276
  interface ErrorReport {
275
277
  readonly id: string;
@@ -334,4 +336,4 @@ type ReservedKey = keyof BanInstance<Catalog> | FactoryName;
334
336
  type NoReserved<T extends Catalog> = { readonly [K in keyof T]: K extends ReservedKey ? never : T[K]; };
335
337
  //#endregion
336
338
  export { BUILTIN_CATALOG as _, Catalog as a, FACTORY_NAMES as b, EmptyCatalog as c, ErrorReport as d, Factory as f, ValidationOptions as g, ReservedKey as h, BuiltinFactories as i, ErrorKey as l, NoReserved as m, BanInstance as n, CustomErrorInit as o, HandlerOptions as p, BanOptions as r, CustomFactories as s, Ban as t, ErrorMapper as u, BuiltinCatalog as v, FactoryName as x, BuiltinKey as y };
337
- //# sourceMappingURL=types-DYr4CtCC.d.ts.map
339
+ //# sourceMappingURL=types-DH_JfdBQ.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-DYr4CtCC.d.ts","names":[],"sources":["../src/core/catalog.ts","../src/core/types.ts"],"mappings":"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"}
1
+ {"version":3,"file":"types-DH_JfdBQ.d.ts","names":[],"sources":["../src/core/catalog.ts","../src/core/types.ts"],"mappings":"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"}
package/dist/valibot.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { d as IssueLocation, f as ValidationIssue, n as BanError } from "./types-WuQ-karh.js";
2
- import { g as ValidationOptions } from "./types-DYr4CtCC.js";
3
- import { n as ValidationSource, t as HookResultBase } from "./hook-TWCLnCmZ.js";
2
+ import { g as ValidationOptions } from "./types-DH_JfdBQ.js";
3
+ import { n as ValidationSource, t as HookResultBase } from "./hook-RdzR-1NX.js";
4
4
  //#region src/validation/valibot.d.ts
5
5
  /** Every Valibot `IssuePathItem` carries a `key`; map and set keys can be anything. */
6
6
  export interface ValibotPathItemLike {
package/dist/zod.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { d as IssueLocation, f as ValidationIssue, n as BanError } from "./types-WuQ-karh.js";
2
- import { g as ValidationOptions } from "./types-DYr4CtCC.js";
3
- import { n as ValidationSource, t as HookResultBase } from "./hook-TWCLnCmZ.js";
2
+ import { g as ValidationOptions } from "./types-DH_JfdBQ.js";
3
+ import { n as ValidationSource, t as HookResultBase } from "./hook-RdzR-1NX.js";
4
4
  //#region src/validation/zod.d.ts
5
5
  /** The subset of a Zod 3 `ZodIssue` or Zod 4 `$ZodIssue` this module reads. */
6
6
  export interface ZodIssueLike {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono-ban",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Structured, extensible HTTP errors for Hono. One API, any wire format: RFC 9457 Problem Details, JSON:API, Google API (AIP-193), Stripe, or your own.",
5
5
  "keywords": [
6
6
  "aip-193",
@@ -1 +0,0 @@
1
- {"version":3,"file":"hook-TWCLnCmZ.d.ts","names":[],"sources":["../src/validation/hook.ts"],"mappings":";;;;UAOiB;WACN,aACP,QAAQ,cAAc,kBACtB,SAAS,sBACN;;;UAIU;WACN;WACA,QAAQ"}