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 +15 -0
- package/README.md +12 -0
- package/dist/{hook-TWCLnCmZ.d.ts → hook-RdzR-1NX.d.ts} +2 -2
- package/dist/hook-RdzR-1NX.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/postgresql.d.ts +49 -46
- package/dist/postgresql.d.ts.map +1 -1
- package/dist/postgresql.js +53 -38
- package/dist/postgresql.js.map +1 -1
- package/dist/standard-schema.d.ts +2 -2
- package/dist/testing.d.ts +1 -1
- package/dist/{types-DYr4CtCC.d.ts → types-DH_JfdBQ.d.ts} +3 -1
- package/dist/{types-DYr4CtCC.d.ts.map → types-DH_JfdBQ.d.ts.map} +1 -1
- package/dist/valibot.d.ts +2 -2
- package/dist/zod.d.ts +2 -2
- package/package.json +1 -1
- package/dist/hook-TWCLnCmZ.d.ts.map +0 -1
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-
|
|
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-
|
|
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-
|
|
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
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":";;;;;;;;;;;;;;;;;;AAWA,SAAgB,OACd,OACA,OACiD;CACjD,IAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,OACrD,MAAM,MAAM;AAEhB;;;ACXA,MAAM,0BAA0B;AAChC,MAAM,UAAU,IAAI,YAAY;;;;;;;AAQhC,SAAgB,QACd,MACA,OACA,SACA,QACA,KACA,WACQ;CACR,MAAM,OAAO,cAAc,IAAI;CAE/B,IACE,KAAK,SAAS,2BAA2B,OACzC,QAAQ,OAAO,IAAI,CAAC,CAAC,cAAc,KAEnC,OAAO;CAET,MAAM,OAAO,MAAM,OAAO;CAS1B,MAAM,WAAW,OAAO,IARJ,SAAS;EAC3B,GAAG;EACH,MAAM,CAAC;EACP,QAAQ,KAAA;EACR,QAAQ,KAAK,QAAQ,MAAM,GAAG,uBAAuB;CACvD,CAG8B,GAAG;EAC/B,GAAG;EACH,cAAc;EACd,WAAW;CACb,CAAC;CACD,OAAO,cAAc,UAAU,SAAS,IAAI,CAAC;AAC/C;;;AChCA,SAAgB,YAAY,OAAiC;CAC3D,MAAM,EAAE,UAAU;CAClB,OAAO;EACL,IAAI,MAAM;EACV,QAAQ,MAAM;EACd,MAAM,MAAM;EACZ;EACA,OAAO,MAAM,WAAW,QAAQ,MAAM,QAAQ,MAAM;EACpD,SAAS,MAAM;EACf,gBAAgB,MAAM;EACtB,SAAS;GACP,WAAW,MAAM;GACjB,SAAS,MAAM;GACf,QAAQ,MAAM;GACd,QAAQ,MAAM;GACd,MAAM,MAAM;EACd;CACF;AACF;;;;;;;AC3BA,SAAgB,cAAc,OAA+C;CAC3E,OAAO,UAAU,KAAA,KAAa,mBAAmB,KAAK,KAAK,IACvD,QACA,KAAA;AACN;;;;;;;;ACOA,SAAgB,cACd,QACA,KACA,MACA,KACU;CACV,IAAI,WAAW,MAAM,KAAK,kBAAkB,eAC1C,OAAO;EAAE,OAAO,KAAK,MAAM;EAAG,SAAS;CAAK;CAE9C,IAAI,QAAQ,KAAA,GAAW;EACrB,MAAM,SAAkB,IAAI,QAAQ,GAAG;EACvC,IAAI,WAAW,MAAM,GACnB,OAAO;GAAE,OAAO;GAAQ,SAAS;EAAK;EAExC,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,UACR,8EACF;CAEJ;CACA,OAAO;EAAE,OAAO,KAAK,MAAM;EAAG,SAAS;CAAM;AAC/C;;;AC9BA,MAAM,WAAW;AACjB,MAAM,MAAM;AACZ,MAAM,eAAe;;;;;;;;;AAUrB,SAAgB,iBACd,OAC0B;CAC1B,IAAI,UAAU,KAAA,GACZ;CAEF,MAAM,QAAQ,oBAAoB,KAAK,KAAK;CAC5C,MAAM,UAAU,QAAQ;CACxB,MAAM,UAAU,QAAQ;CACxB,MAAM,WAAW,QAAQ;CACzB,MAAM,QAAQ,QAAQ;CACtB,IACE,YAAY,KAAA,KACZ,YAAY,KAAA,KACZ,aAAa,KAAA,KACb,UAAU,KAAA,GAEV;CAEF,IAAI,YAAY,MACd;CAEF,IAAI,YAAY,MACV;MAAA,MAAM,WAAA,IACR;CAAA,OAEG,IACL,MAAM,SAAA,MACN,MAAA,QAA8B,KAE9B;CAEF,IAAI,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,QAAQ,GAClD;CAEF,OAAO;EACL;EACA;EACA,UAAU,OAAO,SAAS,OAAO,GAAG,IAAI,kBAAkB;CAC5D;AACF;;;AC3BA,MAAMA,iBAAe;AAOrB,SAAS,UACP,GACA,UACO;CACP,IAAI,aAAa,KAAA,GACf,OAAO;EAAE,SAAS,SAAS,CAAC;EAAG,QAAQ,KAAA;CAAU;CAEnD,MAAM,SAAS,iBAAiB,EAAE,IAAI,OAAO,aAAa,CAAC;CAC3D,OAAO;EAAE,SAAS,QAAQ;EAAS,QAAQ,QAAQ;CAAS;AAC9D;;;;;AAMA,eAAe,OACb,UACA,OACA,GACe;CACf,IAAI,aAAa,KAAA,GACf;CAEF,IAAI;EACF,MAAM,SAAS,OAAO,CAAC;CACzB,QAAQ,CAER;AACF;;;;;;AAOA,eAAe,SACb,SACA,QACA,GACA,MACA,SACmB;CACnB,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,QAAQ,KAAK,MAAM,KAAK,WAAW,uBAAuB,GAAG;GAC3D,QAAQ;GACR,OAAO;EACT,CAAC;EACD,MAAM,WAAW,KAAK,OAAO,KAAK;EAClC,WAAW,IAAI,SAAS,cAAc,SAAS,IAAI,GAAG;GACpD,QAAQA;GACR,SAAS,aACP,QAAQ,SACR,SAAS,SACT,QAAQ,iBAAA,cACR,MAAM,EACR;EACF,CAAC;CACH,QAAQ;EACN,QAAQ,IAAIC,SAAc;GACxB,QAAQD;GACR,MAAM;GACN,OAAO;GACP,QAAQ;GACR,OAAO;EACT,CAAC;EAED,WAAW,IAAI,SACb,cAAc;GACZ,QAAQA;GACR,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,IAAI,MAAM;EACZ,CAAC,GACD;GACE,QAAQA;GACR,SAAS,kBACP,QAAQ,SACR,QAAQ,iBAAA,cACR,MAAM,EACR;EACF,CACF;CACF;CACA,MAAM,OACJ,QAAQ,UACR,YAAY;EACV;EACA;EACA,SAAS;EACT,gBAAgB;EAChB,WAAW,KAAA;EACX,SAAS,KAAA;EACT,QAAQ,KAAA;EACR,QAAQ,EAAE,IAAI;EACd,MAAM,EAAE,IAAI;CACd,CAAC,GACD,CACF;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,cACd,MACA,KACA,MACA,aAGmB;CACnB,QACE,UAAsC,CAAC,MACnB;EACpB,MAAM,MAAM,QAAQ,OAAO;EAC3B,MAAM,eAAe,QAAQ,gBAAgB;EAC7C,MAAM,kBACJ,QAAQ,mBAAA;EACV,MAAM,gBAAgB,QAAQ,iBAAA;EAC9B,MAAM,eAAe,QAAQ,gBAAA;EAC7B,iBAAiB,mBAAmB,eAAe;EACnD,iBAAiB,iBAAiB,aAAa;EAC/C,kBAAkB,QAAQ,OAAO;EAEjC,OAAO,OAAO,QAAiB,MAAqC;GAClE,IAAI;IACF,MAAM,YACJ,QAAQ,cAAc,KAAA,IAClB,cACE,oBAAoB,QAChB,KAAA,IACA,EAAE,IAAI,OAAO,eAAe,CAClC,IACA,QAAQ,UAAU,CAAC;IACzB,MAAM,QAAQ,UAAU,GAAG,QAAQ,OAAO;IAC1C,MAAM,EAAE,OAAO,YAAY,cAAc,QAAQ,KAAK,MAAM,GAAG;IAC/D,MAAM,gBAA+B;KACnC;KACA,SAAS,MAAM;KACf,UAAU,EAAE,IAAI;KAChB,QAAQ,EAAE,IAAI;KACd;IACF;IACA,MAAM,WAAW,KAAK,OAAO,OAAO,aAAa;IACjD,MAAM,aAAa,SACjB,QAAQ,cAAc,KAAA,IAClB,OACA,QAAQ,UAAU,MAAM,OAAO,CAAC;IACtC,MAAM,OAAO,QACX,UAAU,SAAS,IAAI,GACvB,OACA,eACA,KAAK,QACL,cACA,SACF;IACA,MAAM,UAAU,aACd,QAAQ,SACR,SAAS,SACT,eACA,MAAM,EACR;IACA,MAAM,OACJ,QAAQ,UACR,YAAY;KACV;KACA;KACA;KACA,gBAAgB,KAAA;KAChB;KACA,SAAS,MAAM;KACf,QAAQ,MAAM;KACd,QAAQ,EAAE,IAAI;KACd,MAAM,EAAE,IAAI;IACd,CAAC,GACD,CACF;IACA,OAAO,IAAI,SAAS,MAAM;KAAE,QAAQ,MAAM;KAAQ;IAAQ,CAAC;GAC7D,SAAS,SAAS;IAChB,OAAO,SAAS,SAAS,QAAQ,GAAG,MAAM,OAAO;GACnD;EACF;CACF;AACF;;;;;;;AC3NA,SAAgB,iBACd,MAIY;CACZ,QAAQ,QAAQ,YACd,KAAK,MACH,KAAK,WAAW,KAAK,aAAa,GAClC;EACE,QAAQ,QAAQ;EAChB,MAAM;GAAE,GAAG,QAAQ;GAAM,UAAU,QAAQ;EAAS;CACtD,GACA,MACF;AACJ;;;ACpBA,SAAS,UAAU,OAA0C;CAC3D,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;;;;;AAMA,SAAgB,iBACd,OACA,QACiB;CACjB,IAAI,UAAU,KAAA,GACZ,OAAO,UAAU,CAAC;CAEpB,IAAI,OAAO,UAAU,UACnB,OAAO;EAAE,GAAG;EAAQ,QAAQ;CAAM;CAEpC,IAAI,UAAU,KAAK,GACjB,OAAO;CAET,MAAM,IAAI,UACR,2DAA2D,OAAO,OACpE;AACF;AAEA,SAAgB,cAAc,MAAe,KAAsB;CACjE,MAAM,aAAa,KAAK,WAAW,GAAG;CACtC,QACE,OACA,WACa,KAAK,MAAM,YAAY,iBAAiB,OAAO,MAAM,CAAC;AACvE;;;ACxBA,MAAM,cAAc;AACpB,MAAM,wBAAwB;AAC9B,MAAM,gBAAgB;;;;;;;;;;;AAYtB,SAAS,mBACP,QACA,QACqB;CACrB,IAAI,WAAW,KAAA,GACb;CAEF,MAAM,UAAU,IAAI,QAAQ,MAAM;CAClC,MAAM,mBAAmB,WAAW;CACpC,KAAK,MAAM,QAAQ,cACjB,IAAI,EAAE,oBAAoB,SAAS,gBACjC,QAAQ,OAAO,IAAI;CAGvB,OAAO;AACT;;;;;;;;AASA,SAAS,kBAAkB,MAAe,OAAgC;CACxE,MAAM,SAAS,MAAM;CACrB,MAAM,aACJ,WAAW,eAAe,MAAM,YAAA,mCAC5B,KAAK,WAAW,gBAAgB,IAChC,KAAK,SAAS,IAAI,MAAM;CAC9B,MAAM,SAAS,MAAM,YAAY,KAAK,KAAA,IAAY,MAAM;CACxD,MAAM,UAAU,mBAAmB,MAAM,KAAK,SAAS,MAAM;CAC7D,IAAI,eAAe,KAAA,GACjB,OAAO,KAAK,OAAO;EACjB;EACA,OAAO;EACP;EACA;EACA,OAAO;CACT,CAAC;CAEH,OAAO,KAAK,MAAM,YAAY;EAAE;EAAQ;EAAS,OAAO;CAAM,CAAC;AACjE;AAEA,SAAgB,WAAW,MAA6C;CACtE,QAAQ,UAA6B;EACnC,IAAI,WAAW,KAAK,GAClB,OAAO;EAET,IAAI,iBAAiB,eACnB,OAAO,kBAAkB,MAAM,KAAK;EAEtC,OAAO,KAAK,MAAM,KAAK,WAAW,uBAAuB,GAAG;GAC1D,QAAQ;GACR,OAAO;EACT,CAAC;CACH;AACF;;;;;;;AC7EA,SAAgB,aACd,MACyB;CACzB,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAChC,IAAI,CAAC,WAAW,IAAI,GAAG,GACrB,IAAI,OAAO,KAAK;CAGpB,OAAO;AACT;;;ACVA,MAAM,eAAe;;;;;AAMrB,SAAgB,eACd,QACA,aACU;CACV,QAAQ,OAAiB,UAAyB,CAAC,MAAqB;EACtE,MAAM,eAAe,QAAQ,gBAAgB;EAC7C,MAAM,SAAS,MAAM,iBAAiB,QAAQ,MAAM,QAAQ;EAC5D,MAAM,MAAqB;GACzB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,UAAU,MAAM,YAAY,QAAQ;GACpC,QAAQ,QAAQ;GAChB;GACA,aAAa,QAAQ,eAAe;GACpC,MAAM,aAAa,MAAM,IAAI;GAC7B,OACE,gBAAgB,MAAM,UAAU,eAAe,OAAO,QAAQ,KAAA;GAChE,WAAW,QAAQ,aAAa;EAClC;EACA,MAAM,OACJ,MAAM,WAAW,KAAA,IACb,OAAO,OAAO,OAAO,GAAG,IACxB,OAAO,iBAAiB,OAAO,MAAM,QAAQ,GAAG;EACtD,MAAM,UAAU,IAAI,QAAQ,MAAM,OAAO;EACzC,QAAQ,IAAI,gBAAgB,OAAO,WAAW;EAC9C,OAAO;GAAE,QAAQ,MAAM;GAAQ;GAAS;EAAK;CAC/C;AACF;;;ACDA,MAAM,gCAAqC,IAAI,IAAY,CACzD,GAAG;CAbH;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAIe,GACf,GAAG,OAAO,KAAK,aAAa,CAC9B,CAAC;AAED,SAAS,aAAa,QAGpB;CACA,MAAM,UAA8C,CAAC;CACrD,KAAK,MAAM,CAAC,KAAK,eAAe,OAAO,QAAQ,eAAe,GAC5D,QAAQ,OAAO,kBAAkB,KAAK,UAAU;CAElD,KAAK,MAAM,CAAC,KAAK,eAAe,OAAO,QAAQ,MAAM,GACnD,QAAQ,OAAO,kBAAkB,KAAK,UAAU;CAElD,OAAO;EAAE;EAAS,UAAU,cAAc,OAAO;CAAE;AACrD;;;;;AAMA,SAAgB,UACd,UAEI,CAAC,GACS;CACd,MAAM,SAAU,QAAQ,UAAU,CAAC;CACnC,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,IAAI,cAAc,IAAI,GAAG,GACvB,MAAM,IAAI,UACR,gBAAgB,IAAI,2CACtB;CAGJ,MAAM,SAAsB,QAAQ,UAAU,eAAe;CAC7D,MAAM,cAAc,QAAQ;CAC5B,MAAM,aAAa,QAAQ,MAAM;CACjC,MAAM,EAAE,SAAS,aAAa,aAAa,MAAM;CACjD,MAAM,SAAS,eAAe,QAAQ,WAAW;CAEjD,MAAM,OAAgB;EACpB;EACA;EACA;EACA;EACA,eAAe,QAAQ,iBAAiB;EACxC;EACA;EACA,WAAW,KAAiC;GAC1C,MAAM,aAAa,QAAQ;GAC3B,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,WAAW,sBAAsB,IAAI,EAAE;GAEnD,OAAO;EACT;EACA,MAAM,YAAY,cAA+B,QAAkB;GACjE,OAAO,IAAIE,SAAc;IACvB,GAAG;IACH,QAAQ,WAAW;IACnB,MAAM,WAAW;IACjB,OAAO,WAAW;IAClB,MAAM,aAAa,QAAQ,WAAW;IACtC,IAAI,aAAa,MAAM,WAAW;IAClC;IACA;IACA;GACF,CAAC;EACH;EACA,OAAO,MAAiC;GACtC,OAAO,IAAIA,SAAc;IACvB,GAAG;IACH,MAAM,KAAK,QAAQ;IACnB,OAAO,KAAK,SAAS,aAAa,KAAK,MAAM,KAAK;IAClD,IAAI,KAAK,MAAM,WAAW;IAC1B;GACF,CAAC;EACH;CACF;CACA,IAAI,CAAC,OAAO,OAAO,SAAS,KAAK,aAAa,GAC5C,MAAM,IAAI,WACR,kBAAkB,KAAK,cAAc,wBACvC;CAGF,MAAM,OAAO,WAAW,IAAI;CAC5B,MAAM,WAAoC;EACxC;EACA;EACA;EACA,eAAe,KAAK;EACpB,QAAQ,KAAa,eAAgC,CAAC,MACpD,KAAK,MAAM,KAAK,WAAW,GAAG,GAAG,YAAY;EAC/C,SAAS,SAAoC,KAAK,OAAO,IAAI;EAC7D,YAAY,iBAAiB,IAAI;EACjC;EACA;CACF;CAEA,MAAM,MAAM;CACZ,SAAS,aAAa,cAAc,MAAM,KAAK,MAAM,QAAQ,GAAG;CAChE,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,aAAa,GACpD,SAAS,QAAQ,cAAc,MAAM,GAAG;CAE1C,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAClC,SAAS,OAAO,cAAc,MAAM,GAAG;CAEzC,OAAO;AACT;;;ACrGA,SAAS,eACP,MACgD;CAChD,OAAO,OAAO,KAAK,WAAW;AAChC;AAQA,SAAgB,aACd,MACa;CACb,IAAI,eAAe,IAAI,GAAG;EACxB,MAAM,cACJ,aACA,QAEA,KAAK,OAAO,YAAY,CAAC,WAAW,OAAO,EAAE,QAAQ,IAAI,QAAQ,CAAC;EACpE,MAAM,mBAAmB,KAAK,kBAAkB,KAAK,IAAI;EACzD,OAAO;GACL,MAAM,KAAK;GACX,aAAa,KAAK;GAClB,SAAS,OAAO,QAAQ,KAAK,OAAO,OAAO,GAAG;GAC9C,kBACE,sBAAsB,OAAO,SAAS,QAAQ,KAAK,OAAO,OAAO,GAAG;GACtE,QAAQ;GACR,kBAAkB;EACpB;CACF;CACA,MAAM,mBAAmB,KAAK,kBAAkB,KAAK,IAAI;CACzD,MAAM,mBAAmB,KAAK,kBAAkB,KAAK,IAAI;CACzD,OAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,SAAS,OAAO,QAAQ,KAAK,OAAO,OAAO,GAAG;EAC9C,kBACE,sBAAsB,OAAO,SAAS,QAAQ,KAAK,OAAO,OAAO,GAAG;EACtE,SAAS,YAAY,QAAQ,KAAK,OAAO,YAAY,GAAG;EACxD,kBACE,sBAAsB,YAAY,QAAQ,KAAK,OAAO,YAAY,GAAG;CACzE;AACF;;;AC/CA,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AAGxB,MAAM,0BAA0B;AAChC,MAAM,sBAAsB;AAE5B,SAAS,OAAO,MAAc,OAAe,SAAwB;CACnE,MAAM,IAAI,UACR,oBAAoB,KAAK,WAAW,QAAQ,aAAa,KAAK,UAAU,KAAK,GAC/E;AACF;AAEA,SAAS,WAAW,OAAuB;CACzC,IAAI,CAAC,wBAAwB,KAAK,KAAK,GACrC,OAAO,SAAS,OAAO,0CAA0C;CAEnE,OAAO,UAAU,MAAM,WAAW,qBAAqB,MAAM,EAAE;AACjE;AAEA,SAAS,WACP,OACoB;CACpB,IAAI,UAAU,KAAA,GACZ;CAEF,MAAM,SAAS,OAAO,UAAU,WAAW,MAAM,MAAM,GAAG,IAAI;CAC9D,IAAI,OAAO,WAAW,GACpB;CAEF,KAAK,MAAM,SAAS,QAClB,IAAI,CAAC,eAAe,KAAK,KAAK,GAC5B,OACE,SACA,OACA,sFACF;CAGJ,OAAO,UAAU,OAAO,KAAK,GAAG,EAAE;AACpC;AAEA,SAAS,MAAM,MAAc,OAAe,SAAyB;CACnE,IAAI,CAAC,QAAQ,KAAK,KAAK,GACrB,OACE,MACA,OACA,YAAY,iBACR,gFACA,sEACN;CAEF,OAAO,GAAG,KAAK,IAAI,MAAM;AAC3B;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,gBAAgB,UAAkC,CAAC,GAAW;CAC5E,MAAM,SAAwB,CAAC;CAC/B,IAAI,QAAQ,UAAU,KAAA,GACpB,OAAO,KAAK,WAAW,QAAQ,KAAK,CAAC;CAEvC,MAAM,QAAQ,WAAW,QAAQ,KAAK;CACtC,IAAI,UAAU,KAAA,GACZ,OAAO,KAAK,KAAK;CAEnB,MAAM,aAEF;EACF;GAAC;GAAS,QAAQ;GAAO;EAAe;EACxC;GAAC;GAAqB,QAAQ;GAAkB;EAAe;EAC/D;GAAC;GAAa,QAAQ;GAAU;EAAc;EAC9C;GAAC;GAAqB,QAAQ;GAAkB;EAAc;CAChE;CACA,KAAK,MAAM,CAAC,MAAM,OAAO,YAAY,YACnC,IAAI,UAAU,KAAA,GACZ,OAAO,KAAK,MAAM,MAAM,OAAO,OAAO,CAAC;CAG3C,OAAO,UAAU,OAAO,WAAW,IAAI,eAAa,OAAO,KAAK,IAAI;AACtE"}
|
|
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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"}
|
package/dist/postgresql.d.ts
CHANGED
|
@@ -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-
|
|
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
|
|
152
|
-
* lacks fails at map time with the `RangeError` `ban.error`
|
|
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
|
package/dist/postgresql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgresql.d.ts","names":[],"sources":["../src/postgresql/error.ts","../src/postgresql/index.ts"],"mappings":"
|
|
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"}
|
package/dist/postgresql.js
CHANGED
|
@@ -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
|
-
/**
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
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], {
|
|
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
|
|
244
|
-
* lacks fails at map time with the `RangeError` `ban.error`
|
|
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
|
|
275
|
+
const byCode = lookup(codes, fields.sqlstate);
|
|
261
276
|
if (byCode !== void 0) mappings.push(byCode);
|
|
262
|
-
const byClass = codes
|
|
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
|
};
|
package/dist/postgresql.js.map
CHANGED
|
@@ -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-
|
|
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-
|
|
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-
|
|
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-
|
|
339
|
+
//# sourceMappingURL=types-DH_JfdBQ.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-
|
|
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-
|
|
3
|
-
import { n as ValidationSource, t as HookResultBase } from "./hook-
|
|
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-
|
|
3
|
-
import { n as ValidationSource, t as HookResultBase } from "./hook-
|
|
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 +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"}
|