@nextrush/errors 4.0.0-beta.0 → 4.0.0-beta.2
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/dist/index.d.ts +36 -1
- package/dist/index.js +47 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/header-validation.test.ts +43 -0
- package/src/__tests__/middleware.test.ts +82 -0
- package/src/__tests__/public-surface.test.ts +3 -0
- package/src/__tests__/security-audit-contribution.test.ts +40 -0
- package/src/header-validation.ts +24 -0
- package/src/index.ts +3 -0
- package/src/middleware.ts +65 -3
package/dist/index.d.ts
CHANGED
|
@@ -104,6 +104,27 @@ declare class HttpError extends NextRushError {
|
|
|
104
104
|
static fromJSON(json: Record<string, unknown>): HttpError;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* @nextrush/errors - Header Validation Error
|
|
109
|
+
*
|
|
110
|
+
* @packageDocumentation
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Thrown when a header field name or value fails RFC 9110 grammar
|
|
115
|
+
* validation (field-name token grammar, field-value grammar — no control
|
|
116
|
+
* characters, no leading/trailing whitespace, no obs-fold).
|
|
117
|
+
*
|
|
118
|
+
* @remarks
|
|
119
|
+
* A rejected write here means the application (or a framework internal) is
|
|
120
|
+
* constructing an invalid header, not that a client sent bad input — so this
|
|
121
|
+
* is a 500-class programming error, not a validation-issue-list shape like
|
|
122
|
+
* {@link ValidationError}.
|
|
123
|
+
*/
|
|
124
|
+
declare class HeaderValidationError extends NextRushError {
|
|
125
|
+
constructor(message: string);
|
|
126
|
+
}
|
|
127
|
+
|
|
107
128
|
/**
|
|
108
129
|
* @nextrush/errors - Central Error Code Registry
|
|
109
130
|
*
|
|
@@ -597,6 +618,20 @@ declare function getSafeErrorMessage(error: unknown): string;
|
|
|
597
618
|
interface ErrorHandlerOptions {
|
|
598
619
|
/** Include stack trace in development */
|
|
599
620
|
includeStack?: boolean;
|
|
621
|
+
/**
|
|
622
|
+
* Whether the application is running in production (SEC-14).
|
|
623
|
+
*
|
|
624
|
+
* @remarks
|
|
625
|
+
* `@nextrush/errors` has no access to `app.isProduction` on its own — the
|
|
626
|
+
* caller threads it through explicitly, mirroring `@nextrush/core`'s
|
|
627
|
+
* `writeDefaultErrorResponse(opts.isProduction)`. When `true`, a truthy
|
|
628
|
+
* `includeStack` is ignored (fail closed) and a single warning is logged
|
|
629
|
+
* once per process, rather than once per request. Defaults to `false` so
|
|
630
|
+
* existing callers that don't pass this option keep today's behavior.
|
|
631
|
+
*
|
|
632
|
+
* @default false
|
|
633
|
+
*/
|
|
634
|
+
isProduction?: boolean;
|
|
600
635
|
/** Custom error logger */
|
|
601
636
|
logger?: (error: Error, ctx: Context) => void;
|
|
602
637
|
/** Custom error transformer */
|
|
@@ -630,4 +665,4 @@ declare function errorHandler(options?: ErrorHandlerOptions): Middleware;
|
|
|
630
665
|
*/
|
|
631
666
|
declare function notFoundHandler(message?: string): Middleware;
|
|
632
667
|
|
|
633
|
-
export { BadGatewayError, BadRequestError, ConflictError, ERROR_CODES, type ErrorHandlerOptions, ExpectationFailedError, FailedDependencyError, ForbiddenError, GENERIC_ERROR_CODE, GatewayTimeoutError, GoneError, HttpError, type HttpErrorOptions, HttpVersionNotSupportedError, ImATeapotError, InsufficientStorageError, InternalServerError, InvalidEmailError, InvalidUrlError, LengthError, LengthRequiredError, LockedError, LoopDetectedError, MethodNotAllowedError, NetworkAuthRequiredError, NextRushError, NotAcceptableError, NotExtendedError, NotFoundError, NotImplementedError, PatternError, PayloadTooLargeError, PaymentRequiredError, PreconditionFailedError, PreconditionRequiredError, ProxyAuthRequiredError, RangeNotSatisfiableError, RangeValidationError, RequestHeaderFieldsTooLargeError, RequestTimeoutError, RequiredFieldError, ServiceUnavailableError, TooEarlyError, TooManyRequestsError, TypeMismatchError, UnauthorizedError, UnavailableForLegalReasonsError, UnprocessableEntityError, UnsupportedMediaTypeError, UpgradeRequiredError, UriTooLongError, VALIDATION_ERROR_CODE, ValidationError, type ValidationIssue, VariantAlsoNegotiatesError, badGateway, badRequest, codeForStatus, conflict, createError, errorHandler, forbidden, gatewayTimeout, getErrorStatus, getHttpStatusMessage, getSafeErrorMessage, internalError, isHttpError, methodNotAllowed, notFound, notFoundHandler, serviceUnavailable, tooManyRequests, unauthorized, unprocessableEntity };
|
|
668
|
+
export { BadGatewayError, BadRequestError, ConflictError, ERROR_CODES, type ErrorHandlerOptions, ExpectationFailedError, FailedDependencyError, ForbiddenError, GENERIC_ERROR_CODE, GatewayTimeoutError, GoneError, HeaderValidationError, HttpError, type HttpErrorOptions, HttpVersionNotSupportedError, ImATeapotError, InsufficientStorageError, InternalServerError, InvalidEmailError, InvalidUrlError, LengthError, LengthRequiredError, LockedError, LoopDetectedError, MethodNotAllowedError, NetworkAuthRequiredError, NextRushError, NotAcceptableError, NotExtendedError, NotFoundError, NotImplementedError, PatternError, PayloadTooLargeError, PaymentRequiredError, PreconditionFailedError, PreconditionRequiredError, ProxyAuthRequiredError, RangeNotSatisfiableError, RangeValidationError, RequestHeaderFieldsTooLargeError, RequestTimeoutError, RequiredFieldError, ServiceUnavailableError, TooEarlyError, TooManyRequestsError, TypeMismatchError, UnauthorizedError, UnavailableForLegalReasonsError, UnprocessableEntityError, UnsupportedMediaTypeError, UpgradeRequiredError, UriTooLongError, VALIDATION_ERROR_CODE, ValidationError, type ValidationIssue, VariantAlsoNegotiatesError, badGateway, badRequest, codeForStatus, conflict, createError, errorHandler, forbidden, gatewayTimeout, getErrorStatus, getHttpStatusMessage, getSafeErrorMessage, internalError, isHttpError, methodNotAllowed, notFound, notFoundHandler, serviceUnavailable, tooManyRequests, unauthorized, unprocessableEntity };
|
package/dist/index.js
CHANGED
|
@@ -254,6 +254,20 @@ var HttpError = class _HttpError extends NextRushError {
|
|
|
254
254
|
}
|
|
255
255
|
};
|
|
256
256
|
|
|
257
|
+
// src/header-validation.ts
|
|
258
|
+
var HeaderValidationError = class extends NextRushError {
|
|
259
|
+
static {
|
|
260
|
+
__name(this, "HeaderValidationError");
|
|
261
|
+
}
|
|
262
|
+
constructor(message) {
|
|
263
|
+
super(message, {
|
|
264
|
+
status: 500,
|
|
265
|
+
code: "HEADER_VALIDATION_ERROR",
|
|
266
|
+
expose: false
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
257
271
|
// src/http-errors.ts
|
|
258
272
|
var BadRequestError = class extends HttpError {
|
|
259
273
|
static {
|
|
@@ -1039,6 +1053,7 @@ function getSafeErrorMessage(error) {
|
|
|
1039
1053
|
__name(getSafeErrorMessage, "getSafeErrorMessage");
|
|
1040
1054
|
|
|
1041
1055
|
// src/middleware.ts
|
|
1056
|
+
import { SECURITY_AUDIT } from "@nextrush/types";
|
|
1042
1057
|
function defaultLogger(error, ctx) {
|
|
1043
1058
|
const status = error instanceof HttpError ? error.status : 500;
|
|
1044
1059
|
if (status >= 500) {
|
|
@@ -1049,17 +1064,18 @@ function defaultLogger(error, ctx) {
|
|
|
1049
1064
|
}
|
|
1050
1065
|
__name(defaultLogger, "defaultLogger");
|
|
1051
1066
|
function errorHandler(options = {}) {
|
|
1052
|
-
const { includeStack = false, logger = defaultLogger, transform, handlers } = options;
|
|
1053
|
-
|
|
1067
|
+
const { includeStack = false, isProduction = false, logger = defaultLogger, transform, handlers } = options;
|
|
1068
|
+
let warnedIncludeStackInProduction = false;
|
|
1069
|
+
const handler = /* @__PURE__ */ __name(async (ctx, next) => {
|
|
1054
1070
|
try {
|
|
1055
1071
|
await next();
|
|
1056
1072
|
} catch (error) {
|
|
1057
1073
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
1058
1074
|
logger(err, ctx);
|
|
1059
1075
|
if (handlers) {
|
|
1060
|
-
for (const [ErrorType,
|
|
1076
|
+
for (const [ErrorType, handler2] of handlers) {
|
|
1061
1077
|
if (err instanceof ErrorType) {
|
|
1062
|
-
|
|
1078
|
+
handler2(err, ctx);
|
|
1063
1079
|
return;
|
|
1064
1080
|
}
|
|
1065
1081
|
}
|
|
@@ -1092,11 +1108,35 @@ function errorHandler(options = {}) {
|
|
|
1092
1108
|
}
|
|
1093
1109
|
}
|
|
1094
1110
|
if (includeStack && err.stack) {
|
|
1095
|
-
|
|
1111
|
+
if (isProduction) {
|
|
1112
|
+
if (!warnedIncludeStackInProduction) {
|
|
1113
|
+
warnedIncludeStackInProduction = true;
|
|
1114
|
+
console.warn("[@nextrush/errors] includeStack: true was ignored because isProduction is true. Stack traces are never emitted in production regardless of configuration.");
|
|
1115
|
+
}
|
|
1116
|
+
} else {
|
|
1117
|
+
body.stack = err.stack.split("\n").map((line) => line.trim());
|
|
1118
|
+
}
|
|
1096
1119
|
}
|
|
1097
1120
|
ctx.json(body);
|
|
1098
1121
|
}
|
|
1099
|
-
};
|
|
1122
|
+
}, "handler");
|
|
1123
|
+
function auditVerdict() {
|
|
1124
|
+
if (includeStack && !isProduction) {
|
|
1125
|
+
return {
|
|
1126
|
+
level: "warn",
|
|
1127
|
+
message: "errorHandler({ includeStack: true }) was constructed without isProduction \u2014 the per-request guard that ignores includeStack in production never fires unless isProduction is explicitly threaded through (e.g. isProduction: app.isProduction). Wire it, or this instance will leak stack traces in production."
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
return {
|
|
1131
|
+
level: "ok"
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
__name(auditVerdict, "auditVerdict");
|
|
1135
|
+
Object.defineProperty(handler, SECURITY_AUDIT, {
|
|
1136
|
+
value: auditVerdict,
|
|
1137
|
+
enumerable: false
|
|
1138
|
+
});
|
|
1139
|
+
return handler;
|
|
1100
1140
|
}
|
|
1101
1141
|
__name(errorHandler, "errorHandler");
|
|
1102
1142
|
function notFoundHandler(message = "Not Found") {
|
|
@@ -1124,6 +1164,7 @@ export {
|
|
|
1124
1164
|
GENERIC_ERROR_CODE,
|
|
1125
1165
|
GatewayTimeoutError,
|
|
1126
1166
|
GoneError,
|
|
1167
|
+
HeaderValidationError,
|
|
1127
1168
|
HttpError,
|
|
1128
1169
|
HttpVersionNotSupportedError,
|
|
1129
1170
|
ImATeapotError,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/codes.ts","../src/base.ts","../src/http-errors.ts","../src/validation.ts","../src/factory.ts","../src/middleware.ts"],"sourcesContent":["/**\n * @nextrush/errors - Central Error Code Registry\n *\n * Single source of truth mapping HTTP status codes to their canonical,\n * machine-readable error codes (audit E-4). Both the typed error classes and\n * the {@link createError} factory resolve codes through this registry, so a\n * given status maps to exactly one code regardless of construction path.\n *\n * @packageDocumentation\n */\n\n/**\n * Canonical error code for each supported HTTP status.\n *\n * @remarks\n * Keep this in sync with the typed classes in `http-errors.ts`. The\n * `audit-fixes` test suite asserts `createError(status).code === ERROR_CODES[status]`\n * for every entry, so drift between a class and this registry fails CI.\n */\nexport const ERROR_CODES: Readonly<Record<number, string>> = Object.freeze({\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 402: 'PAYMENT_REQUIRED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 405: 'METHOD_NOT_ALLOWED',\n 406: 'NOT_ACCEPTABLE',\n 407: 'PROXY_AUTH_REQUIRED',\n 408: 'REQUEST_TIMEOUT',\n 409: 'CONFLICT',\n 410: 'GONE',\n 411: 'LENGTH_REQUIRED',\n 412: 'PRECONDITION_FAILED',\n 413: 'PAYLOAD_TOO_LARGE',\n 414: 'URI_TOO_LONG',\n 415: 'UNSUPPORTED_MEDIA_TYPE',\n 416: 'RANGE_NOT_SATISFIABLE',\n 417: 'EXPECTATION_FAILED',\n 418: 'IM_A_TEAPOT',\n 422: 'UNPROCESSABLE_ENTITY',\n 423: 'LOCKED',\n 424: 'FAILED_DEPENDENCY',\n 425: 'TOO_EARLY',\n 426: 'UPGRADE_REQUIRED',\n 428: 'PRECONDITION_REQUIRED',\n 429: 'TOO_MANY_REQUESTS',\n 431: 'REQUEST_HEADER_FIELDS_TOO_LARGE',\n 451: 'UNAVAILABLE_FOR_LEGAL_REASONS',\n 500: 'INTERNAL_SERVER_ERROR',\n 501: 'NOT_IMPLEMENTED',\n 502: 'BAD_GATEWAY',\n 503: 'SERVICE_UNAVAILABLE',\n 504: 'GATEWAY_TIMEOUT',\n 505: 'HTTP_VERSION_NOT_SUPPORTED',\n 506: 'VARIANT_ALSO_NEGOTIATES',\n 507: 'INSUFFICIENT_STORAGE',\n 508: 'LOOP_DETECTED',\n 510: 'NOT_EXTENDED',\n 511: 'NETWORK_AUTH_REQUIRED',\n});\n\n/**\n * Resolve the canonical error code for an HTTP status.\n *\n * @param status - HTTP status code.\n * @returns The registered canonical code, or `HTTP_<status>` for statuses with\n * no dedicated class.\n */\nexport function codeForStatus(status: number): string {\n return ERROR_CODES[status] ?? `HTTP_${status}`;\n}\n\n/** Generic internal-error code used when no status-specific code applies. */\nexport const GENERIC_ERROR_CODE = 'INTERNAL_ERROR';\n\n/** Canonical code for validation failures. */\nexport const VALIDATION_ERROR_CODE = 'VALIDATION_ERROR';\n","/**\n * @nextrush/errors - Base Error Classes\n *\n * Foundational error classes for NextRush framework.\n *\n * @packageDocumentation\n */\n\nimport { codeForStatus } from './codes';\n\n/** Options accepted by {@link NextRushError.hydrate} when reconstructing errors. */\ninterface HydrateOptions {\n status?: number;\n code?: string;\n details?: Record<string, unknown>;\n requestId?: string;\n traceId?: string;\n timestamp?: string;\n}\n\nconst V8Error = Error as ErrorConstructor & {\n captureStackTrace?: (targetObject: object, constructorOpt?: Function) => void;\n};\n\n/** Maximum depth walked when serializing a `cause` chain (audit E-2). */\nconst MAX_CAUSE_DEPTH = 5;\n\n/**\n * Serialize an error `cause` chain into a plain, JSON-safe object.\n *\n * @remarks\n * Walks nested `cause` links up to {@link MAX_CAUSE_DEPTH}, guards against\n * cyclic chains via a visited set, and only surfaces `name`/`message`/`code`\n * so no unexpected enumerable properties (or secrets on ad-hoc error objects)\n * leak. Returns `undefined` when there is nothing meaningful to serialize.\n */\nfunction serializeCause(cause: unknown, seen: Set<unknown>, depth: number): unknown {\n if (cause === undefined || cause === null || depth >= MAX_CAUSE_DEPTH) return undefined;\n if (typeof cause !== 'object') return { message: String(cause) };\n if (seen.has(cause)) return undefined; // cycle guard\n seen.add(cause);\n\n const err = cause as { name?: unknown; message?: unknown; code?: unknown; cause?: unknown };\n const out: Record<string, unknown> = {};\n if (typeof err.name === 'string') out.name = err.name;\n if (typeof err.message === 'string') out.message = err.message;\n if (typeof err.code === 'string') out.code = err.code;\n\n const nested = serializeCause(err.cause, seen, depth + 1);\n if (nested !== undefined) out.cause = nested;\n\n return out;\n}\n\n/**\n * Base error class for all NextRush errors\n */\nexport class NextRushError extends Error {\n /** HTTP status code */\n readonly status: number;\n\n /** Error code for programmatic handling */\n readonly code: string;\n\n /** Whether error message is safe to expose to client */\n readonly expose: boolean;\n\n /** Additional error details */\n readonly details?: Record<string, unknown>;\n\n /** Original error that caused this error */\n readonly cause?: unknown;\n\n /** Correlation/request identifier for tracing across boundaries (audit E-5). */\n readonly requestId?: string;\n\n /** Distributed-trace identifier (audit E-5). */\n readonly traceId?: string;\n\n /** ISO-8601 timestamp of when the error was created, when supplied. */\n readonly timestamp?: string;\n\n constructor(\n message: string,\n options: {\n status?: number;\n code?: string;\n expose?: boolean;\n details?: Record<string, unknown>;\n cause?: unknown;\n requestId?: string;\n traceId?: string;\n timestamp?: string;\n } = {}\n ) {\n // Pass cause to the native Error constructor so `util.inspect` / logging\n // tooling walks the chain natively, in addition to our own `cause` field.\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = this.constructor.name;\n this.status = options.status ?? 500;\n this.code = options.code ?? 'INTERNAL_ERROR';\n this.expose = options.expose ?? this.status < 500;\n // Freeze a shallow snapshot so the error owns immutable details and the\n // caller's object cannot be mutated through the error (audit E-6).\n this.details = options.details ? Object.freeze({ ...options.details }) : undefined;\n this.cause = options.cause;\n this.requestId = options.requestId;\n this.traceId = options.traceId;\n this.timestamp = options.timestamp;\n\n // Maintain proper stack trace for V8\n // Skip for common client errors (4xx with expose=true) to reduce overhead.\n // These are expected control-flow errors, not bugs — stack traces add cost\n // without diagnostic value in production.\n if (V8Error.captureStackTrace && !(this.expose && this.status < 500)) {\n V8Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Convert error to JSON representation\n */\n toJSON(): Record<string, unknown> {\n const json: Record<string, unknown> = {\n error: this.name,\n message: this.expose ? this.message : 'Internal Server Error',\n code: this.code,\n status: this.status,\n };\n\n if (this.expose && this.details) {\n json.details = this.details;\n }\n\n // Surface the cause chain for diagnosability, but only on exposed errors —\n // a non-exposed 5xx cause may contain internal detail (audit E-2). Server\n // -side logging should read `error.cause` directly, which is never gated.\n if (this.expose && this.cause !== undefined) {\n const serialized = serializeCause(this.cause, new Set(), 0);\n if (serialized !== undefined) {\n json.cause = serialized;\n }\n }\n\n // Correlation identifiers are safe to surface and are the primary handle\n // for cross-service debugging (audit E-5). Only emitted when set, so the\n // default serialized shape is unchanged.\n if (this.requestId !== undefined) json.requestId = this.requestId;\n if (this.traceId !== undefined) json.traceId = this.traceId;\n if (this.timestamp !== undefined) json.timestamp = this.timestamp;\n\n return json;\n }\n\n /**\n * Reconstruct a {@link NextRushError} from a serialized {@link toJSON} payload\n * (audit E-7).\n *\n * @remarks\n * Enables cross-service transport: a downstream service can rebuild a typed\n * error (with a working `instanceof NextRushError`) from the JSON it received,\n * rather than being left with an opaque plain object. `message` falls back to\n * a generic string when the source error was not exposed.\n *\n * @param json - A payload previously produced by {@link toJSON}.\n * @returns A reconstructed {@link NextRushError}.\n */\n static fromJSON(json: Record<string, unknown>): NextRushError {\n return NextRushError.hydrate(json, (message, options) => new NextRushError(message, options));\n }\n\n /**\n * Shared hydration logic for {@link fromJSON} across the hierarchy.\n * @internal\n */\n protected static hydrate<T extends NextRushError>(\n json: Record<string, unknown>,\n build: (message: string, options: HydrateOptions) => T\n ): T {\n const status = typeof json.status === 'number' ? json.status : 500;\n const message = typeof json.message === 'string' ? json.message : getHttpStatusMessage(status);\n return build(message, {\n status,\n code: typeof json.code === 'string' ? json.code : undefined,\n details:\n json.details !== null && typeof json.details === 'object'\n ? (json.details as Record<string, unknown>)\n : undefined,\n requestId: typeof json.requestId === 'string' ? json.requestId : undefined,\n traceId: typeof json.traceId === 'string' ? json.traceId : undefined,\n timestamp: typeof json.timestamp === 'string' ? json.timestamp : undefined,\n });\n }\n\n /**\n * Create a response-safe version of the error\n */\n toResponse(): { status: number; body: Record<string, unknown> } {\n return {\n status: this.status,\n body: this.toJSON(),\n };\n }\n}\n\n/**\n * Default HTTP status messages\n */\nconst HTTP_STATUS_MESSAGES: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 402: 'Payment Required',\n 403: 'Forbidden',\n 404: 'Not Found',\n 405: 'Method Not Allowed',\n 406: 'Not Acceptable',\n 407: 'Proxy Authentication Required',\n 408: 'Request Timeout',\n 409: 'Conflict',\n 410: 'Gone',\n 411: 'Length Required',\n 412: 'Precondition Failed',\n 413: 'Payload Too Large',\n 414: 'URI Too Long',\n 415: 'Unsupported Media Type',\n 416: 'Range Not Satisfiable',\n 417: 'Expectation Failed',\n 418: \"I'm a Teapot\",\n 422: 'Unprocessable Entity',\n 423: 'Locked',\n 424: 'Failed Dependency',\n 425: 'Too Early',\n 426: 'Upgrade Required',\n 428: 'Precondition Required',\n 429: 'Too Many Requests',\n 431: 'Request Header Fields Too Large',\n 451: 'Unavailable For Legal Reasons',\n 500: 'Internal Server Error',\n 501: 'Not Implemented',\n 502: 'Bad Gateway',\n 503: 'Service Unavailable',\n 504: 'Gateway Timeout',\n 505: 'HTTP Version Not Supported',\n 506: 'Variant Also Negotiates',\n 507: 'Insufficient Storage',\n 508: 'Loop Detected',\n 510: 'Not Extended',\n 511: 'Network Authentication Required',\n};\n\n/**\n * Get default message for an HTTP status code\n */\nexport function getHttpStatusMessage(status: number): string {\n return HTTP_STATUS_MESSAGES[status] ?? `HTTP Error ${status}`;\n}\n\n/**\n * Base class for HTTP errors\n */\nexport class HttpError extends NextRushError {\n constructor(\n status: number,\n message?: string,\n options: {\n code?: string;\n expose?: boolean;\n details?: Record<string, unknown>;\n cause?: unknown;\n requestId?: string;\n traceId?: string;\n timestamp?: string;\n } = {}\n ) {\n super(message ?? getHttpStatusMessage(status), {\n status,\n code: options.code ?? codeForStatus(status),\n expose: options.expose ?? status < 500,\n details: options.details,\n cause: options.cause,\n requestId: options.requestId,\n traceId: options.traceId,\n timestamp: options.timestamp,\n });\n }\n\n /**\n * Reconstruct an {@link HttpError} from a serialized {@link NextRushError.toJSON}\n * payload (audit E-7).\n *\n * @param json - A payload previously produced by `toJSON()`.\n * @returns A reconstructed {@link HttpError}.\n */\n static override fromJSON(json: Record<string, unknown>): HttpError {\n return NextRushError.hydrate(\n json,\n (message, options) =>\n new HttpError(options.status ?? 500, message, {\n code: options.code,\n details: options.details,\n requestId: options.requestId,\n traceId: options.traceId,\n timestamp: options.timestamp,\n })\n );\n }\n}\n","/**\n * @nextrush/errors - HTTP Error Classes\n *\n * Standard HTTP error classes for common status codes.\n *\n * @packageDocumentation\n */\n\nimport { HttpError } from './base';\n\n/**\n * Options for creating HTTP errors\n */\nexport interface HttpErrorOptions {\n code?: string;\n expose?: boolean;\n details?: Record<string, unknown>;\n cause?: unknown;\n /** Correlation/request identifier (audit E-5). */\n requestId?: string;\n /** Distributed-trace identifier (audit E-5). */\n traceId?: string;\n /** ISO-8601 creation timestamp (audit E-5). */\n timestamp?: string;\n}\n\n// =============================================================================\n// 4xx Client Errors\n// =============================================================================\n\n/**\n * 400 Bad Request - Invalid syntax or malformed request\n */\nexport class BadRequestError extends HttpError {\n constructor(message = 'Bad Request', options: HttpErrorOptions = {}) {\n super(400, message, { code: 'BAD_REQUEST', ...options });\n }\n}\n\n/**\n * 401 Unauthorized - Authentication required\n */\nexport class UnauthorizedError extends HttpError {\n constructor(message = 'Unauthorized', options: HttpErrorOptions = {}) {\n super(401, message, { code: 'UNAUTHORIZED', ...options });\n }\n}\n\n/**\n * 402 Payment Required - Payment needed\n */\nexport class PaymentRequiredError extends HttpError {\n constructor(message = 'Payment Required', options: HttpErrorOptions = {}) {\n super(402, message, { code: 'PAYMENT_REQUIRED', ...options });\n }\n}\n\n/**\n * 403 Forbidden - Access denied\n */\nexport class ForbiddenError extends HttpError {\n constructor(message = 'Forbidden', options: HttpErrorOptions = {}) {\n super(403, message, { code: 'FORBIDDEN', ...options });\n }\n}\n\n/**\n * 404 Not Found - Resource not found\n */\nexport class NotFoundError extends HttpError {\n constructor(message = 'Not Found', options: HttpErrorOptions = {}) {\n super(404, message, { code: 'NOT_FOUND', ...options });\n }\n}\n\n/**\n * 405 Method Not Allowed - HTTP method not supported\n */\nexport class MethodNotAllowedError extends HttpError {\n readonly allowedMethods: string[];\n\n constructor(\n allowedMethods: string[] = [],\n message = 'Method Not Allowed',\n options: HttpErrorOptions = {}\n ) {\n super(405, message, {\n code: 'METHOD_NOT_ALLOWED',\n details: { allowedMethods },\n ...options,\n });\n this.allowedMethods = allowedMethods;\n }\n}\n\n/**\n * 406 Not Acceptable - Cannot produce acceptable response\n */\nexport class NotAcceptableError extends HttpError {\n constructor(message = 'Not Acceptable', options: HttpErrorOptions = {}) {\n super(406, message, { code: 'NOT_ACCEPTABLE', ...options });\n }\n}\n\n/**\n * 407 Proxy Authentication Required\n */\nexport class ProxyAuthRequiredError extends HttpError {\n constructor(message = 'Proxy Authentication Required', options: HttpErrorOptions = {}) {\n super(407, message, { code: 'PROXY_AUTH_REQUIRED', ...options });\n }\n}\n\n/**\n * 408 Request Timeout - Client took too long\n */\nexport class RequestTimeoutError extends HttpError {\n constructor(message = 'Request Timeout', options: HttpErrorOptions = {}) {\n super(408, message, { code: 'REQUEST_TIMEOUT', ...options });\n }\n}\n\n/**\n * 409 Conflict - Request conflicts with current state\n */\nexport class ConflictError extends HttpError {\n constructor(message = 'Conflict', options: HttpErrorOptions = {}) {\n super(409, message, { code: 'CONFLICT', ...options });\n }\n}\n\n/**\n * 410 Gone - Resource permanently deleted\n */\nexport class GoneError extends HttpError {\n constructor(message = 'Gone', options: HttpErrorOptions = {}) {\n super(410, message, { code: 'GONE', ...options });\n }\n}\n\n/**\n * 411 Length Required - Content-Length header required\n */\nexport class LengthRequiredError extends HttpError {\n constructor(message = 'Length Required', options: HttpErrorOptions = {}) {\n super(411, message, { code: 'LENGTH_REQUIRED', ...options });\n }\n}\n\n/**\n * 412 Precondition Failed - Precondition in headers not met\n */\nexport class PreconditionFailedError extends HttpError {\n constructor(message = 'Precondition Failed', options: HttpErrorOptions = {}) {\n super(412, message, { code: 'PRECONDITION_FAILED', ...options });\n }\n}\n\n/**\n * 413 Payload Too Large - Request body too large\n */\nexport class PayloadTooLargeError extends HttpError {\n constructor(message = 'Payload Too Large', options: HttpErrorOptions = {}) {\n super(413, message, { code: 'PAYLOAD_TOO_LARGE', ...options });\n }\n}\n\n/**\n * 414 URI Too Long - Request URI too long\n */\nexport class UriTooLongError extends HttpError {\n constructor(message = 'URI Too Long', options: HttpErrorOptions = {}) {\n super(414, message, { code: 'URI_TOO_LONG', ...options });\n }\n}\n\n/**\n * 415 Unsupported Media Type - Content type not supported\n */\nexport class UnsupportedMediaTypeError extends HttpError {\n constructor(message = 'Unsupported Media Type', options: HttpErrorOptions = {}) {\n super(415, message, { code: 'UNSUPPORTED_MEDIA_TYPE', ...options });\n }\n}\n\n/**\n * 416 Range Not Satisfiable - Cannot satisfy Range header\n */\nexport class RangeNotSatisfiableError extends HttpError {\n constructor(message = 'Range Not Satisfiable', options: HttpErrorOptions = {}) {\n super(416, message, { code: 'RANGE_NOT_SATISFIABLE', ...options });\n }\n}\n\n/**\n * 417 Expectation Failed - Cannot meet Expect header\n */\nexport class ExpectationFailedError extends HttpError {\n constructor(message = 'Expectation Failed', options: HttpErrorOptions = {}) {\n super(417, message, { code: 'EXPECTATION_FAILED', ...options });\n }\n}\n\n/**\n * 418 I'm a Teapot - RFC 2324\n */\nexport class ImATeapotError extends HttpError {\n constructor(message = \"I'm a teapot\", options: HttpErrorOptions = {}) {\n super(418, message, { code: 'IM_A_TEAPOT', ...options });\n }\n}\n\n/**\n * 422 Unprocessable Entity - Semantic errors\n */\nexport class UnprocessableEntityError extends HttpError {\n constructor(message = 'Unprocessable Entity', options: HttpErrorOptions = {}) {\n super(422, message, { code: 'UNPROCESSABLE_ENTITY', ...options });\n }\n}\n\n/**\n * 423 Locked - Resource is locked\n */\nexport class LockedError extends HttpError {\n constructor(message = 'Locked', options: HttpErrorOptions = {}) {\n super(423, message, { code: 'LOCKED', ...options });\n }\n}\n\n/**\n * 424 Failed Dependency - Dependent request failed\n */\nexport class FailedDependencyError extends HttpError {\n constructor(message = 'Failed Dependency', options: HttpErrorOptions = {}) {\n super(424, message, { code: 'FAILED_DEPENDENCY', ...options });\n }\n}\n\n/**\n * 425 Too Early - Request replayed too early\n */\nexport class TooEarlyError extends HttpError {\n constructor(message = 'Too Early', options: HttpErrorOptions = {}) {\n super(425, message, { code: 'TOO_EARLY', ...options });\n }\n}\n\n/**\n * 426 Upgrade Required - Client should upgrade protocol\n */\nexport class UpgradeRequiredError extends HttpError {\n constructor(message = 'Upgrade Required', options: HttpErrorOptions = {}) {\n super(426, message, { code: 'UPGRADE_REQUIRED', ...options });\n }\n}\n\n/**\n * 428 Precondition Required - Origin server requires conditional request\n */\nexport class PreconditionRequiredError extends HttpError {\n constructor(message = 'Precondition Required', options: HttpErrorOptions = {}) {\n super(428, message, { code: 'PRECONDITION_REQUIRED', ...options });\n }\n}\n\n/**\n * 429 Too Many Requests - Rate limit exceeded\n */\nexport class TooManyRequestsError extends HttpError {\n readonly retryAfter?: number;\n\n constructor(\n message = 'Too Many Requests',\n options: HttpErrorOptions & { retryAfter?: number } = {}\n ) {\n super(429, message, {\n code: 'TOO_MANY_REQUESTS',\n details: options.retryAfter ? { retryAfter: options.retryAfter } : undefined,\n ...options,\n });\n this.retryAfter = options.retryAfter;\n }\n}\n\n/**\n * 431 Request Header Fields Too Large\n */\nexport class RequestHeaderFieldsTooLargeError extends HttpError {\n constructor(message = 'Request Header Fields Too Large', options: HttpErrorOptions = {}) {\n super(431, message, { code: 'REQUEST_HEADER_FIELDS_TOO_LARGE', ...options });\n }\n}\n\n/**\n * 451 Unavailable For Legal Reasons\n */\nexport class UnavailableForLegalReasonsError extends HttpError {\n constructor(message = 'Unavailable For Legal Reasons', options: HttpErrorOptions = {}) {\n super(451, message, { code: 'UNAVAILABLE_FOR_LEGAL_REASONS', ...options });\n }\n}\n\n// =============================================================================\n// 5xx Server Errors\n// =============================================================================\n\n/**\n * 500 Internal Server Error - Generic server error\n */\nexport class InternalServerError extends HttpError {\n constructor(message = 'Internal Server Error', options: HttpErrorOptions = {}) {\n super(500, message, { code: 'INTERNAL_SERVER_ERROR', expose: false, ...options });\n }\n}\n\n/**\n * 501 Not Implemented - Feature not implemented\n */\nexport class NotImplementedError extends HttpError {\n constructor(message = 'Not Implemented', options: HttpErrorOptions = {}) {\n super(501, message, { code: 'NOT_IMPLEMENTED', expose: false, ...options });\n }\n}\n\n/**\n * 502 Bad Gateway - Invalid upstream response\n */\nexport class BadGatewayError extends HttpError {\n constructor(message = 'Bad Gateway', options: HttpErrorOptions = {}) {\n super(502, message, { code: 'BAD_GATEWAY', expose: false, ...options });\n }\n}\n\n/**\n * 503 Service Unavailable - Server temporarily unavailable\n */\nexport class ServiceUnavailableError extends HttpError {\n readonly retryAfter?: number;\n\n constructor(\n message = 'Service Unavailable',\n options: HttpErrorOptions & { retryAfter?: number } = {}\n ) {\n super(503, message, {\n code: 'SERVICE_UNAVAILABLE',\n expose: false,\n ...options,\n });\n this.retryAfter = options.retryAfter;\n }\n}\n\n/**\n * 504 Gateway Timeout - Upstream timeout\n */\nexport class GatewayTimeoutError extends HttpError {\n constructor(message = 'Gateway Timeout', options: HttpErrorOptions = {}) {\n super(504, message, { code: 'GATEWAY_TIMEOUT', expose: false, ...options });\n }\n}\n\n/**\n * 505 HTTP Version Not Supported\n */\nexport class HttpVersionNotSupportedError extends HttpError {\n constructor(message = 'HTTP Version Not Supported', options: HttpErrorOptions = {}) {\n super(505, message, { code: 'HTTP_VERSION_NOT_SUPPORTED', expose: false, ...options });\n }\n}\n\n/**\n * 506 Variant Also Negotiates\n */\nexport class VariantAlsoNegotiatesError extends HttpError {\n constructor(message = 'Variant Also Negotiates', options: HttpErrorOptions = {}) {\n super(506, message, { code: 'VARIANT_ALSO_NEGOTIATES', expose: false, ...options });\n }\n}\n\n/**\n * 507 Insufficient Storage\n */\nexport class InsufficientStorageError extends HttpError {\n constructor(message = 'Insufficient Storage', options: HttpErrorOptions = {}) {\n super(507, message, { code: 'INSUFFICIENT_STORAGE', expose: false, ...options });\n }\n}\n\n/**\n * 508 Loop Detected\n */\nexport class LoopDetectedError extends HttpError {\n constructor(message = 'Loop Detected', options: HttpErrorOptions = {}) {\n super(508, message, { code: 'LOOP_DETECTED', expose: false, ...options });\n }\n}\n\n/**\n * 510 Not Extended\n */\nexport class NotExtendedError extends HttpError {\n constructor(message = 'Not Extended', options: HttpErrorOptions = {}) {\n super(510, message, { code: 'NOT_EXTENDED', expose: false, ...options });\n }\n}\n\n/**\n * 511 Network Authentication Required\n */\nexport class NetworkAuthRequiredError extends HttpError {\n constructor(message = 'Network Authentication Required', options: HttpErrorOptions = {}) {\n super(511, message, { code: 'NETWORK_AUTH_REQUIRED', expose: false, ...options });\n }\n}\n","/**\n * @nextrush/errors - Validation Error Classes\n *\n * Specialized errors for input validation.\n *\n * @packageDocumentation\n */\n\nimport { NextRushError } from './base';\n\n/**\n * Single validation issue\n */\nexport interface ValidationIssue {\n /** Field path (e.g., 'user.email' or 'items[0].name') */\n path: string;\n /** Error message for this field */\n message: string;\n /** Validation rule that failed */\n rule?: string;\n /** Expected value or constraint */\n expected?: unknown;\n /** Actual value received */\n received?: unknown;\n}\n\n/**\n * Validation error with multiple issues\n */\nexport class ValidationError extends NextRushError {\n /** List of validation issues */\n readonly issues: ValidationIssue[];\n\n constructor(issues: ValidationIssue[], message = 'Validation failed') {\n super(message, {\n status: 400,\n code: 'VALIDATION_ERROR',\n expose: true,\n });\n // Freeze a snapshot so issues are immutable after construction (audit E-6).\n this.issues = Object.freeze([...issues]) as ValidationIssue[];\n }\n\n /**\n * Create from a single field error\n */\n static fromField(path: string, message: string, rule?: string): ValidationError {\n return new ValidationError([{ path, message, rule }]);\n }\n\n /**\n * Create from multiple field errors\n */\n static fromFields(errors: Record<string, string>): ValidationError {\n const issues = Object.entries(errors).map(([path, message]) => ({\n path,\n message,\n }));\n return new ValidationError(issues);\n }\n\n /**\n * Check if a specific field has errors\n */\n hasErrorFor(path: string): boolean {\n return this.issues.some((issue) => issue.path === path);\n }\n\n /**\n * Get errors for a specific field\n */\n getErrorsFor(path: string): ValidationIssue[] {\n return this.issues.filter((issue) => issue.path === path);\n }\n\n /**\n * Get first error message for a field\n */\n getFirstError(path: string): string | undefined {\n return this.issues.find((issue) => issue.path === path)?.message;\n }\n\n /**\n * Convert to flat error object\n */\n toFlatObject(): Record<string, string> {\n const result: Record<string, string> = {};\n for (const issue of this.issues) {\n if (!result[issue.path]) {\n result[issue.path] = issue.message;\n }\n }\n return result;\n }\n\n override toJSON(): Record<string, unknown> {\n return {\n error: this.name,\n message: this.message,\n code: this.code,\n status: this.status,\n // Strip `received` to prevent leaking sensitive input values (passwords, tokens)\n issues: this.issues.map(({ path, message, rule, expected }) => ({\n path,\n message,\n ...(rule !== undefined && { rule }),\n ...(expected !== undefined && { expected }),\n })),\n };\n }\n}\n\n/**\n * Required field missing\n */\nexport class RequiredFieldError extends ValidationError {\n constructor(field: string) {\n super(\n [{ path: field, message: `${field} is required`, rule: 'required' }],\n `${field} is required`\n );\n }\n}\n\n/**\n * Field type mismatch\n */\nexport class TypeMismatchError extends ValidationError {\n constructor(field: string, expected: string, received: string) {\n super(\n [\n {\n path: field,\n message: `Expected ${expected}, received ${received}`,\n rule: 'type',\n expected,\n received,\n },\n ],\n `${field} must be of type ${expected}`\n );\n }\n}\n\n/**\n * Value out of range\n */\nexport class RangeValidationError extends ValidationError {\n constructor(field: string, min?: number, max?: number) {\n const parts: string[] = [];\n if (min !== undefined) parts.push(`at least ${min}`);\n if (max !== undefined) parts.push(`at most ${max}`);\n const message = `${field} must be ${parts.join(' and ')}`;\n\n super([{ path: field, message, rule: 'range', expected: { min, max } }], message);\n }\n}\n\n/**\n * String length violation\n */\nexport class LengthError extends ValidationError {\n constructor(field: string, min?: number, max?: number) {\n const parts: string[] = [];\n if (min !== undefined) parts.push(`at least ${min} characters`);\n if (max !== undefined) parts.push(`at most ${max} characters`);\n const message = `${field} must be ${parts.join(' and ')}`;\n\n super([{ path: field, message, rule: 'length', expected: { min, max } }], message);\n }\n}\n\n/**\n * Pattern mismatch\n */\nexport class PatternError extends ValidationError {\n constructor(field: string, pattern: string, message?: string) {\n super(\n [\n {\n path: field,\n message: message ?? `${field} does not match required pattern`,\n rule: 'pattern',\n expected: pattern,\n },\n ],\n message ?? `${field} does not match required pattern`\n );\n }\n}\n\n/**\n * Invalid email format\n */\nexport class InvalidEmailError extends ValidationError {\n constructor(field = 'email') {\n super(\n [{ path: field, message: 'Invalid email address', rule: 'email' }],\n 'Invalid email address'\n );\n }\n}\n\n/**\n * Invalid URL format\n */\nexport class InvalidUrlError extends ValidationError {\n constructor(field = 'url') {\n super([{ path: field, message: 'Invalid URL', rule: 'url' }], 'Invalid URL');\n }\n}\n","/**\n * @nextrush/errors - Error Factory\n *\n * Factory functions for creating errors.\n *\n * @packageDocumentation\n */\n\nimport { HttpError, NextRushError } from './base';\nimport {\n BadGatewayError,\n BadRequestError,\n ConflictError,\n ExpectationFailedError,\n FailedDependencyError,\n ForbiddenError,\n GatewayTimeoutError,\n GoneError,\n HttpVersionNotSupportedError,\n ImATeapotError,\n InsufficientStorageError,\n InternalServerError,\n LengthRequiredError,\n LockedError,\n LoopDetectedError,\n MethodNotAllowedError,\n NetworkAuthRequiredError,\n NotAcceptableError,\n NotExtendedError,\n NotFoundError,\n NotImplementedError,\n PayloadTooLargeError,\n PaymentRequiredError,\n PreconditionFailedError,\n PreconditionRequiredError,\n ProxyAuthRequiredError,\n RangeNotSatisfiableError,\n RequestHeaderFieldsTooLargeError,\n RequestTimeoutError,\n ServiceUnavailableError,\n TooEarlyError,\n TooManyRequestsError,\n UnauthorizedError,\n UnavailableForLegalReasonsError,\n UnprocessableEntityError,\n UnsupportedMediaTypeError,\n UpgradeRequiredError,\n UriTooLongError,\n VariantAlsoNegotiatesError,\n type HttpErrorOptions,\n} from './http-errors';\n\n/**\n * HTTP status code to error class mapping.\n *\n * @remarks\n * Covers every status that has a dedicated typed class so {@link createError}\n * returns the correctly-coded instance (audit E-3). `405` is handled separately\n * because {@link MethodNotAllowedError} has a divergent constructor signature.\n */\nconst ERROR_MAP: Record<number, new (message?: string, options?: HttpErrorOptions) => HttpError> = {\n 400: BadRequestError,\n 401: UnauthorizedError,\n 402: PaymentRequiredError,\n 403: ForbiddenError,\n 404: NotFoundError,\n 406: NotAcceptableError,\n 407: ProxyAuthRequiredError,\n 408: RequestTimeoutError,\n 409: ConflictError,\n 410: GoneError,\n 411: LengthRequiredError,\n 412: PreconditionFailedError,\n 413: PayloadTooLargeError,\n 414: UriTooLongError,\n 415: UnsupportedMediaTypeError,\n 416: RangeNotSatisfiableError,\n 417: ExpectationFailedError,\n 418: ImATeapotError,\n 422: UnprocessableEntityError,\n 423: LockedError,\n 424: FailedDependencyError,\n 425: TooEarlyError,\n 426: UpgradeRequiredError,\n 428: PreconditionRequiredError,\n 429: TooManyRequestsError,\n 431: RequestHeaderFieldsTooLargeError,\n 451: UnavailableForLegalReasonsError,\n 500: InternalServerError,\n 501: NotImplementedError,\n 502: BadGatewayError,\n 503: ServiceUnavailableError,\n 504: GatewayTimeoutError,\n 505: HttpVersionNotSupportedError,\n 506: VariantAlsoNegotiatesError,\n 507: InsufficientStorageError,\n 508: LoopDetectedError,\n 510: NotExtendedError,\n 511: NetworkAuthRequiredError,\n};\n\n/**\n * Create an HTTP error by status code\n */\nexport function createError(\n status: number,\n message?: string,\n options?: HttpErrorOptions\n): HttpError {\n // Special case: MethodNotAllowedError requires allowedMethods array\n if (status === 405) {\n return new MethodNotAllowedError([], message, options);\n }\n\n const ErrorClass = ERROR_MAP[status];\n\n if (ErrorClass) {\n return new ErrorClass(message, options);\n }\n\n // Fallback: let HttpError constructor resolve the message via getHttpStatusMessage()\n return new HttpError(status, message, options);\n}\n\n/**\n * Create a 400 Bad Request error\n */\nexport function badRequest(message?: string, options?: HttpErrorOptions): BadRequestError {\n return new BadRequestError(message, options);\n}\n\n/**\n * Create a 401 Unauthorized error\n */\nexport function unauthorized(message?: string, options?: HttpErrorOptions): UnauthorizedError {\n return new UnauthorizedError(message, options);\n}\n\n/**\n * Create a 403 Forbidden error\n */\nexport function forbidden(message?: string, options?: HttpErrorOptions): ForbiddenError {\n return new ForbiddenError(message, options);\n}\n\n/**\n * Create a 404 Not Found error\n */\nexport function notFound(message?: string, options?: HttpErrorOptions): NotFoundError {\n return new NotFoundError(message, options);\n}\n\n/**\n * Create a 405 Method Not Allowed error\n */\nexport function methodNotAllowed(\n allowedMethods?: string[],\n message?: string,\n options?: HttpErrorOptions\n): MethodNotAllowedError {\n return new MethodNotAllowedError(allowedMethods, message, options);\n}\n\n/**\n * Create a 409 Conflict error\n */\nexport function conflict(message?: string, options?: HttpErrorOptions): ConflictError {\n return new ConflictError(message, options);\n}\n\n/**\n * Create a 422 Unprocessable Entity error\n */\nexport function unprocessableEntity(\n message?: string,\n options?: HttpErrorOptions\n): UnprocessableEntityError {\n return new UnprocessableEntityError(message, options);\n}\n\n/**\n * Create a 429 Too Many Requests error\n */\nexport function tooManyRequests(\n message?: string,\n options?: HttpErrorOptions & { retryAfter?: number }\n): TooManyRequestsError {\n return new TooManyRequestsError(message, options);\n}\n\n/**\n * Create a 500 Internal Server Error\n */\nexport function internalError(message?: string, options?: HttpErrorOptions): InternalServerError {\n return new InternalServerError(message, options);\n}\n\n/**\n * Create a 502 Bad Gateway error\n */\nexport function badGateway(message?: string, options?: HttpErrorOptions): BadGatewayError {\n return new BadGatewayError(message, options);\n}\n\n/**\n * Create a 503 Service Unavailable error\n */\nexport function serviceUnavailable(\n message?: string,\n options?: HttpErrorOptions & { retryAfter?: number }\n): ServiceUnavailableError {\n return new ServiceUnavailableError(message, options);\n}\n\n/**\n * Create a 504 Gateway Timeout error\n */\nexport function gatewayTimeout(message?: string, options?: HttpErrorOptions): GatewayTimeoutError {\n return new GatewayTimeoutError(message, options);\n}\n\n/**\n * Check if an error is a NextRush HttpError\n */\nexport function isHttpError(error: unknown): error is HttpError {\n return error instanceof HttpError;\n}\n\n/**\n * Get HTTP status from any error\n *\n * Checks NextRushError first (covers HttpError, ValidationError, and all subclasses),\n * then falls back to duck-typed status property.\n */\nexport function getErrorStatus(error: unknown): number {\n if (error instanceof NextRushError) {\n return error.status;\n }\n if (\n error != null &&\n typeof error === 'object' &&\n 'status' in error &&\n typeof (error as { status: unknown }).status === 'number'\n ) {\n const status = (error as { status: number }).status;\n return status >= 400 && status < 600 ? status : 500;\n }\n return 500;\n}\n\n/**\n * Get error message safe for client response\n *\n * Exposes message for any NextRushError (including ValidationError)\n * when the error's expose flag is true.\n */\nexport function getSafeErrorMessage(error: unknown): string {\n if (error instanceof NextRushError && error.expose) {\n return error.message;\n }\n return 'Internal Server Error';\n}\n","/**\n * @nextrush/errors - Error Handler Middleware\n *\n * Middleware for handling errors in NextRush applications.\n *\n * @packageDocumentation\n */\n\nimport type { Context, Middleware, Next } from '@nextrush/types';\nimport { HttpError, NextRushError, getHttpStatusMessage } from './base';\n\n/**\n * Error handler options\n */\nexport interface ErrorHandlerOptions {\n /** Include stack trace in development */\n includeStack?: boolean;\n\n /** Custom error logger */\n logger?: (error: Error, ctx: Context) => void;\n\n /** Custom error transformer */\n transform?: (error: Error, ctx: Context) => Record<string, unknown>;\n\n /** Handle specific error types */\n handlers?: Map<new (...args: unknown[]) => Error, (error: Error, ctx: Context) => void>;\n}\n\n/**\n * Default error logger\n */\nfunction defaultLogger(error: Error, ctx: Context): void {\n const status = error instanceof HttpError ? error.status : 500;\n\n if (status >= 500) {\n console.error(`[ERROR] ${ctx.method} ${ctx.path}:`, error);\n } else {\n console.warn(`[WARN] ${ctx.method} ${ctx.path}: ${error.message}`);\n }\n}\n\n/**\n * Create error handler middleware\n *\n * @example\n * ```typescript\n * const app = createApp();\n *\n * // Add error handler first\n * app.use(errorHandler({\n * includeStack: process.env.NODE_ENV !== 'production',\n * logger: (err, ctx) => myLogger.error(err),\n * }));\n * ```\n */\nexport function errorHandler(options: ErrorHandlerOptions = {}): Middleware {\n const { includeStack = false, logger = defaultLogger, transform, handlers } = options;\n\n return async (ctx: Context, next: Next): Promise<void> => {\n try {\n await next();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Log the error\n logger(err, ctx);\n\n // Check for custom handlers\n if (handlers) {\n for (const [ErrorType, handler] of handlers) {\n if (err instanceof ErrorType) {\n handler(err, ctx);\n return;\n }\n }\n }\n\n // Determine status code\n let status = 500;\n let expose = false;\n let code = 'INTERNAL_ERROR';\n let details: Record<string, unknown> | undefined;\n\n if (err instanceof HttpError || err instanceof NextRushError) {\n status = err.status;\n expose = err.expose;\n code = err.code;\n details = err.details;\n }\n\n ctx.status = status;\n\n // Build response body\n let body: Record<string, unknown>;\n\n if (transform) {\n body = transform(err, ctx);\n } else if (err instanceof NextRushError) {\n // Delegate to the error's own toJSON() — this is the single source of\n // truth for what a given error type serializes to (e.g. ValidationError\n // adds `issues` while stripping `received`). Duplicating that shape here\n // would silently drift out of sync with subclass overrides.\n body = err.toJSON();\n } else {\n body = {\n error: expose ? err.name : getHttpStatusMessage(status),\n message: expose ? err.message : 'Internal Server Error',\n code,\n status,\n };\n\n if (expose && details) {\n body.details = details;\n }\n }\n\n if (includeStack && err.stack) {\n body.stack = err.stack.split('\\n').map((line) => line.trim());\n }\n\n ctx.json(body);\n }\n };\n}\n\n/**\n * Not found handler middleware - catches unhandled requests\n *\n * @example\n * ```typescript\n * // Add at the end of middleware chain\n * app.use(notFoundHandler());\n * ```\n */\nexport function notFoundHandler(message = 'Not Found'): Middleware {\n return async (ctx: Context, next: Next): Promise<void> => {\n await next();\n // Only handle if no response was sent and status indicates unhandled\n if (!ctx.responded && ctx.status === 404) {\n ctx.json({\n error: 'NotFoundError',\n message,\n code: 'NOT_FOUND',\n status: 404,\n });\n }\n };\n}\n\n"],"mappings":";;;;AAmBO,IAAMA,cAAgDC,OAAOC,OAAO;EACzE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;AACP,CAAA;AASO,SAASC,cAAcC,QAAc;AAC1C,SAAOJ,YAAYI,MAAAA,KAAW,QAAQA,MAAAA;AACxC;AAFgBD;AAKT,IAAME,qBAAqB;AAG3B,IAAMC,wBAAwB;;;ACxDrC,IAAMC,UAAUC;AAKhB,IAAMC,kBAAkB;AAWxB,SAASC,eAAeC,OAAgBC,MAAoBC,OAAa;AACvE,MAAIF,UAAUG,UAAaH,UAAU,QAAQE,SAASJ,gBAAiB,QAAOK;AAC9E,MAAI,OAAOH,UAAU,SAAU,QAAO;IAAEI,SAASC,OAAOL,KAAAA;EAAO;AAC/D,MAAIC,KAAKK,IAAIN,KAAAA,EAAQ,QAAOG;AAC5BF,OAAKM,IAAIP,KAAAA;AAET,QAAMQ,MAAMR;AACZ,QAAMS,MAA+B,CAAC;AACtC,MAAI,OAAOD,IAAIE,SAAS,SAAUD,KAAIC,OAAOF,IAAIE;AACjD,MAAI,OAAOF,IAAIJ,YAAY,SAAUK,KAAIL,UAAUI,IAAIJ;AACvD,MAAI,OAAOI,IAAIG,SAAS,SAAUF,KAAIE,OAAOH,IAAIG;AAEjD,QAAMC,SAASb,eAAeS,IAAIR,OAAOC,MAAMC,QAAQ,CAAA;AACvD,MAAIU,WAAWT,OAAWM,KAAIT,QAAQY;AAEtC,SAAOH;AACT;AAhBSV;AAqBF,IAAMc,gBAAN,MAAMA,uBAAsBhB,MAAAA;EAzDnC,OAyDmCA;;;;EAExBiB;;EAGAH;;EAGAI;;EAGAC;;EAGAhB;;EAGAiB;;EAGAC;;EAGAC;EAET,YACEf,SACAgB,UASI,CAAC,GACL;AAGA,UAAMhB,SAASgB,QAAQpB,UAAUG,SAAY;MAAEH,OAAOoB,QAAQpB;IAAM,IAAIG,MAAAA;AACxE,SAAKO,OAAO,KAAK,YAAYA;AAC7B,SAAKI,SAASM,QAAQN,UAAU;AAChC,SAAKH,OAAOS,QAAQT,QAAQ;AAC5B,SAAKI,SAASK,QAAQL,UAAU,KAAKD,SAAS;AAG9C,SAAKE,UAAUI,QAAQJ,UAAUK,OAAOC,OAAO;MAAE,GAAGF,QAAQJ;IAAQ,CAAA,IAAKb;AACzE,SAAKH,QAAQoB,QAAQpB;AACrB,SAAKiB,YAAYG,QAAQH;AACzB,SAAKC,UAAUE,QAAQF;AACvB,SAAKC,YAAYC,QAAQD;AAMzB,QAAIvB,QAAQ2B,qBAAqB,EAAE,KAAKR,UAAU,KAAKD,SAAS,MAAM;AACpElB,cAAQ2B,kBAAkB,MAAM,KAAK,WAAW;IAClD;EACF;;;;EAKAC,SAAkC;AAChC,UAAMC,OAAgC;MACpCC,OAAO,KAAKhB;MACZN,SAAS,KAAKW,SAAS,KAAKX,UAAU;MACtCO,MAAM,KAAKA;MACXG,QAAQ,KAAKA;IACf;AAEA,QAAI,KAAKC,UAAU,KAAKC,SAAS;AAC/BS,WAAKT,UAAU,KAAKA;IACtB;AAKA,QAAI,KAAKD,UAAU,KAAKf,UAAUG,QAAW;AAC3C,YAAMwB,aAAa5B,eAAe,KAAKC,OAAO,oBAAI4B,IAAAA,GAAO,CAAA;AACzD,UAAID,eAAexB,QAAW;AAC5BsB,aAAKzB,QAAQ2B;MACf;IACF;AAKA,QAAI,KAAKV,cAAcd,OAAWsB,MAAKR,YAAY,KAAKA;AACxD,QAAI,KAAKC,YAAYf,OAAWsB,MAAKP,UAAU,KAAKA;AACpD,QAAI,KAAKC,cAAchB,OAAWsB,MAAKN,YAAY,KAAKA;AAExD,WAAOM;EACT;;;;;;;;;;;;;;EAeA,OAAOI,SAASJ,MAA8C;AAC5D,WAAOZ,eAAciB,QAAQL,MAAM,CAACrB,SAASgB,YAAY,IAAIP,eAAcT,SAASgB,OAAAA,CAAAA;EACtF;;;;;EAMA,OAAiBU,QACfL,MACAM,OACG;AACH,UAAMjB,SAAS,OAAOW,KAAKX,WAAW,WAAWW,KAAKX,SAAS;AAC/D,UAAMV,UAAU,OAAOqB,KAAKrB,YAAY,WAAWqB,KAAKrB,UAAU4B,qBAAqBlB,MAAAA;AACvF,WAAOiB,MAAM3B,SAAS;MACpBU;MACAH,MAAM,OAAOc,KAAKd,SAAS,WAAWc,KAAKd,OAAOR;MAClDa,SACES,KAAKT,YAAY,QAAQ,OAAOS,KAAKT,YAAY,WAC5CS,KAAKT,UACNb;MACNc,WAAW,OAAOQ,KAAKR,cAAc,WAAWQ,KAAKR,YAAYd;MACjEe,SAAS,OAAOO,KAAKP,YAAY,WAAWO,KAAKP,UAAUf;MAC3DgB,WAAW,OAAOM,KAAKN,cAAc,WAAWM,KAAKN,YAAYhB;IACnE,CAAA;EACF;;;;EAKA8B,aAAgE;AAC9D,WAAO;MACLnB,QAAQ,KAAKA;MACboB,MAAM,KAAKV,OAAM;IACnB;EACF;AACF;AAKA,IAAMW,uBAA+C;EACnD,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;AACP;AAKO,SAASH,qBAAqBlB,QAAc;AACjD,SAAOqB,qBAAqBrB,MAAAA,KAAW,cAAcA,MAAAA;AACvD;AAFgBkB;AAOT,IAAMI,YAAN,MAAMA,mBAAkBvB,cAAAA;EApQ/B,OAoQ+BA;;;EAC7B,YACEC,QACAV,SACAgB,UAQI,CAAC,GACL;AACA,UAAMhB,WAAW4B,qBAAqBlB,MAAAA,GAAS;MAC7CA;MACAH,MAAMS,QAAQT,QAAQ0B,cAAcvB,MAAAA;MACpCC,QAAQK,QAAQL,UAAUD,SAAS;MACnCE,SAASI,QAAQJ;MACjBhB,OAAOoB,QAAQpB;MACfiB,WAAWG,QAAQH;MACnBC,SAASE,QAAQF;MACjBC,WAAWC,QAAQD;IACrB,CAAA;EACF;;;;;;;;EASA,OAAgBU,SAASJ,MAA0C;AACjE,WAAOZ,cAAciB,QACnBL,MACA,CAACrB,SAASgB,YACR,IAAIgB,WAAUhB,QAAQN,UAAU,KAAKV,SAAS;MAC5CO,MAAMS,QAAQT;MACdK,SAASI,QAAQJ;MACjBC,WAAWG,QAAQH;MACnBC,SAASE,QAAQF;MACjBC,WAAWC,QAAQD;IACrB,CAAA,CAAA;EAEN;AACF;;;ACjRO,IAAMmB,kBAAN,cAA8BC,UAAAA;EAjCrC,OAiCqCA;;;EACnC,YAAYC,UAAU,eAAeC,UAA4B,CAAC,GAAG;AACnE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAe,GAAGD;IAAQ,CAAA;EACxD;AACF;AAKO,IAAME,oBAAN,cAAgCJ,UAAAA;EA1CvC,OA0CuCA;;;EACrC,YAAYC,UAAU,gBAAgBC,UAA4B,CAAC,GAAG;AACpE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAgB,GAAGD;IAAQ,CAAA;EACzD;AACF;AAKO,IAAMG,uBAAN,cAAmCL,UAAAA;EAnD1C,OAmD0CA;;;EACxC,YAAYC,UAAU,oBAAoBC,UAA4B,CAAC,GAAG;AACxE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAoB,GAAGD;IAAQ,CAAA;EAC7D;AACF;AAKO,IAAMI,iBAAN,cAA6BN,UAAAA;EA5DpC,OA4DoCA;;;EAClC,YAAYC,UAAU,aAAaC,UAA4B,CAAC,GAAG;AACjE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAa,GAAGD;IAAQ,CAAA;EACtD;AACF;AAKO,IAAMK,gBAAN,cAA4BP,UAAAA;EArEnC,OAqEmCA;;;EACjC,YAAYC,UAAU,aAAaC,UAA4B,CAAC,GAAG;AACjE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAa,GAAGD;IAAQ,CAAA;EACtD;AACF;AAKO,IAAMM,wBAAN,cAAoCR,UAAAA;EA9E3C,OA8E2CA;;;EAChCS;EAET,YACEA,iBAA2B,CAAA,GAC3BR,UAAU,sBACVC,UAA4B,CAAC,GAC7B;AACA,UAAM,KAAKD,SAAS;MAClBE,MAAM;MACNO,SAAS;QAAED;MAAe;MAC1B,GAAGP;IACL,CAAA;AACA,SAAKO,iBAAiBA;EACxB;AACF;AAKO,IAAME,qBAAN,cAAiCX,UAAAA;EAlGxC,OAkGwCA;;;EACtC,YAAYC,UAAU,kBAAkBC,UAA4B,CAAC,GAAG;AACtE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAkB,GAAGD;IAAQ,CAAA;EAC3D;AACF;AAKO,IAAMU,yBAAN,cAAqCZ,UAAAA;EA3G5C,OA2G4CA;;;EAC1C,YAAYC,UAAU,iCAAiCC,UAA4B,CAAC,GAAG;AACrF,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAuB,GAAGD;IAAQ,CAAA;EAChE;AACF;AAKO,IAAMW,sBAAN,cAAkCb,UAAAA;EApHzC,OAoHyCA;;;EACvC,YAAYC,UAAU,mBAAmBC,UAA4B,CAAC,GAAG;AACvE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAmB,GAAGD;IAAQ,CAAA;EAC5D;AACF;AAKO,IAAMY,gBAAN,cAA4Bd,UAAAA;EA7HnC,OA6HmCA;;;EACjC,YAAYC,UAAU,YAAYC,UAA4B,CAAC,GAAG;AAChE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAY,GAAGD;IAAQ,CAAA;EACrD;AACF;AAKO,IAAMa,YAAN,cAAwBf,UAAAA;EAtI/B,OAsI+BA;;;EAC7B,YAAYC,UAAU,QAAQC,UAA4B,CAAC,GAAG;AAC5D,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAQ,GAAGD;IAAQ,CAAA;EACjD;AACF;AAKO,IAAMc,sBAAN,cAAkChB,UAAAA;EA/IzC,OA+IyCA;;;EACvC,YAAYC,UAAU,mBAAmBC,UAA4B,CAAC,GAAG;AACvE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAmB,GAAGD;IAAQ,CAAA;EAC5D;AACF;AAKO,IAAMe,0BAAN,cAAsCjB,UAAAA;EAxJ7C,OAwJ6CA;;;EAC3C,YAAYC,UAAU,uBAAuBC,UAA4B,CAAC,GAAG;AAC3E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAuB,GAAGD;IAAQ,CAAA;EAChE;AACF;AAKO,IAAMgB,uBAAN,cAAmClB,UAAAA;EAjK1C,OAiK0CA;;;EACxC,YAAYC,UAAU,qBAAqBC,UAA4B,CAAC,GAAG;AACzE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAqB,GAAGD;IAAQ,CAAA;EAC9D;AACF;AAKO,IAAMiB,kBAAN,cAA8BnB,UAAAA;EA1KrC,OA0KqCA;;;EACnC,YAAYC,UAAU,gBAAgBC,UAA4B,CAAC,GAAG;AACpE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAgB,GAAGD;IAAQ,CAAA;EACzD;AACF;AAKO,IAAMkB,4BAAN,cAAwCpB,UAAAA;EAnL/C,OAmL+CA;;;EAC7C,YAAYC,UAAU,0BAA0BC,UAA4B,CAAC,GAAG;AAC9E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAA0B,GAAGD;IAAQ,CAAA;EACnE;AACF;AAKO,IAAMmB,2BAAN,cAAuCrB,UAAAA;EA5L9C,OA4L8CA;;;EAC5C,YAAYC,UAAU,yBAAyBC,UAA4B,CAAC,GAAG;AAC7E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAyB,GAAGD;IAAQ,CAAA;EAClE;AACF;AAKO,IAAMoB,yBAAN,cAAqCtB,UAAAA;EArM5C,OAqM4CA;;;EAC1C,YAAYC,UAAU,sBAAsBC,UAA4B,CAAC,GAAG;AAC1E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAsB,GAAGD;IAAQ,CAAA;EAC/D;AACF;AAKO,IAAMqB,iBAAN,cAA6BvB,UAAAA;EA9MpC,OA8MoCA;;;EAClC,YAAYC,UAAU,gBAAgBC,UAA4B,CAAC,GAAG;AACpE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAe,GAAGD;IAAQ,CAAA;EACxD;AACF;AAKO,IAAMsB,2BAAN,cAAuCxB,UAAAA;EAvN9C,OAuN8CA;;;EAC5C,YAAYC,UAAU,wBAAwBC,UAA4B,CAAC,GAAG;AAC5E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAwB,GAAGD;IAAQ,CAAA;EACjE;AACF;AAKO,IAAMuB,cAAN,cAA0BzB,UAAAA;EAhOjC,OAgOiCA;;;EAC/B,YAAYC,UAAU,UAAUC,UAA4B,CAAC,GAAG;AAC9D,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAU,GAAGD;IAAQ,CAAA;EACnD;AACF;AAKO,IAAMwB,wBAAN,cAAoC1B,UAAAA;EAzO3C,OAyO2CA;;;EACzC,YAAYC,UAAU,qBAAqBC,UAA4B,CAAC,GAAG;AACzE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAqB,GAAGD;IAAQ,CAAA;EAC9D;AACF;AAKO,IAAMyB,gBAAN,cAA4B3B,UAAAA;EAlPnC,OAkPmCA;;;EACjC,YAAYC,UAAU,aAAaC,UAA4B,CAAC,GAAG;AACjE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAa,GAAGD;IAAQ,CAAA;EACtD;AACF;AAKO,IAAM0B,uBAAN,cAAmC5B,UAAAA;EA3P1C,OA2P0CA;;;EACxC,YAAYC,UAAU,oBAAoBC,UAA4B,CAAC,GAAG;AACxE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAoB,GAAGD;IAAQ,CAAA;EAC7D;AACF;AAKO,IAAM2B,4BAAN,cAAwC7B,UAAAA;EApQ/C,OAoQ+CA;;;EAC7C,YAAYC,UAAU,yBAAyBC,UAA4B,CAAC,GAAG;AAC7E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAyB,GAAGD;IAAQ,CAAA;EAClE;AACF;AAKO,IAAM4B,uBAAN,cAAmC9B,UAAAA;EA7Q1C,OA6Q0CA;;;EAC/B+B;EAET,YACE9B,UAAU,qBACVC,UAAsD,CAAC,GACvD;AACA,UAAM,KAAKD,SAAS;MAClBE,MAAM;MACNO,SAASR,QAAQ6B,aAAa;QAAEA,YAAY7B,QAAQ6B;MAAW,IAAIC;MACnE,GAAG9B;IACL,CAAA;AACA,SAAK6B,aAAa7B,QAAQ6B;EAC5B;AACF;AAKO,IAAME,mCAAN,cAA+CjC,UAAAA;EAhStD,OAgSsDA;;;EACpD,YAAYC,UAAU,mCAAmCC,UAA4B,CAAC,GAAG;AACvF,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAmC,GAAGD;IAAQ,CAAA;EAC5E;AACF;AAKO,IAAMgC,kCAAN,cAA8ClC,UAAAA;EAzSrD,OAySqDA;;;EACnD,YAAYC,UAAU,iCAAiCC,UAA4B,CAAC,GAAG;AACrF,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAiC,GAAGD;IAAQ,CAAA;EAC1E;AACF;AASO,IAAMiC,sBAAN,cAAkCnC,UAAAA;EAtTzC,OAsTyCA;;;EACvC,YAAYC,UAAU,yBAAyBC,UAA4B,CAAC,GAAG;AAC7E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAyBiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EACjF;AACF;AAKO,IAAMmC,sBAAN,cAAkCrC,UAAAA;EA/TzC,OA+TyCA;;;EACvC,YAAYC,UAAU,mBAAmBC,UAA4B,CAAC,GAAG;AACvE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAmBiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EAC3E;AACF;AAKO,IAAMoC,kBAAN,cAA8BtC,UAAAA;EAxUrC,OAwUqCA;;;EACnC,YAAYC,UAAU,eAAeC,UAA4B,CAAC,GAAG;AACnE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAeiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EACvE;AACF;AAKO,IAAMqC,0BAAN,cAAsCvC,UAAAA;EAjV7C,OAiV6CA;;;EAClC+B;EAET,YACE9B,UAAU,uBACVC,UAAsD,CAAC,GACvD;AACA,UAAM,KAAKD,SAAS;MAClBE,MAAM;MACNiC,QAAQ;MACR,GAAGlC;IACL,CAAA;AACA,SAAK6B,aAAa7B,QAAQ6B;EAC5B;AACF;AAKO,IAAMS,sBAAN,cAAkCxC,UAAAA;EApWzC,OAoWyCA;;;EACvC,YAAYC,UAAU,mBAAmBC,UAA4B,CAAC,GAAG;AACvE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAmBiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EAC3E;AACF;AAKO,IAAMuC,+BAAN,cAA2CzC,UAAAA;EA7WlD,OA6WkDA;;;EAChD,YAAYC,UAAU,8BAA8BC,UAA4B,CAAC,GAAG;AAClF,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAA8BiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EACtF;AACF;AAKO,IAAMwC,6BAAN,cAAyC1C,UAAAA;EAtXhD,OAsXgDA;;;EAC9C,YAAYC,UAAU,2BAA2BC,UAA4B,CAAC,GAAG;AAC/E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAA2BiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EACnF;AACF;AAKO,IAAMyC,2BAAN,cAAuC3C,UAAAA;EA/X9C,OA+X8CA;;;EAC5C,YAAYC,UAAU,wBAAwBC,UAA4B,CAAC,GAAG;AAC5E,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAwBiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EAChF;AACF;AAKO,IAAM0C,oBAAN,cAAgC5C,UAAAA;EAxYvC,OAwYuCA;;;EACrC,YAAYC,UAAU,iBAAiBC,UAA4B,CAAC,GAAG;AACrE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAiBiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EACzE;AACF;AAKO,IAAM2C,mBAAN,cAA+B7C,UAAAA;EAjZtC,OAiZsCA;;;EACpC,YAAYC,UAAU,gBAAgBC,UAA4B,CAAC,GAAG;AACpE,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAgBiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EACxE;AACF;AAKO,IAAM4C,2BAAN,cAAuC9C,UAAAA;EA1Z9C,OA0Z8CA;;;EAC5C,YAAYC,UAAU,mCAAmCC,UAA4B,CAAC,GAAG;AACvF,UAAM,KAAKD,SAAS;MAAEE,MAAM;MAAyBiC,QAAQ;MAAO,GAAGlC;IAAQ,CAAA;EACjF;AACF;;;ACjYO,IAAM6C,kBAAN,MAAMA,yBAAwBC,cAAAA;EA7BrC,OA6BqCA;;;;EAE1BC;EAET,YAAYA,QAA2BC,UAAU,qBAAqB;AACpE,UAAMA,SAAS;MACbC,QAAQ;MACRC,MAAM;MACNC,QAAQ;IACV,CAAA;AAEA,SAAKJ,SAASK,OAAOC,OAAO;SAAIN;KAAO;EACzC;;;;EAKA,OAAOO,UAAUC,MAAcP,SAAiBQ,MAAgC;AAC9E,WAAO,IAAIX,iBAAgB;MAAC;QAAEU;QAAMP;QAASQ;MAAK;KAAE;EACtD;;;;EAKA,OAAOC,WAAWC,QAAiD;AACjE,UAAMX,SAASK,OAAOO,QAAQD,MAAAA,EAAQE,IAAI,CAAC,CAACL,MAAMP,OAAAA,OAAc;MAC9DO;MACAP;IACF,EAAA;AACA,WAAO,IAAIH,iBAAgBE,MAAAA;EAC7B;;;;EAKAc,YAAYN,MAAuB;AACjC,WAAO,KAAKR,OAAOe,KAAK,CAACC,UAAUA,MAAMR,SAASA,IAAAA;EACpD;;;;EAKAS,aAAaT,MAAiC;AAC5C,WAAO,KAAKR,OAAOkB,OAAO,CAACF,UAAUA,MAAMR,SAASA,IAAAA;EACtD;;;;EAKAW,cAAcX,MAAkC;AAC9C,WAAO,KAAKR,OAAOoB,KAAK,CAACJ,UAAUA,MAAMR,SAASA,IAAAA,GAAOP;EAC3D;;;;EAKAoB,eAAuC;AACrC,UAAMC,SAAiC,CAAC;AACxC,eAAWN,SAAS,KAAKhB,QAAQ;AAC/B,UAAI,CAACsB,OAAON,MAAMR,IAAI,GAAG;AACvBc,eAAON,MAAMR,IAAI,IAAIQ,MAAMf;MAC7B;IACF;AACA,WAAOqB;EACT;EAESC,SAAkC;AACzC,WAAO;MACLC,OAAO,KAAKC;MACZxB,SAAS,KAAKA;MACdE,MAAM,KAAKA;MACXD,QAAQ,KAAKA;;MAEbF,QAAQ,KAAKA,OAAOa,IAAI,CAAC,EAAEL,MAAMP,SAASQ,MAAMiB,SAAQ,OAAQ;QAC9DlB;QACAP;QACA,GAAIQ,SAASkB,UAAa;UAAElB;QAAK;QACjC,GAAIiB,aAAaC,UAAa;UAAED;QAAS;MAC3C,EAAA;IACF;EACF;AACF;AAKO,IAAME,qBAAN,cAAiC9B,gBAAAA;EAnHxC,OAmHwCA;;;EACtC,YAAY+B,OAAe;AACzB,UACE;MAAC;QAAErB,MAAMqB;QAAO5B,SAAS,GAAG4B,KAAAA;QAAqBpB,MAAM;MAAW;OAClE,GAAGoB,KAAAA,cAAmB;EAE1B;AACF;AAKO,IAAMC,oBAAN,cAAgChC,gBAAAA;EA/HvC,OA+HuCA;;;EACrC,YAAY+B,OAAeH,UAAkBK,UAAkB;AAC7D,UACE;MACE;QACEvB,MAAMqB;QACN5B,SAAS,YAAYyB,QAAAA,cAAsBK,QAAAA;QAC3CtB,MAAM;QACNiB;QACAK;MACF;OAEF,GAAGF,KAAAA,oBAAyBH,QAAAA,EAAU;EAE1C;AACF;AAKO,IAAMM,uBAAN,cAAmClC,gBAAAA;EAnJ1C,OAmJ0CA;;;EACxC,YAAY+B,OAAeI,KAAcC,KAAc;AACrD,UAAMC,QAAkB,CAAA;AACxB,QAAIF,QAAQN,OAAWQ,OAAMC,KAAK,YAAYH,GAAAA,EAAK;AACnD,QAAIC,QAAQP,OAAWQ,OAAMC,KAAK,WAAWF,GAAAA,EAAK;AAClD,UAAMjC,UAAU,GAAG4B,KAAAA,YAAiBM,MAAME,KAAK,OAAA,CAAA;AAE/C,UAAM;MAAC;QAAE7B,MAAMqB;QAAO5B;QAASQ,MAAM;QAASiB,UAAU;UAAEO;UAAKC;QAAI;MAAE;OAAIjC,OAAAA;EAC3E;AACF;AAKO,IAAMqC,cAAN,cAA0BxC,gBAAAA;EAjKjC,OAiKiCA;;;EAC/B,YAAY+B,OAAeI,KAAcC,KAAc;AACrD,UAAMC,QAAkB,CAAA;AACxB,QAAIF,QAAQN,OAAWQ,OAAMC,KAAK,YAAYH,GAAAA,aAAgB;AAC9D,QAAIC,QAAQP,OAAWQ,OAAMC,KAAK,WAAWF,GAAAA,aAAgB;AAC7D,UAAMjC,UAAU,GAAG4B,KAAAA,YAAiBM,MAAME,KAAK,OAAA,CAAA;AAE/C,UAAM;MAAC;QAAE7B,MAAMqB;QAAO5B;QAASQ,MAAM;QAAUiB,UAAU;UAAEO;UAAKC;QAAI;MAAE;OAAIjC,OAAAA;EAC5E;AACF;AAKO,IAAMsC,eAAN,cAA2BzC,gBAAAA;EA/KlC,OA+KkCA;;;EAChC,YAAY+B,OAAeW,SAAiBvC,SAAkB;AAC5D,UACE;MACE;QACEO,MAAMqB;QACN5B,SAASA,WAAW,GAAG4B,KAAAA;QACvBpB,MAAM;QACNiB,UAAUc;MACZ;OAEFvC,WAAW,GAAG4B,KAAAA,kCAAuC;EAEzD;AACF;AAKO,IAAMY,oBAAN,cAAgC3C,gBAAAA;EAlMvC,OAkMuCA;;;EACrC,YAAY+B,QAAQ,SAAS;AAC3B,UACE;MAAC;QAAErB,MAAMqB;QAAO5B,SAAS;QAAyBQ,MAAM;MAAQ;OAChE,uBAAA;EAEJ;AACF;AAKO,IAAMiC,kBAAN,cAA8B5C,gBAAAA;EA9MrC,OA8MqCA;;;EACnC,YAAY+B,QAAQ,OAAO;AACzB,UAAM;MAAC;QAAErB,MAAMqB;QAAO5B,SAAS;QAAeQ,MAAM;MAAM;OAAI,aAAA;EAChE;AACF;;;ACtJA,IAAMkC,YAA6F;EACjG,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;EACL,KAAKC;AACP;AAKO,SAASC,YACdC,QACAC,SACAC,SAA0B;AAG1B,MAAIF,WAAW,KAAK;AAClB,WAAO,IAAIG,sBAAsB,CAAA,GAAIF,SAASC,OAAAA;EAChD;AAEA,QAAME,aAAa5C,UAAUwC,MAAAA;AAE7B,MAAII,YAAY;AACd,WAAO,IAAIA,WAAWH,SAASC,OAAAA;EACjC;AAGA,SAAO,IAAIG,UAAUL,QAAQC,SAASC,OAAAA;AACxC;AAlBgBH;AAuBT,SAASO,WAAWL,SAAkBC,SAA0B;AACrE,SAAO,IAAIzC,gBAAgBwC,SAASC,OAAAA;AACtC;AAFgBI;AAOT,SAASC,aAAaN,SAAkBC,SAA0B;AACvE,SAAO,IAAIxC,kBAAkBuC,SAASC,OAAAA;AACxC;AAFgBK;AAOT,SAASC,UAAUP,SAAkBC,SAA0B;AACpE,SAAO,IAAItC,eAAeqC,SAASC,OAAAA;AACrC;AAFgBM;AAOT,SAASC,SAASR,SAAkBC,SAA0B;AACnE,SAAO,IAAIrC,cAAcoC,SAASC,OAAAA;AACpC;AAFgBO;AAOT,SAASC,iBACdC,gBACAV,SACAC,SAA0B;AAE1B,SAAO,IAAIC,sBAAsBQ,gBAAgBV,SAASC,OAAAA;AAC5D;AANgBQ;AAWT,SAASE,SAASX,SAAkBC,SAA0B;AACnE,SAAO,IAAIjC,cAAcgC,SAASC,OAAAA;AACpC;AAFgBU;AAOT,SAASC,oBACdZ,SACAC,SAA0B;AAE1B,SAAO,IAAIvB,yBAAyBsB,SAASC,OAAAA;AAC/C;AALgBW;AAUT,SAASC,gBACdb,SACAC,SAAoD;AAEpD,SAAO,IAAIjB,qBAAqBgB,SAASC,OAAAA;AAC3C;AALgBY;AAUT,SAASC,cAAcd,SAAkBC,SAA0B;AACxE,SAAO,IAAId,oBAAoBa,SAASC,OAAAA;AAC1C;AAFgBa;AAOT,SAASC,WAAWf,SAAkBC,SAA0B;AACrE,SAAO,IAAIZ,gBAAgBW,SAASC,OAAAA;AACtC;AAFgBc;AAOT,SAASC,mBACdhB,SACAC,SAAoD;AAEpD,SAAO,IAAIX,wBAAwBU,SAASC,OAAAA;AAC9C;AALgBe;AAUT,SAASC,eAAejB,SAAkBC,SAA0B;AACzE,SAAO,IAAIV,oBAAoBS,SAASC,OAAAA;AAC1C;AAFgBgB;AAOT,SAASC,YAAYC,OAAc;AACxC,SAAOA,iBAAiBf;AAC1B;AAFgBc;AAUT,SAASE,eAAeD,OAAc;AAC3C,MAAIA,iBAAiBE,eAAe;AAClC,WAAOF,MAAMpB;EACf;AACA,MACEoB,SAAS,QACT,OAAOA,UAAU,YACjB,YAAYA,SACZ,OAAQA,MAA8BpB,WAAW,UACjD;AACA,UAAMA,SAAUoB,MAA6BpB;AAC7C,WAAOA,UAAU,OAAOA,SAAS,MAAMA,SAAS;EAClD;AACA,SAAO;AACT;AAdgBqB;AAsBT,SAASE,oBAAoBH,OAAc;AAChD,MAAIA,iBAAiBE,iBAAiBF,MAAMI,QAAQ;AAClD,WAAOJ,MAAMnB;EACf;AACA,SAAO;AACT;AALgBsB;;;ACjOhB,SAASE,cAAcC,OAAcC,KAAY;AAC/C,QAAMC,SAASF,iBAAiBG,YAAYH,MAAME,SAAS;AAE3D,MAAIA,UAAU,KAAK;AACjBE,YAAQJ,MAAM,WAAWC,IAAII,MAAM,IAAIJ,IAAIK,IAAI,KAAKN,KAAAA;EACtD,OAAO;AACLI,YAAQG,KAAK,UAAUN,IAAII,MAAM,IAAIJ,IAAIK,IAAI,KAAKN,MAAMQ,OAAO,EAAE;EACnE;AACF;AARST;AAwBF,SAASU,aAAaC,UAA+B,CAAC,GAAC;AAC5D,QAAM,EAAEC,eAAe,OAAOC,SAASb,eAAec,WAAWC,SAAQ,IAAKJ;AAE9E,SAAO,OAAOT,KAAcc,SAAAA;AAC1B,QAAI;AACF,YAAMA,KAAAA;IACR,SAASf,OAAO;AACd,YAAMgB,MAAMhB,iBAAiBiB,QAAQjB,QAAQ,IAAIiB,MAAMC,OAAOlB,KAAAA,CAAAA;AAG9DY,aAAOI,KAAKf,GAAAA;AAGZ,UAAIa,UAAU;AACZ,mBAAW,CAACK,WAAWC,OAAAA,KAAYN,UAAU;AAC3C,cAAIE,eAAeG,WAAW;AAC5BC,oBAAQJ,KAAKf,GAAAA;AACb;UACF;QACF;MACF;AAGA,UAAIC,SAAS;AACb,UAAImB,SAAS;AACb,UAAIC,OAAO;AACX,UAAIC;AAEJ,UAAIP,eAAeb,aAAaa,eAAeQ,eAAe;AAC5DtB,iBAASc,IAAId;AACbmB,iBAASL,IAAIK;AACbC,eAAON,IAAIM;AACXC,kBAAUP,IAAIO;MAChB;AAEAtB,UAAIC,SAASA;AAGb,UAAIuB;AAEJ,UAAIZ,WAAW;AACbY,eAAOZ,UAAUG,KAAKf,GAAAA;MACxB,WAAWe,eAAeQ,eAAe;AAKvCC,eAAOT,IAAIU,OAAM;MACnB,OAAO;AACLD,eAAO;UACLzB,OAAOqB,SAASL,IAAIW,OAAOC,qBAAqB1B,MAAAA;UAChDM,SAASa,SAASL,IAAIR,UAAU;UAChCc;UACApB;QACF;AAEA,YAAImB,UAAUE,SAAS;AACrBE,eAAKF,UAAUA;QACjB;MACF;AAEA,UAAIZ,gBAAgBK,IAAIa,OAAO;AAC7BJ,aAAKI,QAAQb,IAAIa,MAAMC,MAAM,IAAA,EAAMC,IAAI,CAACC,SAASA,KAAKC,KAAI,CAAA;MAC5D;AAEAhC,UAAIiC,KAAKT,IAAAA;IACX;EACF;AACF;AApEgBhB;AA+ET,SAAS0B,gBAAgB3B,UAAU,aAAW;AACnD,SAAO,OAAOP,KAAcc,SAAAA;AAC1B,UAAMA,KAAAA;AAEN,QAAI,CAACd,IAAImC,aAAanC,IAAIC,WAAW,KAAK;AACxCD,UAAIiC,KAAK;QACPlC,OAAO;QACPQ;QACAc,MAAM;QACNpB,QAAQ;MACV,CAAA;IACF;EACF;AACF;AAbgBiC;","names":["ERROR_CODES","Object","freeze","codeForStatus","status","GENERIC_ERROR_CODE","VALIDATION_ERROR_CODE","V8Error","Error","MAX_CAUSE_DEPTH","serializeCause","cause","seen","depth","undefined","message","String","has","add","err","out","name","code","nested","NextRushError","status","expose","details","requestId","traceId","timestamp","options","Object","freeze","captureStackTrace","toJSON","json","error","serialized","Set","fromJSON","hydrate","build","getHttpStatusMessage","toResponse","body","HTTP_STATUS_MESSAGES","HttpError","codeForStatus","BadRequestError","HttpError","message","options","code","UnauthorizedError","PaymentRequiredError","ForbiddenError","NotFoundError","MethodNotAllowedError","allowedMethods","details","NotAcceptableError","ProxyAuthRequiredError","RequestTimeoutError","ConflictError","GoneError","LengthRequiredError","PreconditionFailedError","PayloadTooLargeError","UriTooLongError","UnsupportedMediaTypeError","RangeNotSatisfiableError","ExpectationFailedError","ImATeapotError","UnprocessableEntityError","LockedError","FailedDependencyError","TooEarlyError","UpgradeRequiredError","PreconditionRequiredError","TooManyRequestsError","retryAfter","undefined","RequestHeaderFieldsTooLargeError","UnavailableForLegalReasonsError","InternalServerError","expose","NotImplementedError","BadGatewayError","ServiceUnavailableError","GatewayTimeoutError","HttpVersionNotSupportedError","VariantAlsoNegotiatesError","InsufficientStorageError","LoopDetectedError","NotExtendedError","NetworkAuthRequiredError","ValidationError","NextRushError","issues","message","status","code","expose","Object","freeze","fromField","path","rule","fromFields","errors","entries","map","hasErrorFor","some","issue","getErrorsFor","filter","getFirstError","find","toFlatObject","result","toJSON","error","name","expected","undefined","RequiredFieldError","field","TypeMismatchError","received","RangeValidationError","min","max","parts","push","join","LengthError","PatternError","pattern","InvalidEmailError","InvalidUrlError","ERROR_MAP","BadRequestError","UnauthorizedError","PaymentRequiredError","ForbiddenError","NotFoundError","NotAcceptableError","ProxyAuthRequiredError","RequestTimeoutError","ConflictError","GoneError","LengthRequiredError","PreconditionFailedError","PayloadTooLargeError","UriTooLongError","UnsupportedMediaTypeError","RangeNotSatisfiableError","ExpectationFailedError","ImATeapotError","UnprocessableEntityError","LockedError","FailedDependencyError","TooEarlyError","UpgradeRequiredError","PreconditionRequiredError","TooManyRequestsError","RequestHeaderFieldsTooLargeError","UnavailableForLegalReasonsError","InternalServerError","NotImplementedError","BadGatewayError","ServiceUnavailableError","GatewayTimeoutError","HttpVersionNotSupportedError","VariantAlsoNegotiatesError","InsufficientStorageError","LoopDetectedError","NotExtendedError","NetworkAuthRequiredError","createError","status","message","options","MethodNotAllowedError","ErrorClass","HttpError","badRequest","unauthorized","forbidden","notFound","methodNotAllowed","allowedMethods","conflict","unprocessableEntity","tooManyRequests","internalError","badGateway","serviceUnavailable","gatewayTimeout","isHttpError","error","getErrorStatus","NextRushError","getSafeErrorMessage","expose","defaultLogger","error","ctx","status","HttpError","console","method","path","warn","message","errorHandler","options","includeStack","logger","transform","handlers","next","err","Error","String","ErrorType","handler","expose","code","details","NextRushError","body","toJSON","name","getHttpStatusMessage","stack","split","map","line","trim","json","notFoundHandler","responded"]}
|
|
1
|
+
{"version":3,"sources":["../src/codes.ts","../src/base.ts","../src/header-validation.ts","../src/http-errors.ts","../src/validation.ts","../src/factory.ts","../src/middleware.ts"],"sourcesContent":["/**\n * @nextrush/errors - Central Error Code Registry\n *\n * Single source of truth mapping HTTP status codes to their canonical,\n * machine-readable error codes (audit E-4). Both the typed error classes and\n * the {@link createError} factory resolve codes through this registry, so a\n * given status maps to exactly one code regardless of construction path.\n *\n * @packageDocumentation\n */\n\n/**\n * Canonical error code for each supported HTTP status.\n *\n * @remarks\n * Keep this in sync with the typed classes in `http-errors.ts`. The\n * `audit-fixes` test suite asserts `createError(status).code === ERROR_CODES[status]`\n * for every entry, so drift between a class and this registry fails CI.\n */\nexport const ERROR_CODES: Readonly<Record<number, string>> = Object.freeze({\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 402: 'PAYMENT_REQUIRED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 405: 'METHOD_NOT_ALLOWED',\n 406: 'NOT_ACCEPTABLE',\n 407: 'PROXY_AUTH_REQUIRED',\n 408: 'REQUEST_TIMEOUT',\n 409: 'CONFLICT',\n 410: 'GONE',\n 411: 'LENGTH_REQUIRED',\n 412: 'PRECONDITION_FAILED',\n 413: 'PAYLOAD_TOO_LARGE',\n 414: 'URI_TOO_LONG',\n 415: 'UNSUPPORTED_MEDIA_TYPE',\n 416: 'RANGE_NOT_SATISFIABLE',\n 417: 'EXPECTATION_FAILED',\n 418: 'IM_A_TEAPOT',\n 422: 'UNPROCESSABLE_ENTITY',\n 423: 'LOCKED',\n 424: 'FAILED_DEPENDENCY',\n 425: 'TOO_EARLY',\n 426: 'UPGRADE_REQUIRED',\n 428: 'PRECONDITION_REQUIRED',\n 429: 'TOO_MANY_REQUESTS',\n 431: 'REQUEST_HEADER_FIELDS_TOO_LARGE',\n 451: 'UNAVAILABLE_FOR_LEGAL_REASONS',\n 500: 'INTERNAL_SERVER_ERROR',\n 501: 'NOT_IMPLEMENTED',\n 502: 'BAD_GATEWAY',\n 503: 'SERVICE_UNAVAILABLE',\n 504: 'GATEWAY_TIMEOUT',\n 505: 'HTTP_VERSION_NOT_SUPPORTED',\n 506: 'VARIANT_ALSO_NEGOTIATES',\n 507: 'INSUFFICIENT_STORAGE',\n 508: 'LOOP_DETECTED',\n 510: 'NOT_EXTENDED',\n 511: 'NETWORK_AUTH_REQUIRED',\n});\n\n/**\n * Resolve the canonical error code for an HTTP status.\n *\n * @param status - HTTP status code.\n * @returns The registered canonical code, or `HTTP_<status>` for statuses with\n * no dedicated class.\n */\nexport function codeForStatus(status: number): string {\n return ERROR_CODES[status] ?? `HTTP_${status}`;\n}\n\n/** Generic internal-error code used when no status-specific code applies. */\nexport const GENERIC_ERROR_CODE = 'INTERNAL_ERROR';\n\n/** Canonical code for validation failures. */\nexport const VALIDATION_ERROR_CODE = 'VALIDATION_ERROR';\n","/**\n * @nextrush/errors - Base Error Classes\n *\n * Foundational error classes for NextRush framework.\n *\n * @packageDocumentation\n */\n\nimport { codeForStatus } from './codes';\n\n/** Options accepted by {@link NextRushError.hydrate} when reconstructing errors. */\ninterface HydrateOptions {\n status?: number;\n code?: string;\n details?: Record<string, unknown>;\n requestId?: string;\n traceId?: string;\n timestamp?: string;\n}\n\nconst V8Error = Error as ErrorConstructor & {\n captureStackTrace?: (targetObject: object, constructorOpt?: Function) => void;\n};\n\n/** Maximum depth walked when serializing a `cause` chain (audit E-2). */\nconst MAX_CAUSE_DEPTH = 5;\n\n/**\n * Serialize an error `cause` chain into a plain, JSON-safe object.\n *\n * @remarks\n * Walks nested `cause` links up to {@link MAX_CAUSE_DEPTH}, guards against\n * cyclic chains via a visited set, and only surfaces `name`/`message`/`code`\n * so no unexpected enumerable properties (or secrets on ad-hoc error objects)\n * leak. Returns `undefined` when there is nothing meaningful to serialize.\n */\nfunction serializeCause(cause: unknown, seen: Set<unknown>, depth: number): unknown {\n if (cause === undefined || cause === null || depth >= MAX_CAUSE_DEPTH) return undefined;\n if (typeof cause !== 'object') return { message: String(cause) };\n if (seen.has(cause)) return undefined; // cycle guard\n seen.add(cause);\n\n const err = cause as { name?: unknown; message?: unknown; code?: unknown; cause?: unknown };\n const out: Record<string, unknown> = {};\n if (typeof err.name === 'string') out.name = err.name;\n if (typeof err.message === 'string') out.message = err.message;\n if (typeof err.code === 'string') out.code = err.code;\n\n const nested = serializeCause(err.cause, seen, depth + 1);\n if (nested !== undefined) out.cause = nested;\n\n return out;\n}\n\n/**\n * Base error class for all NextRush errors\n */\nexport class NextRushError extends Error {\n /** HTTP status code */\n readonly status: number;\n\n /** Error code for programmatic handling */\n readonly code: string;\n\n /** Whether error message is safe to expose to client */\n readonly expose: boolean;\n\n /** Additional error details */\n readonly details?: Record<string, unknown>;\n\n /** Original error that caused this error */\n readonly cause?: unknown;\n\n /** Correlation/request identifier for tracing across boundaries (audit E-5). */\n readonly requestId?: string;\n\n /** Distributed-trace identifier (audit E-5). */\n readonly traceId?: string;\n\n /** ISO-8601 timestamp of when the error was created, when supplied. */\n readonly timestamp?: string;\n\n constructor(\n message: string,\n options: {\n status?: number;\n code?: string;\n expose?: boolean;\n details?: Record<string, unknown>;\n cause?: unknown;\n requestId?: string;\n traceId?: string;\n timestamp?: string;\n } = {}\n ) {\n // Pass cause to the native Error constructor so `util.inspect` / logging\n // tooling walks the chain natively, in addition to our own `cause` field.\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = this.constructor.name;\n this.status = options.status ?? 500;\n this.code = options.code ?? 'INTERNAL_ERROR';\n this.expose = options.expose ?? this.status < 500;\n // Freeze a shallow snapshot so the error owns immutable details and the\n // caller's object cannot be mutated through the error (audit E-6).\n this.details = options.details ? Object.freeze({ ...options.details }) : undefined;\n this.cause = options.cause;\n this.requestId = options.requestId;\n this.traceId = options.traceId;\n this.timestamp = options.timestamp;\n\n // Maintain proper stack trace for V8\n // Skip for common client errors (4xx with expose=true) to reduce overhead.\n // These are expected control-flow errors, not bugs — stack traces add cost\n // without diagnostic value in production.\n if (V8Error.captureStackTrace && !(this.expose && this.status < 500)) {\n V8Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Convert error to JSON representation\n */\n toJSON(): Record<string, unknown> {\n const json: Record<string, unknown> = {\n error: this.name,\n message: this.expose ? this.message : 'Internal Server Error',\n code: this.code,\n status: this.status,\n };\n\n if (this.expose && this.details) {\n json.details = this.details;\n }\n\n // Surface the cause chain for diagnosability, but only on exposed errors —\n // a non-exposed 5xx cause may contain internal detail (audit E-2). Server\n // -side logging should read `error.cause` directly, which is never gated.\n if (this.expose && this.cause !== undefined) {\n const serialized = serializeCause(this.cause, new Set(), 0);\n if (serialized !== undefined) {\n json.cause = serialized;\n }\n }\n\n // Correlation identifiers are safe to surface and are the primary handle\n // for cross-service debugging (audit E-5). Only emitted when set, so the\n // default serialized shape is unchanged.\n if (this.requestId !== undefined) json.requestId = this.requestId;\n if (this.traceId !== undefined) json.traceId = this.traceId;\n if (this.timestamp !== undefined) json.timestamp = this.timestamp;\n\n return json;\n }\n\n /**\n * Reconstruct a {@link NextRushError} from a serialized {@link toJSON} payload\n * (audit E-7).\n *\n * @remarks\n * Enables cross-service transport: a downstream service can rebuild a typed\n * error (with a working `instanceof NextRushError`) from the JSON it received,\n * rather than being left with an opaque plain object. `message` falls back to\n * a generic string when the source error was not exposed.\n *\n * @param json - A payload previously produced by {@link toJSON}.\n * @returns A reconstructed {@link NextRushError}.\n */\n static fromJSON(json: Record<string, unknown>): NextRushError {\n return NextRushError.hydrate(json, (message, options) => new NextRushError(message, options));\n }\n\n /**\n * Shared hydration logic for {@link fromJSON} across the hierarchy.\n * @internal\n */\n protected static hydrate<T extends NextRushError>(\n json: Record<string, unknown>,\n build: (message: string, options: HydrateOptions) => T\n ): T {\n const status = typeof json.status === 'number' ? json.status : 500;\n const message = typeof json.message === 'string' ? json.message : getHttpStatusMessage(status);\n return build(message, {\n status,\n code: typeof json.code === 'string' ? json.code : undefined,\n details:\n json.details !== null && typeof json.details === 'object'\n ? (json.details as Record<string, unknown>)\n : undefined,\n requestId: typeof json.requestId === 'string' ? json.requestId : undefined,\n traceId: typeof json.traceId === 'string' ? json.traceId : undefined,\n timestamp: typeof json.timestamp === 'string' ? json.timestamp : undefined,\n });\n }\n\n /**\n * Create a response-safe version of the error\n */\n toResponse(): { status: number; body: Record<string, unknown> } {\n return {\n status: this.status,\n body: this.toJSON(),\n };\n }\n}\n\n/**\n * Default HTTP status messages\n */\nconst HTTP_STATUS_MESSAGES: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 402: 'Payment Required',\n 403: 'Forbidden',\n 404: 'Not Found',\n 405: 'Method Not Allowed',\n 406: 'Not Acceptable',\n 407: 'Proxy Authentication Required',\n 408: 'Request Timeout',\n 409: 'Conflict',\n 410: 'Gone',\n 411: 'Length Required',\n 412: 'Precondition Failed',\n 413: 'Payload Too Large',\n 414: 'URI Too Long',\n 415: 'Unsupported Media Type',\n 416: 'Range Not Satisfiable',\n 417: 'Expectation Failed',\n 418: \"I'm a Teapot\",\n 422: 'Unprocessable Entity',\n 423: 'Locked',\n 424: 'Failed Dependency',\n 425: 'Too Early',\n 426: 'Upgrade Required',\n 428: 'Precondition Required',\n 429: 'Too Many Requests',\n 431: 'Request Header Fields Too Large',\n 451: 'Unavailable For Legal Reasons',\n 500: 'Internal Server Error',\n 501: 'Not Implemented',\n 502: 'Bad Gateway',\n 503: 'Service Unavailable',\n 504: 'Gateway Timeout',\n 505: 'HTTP Version Not Supported',\n 506: 'Variant Also Negotiates',\n 507: 'Insufficient Storage',\n 508: 'Loop Detected',\n 510: 'Not Extended',\n 511: 'Network Authentication Required',\n};\n\n/**\n * Get default message for an HTTP status code\n */\nexport function getHttpStatusMessage(status: number): string {\n return HTTP_STATUS_MESSAGES[status] ?? `HTTP Error ${status}`;\n}\n\n/**\n * Base class for HTTP errors\n */\nexport class HttpError extends NextRushError {\n constructor(\n status: number,\n message?: string,\n options: {\n code?: string;\n expose?: boolean;\n details?: Record<string, unknown>;\n cause?: unknown;\n requestId?: string;\n traceId?: string;\n timestamp?: string;\n } = {}\n ) {\n super(message ?? getHttpStatusMessage(status), {\n status,\n code: options.code ?? codeForStatus(status),\n expose: options.expose ?? status < 500,\n details: options.details,\n cause: options.cause,\n requestId: options.requestId,\n traceId: options.traceId,\n timestamp: options.timestamp,\n });\n }\n\n /**\n * Reconstruct an {@link HttpError} from a serialized {@link NextRushError.toJSON}\n * payload (audit E-7).\n *\n * @param json - A payload previously produced by `toJSON()`.\n * @returns A reconstructed {@link HttpError}.\n */\n static override fromJSON(json: Record<string, unknown>): HttpError {\n return NextRushError.hydrate(\n json,\n (message, options) =>\n new HttpError(options.status ?? 500, message, {\n code: options.code,\n details: options.details,\n requestId: options.requestId,\n traceId: options.traceId,\n timestamp: options.timestamp,\n })\n );\n }\n}\n","/**\n * @nextrush/errors - Header Validation Error\n *\n * @packageDocumentation\n */\n\nimport { NextRushError } from './base';\n\n/**\n * Thrown when a header field name or value fails RFC 9110 grammar\n * validation (field-name token grammar, field-value grammar — no control\n * characters, no leading/trailing whitespace, no obs-fold).\n *\n * @remarks\n * A rejected write here means the application (or a framework internal) is\n * constructing an invalid header, not that a client sent bad input — so this\n * is a 500-class programming error, not a validation-issue-list shape like\n * {@link ValidationError}.\n */\nexport class HeaderValidationError extends NextRushError {\n constructor(message: string) {\n super(message, { status: 500, code: 'HEADER_VALIDATION_ERROR', expose: false });\n }\n}\n","/**\n * @nextrush/errors - HTTP Error Classes\n *\n * Standard HTTP error classes for common status codes.\n *\n * @packageDocumentation\n */\n\nimport { HttpError } from './base';\n\n/**\n * Options for creating HTTP errors\n */\nexport interface HttpErrorOptions {\n code?: string;\n expose?: boolean;\n details?: Record<string, unknown>;\n cause?: unknown;\n /** Correlation/request identifier (audit E-5). */\n requestId?: string;\n /** Distributed-trace identifier (audit E-5). */\n traceId?: string;\n /** ISO-8601 creation timestamp (audit E-5). */\n timestamp?: string;\n}\n\n// =============================================================================\n// 4xx Client Errors\n// =============================================================================\n\n/**\n * 400 Bad Request - Invalid syntax or malformed request\n */\nexport class BadRequestError extends HttpError {\n constructor(message = 'Bad Request', options: HttpErrorOptions = {}) {\n super(400, message, { code: 'BAD_REQUEST', ...options });\n }\n}\n\n/**\n * 401 Unauthorized - Authentication required\n */\nexport class UnauthorizedError extends HttpError {\n constructor(message = 'Unauthorized', options: HttpErrorOptions = {}) {\n super(401, message, { code: 'UNAUTHORIZED', ...options });\n }\n}\n\n/**\n * 402 Payment Required - Payment needed\n */\nexport class PaymentRequiredError extends HttpError {\n constructor(message = 'Payment Required', options: HttpErrorOptions = {}) {\n super(402, message, { code: 'PAYMENT_REQUIRED', ...options });\n }\n}\n\n/**\n * 403 Forbidden - Access denied\n */\nexport class ForbiddenError extends HttpError {\n constructor(message = 'Forbidden', options: HttpErrorOptions = {}) {\n super(403, message, { code: 'FORBIDDEN', ...options });\n }\n}\n\n/**\n * 404 Not Found - Resource not found\n */\nexport class NotFoundError extends HttpError {\n constructor(message = 'Not Found', options: HttpErrorOptions = {}) {\n super(404, message, { code: 'NOT_FOUND', ...options });\n }\n}\n\n/**\n * 405 Method Not Allowed - HTTP method not supported\n */\nexport class MethodNotAllowedError extends HttpError {\n readonly allowedMethods: string[];\n\n constructor(\n allowedMethods: string[] = [],\n message = 'Method Not Allowed',\n options: HttpErrorOptions = {}\n ) {\n super(405, message, {\n code: 'METHOD_NOT_ALLOWED',\n details: { allowedMethods },\n ...options,\n });\n this.allowedMethods = allowedMethods;\n }\n}\n\n/**\n * 406 Not Acceptable - Cannot produce acceptable response\n */\nexport class NotAcceptableError extends HttpError {\n constructor(message = 'Not Acceptable', options: HttpErrorOptions = {}) {\n super(406, message, { code: 'NOT_ACCEPTABLE', ...options });\n }\n}\n\n/**\n * 407 Proxy Authentication Required\n */\nexport class ProxyAuthRequiredError extends HttpError {\n constructor(message = 'Proxy Authentication Required', options: HttpErrorOptions = {}) {\n super(407, message, { code: 'PROXY_AUTH_REQUIRED', ...options });\n }\n}\n\n/**\n * 408 Request Timeout - Client took too long\n */\nexport class RequestTimeoutError extends HttpError {\n constructor(message = 'Request Timeout', options: HttpErrorOptions = {}) {\n super(408, message, { code: 'REQUEST_TIMEOUT', ...options });\n }\n}\n\n/**\n * 409 Conflict - Request conflicts with current state\n */\nexport class ConflictError extends HttpError {\n constructor(message = 'Conflict', options: HttpErrorOptions = {}) {\n super(409, message, { code: 'CONFLICT', ...options });\n }\n}\n\n/**\n * 410 Gone - Resource permanently deleted\n */\nexport class GoneError extends HttpError {\n constructor(message = 'Gone', options: HttpErrorOptions = {}) {\n super(410, message, { code: 'GONE', ...options });\n }\n}\n\n/**\n * 411 Length Required - Content-Length header required\n */\nexport class LengthRequiredError extends HttpError {\n constructor(message = 'Length Required', options: HttpErrorOptions = {}) {\n super(411, message, { code: 'LENGTH_REQUIRED', ...options });\n }\n}\n\n/**\n * 412 Precondition Failed - Precondition in headers not met\n */\nexport class PreconditionFailedError extends HttpError {\n constructor(message = 'Precondition Failed', options: HttpErrorOptions = {}) {\n super(412, message, { code: 'PRECONDITION_FAILED', ...options });\n }\n}\n\n/**\n * 413 Payload Too Large - Request body too large\n */\nexport class PayloadTooLargeError extends HttpError {\n constructor(message = 'Payload Too Large', options: HttpErrorOptions = {}) {\n super(413, message, { code: 'PAYLOAD_TOO_LARGE', ...options });\n }\n}\n\n/**\n * 414 URI Too Long - Request URI too long\n */\nexport class UriTooLongError extends HttpError {\n constructor(message = 'URI Too Long', options: HttpErrorOptions = {}) {\n super(414, message, { code: 'URI_TOO_LONG', ...options });\n }\n}\n\n/**\n * 415 Unsupported Media Type - Content type not supported\n */\nexport class UnsupportedMediaTypeError extends HttpError {\n constructor(message = 'Unsupported Media Type', options: HttpErrorOptions = {}) {\n super(415, message, { code: 'UNSUPPORTED_MEDIA_TYPE', ...options });\n }\n}\n\n/**\n * 416 Range Not Satisfiable - Cannot satisfy Range header\n */\nexport class RangeNotSatisfiableError extends HttpError {\n constructor(message = 'Range Not Satisfiable', options: HttpErrorOptions = {}) {\n super(416, message, { code: 'RANGE_NOT_SATISFIABLE', ...options });\n }\n}\n\n/**\n * 417 Expectation Failed - Cannot meet Expect header\n */\nexport class ExpectationFailedError extends HttpError {\n constructor(message = 'Expectation Failed', options: HttpErrorOptions = {}) {\n super(417, message, { code: 'EXPECTATION_FAILED', ...options });\n }\n}\n\n/**\n * 418 I'm a Teapot - RFC 2324\n */\nexport class ImATeapotError extends HttpError {\n constructor(message = \"I'm a teapot\", options: HttpErrorOptions = {}) {\n super(418, message, { code: 'IM_A_TEAPOT', ...options });\n }\n}\n\n/**\n * 422 Unprocessable Entity - Semantic errors\n */\nexport class UnprocessableEntityError extends HttpError {\n constructor(message = 'Unprocessable Entity', options: HttpErrorOptions = {}) {\n super(422, message, { code: 'UNPROCESSABLE_ENTITY', ...options });\n }\n}\n\n/**\n * 423 Locked - Resource is locked\n */\nexport class LockedError extends HttpError {\n constructor(message = 'Locked', options: HttpErrorOptions = {}) {\n super(423, message, { code: 'LOCKED', ...options });\n }\n}\n\n/**\n * 424 Failed Dependency - Dependent request failed\n */\nexport class FailedDependencyError extends HttpError {\n constructor(message = 'Failed Dependency', options: HttpErrorOptions = {}) {\n super(424, message, { code: 'FAILED_DEPENDENCY', ...options });\n }\n}\n\n/**\n * 425 Too Early - Request replayed too early\n */\nexport class TooEarlyError extends HttpError {\n constructor(message = 'Too Early', options: HttpErrorOptions = {}) {\n super(425, message, { code: 'TOO_EARLY', ...options });\n }\n}\n\n/**\n * 426 Upgrade Required - Client should upgrade protocol\n */\nexport class UpgradeRequiredError extends HttpError {\n constructor(message = 'Upgrade Required', options: HttpErrorOptions = {}) {\n super(426, message, { code: 'UPGRADE_REQUIRED', ...options });\n }\n}\n\n/**\n * 428 Precondition Required - Origin server requires conditional request\n */\nexport class PreconditionRequiredError extends HttpError {\n constructor(message = 'Precondition Required', options: HttpErrorOptions = {}) {\n super(428, message, { code: 'PRECONDITION_REQUIRED', ...options });\n }\n}\n\n/**\n * 429 Too Many Requests - Rate limit exceeded\n */\nexport class TooManyRequestsError extends HttpError {\n readonly retryAfter?: number;\n\n constructor(\n message = 'Too Many Requests',\n options: HttpErrorOptions & { retryAfter?: number } = {}\n ) {\n super(429, message, {\n code: 'TOO_MANY_REQUESTS',\n details: options.retryAfter ? { retryAfter: options.retryAfter } : undefined,\n ...options,\n });\n this.retryAfter = options.retryAfter;\n }\n}\n\n/**\n * 431 Request Header Fields Too Large\n */\nexport class RequestHeaderFieldsTooLargeError extends HttpError {\n constructor(message = 'Request Header Fields Too Large', options: HttpErrorOptions = {}) {\n super(431, message, { code: 'REQUEST_HEADER_FIELDS_TOO_LARGE', ...options });\n }\n}\n\n/**\n * 451 Unavailable For Legal Reasons\n */\nexport class UnavailableForLegalReasonsError extends HttpError {\n constructor(message = 'Unavailable For Legal Reasons', options: HttpErrorOptions = {}) {\n super(451, message, { code: 'UNAVAILABLE_FOR_LEGAL_REASONS', ...options });\n }\n}\n\n// =============================================================================\n// 5xx Server Errors\n// =============================================================================\n\n/**\n * 500 Internal Server Error - Generic server error\n */\nexport class InternalServerError extends HttpError {\n constructor(message = 'Internal Server Error', options: HttpErrorOptions = {}) {\n super(500, message, { code: 'INTERNAL_SERVER_ERROR', expose: false, ...options });\n }\n}\n\n/**\n * 501 Not Implemented - Feature not implemented\n */\nexport class NotImplementedError extends HttpError {\n constructor(message = 'Not Implemented', options: HttpErrorOptions = {}) {\n super(501, message, { code: 'NOT_IMPLEMENTED', expose: false, ...options });\n }\n}\n\n/**\n * 502 Bad Gateway - Invalid upstream response\n */\nexport class BadGatewayError extends HttpError {\n constructor(message = 'Bad Gateway', options: HttpErrorOptions = {}) {\n super(502, message, { code: 'BAD_GATEWAY', expose: false, ...options });\n }\n}\n\n/**\n * 503 Service Unavailable - Server temporarily unavailable\n */\nexport class ServiceUnavailableError extends HttpError {\n readonly retryAfter?: number;\n\n constructor(\n message = 'Service Unavailable',\n options: HttpErrorOptions & { retryAfter?: number } = {}\n ) {\n super(503, message, {\n code: 'SERVICE_UNAVAILABLE',\n expose: false,\n ...options,\n });\n this.retryAfter = options.retryAfter;\n }\n}\n\n/**\n * 504 Gateway Timeout - Upstream timeout\n */\nexport class GatewayTimeoutError extends HttpError {\n constructor(message = 'Gateway Timeout', options: HttpErrorOptions = {}) {\n super(504, message, { code: 'GATEWAY_TIMEOUT', expose: false, ...options });\n }\n}\n\n/**\n * 505 HTTP Version Not Supported\n */\nexport class HttpVersionNotSupportedError extends HttpError {\n constructor(message = 'HTTP Version Not Supported', options: HttpErrorOptions = {}) {\n super(505, message, { code: 'HTTP_VERSION_NOT_SUPPORTED', expose: false, ...options });\n }\n}\n\n/**\n * 506 Variant Also Negotiates\n */\nexport class VariantAlsoNegotiatesError extends HttpError {\n constructor(message = 'Variant Also Negotiates', options: HttpErrorOptions = {}) {\n super(506, message, { code: 'VARIANT_ALSO_NEGOTIATES', expose: false, ...options });\n }\n}\n\n/**\n * 507 Insufficient Storage\n */\nexport class InsufficientStorageError extends HttpError {\n constructor(message = 'Insufficient Storage', options: HttpErrorOptions = {}) {\n super(507, message, { code: 'INSUFFICIENT_STORAGE', expose: false, ...options });\n }\n}\n\n/**\n * 508 Loop Detected\n */\nexport class LoopDetectedError extends HttpError {\n constructor(message = 'Loop Detected', options: HttpErrorOptions = {}) {\n super(508, message, { code: 'LOOP_DETECTED', expose: false, ...options });\n }\n}\n\n/**\n * 510 Not Extended\n */\nexport class NotExtendedError extends HttpError {\n constructor(message = 'Not Extended', options: HttpErrorOptions = {}) {\n super(510, message, { code: 'NOT_EXTENDED', expose: false, ...options });\n }\n}\n\n/**\n * 511 Network Authentication Required\n */\nexport class NetworkAuthRequiredError extends HttpError {\n constructor(message = 'Network Authentication Required', options: HttpErrorOptions = {}) {\n super(511, message, { code: 'NETWORK_AUTH_REQUIRED', expose: false, ...options });\n }\n}\n","/**\n * @nextrush/errors - Validation Error Classes\n *\n * Specialized errors for input validation.\n *\n * @packageDocumentation\n */\n\nimport { NextRushError } from './base';\n\n/**\n * Single validation issue\n */\nexport interface ValidationIssue {\n /** Field path (e.g., 'user.email' or 'items[0].name') */\n path: string;\n /** Error message for this field */\n message: string;\n /** Validation rule that failed */\n rule?: string;\n /** Expected value or constraint */\n expected?: unknown;\n /** Actual value received */\n received?: unknown;\n}\n\n/**\n * Validation error with multiple issues\n */\nexport class ValidationError extends NextRushError {\n /** List of validation issues */\n readonly issues: ValidationIssue[];\n\n constructor(issues: ValidationIssue[], message = 'Validation failed') {\n super(message, {\n status: 400,\n code: 'VALIDATION_ERROR',\n expose: true,\n });\n // Freeze a snapshot so issues are immutable after construction (audit E-6).\n this.issues = Object.freeze([...issues]) as ValidationIssue[];\n }\n\n /**\n * Create from a single field error\n */\n static fromField(path: string, message: string, rule?: string): ValidationError {\n return new ValidationError([{ path, message, rule }]);\n }\n\n /**\n * Create from multiple field errors\n */\n static fromFields(errors: Record<string, string>): ValidationError {\n const issues = Object.entries(errors).map(([path, message]) => ({\n path,\n message,\n }));\n return new ValidationError(issues);\n }\n\n /**\n * Check if a specific field has errors\n */\n hasErrorFor(path: string): boolean {\n return this.issues.some((issue) => issue.path === path);\n }\n\n /**\n * Get errors for a specific field\n */\n getErrorsFor(path: string): ValidationIssue[] {\n return this.issues.filter((issue) => issue.path === path);\n }\n\n /**\n * Get first error message for a field\n */\n getFirstError(path: string): string | undefined {\n return this.issues.find((issue) => issue.path === path)?.message;\n }\n\n /**\n * Convert to flat error object\n */\n toFlatObject(): Record<string, string> {\n const result: Record<string, string> = {};\n for (const issue of this.issues) {\n if (!result[issue.path]) {\n result[issue.path] = issue.message;\n }\n }\n return result;\n }\n\n override toJSON(): Record<string, unknown> {\n return {\n error: this.name,\n message: this.message,\n code: this.code,\n status: this.status,\n // Strip `received` to prevent leaking sensitive input values (passwords, tokens)\n issues: this.issues.map(({ path, message, rule, expected }) => ({\n path,\n message,\n ...(rule !== undefined && { rule }),\n ...(expected !== undefined && { expected }),\n })),\n };\n }\n}\n\n/**\n * Required field missing\n */\nexport class RequiredFieldError extends ValidationError {\n constructor(field: string) {\n super(\n [{ path: field, message: `${field} is required`, rule: 'required' }],\n `${field} is required`\n );\n }\n}\n\n/**\n * Field type mismatch\n */\nexport class TypeMismatchError extends ValidationError {\n constructor(field: string, expected: string, received: string) {\n super(\n [\n {\n path: field,\n message: `Expected ${expected}, received ${received}`,\n rule: 'type',\n expected,\n received,\n },\n ],\n `${field} must be of type ${expected}`\n );\n }\n}\n\n/**\n * Value out of range\n */\nexport class RangeValidationError extends ValidationError {\n constructor(field: string, min?: number, max?: number) {\n const parts: string[] = [];\n if (min !== undefined) parts.push(`at least ${min}`);\n if (max !== undefined) parts.push(`at most ${max}`);\n const message = `${field} must be ${parts.join(' and ')}`;\n\n super([{ path: field, message, rule: 'range', expected: { min, max } }], message);\n }\n}\n\n/**\n * String length violation\n */\nexport class LengthError extends ValidationError {\n constructor(field: string, min?: number, max?: number) {\n const parts: string[] = [];\n if (min !== undefined) parts.push(`at least ${min} characters`);\n if (max !== undefined) parts.push(`at most ${max} characters`);\n const message = `${field} must be ${parts.join(' and ')}`;\n\n super([{ path: field, message, rule: 'length', expected: { min, max } }], message);\n }\n}\n\n/**\n * Pattern mismatch\n */\nexport class PatternError extends ValidationError {\n constructor(field: string, pattern: string, message?: string) {\n super(\n [\n {\n path: field,\n message: message ?? `${field} does not match required pattern`,\n rule: 'pattern',\n expected: pattern,\n },\n ],\n message ?? `${field} does not match required pattern`\n );\n }\n}\n\n/**\n * Invalid email format\n */\nexport class InvalidEmailError extends ValidationError {\n constructor(field = 'email') {\n super(\n [{ path: field, message: 'Invalid email address', rule: 'email' }],\n 'Invalid email address'\n );\n }\n}\n\n/**\n * Invalid URL format\n */\nexport class InvalidUrlError extends ValidationError {\n constructor(field = 'url') {\n super([{ path: field, message: 'Invalid URL', rule: 'url' }], 'Invalid URL');\n }\n}\n","/**\n * @nextrush/errors - Error Factory\n *\n * Factory functions for creating errors.\n *\n * @packageDocumentation\n */\n\nimport { HttpError, NextRushError } from './base';\nimport {\n BadGatewayError,\n BadRequestError,\n ConflictError,\n ExpectationFailedError,\n FailedDependencyError,\n ForbiddenError,\n GatewayTimeoutError,\n GoneError,\n HttpVersionNotSupportedError,\n ImATeapotError,\n InsufficientStorageError,\n InternalServerError,\n LengthRequiredError,\n LockedError,\n LoopDetectedError,\n MethodNotAllowedError,\n NetworkAuthRequiredError,\n NotAcceptableError,\n NotExtendedError,\n NotFoundError,\n NotImplementedError,\n PayloadTooLargeError,\n PaymentRequiredError,\n PreconditionFailedError,\n PreconditionRequiredError,\n ProxyAuthRequiredError,\n RangeNotSatisfiableError,\n RequestHeaderFieldsTooLargeError,\n RequestTimeoutError,\n ServiceUnavailableError,\n TooEarlyError,\n TooManyRequestsError,\n UnauthorizedError,\n UnavailableForLegalReasonsError,\n UnprocessableEntityError,\n UnsupportedMediaTypeError,\n UpgradeRequiredError,\n UriTooLongError,\n VariantAlsoNegotiatesError,\n type HttpErrorOptions,\n} from './http-errors';\n\n/**\n * HTTP status code to error class mapping.\n *\n * @remarks\n * Covers every status that has a dedicated typed class so {@link createError}\n * returns the correctly-coded instance (audit E-3). `405` is handled separately\n * because {@link MethodNotAllowedError} has a divergent constructor signature.\n */\nconst ERROR_MAP: Record<number, new (message?: string, options?: HttpErrorOptions) => HttpError> = {\n 400: BadRequestError,\n 401: UnauthorizedError,\n 402: PaymentRequiredError,\n 403: ForbiddenError,\n 404: NotFoundError,\n 406: NotAcceptableError,\n 407: ProxyAuthRequiredError,\n 408: RequestTimeoutError,\n 409: ConflictError,\n 410: GoneError,\n 411: LengthRequiredError,\n 412: PreconditionFailedError,\n 413: PayloadTooLargeError,\n 414: UriTooLongError,\n 415: UnsupportedMediaTypeError,\n 416: RangeNotSatisfiableError,\n 417: ExpectationFailedError,\n 418: ImATeapotError,\n 422: UnprocessableEntityError,\n 423: LockedError,\n 424: FailedDependencyError,\n 425: TooEarlyError,\n 426: UpgradeRequiredError,\n 428: PreconditionRequiredError,\n 429: TooManyRequestsError,\n 431: RequestHeaderFieldsTooLargeError,\n 451: UnavailableForLegalReasonsError,\n 500: InternalServerError,\n 501: NotImplementedError,\n 502: BadGatewayError,\n 503: ServiceUnavailableError,\n 504: GatewayTimeoutError,\n 505: HttpVersionNotSupportedError,\n 506: VariantAlsoNegotiatesError,\n 507: InsufficientStorageError,\n 508: LoopDetectedError,\n 510: NotExtendedError,\n 511: NetworkAuthRequiredError,\n};\n\n/**\n * Create an HTTP error by status code\n */\nexport function createError(\n status: number,\n message?: string,\n options?: HttpErrorOptions\n): HttpError {\n // Special case: MethodNotAllowedError requires allowedMethods array\n if (status === 405) {\n return new MethodNotAllowedError([], message, options);\n }\n\n const ErrorClass = ERROR_MAP[status];\n\n if (ErrorClass) {\n return new ErrorClass(message, options);\n }\n\n // Fallback: let HttpError constructor resolve the message via getHttpStatusMessage()\n return new HttpError(status, message, options);\n}\n\n/**\n * Create a 400 Bad Request error\n */\nexport function badRequest(message?: string, options?: HttpErrorOptions): BadRequestError {\n return new BadRequestError(message, options);\n}\n\n/**\n * Create a 401 Unauthorized error\n */\nexport function unauthorized(message?: string, options?: HttpErrorOptions): UnauthorizedError {\n return new UnauthorizedError(message, options);\n}\n\n/**\n * Create a 403 Forbidden error\n */\nexport function forbidden(message?: string, options?: HttpErrorOptions): ForbiddenError {\n return new ForbiddenError(message, options);\n}\n\n/**\n * Create a 404 Not Found error\n */\nexport function notFound(message?: string, options?: HttpErrorOptions): NotFoundError {\n return new NotFoundError(message, options);\n}\n\n/**\n * Create a 405 Method Not Allowed error\n */\nexport function methodNotAllowed(\n allowedMethods?: string[],\n message?: string,\n options?: HttpErrorOptions\n): MethodNotAllowedError {\n return new MethodNotAllowedError(allowedMethods, message, options);\n}\n\n/**\n * Create a 409 Conflict error\n */\nexport function conflict(message?: string, options?: HttpErrorOptions): ConflictError {\n return new ConflictError(message, options);\n}\n\n/**\n * Create a 422 Unprocessable Entity error\n */\nexport function unprocessableEntity(\n message?: string,\n options?: HttpErrorOptions\n): UnprocessableEntityError {\n return new UnprocessableEntityError(message, options);\n}\n\n/**\n * Create a 429 Too Many Requests error\n */\nexport function tooManyRequests(\n message?: string,\n options?: HttpErrorOptions & { retryAfter?: number }\n): TooManyRequestsError {\n return new TooManyRequestsError(message, options);\n}\n\n/**\n * Create a 500 Internal Server Error\n */\nexport function internalError(message?: string, options?: HttpErrorOptions): InternalServerError {\n return new InternalServerError(message, options);\n}\n\n/**\n * Create a 502 Bad Gateway error\n */\nexport function badGateway(message?: string, options?: HttpErrorOptions): BadGatewayError {\n return new BadGatewayError(message, options);\n}\n\n/**\n * Create a 503 Service Unavailable error\n */\nexport function serviceUnavailable(\n message?: string,\n options?: HttpErrorOptions & { retryAfter?: number }\n): ServiceUnavailableError {\n return new ServiceUnavailableError(message, options);\n}\n\n/**\n * Create a 504 Gateway Timeout error\n */\nexport function gatewayTimeout(message?: string, options?: HttpErrorOptions): GatewayTimeoutError {\n return new GatewayTimeoutError(message, options);\n}\n\n/**\n * Check if an error is a NextRush HttpError\n */\nexport function isHttpError(error: unknown): error is HttpError {\n return error instanceof HttpError;\n}\n\n/**\n * Get HTTP status from any error\n *\n * Checks NextRushError first (covers HttpError, ValidationError, and all subclasses),\n * then falls back to duck-typed status property.\n */\nexport function getErrorStatus(error: unknown): number {\n if (error instanceof NextRushError) {\n return error.status;\n }\n if (\n error != null &&\n typeof error === 'object' &&\n 'status' in error &&\n typeof (error as { status: unknown }).status === 'number'\n ) {\n const status = (error as { status: number }).status;\n return status >= 400 && status < 600 ? status : 500;\n }\n return 500;\n}\n\n/**\n * Get error message safe for client response\n *\n * Exposes message for any NextRushError (including ValidationError)\n * when the error's expose flag is true.\n */\nexport function getSafeErrorMessage(error: unknown): string {\n if (error instanceof NextRushError && error.expose) {\n return error.message;\n }\n return 'Internal Server Error';\n}\n","/**\n * @nextrush/errors - Error Handler Middleware\n *\n * Middleware for handling errors in NextRush applications.\n *\n * @packageDocumentation\n */\n\nimport type { Context, Middleware, Next } from '@nextrush/types';\nimport { SECURITY_AUDIT, type SecurityAuditVerdict } from '@nextrush/types';\nimport { HttpError, NextRushError, getHttpStatusMessage } from './base';\n\n/**\n * Error handler options\n */\nexport interface ErrorHandlerOptions {\n /** Include stack trace in development */\n includeStack?: boolean;\n\n /**\n * Whether the application is running in production (SEC-14).\n *\n * @remarks\n * `@nextrush/errors` has no access to `app.isProduction` on its own — the\n * caller threads it through explicitly, mirroring `@nextrush/core`'s\n * `writeDefaultErrorResponse(opts.isProduction)`. When `true`, a truthy\n * `includeStack` is ignored (fail closed) and a single warning is logged\n * once per process, rather than once per request. Defaults to `false` so\n * existing callers that don't pass this option keep today's behavior.\n *\n * @default false\n */\n isProduction?: boolean;\n\n /** Custom error logger */\n logger?: (error: Error, ctx: Context) => void;\n\n /** Custom error transformer */\n transform?: (error: Error, ctx: Context) => Record<string, unknown>;\n\n /** Handle specific error types */\n handlers?: Map<new (...args: unknown[]) => Error, (error: Error, ctx: Context) => void>;\n}\n\n/**\n * Default error logger\n */\nfunction defaultLogger(error: Error, ctx: Context): void {\n const status = error instanceof HttpError ? error.status : 500;\n\n if (status >= 500) {\n console.error(`[ERROR] ${ctx.method} ${ctx.path}:`, error);\n } else {\n console.warn(`[WARN] ${ctx.method} ${ctx.path}: ${error.message}`);\n }\n}\n\n/**\n * Create error handler middleware\n *\n * @example\n * ```typescript\n * const app = createApp();\n *\n * // Add error handler first\n * app.use(errorHandler({\n * includeStack: process.env.NODE_ENV !== 'production',\n * logger: (err, ctx) => myLogger.error(err),\n * }));\n * ```\n */\nexport function errorHandler(options: ErrorHandlerOptions = {}): Middleware {\n const { includeStack = false, isProduction = false, logger = defaultLogger, transform, handlers } = options;\n\n // SEC-14: warn once per process, not once per request — a per-request\n // warning on a hot error path floods logs for no added value once the\n // misconfiguration is known.\n let warnedIncludeStackInProduction = false;\n\n const handler: Middleware = async (ctx: Context, next: Next): Promise<void> => {\n try {\n await next();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Log the error\n logger(err, ctx);\n\n // Check for custom handlers\n if (handlers) {\n for (const [ErrorType, handler] of handlers) {\n if (err instanceof ErrorType) {\n handler(err, ctx);\n return;\n }\n }\n }\n\n // Determine status code\n let status = 500;\n let expose = false;\n let code = 'INTERNAL_ERROR';\n let details: Record<string, unknown> | undefined;\n\n if (err instanceof HttpError || err instanceof NextRushError) {\n status = err.status;\n expose = err.expose;\n code = err.code;\n details = err.details;\n }\n\n ctx.status = status;\n\n // Build response body\n let body: Record<string, unknown>;\n\n if (transform) {\n body = transform(err, ctx);\n } else if (err instanceof NextRushError) {\n // Delegate to the error's own toJSON() — this is the single source of\n // truth for what a given error type serializes to (e.g. ValidationError\n // adds `issues` while stripping `received`). Duplicating that shape here\n // would silently drift out of sync with subclass overrides.\n body = err.toJSON();\n } else {\n body = {\n error: expose ? err.name : getHttpStatusMessage(status),\n message: expose ? err.message : 'Internal Server Error',\n code,\n status,\n };\n\n if (expose && details) {\n body.details = details;\n }\n }\n\n if (includeStack && err.stack) {\n // SEC-14: fail closed — a stack trace is ignored in production\n // regardless of the caller's configuration, since it maps internal\n // paths, package layout, and dependency versions for the client.\n if (isProduction) {\n if (!warnedIncludeStackInProduction) {\n warnedIncludeStackInProduction = true;\n console.warn(\n '[@nextrush/errors] includeStack: true was ignored because isProduction is true. ' +\n 'Stack traces are never emitted in production regardless of configuration.'\n );\n }\n } else {\n body.stack = err.stack.split('\\n').map((line) => line.trim());\n }\n }\n\n ctx.json(body);\n }\n };\n\n /**\n * Boot-time verdict for `includeStack: true` (task 8.1): the per-request\n * guard (SEC-14, line ~140 above) only fires when `isProduction` is\n * explicitly passed to this call — an app that never threads\n * `app.options.env` through never gets that guard's warning even in a real\n * production deployment. This is the gap the boot audit closes.\n */\n function auditVerdict(): SecurityAuditVerdict {\n if (includeStack && !isProduction) {\n return {\n level: 'warn',\n message:\n 'errorHandler({ includeStack: true }) was constructed without isProduction — the ' +\n 'per-request guard that ignores includeStack in production never fires unless ' +\n \"isProduction is explicitly threaded through (e.g. isProduction: app.isProduction). \" +\n 'Wire it, or this instance will leak stack traces in production.',\n };\n }\n return { level: 'ok' };\n }\n\n Object.defineProperty(handler, SECURITY_AUDIT, {\n value: auditVerdict,\n enumerable: false,\n });\n\n return handler;\n}\n\n/**\n * Not found handler middleware - catches unhandled requests\n *\n * @example\n * ```typescript\n * // Add at the end of middleware chain\n * app.use(notFoundHandler());\n * ```\n */\nexport function notFoundHandler(message = 'Not Found'): Middleware {\n return async (ctx: Context, next: Next): Promise<void> => {\n await next();\n // Only handle if no response was sent and status indicates unhandled\n if (!ctx.responded && ctx.status === 404) {\n ctx.json({\n error: 'NotFoundError',\n message,\n code: 'NOT_FOUND',\n status: 404,\n });\n }\n };\n}\n\n"],"mappings":"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","names":["ERROR_CODES","Object","freeze","codeForStatus","status","GENERIC_ERROR_CODE","VALIDATION_ERROR_CODE","V8Error","Error","MAX_CAUSE_DEPTH","serializeCause","cause","seen","depth","undefined","message","String","has","add","err","out","name","code","nested","NextRushError","status","expose","details","requestId","traceId","timestamp","options","Object","freeze","captureStackTrace","toJSON","json","error","serialized","Set","fromJSON","hydrate","build","getHttpStatusMessage","toResponse","body","HTTP_STATUS_MESSAGES","HttpError","codeForStatus","HeaderValidationError","NextRushError","message","status","code","expose","BadRequestError","HttpError","message","options","code","UnauthorizedError","PaymentRequiredError","ForbiddenError","NotFoundError","MethodNotAllowedError","allowedMethods","details","NotAcceptableError","ProxyAuthRequiredError","RequestTimeoutError","ConflictError","GoneError","LengthRequiredError","PreconditionFailedError","PayloadTooLargeError","UriTooLongError","UnsupportedMediaTypeError","RangeNotSatisfiableError","ExpectationFailedError","ImATeapotError","UnprocessableEntityError","LockedError","FailedDependencyError","TooEarlyError","UpgradeRequiredError","PreconditionRequiredError","TooManyRequestsError","retryAfter","undefined","RequestHeaderFieldsTooLargeError","UnavailableForLegalReasonsError","InternalServerError","expose","NotImplementedError","BadGatewayError","ServiceUnavailableError","GatewayTimeoutError","HttpVersionNotSupportedError","VariantAlsoNegotiatesError","InsufficientStorageError","LoopDetectedError","NotExtendedError","NetworkAuthRequiredError","ValidationError","NextRushError","issues","message","status","code","expose","Object","freeze","fromField","path","rule","fromFields","errors","entries","map","hasErrorFor","some","issue","getErrorsFor","filter","getFirstError","find","toFlatObject","result","toJSON","error","name","expected","undefined","RequiredFieldError","field","TypeMismatchError","received","RangeValidationError","min","max","parts","push","join","LengthError","PatternError","pattern","InvalidEmailError","InvalidUrlError","ERROR_MAP","BadRequestError","UnauthorizedError","PaymentRequiredError","ForbiddenError","NotFoundError","NotAcceptableError","ProxyAuthRequiredError","RequestTimeoutError","ConflictError","GoneError","LengthRequiredError","PreconditionFailedError","PayloadTooLargeError","UriTooLongError","UnsupportedMediaTypeError","RangeNotSatisfiableError","ExpectationFailedError","ImATeapotError","UnprocessableEntityError","LockedError","FailedDependencyError","TooEarlyError","UpgradeRequiredError","PreconditionRequiredError","TooManyRequestsError","RequestHeaderFieldsTooLargeError","UnavailableForLegalReasonsError","InternalServerError","NotImplementedError","BadGatewayError","ServiceUnavailableError","GatewayTimeoutError","HttpVersionNotSupportedError","VariantAlsoNegotiatesError","InsufficientStorageError","LoopDetectedError","NotExtendedError","NetworkAuthRequiredError","createError","status","message","options","MethodNotAllowedError","ErrorClass","HttpError","badRequest","unauthorized","forbidden","notFound","methodNotAllowed","allowedMethods","conflict","unprocessableEntity","tooManyRequests","internalError","badGateway","serviceUnavailable","gatewayTimeout","isHttpError","error","getErrorStatus","NextRushError","getSafeErrorMessage","expose","SECURITY_AUDIT","defaultLogger","error","ctx","status","HttpError","console","method","path","warn","message","errorHandler","options","includeStack","isProduction","logger","transform","handlers","warnedIncludeStackInProduction","handler","next","err","Error","String","ErrorType","expose","code","details","NextRushError","body","toJSON","name","getHttpStatusMessage","stack","split","map","line","trim","json","auditVerdict","level","Object","defineProperty","SECURITY_AUDIT","value","enumerable","notFoundHandler","responded"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextrush/errors",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.2",
|
|
4
4
|
"description": "Standardized error handling for NextRush",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"README.md"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@nextrush/types": "4.0.0-beta.
|
|
21
|
+
"@nextrush/types": "4.0.0-beta.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"tsup": "^8.5.1",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nextrush/errors - HeaderValidationError tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
import { NextRushError } from '../base';
|
|
7
|
+
import { HeaderValidationError } from '../header-validation';
|
|
8
|
+
|
|
9
|
+
describe('HeaderValidationError', () => {
|
|
10
|
+
it('carries the given message', () => {
|
|
11
|
+
const error = new HeaderValidationError('Header field contains invalid characters');
|
|
12
|
+
|
|
13
|
+
expect(error.message).toBe('Header field contains invalid characters');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('is a 500-class, non-exposed error with the HEADER_VALIDATION_ERROR code', () => {
|
|
17
|
+
const error = new HeaderValidationError('bad header');
|
|
18
|
+
|
|
19
|
+
expect(error.status).toBe(500);
|
|
20
|
+
expect(error.code).toBe('HEADER_VALIDATION_ERROR');
|
|
21
|
+
expect(error.expose).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('is an instanceof NextRushError and Error', () => {
|
|
25
|
+
const error = new HeaderValidationError('bad header');
|
|
26
|
+
|
|
27
|
+
expect(error).toBeInstanceOf(NextRushError);
|
|
28
|
+
expect(error).toBeInstanceOf(Error);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('sets name to HeaderValidationError', () => {
|
|
32
|
+
const error = new HeaderValidationError('bad header');
|
|
33
|
+
|
|
34
|
+
expect(error.name).toBe('HeaderValidationError');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('does not expose the raw message via toJSON, matching a non-exposed error contract', () => {
|
|
38
|
+
const error = new HeaderValidationError('internal detail: X-Bad\\r\\nInjected');
|
|
39
|
+
const json = error.toJSON();
|
|
40
|
+
|
|
41
|
+
expect(json.message).not.toBe('internal detail: X-Bad\\r\\nInjected');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -120,6 +120,88 @@ describe('errorHandler', () => {
|
|
|
120
120
|
const jsonCall = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
121
121
|
expect(jsonCall.stack).toBeUndefined();
|
|
122
122
|
});
|
|
123
|
+
|
|
124
|
+
// SEC-14: includeStack: true must be a no-op in production, not honored
|
|
125
|
+
// unconditionally — a stack trace is a map of internal paths/dependency
|
|
126
|
+
// versions, and the previous behavior handed that map to any client the
|
|
127
|
+
// moment a deploy forgot to flip includeStack off.
|
|
128
|
+
describe('production guard (SEC-14)', () => {
|
|
129
|
+
it('ignores includeStack: true in production and logs exactly one warning', async () => {
|
|
130
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
131
|
+
const handler = errorHandler({ includeStack: true, isProduction: true });
|
|
132
|
+
const ctx = createMockContext();
|
|
133
|
+
|
|
134
|
+
await handler(ctx, async () => {
|
|
135
|
+
throw new BadRequestError('Invalid');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const jsonCall = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
139
|
+
expect(jsonCall.stack).toBeUndefined();
|
|
140
|
+
// The default 4xx logger also calls console.warn — isolate the
|
|
141
|
+
// SEC-14 guard's own warning by content rather than call count.
|
|
142
|
+
const sec14Warnings = warnSpy.mock.calls.filter((call) =>
|
|
143
|
+
String(call[0]).includes('includeStack')
|
|
144
|
+
);
|
|
145
|
+
expect(sec14Warnings).toHaveLength(1);
|
|
146
|
+
warnSpy.mockRestore();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('warns only once across multiple requests in production, not once per request', async () => {
|
|
150
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
151
|
+
const handler = errorHandler({ includeStack: true, isProduction: true });
|
|
152
|
+
|
|
153
|
+
for (let i = 0; i < 3; i++) {
|
|
154
|
+
const ctx = createMockContext();
|
|
155
|
+
await handler(ctx, async () => {
|
|
156
|
+
throw new BadRequestError('Invalid');
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const sec14Warnings = warnSpy.mock.calls.filter((call) =>
|
|
161
|
+
String(call[0]).includes('includeStack')
|
|
162
|
+
);
|
|
163
|
+
expect(sec14Warnings).toHaveLength(1);
|
|
164
|
+
warnSpy.mockRestore();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('preserves development behavior — stack is present when isProduction is false', async () => {
|
|
168
|
+
const handler = errorHandler({ includeStack: true, isProduction: false });
|
|
169
|
+
const ctx = createMockContext();
|
|
170
|
+
|
|
171
|
+
await handler(ctx, async () => {
|
|
172
|
+
throw new BadRequestError('Invalid');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const jsonCall = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
176
|
+
expect(jsonCall.stack).toBeDefined();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('preserves development behavior when isProduction is omitted (default)', async () => {
|
|
180
|
+
const handler = errorHandler({ includeStack: true });
|
|
181
|
+
const ctx = createMockContext();
|
|
182
|
+
|
|
183
|
+
await handler(ctx, async () => {
|
|
184
|
+
throw new BadRequestError('Invalid');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const jsonCall = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
188
|
+
expect(jsonCall.stack).toBeDefined();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('a plain (non-HttpError) Error never exposes its message in production, with or without includeStack', async () => {
|
|
192
|
+
const handler = errorHandler({ includeStack: true, isProduction: true });
|
|
193
|
+
const ctx = createMockContext();
|
|
194
|
+
|
|
195
|
+
await handler(ctx, async () => {
|
|
196
|
+
throw new Error('internal db connection string: postgres://secret');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const jsonCall = (ctx.json as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
200
|
+
expect(jsonCall.stack).toBeUndefined();
|
|
201
|
+
expect(JSON.stringify(jsonCall)).not.toContain('postgres://secret');
|
|
202
|
+
expect(jsonCall.message).toBe('Internal Server Error');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
123
205
|
});
|
|
124
206
|
|
|
125
207
|
describe('logger option', () => {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nextrush/errors - Boot-time security audit contribution (task 8.1/8.2)
|
|
3
|
+
*
|
|
4
|
+
* `errorHandler()` tags its returned middleware with a {@link SECURITY_AUDIT}
|
|
5
|
+
* check so `Application.ready()` warns, in production, when `includeStack:
|
|
6
|
+
* true` was configured but `isProduction` was never wired to this instance —
|
|
7
|
+
* the per-request guard (task 7.8) only fires when the caller explicitly
|
|
8
|
+
* passes `isProduction: true`, so an app that forgets to thread it never
|
|
9
|
+
* gets the per-request warning even in a real production deployment. The
|
|
10
|
+
* boot audit catches exactly that omission.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, expect, it } from 'vitest';
|
|
13
|
+
import { SECURITY_AUDIT, type SecurityAuditCheck } from '@nextrush/types';
|
|
14
|
+
import { errorHandler } from '../middleware';
|
|
15
|
+
|
|
16
|
+
function auditOf(mw: unknown): SecurityAuditCheck {
|
|
17
|
+
const check = (mw as Record<typeof SECURITY_AUDIT, SecurityAuditCheck>)[SECURITY_AUDIT];
|
|
18
|
+
expect(typeof check).toBe('function');
|
|
19
|
+
return check;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('errorHandler() — security-boundaries boot audit contribution', () => {
|
|
23
|
+
it('reports a warn-level verdict for includeStack: true with isProduction never wired', () => {
|
|
24
|
+
const mw = errorHandler({ includeStack: true });
|
|
25
|
+
const verdict = auditOf(mw)();
|
|
26
|
+
|
|
27
|
+
expect(verdict.level).toBe('warn');
|
|
28
|
+
expect(verdict).toMatchObject({ message: expect.stringContaining('includeStack') });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('reports ok when includeStack: true is paired with an explicit isProduction', () => {
|
|
32
|
+
const mw = errorHandler({ includeStack: true, isProduction: true });
|
|
33
|
+
expect(auditOf(mw)()).toEqual({ level: 'ok' });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('reports ok for the default options (includeStack: false)', () => {
|
|
37
|
+
const mw = errorHandler();
|
|
38
|
+
expect(auditOf(mw)()).toEqual({ level: 'ok' });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nextrush/errors - Header Validation Error
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { NextRushError } from './base';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when a header field name or value fails RFC 9110 grammar
|
|
11
|
+
* validation (field-name token grammar, field-value grammar — no control
|
|
12
|
+
* characters, no leading/trailing whitespace, no obs-fold).
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* A rejected write here means the application (or a framework internal) is
|
|
16
|
+
* constructing an invalid header, not that a client sent bad input — so this
|
|
17
|
+
* is a 500-class programming error, not a validation-issue-list shape like
|
|
18
|
+
* {@link ValidationError}.
|
|
19
|
+
*/
|
|
20
|
+
export class HeaderValidationError extends NextRushError {
|
|
21
|
+
constructor(message: string) {
|
|
22
|
+
super(message, { status: 500, code: 'HEADER_VALIDATION_ERROR', expose: false });
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
// Base classes
|
|
10
10
|
export { HttpError, NextRushError, getHttpStatusMessage } from './base';
|
|
11
11
|
|
|
12
|
+
// Header validation
|
|
13
|
+
export { HeaderValidationError } from './header-validation';
|
|
14
|
+
|
|
12
15
|
// Central error-code registry
|
|
13
16
|
export { ERROR_CODES, GENERIC_ERROR_CODE, VALIDATION_ERROR_CODE, codeForStatus } from './codes';
|
|
14
17
|
|
package/src/middleware.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Context, Middleware, Next } from '@nextrush/types';
|
|
10
|
+
import { SECURITY_AUDIT, type SecurityAuditVerdict } from '@nextrush/types';
|
|
10
11
|
import { HttpError, NextRushError, getHttpStatusMessage } from './base';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -16,6 +17,21 @@ export interface ErrorHandlerOptions {
|
|
|
16
17
|
/** Include stack trace in development */
|
|
17
18
|
includeStack?: boolean;
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Whether the application is running in production (SEC-14).
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* `@nextrush/errors` has no access to `app.isProduction` on its own — the
|
|
25
|
+
* caller threads it through explicitly, mirroring `@nextrush/core`'s
|
|
26
|
+
* `writeDefaultErrorResponse(opts.isProduction)`. When `true`, a truthy
|
|
27
|
+
* `includeStack` is ignored (fail closed) and a single warning is logged
|
|
28
|
+
* once per process, rather than once per request. Defaults to `false` so
|
|
29
|
+
* existing callers that don't pass this option keep today's behavior.
|
|
30
|
+
*
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
isProduction?: boolean;
|
|
34
|
+
|
|
19
35
|
/** Custom error logger */
|
|
20
36
|
logger?: (error: Error, ctx: Context) => void;
|
|
21
37
|
|
|
@@ -54,9 +70,14 @@ function defaultLogger(error: Error, ctx: Context): void {
|
|
|
54
70
|
* ```
|
|
55
71
|
*/
|
|
56
72
|
export function errorHandler(options: ErrorHandlerOptions = {}): Middleware {
|
|
57
|
-
const { includeStack = false, logger = defaultLogger, transform, handlers } = options;
|
|
73
|
+
const { includeStack = false, isProduction = false, logger = defaultLogger, transform, handlers } = options;
|
|
58
74
|
|
|
59
|
-
|
|
75
|
+
// SEC-14: warn once per process, not once per request — a per-request
|
|
76
|
+
// warning on a hot error path floods logs for no added value once the
|
|
77
|
+
// misconfiguration is known.
|
|
78
|
+
let warnedIncludeStackInProduction = false;
|
|
79
|
+
|
|
80
|
+
const handler: Middleware = async (ctx: Context, next: Next): Promise<void> => {
|
|
60
81
|
try {
|
|
61
82
|
await next();
|
|
62
83
|
} catch (error) {
|
|
@@ -115,12 +136,53 @@ export function errorHandler(options: ErrorHandlerOptions = {}): Middleware {
|
|
|
115
136
|
}
|
|
116
137
|
|
|
117
138
|
if (includeStack && err.stack) {
|
|
118
|
-
|
|
139
|
+
// SEC-14: fail closed — a stack trace is ignored in production
|
|
140
|
+
// regardless of the caller's configuration, since it maps internal
|
|
141
|
+
// paths, package layout, and dependency versions for the client.
|
|
142
|
+
if (isProduction) {
|
|
143
|
+
if (!warnedIncludeStackInProduction) {
|
|
144
|
+
warnedIncludeStackInProduction = true;
|
|
145
|
+
console.warn(
|
|
146
|
+
'[@nextrush/errors] includeStack: true was ignored because isProduction is true. ' +
|
|
147
|
+
'Stack traces are never emitted in production regardless of configuration.'
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
body.stack = err.stack.split('\n').map((line) => line.trim());
|
|
152
|
+
}
|
|
119
153
|
}
|
|
120
154
|
|
|
121
155
|
ctx.json(body);
|
|
122
156
|
}
|
|
123
157
|
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Boot-time verdict for `includeStack: true` (task 8.1): the per-request
|
|
161
|
+
* guard (SEC-14, line ~140 above) only fires when `isProduction` is
|
|
162
|
+
* explicitly passed to this call — an app that never threads
|
|
163
|
+
* `app.options.env` through never gets that guard's warning even in a real
|
|
164
|
+
* production deployment. This is the gap the boot audit closes.
|
|
165
|
+
*/
|
|
166
|
+
function auditVerdict(): SecurityAuditVerdict {
|
|
167
|
+
if (includeStack && !isProduction) {
|
|
168
|
+
return {
|
|
169
|
+
level: 'warn',
|
|
170
|
+
message:
|
|
171
|
+
'errorHandler({ includeStack: true }) was constructed without isProduction — the ' +
|
|
172
|
+
'per-request guard that ignores includeStack in production never fires unless ' +
|
|
173
|
+
"isProduction is explicitly threaded through (e.g. isProduction: app.isProduction). " +
|
|
174
|
+
'Wire it, or this instance will leak stack traces in production.',
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return { level: 'ok' };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Object.defineProperty(handler, SECURITY_AUDIT, {
|
|
181
|
+
value: auditVerdict,
|
|
182
|
+
enumerable: false,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return handler;
|
|
124
186
|
}
|
|
125
187
|
|
|
126
188
|
/**
|