UNPKG

@fin.cx/einvoice

Version:

A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.

241 lines 16.6 kB
/** * Base error class for all EInvoice-related errors */ export class EInvoiceError extends Error { constructor(message, code, details, cause) { super(message); this.name = 'EInvoiceError'; this.code = code; this.details = details; this.cause = cause; // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } } /** * Returns a detailed error message including cause if available */ getDetailedMessage() { let message = `${this.name} [${this.code}]: ${this.message}`; if (this.details) { message += `\nDetails: ${JSON.stringify(this.details, null, 2)}`; } if (this.cause) { message += `\nCaused by: ${this.cause.message}`; } return message; } } /** * Error thrown when XML parsing fails */ export class EInvoiceParsingError extends EInvoiceError { constructor(message, details, cause) { super(message, 'PARSE_ERROR', details, cause); this.name = 'EInvoiceParsingError'; this.line = details?.line; this.column = details?.column; this.xmlSnippet = details?.xmlSnippet; } /** * Returns a user-friendly error message with location information */ getLocationMessage() { let message = this.message; if (this.line !== undefined && this.column !== undefined) { message += ` at line ${this.line}, column ${this.column}`; } if (this.xmlSnippet) { message += `\nNear: ${this.xmlSnippet}`; } return message; } } /** * Error thrown when validation fails */ export class EInvoiceValidationError extends EInvoiceError { constructor(message, validationErrors, details) { super(message, 'VALIDATION_ERROR', details); this.name = 'EInvoiceValidationError'; this.validationErrors = validationErrors; } /** * Returns a formatted validation report */ getValidationReport() { let report = `${this.message}\n\nValidation errors:\n`; for (const error of this.validationErrors) { report += `- [${error.code}] ${error.message}`; if (error.location) { report += ` (at ${error.location})`; } if (error.severity) { report += ` [${error.severity.toUpperCase()}]`; } report += '\n'; } return report; } /** * Gets validation errors by severity */ getErrorsBySeverity(severity) { return this.validationErrors.filter(e => (e.severity || 'error') === severity); } } /** * Error thrown during PDF operations */ export class EInvoicePDFError extends EInvoiceError { constructor(message, operation, details, cause) { super(message, `PDF_${operation.toUpperCase()}_ERROR`, details, cause); this.name = 'EInvoicePDFError'; this.operation = operation; this.pdfInfo = details?.pdfInfo; } /** * Returns recovery suggestions based on the operation */ getRecoverySuggestions() { const suggestions = []; switch (this.operation) { case 'extract': suggestions.push('Ensure the PDF contains embedded XML data', 'Check if the PDF is a valid PDF/A-3 document', 'Try using a different extraction method', 'Verify the PDF is not corrupted'); break; case 'embed': suggestions.push('Ensure the source PDF is valid', 'Check that the XML data is well-formed', 'Verify sufficient memory is available', 'Try with a smaller XML payload'); break; case 'create': suggestions.push('Verify all required invoice data is provided', 'Check that the template PDF exists', 'Ensure write permissions for output directory'); break; case 'validate': suggestions.push('Check PDF/A-3 compliance', 'Verify XML attachment structure', 'Ensure proper PDF metadata'); break; } return suggestions; } } /** * Error thrown for format-specific issues */ export class EInvoiceFormatError extends EInvoiceError { constructor(message, details, cause) { super(message, 'FORMAT_ERROR', details, cause); this.name = 'EInvoiceFormatError'; this.sourceFormat = details.sourceFormat; this.targetFormat = details.targetFormat; this.unsupportedFeatures = details.unsupportedFeatures; } /** * Returns a compatibility report */ getCompatibilityReport() { let report = this.message; if (this.sourceFormat && this.targetFormat) { report += `\n\nConversion: ${this.sourceFormat} → ${this.targetFormat}`; } if (this.unsupportedFeatures && this.unsupportedFeatures.length > 0) { report += '\n\nUnsupported features:'; for (const feature of this.unsupportedFeatures) { report += `\n- ${feature}`; } } return report; } } /** * Error recovery helper class */ export class ErrorRecovery { /** * Attempts to recover from a parsing error by cleaning the XML */ static async attemptXMLRecovery(xmlString, error) { try { let cleanedXml = xmlString; // Remove BOM if present if (cleanedXml.charCodeAt(0) === 0xFEFF) { cleanedXml = cleanedXml.slice(1); } // Fix common encoding issues cleanedXml = cleanedXml .replace(/&(?!(amp|lt|gt|apos|quot);)/g, '&amp;') .replace(/</g, (match, offset) => { // Don't replace if it's part of <![CDATA[ const before = cleanedXml.substring(Math.max(0, offset - 10), offset); if (before.includes('CDATA[')) return match; return '&lt;'; }); // Try to fix unclosed tags if we have location info if (error.line && error.xmlSnippet) { // This is a simplified approach - real implementation would be more sophisticated const lines = cleanedXml.split('\n'); if (lines[error.line - 1]) { // Attempt basic fixes based on the error // This is just a placeholder for more sophisticated recovery } } return { success: true, cleanedXml, message: 'Applied basic XML cleaning and encoding fixes' }; } catch (recoveryError) { return { success: false, message: `Recovery failed: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}` }; } } /** * Provides partial data extraction on validation failure */ static extractPartialData(xmlString, format) { try { // This would implement format-specific partial extraction // For now, returning a placeholder return { success: false, message: 'Partial data extraction not yet implemented' }; } catch (error) { return { success: false, message: `Partial extraction failed: ${error instanceof Error ? error.message : String(error)}` }; } } } /** * Error context builder for detailed error information */ export class ErrorContext { constructor() { this.context = new Map(); } add(key, value) { this.context.set(key, value); return this; } addTimestamp() { this.context.set('timestamp', new Date().toISOString()); return this; } addEnvironment() { this.context.set('environment', { nodeVersion: process.version, platform: process.platform, arch: process.arch }); return this; } build() { return Object.fromEntries(this.context); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvZXJyb3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGFBQWMsU0FBUSxLQUFLO0lBS3RDLFlBQVksT0FBZSxFQUFFLElBQVksRUFBRSxPQUFhLEVBQUUsS0FBYTtRQUNyRSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDZixJQUFJLENBQUMsSUFBSSxHQUFHLGVBQWUsQ0FBQztRQUM1QixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztRQUNqQixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUVuQixxRkFBcUY7UUFDckYsSUFBSSxLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUM1QixLQUFLLENBQUMsaUJBQWlCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNsRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksa0JBQWtCO1FBQ3ZCLElBQUksT0FBTyxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksS0FBSyxJQUFJLENBQUMsSUFBSSxNQUFNLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUM3RCxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNqQixPQUFPLElBQUksY0FBYyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDbkUsQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxJQUFJLGdCQUFnQixJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2xELENBQUM7UUFDRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0NBQ0Y7QUFFRDs7R0FFRztB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