UNPKG

n8n-nodes-google-pagespeed

Version:

n8n community node for Google PageSpeed Insights API with comprehensive performance, accessibility, and SEO analysis

275 lines 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeAnalyzeMultiple = executeAnalyzeMultiple; exports.validateMultipleUrlsParams = validateMultipleUrlsParams; exports.optimizeBatchSize = optimizeBatchSize; exports.groupUrlsByDomain = groupUrlsByDomain; const n8n_workflow_1 = require("n8n-workflow"); const config_1 = require("../config"); const urlUtils_1 = require("../utils/urlUtils"); const apiUtils_1 = require("../utils/apiUtils"); const responseFormatter_1 = require("../helpers/responseFormatter"); /** * Execute multiple URLs analysis operation * @param context - n8n execution context * @param apiKey - Google API key * @returns Array of analysis results with batch summary */ async function executeAnalyzeMultiple(context, apiKey) { const rawUrls = context.getNodeParameter('urls', 0); const strategy = context.getNodeParameter('strategy', 0); const categories = context.getNodeParameter('categories', 0); const additionalFields = context.getNodeParameter('additionalFields', 0); if (!rawUrls || rawUrls.length === 0) { throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'At least one URL is required for batch analysis'); } console.log(`🔄 Starting batch analysis of ${rawUrls.length} URLs`); try { // Normalize all URLs and identify any that failed const urlPairs = (0, urlUtils_1.batchNormalizeUrls)(rawUrls); const validUrls = urlPairs.filter(pair => !pair.error); const invalidUrls = urlPairs.filter(pair => pair.error); console.log(`✅ Normalized URLs: ${validUrls.length} valid, ${invalidUrls.length} invalid`); if (validUrls.length === 0) { throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'No valid URLs found for analysis'); } // Log invalid URLs for debugging if (invalidUrls.length > 0) { console.warn('⚠️ Invalid URLs skipped:', invalidUrls.map(pair => `${pair.original}: ${pair.error}`)); } // Prepare configurations for batch processing const configs = createBatchConfigs(validUrls, strategy, categories, additionalFields); // Estimate and log expected completion time const estimatedTime = estimateBatchTime(configs.length); console.log(`⏱️ Estimated batch completion time: ${estimatedTime} seconds`); // Progress tracking let completedCount = 0; const onProgress = (completed, total) => { completedCount = completed; const percentage = Math.round((completed / total) * 100); console.log(`📊 Progress: ${completed}/${total} (${percentage}%) - ${(0, urlUtils_1.shortenUrlForDisplay)(configs[completed - 1]?.url || '')}`); }; // Execute batch processing console.log('🚀 Starting batch processing...'); const startTime = Date.now(); const apiResponses = await (0, apiUtils_1.batchProcessUrls)(context, apiKey, configs, additionalFields, onProgress); const totalDuration = Date.now() - startTime; console.log(`✅ Batch processing completed in ${Math.round(totalDuration / 1000)}s`); // Format all responses const analysisResults = []; for (let i = 0; i < apiResponses.length; i++) { const response = apiResponses[i]; const config = configs[i]; const originalUrl = validUrls[i].original; const result = (0, responseFormatter_1.formatResponse)(response, additionalFields.outputFormat || 'complete', config.strategy, originalUrl); // Add batch metadata const batchResult = { ...result, batchIndex: i + 1, totalBatches: configs.length, }; analysisResults.push(batchResult); } // Add results for invalid URLs invalidUrls.forEach((urlPair, index) => { const errorResult = { url: urlPair.normalized, originalUrl: urlPair.original, strategy: strategy === 'both' ? 'both' : strategy, error: urlPair.error, errorType: 'INVALID_URL', skipped: true, analysisTime: new Date().toISOString(), }; analysisResults.push(errorResult); }); // Create batch summary const batchResults = (0, responseFormatter_1.formatBatchResults)(analysisResults); // Prepare output const results = []; // Add batch metadata as first item results.push({ json: { type: 'batch-summary', ...batchResults.summary, performance: { totalDuration, averageTimePerUrl: Math.round(totalDuration / validUrls.length), successRate: Math.round((batchResults.summary.successful / batchResults.summary.total) * 100), }, urlBreakdown: { total: rawUrls.length, valid: validUrls.length, invalid: invalidUrls.length, successful: batchResults.summary.successful, failed: batchResults.summary.failed, }, }, }); // Add individual results batchResults.results.forEach(result => { results.push({ json: result }); }); console.log(`📋 Batch analysis complete: ${results.length} total items (1 summary + ${results.length - 1} results)`); return results; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error('❌ Batch analysis failed:', errorMessage); throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch analysis failed: ${errorMessage}`); } } /** * Create API configurations for batch processing * @param urlPairs - Array of normalized URL pairs * @param strategy - Analysis strategy * @param categories - Categories to analyze * @param additionalFields - Additional options * @returns Array of API request configurations */ function createBatchConfigs(urlPairs, strategy, categories, additionalFields) { const configs = []; urlPairs.forEach(urlPair => { if (strategy === 'both') { // Create configs for both mobile and desktop configs.push({ url: urlPair.normalized, strategy: 'mobile', categories, locale: additionalFields.locale, screenshot: additionalFields.screenshot, timeout: additionalFields.customTimeout, retryAttempts: additionalFields.retryAttempts, }); configs.push({ url: urlPair.normalized, strategy: 'desktop', categories, locale: additionalFields.locale, screenshot: additionalFields.screenshot, timeout: additionalFields.customTimeout, retryAttempts: additionalFields.retryAttempts, }); } else { // Single strategy configs.push({ url: urlPair.normalized, strategy, categories, locale: additionalFields.locale, screenshot: additionalFields.screenshot, timeout: additionalFields.customTimeout, retryAttempts: additionalFields.retryAttempts, }); } }); return configs; } /** * Estimate total batch processing time * @param configCount - Number of API requests to make * @returns Estimated time in seconds */ function estimateBatchTime(configCount) { const timePerRequest = 15; // seconds const batchSize = config_1.PAGESPEED_CONFIG.MAX_CONCURRENT_REQUESTS; const batchDelaySeconds = config_1.PAGESPEED_CONFIG.BATCH_DELAY_MS / 1000; const numberOfBatches = Math.ceil(configCount / batchSize); const totalRequestTime = numberOfBatches * timePerRequest; const totalDelayTime = (numberOfBatches - 1) * batchDelaySeconds; return Math.round(totalRequestTime + totalDelayTime); } /** * Validate multiple URLs analysis parameters * @param context - n8n execution context * @returns Validation result */ function validateMultipleUrlsParams(context) { const rawUrls = context.getNodeParameter('urls', 0); const warnings = []; if (!rawUrls || rawUrls.length === 0) { return { isValid: false, error: 'At least one URL is required for batch analysis' }; } // Check for reasonable batch size if (rawUrls.length > 100) { warnings.push(`Large batch size (${rawUrls.length} URLs) may take significant time and consume API quota`); } // Check for duplicate URLs const uniqueUrls = new Set(rawUrls.map(url => url.toLowerCase().trim())); if (uniqueUrls.size < rawUrls.length) { warnings.push(`${rawUrls.length - uniqueUrls.size} duplicate URLs detected`); } // Validate URL formats (basic check) const invalidUrls = rawUrls.filter(url => !url || typeof url !== 'string' || url.trim().length === 0); if (invalidUrls.length > 0) { warnings.push(`${invalidUrls.length} empty or invalid URLs detected`); } return { isValid: true, warnings: warnings.length > 0 ? warnings : undefined }; } /** * Optimize batch size based on API limits and configuration * @param urlCount - Number of URLs to process * @param strategy - Analysis strategy * @returns Optimized batch configuration */ function optimizeBatchSize(urlCount, strategy) { const recommendations = []; let recommendedBatchSize = urlCount; // Calculate request multiplier based on strategy const requestMultiplier = strategy === 'both' ? 2 : 1; const totalRequests = urlCount * requestMultiplier; // Recommend smaller batches for large sets if (totalRequests > 50) { recommendedBatchSize = Math.min(25, urlCount); recommendations.push('Consider processing URLs in smaller batches to avoid API rate limits'); } if (totalRequests > 200) { recommendedBatchSize = Math.min(10, urlCount); recommendations.push('Large batch detected - strongly recommend splitting into multiple smaller batches'); recommendations.push('Monitor API quota consumption during processing'); } const estimatedBatches = Math.ceil(urlCount / recommendedBatchSize); const estimatedTime = estimateBatchTime(totalRequests); if (estimatedTime > 300) { // 5 minutes recommendations.push(`Estimated processing time: ${Math.round(estimatedTime / 60)} minutes`); } return { recommendedBatchSize, estimatedBatches, estimatedTime, recommendations }; } /** * Group URLs by domain for better batch organization * @param urls - Array of URLs to group * @returns Grouped URLs by domain */ function groupUrlsByDomain(urls) { const domainGroups = new Map(); urls.forEach(url => { try { const domain = new URL(url).hostname; if (!domainGroups.has(domain)) { domainGroups.set(domain, []); } domainGroups.get(domain).push(url); } catch (error) { // Handle invalid URLs by grouping them under 'invalid' if (!domainGroups.has('invalid')) { domainGroups.set('invalid', []); } domainGroups.get('invalid').push(url); } }); return domainGroups; } //# sourceMappingURL=analyzeMultipleUrls.js.map