UNPKG

safeer-pdf-generator

Version:

Framework-agnostic PDF generation library with chunking, merging, S3 upload, and email delivery

271 lines 9.29 kB
import { ConfigurationError } from '../core/errors.js'; /** * Validate PDF generation options */ export function validatePdfOptions(options) { if (!options.title || typeof options.title !== 'string') { throw new ConfigurationError('Title is required and must be a string', 'title'); } if (!Array.isArray(options.data)) { throw new ConfigurationError('Data must be an array', 'data'); } if (!Array.isArray(options.columns)) { throw new ConfigurationError('Columns must be an array', 'columns'); } // Validate columns for (let i = 0; i < options.columns.length; i++) { const column = options.columns[i]; if (!isValidColumn(column)) { throw new ConfigurationError(`Invalid column definition at index ${i}`, 'columns'); } } // Validate chunking options if (options.chunking) { if (options.chunking.chunkSize && (typeof options.chunking.chunkSize !== 'number' || options.chunking.chunkSize < 1)) { throw new ConfigurationError('Chunk size must be a positive number', 'chunking.chunkSize'); } if (options.chunking.maxConcurrency && (typeof options.chunking.maxConcurrency !== 'number' || options.chunking.maxConcurrency < 1)) { throw new ConfigurationError('Max concurrency must be a positive number', 'chunking.maxConcurrency'); } } // Validate timeout if (options.timeoutMs && (typeof options.timeoutMs !== 'number' || options.timeoutMs < 1000)) { throw new ConfigurationError('Timeout must be at least 1000ms', 'timeoutMs'); } } /** * Check if column definition is valid */ export function isValidColumn(column) { return (typeof column === 'object' && column !== null && typeof column.key === 'string' && typeof column.title === 'string' && typeof column.dataIndex === 'string'); } /** * Validate S3 upload configuration */ export function validateS3Config(config) { if (typeof config !== 'object' || config === null) { throw new ConfigurationError('S3 config must be an object', 's3'); } if (!config.bucket || typeof config.bucket !== 'string') { throw new ConfigurationError('S3 bucket is required', 's3.bucket'); } if (!config.region || typeof config.region !== 'string') { throw new ConfigurationError('S3 region is required', 's3.region'); } } /** * Validate and enhance SMTP configuration */ export function validateSmtpConfig(config) { if (config.smtp) { const { port, secure } = config.smtp; // Validate port/secure combination if (port === 465 && secure === false) { throw new ConfigurationError('Port 465 requires secure=true (SSL). Use port 587 with secure=false for STARTTLS.', 'email.smtp.port'); } if (port === 25 && secure === true) { throw new ConfigurationError('Port 25 should use secure=false. Use port 465 for SSL or 587 for STARTTLS.', 'email.smtp.port'); } // Auto-correct common misconfigurations if (port === 465 && secure === undefined) { config.smtp.secure = true; console.warn('Auto-corrected: Port 465 detected, setting secure=true'); } if (port === 587 && secure === undefined) { config.smtp.secure = false; console.warn('Auto-corrected: Port 587 detected, setting secure=false'); } } } /** * Apply smart configuration defaults based on the SMTP host */ export function applySmartEmailDefaults(config) { if (config.smtp?.host && !config.smtp.port) { const host = config.smtp.host.toLowerCase(); if (host.includes('gmail')) { config.smtp.port = 587; config.smtp.secure = false; console.info('Applied Gmail defaults: port 587, secure=false'); } else if (host.includes('outlook') || host.includes('office365')) { config.smtp.port = 587; config.smtp.secure = false; console.info('Applied Outlook/Office365 defaults: port 587, secure=false'); } else if (host.includes('yahoo')) { config.smtp.port = 587; config.smtp.secure = false; console.info('Applied Yahoo defaults: port 587, secure=false'); } else if (host.includes('sendgrid')) { config.smtp.port = 587; config.smtp.secure = false; console.info('Applied SendGrid defaults: port 587, secure=false'); } else if (host.includes('mailgun')) { config.smtp.port = 587; config.smtp.secure = false; console.info('Applied Mailgun defaults: port 587, secure=false'); } } } /** * Validate email configuration */ export function validateEmailConfig(config) { if (typeof config !== 'object' || config === null) { throw new ConfigurationError('Email config must be an object', 'email'); } if (!config.to || typeof config.to !== 'string') { throw new ConfigurationError('Email recipient is required', 'email.to'); } if (!isValidEmail(config.to)) { throw new ConfigurationError('Invalid email address format', 'email.to'); } // Validate transport or SMTP config if (!config.transport && !config.smtp) { throw new ConfigurationError('Either transport or SMTP config is required', 'email'); } if (config.smtp) { if (!config.smtp.host || typeof config.smtp.host !== 'string') { throw new ConfigurationError('SMTP host is required', 'email.smtp.host'); } if (typeof config.smtp.port !== 'number' || config.smtp.port < 1 || config.smtp.port > 65535) { throw new ConfigurationError('SMTP port must be a valid port number', 'email.smtp.port'); } if (!config.smtp.auth || !config.smtp.auth.user || !config.smtp.auth.pass) { throw new ConfigurationError('SMTP authentication is required', 'email.smtp.auth'); } // Apply smart defaults first applySmartEmailDefaults(config); // Then validate the configuration validateSmtpConfig(config); } } /** * Validate email address format */ export function isValidEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); } /** * Check if value is a Buffer */ export function isBuffer(value) { return Buffer.isBuffer(value); } /** * Check if value is a valid logging adapter */ export function isValidLogger(logger) { return (typeof logger === 'object' && logger !== null && typeof logger.debug === 'function' && typeof logger.info === 'function' && typeof logger.warn === 'function' && typeof logger.error === 'function'); } /** * Sanitize filename for cross-platform compatibility */ export function sanitizeFilename(filename) { // Remove or replace invalid characters return filename .replace(/[<>:"/\\|?*\x00-\x1f]/g, '-') .replace(/\s+/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, ''); } /** * Validate that a value is a positive integer */ export function isPositiveInteger(value) { return typeof value === 'number' && Number.isInteger(value) && value > 0; } /** * Validate that a value is a non-negative number */ export function isNonNegativeNumber(value) { return typeof value === 'number' && !isNaN(value) && value >= 0; } /** * Check if an object has a specific property */ export function hasProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } /** * Deep clone an object (simple version for configuration) */ export function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof Array) { return obj.map(item => deepClone(item)); } if (typeof obj === 'object') { const cloned = {}; for (const key in obj) { if (hasProperty(obj, key)) { cloned[key] = deepClone(obj[key]); } } return cloned; } return obj; } /** * Check if a string is a valid URL */ export function isValidUrl(url) { try { new URL(url); return true; } catch { return false; } } /** * Ensure a value is an array */ export function ensureArray(value) { return Array.isArray(value) ? value : [value]; } /** * Get nested property value safely */ export function getNestedProperty(obj, path) { return path.split('.').reduce((current, key) => { return current && typeof current === 'object' ? current[key] : undefined; }, obj); } /** * Set nested property value safely */ export function setNestedProperty(obj, path, value) { const keys = path.split('.'); const lastKey = keys.pop(); if (!lastKey) return; const target = keys.reduce((current, key) => { if (!current[key] || typeof current[key] !== 'object') { current[key] = {}; } return current[key]; }, obj); target[lastKey] = value; } //# sourceMappingURL=guards.js.map