UNPKG

adpa-enterprise-framework-automation

Version:

Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe

445 lines 18 kB
/** * Adobe PDF Processor - Core Implementation * Phase 1: PDF Services Integration with Enhanced Error Handling and Security */ import { getAdobeConfig, ADOBE_API_LIMITS, ADOBE_ERROR_CODES, QUALITY_PRESETS } from './config.js'; import { CircuitBreaker } from '../utils/circuit-breaker.js'; import { RateLimiter } from '../utils/rate-limiter.js'; import { Logger } from '../utils/logger.js'; /** * Adobe PDF Services processor with enterprise-grade reliability and security */ export class AdobePDFProcessor { config; circuitBreaker; rateLimiter; logger; authToken = null; tokenExpiry = null; constructor(config) { this.config = { ...getAdobeConfig(), ...config }; this.logger = new Logger('AdobePDFProcessor'); // Initialize circuit breaker for resilience this.circuitBreaker = new CircuitBreaker({ failureThreshold: ADOBE_API_LIMITS.CIRCUIT_BREAKER_THRESHOLD, resetTimeout: ADOBE_API_LIMITS.CIRCUIT_BREAKER_RESET_TIMEOUT, monitor: (state) => { this.logger.info(`Circuit breaker state changed: ${state}`); } }); // Initialize rate limiter this.rateLimiter = new RateLimiter({ requestsPerMinute: ADOBE_API_LIMITS.REQUESTS_PER_MINUTE, requestsPerHour: ADOBE_API_LIMITS.REQUESTS_PER_HOUR }); this.logger.info('Adobe PDF Processor initialized', { environment: this.config.environment, apiBaseUrl: this.config.apiBaseUrl }); } /** * Generate professional PDF from markdown content with corporate branding */ async generateProfessionalPDF(markdownContent, template) { const startTime = Date.now(); const operationId = this.generateOperationId(); this.logger.info('Starting PDF generation', { operationId, templateId: template.id, contentLength: markdownContent.length }); try { // Rate limiting check await this.rateLimiter.waitForAvailableSlot(); // Input validation this.validateInputs(markdownContent, template); // Execute with circuit breaker protection const result = await this.circuitBreaker.execute(async () => { return await this.executeProcessing(markdownContent, template, operationId); }); const processingTime = Date.now() - startTime; this.logger.info('PDF generation completed successfully', { operationId, processingTime, outputSize: result.size }); return result; } catch (error) { const processingTime = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error('PDF generation failed', { operationId, error: errorMessage, processingTime }); throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `PDF generation failed: ${errorMessage}`, error, { operationId, templateId: template.id }); } } /** * Add interactive elements to PDF (bookmarks, forms, signatures) */ async addInteractiveElements(pdf, metadata) { const operationId = this.generateOperationId(); this.logger.info('Adding interactive elements', { operationId, pdfId: pdf.id, elementsCount: { bookmarks: metadata.tableOfContents?.length || 0, reviewFields: metadata.reviewFields?.length || 0, approvers: metadata.approvers?.length || 0 } }); try { await this.rateLimiter.waitForAvailableSlot(); const enhancedPdf = await this.circuitBreaker.execute(async () => { return await this.processInteractiveElements(pdf, metadata, operationId); }); this.logger.info('Interactive elements added successfully', { operationId, pdfId: enhancedPdf.id }); return enhancedPdf; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error('Failed to add interactive elements', { operationId, pdfId: pdf.id, error: errorMessage }); throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `Interactive elements processing failed: ${errorMessage}`, error, { operationId, pdfId: pdf.id }); } } /** * Validate PDF document structure and content */ async validateDocument(pdf) { const operationId = this.generateOperationId(); this.logger.info('Starting document validation', { operationId, pdfId: pdf.id, size: pdf.size }); try { const validation = await this.performValidation(pdf, operationId); this.logger.info('Document validation completed', { operationId, valid: validation.valid, errorsCount: validation.errors.length, warningsCount: validation.warnings.length }); return validation; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error('Document validation failed', { operationId, pdfId: pdf.id, error: errorMessage }); throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `Document validation failed: ${errorMessage}`, error, { operationId, pdfId: pdf.id }); } } /** * Optimize PDF for web delivery */ async optimizeForWeb(pdf) { return this.optimizeDocument(pdf, 'web', QUALITY_PRESETS.standard); } /** * Optimize PDF for print production */ async optimizeForPrint(pdf) { return this.optimizeDocument(pdf, 'print', QUALITY_PRESETS.print); } // Private implementation methods async executeProcessing(markdownContent, template, operationId) { // Ensure authentication await this.ensureAuthentication(); // Step 1: Convert markdown to structured HTML const htmlContent = await this.markdownToHTML(markdownContent); this.logger.debug('Markdown converted to HTML', { operationId }); // Step 2: Apply corporate branding template const styledHTML = await this.applyBrandingTemplate(htmlContent, template); this.logger.debug('Branding template applied', { operationId }); // Step 3: Generate PDF using Adobe PDF Services const pdfBuffer = await this.callAdobePDFAPI({ html: styledHTML, options: this.buildPDFOptions(template), operationId }); // Step 4: Create PDF document object const pdfDocument = { id: this.generateDocumentId(), content: pdfBuffer, metadata: this.extractMetadata(styledHTML, template), format: 'pdf', size: pdfBuffer.length, createdAt: new Date() }; return pdfDocument; } async processInteractiveElements(pdf, metadata, operationId) { await this.ensureAuthentication(); let enhancedContent = pdf.content; // Add bookmarks for navigation if (metadata.tableOfContents && metadata.tableOfContents.length > 0) { enhancedContent = await this.addBookmarks(enhancedContent, metadata.tableOfContents); this.logger.debug('Bookmarks added', { operationId, bookmarksCount: metadata.tableOfContents.length }); } // Add form fields for stakeholder input if (metadata.reviewFields && metadata.reviewFields.length > 0) { enhancedContent = await this.addFormFields(enhancedContent, metadata.reviewFields); this.logger.debug('Form fields added', { operationId, fieldsCount: metadata.reviewFields.length }); } // Add digital signature fields for approval workflow if (metadata.approvers && metadata.approvers.length > 0) { enhancedContent = await this.addSignatureFields(enhancedContent, metadata.approvers); this.logger.debug('Signature fields added', { operationId, signatureFields: metadata.approvers.length }); } return { ...pdf, content: enhancedContent, metadata: { ...pdf.metadata, ...metadata } }; } async performValidation(pdf, operationId) { const errors = []; const warnings = []; // Validate file size if (pdf.size > ADOBE_API_LIMITS.MAX_FILE_SIZE_MB * 1024 * 1024) { errors.push({ code: ADOBE_ERROR_CODES.FILE_TOO_LARGE, message: `File size ${pdf.size} exceeds maximum allowed size`, severity: 'error' }); } // Validate PDF structure (mock implementation) try { // This would integrate with Adobe PDF validation APIs const structureValid = await this.validatePDFStructure(pdf.content); if (!structureValid) { warnings.push({ code: 'PDF_STRUCTURE_WARNING', message: 'PDF structure may have issues', suggestion: 'Consider regenerating the document' }); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); errors.push({ code: 'VALIDATION_ERROR', message: `Structure validation failed: ${errorMessage}`, severity: 'warning' }); } return { valid: errors.length === 0, errors, warnings, metadata: pdf.metadata }; } async optimizeDocument(pdf, target, preset) { const operationId = this.generateOperationId(); this.logger.info('Optimizing document', { operationId, pdfId: pdf.id, target, preset: preset }); try { await this.ensureAuthentication(); const optimizedContent = await this.callAdobeOptimizationAPI({ content: pdf.content, preset, target, operationId }); const optimizedPdf = { ...pdf, content: optimizedContent, size: optimizedContent.length }; this.logger.info('Document optimization completed', { operationId, originalSize: pdf.size, optimizedSize: optimizedContent.length, compressionRatio: ((pdf.size - optimizedContent.length) / pdf.size * 100).toFixed(2) + '%' }); return optimizedPdf; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error('Document optimization failed', { operationId, pdfId: pdf.id, error: errorMessage }); throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `Document optimization failed: ${errorMessage}`, error, { operationId, pdfId: pdf.id, target }); } } // Adobe API integration methods async ensureAuthentication() { if (this.authToken && this.tokenExpiry && new Date() < this.tokenExpiry) { return; // Token still valid } try { const response = await this.authenticateWithAdobe(); this.authToken = response.access_token; this.tokenExpiry = new Date(Date.now() + response.expires_in * 1000); this.logger.info('Adobe authentication successful', { expiresAt: this.tokenExpiry.toISOString() }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error('Adobe authentication failed', { error: errorMessage }); throw this.createProcessingError(ADOBE_ERROR_CODES.AUTH_FAILED, `Authentication failed: ${errorMessage}`, error); } } async authenticateWithAdobe() { // Mock implementation - would integrate with actual Adobe authentication // This would use JWT authentication with private key signing return { access_token: 'mock_token_' + Date.now(), expires_in: 3600, token_type: 'Bearer' }; } async callAdobePDFAPI(params) { // Mock implementation - would make actual Adobe API calls // This represents the HTML to PDF conversion this.logger.debug('Calling Adobe PDF API', { operationId: params.operationId, htmlLength: params.html.length }); // Simulate processing time await new Promise(resolve => setTimeout(resolve, 1000)); // Return mock PDF buffer return Buffer.from(`Mock PDF content for operation ${params.operationId}`); } async callAdobeOptimizationAPI(params) { // Mock implementation for PDF optimization this.logger.debug('Calling Adobe optimization API', { operationId: params.operationId, target: params.target }); await new Promise(resolve => setTimeout(resolve, 500)); // Simulate compression const compressionRatio = params.preset.compression === 'high' ? 0.6 : 0.8; const optimizedSize = Math.floor(params.content.length * compressionRatio); return Buffer.alloc(optimizedSize, 'Optimized PDF content'); } // Helper methods async markdownToHTML(markdown) { // Mock implementation - would use a robust markdown parser return `<html><body>${markdown.replace(/\n/g, '<br>')}</body></html>`; } async applyBrandingTemplate(html, template) { // Mock implementation - would apply CSS styling based on template const styles = this.generateCSSFromTemplate(template); return `<html><head><style>${styles}</style></head><body>${html}</body></html>`; } generateCSSFromTemplate(template) { return ` body { font-family: ${template.corporateFonts[0]?.family || 'Arial'}, sans-serif; color: ${template.colorScheme.text}; background: ${template.colorScheme.background}; margin: ${template.margins.top}mm ${template.margins.right}mm ${template.margins.bottom}mm ${template.margins.left}mm; } h1 { color: ${template.colorScheme.primary}; } h2 { color: ${template.colorScheme.secondary}; } `; } buildPDFOptions(template) { return { pageLayout: template.pageLayout, margins: template.margins, fonts: template.corporateFonts, watermark: template.watermark, headerFooter: template.headerFooter }; } extractMetadata(html, template) { // Mock metadata extraction return { title: 'Generated Document', author: 'ADPA System', subject: 'Automated Documentation', keywords: ['ADPA', 'Documentation', 'Adobe'], tableOfContents: this.extractTOC(html) }; } extractTOC(html) { // Mock TOC extraction from HTML headings return [ { title: 'Introduction', level: 1, pageNumber: 1 }, { title: 'Overview', level: 2, pageNumber: 2 } ]; } async addBookmarks(content, toc) { // Mock bookmark addition this.logger.debug('Adding bookmarks', { bookmarksCount: toc.length }); return content; } async addFormFields(content, fields) { // Mock form field addition this.logger.debug('Adding form fields', { fieldsCount: fields.length }); return content; } async addSignatureFields(content, approvers) { // Mock signature field addition this.logger.debug('Adding signature fields', { signatureFields: approvers.length }); return content; } async validatePDFStructure(content) { // Mock PDF structure validation return content.length > 0; } // Utility methods validateInputs(markdownContent, template) { if (!markdownContent || markdownContent.trim().length === 0) { throw new Error('Markdown content cannot be empty'); } if (!template || !template.id) { throw new Error('Valid template is required'); } if (markdownContent.length > 10 * 1024 * 1024) { // 10MB limit throw new Error('Markdown content exceeds maximum size limit'); } } generateOperationId() { return `adobe-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } generateDocumentId() { return `pdf-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } createProcessingError(code, message, originalError, context) { const error = new Error(message); error.code = code; error.severity = 'recoverable'; error.context = context; error.timestamp = new Date(); error.name = 'ProcessingError'; if (originalError) { error.stack = originalError.stack; } return error; } } //# sourceMappingURL=pdf-processor.js.map