Files
flyer-crawler.projectium.com/src/services/processingErrors.ts
Torben Sorensen 296698758c
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 19m20s
flyer upload (anon) issues
2025-12-31 09:02:09 -08:00

106 lines
3.3 KiB
TypeScript

// src/services/processingErrors.ts
/**
* Base class for all flyer processing errors.
* This allows for catching all processing-related errors with a single `catch` block.
* Each custom error should define its own `errorCode` and a user-friendly `message`.
*/
export class FlyerProcessingError extends Error {
public errorCode: string;
public userMessage: string;
constructor(message: string, errorCode: string = 'UNKNOWN_ERROR', userMessage?: string) {
super(message); // The 'message' property of Error is for internal/developer use.
this.name = this.constructor.name;
this.errorCode = errorCode;
this.userMessage = userMessage || message; // User-friendly message for UI
Object.setPrototypeOf(this, new.target.prototype);
}
toErrorPayload(): { errorCode: string; message: string; [key: string]: any } {
return { errorCode: this.errorCode, message: this.userMessage };
}
}
/**
* Error thrown when PDF to image conversion fails (e.g., pdftocairo produces no output).
*/
export class PdfConversionError extends FlyerProcessingError {
public stderr?: string;
constructor(message: string, stderr?: string) {
super(
message,
'PDF_CONVERSION_FAILED',
'The uploaded PDF could not be processed. It might be blank, corrupt, or password-protected.',
);
this.stderr = stderr;
}
toErrorPayload(): { errorCode: string; message: string; [key: string]: any } {
return { ...super.toErrorPayload(), stderr: this.stderr };
}
}
/**
* Error thrown when the data returned from the AI service fails Zod validation.
*/
export class AiDataValidationError extends FlyerProcessingError {
constructor(
message: string,
public validationErrors: object,
public rawData: unknown,
) {
super(
message,
'AI_VALIDATION_FAILED',
"The AI couldn't read the flyer's format. Please try a clearer image or a different flyer.",
);
}
toErrorPayload(): { errorCode: string; message: string; [key: string]: any } {
return { ...super.toErrorPayload(), validationErrors: this.validationErrors, rawData: this.rawData };
}
}
/**
* Error thrown when a transformation step fails.
*/
export class TransformationError extends FlyerProcessingError {
constructor(message: string) {
super(
message,
'TRANSFORMATION_FAILED',
'There was a problem transforming the flyer data. Please check the input.',
);
}
}
/**
* Error thrown when an image conversion fails (e.g., using sharp).
*/
export class ImageConversionError extends FlyerProcessingError {
constructor(message: string) {
super(
message,
'IMAGE_CONVERSION_FAILED',
'The uploaded image could not be processed. It might be corrupt or in an unsupported format.',
);
}
}
/**
* Error thrown when all geocoding providers fail to find coordinates for an address.
*/
export class GeocodingFailedError extends FlyerProcessingError {
constructor(message: string) {
super(message, 'GEOCODING_FAILED', 'Failed to geocode the address.');
}
}
/**
* Error thrown when an uploaded file is of an unsupported type (e.g., .gif, .tiff).
*/
export class UnsupportedFileTypeError extends FlyerProcessingError {
constructor(message: string) {
super(message, 'UNSUPPORTED_FILE_TYPE', message); // The message is already user-friendly.
}
}