diff --git a/packages/payload/src/errors/APIError.ts b/packages/payload/src/errors/APIError.ts index 0c5048b4833..11144781ef4 100644 --- a/packages/payload/src/errors/APIError.ts +++ b/packages/payload/src/errors/APIError.ts @@ -1,7 +1,9 @@ import { status as httpStatus } from 'http-status' -// This gets dynamically reassigned during compilation -export let APIErrorName = 'APIError' +/** + * @deprecated Remove this in 4.0 - this is no longer needed as we assign the prototype correctly in the constructor + */ +export const APIErrorName = 'APIError' class ExtendableError extends Error { data: TData @@ -17,13 +19,17 @@ class ExtendableError extends // show data in cause cause: data, }) - APIErrorName = this.constructor.name - this.name = this.constructor.name this.message = message this.status = status this.data = data this.isPublic = isPublic this.isOperational = true // This is required since bluebird 4 doesn't append it anymore. + + // Ensure error name is not lost during swc minification when running next build + this.name = 'ExtendableError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, ExtendableError.prototype) + Error.captureStackTrace(this, this.constructor) } } @@ -50,5 +56,10 @@ export class APIError< isPublic = false, ) { super(message, status, data, isPublic) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'APIError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, APIError.prototype) } } diff --git a/packages/payload/src/errors/AuthenticationError.ts b/packages/payload/src/errors/AuthenticationError.ts index b756974ab99..582089ec50a 100644 --- a/packages/payload/src/errors/AuthenticationError.ts +++ b/packages/payload/src/errors/AuthenticationError.ts @@ -13,5 +13,10 @@ export class AuthenticationError extends APIError { : en.translations.error.emailOrPasswordIncorrect, httpStatus.UNAUTHORIZED, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'AuthenticationError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, AuthenticationError.prototype) } } diff --git a/packages/payload/src/errors/DuplicateCollection.ts b/packages/payload/src/errors/DuplicateCollection.ts index e2f97b493de..14a1b1d1fd6 100644 --- a/packages/payload/src/errors/DuplicateCollection.ts +++ b/packages/payload/src/errors/DuplicateCollection.ts @@ -3,5 +3,10 @@ import { APIError } from './APIError.js' export class DuplicateCollection extends APIError { constructor(propertyName: string, duplicate: string) { super(`Collection ${propertyName} already in use: "${duplicate}"`) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'DuplicateCollection' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, DuplicateCollection.prototype) } } diff --git a/packages/payload/src/errors/DuplicateFieldName.ts b/packages/payload/src/errors/DuplicateFieldName.ts index 4760415c385..158e51269c2 100644 --- a/packages/payload/src/errors/DuplicateFieldName.ts +++ b/packages/payload/src/errors/DuplicateFieldName.ts @@ -5,5 +5,10 @@ export class DuplicateFieldName extends APIError { super( `A field with the name '${fieldName}' was found multiple times on the same level. Field names must be unique.`, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'DuplicateFieldName' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, DuplicateFieldName.prototype) } } diff --git a/packages/payload/src/errors/DuplicateGlobal.ts b/packages/payload/src/errors/DuplicateGlobal.ts index a12e66ad5a5..e4b35cbebbf 100644 --- a/packages/payload/src/errors/DuplicateGlobal.ts +++ b/packages/payload/src/errors/DuplicateGlobal.ts @@ -5,5 +5,10 @@ import { APIError } from './APIError.js' export class DuplicateGlobal extends APIError { constructor(config: GlobalConfig) { super(`Global label "${config.label}" is already in use`) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'DuplicateGlobal' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, DuplicateGlobal.prototype) } } diff --git a/packages/payload/src/errors/ErrorDeletingFile.ts b/packages/payload/src/errors/ErrorDeletingFile.ts index 01ef0e78004..b6d83ee14b2 100644 --- a/packages/payload/src/errors/ErrorDeletingFile.ts +++ b/packages/payload/src/errors/ErrorDeletingFile.ts @@ -11,5 +11,10 @@ export class ErrorDeletingFile extends APIError { t ? t('error:deletingFile') : en.translations.error.deletingFile, httpStatus.INTERNAL_SERVER_ERROR, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'ErrorDeletingFile' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, ErrorDeletingFile.prototype) } } diff --git a/packages/payload/src/errors/FileRetrievalError.ts b/packages/payload/src/errors/FileRetrievalError.ts index 51d881fe0e4..9598a532e85 100644 --- a/packages/payload/src/errors/FileRetrievalError.ts +++ b/packages/payload/src/errors/FileRetrievalError.ts @@ -12,5 +12,10 @@ export class FileRetrievalError extends APIError { msg += ` ${message}` } super(msg, httpStatus.INTERNAL_SERVER_ERROR) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'FileRetrievalError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, FileRetrievalError.prototype) } } diff --git a/packages/payload/src/errors/FileUploadError.ts b/packages/payload/src/errors/FileUploadError.ts index 11ada9350f9..77d6254b9fc 100644 --- a/packages/payload/src/errors/FileUploadError.ts +++ b/packages/payload/src/errors/FileUploadError.ts @@ -11,5 +11,10 @@ export class FileUploadError extends APIError { t ? t('error:problemUploadingFile') : en.translations.error.problemUploadingFile, httpStatus.BAD_REQUEST, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'FileUploadError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, FileUploadError.prototype) } } diff --git a/packages/payload/src/errors/Forbidden.ts b/packages/payload/src/errors/Forbidden.ts index 35beba489f1..df9b4639a1c 100644 --- a/packages/payload/src/errors/Forbidden.ts +++ b/packages/payload/src/errors/Forbidden.ts @@ -11,5 +11,10 @@ export class Forbidden extends APIError { t ? t('error:notAllowedToPerformAction') : en.translations.error.notAllowedToPerformAction, httpStatus.FORBIDDEN, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'Forbidden' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, Forbidden.prototype) } } diff --git a/packages/payload/src/errors/InvalidConfiguration.ts b/packages/payload/src/errors/InvalidConfiguration.ts index 7d0b53e18f8..f65070eac67 100644 --- a/packages/payload/src/errors/InvalidConfiguration.ts +++ b/packages/payload/src/errors/InvalidConfiguration.ts @@ -5,5 +5,10 @@ import { APIError } from './APIError.js' export class InvalidConfiguration extends APIError { constructor(message: string) { super(message, httpStatus.INTERNAL_SERVER_ERROR) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'InvalidConfiguration' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, InvalidConfiguration.prototype) } } diff --git a/packages/payload/src/errors/InvalidFieldJoin.ts b/packages/payload/src/errors/InvalidFieldJoin.ts index fe29bcef8d4..400e942c995 100644 --- a/packages/payload/src/errors/InvalidFieldJoin.ts +++ b/packages/payload/src/errors/InvalidFieldJoin.ts @@ -7,5 +7,10 @@ export class InvalidFieldJoin extends APIError { super( `Invalid join field ${field.name}. The config does not have a field '${field.on}' in collection '${field.collection}'.`, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'InvalidFieldJoin' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, InvalidFieldJoin.prototype) } } diff --git a/packages/payload/src/errors/InvalidFieldName.ts b/packages/payload/src/errors/InvalidFieldName.ts index 3c45d0c5d5d..a348675b3d6 100644 --- a/packages/payload/src/errors/InvalidFieldName.ts +++ b/packages/payload/src/errors/InvalidFieldName.ts @@ -7,5 +7,10 @@ export class InvalidFieldName extends APIError { super( `Field ${field.label} has invalid name '${fieldName}'. Field names can not include periods (.) and must be alphanumeric.`, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'InvalidFieldName' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, InvalidFieldName.prototype) } } diff --git a/packages/payload/src/errors/InvalidFieldRelationship.ts b/packages/payload/src/errors/InvalidFieldRelationship.ts index 65e6f0fc177..def08f6e409 100644 --- a/packages/payload/src/errors/InvalidFieldRelationship.ts +++ b/packages/payload/src/errors/InvalidFieldRelationship.ts @@ -5,5 +5,10 @@ import { APIError } from './APIError.js' export class InvalidFieldRelationship extends APIError { constructor(field: RelationshipField | UploadField, relationship: string) { super(`Field ${field.label} has invalid relationship '${relationship}'.`) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'InvalidFieldRelationship' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, InvalidFieldRelationship.prototype) } } diff --git a/packages/payload/src/errors/InvalidSchema.ts b/packages/payload/src/errors/InvalidSchema.ts index 78c5a4b4498..e0798b2b035 100644 --- a/packages/payload/src/errors/InvalidSchema.ts +++ b/packages/payload/src/errors/InvalidSchema.ts @@ -5,5 +5,10 @@ import { APIError } from './APIError.js' export class InvalidSchema extends APIError { constructor(message: string, results: any) { super(message, httpStatus.INTERNAL_SERVER_ERROR, results) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'InvalidSchema' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, InvalidSchema.prototype) } } diff --git a/packages/payload/src/errors/Locked.ts b/packages/payload/src/errors/Locked.ts index 536c046f499..e50fd94ecdd 100644 --- a/packages/payload/src/errors/Locked.ts +++ b/packages/payload/src/errors/Locked.ts @@ -5,5 +5,10 @@ import { APIError } from './APIError.js' export class Locked extends APIError { constructor(message: string) { super(message, httpStatus.LOCKED) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'Locked' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, Locked.prototype) } } diff --git a/packages/payload/src/errors/LockedAuth.ts b/packages/payload/src/errors/LockedAuth.ts index a2fa1bc40b5..5606d5ab0ab 100644 --- a/packages/payload/src/errors/LockedAuth.ts +++ b/packages/payload/src/errors/LockedAuth.ts @@ -8,5 +8,10 @@ import { APIError } from './APIError.js' export class LockedAuth extends APIError { constructor(t?: TFunction) { super(t ? t('error:userLocked') : en.translations.error.userLocked, httpStatus.UNAUTHORIZED) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'LockedAuth' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, LockedAuth.prototype) } } diff --git a/packages/payload/src/errors/MissingCollectionLabel.ts b/packages/payload/src/errors/MissingCollectionLabel.ts index ea8206635fe..c7be260c23e 100644 --- a/packages/payload/src/errors/MissingCollectionLabel.ts +++ b/packages/payload/src/errors/MissingCollectionLabel.ts @@ -3,5 +3,10 @@ import { APIError } from './APIError.js' export class MissingCollectionLabel extends APIError { constructor() { super('payload.config.collection object is missing label') + + // Ensure error name is not lost during swc minification when running next build + this.name = 'MissingCollectionLabel' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, MissingCollectionLabel.prototype) } } diff --git a/packages/payload/src/errors/MissingEditorProp.ts b/packages/payload/src/errors/MissingEditorProp.ts index 63adf9cf12d..87a630193a0 100644 --- a/packages/payload/src/errors/MissingEditorProp.ts +++ b/packages/payload/src/errors/MissingEditorProp.ts @@ -8,5 +8,10 @@ export class MissingEditorProp extends APIError { super( `RichText field${fieldAffectsData(field) ? ` "${field.name}"` : ''} is missing the editor prop. For sub-richText fields, the editor props is required, as it would otherwise create infinite recursion.`, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'MissingEditorProp' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, MissingEditorProp.prototype) } } diff --git a/packages/payload/src/errors/MissingFieldInputOptions.ts b/packages/payload/src/errors/MissingFieldInputOptions.ts index 7907598285e..22126307c8a 100644 --- a/packages/payload/src/errors/MissingFieldInputOptions.ts +++ b/packages/payload/src/errors/MissingFieldInputOptions.ts @@ -5,5 +5,10 @@ import { APIError } from './APIError.js' export class MissingFieldInputOptions extends APIError { constructor(field: RadioField | SelectField) { super(`Field ${field.label} is missing options.`) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'MissingFieldInputOptions' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, MissingFieldInputOptions.prototype) } } diff --git a/packages/payload/src/errors/MissingFieldType.ts b/packages/payload/src/errors/MissingFieldType.ts index ab805c7b104..7e4e5ff35d3 100644 --- a/packages/payload/src/errors/MissingFieldType.ts +++ b/packages/payload/src/errors/MissingFieldType.ts @@ -10,5 +10,10 @@ export class MissingFieldType extends APIError { fieldAffectsData(field) ? ` "${field.name}"` : '' } is either missing a field type or it does not match an available field type`, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'MissingFieldType' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, MissingFieldType.prototype) } } diff --git a/packages/payload/src/errors/MissingFile.ts b/packages/payload/src/errors/MissingFile.ts index d8dcd3a9a2f..ffa9350632d 100644 --- a/packages/payload/src/errors/MissingFile.ts +++ b/packages/payload/src/errors/MissingFile.ts @@ -11,5 +11,10 @@ export class MissingFile extends APIError { t ? t('error:noFilesUploaded') : en.translations.error.noFilesUploaded, httpStatus.BAD_REQUEST, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'MissingFile' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, MissingFile.prototype) } } diff --git a/packages/payload/src/errors/NotFound.ts b/packages/payload/src/errors/NotFound.ts index 6f66f35ad68..b25c4681f4c 100644 --- a/packages/payload/src/errors/NotFound.ts +++ b/packages/payload/src/errors/NotFound.ts @@ -8,5 +8,10 @@ import { APIError } from './APIError.js' export class NotFound extends APIError { constructor(t?: TFunction) { super(t ? t('general:notFound') : en.translations.general.notFound, httpStatus.NOT_FOUND) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'NotFound' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, NotFound.prototype) } } diff --git a/packages/payload/src/errors/QueryError.ts b/packages/payload/src/errors/QueryError.ts index 2d891e6d3dc..bbad931b800 100644 --- a/packages/payload/src/errors/QueryError.ts +++ b/packages/payload/src/errors/QueryError.ts @@ -11,5 +11,10 @@ export class QueryError extends APIError<{ path: string }[]> { httpStatus.BAD_REQUEST, results, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'QueryError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, QueryError.prototype) } } diff --git a/packages/payload/src/errors/ReservedFieldName.ts b/packages/payload/src/errors/ReservedFieldName.ts index d9317fa96fa..0aedd2d30c9 100644 --- a/packages/payload/src/errors/ReservedFieldName.ts +++ b/packages/payload/src/errors/ReservedFieldName.ts @@ -5,5 +5,10 @@ import { APIError } from './APIError.js' export class ReservedFieldName extends APIError { constructor(field: FieldAffectingData, fieldName: string) { super(`Field ${field.label} has reserved name '${fieldName}'.`) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'ReservedFieldName' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, ReservedFieldName.prototype) } } diff --git a/packages/payload/src/errors/TimestampsRequired.ts b/packages/payload/src/errors/TimestampsRequired.ts index 0846f9fcd9e..818dbb5672d 100644 --- a/packages/payload/src/errors/TimestampsRequired.ts +++ b/packages/payload/src/errors/TimestampsRequired.ts @@ -7,5 +7,10 @@ export class TimestampsRequired extends APIError { super( `Timestamps are required in the collection ${collection.slug} because you have opted in to Versions.`, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'TimestampsRequired' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, TimestampsRequired.prototype) } } diff --git a/packages/payload/src/errors/UnauthorizedError.ts b/packages/payload/src/errors/UnauthorizedError.ts index 7baceb8167c..067f533bde1 100644 --- a/packages/payload/src/errors/UnauthorizedError.ts +++ b/packages/payload/src/errors/UnauthorizedError.ts @@ -8,5 +8,10 @@ import { APIError } from './APIError.js' export class UnauthorizedError extends APIError { constructor(t?: TFunction) { super(t ? t('error:unauthorized') : en.translations.error.unauthorized, httpStatus.UNAUTHORIZED) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'UnauthorizedError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, UnauthorizedError.prototype) } } diff --git a/packages/payload/src/errors/UnverifiedEmail.ts b/packages/payload/src/errors/UnverifiedEmail.ts index 7a969d31e58..5e2dff40c23 100644 --- a/packages/payload/src/errors/UnverifiedEmail.ts +++ b/packages/payload/src/errors/UnverifiedEmail.ts @@ -11,5 +11,10 @@ export class UnverifiedEmail extends APIError { t ? t('error:unverifiedEmail') : en.translations.error.unverifiedEmail, httpStatus.FORBIDDEN, ) + + // Ensure error name is not lost during swc minification when running next build + this.name = 'UnverifiedEmail' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, UnverifiedEmail.prototype) } } diff --git a/packages/payload/src/errors/ValidationError.ts b/packages/payload/src/errors/ValidationError.ts index 2dcca9b44a2..7cdbc975ed5 100644 --- a/packages/payload/src/errors/ValidationError.ts +++ b/packages/payload/src/errors/ValidationError.ts @@ -8,8 +8,10 @@ import type { PayloadRequest } from '../types/index.js' import { APIError } from './APIError.js' -// This gets dynamically reassigned during compilation -export let ValidationErrorName = 'ValidationError' +/** + * @deprecated Remove this in 4.0 - this is no longer needed as we assign the prototype correctly in the constructor + */ +export const ValidationErrorName = 'ValidationError' export type ValidationFieldError = { label?: LabelFunction | StaticLabel @@ -76,6 +78,9 @@ export class ValidationError extends APIError<{ results, ) - ValidationErrorName = this.constructor.name + // Ensure error name is not lost during swc minification when running next build + this.name = 'ValidationError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, ValidationError.prototype) } } diff --git a/packages/payload/src/queues/errors/index.ts b/packages/payload/src/queues/errors/index.ts index 56b4b46ec5a..380bd89026c 100644 --- a/packages/payload/src/queues/errors/index.ts +++ b/packages/payload/src/queues/errors/index.ts @@ -28,6 +28,11 @@ export class TaskError extends Error { constructor(args: TaskErrorArgs) { super(args.message) this.args = args + + // Ensure error name is not lost during swc minification when running next build + this.name = 'TaskError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, TaskError.prototype) } } export class WorkflowError extends Error { @@ -36,6 +41,11 @@ export class WorkflowError extends Error { constructor(args: WorkflowErrorArgs) { super(args.message) this.args = args + + // Ensure error name is not lost during swc minification when running next build + this.name = 'WorkflowError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, WorkflowError.prototype) } } @@ -47,5 +57,10 @@ export class JobCancelledError extends Error { constructor(args: { job: Job }) { super(`Job ${args.job.id} was cancelled`) this.args = args + + // Ensure error name is not lost during swc minification when running next build + this.name = 'JobCancelledError' + // Ensure instanceof works correctly + Object.setPrototypeOf(this, JobCancelledError.prototype) } } diff --git a/packages/payload/src/utilities/formatErrors.ts b/packages/payload/src/utilities/formatErrors.ts index 9011322d1ee..a81616ad655 100644 --- a/packages/payload/src/utilities/formatErrors.ts +++ b/packages/payload/src/utilities/formatErrors.ts @@ -1,20 +1,12 @@ import type { ErrorResult } from '../config/types.js' -import type { APIError } from '../errors/APIError.js' -import { APIErrorName } from '../errors/APIError.js' -import { ValidationErrorName } from '../errors/ValidationError.js' +import { APIError } from '../errors/APIError.js' +import { ValidationError } from '../errors/ValidationError.js' -export const formatErrors = (incoming: { [key: string]: unknown } | APIError): ErrorResult => { +export const formatErrors = (incoming: unknown): ErrorResult => { if (incoming) { - // Cannot use `instanceof` to check error type: https://github.com/microsoft/TypeScript/issues/13965 - // Instead, get the prototype of the incoming error and check its constructor name - const proto = Object.getPrototypeOf(incoming) - // Payload 'ValidationError' and 'APIError' - if ( - (proto.constructor.name === ValidationErrorName || proto.constructor.name === APIErrorName) && - incoming.data - ) { + if ((incoming instanceof ValidationError || incoming instanceof APIError) && incoming.data) { return { errors: [ { @@ -27,7 +19,13 @@ export const formatErrors = (incoming: { [key: string]: unknown } | APIError): E } // Mongoose 'ValidationError': https://mongoosejs.com/docs/api/error.html#Error.ValidationError - if (proto.constructor.name === ValidationErrorName && 'errors' in incoming && incoming.errors) { + if ( + typeof incoming === 'object' && + incoming !== null && + !(incoming instanceof APIError || incoming instanceof Error) && + 'errors' in incoming && + incoming.errors + ) { return { errors: Object.keys(incoming.errors).reduce( (acc, key) => { @@ -42,17 +40,22 @@ export const formatErrors = (incoming: { [key: string]: unknown } | APIError): E } } - if (Array.isArray(incoming.message)) { + if ( + typeof incoming === 'object' && + incoming !== null && + 'message' in incoming && + Array.isArray(incoming.message) + ) { return { errors: incoming.message, } } - if (incoming.name) { + if (typeof incoming === 'object' && incoming !== null && 'name' in incoming) { return { errors: [ { - message: incoming.message, + message: (incoming as any).message, }, ], }