@vritti/api-sdk 0.4.3 → 0.4.4
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/auth.cjs +58 -17
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/auth.js +58 -17
- package/dist/auth.js.map +1 -1
- package/dist/cache.cjs +51 -17
- package/dist/cache.cjs.map +1 -1
- package/dist/cache.js +51 -17
- package/dist/cache.js.map +1 -1
- package/dist/catalog-resolver.d.cts +2 -2
- package/dist/catalog-resolver.d.ts +2 -2
- package/dist/{code-pattern-BP4zkNtT.d.cts → code-pattern-BQM7qLcU.d.cts} +1 -1
- package/dist/{code-pattern-BP4zkNtT.d.ts → code-pattern-BQM7qLcU.d.ts} +1 -1
- package/dist/data-table.cjs +153 -47
- package/dist/data-table.cjs.map +1 -1
- package/dist/data-table.js +153 -47
- package/dist/data-table.js.map +1 -1
- package/dist/database.cjs +101 -31
- package/dist/database.cjs.map +1 -1
- package/dist/database.js +101 -31
- package/dist/database.js.map +1 -1
- package/dist/decorators.d.cts +2 -2
- package/dist/decorators.d.ts +2 -2
- package/dist/drizzle-pg-core.d.cts +1 -1
- package/dist/drizzle-pg-core.d.ts +1 -1
- package/dist/email.cjs +22 -6
- package/dist/email.cjs.map +1 -1
- package/dist/email.js +22 -6
- package/dist/email.js.map +1 -1
- package/dist/files.cjs +1 -1
- package/dist/files.cjs.map +1 -1
- package/dist/files.js +1 -1
- package/dist/files.js.map +1 -1
- package/dist/filters.cjs +18 -4
- package/dist/filters.cjs.map +1 -1
- package/dist/filters.js +18 -4
- package/dist/filters.js.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/license.d.cts +1 -1
- package/dist/license.d.ts +1 -1
- package/dist/logger.cjs +49 -15
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.js +49 -15
- package/dist/logger.js.map +1 -1
- package/dist/nats.cjs +49 -17
- package/dist/nats.cjs.map +1 -1
- package/dist/nats.js +49 -17
- package/dist/nats.js.map +1 -1
- package/dist/root.cjs +44 -12
- package/dist/root.cjs.map +1 -1
- package/dist/root.js +44 -12
- package/dist/root.js.map +1 -1
- package/dist/{types-BQY0Aa1p.d.cts → types-VDKSjje5.d.cts} +1 -1
- package/dist/{types-BQY0Aa1p.d.ts → types-VDKSjje5.d.ts} +1 -1
- package/package.json +29 -29
package/dist/files.cjs
CHANGED
|
@@ -115,7 +115,7 @@ var import_common18 = require("@nestjs/common");
|
|
|
115
115
|
var import_common19 = require("@nestjs/common");
|
|
116
116
|
|
|
117
117
|
// src/files/uploaded-file.decorator.ts
|
|
118
|
-
var PARSED = Symbol("vritti.multipart");
|
|
118
|
+
var PARSED = /* @__PURE__ */ Symbol("vritti.multipart");
|
|
119
119
|
function readMultipart(request) {
|
|
120
120
|
const cached = request;
|
|
121
121
|
cached[PARSED] ??= (async () => {
|
package/dist/files.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/files/index.ts","../src/files/uploaded-file.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts"],"sourcesContent":["export {\n type MultipartContent,\n MultipartDto,\n readMultipart,\n UploadedFile,\n type UploadedFileMap,\n type UploadedFileResult,\n UploadedFiles,\n} from './uploaded-file.decorator';\n","import type {} from '@fastify/multipart';\nimport { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { type ClassConstructor, plainToInstance } from 'class-transformer';\nimport { validate } from 'class-validator';\nimport type { FastifyRequest } from 'fastify';\nimport { BadRequestException } from '../exceptions';\n\nexport interface UploadedFileResult {\n buffer: Buffer;\n filename: string;\n mimetype: string;\n}\n\nexport type UploadedFileMap = Record<string, UploadedFileResult[]>;\n\nexport interface MultipartContent {\n fields: Record<string, unknown>;\n files: UploadedFileMap;\n}\n\nconst PARSED = Symbol('vritti.multipart');\n\ntype ParsedRequest = FastifyRequest & { [PARSED]?: Promise<MultipartContent> };\n\n// Reads the whole multipart body once and caches it — the part stream cannot be walked twice\nexport function readMultipart(request: FastifyRequest): Promise<MultipartContent> {\n const cached = request as ParsedRequest;\n cached[PARSED] ??= (async () => {\n const content: MultipartContent = { fields: {}, files: {} };\n for await (const part of request.parts()) {\n if (part.type === 'file') {\n const file = { buffer: await part.toBuffer(), filename: part.filename, mimetype: part.mimetype };\n const existing = content.files[part.fieldname];\n if (existing) existing.push(file);\n else content.files[part.fieldname] = [file];\n } else {\n content.fields[part.fieldname] = part.value;\n }\n }\n return content;\n })();\n return cached[PARSED];\n}\n\nfunction flatten(files: Record<string, UploadedFileResult[]>): UploadedFileResult[] {\n return Object.values(files).flat();\n}\n\n// The single file sent under `fieldName`, or the only file on the request; throws when absent\nexport const UploadedFile = createParamDecorator(\n async (fieldName: string | undefined, ctx: ExecutionContext): Promise<UploadedFileResult> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const file = (fieldName ? files[fieldName] : flatten(files))?.[0];\n\n if (!file) {\n const field = fieldName ?? 'file';\n throw new BadRequestException({\n label: 'File Required',\n detail: `Please attach a file${fieldName ? ` under \"${fieldName}\"` : ''} to your request.`,\n errors: [{ field, message: 'File required' }],\n });\n }\n return file;\n },\n);\n\n// Every file under `fieldName` (throws when none), or with no name every file keyed by field name (never throws)\nexport const UploadedFiles = createParamDecorator(\n async (\n fieldName: string | undefined,\n ctx: ExecutionContext,\n ): Promise<UploadedFileResult[] | Record<string, UploadedFileResult[]>> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n if (!fieldName) return files;\n\n const matched = files[fieldName] ?? [];\n if (matched.length === 0) {\n throw new BadRequestException({\n label: 'Files Required',\n detail: `Please attach at least one file under \"${fieldName}\" to your request.`,\n errors: [{ field: fieldName, message: 'At least one file is required' }],\n });\n }\n return matched;\n },\n);\n\n// The request's text fields validated into `dto`, for a multipart form whose body cannot reach @Body() —\n// fastify only surfaces fields while walking the parts, so request.body is empty without attachFieldsToBody.\nexport const MultipartDto = createParamDecorator(\n async <T extends object>(dtoClass: ClassConstructor<T>, ctx: ExecutionContext): Promise<T> => {\n const { fields } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const dto = plainToInstance(dtoClass, fields);\n const errors = await validate(dto as object);\n\n if (errors.length > 0) {\n throw new BadRequestException({\n label: 'Validation Failed',\n detail: 'One or more fields are invalid.',\n errors: errors.map((e) => ({\n field: e.property,\n message: Object.values(e.constraints ?? {})[0] ?? 'Invalid value',\n })),\n });\n }\n return dto;\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n"],"mappings":"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","names":["import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","import_common","BadRequestException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","PARSED","Symbol","readMultipart","request","cached","content","fields","files","part","parts","type","file","buffer","toBuffer","filename","mimetype","existing","fieldname","push","value","flatten","Object","values","flat","UploadedFile","createParamDecorator","fieldName","ctx","switchToHttp","getRequest","field","BadRequestException","label","detail","errors","message","UploadedFiles","matched","length","MultipartDto","dtoClass","dto","plainToInstance","validate","map","e","property","constraints"]}
|
|
1
|
+
{"version":3,"sources":["../src/files/index.ts","../src/files/uploaded-file.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts"],"sourcesContent":["export {\n type MultipartContent,\n MultipartDto,\n readMultipart,\n UploadedFile,\n type UploadedFileMap,\n type UploadedFileResult,\n UploadedFiles,\n} from './uploaded-file.decorator';\n","import type {} from '@fastify/multipart';\nimport { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { type ClassConstructor, plainToInstance } from 'class-transformer';\nimport { validate } from 'class-validator';\nimport type { FastifyRequest } from 'fastify';\nimport { BadRequestException } from '../exceptions';\n\nexport interface UploadedFileResult {\n buffer: Buffer;\n filename: string;\n mimetype: string;\n}\n\nexport type UploadedFileMap = Record<string, UploadedFileResult[]>;\n\nexport interface MultipartContent {\n fields: Record<string, unknown>;\n files: UploadedFileMap;\n}\n\nconst PARSED = Symbol('vritti.multipart');\n\ntype ParsedRequest = FastifyRequest & { [PARSED]?: Promise<MultipartContent> };\n\n// Reads the whole multipart body once and caches it — the part stream cannot be walked twice\nexport function readMultipart(request: FastifyRequest): Promise<MultipartContent> {\n const cached = request as ParsedRequest;\n cached[PARSED] ??= (async () => {\n const content: MultipartContent = { fields: {}, files: {} };\n for await (const part of request.parts()) {\n if (part.type === 'file') {\n const file = { buffer: await part.toBuffer(), filename: part.filename, mimetype: part.mimetype };\n const existing = content.files[part.fieldname];\n if (existing) existing.push(file);\n else content.files[part.fieldname] = [file];\n } else {\n content.fields[part.fieldname] = part.value;\n }\n }\n return content;\n })();\n return cached[PARSED];\n}\n\nfunction flatten(files: Record<string, UploadedFileResult[]>): UploadedFileResult[] {\n return Object.values(files).flat();\n}\n\n// The single file sent under `fieldName`, or the only file on the request; throws when absent\nexport const UploadedFile = createParamDecorator(\n async (fieldName: string | undefined, ctx: ExecutionContext): Promise<UploadedFileResult> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const file = (fieldName ? files[fieldName] : flatten(files))?.[0];\n\n if (!file) {\n const field = fieldName ?? 'file';\n throw new BadRequestException({\n label: 'File Required',\n detail: `Please attach a file${fieldName ? ` under \"${fieldName}\"` : ''} to your request.`,\n errors: [{ field, message: 'File required' }],\n });\n }\n return file;\n },\n);\n\n// Every file under `fieldName` (throws when none), or with no name every file keyed by field name (never throws)\nexport const UploadedFiles = createParamDecorator(\n async (\n fieldName: string | undefined,\n ctx: ExecutionContext,\n ): Promise<UploadedFileResult[] | Record<string, UploadedFileResult[]>> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n if (!fieldName) return files;\n\n const matched = files[fieldName] ?? [];\n if (matched.length === 0) {\n throw new BadRequestException({\n label: 'Files Required',\n detail: `Please attach at least one file under \"${fieldName}\" to your request.`,\n errors: [{ field: fieldName, message: 'At least one file is required' }],\n });\n }\n return matched;\n },\n);\n\n// The request's text fields validated into `dto`, for a multipart form whose body cannot reach @Body() —\n// fastify only surfaces fields while walking the parts, so request.body is empty without attachFieldsToBody.\nexport const MultipartDto = createParamDecorator(\n async <T extends object>(dtoClass: ClassConstructor<T>, ctx: ExecutionContext): Promise<T> => {\n const { fields } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const dto = plainToInstance(dtoClass, fields);\n const errors = await validate(dto as object);\n\n if (errors.length > 0) {\n throw new BadRequestException({\n label: 'Validation Failed',\n detail: 'One or more fields are invalid.',\n errors: errors.map((e) => ({\n field: e.property,\n message: Object.values(e.constraints ?? {})[0] ?? 'Invalid value',\n })),\n });\n }\n return dto;\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n"],"mappings":"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","names":["import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","import_common","BadRequestException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","import_common","PARSED","Symbol","readMultipart","request","cached","content","fields","files","part","parts","type","file","buffer","toBuffer","filename","mimetype","existing","fieldname","push","value","flatten","Object","values","flat","UploadedFile","createParamDecorator","fieldName","ctx","switchToHttp","getRequest","field","BadRequestException","label","detail","errors","message","UploadedFiles","matched","length","MultipartDto","dtoClass","dto","plainToInstance","validate","map","e","property","constraints"]}
|
package/dist/files.js
CHANGED
|
@@ -88,7 +88,7 @@ import { HttpStatus as HttpStatus17 } from "@nestjs/common";
|
|
|
88
88
|
import { HttpStatus as HttpStatus18 } from "@nestjs/common";
|
|
89
89
|
|
|
90
90
|
// src/files/uploaded-file.decorator.ts
|
|
91
|
-
var PARSED = Symbol("vritti.multipart");
|
|
91
|
+
var PARSED = /* @__PURE__ */ Symbol("vritti.multipart");
|
|
92
92
|
function readMultipart(request) {
|
|
93
93
|
const cached = request;
|
|
94
94
|
cached[PARSED] ??= (async () => {
|
package/dist/files.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/files/uploaded-file.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts"],"sourcesContent":["import type {} from '@fastify/multipart';\nimport { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { type ClassConstructor, plainToInstance } from 'class-transformer';\nimport { validate } from 'class-validator';\nimport type { FastifyRequest } from 'fastify';\nimport { BadRequestException } from '../exceptions';\n\nexport interface UploadedFileResult {\n buffer: Buffer;\n filename: string;\n mimetype: string;\n}\n\nexport type UploadedFileMap = Record<string, UploadedFileResult[]>;\n\nexport interface MultipartContent {\n fields: Record<string, unknown>;\n files: UploadedFileMap;\n}\n\nconst PARSED = Symbol('vritti.multipart');\n\ntype ParsedRequest = FastifyRequest & { [PARSED]?: Promise<MultipartContent> };\n\n// Reads the whole multipart body once and caches it — the part stream cannot be walked twice\nexport function readMultipart(request: FastifyRequest): Promise<MultipartContent> {\n const cached = request as ParsedRequest;\n cached[PARSED] ??= (async () => {\n const content: MultipartContent = { fields: {}, files: {} };\n for await (const part of request.parts()) {\n if (part.type === 'file') {\n const file = { buffer: await part.toBuffer(), filename: part.filename, mimetype: part.mimetype };\n const existing = content.files[part.fieldname];\n if (existing) existing.push(file);\n else content.files[part.fieldname] = [file];\n } else {\n content.fields[part.fieldname] = part.value;\n }\n }\n return content;\n })();\n return cached[PARSED];\n}\n\nfunction flatten(files: Record<string, UploadedFileResult[]>): UploadedFileResult[] {\n return Object.values(files).flat();\n}\n\n// The single file sent under `fieldName`, or the only file on the request; throws when absent\nexport const UploadedFile = createParamDecorator(\n async (fieldName: string | undefined, ctx: ExecutionContext): Promise<UploadedFileResult> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const file = (fieldName ? files[fieldName] : flatten(files))?.[0];\n\n if (!file) {\n const field = fieldName ?? 'file';\n throw new BadRequestException({\n label: 'File Required',\n detail: `Please attach a file${fieldName ? ` under \"${fieldName}\"` : ''} to your request.`,\n errors: [{ field, message: 'File required' }],\n });\n }\n return file;\n },\n);\n\n// Every file under `fieldName` (throws when none), or with no name every file keyed by field name (never throws)\nexport const UploadedFiles = createParamDecorator(\n async (\n fieldName: string | undefined,\n ctx: ExecutionContext,\n ): Promise<UploadedFileResult[] | Record<string, UploadedFileResult[]>> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n if (!fieldName) return files;\n\n const matched = files[fieldName] ?? [];\n if (matched.length === 0) {\n throw new BadRequestException({\n label: 'Files Required',\n detail: `Please attach at least one file under \"${fieldName}\" to your request.`,\n errors: [{ field: fieldName, message: 'At least one file is required' }],\n });\n }\n return matched;\n },\n);\n\n// The request's text fields validated into `dto`, for a multipart form whose body cannot reach @Body() —\n// fastify only surfaces fields while walking the parts, so request.body is empty without attachFieldsToBody.\nexport const MultipartDto = createParamDecorator(\n async <T extends object>(dtoClass: ClassConstructor<T>, ctx: ExecutionContext): Promise<T> => {\n const { fields } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const dto = plainToInstance(dtoClass, fields);\n const errors = await validate(dto as object);\n\n if (errors.length > 0) {\n throw new BadRequestException({\n label: 'Validation Failed',\n detail: 'One or more fields are invalid.',\n errors: errors.map((e) => ({\n field: e.property,\n message: Object.values(e.constraints ?? {})[0] ?? 'Invalid value',\n })),\n });\n }\n return dto;\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n"],"mappings":"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","names":["createParamDecorator","plainToInstance","validate","HttpStatus","HttpException","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","HttpStatus","BadRequestException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","PARSED","Symbol","readMultipart","request","cached","content","fields","files","part","parts","type","file","buffer","toBuffer","filename","mimetype","existing","fieldname","push","value","flatten","Object","values","flat","UploadedFile","createParamDecorator","fieldName","ctx","switchToHttp","getRequest","field","BadRequestException","label","detail","errors","message","UploadedFiles","matched","length","MultipartDto","dtoClass","dto","plainToInstance","validate","map","e","property","constraints"]}
|
|
1
|
+
{"version":3,"sources":["../src/files/uploaded-file.decorator.ts","../src/exceptions/bad-gateway.exception.ts","../src/exceptions/base-field.exception.ts","../src/exceptions/bad-request.exception.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/forbidden.exception.ts","../src/exceptions/gone.exception.ts","../src/exceptions/internal-server-error.exception.ts","../src/exceptions/method-not-allowed.exception.ts","../src/exceptions/not-acceptable.exception.ts","../src/exceptions/not-found.exception.ts","../src/exceptions/not-implemented.exception.ts","../src/exceptions/payload-too-large.exception.ts","../src/exceptions/request-timeout.exception.ts","../src/exceptions/service-unavailable.exception.ts","../src/exceptions/too-many-requests.exception.ts","../src/exceptions/unauthorized.exception.ts","../src/exceptions/unprocessable-entity.exception.ts","../src/exceptions/unsupported-media-type.exception.ts","../src/exceptions/validation.exception.ts"],"sourcesContent":["import type {} from '@fastify/multipart';\nimport { createParamDecorator, type ExecutionContext } from '@nestjs/common';\nimport { type ClassConstructor, plainToInstance } from 'class-transformer';\nimport { validate } from 'class-validator';\nimport type { FastifyRequest } from 'fastify';\nimport { BadRequestException } from '../exceptions';\n\nexport interface UploadedFileResult {\n buffer: Buffer;\n filename: string;\n mimetype: string;\n}\n\nexport type UploadedFileMap = Record<string, UploadedFileResult[]>;\n\nexport interface MultipartContent {\n fields: Record<string, unknown>;\n files: UploadedFileMap;\n}\n\nconst PARSED = Symbol('vritti.multipart');\n\ntype ParsedRequest = FastifyRequest & { [PARSED]?: Promise<MultipartContent> };\n\n// Reads the whole multipart body once and caches it — the part stream cannot be walked twice\nexport function readMultipart(request: FastifyRequest): Promise<MultipartContent> {\n const cached = request as ParsedRequest;\n cached[PARSED] ??= (async () => {\n const content: MultipartContent = { fields: {}, files: {} };\n for await (const part of request.parts()) {\n if (part.type === 'file') {\n const file = { buffer: await part.toBuffer(), filename: part.filename, mimetype: part.mimetype };\n const existing = content.files[part.fieldname];\n if (existing) existing.push(file);\n else content.files[part.fieldname] = [file];\n } else {\n content.fields[part.fieldname] = part.value;\n }\n }\n return content;\n })();\n return cached[PARSED];\n}\n\nfunction flatten(files: Record<string, UploadedFileResult[]>): UploadedFileResult[] {\n return Object.values(files).flat();\n}\n\n// The single file sent under `fieldName`, or the only file on the request; throws when absent\nexport const UploadedFile = createParamDecorator(\n async (fieldName: string | undefined, ctx: ExecutionContext): Promise<UploadedFileResult> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const file = (fieldName ? files[fieldName] : flatten(files))?.[0];\n\n if (!file) {\n const field = fieldName ?? 'file';\n throw new BadRequestException({\n label: 'File Required',\n detail: `Please attach a file${fieldName ? ` under \"${fieldName}\"` : ''} to your request.`,\n errors: [{ field, message: 'File required' }],\n });\n }\n return file;\n },\n);\n\n// Every file under `fieldName` (throws when none), or with no name every file keyed by field name (never throws)\nexport const UploadedFiles = createParamDecorator(\n async (\n fieldName: string | undefined,\n ctx: ExecutionContext,\n ): Promise<UploadedFileResult[] | Record<string, UploadedFileResult[]>> => {\n const { files } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n if (!fieldName) return files;\n\n const matched = files[fieldName] ?? [];\n if (matched.length === 0) {\n throw new BadRequestException({\n label: 'Files Required',\n detail: `Please attach at least one file under \"${fieldName}\" to your request.`,\n errors: [{ field: fieldName, message: 'At least one file is required' }],\n });\n }\n return matched;\n },\n);\n\n// The request's text fields validated into `dto`, for a multipart form whose body cannot reach @Body() —\n// fastify only surfaces fields while walking the parts, so request.body is empty without attachFieldsToBody.\nexport const MultipartDto = createParamDecorator(\n async <T extends object>(dtoClass: ClassConstructor<T>, ctx: ExecutionContext): Promise<T> => {\n const { fields } = await readMultipart(ctx.switchToHttp().getRequest<FastifyRequest>());\n const dto = plainToInstance(dtoClass, fields);\n const errors = await validate(dto as object);\n\n if (errors.length > 0) {\n throw new BadRequestException({\n label: 'Validation Failed',\n detail: 'One or more fields are invalid.',\n errors: errors.map((e) => ({\n field: e.property,\n message: Object.values(e.constraints ?? {})[0] ?? 'Invalid value',\n })),\n });\n }\n return dto;\n },\n);\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadGatewayException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Gateway', HttpStatus.BAD_GATEWAY);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class BadRequestException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Bad Request', HttpStatus.BAD_REQUEST);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ForbiddenException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Forbidden', HttpStatus.FORBIDDEN);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class GoneException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Gone', HttpStatus.GONE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class InternalServerErrorException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Internal Server Error', HttpStatus.INTERNAL_SERVER_ERROR);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class MethodNotAllowedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Method Not Allowed', HttpStatus.METHOD_NOT_ALLOWED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotAcceptableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Acceptable', HttpStatus.NOT_ACCEPTABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotFoundException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Found', HttpStatus.NOT_FOUND);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class NotImplementedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Not Implemented', HttpStatus.NOT_IMPLEMENTED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class PayloadTooLargeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Payload Too Large', HttpStatus.PAYLOAD_TOO_LARGE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class RequestTimeoutException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Request Timeout', HttpStatus.REQUEST_TIMEOUT);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ServiceUnavailableException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Service Unavailable', HttpStatus.SERVICE_UNAVAILABLE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class TooManyRequestsException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Too Many Requests', HttpStatus.TOO_MANY_REQUESTS);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnauthorizedException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unauthorized', HttpStatus.UNAUTHORIZED);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnprocessableEntityException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unprocessable Entity', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class UnsupportedMediaTypeException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Unsupported Media Type', HttpStatus.UNSUPPORTED_MEDIA_TYPE);\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ValidationException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Validation Failed', HttpStatus.UNPROCESSABLE_ENTITY);\n }\n}\n"],"mappings":";;;;AACA,SAASA,4BAAmD;AAC5D,SAAgCC,uBAAuB;AACvD,SAASC,gBAAgB;;;ACHzB,SAASC,kBAAkB;;;ACA3B,SAASC,qBAAiC;AAanC,IAAeC,uBAAf,cAA4CC,cAAAA;EAbnD,OAamDA;;;EACjD,YAAYC,iBAA0CC,YAAwB;AAC5E,UAAMC,UAAU,OAAOF,oBAAoB,WAAW;MAAEG,QAAQH;IAAgB,IAAIA;AAEpF,UACE;MACEI,MAAMF,QAAQE,QAAQ;MACtBC,OAAOH,QAAQG;MACfF,QAAQD,QAAQC;MAChBG,QAAQJ,QAAQI,UAAU,CAAA;IAC5B,GACAL,UAAAA;EAEJ;AACF;;;AC3BA,SAASM,cAAAA,mBAAkB;AAGpB,IAAMC,sBAAN,cAAkCC,qBAAAA;EAHzC,OAGyCA;;;EACvC,YAAYC,iBAA2C;AACrD,UAAMA,mBAAmB,eAAeC,YAAWC,WAAW;EAChE;AACF;;;ACPA,SAASC,cAAAA,mBAAkB;;;ACA3B,SAASC,cAAAA,mBAAkB;;;ACA3B,SAASC,cAAAA,mBAAkB;;;ACA3B,SAASC,cAAAA,mBAAkB;;;ACA3B,SAASC,cAAAA,mBAAkB;;;ACA3B,SAASC,cAAAA,mBAAkB;;;ACA3B,SAASC,cAAAA,mBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;ACA3B,SAASC,cAAAA,oBAAkB;;;AnBoB3B,IAAMC,SAASC,uBAAO,kBAAA;AAKf,SAASC,cAAcC,SAAuB;AACnD,QAAMC,SAASD;AACfC,SAAOJ,MAAAA,OAAa,YAAA;AAClB,UAAMK,UAA4B;MAAEC,QAAQ,CAAC;MAAGC,OAAO,CAAC;IAAE;AAC1D,qBAAiBC,QAAQL,QAAQM,MAAK,GAAI;AACxC,UAAID,KAAKE,SAAS,QAAQ;AACxB,cAAMC,OAAO;UAAEC,QAAQ,MAAMJ,KAAKK,SAAQ;UAAIC,UAAUN,KAAKM;UAAUC,UAAUP,KAAKO;QAAS;AAC/F,cAAMC,WAAWX,QAAQE,MAAMC,KAAKS,SAAS;AAC7C,YAAID,SAAUA,UAASE,KAAKP,IAAAA;YACvBN,SAAQE,MAAMC,KAAKS,SAAS,IAAI;UAACN;;MACxC,OAAO;AACLN,gBAAQC,OAAOE,KAAKS,SAAS,IAAIT,KAAKW;MACxC;IACF;AACA,WAAOd;EACT,GAAA;AACA,SAAOD,OAAOJ,MAAAA;AAChB;AAjBgBE;AAmBhB,SAASkB,QAAQb,OAA2C;AAC1D,SAAOc,OAAOC,OAAOf,KAAAA,EAAOgB,KAAI;AAClC;AAFSH;AAKF,IAAMI,eAAeC,qBAC1B,OAAOC,WAA+BC,QAAAA;AACpC,QAAM,EAAEpB,MAAK,IAAK,MAAML,cAAcyB,IAAIC,aAAY,EAAGC,WAAU,CAAA;AACnE,QAAMlB,QAAQe,YAAYnB,MAAMmB,SAAAA,IAAaN,QAAQb,KAAAA,KAAU,CAAA;AAE/D,MAAI,CAACI,MAAM;AACT,UAAMmB,QAAQJ,aAAa;AAC3B,UAAM,IAAIK,oBAAoB;MAC5BC,OAAO;MACPC,QAAQ,uBAAuBP,YAAY,WAAWA,SAAAA,MAAe,EAAA;MACrEQ,QAAQ;QAAC;UAAEJ;UAAOK,SAAS;QAAgB;;IAC7C,CAAA;EACF;AACA,SAAOxB;AACT,CAAA;AAIK,IAAMyB,gBAAgBX,qBAC3B,OACEC,WACAC,QAAAA;AAEA,QAAM,EAAEpB,MAAK,IAAK,MAAML,cAAcyB,IAAIC,aAAY,EAAGC,WAAU,CAAA;AACnE,MAAI,CAACH,UAAW,QAAOnB;AAEvB,QAAM8B,UAAU9B,MAAMmB,SAAAA,KAAc,CAAA;AACpC,MAAIW,QAAQC,WAAW,GAAG;AACxB,UAAM,IAAIP,oBAAoB;MAC5BC,OAAO;MACPC,QAAQ,0CAA0CP,SAAAA;MAClDQ,QAAQ;QAAC;UAAEJ,OAAOJ;UAAWS,SAAS;QAAgC;;IACxE,CAAA;EACF;AACA,SAAOE;AACT,CAAA;AAKK,IAAME,eAAed,qBAC1B,OAAyBe,UAA+Bb,QAAAA;AACtD,QAAM,EAAErB,OAAM,IAAK,MAAMJ,cAAcyB,IAAIC,aAAY,EAAGC,WAAU,CAAA;AACpE,QAAMY,MAAMC,gBAAgBF,UAAUlC,MAAAA;AACtC,QAAM4B,SAAS,MAAMS,SAASF,GAAAA;AAE9B,MAAIP,OAAOI,SAAS,GAAG;AACrB,UAAM,IAAIP,oBAAoB;MAC5BC,OAAO;MACPC,QAAQ;MACRC,QAAQA,OAAOU,IAAI,CAACC,OAAO;QACzBf,OAAOe,EAAEC;QACTX,SAASd,OAAOC,OAAOuB,EAAEE,eAAe,CAAC,CAAA,EAAG,CAAA,KAAM;MACpD,EAAA;IACF,CAAA;EACF;AACA,SAAON;AACT,CAAA;","names":["createParamDecorator","plainToInstance","validate","HttpStatus","HttpException","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","HttpStatus","BadRequestException","HttpProblemException","detailOrOptions","HttpStatus","BAD_REQUEST","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","HttpStatus","PARSED","Symbol","readMultipart","request","cached","content","fields","files","part","parts","type","file","buffer","toBuffer","filename","mimetype","existing","fieldname","push","value","flatten","Object","values","flat","UploadedFile","createParamDecorator","fieldName","ctx","switchToHttp","getRequest","field","BadRequestException","label","detail","errors","message","UploadedFiles","matched","length","MultipartDto","dtoClass","dto","plainToInstance","validate","map","e","property","constraints"]}
|
package/dist/filters.cjs
CHANGED
|
@@ -291,8 +291,15 @@ __name(findPgError, "findPgError");
|
|
|
291
291
|
// src/filters/http-exception.filter.ts
|
|
292
292
|
function _ts_decorate(decorators, target, key, desc) {
|
|
293
293
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
294
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
295
|
-
|
|
294
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
295
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
296
|
+
} else {
|
|
297
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
298
|
+
if (d = decorators[i]) {
|
|
299
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
296
303
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
297
304
|
}
|
|
298
305
|
__name(_ts_decorate, "_ts_decorate");
|
|
@@ -375,8 +382,15 @@ HttpExceptionFilter = _ts_decorate([
|
|
|
375
382
|
var import_common6 = require("@nestjs/common");
|
|
376
383
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
377
384
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
378
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
379
|
-
|
|
385
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
386
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
387
|
+
} else {
|
|
388
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
389
|
+
if (d = decorators[i]) {
|
|
390
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
380
394
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
381
395
|
}
|
|
382
396
|
__name(_ts_decorate2, "_ts_decorate");
|
package/dist/filters.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/filters/index.ts","../src/filters/error-code.ts","../src/filters/problem-extraction.ts","../src/filters/graphql-format-error.ts","../src/filters/http-exception.filter.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/base-field.exception.ts","../src/filters/pg-error.translator.ts","../src/filters/transport-aware-exception.filter.ts"],"sourcesContent":["// Machine-readable error contract + status mapper (shared across HTTP and GraphQL transports).\n\nexport { ErrorCode, errorCodeFromStatus } from './error-code';\n// GraphQL error shaping (Apollo formatError factory) — composes with TransportAwareExceptionFilter.\nexport {\n createGraphqlFormatError,\n type FormattedErrorExtensions,\n type GraphqlFormatErrorOptions,\n} from './graphql-format-error';\nexport { getHttpStatusTitle, HttpExceptionFilter } from './http-exception.filter';\nexport { TransportAwareExceptionFilter } from './transport-aware-exception.filter';\n// RpcProblemExceptionFilter is exported from '@vritti/api-sdk/nats' (it needs @nestjs/microservices).\n","import { HttpStatus } from '@nestjs/common';\n\nexport enum ErrorCode {\n UNAUTHENTICATED = 'UNAUTHENTICATED',\n FORBIDDEN = 'FORBIDDEN',\n NOT_FOUND = 'NOT_FOUND',\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n CONFLICT = 'CONFLICT',\n RATE_LIMITED = 'RATE_LIMITED',\n BAD_REQUEST = 'BAD_REQUEST',\n INTERNAL = 'INTERNAL',\n}\n\n// Maps an HTTP status code to the canonical ErrorCode so HTTP and GraphQL classify identically.\nexport function errorCodeFromStatus(status: number): ErrorCode {\n switch (status) {\n case HttpStatus.UNAUTHORIZED: // 401\n return ErrorCode.UNAUTHENTICATED;\n case HttpStatus.FORBIDDEN: // 403\n return ErrorCode.FORBIDDEN;\n case HttpStatus.NOT_FOUND: // 404\n return ErrorCode.NOT_FOUND;\n case HttpStatus.BAD_REQUEST: // 400\n case HttpStatus.UNPROCESSABLE_ENTITY: // 422\n return ErrorCode.VALIDATION_FAILED;\n case HttpStatus.CONFLICT: // 409\n return ErrorCode.CONFLICT;\n case HttpStatus.TOO_MANY_REQUESTS: // 429\n return ErrorCode.RATE_LIMITED;\n default:\n // Any other 4xx is a generic bad request; 5xx and unknown collapse to INTERNAL.\n if (status >= 400 && status < 500) {\n return ErrorCode.BAD_REQUEST;\n }\n return ErrorCode.INTERNAL;\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\ninterface ProblemExceptionResponse {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\ninterface ValidationExceptionResponse {\n message: Array<string | { property: string; constraints: Record<string, string> }>;\n error?: string;\n}\n\ninterface StandardExceptionResponse {\n message: string | string[];\n error?: string;\n}\n\ntype ExceptionResponseObject = ProblemExceptionResponse | ValidationExceptionResponse | StandardExceptionResponse;\n\nexport interface ExtractedProblem {\n type: string;\n label?: string;\n detail?: string;\n errors: FieldError[];\n}\n\n// Converts an HTTP status code to its title string (e.g., 400 → \"Bad Request\")\nexport function getHttpStatusTitle(status: number): string {\n // Find the enum key for the given status code\n const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];\n\n if (!enumKey) {\n return 'Error';\n }\n\n // Convert enum key to title case (e.g., BAD_REQUEST -> Bad Request)\n return enumKey\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\n// Extracts field-specific errors from a class-validator ValidationPipe message array, mirroring the RFC 9457 errors[].\nfunction extractValidationFieldErrors(\n message: Array<string | { property: string; constraints: Record<string, string> }>,\n): FieldError[] {\n return message\n .map((msg) => {\n if (typeof msg === 'object' && 'property' in msg && 'constraints' in msg) {\n const constraintValues = Object.values(msg.constraints);\n return {\n field: msg.property,\n message: constraintValues[0] ?? 'Validation failed',\n };\n }\n // Non-field-specific validation messages are ignored here; they belong at the detail level.\n return null;\n })\n .filter((error): error is FieldError => error !== null);\n}\n\n// Parses a NestJS HttpException response body into normalized problem fields across the SDK's three body shapes.\nexport function extractProblemFromResponse(\n exceptionResponse: string | object,\n problemFallbackDetail: string,\n): ExtractedProblem {\n let type = 'about:blank';\n let label: string | undefined;\n let detail: string | undefined;\n let errors: FieldError[] = [];\n\n if (typeof exceptionResponse === 'string') {\n detail = exceptionResponse;\n return { type, label, detail, errors };\n }\n\n if (exceptionResponse !== null && typeof exceptionResponse === 'object') {\n const responseObj = exceptionResponse as ExceptionResponseObject;\n\n // Custom HttpProblemException from @vritti/api-sdk\n if ('type' in responseObj || 'label' in responseObj || 'errors' in responseObj) {\n const problemResponse = responseObj as ProblemExceptionResponse;\n type = problemResponse.type ?? 'about:blank';\n label = problemResponse.label;\n detail = problemResponse.detail ?? problemFallbackDetail;\n errors = problemResponse.errors ?? [];\n }\n // class-validator DTO validation errors\n else if ('message' in responseObj && Array.isArray(responseObj.message)) {\n errors = extractValidationFieldErrors(responseObj.message);\n detail = 'Validation failed';\n }\n // Standard NestJS exceptions\n else if ('message' in responseObj) {\n const message = (responseObj as StandardExceptionResponse).message;\n detail = Array.isArray(message) ? message.join(', ') : message;\n }\n }\n\n return { type, label, detail, errors };\n}\n","import { type ErrorCode, errorCodeFromStatus } from './error-code';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\n// graphql and @nestjs/graphql are optional peers not installed here, so we declare minimal structural shapes locally.\n\ninterface GraphqlFormattedErrorShape {\n message: string;\n locations?: ReadonlyArray<{ line: number; column: number }>;\n path?: ReadonlyArray<string | number>;\n extensions?: Record<string, unknown>;\n}\n\ninterface GraphqlErrorShape {\n message: string;\n originalError?: unknown;\n extensions?: Record<string, unknown>;\n}\n\ninterface HttpExceptionLike {\n getStatus(): number;\n getResponse(): string | object;\n message: string;\n}\n\nexport interface GraphqlFormatErrorOptions {\n isProduction: boolean;\n getTraceId?: () => string | undefined;\n}\n\nexport interface FormattedErrorExtensions {\n code: ErrorCode;\n traceId?: string;\n timestamp: string;\n fieldErrors?: Array<{ field: string; message: string }>;\n [key: string]: unknown;\n}\n\n// Duck-types a NestJS HttpException without importing @nestjs/common (cross-package-instance safe).\nfunction isHttpExceptionLike(error: unknown): error is HttpExceptionLike {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n}\n\ninterface ProblemLike {\n status?: number;\n statusCode?: number;\n detail?: string;\n message?: string;\n}\n\nfunction isProblemLike(error: unknown): error is ProblemLike {\n if (error == null || typeof error !== 'object') {\n return false;\n }\n const o = error as Record<string, unknown>;\n return typeof o.status === 'number' || typeof o.statusCode === 'number';\n}\n\ninterface ProblemSource {\n status: number;\n response: string | object;\n fallback: string;\n}\n\n// Walks the error chain to find the underlying HttpException or plain RFC 9457 problem via originalError/thrownValue.\nfunction findProblemSource(error: GraphqlErrorShape | undefined): ProblemSource | undefined {\n let current: unknown = error;\n // Bounded walk — guard against cyclic chains.\n for (let depth = 0; current && depth < 10; depth++) {\n if (isHttpExceptionLike(current)) {\n const status = current.getStatus();\n return { status, response: current.getResponse(), fallback: current.message ?? getHttpStatusTitle(status) };\n }\n if (isProblemLike(current)) {\n const status = current.status ?? current.statusCode ?? 500;\n return { status, response: current, fallback: current.detail ?? current.message ?? getHttpStatusTitle(status) };\n }\n const next = current as { originalError?: unknown; thrownValue?: unknown };\n current = next.originalError ?? next.thrownValue;\n }\n return undefined;\n}\n\nconst GENERIC_INTERNAL_MESSAGE = 'Internal server error';\n\n// Builds an Apollo formatError that normalizes every GraphQL error into a stable, transport-consistent shape.\nexport function createGraphqlFormatError(\n opts: GraphqlFormatErrorOptions,\n): (formattedError: GraphqlFormattedErrorShape, error: unknown) => GraphqlFormattedErrorShape {\n const { isProduction, getTraceId } = opts;\n\n return (formattedError, error) => {\n const gqlError = (error ?? undefined) as GraphqlErrorShape | undefined;\n const source = findProblemSource(gqlError);\n\n // Derive status + problem body from the underlying source since Apollo only copies .message and defaults code to INTERNAL.\n let status = 500;\n let detail: string | undefined;\n let label: string | undefined;\n let fieldErrors: Array<{ field: string; message: string }> = [];\n\n if (source) {\n status = source.status;\n const extracted = extractProblemFromResponse(source.response, source.fallback);\n detail = extracted.detail;\n label = extracted.label;\n fieldErrors = extracted.errors;\n }\n\n const code = errorCodeFromStatus(status);\n\n // Prefer the problem detail, then the formatted message; never surface internals for INTERNAL in production.\n let message = detail ?? formattedError.message;\n if (code === 'INTERNAL' && isProduction) {\n message = GENERIC_INTERNAL_MESSAGE;\n }\n\n // Start from Apollo's extensions so any framework-set keys survive, then normalize ours on top.\n const incoming: Record<string, unknown> = { ...(formattedError.extensions ?? {}) };\n\n const traceId = getTraceId?.();\n const timestamp = new Date().toISOString();\n\n const extensions: FormattedErrorExtensions = {\n ...incoming,\n // Always our canonical code — overrides Apollo's default INTERNAL_SERVER_ERROR.\n code,\n ...(traceId !== undefined ? { traceId } : {}),\n timestamp,\n ...(label !== undefined ? { label } : {}),\n ...(fieldErrors.length > 0 ? { fieldErrors } : {}),\n };\n\n if (isProduction) {\n // Strip anything that could leak server internals to clients.\n delete extensions.stacktrace;\n delete extensions.exception;\n delete extensions.originalError;\n }\n\n const result: GraphqlFormattedErrorShape = {\n message,\n extensions,\n };\n // Preserve location/path metadata GraphQL attaches (useful to clients, leaks nothing).\n if (formattedError.locations) result.locations = formattedError.locations;\n if (formattedError.path) result.path = formattedError.path;\n\n return result;\n };\n}\n","import {\n type ArgumentsHost,\n Catch,\n type ExceptionFilter,\n type HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ApiErrorResponse, FieldError } from '../types/error-response.types';\nimport { tryTranslatePgError } from './pg-error.translator';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\nexport { getHttpStatusTitle };\n\n@Catch()\nexport class HttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(HttpExceptionFilter.name);\n\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<FastifyReply>();\n const request = ctx.getRequest<FastifyRequest>();\n\n // Translate raw Postgres errors (e.g. 23505 unique_violation) into a ConflictException before classifying as 500.\n const translatedPgError = tryTranslatePgError(exception);\n if (translatedPgError) exception = translatedPgError;\n\n let status = HttpStatus.INTERNAL_SERVER_ERROR;\n let type = 'about:blank';\n let label: string | undefined;\n let detail = 'Internal server error';\n let errors: FieldError[] = [];\n\n if (this.isHttpException(exception)) {\n status = exception.getStatus();\n const exceptionResponse = exception.getResponse();\n\n // Reuse the shared extractor so HTTP and GraphQL surface identical problem details.\n const extracted = extractProblemFromResponse(exceptionResponse, exception.message ?? getHttpStatusTitle(status));\n type = extracted.type;\n label = extracted.label;\n // Keep the initial 'Internal server error' when the body matched no known shape (byte-identical HTTP output).\n detail = extracted.detail ?? detail;\n errors = extracted.errors;\n } else if (this.isProblemLikeObject(exception)) {\n const problemObj = exception as Record<string, unknown>;\n const statusCandidate = problemObj.status ?? problemObj.statusCode;\n if (typeof statusCandidate === 'number' && statusCandidate >= 400 && statusCandidate <= 599) {\n status = statusCandidate;\n }\n type = typeof problemObj.type === 'string' ? problemObj.type : 'about:blank';\n label = typeof problemObj.label === 'string' ? problemObj.label : undefined;\n detail =\n typeof problemObj.detail === 'string'\n ? problemObj.detail\n : typeof problemObj.message === 'string'\n ? problemObj.message\n : getHttpStatusTitle(status);\n errors = Array.isArray(problemObj.errors) ? (problemObj.errors as FieldError[]) : [];\n } else if (this.isAxiosError(exception)) {\n // Outgoing HTTP call failures (e.g., service-to-service calls)\n const axiosStatus = exception.response?.status;\n const axiosDetail = exception.response?.data?.message || exception.response?.data?.detail || exception.message;\n const url = exception.config?.url;\n status = HttpStatus.BAD_GATEWAY;\n detail = `Upstream service error${axiosStatus ? ` (${axiosStatus})` : ''}: ${axiosDetail}`;\n this.logger.error(`Upstream API error [${axiosStatus}]: ${axiosDetail} — URL: ${url}`, exception.stack);\n } else {\n // Unknown errors — logged by HttpLoggerInterceptor, no need to log again here\n detail = 'An unexpected error occurred';\n }\n\n const problemDetails: ApiErrorResponse = {\n type,\n title: getHttpStatusTitle(status),\n status,\n ...(label && { label }),\n detail,\n instance: request.url,\n errors,\n };\n\n response.header('Content-Type', 'application/problem+json').status(status).send(problemDetails);\n }\n\n // Duck-type check for HttpException — avoids instanceof failing across pnpm package instances\n private isHttpException(error: unknown): error is HttpException {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n }\n\n // Duck-type check for AxiosError without importing axios\n private isAxiosError(error: unknown): error is Error & {\n isAxiosError: true;\n response?: { status?: number; data?: Record<string, unknown> };\n config?: { url?: string };\n } {\n return error instanceof Error && (error as { isAxiosError?: boolean }).isAxiosError === true;\n }\n\n private isProblemLikeObject(error: unknown): error is Record<string, unknown> {\n if (!error || typeof error !== 'object') return false;\n const obj = error as Record<string, unknown>;\n return (\n typeof obj.status === 'number' ||\n typeof obj.statusCode === 'number' ||\n typeof obj.detail === 'string' ||\n Array.isArray(obj.errors)\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { ConflictException } from '../exceptions/conflict.exception';\n\nconst PG_UNIQUE_VIOLATION = '23505';\n\nexport type PgErrorShape = {\n code?: string;\n message?: string;\n constraint?: string;\n table?: string;\n schema?: string;\n column?: string;\n detail?: string;\n hint?: string;\n};\n\n// Returns a ConflictException for a Postgres unique-violation error, otherwise undefined.\nexport function tryTranslatePgError(error: unknown): ConflictException | undefined {\n const pgError = findPgError(error);\n if (!pgError) return undefined;\n const { code, constraint, table, detail } = pgError;\n if (code !== PG_UNIQUE_VIOLATION) return undefined;\n return new ConflictException({\n label: 'Duplicate Entry',\n detail: detail?.trim() || 'A record with these values already exists.',\n errors: [],\n ...(constraint || table ? { meta: { constraint, table } } : {}),\n });\n}\n\n// Walks up to N levels of `.cause` looking for a pg-shaped error (has SQLSTATE `code`), capping depth.\nexport function findPgError(error: unknown, depth = 0): PgErrorShape | undefined {\n if (!error || typeof error !== 'object' || depth > 5) return undefined;\n const candidate = error as PgErrorShape & { cause?: unknown };\n if (typeof candidate.code === 'string') return candidate;\n return findPgError(candidate.cause, depth + 1);\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter } from '@nestjs/common';\nimport { HttpExceptionFilter } from './http-exception.filter';\n\n@Catch()\nexport class TransportAwareExceptionFilter implements ExceptionFilter {\n private readonly httpFilter = new HttpExceptionFilter();\n\n catch(exception: unknown, host: ArgumentsHost): void {\n if (host.getType() !== 'http') {\n throw exception;\n }\n this.httpFilter.catch(exception, host);\n }\n}\n"],"mappings":"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","names":["ErrorCode","errorCodeFromStatus","status","HttpStatus","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","BAD_REQUEST","UNPROCESSABLE_ENTITY","CONFLICT","TOO_MANY_REQUESTS","import_common","getHttpStatusTitle","status","enumKey","Object","entries","HttpStatus","find","key","value","Number","isNaN","split","map","word","charAt","toUpperCase","slice","toLowerCase","join","extractValidationFieldErrors","message","msg","constraintValues","values","constraints","field","property","filter","error","extractProblemFromResponse","exceptionResponse","problemFallbackDetail","type","label","detail","errors","responseObj","problemResponse","Array","isArray","isHttpExceptionLike","error","Error","getStatus","getResponse","isProblemLike","o","status","statusCode","findProblemSource","current","depth","response","fallback","message","getHttpStatusTitle","detail","next","originalError","thrownValue","undefined","GENERIC_INTERNAL_MESSAGE","createGraphqlFormatError","opts","isProduction","getTraceId","formattedError","gqlError","source","label","fieldErrors","extracted","extractProblemFromResponse","errors","code","errorCodeFromStatus","incoming","extensions","traceId","timestamp","Date","toISOString","length","stacktrace","exception","result","locations","path","import_common","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","ConflictException","HttpProblemException","detailOrOptions","HttpStatus","CONFLICT","PG_UNIQUE_VIOLATION","tryTranslatePgError","error","pgError","findPgError","undefined","code","constraint","table","detail","ConflictException","label","trim","errors","meta","depth","candidate","cause","HttpExceptionFilter","logger","Logger","name","catch","exception","host","ctx","switchToHttp","response","getResponse","request","getRequest","translatedPgError","tryTranslatePgError","status","HttpStatus","INTERNAL_SERVER_ERROR","type","label","detail","errors","isHttpException","getStatus","exceptionResponse","extracted","extractProblemFromResponse","message","getHttpStatusTitle","isProblemLikeObject","problemObj","statusCandidate","statusCode","undefined","Array","isArray","isAxiosError","axiosStatus","axiosDetail","data","url","config","BAD_GATEWAY","error","stack","problemDetails","title","instance","header","send","Error","obj","import_common","TransportAwareExceptionFilter","httpFilter","HttpExceptionFilter","catch","exception","host","getType"]}
|
|
1
|
+
{"version":3,"sources":["../src/filters/index.ts","../src/filters/error-code.ts","../src/filters/problem-extraction.ts","../src/filters/graphql-format-error.ts","../src/filters/http-exception.filter.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/base-field.exception.ts","../src/filters/pg-error.translator.ts","../src/filters/transport-aware-exception.filter.ts"],"sourcesContent":["// Machine-readable error contract + status mapper (shared across HTTP and GraphQL transports).\n\nexport { ErrorCode, errorCodeFromStatus } from './error-code';\n// GraphQL error shaping (Apollo formatError factory) — composes with TransportAwareExceptionFilter.\nexport {\n createGraphqlFormatError,\n type FormattedErrorExtensions,\n type GraphqlFormatErrorOptions,\n} from './graphql-format-error';\nexport { getHttpStatusTitle, HttpExceptionFilter } from './http-exception.filter';\nexport { TransportAwareExceptionFilter } from './transport-aware-exception.filter';\n// RpcProblemExceptionFilter is exported from '@vritti/api-sdk/nats' (it needs @nestjs/microservices).\n","import { HttpStatus } from '@nestjs/common';\n\nexport enum ErrorCode {\n UNAUTHENTICATED = 'UNAUTHENTICATED',\n FORBIDDEN = 'FORBIDDEN',\n NOT_FOUND = 'NOT_FOUND',\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n CONFLICT = 'CONFLICT',\n RATE_LIMITED = 'RATE_LIMITED',\n BAD_REQUEST = 'BAD_REQUEST',\n INTERNAL = 'INTERNAL',\n}\n\n// Maps an HTTP status code to the canonical ErrorCode so HTTP and GraphQL classify identically.\nexport function errorCodeFromStatus(status: number): ErrorCode {\n switch (status) {\n case HttpStatus.UNAUTHORIZED: // 401\n return ErrorCode.UNAUTHENTICATED;\n case HttpStatus.FORBIDDEN: // 403\n return ErrorCode.FORBIDDEN;\n case HttpStatus.NOT_FOUND: // 404\n return ErrorCode.NOT_FOUND;\n case HttpStatus.BAD_REQUEST: // 400\n case HttpStatus.UNPROCESSABLE_ENTITY: // 422\n return ErrorCode.VALIDATION_FAILED;\n case HttpStatus.CONFLICT: // 409\n return ErrorCode.CONFLICT;\n case HttpStatus.TOO_MANY_REQUESTS: // 429\n return ErrorCode.RATE_LIMITED;\n default:\n // Any other 4xx is a generic bad request; 5xx and unknown collapse to INTERNAL.\n if (status >= 400 && status < 500) {\n return ErrorCode.BAD_REQUEST;\n }\n return ErrorCode.INTERNAL;\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\ninterface ProblemExceptionResponse {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\ninterface ValidationExceptionResponse {\n message: Array<string | { property: string; constraints: Record<string, string> }>;\n error?: string;\n}\n\ninterface StandardExceptionResponse {\n message: string | string[];\n error?: string;\n}\n\ntype ExceptionResponseObject = ProblemExceptionResponse | ValidationExceptionResponse | StandardExceptionResponse;\n\nexport interface ExtractedProblem {\n type: string;\n label?: string;\n detail?: string;\n errors: FieldError[];\n}\n\n// Converts an HTTP status code to its title string (e.g., 400 → \"Bad Request\")\nexport function getHttpStatusTitle(status: number): string {\n // Find the enum key for the given status code\n const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];\n\n if (!enumKey) {\n return 'Error';\n }\n\n // Convert enum key to title case (e.g., BAD_REQUEST -> Bad Request)\n return enumKey\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\n// Extracts field-specific errors from a class-validator ValidationPipe message array, mirroring the RFC 9457 errors[].\nfunction extractValidationFieldErrors(\n message: Array<string | { property: string; constraints: Record<string, string> }>,\n): FieldError[] {\n return message\n .map((msg) => {\n if (typeof msg === 'object' && 'property' in msg && 'constraints' in msg) {\n const constraintValues = Object.values(msg.constraints);\n return {\n field: msg.property,\n message: constraintValues[0] ?? 'Validation failed',\n };\n }\n // Non-field-specific validation messages are ignored here; they belong at the detail level.\n return null;\n })\n .filter((error): error is FieldError => error !== null);\n}\n\n// Parses a NestJS HttpException response body into normalized problem fields across the SDK's three body shapes.\nexport function extractProblemFromResponse(\n exceptionResponse: string | object,\n problemFallbackDetail: string,\n): ExtractedProblem {\n let type = 'about:blank';\n let label: string | undefined;\n let detail: string | undefined;\n let errors: FieldError[] = [];\n\n if (typeof exceptionResponse === 'string') {\n detail = exceptionResponse;\n return { type, label, detail, errors };\n }\n\n if (exceptionResponse !== null && typeof exceptionResponse === 'object') {\n const responseObj = exceptionResponse as ExceptionResponseObject;\n\n // Custom HttpProblemException from @vritti/api-sdk\n if ('type' in responseObj || 'label' in responseObj || 'errors' in responseObj) {\n const problemResponse = responseObj as ProblemExceptionResponse;\n type = problemResponse.type ?? 'about:blank';\n label = problemResponse.label;\n detail = problemResponse.detail ?? problemFallbackDetail;\n errors = problemResponse.errors ?? [];\n }\n // class-validator DTO validation errors\n else if ('message' in responseObj && Array.isArray(responseObj.message)) {\n errors = extractValidationFieldErrors(responseObj.message);\n detail = 'Validation failed';\n }\n // Standard NestJS exceptions\n else if ('message' in responseObj) {\n const message = (responseObj as StandardExceptionResponse).message;\n detail = Array.isArray(message) ? message.join(', ') : message;\n }\n }\n\n return { type, label, detail, errors };\n}\n","import { type ErrorCode, errorCodeFromStatus } from './error-code';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\n// graphql and @nestjs/graphql are optional peers not installed here, so we declare minimal structural shapes locally.\n\ninterface GraphqlFormattedErrorShape {\n message: string;\n locations?: ReadonlyArray<{ line: number; column: number }>;\n path?: ReadonlyArray<string | number>;\n extensions?: Record<string, unknown>;\n}\n\ninterface GraphqlErrorShape {\n message: string;\n originalError?: unknown;\n extensions?: Record<string, unknown>;\n}\n\ninterface HttpExceptionLike {\n getStatus(): number;\n getResponse(): string | object;\n message: string;\n}\n\nexport interface GraphqlFormatErrorOptions {\n isProduction: boolean;\n getTraceId?: () => string | undefined;\n}\n\nexport interface FormattedErrorExtensions {\n code: ErrorCode;\n traceId?: string;\n timestamp: string;\n fieldErrors?: Array<{ field: string; message: string }>;\n [key: string]: unknown;\n}\n\n// Duck-types a NestJS HttpException without importing @nestjs/common (cross-package-instance safe).\nfunction isHttpExceptionLike(error: unknown): error is HttpExceptionLike {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n}\n\ninterface ProblemLike {\n status?: number;\n statusCode?: number;\n detail?: string;\n message?: string;\n}\n\nfunction isProblemLike(error: unknown): error is ProblemLike {\n if (error == null || typeof error !== 'object') {\n return false;\n }\n const o = error as Record<string, unknown>;\n return typeof o.status === 'number' || typeof o.statusCode === 'number';\n}\n\ninterface ProblemSource {\n status: number;\n response: string | object;\n fallback: string;\n}\n\n// Walks the error chain to find the underlying HttpException or plain RFC 9457 problem via originalError/thrownValue.\nfunction findProblemSource(error: GraphqlErrorShape | undefined): ProblemSource | undefined {\n let current: unknown = error;\n // Bounded walk — guard against cyclic chains.\n for (let depth = 0; current && depth < 10; depth++) {\n if (isHttpExceptionLike(current)) {\n const status = current.getStatus();\n return { status, response: current.getResponse(), fallback: current.message ?? getHttpStatusTitle(status) };\n }\n if (isProblemLike(current)) {\n const status = current.status ?? current.statusCode ?? 500;\n return { status, response: current, fallback: current.detail ?? current.message ?? getHttpStatusTitle(status) };\n }\n const next = current as { originalError?: unknown; thrownValue?: unknown };\n current = next.originalError ?? next.thrownValue;\n }\n return undefined;\n}\n\nconst GENERIC_INTERNAL_MESSAGE = 'Internal server error';\n\n// Builds an Apollo formatError that normalizes every GraphQL error into a stable, transport-consistent shape.\nexport function createGraphqlFormatError(\n opts: GraphqlFormatErrorOptions,\n): (formattedError: GraphqlFormattedErrorShape, error: unknown) => GraphqlFormattedErrorShape {\n const { isProduction, getTraceId } = opts;\n\n return (formattedError, error) => {\n const gqlError = (error ?? undefined) as GraphqlErrorShape | undefined;\n const source = findProblemSource(gqlError);\n\n // Derive status + problem body from the underlying source since Apollo only copies .message and defaults code to INTERNAL.\n let status = 500;\n let detail: string | undefined;\n let label: string | undefined;\n let fieldErrors: Array<{ field: string; message: string }> = [];\n\n if (source) {\n status = source.status;\n const extracted = extractProblemFromResponse(source.response, source.fallback);\n detail = extracted.detail;\n label = extracted.label;\n fieldErrors = extracted.errors;\n }\n\n const code = errorCodeFromStatus(status);\n\n // Prefer the problem detail, then the formatted message; never surface internals for INTERNAL in production.\n let message = detail ?? formattedError.message;\n if (code === 'INTERNAL' && isProduction) {\n message = GENERIC_INTERNAL_MESSAGE;\n }\n\n // Start from Apollo's extensions so any framework-set keys survive, then normalize ours on top.\n const incoming: Record<string, unknown> = { ...(formattedError.extensions ?? {}) };\n\n const traceId = getTraceId?.();\n const timestamp = new Date().toISOString();\n\n const extensions: FormattedErrorExtensions = {\n ...incoming,\n // Always our canonical code — overrides Apollo's default INTERNAL_SERVER_ERROR.\n code,\n ...(traceId !== undefined ? { traceId } : {}),\n timestamp,\n ...(label !== undefined ? { label } : {}),\n ...(fieldErrors.length > 0 ? { fieldErrors } : {}),\n };\n\n if (isProduction) {\n // Strip anything that could leak server internals to clients.\n delete extensions.stacktrace;\n delete extensions.exception;\n delete extensions.originalError;\n }\n\n const result: GraphqlFormattedErrorShape = {\n message,\n extensions,\n };\n // Preserve location/path metadata GraphQL attaches (useful to clients, leaks nothing).\n if (formattedError.locations) result.locations = formattedError.locations;\n if (formattedError.path) result.path = formattedError.path;\n\n return result;\n };\n}\n","import {\n type ArgumentsHost,\n Catch,\n type ExceptionFilter,\n type HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ApiErrorResponse, FieldError } from '../types/error-response.types';\nimport { tryTranslatePgError } from './pg-error.translator';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\nexport { getHttpStatusTitle };\n\n@Catch()\nexport class HttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(HttpExceptionFilter.name);\n\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<FastifyReply>();\n const request = ctx.getRequest<FastifyRequest>();\n\n // Translate raw Postgres errors (e.g. 23505 unique_violation) into a ConflictException before classifying as 500.\n const translatedPgError = tryTranslatePgError(exception);\n if (translatedPgError) exception = translatedPgError;\n\n let status = HttpStatus.INTERNAL_SERVER_ERROR;\n let type = 'about:blank';\n let label: string | undefined;\n let detail = 'Internal server error';\n let errors: FieldError[] = [];\n\n if (this.isHttpException(exception)) {\n status = exception.getStatus();\n const exceptionResponse = exception.getResponse();\n\n // Reuse the shared extractor so HTTP and GraphQL surface identical problem details.\n const extracted = extractProblemFromResponse(exceptionResponse, exception.message ?? getHttpStatusTitle(status));\n type = extracted.type;\n label = extracted.label;\n // Keep the initial 'Internal server error' when the body matched no known shape (byte-identical HTTP output).\n detail = extracted.detail ?? detail;\n errors = extracted.errors;\n } else if (this.isProblemLikeObject(exception)) {\n const problemObj = exception as Record<string, unknown>;\n const statusCandidate = problemObj.status ?? problemObj.statusCode;\n if (typeof statusCandidate === 'number' && statusCandidate >= 400 && statusCandidate <= 599) {\n status = statusCandidate;\n }\n type = typeof problemObj.type === 'string' ? problemObj.type : 'about:blank';\n label = typeof problemObj.label === 'string' ? problemObj.label : undefined;\n detail =\n typeof problemObj.detail === 'string'\n ? problemObj.detail\n : typeof problemObj.message === 'string'\n ? problemObj.message\n : getHttpStatusTitle(status);\n errors = Array.isArray(problemObj.errors) ? (problemObj.errors as FieldError[]) : [];\n } else if (this.isAxiosError(exception)) {\n // Outgoing HTTP call failures (e.g., service-to-service calls)\n const axiosStatus = exception.response?.status;\n const axiosDetail = exception.response?.data?.message || exception.response?.data?.detail || exception.message;\n const url = exception.config?.url;\n status = HttpStatus.BAD_GATEWAY;\n detail = `Upstream service error${axiosStatus ? ` (${axiosStatus})` : ''}: ${axiosDetail}`;\n this.logger.error(`Upstream API error [${axiosStatus}]: ${axiosDetail} — URL: ${url}`, exception.stack);\n } else {\n // Unknown errors — logged by HttpLoggerInterceptor, no need to log again here\n detail = 'An unexpected error occurred';\n }\n\n const problemDetails: ApiErrorResponse = {\n type,\n title: getHttpStatusTitle(status),\n status,\n ...(label && { label }),\n detail,\n instance: request.url,\n errors,\n };\n\n response.header('Content-Type', 'application/problem+json').status(status).send(problemDetails);\n }\n\n // Duck-type check for HttpException — avoids instanceof failing across pnpm package instances\n private isHttpException(error: unknown): error is HttpException {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n }\n\n // Duck-type check for AxiosError without importing axios\n private isAxiosError(error: unknown): error is Error & {\n isAxiosError: true;\n response?: { status?: number; data?: Record<string, unknown> };\n config?: { url?: string };\n } {\n return error instanceof Error && (error as { isAxiosError?: boolean }).isAxiosError === true;\n }\n\n private isProblemLikeObject(error: unknown): error is Record<string, unknown> {\n if (!error || typeof error !== 'object') return false;\n const obj = error as Record<string, unknown>;\n return (\n typeof obj.status === 'number' ||\n typeof obj.statusCode === 'number' ||\n typeof obj.detail === 'string' ||\n Array.isArray(obj.errors)\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { ConflictException } from '../exceptions/conflict.exception';\n\nconst PG_UNIQUE_VIOLATION = '23505';\n\nexport type PgErrorShape = {\n code?: string;\n message?: string;\n constraint?: string;\n table?: string;\n schema?: string;\n column?: string;\n detail?: string;\n hint?: string;\n};\n\n// Returns a ConflictException for a Postgres unique-violation error, otherwise undefined.\nexport function tryTranslatePgError(error: unknown): ConflictException | undefined {\n const pgError = findPgError(error);\n if (!pgError) return undefined;\n const { code, constraint, table, detail } = pgError;\n if (code !== PG_UNIQUE_VIOLATION) return undefined;\n return new ConflictException({\n label: 'Duplicate Entry',\n detail: detail?.trim() || 'A record with these values already exists.',\n errors: [],\n ...(constraint || table ? { meta: { constraint, table } } : {}),\n });\n}\n\n// Walks up to N levels of `.cause` looking for a pg-shaped error (has SQLSTATE `code`), capping depth.\nexport function findPgError(error: unknown, depth = 0): PgErrorShape | undefined {\n if (!error || typeof error !== 'object' || depth > 5) return undefined;\n const candidate = error as PgErrorShape & { cause?: unknown };\n if (typeof candidate.code === 'string') return candidate;\n return findPgError(candidate.cause, depth + 1);\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter } from '@nestjs/common';\nimport { HttpExceptionFilter } from './http-exception.filter';\n\n@Catch()\nexport class TransportAwareExceptionFilter implements ExceptionFilter {\n private readonly httpFilter = new HttpExceptionFilter();\n\n catch(exception: unknown, host: ArgumentsHost): void {\n if (host.getType() !== 'http') {\n throw exception;\n }\n this.httpFilter.catch(exception, host);\n }\n}\n"],"mappings":"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","names":["ErrorCode","errorCodeFromStatus","status","HttpStatus","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","BAD_REQUEST","UNPROCESSABLE_ENTITY","CONFLICT","TOO_MANY_REQUESTS","import_common","getHttpStatusTitle","status","enumKey","Object","entries","HttpStatus","find","key","value","Number","isNaN","split","map","word","charAt","toUpperCase","slice","toLowerCase","join","extractValidationFieldErrors","message","msg","constraintValues","values","constraints","field","property","filter","error","extractProblemFromResponse","exceptionResponse","problemFallbackDetail","type","label","detail","errors","responseObj","problemResponse","Array","isArray","isHttpExceptionLike","error","Error","getStatus","getResponse","isProblemLike","o","status","statusCode","findProblemSource","current","depth","response","fallback","message","getHttpStatusTitle","detail","next","originalError","thrownValue","undefined","GENERIC_INTERNAL_MESSAGE","createGraphqlFormatError","opts","isProduction","getTraceId","formattedError","gqlError","source","label","fieldErrors","extracted","extractProblemFromResponse","errors","code","errorCodeFromStatus","incoming","extensions","traceId","timestamp","Date","toISOString","length","stacktrace","exception","result","locations","path","import_common","import_common","import_common","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","ConflictException","HttpProblemException","detailOrOptions","HttpStatus","CONFLICT","PG_UNIQUE_VIOLATION","tryTranslatePgError","error","pgError","findPgError","undefined","code","constraint","table","detail","ConflictException","label","trim","errors","meta","depth","candidate","cause","HttpExceptionFilter","logger","Logger","name","catch","exception","host","ctx","switchToHttp","response","getResponse","request","getRequest","translatedPgError","tryTranslatePgError","status","HttpStatus","INTERNAL_SERVER_ERROR","type","label","detail","errors","isHttpException","getStatus","exceptionResponse","extracted","extractProblemFromResponse","message","getHttpStatusTitle","isProblemLikeObject","problemObj","statusCandidate","statusCode","undefined","Array","isArray","isAxiosError","axiosStatus","axiosDetail","data","url","config","BAD_GATEWAY","error","stack","problemDetails","title","instance","header","send","Error","obj","import_common","TransportAwareExceptionFilter","httpFilter","HttpExceptionFilter","catch","exception","host","getType"]}
|
package/dist/filters.js
CHANGED
|
@@ -262,8 +262,15 @@ __name(findPgError, "findPgError");
|
|
|
262
262
|
// src/filters/http-exception.filter.ts
|
|
263
263
|
function _ts_decorate(decorators, target, key, desc) {
|
|
264
264
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
265
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
266
|
-
|
|
265
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
266
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
267
|
+
} else {
|
|
268
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
269
|
+
if (d = decorators[i]) {
|
|
270
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
267
274
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
268
275
|
}
|
|
269
276
|
__name(_ts_decorate, "_ts_decorate");
|
|
@@ -346,8 +353,15 @@ HttpExceptionFilter = _ts_decorate([
|
|
|
346
353
|
import { Catch as Catch2 } from "@nestjs/common";
|
|
347
354
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
348
355
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
349
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
|
|
350
|
-
|
|
356
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
|
|
357
|
+
r = Reflect.decorate(decorators, target, key, desc);
|
|
358
|
+
} else {
|
|
359
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
360
|
+
if (d = decorators[i]) {
|
|
361
|
+
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
351
365
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
352
366
|
}
|
|
353
367
|
__name(_ts_decorate2, "_ts_decorate");
|
package/dist/filters.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/filters/error-code.ts","../src/filters/problem-extraction.ts","../src/filters/graphql-format-error.ts","../src/filters/http-exception.filter.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/base-field.exception.ts","../src/filters/pg-error.translator.ts","../src/filters/transport-aware-exception.filter.ts"],"sourcesContent":["import { HttpStatus } from '@nestjs/common';\n\nexport enum ErrorCode {\n UNAUTHENTICATED = 'UNAUTHENTICATED',\n FORBIDDEN = 'FORBIDDEN',\n NOT_FOUND = 'NOT_FOUND',\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n CONFLICT = 'CONFLICT',\n RATE_LIMITED = 'RATE_LIMITED',\n BAD_REQUEST = 'BAD_REQUEST',\n INTERNAL = 'INTERNAL',\n}\n\n// Maps an HTTP status code to the canonical ErrorCode so HTTP and GraphQL classify identically.\nexport function errorCodeFromStatus(status: number): ErrorCode {\n switch (status) {\n case HttpStatus.UNAUTHORIZED: // 401\n return ErrorCode.UNAUTHENTICATED;\n case HttpStatus.FORBIDDEN: // 403\n return ErrorCode.FORBIDDEN;\n case HttpStatus.NOT_FOUND: // 404\n return ErrorCode.NOT_FOUND;\n case HttpStatus.BAD_REQUEST: // 400\n case HttpStatus.UNPROCESSABLE_ENTITY: // 422\n return ErrorCode.VALIDATION_FAILED;\n case HttpStatus.CONFLICT: // 409\n return ErrorCode.CONFLICT;\n case HttpStatus.TOO_MANY_REQUESTS: // 429\n return ErrorCode.RATE_LIMITED;\n default:\n // Any other 4xx is a generic bad request; 5xx and unknown collapse to INTERNAL.\n if (status >= 400 && status < 500) {\n return ErrorCode.BAD_REQUEST;\n }\n return ErrorCode.INTERNAL;\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\ninterface ProblemExceptionResponse {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\ninterface ValidationExceptionResponse {\n message: Array<string | { property: string; constraints: Record<string, string> }>;\n error?: string;\n}\n\ninterface StandardExceptionResponse {\n message: string | string[];\n error?: string;\n}\n\ntype ExceptionResponseObject = ProblemExceptionResponse | ValidationExceptionResponse | StandardExceptionResponse;\n\nexport interface ExtractedProblem {\n type: string;\n label?: string;\n detail?: string;\n errors: FieldError[];\n}\n\n// Converts an HTTP status code to its title string (e.g., 400 → \"Bad Request\")\nexport function getHttpStatusTitle(status: number): string {\n // Find the enum key for the given status code\n const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];\n\n if (!enumKey) {\n return 'Error';\n }\n\n // Convert enum key to title case (e.g., BAD_REQUEST -> Bad Request)\n return enumKey\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\n// Extracts field-specific errors from a class-validator ValidationPipe message array, mirroring the RFC 9457 errors[].\nfunction extractValidationFieldErrors(\n message: Array<string | { property: string; constraints: Record<string, string> }>,\n): FieldError[] {\n return message\n .map((msg) => {\n if (typeof msg === 'object' && 'property' in msg && 'constraints' in msg) {\n const constraintValues = Object.values(msg.constraints);\n return {\n field: msg.property,\n message: constraintValues[0] ?? 'Validation failed',\n };\n }\n // Non-field-specific validation messages are ignored here; they belong at the detail level.\n return null;\n })\n .filter((error): error is FieldError => error !== null);\n}\n\n// Parses a NestJS HttpException response body into normalized problem fields across the SDK's three body shapes.\nexport function extractProblemFromResponse(\n exceptionResponse: string | object,\n problemFallbackDetail: string,\n): ExtractedProblem {\n let type = 'about:blank';\n let label: string | undefined;\n let detail: string | undefined;\n let errors: FieldError[] = [];\n\n if (typeof exceptionResponse === 'string') {\n detail = exceptionResponse;\n return { type, label, detail, errors };\n }\n\n if (exceptionResponse !== null && typeof exceptionResponse === 'object') {\n const responseObj = exceptionResponse as ExceptionResponseObject;\n\n // Custom HttpProblemException from @vritti/api-sdk\n if ('type' in responseObj || 'label' in responseObj || 'errors' in responseObj) {\n const problemResponse = responseObj as ProblemExceptionResponse;\n type = problemResponse.type ?? 'about:blank';\n label = problemResponse.label;\n detail = problemResponse.detail ?? problemFallbackDetail;\n errors = problemResponse.errors ?? [];\n }\n // class-validator DTO validation errors\n else if ('message' in responseObj && Array.isArray(responseObj.message)) {\n errors = extractValidationFieldErrors(responseObj.message);\n detail = 'Validation failed';\n }\n // Standard NestJS exceptions\n else if ('message' in responseObj) {\n const message = (responseObj as StandardExceptionResponse).message;\n detail = Array.isArray(message) ? message.join(', ') : message;\n }\n }\n\n return { type, label, detail, errors };\n}\n","import { type ErrorCode, errorCodeFromStatus } from './error-code';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\n// graphql and @nestjs/graphql are optional peers not installed here, so we declare minimal structural shapes locally.\n\ninterface GraphqlFormattedErrorShape {\n message: string;\n locations?: ReadonlyArray<{ line: number; column: number }>;\n path?: ReadonlyArray<string | number>;\n extensions?: Record<string, unknown>;\n}\n\ninterface GraphqlErrorShape {\n message: string;\n originalError?: unknown;\n extensions?: Record<string, unknown>;\n}\n\ninterface HttpExceptionLike {\n getStatus(): number;\n getResponse(): string | object;\n message: string;\n}\n\nexport interface GraphqlFormatErrorOptions {\n isProduction: boolean;\n getTraceId?: () => string | undefined;\n}\n\nexport interface FormattedErrorExtensions {\n code: ErrorCode;\n traceId?: string;\n timestamp: string;\n fieldErrors?: Array<{ field: string; message: string }>;\n [key: string]: unknown;\n}\n\n// Duck-types a NestJS HttpException without importing @nestjs/common (cross-package-instance safe).\nfunction isHttpExceptionLike(error: unknown): error is HttpExceptionLike {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n}\n\ninterface ProblemLike {\n status?: number;\n statusCode?: number;\n detail?: string;\n message?: string;\n}\n\nfunction isProblemLike(error: unknown): error is ProblemLike {\n if (error == null || typeof error !== 'object') {\n return false;\n }\n const o = error as Record<string, unknown>;\n return typeof o.status === 'number' || typeof o.statusCode === 'number';\n}\n\ninterface ProblemSource {\n status: number;\n response: string | object;\n fallback: string;\n}\n\n// Walks the error chain to find the underlying HttpException or plain RFC 9457 problem via originalError/thrownValue.\nfunction findProblemSource(error: GraphqlErrorShape | undefined): ProblemSource | undefined {\n let current: unknown = error;\n // Bounded walk — guard against cyclic chains.\n for (let depth = 0; current && depth < 10; depth++) {\n if (isHttpExceptionLike(current)) {\n const status = current.getStatus();\n return { status, response: current.getResponse(), fallback: current.message ?? getHttpStatusTitle(status) };\n }\n if (isProblemLike(current)) {\n const status = current.status ?? current.statusCode ?? 500;\n return { status, response: current, fallback: current.detail ?? current.message ?? getHttpStatusTitle(status) };\n }\n const next = current as { originalError?: unknown; thrownValue?: unknown };\n current = next.originalError ?? next.thrownValue;\n }\n return undefined;\n}\n\nconst GENERIC_INTERNAL_MESSAGE = 'Internal server error';\n\n// Builds an Apollo formatError that normalizes every GraphQL error into a stable, transport-consistent shape.\nexport function createGraphqlFormatError(\n opts: GraphqlFormatErrorOptions,\n): (formattedError: GraphqlFormattedErrorShape, error: unknown) => GraphqlFormattedErrorShape {\n const { isProduction, getTraceId } = opts;\n\n return (formattedError, error) => {\n const gqlError = (error ?? undefined) as GraphqlErrorShape | undefined;\n const source = findProblemSource(gqlError);\n\n // Derive status + problem body from the underlying source since Apollo only copies .message and defaults code to INTERNAL.\n let status = 500;\n let detail: string | undefined;\n let label: string | undefined;\n let fieldErrors: Array<{ field: string; message: string }> = [];\n\n if (source) {\n status = source.status;\n const extracted = extractProblemFromResponse(source.response, source.fallback);\n detail = extracted.detail;\n label = extracted.label;\n fieldErrors = extracted.errors;\n }\n\n const code = errorCodeFromStatus(status);\n\n // Prefer the problem detail, then the formatted message; never surface internals for INTERNAL in production.\n let message = detail ?? formattedError.message;\n if (code === 'INTERNAL' && isProduction) {\n message = GENERIC_INTERNAL_MESSAGE;\n }\n\n // Start from Apollo's extensions so any framework-set keys survive, then normalize ours on top.\n const incoming: Record<string, unknown> = { ...(formattedError.extensions ?? {}) };\n\n const traceId = getTraceId?.();\n const timestamp = new Date().toISOString();\n\n const extensions: FormattedErrorExtensions = {\n ...incoming,\n // Always our canonical code — overrides Apollo's default INTERNAL_SERVER_ERROR.\n code,\n ...(traceId !== undefined ? { traceId } : {}),\n timestamp,\n ...(label !== undefined ? { label } : {}),\n ...(fieldErrors.length > 0 ? { fieldErrors } : {}),\n };\n\n if (isProduction) {\n // Strip anything that could leak server internals to clients.\n delete extensions.stacktrace;\n delete extensions.exception;\n delete extensions.originalError;\n }\n\n const result: GraphqlFormattedErrorShape = {\n message,\n extensions,\n };\n // Preserve location/path metadata GraphQL attaches (useful to clients, leaks nothing).\n if (formattedError.locations) result.locations = formattedError.locations;\n if (formattedError.path) result.path = formattedError.path;\n\n return result;\n };\n}\n","import {\n type ArgumentsHost,\n Catch,\n type ExceptionFilter,\n type HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ApiErrorResponse, FieldError } from '../types/error-response.types';\nimport { tryTranslatePgError } from './pg-error.translator';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\nexport { getHttpStatusTitle };\n\n@Catch()\nexport class HttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(HttpExceptionFilter.name);\n\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<FastifyReply>();\n const request = ctx.getRequest<FastifyRequest>();\n\n // Translate raw Postgres errors (e.g. 23505 unique_violation) into a ConflictException before classifying as 500.\n const translatedPgError = tryTranslatePgError(exception);\n if (translatedPgError) exception = translatedPgError;\n\n let status = HttpStatus.INTERNAL_SERVER_ERROR;\n let type = 'about:blank';\n let label: string | undefined;\n let detail = 'Internal server error';\n let errors: FieldError[] = [];\n\n if (this.isHttpException(exception)) {\n status = exception.getStatus();\n const exceptionResponse = exception.getResponse();\n\n // Reuse the shared extractor so HTTP and GraphQL surface identical problem details.\n const extracted = extractProblemFromResponse(exceptionResponse, exception.message ?? getHttpStatusTitle(status));\n type = extracted.type;\n label = extracted.label;\n // Keep the initial 'Internal server error' when the body matched no known shape (byte-identical HTTP output).\n detail = extracted.detail ?? detail;\n errors = extracted.errors;\n } else if (this.isProblemLikeObject(exception)) {\n const problemObj = exception as Record<string, unknown>;\n const statusCandidate = problemObj.status ?? problemObj.statusCode;\n if (typeof statusCandidate === 'number' && statusCandidate >= 400 && statusCandidate <= 599) {\n status = statusCandidate;\n }\n type = typeof problemObj.type === 'string' ? problemObj.type : 'about:blank';\n label = typeof problemObj.label === 'string' ? problemObj.label : undefined;\n detail =\n typeof problemObj.detail === 'string'\n ? problemObj.detail\n : typeof problemObj.message === 'string'\n ? problemObj.message\n : getHttpStatusTitle(status);\n errors = Array.isArray(problemObj.errors) ? (problemObj.errors as FieldError[]) : [];\n } else if (this.isAxiosError(exception)) {\n // Outgoing HTTP call failures (e.g., service-to-service calls)\n const axiosStatus = exception.response?.status;\n const axiosDetail = exception.response?.data?.message || exception.response?.data?.detail || exception.message;\n const url = exception.config?.url;\n status = HttpStatus.BAD_GATEWAY;\n detail = `Upstream service error${axiosStatus ? ` (${axiosStatus})` : ''}: ${axiosDetail}`;\n this.logger.error(`Upstream API error [${axiosStatus}]: ${axiosDetail} — URL: ${url}`, exception.stack);\n } else {\n // Unknown errors — logged by HttpLoggerInterceptor, no need to log again here\n detail = 'An unexpected error occurred';\n }\n\n const problemDetails: ApiErrorResponse = {\n type,\n title: getHttpStatusTitle(status),\n status,\n ...(label && { label }),\n detail,\n instance: request.url,\n errors,\n };\n\n response.header('Content-Type', 'application/problem+json').status(status).send(problemDetails);\n }\n\n // Duck-type check for HttpException — avoids instanceof failing across pnpm package instances\n private isHttpException(error: unknown): error is HttpException {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n }\n\n // Duck-type check for AxiosError without importing axios\n private isAxiosError(error: unknown): error is Error & {\n isAxiosError: true;\n response?: { status?: number; data?: Record<string, unknown> };\n config?: { url?: string };\n } {\n return error instanceof Error && (error as { isAxiosError?: boolean }).isAxiosError === true;\n }\n\n private isProblemLikeObject(error: unknown): error is Record<string, unknown> {\n if (!error || typeof error !== 'object') return false;\n const obj = error as Record<string, unknown>;\n return (\n typeof obj.status === 'number' ||\n typeof obj.statusCode === 'number' ||\n typeof obj.detail === 'string' ||\n Array.isArray(obj.errors)\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { ConflictException } from '../exceptions/conflict.exception';\n\nconst PG_UNIQUE_VIOLATION = '23505';\n\nexport type PgErrorShape = {\n code?: string;\n message?: string;\n constraint?: string;\n table?: string;\n schema?: string;\n column?: string;\n detail?: string;\n hint?: string;\n};\n\n// Returns a ConflictException for a Postgres unique-violation error, otherwise undefined.\nexport function tryTranslatePgError(error: unknown): ConflictException | undefined {\n const pgError = findPgError(error);\n if (!pgError) return undefined;\n const { code, constraint, table, detail } = pgError;\n if (code !== PG_UNIQUE_VIOLATION) return undefined;\n return new ConflictException({\n label: 'Duplicate Entry',\n detail: detail?.trim() || 'A record with these values already exists.',\n errors: [],\n ...(constraint || table ? { meta: { constraint, table } } : {}),\n });\n}\n\n// Walks up to N levels of `.cause` looking for a pg-shaped error (has SQLSTATE `code`), capping depth.\nexport function findPgError(error: unknown, depth = 0): PgErrorShape | undefined {\n if (!error || typeof error !== 'object' || depth > 5) return undefined;\n const candidate = error as PgErrorShape & { cause?: unknown };\n if (typeof candidate.code === 'string') return candidate;\n return findPgError(candidate.cause, depth + 1);\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter } from '@nestjs/common';\nimport { HttpExceptionFilter } from './http-exception.filter';\n\n@Catch()\nexport class TransportAwareExceptionFilter implements ExceptionFilter {\n private readonly httpFilter = new HttpExceptionFilter();\n\n catch(exception: unknown, host: ArgumentsHost): void {\n if (host.getType() !== 'http') {\n throw exception;\n }\n this.httpFilter.catch(exception, host);\n }\n}\n"],"mappings":"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","names":["HttpStatus","ErrorCode","errorCodeFromStatus","status","HttpStatus","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","BAD_REQUEST","UNPROCESSABLE_ENTITY","CONFLICT","TOO_MANY_REQUESTS","HttpStatus","getHttpStatusTitle","status","enumKey","Object","entries","HttpStatus","find","key","value","Number","isNaN","split","map","word","charAt","toUpperCase","slice","toLowerCase","join","extractValidationFieldErrors","message","msg","constraintValues","values","constraints","field","property","filter","error","extractProblemFromResponse","exceptionResponse","problemFallbackDetail","type","label","detail","errors","responseObj","problemResponse","Array","isArray","isHttpExceptionLike","error","Error","getStatus","getResponse","isProblemLike","o","status","statusCode","findProblemSource","current","depth","response","fallback","message","getHttpStatusTitle","detail","next","originalError","thrownValue","undefined","GENERIC_INTERNAL_MESSAGE","createGraphqlFormatError","opts","isProduction","getTraceId","formattedError","gqlError","source","label","fieldErrors","extracted","extractProblemFromResponse","errors","code","errorCodeFromStatus","incoming","extensions","traceId","timestamp","Date","toISOString","length","stacktrace","exception","result","locations","path","Catch","HttpStatus","Logger","HttpStatus","HttpException","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","ConflictException","HttpProblemException","detailOrOptions","HttpStatus","CONFLICT","PG_UNIQUE_VIOLATION","tryTranslatePgError","error","pgError","findPgError","undefined","code","constraint","table","detail","ConflictException","label","trim","errors","meta","depth","candidate","cause","HttpExceptionFilter","logger","Logger","name","catch","exception","host","ctx","switchToHttp","response","getResponse","request","getRequest","translatedPgError","tryTranslatePgError","status","HttpStatus","INTERNAL_SERVER_ERROR","type","label","detail","errors","isHttpException","getStatus","exceptionResponse","extracted","extractProblemFromResponse","message","getHttpStatusTitle","isProblemLikeObject","problemObj","statusCandidate","statusCode","undefined","Array","isArray","isAxiosError","axiosStatus","axiosDetail","data","url","config","BAD_GATEWAY","error","stack","problemDetails","title","instance","header","send","Error","obj","Catch","TransportAwareExceptionFilter","httpFilter","HttpExceptionFilter","catch","exception","host","getType"]}
|
|
1
|
+
{"version":3,"sources":["../src/filters/error-code.ts","../src/filters/problem-extraction.ts","../src/filters/graphql-format-error.ts","../src/filters/http-exception.filter.ts","../src/exceptions/conflict.exception.ts","../src/exceptions/base-field.exception.ts","../src/filters/pg-error.translator.ts","../src/filters/transport-aware-exception.filter.ts"],"sourcesContent":["import { HttpStatus } from '@nestjs/common';\n\nexport enum ErrorCode {\n UNAUTHENTICATED = 'UNAUTHENTICATED',\n FORBIDDEN = 'FORBIDDEN',\n NOT_FOUND = 'NOT_FOUND',\n VALIDATION_FAILED = 'VALIDATION_FAILED',\n CONFLICT = 'CONFLICT',\n RATE_LIMITED = 'RATE_LIMITED',\n BAD_REQUEST = 'BAD_REQUEST',\n INTERNAL = 'INTERNAL',\n}\n\n// Maps an HTTP status code to the canonical ErrorCode so HTTP and GraphQL classify identically.\nexport function errorCodeFromStatus(status: number): ErrorCode {\n switch (status) {\n case HttpStatus.UNAUTHORIZED: // 401\n return ErrorCode.UNAUTHENTICATED;\n case HttpStatus.FORBIDDEN: // 403\n return ErrorCode.FORBIDDEN;\n case HttpStatus.NOT_FOUND: // 404\n return ErrorCode.NOT_FOUND;\n case HttpStatus.BAD_REQUEST: // 400\n case HttpStatus.UNPROCESSABLE_ENTITY: // 422\n return ErrorCode.VALIDATION_FAILED;\n case HttpStatus.CONFLICT: // 409\n return ErrorCode.CONFLICT;\n case HttpStatus.TOO_MANY_REQUESTS: // 429\n return ErrorCode.RATE_LIMITED;\n default:\n // Any other 4xx is a generic bad request; 5xx and unknown collapse to INTERNAL.\n if (status >= 400 && status < 500) {\n return ErrorCode.BAD_REQUEST;\n }\n return ErrorCode.INTERNAL;\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\ninterface ProblemExceptionResponse {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\ninterface ValidationExceptionResponse {\n message: Array<string | { property: string; constraints: Record<string, string> }>;\n error?: string;\n}\n\ninterface StandardExceptionResponse {\n message: string | string[];\n error?: string;\n}\n\ntype ExceptionResponseObject = ProblemExceptionResponse | ValidationExceptionResponse | StandardExceptionResponse;\n\nexport interface ExtractedProblem {\n type: string;\n label?: string;\n detail?: string;\n errors: FieldError[];\n}\n\n// Converts an HTTP status code to its title string (e.g., 400 → \"Bad Request\")\nexport function getHttpStatusTitle(status: number): string {\n // Find the enum key for the given status code\n const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];\n\n if (!enumKey) {\n return 'Error';\n }\n\n // Convert enum key to title case (e.g., BAD_REQUEST -> Bad Request)\n return enumKey\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\n// Extracts field-specific errors from a class-validator ValidationPipe message array, mirroring the RFC 9457 errors[].\nfunction extractValidationFieldErrors(\n message: Array<string | { property: string; constraints: Record<string, string> }>,\n): FieldError[] {\n return message\n .map((msg) => {\n if (typeof msg === 'object' && 'property' in msg && 'constraints' in msg) {\n const constraintValues = Object.values(msg.constraints);\n return {\n field: msg.property,\n message: constraintValues[0] ?? 'Validation failed',\n };\n }\n // Non-field-specific validation messages are ignored here; they belong at the detail level.\n return null;\n })\n .filter((error): error is FieldError => error !== null);\n}\n\n// Parses a NestJS HttpException response body into normalized problem fields across the SDK's three body shapes.\nexport function extractProblemFromResponse(\n exceptionResponse: string | object,\n problemFallbackDetail: string,\n): ExtractedProblem {\n let type = 'about:blank';\n let label: string | undefined;\n let detail: string | undefined;\n let errors: FieldError[] = [];\n\n if (typeof exceptionResponse === 'string') {\n detail = exceptionResponse;\n return { type, label, detail, errors };\n }\n\n if (exceptionResponse !== null && typeof exceptionResponse === 'object') {\n const responseObj = exceptionResponse as ExceptionResponseObject;\n\n // Custom HttpProblemException from @vritti/api-sdk\n if ('type' in responseObj || 'label' in responseObj || 'errors' in responseObj) {\n const problemResponse = responseObj as ProblemExceptionResponse;\n type = problemResponse.type ?? 'about:blank';\n label = problemResponse.label;\n detail = problemResponse.detail ?? problemFallbackDetail;\n errors = problemResponse.errors ?? [];\n }\n // class-validator DTO validation errors\n else if ('message' in responseObj && Array.isArray(responseObj.message)) {\n errors = extractValidationFieldErrors(responseObj.message);\n detail = 'Validation failed';\n }\n // Standard NestJS exceptions\n else if ('message' in responseObj) {\n const message = (responseObj as StandardExceptionResponse).message;\n detail = Array.isArray(message) ? message.join(', ') : message;\n }\n }\n\n return { type, label, detail, errors };\n}\n","import { type ErrorCode, errorCodeFromStatus } from './error-code';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\n// graphql and @nestjs/graphql are optional peers not installed here, so we declare minimal structural shapes locally.\n\ninterface GraphqlFormattedErrorShape {\n message: string;\n locations?: ReadonlyArray<{ line: number; column: number }>;\n path?: ReadonlyArray<string | number>;\n extensions?: Record<string, unknown>;\n}\n\ninterface GraphqlErrorShape {\n message: string;\n originalError?: unknown;\n extensions?: Record<string, unknown>;\n}\n\ninterface HttpExceptionLike {\n getStatus(): number;\n getResponse(): string | object;\n message: string;\n}\n\nexport interface GraphqlFormatErrorOptions {\n isProduction: boolean;\n getTraceId?: () => string | undefined;\n}\n\nexport interface FormattedErrorExtensions {\n code: ErrorCode;\n traceId?: string;\n timestamp: string;\n fieldErrors?: Array<{ field: string; message: string }>;\n [key: string]: unknown;\n}\n\n// Duck-types a NestJS HttpException without importing @nestjs/common (cross-package-instance safe).\nfunction isHttpExceptionLike(error: unknown): error is HttpExceptionLike {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n}\n\ninterface ProblemLike {\n status?: number;\n statusCode?: number;\n detail?: string;\n message?: string;\n}\n\nfunction isProblemLike(error: unknown): error is ProblemLike {\n if (error == null || typeof error !== 'object') {\n return false;\n }\n const o = error as Record<string, unknown>;\n return typeof o.status === 'number' || typeof o.statusCode === 'number';\n}\n\ninterface ProblemSource {\n status: number;\n response: string | object;\n fallback: string;\n}\n\n// Walks the error chain to find the underlying HttpException or plain RFC 9457 problem via originalError/thrownValue.\nfunction findProblemSource(error: GraphqlErrorShape | undefined): ProblemSource | undefined {\n let current: unknown = error;\n // Bounded walk — guard against cyclic chains.\n for (let depth = 0; current && depth < 10; depth++) {\n if (isHttpExceptionLike(current)) {\n const status = current.getStatus();\n return { status, response: current.getResponse(), fallback: current.message ?? getHttpStatusTitle(status) };\n }\n if (isProblemLike(current)) {\n const status = current.status ?? current.statusCode ?? 500;\n return { status, response: current, fallback: current.detail ?? current.message ?? getHttpStatusTitle(status) };\n }\n const next = current as { originalError?: unknown; thrownValue?: unknown };\n current = next.originalError ?? next.thrownValue;\n }\n return undefined;\n}\n\nconst GENERIC_INTERNAL_MESSAGE = 'Internal server error';\n\n// Builds an Apollo formatError that normalizes every GraphQL error into a stable, transport-consistent shape.\nexport function createGraphqlFormatError(\n opts: GraphqlFormatErrorOptions,\n): (formattedError: GraphqlFormattedErrorShape, error: unknown) => GraphqlFormattedErrorShape {\n const { isProduction, getTraceId } = opts;\n\n return (formattedError, error) => {\n const gqlError = (error ?? undefined) as GraphqlErrorShape | undefined;\n const source = findProblemSource(gqlError);\n\n // Derive status + problem body from the underlying source since Apollo only copies .message and defaults code to INTERNAL.\n let status = 500;\n let detail: string | undefined;\n let label: string | undefined;\n let fieldErrors: Array<{ field: string; message: string }> = [];\n\n if (source) {\n status = source.status;\n const extracted = extractProblemFromResponse(source.response, source.fallback);\n detail = extracted.detail;\n label = extracted.label;\n fieldErrors = extracted.errors;\n }\n\n const code = errorCodeFromStatus(status);\n\n // Prefer the problem detail, then the formatted message; never surface internals for INTERNAL in production.\n let message = detail ?? formattedError.message;\n if (code === 'INTERNAL' && isProduction) {\n message = GENERIC_INTERNAL_MESSAGE;\n }\n\n // Start from Apollo's extensions so any framework-set keys survive, then normalize ours on top.\n const incoming: Record<string, unknown> = { ...(formattedError.extensions ?? {}) };\n\n const traceId = getTraceId?.();\n const timestamp = new Date().toISOString();\n\n const extensions: FormattedErrorExtensions = {\n ...incoming,\n // Always our canonical code — overrides Apollo's default INTERNAL_SERVER_ERROR.\n code,\n ...(traceId !== undefined ? { traceId } : {}),\n timestamp,\n ...(label !== undefined ? { label } : {}),\n ...(fieldErrors.length > 0 ? { fieldErrors } : {}),\n };\n\n if (isProduction) {\n // Strip anything that could leak server internals to clients.\n delete extensions.stacktrace;\n delete extensions.exception;\n delete extensions.originalError;\n }\n\n const result: GraphqlFormattedErrorShape = {\n message,\n extensions,\n };\n // Preserve location/path metadata GraphQL attaches (useful to clients, leaks nothing).\n if (formattedError.locations) result.locations = formattedError.locations;\n if (formattedError.path) result.path = formattedError.path;\n\n return result;\n };\n}\n","import {\n type ArgumentsHost,\n Catch,\n type ExceptionFilter,\n type HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { ApiErrorResponse, FieldError } from '../types/error-response.types';\nimport { tryTranslatePgError } from './pg-error.translator';\nimport { extractProblemFromResponse, getHttpStatusTitle } from './problem-extraction';\n\nexport { getHttpStatusTitle };\n\n@Catch()\nexport class HttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(HttpExceptionFilter.name);\n\n catch(exception: unknown, host: ArgumentsHost) {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<FastifyReply>();\n const request = ctx.getRequest<FastifyRequest>();\n\n // Translate raw Postgres errors (e.g. 23505 unique_violation) into a ConflictException before classifying as 500.\n const translatedPgError = tryTranslatePgError(exception);\n if (translatedPgError) exception = translatedPgError;\n\n let status = HttpStatus.INTERNAL_SERVER_ERROR;\n let type = 'about:blank';\n let label: string | undefined;\n let detail = 'Internal server error';\n let errors: FieldError[] = [];\n\n if (this.isHttpException(exception)) {\n status = exception.getStatus();\n const exceptionResponse = exception.getResponse();\n\n // Reuse the shared extractor so HTTP and GraphQL surface identical problem details.\n const extracted = extractProblemFromResponse(exceptionResponse, exception.message ?? getHttpStatusTitle(status));\n type = extracted.type;\n label = extracted.label;\n // Keep the initial 'Internal server error' when the body matched no known shape (byte-identical HTTP output).\n detail = extracted.detail ?? detail;\n errors = extracted.errors;\n } else if (this.isProblemLikeObject(exception)) {\n const problemObj = exception as Record<string, unknown>;\n const statusCandidate = problemObj.status ?? problemObj.statusCode;\n if (typeof statusCandidate === 'number' && statusCandidate >= 400 && statusCandidate <= 599) {\n status = statusCandidate;\n }\n type = typeof problemObj.type === 'string' ? problemObj.type : 'about:blank';\n label = typeof problemObj.label === 'string' ? problemObj.label : undefined;\n detail =\n typeof problemObj.detail === 'string'\n ? problemObj.detail\n : typeof problemObj.message === 'string'\n ? problemObj.message\n : getHttpStatusTitle(status);\n errors = Array.isArray(problemObj.errors) ? (problemObj.errors as FieldError[]) : [];\n } else if (this.isAxiosError(exception)) {\n // Outgoing HTTP call failures (e.g., service-to-service calls)\n const axiosStatus = exception.response?.status;\n const axiosDetail = exception.response?.data?.message || exception.response?.data?.detail || exception.message;\n const url = exception.config?.url;\n status = HttpStatus.BAD_GATEWAY;\n detail = `Upstream service error${axiosStatus ? ` (${axiosStatus})` : ''}: ${axiosDetail}`;\n this.logger.error(`Upstream API error [${axiosStatus}]: ${axiosDetail} — URL: ${url}`, exception.stack);\n } else {\n // Unknown errors — logged by HttpLoggerInterceptor, no need to log again here\n detail = 'An unexpected error occurred';\n }\n\n const problemDetails: ApiErrorResponse = {\n type,\n title: getHttpStatusTitle(status),\n status,\n ...(label && { label }),\n detail,\n instance: request.url,\n errors,\n };\n\n response.header('Content-Type', 'application/problem+json').status(status).send(problemDetails);\n }\n\n // Duck-type check for HttpException — avoids instanceof failing across pnpm package instances\n private isHttpException(error: unknown): error is HttpException {\n return (\n error instanceof Error &&\n typeof (error as { getStatus?: unknown }).getStatus === 'function' &&\n typeof (error as { getResponse?: unknown }).getResponse === 'function'\n );\n }\n\n // Duck-type check for AxiosError without importing axios\n private isAxiosError(error: unknown): error is Error & {\n isAxiosError: true;\n response?: { status?: number; data?: Record<string, unknown> };\n config?: { url?: string };\n } {\n return error instanceof Error && (error as { isAxiosError?: boolean }).isAxiosError === true;\n }\n\n private isProblemLikeObject(error: unknown): error is Record<string, unknown> {\n if (!error || typeof error !== 'object') return false;\n const obj = error as Record<string, unknown>;\n return (\n typeof obj.status === 'number' ||\n typeof obj.statusCode === 'number' ||\n typeof obj.detail === 'string' ||\n Array.isArray(obj.errors)\n );\n }\n}\n","import { HttpStatus } from '@nestjs/common';\nimport { HttpProblemException, type ProblemOptions } from './base-field.exception';\n\nexport class ConflictException extends HttpProblemException {\n constructor(detailOrOptions?: string | ProblemOptions) {\n super(detailOrOptions ?? 'Conflict', HttpStatus.CONFLICT);\n }\n}\n","import { HttpException, HttpStatus } from '@nestjs/common';\nimport type { FieldError } from '../types/error-response.types';\n\n// Re-export FieldError for backwards compatibility\nexport type { FieldError } from '../types/error-response.types';\n\nexport interface ProblemOptions {\n type?: string;\n label?: string;\n detail?: string;\n errors?: FieldError[];\n}\n\nexport abstract class HttpProblemException extends HttpException {\n constructor(detailOrOptions: string | ProblemOptions, httpStatus: HttpStatus) {\n const options = typeof detailOrOptions === 'string' ? { detail: detailOrOptions } : detailOrOptions;\n\n super(\n {\n type: options.type ?? 'about:blank',\n label: options.label,\n detail: options.detail,\n errors: options.errors ?? [],\n },\n httpStatus,\n );\n }\n}\n","import { ConflictException } from '../exceptions/conflict.exception';\n\nconst PG_UNIQUE_VIOLATION = '23505';\n\nexport type PgErrorShape = {\n code?: string;\n message?: string;\n constraint?: string;\n table?: string;\n schema?: string;\n column?: string;\n detail?: string;\n hint?: string;\n};\n\n// Returns a ConflictException for a Postgres unique-violation error, otherwise undefined.\nexport function tryTranslatePgError(error: unknown): ConflictException | undefined {\n const pgError = findPgError(error);\n if (!pgError) return undefined;\n const { code, constraint, table, detail } = pgError;\n if (code !== PG_UNIQUE_VIOLATION) return undefined;\n return new ConflictException({\n label: 'Duplicate Entry',\n detail: detail?.trim() || 'A record with these values already exists.',\n errors: [],\n ...(constraint || table ? { meta: { constraint, table } } : {}),\n });\n}\n\n// Walks up to N levels of `.cause` looking for a pg-shaped error (has SQLSTATE `code`), capping depth.\nexport function findPgError(error: unknown, depth = 0): PgErrorShape | undefined {\n if (!error || typeof error !== 'object' || depth > 5) return undefined;\n const candidate = error as PgErrorShape & { cause?: unknown };\n if (typeof candidate.code === 'string') return candidate;\n return findPgError(candidate.cause, depth + 1);\n}\n","import { type ArgumentsHost, Catch, type ExceptionFilter } from '@nestjs/common';\nimport { HttpExceptionFilter } from './http-exception.filter';\n\n@Catch()\nexport class TransportAwareExceptionFilter implements ExceptionFilter {\n private readonly httpFilter = new HttpExceptionFilter();\n\n catch(exception: unknown, host: ArgumentsHost): void {\n if (host.getType() !== 'http') {\n throw exception;\n }\n this.httpFilter.catch(exception, host);\n }\n}\n"],"mappings":"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","names":["HttpStatus","ErrorCode","errorCodeFromStatus","status","HttpStatus","UNAUTHORIZED","FORBIDDEN","NOT_FOUND","BAD_REQUEST","UNPROCESSABLE_ENTITY","CONFLICT","TOO_MANY_REQUESTS","HttpStatus","getHttpStatusTitle","status","enumKey","Object","entries","HttpStatus","find","key","value","Number","isNaN","split","map","word","charAt","toUpperCase","slice","toLowerCase","join","extractValidationFieldErrors","message","msg","constraintValues","values","constraints","field","property","filter","error","extractProblemFromResponse","exceptionResponse","problemFallbackDetail","type","label","detail","errors","responseObj","problemResponse","Array","isArray","isHttpExceptionLike","error","Error","getStatus","getResponse","isProblemLike","o","status","statusCode","findProblemSource","current","depth","response","fallback","message","getHttpStatusTitle","detail","next","originalError","thrownValue","undefined","GENERIC_INTERNAL_MESSAGE","createGraphqlFormatError","opts","isProduction","getTraceId","formattedError","gqlError","source","label","fieldErrors","extracted","extractProblemFromResponse","errors","code","errorCodeFromStatus","incoming","extensions","traceId","timestamp","Date","toISOString","length","stacktrace","exception","result","locations","path","Catch","HttpStatus","Logger","HttpStatus","HttpException","HttpProblemException","HttpException","detailOrOptions","httpStatus","options","detail","type","label","errors","ConflictException","HttpProblemException","detailOrOptions","HttpStatus","CONFLICT","PG_UNIQUE_VIOLATION","tryTranslatePgError","error","pgError","findPgError","undefined","code","constraint","table","detail","ConflictException","label","trim","errors","meta","depth","candidate","cause","HttpExceptionFilter","logger","Logger","name","catch","exception","host","ctx","switchToHttp","response","getResponse","request","getRequest","translatedPgError","tryTranslatePgError","status","HttpStatus","INTERNAL_SERVER_ERROR","type","label","detail","errors","isHttpException","getStatus","exceptionResponse","extracted","extractProblemFromResponse","message","getHttpStatusTitle","isProblemLikeObject","problemObj","statusCandidate","statusCode","undefined","Array","isArray","isAxiosError","axiosStatus","axiosDetail","data","url","config","BAD_GATEWAY","error","stack","problemDetails","title","instance","header","send","Error","obj","Catch","TransportAwareExceptionFilter","httpFilter","HttpExceptionFilter","catch","exception","host","getType"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -149,4 +149,4 @@ declare module 'fastify' {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
export { type AuthConfig as A, type CookieConfig as C, type DecodedAccessToken as D, type GuardConfig as G, type OnAuthenticatedCallback as O, RequestService as R, type TokenExpiry as T, TokenType as a, type DecodedRefreshToken as b,
|
|
152
|
+
export { type AuthConfig as A, type CookieConfig as C, type DecodedAccessToken as D, type GuardConfig as G, type OnAuthenticatedCallback as O, RequestService as R, type TokenExpiry as T, TokenType as a, type DecodedRefreshToken as b, AUTH_CONFIG as c, AUTH_CONFIG_DEFAULTS as d, type AccessTokenPayload as e, type CookieSerializeOptions as f, type RefreshTokenPayload as g, type TokenExpiryString as h };
|
package/dist/index.d.ts
CHANGED
|
@@ -149,4 +149,4 @@ declare module 'fastify' {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
export { type AuthConfig as A, type CookieConfig as C, type DecodedAccessToken as D, type GuardConfig as G, type OnAuthenticatedCallback as O, RequestService as R, type TokenExpiry as T, TokenType as a, type DecodedRefreshToken as b,
|
|
152
|
+
export { type AuthConfig as A, type CookieConfig as C, type DecodedAccessToken as D, type GuardConfig as G, type OnAuthenticatedCallback as O, RequestService as R, type TokenExpiry as T, TokenType as a, type DecodedRefreshToken as b, AUTH_CONFIG as c, AUTH_CONFIG_DEFAULTS as d, type AccessTokenPayload as e, type CookieSerializeOptions as f, type RefreshTokenPayload as g, type TokenExpiryString as h };
|